parent
5c9afcc785
commit
da079d47d7
72 changed files with 7768 additions and 0 deletions
@ -0,0 +1 @@ |
||||
logcatd |
@ -0,0 +1,2 @@ |
||||
Import('env', 'messaging') |
||||
env.Program('logcatd.cc', LIBS=[messaging, 'cutils', 'zmq', 'czmq', 'capnp', 'kj']) |
@ -0,0 +1,71 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <cassert> |
||||
#include <android/log.h> |
||||
|
||||
//#include <log/log.h>
|
||||
#include <log/logger.h> |
||||
#include <log/logprint.h> |
||||
|
||||
#include <capnp/serialize.h> |
||||
#include "common/timing.h" |
||||
#include "cereal/gen/cpp/log.capnp.h" |
||||
#include "messaging.hpp" |
||||
|
||||
int main() { |
||||
int err; |
||||
|
||||
struct logger_list *logger_list = android_logger_list_alloc(ANDROID_LOG_RDONLY, 0, 0); |
||||
assert(logger_list); |
||||
struct logger *main_logger = android_logger_open(logger_list, LOG_ID_MAIN); |
||||
assert(main_logger); |
||||
struct logger *radio_logger = android_logger_open(logger_list, LOG_ID_RADIO); |
||||
assert(radio_logger); |
||||
struct logger *system_logger = android_logger_open(logger_list, LOG_ID_SYSTEM); |
||||
assert(system_logger); |
||||
struct logger *crash_logger = android_logger_open(logger_list, LOG_ID_CRASH); |
||||
assert(crash_logger); |
||||
// struct logger *kernel_logger = android_logger_open(logger_list, LOG_ID_KERNEL);
|
||||
// assert(kernel_logger);
|
||||
|
||||
Context * c = Context::create(); |
||||
PubSocket * androidLog = PubSocket::create(c, "androidLog"); |
||||
assert(androidLog != NULL); |
||||
|
||||
while (1) { |
||||
log_msg log_msg; |
||||
err = android_logger_list_read(logger_list, &log_msg); |
||||
if (err <= 0) { |
||||
break; |
||||
} |
||||
|
||||
AndroidLogEntry entry; |
||||
err = android_log_processLogBuffer(&log_msg.entry_v1, &entry); |
||||
if (err < 0) { |
||||
continue; |
||||
} |
||||
|
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
auto androidEntry = event.initAndroidLogEntry(); |
||||
androidEntry.setId(log_msg.id()); |
||||
androidEntry.setTs(entry.tv_sec * 1000000000ULL + entry.tv_nsec); |
||||
androidEntry.setPriority(entry.priority); |
||||
androidEntry.setPid(entry.pid); |
||||
androidEntry.setTid(entry.tid); |
||||
androidEntry.setTag(entry.tag); |
||||
androidEntry.setMessage(entry.message); |
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
androidLog->send((char*)bytes.begin(), bytes.size()); |
||||
} |
||||
|
||||
android_logger_list_close(logger_list); |
||||
|
||||
delete c; |
||||
delete androidLog; |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1 @@ |
||||
loggerd |
@ -0,0 +1,12 @@ |
||||
Import('env', 'arch', 'messaging', 'common', 'visionipc') |
||||
|
||||
src = ['loggerd.cc', 'logger.c'] |
||||
libs = ['zmq', 'czmq', 'capnp', 'kj', 'yaml-cpp', 'z', |
||||
'avformat', 'avcodec', 'swscale', 'avutil', |
||||
'yuv', 'bz2', common, 'json', messaging, visionipc] |
||||
|
||||
if arch == "aarch64": |
||||
src += ['encoder.c', 'raw_logger.cc'] |
||||
libs += ['OmxVenc', 'OmxCore', 'cutils'] |
||||
|
||||
env.Program(src, LIBS=libs) |
@ -0,0 +1,29 @@ |
||||
import os |
||||
|
||||
if os.environ.get('LOGGERD_ROOT', False): |
||||
ROOT = os.environ['LOGGERD_ROOT'] |
||||
print("Custom loggerd root: ", ROOT) |
||||
else: |
||||
ROOT = '/data/media/0/realdata/' |
||||
|
||||
SEGMENT_LENGTH = 60 |
||||
|
||||
|
||||
def get_available_percent(default=None): |
||||
try: |
||||
statvfs = os.statvfs(ROOT) |
||||
available_percent = 100.0 * statvfs.f_bavail / statvfs.f_blocks |
||||
except OSError: |
||||
available_percent = default |
||||
|
||||
return available_percent |
||||
|
||||
|
||||
def get_available_bytes(default=None): |
||||
try: |
||||
statvfs = os.statvfs(ROOT) |
||||
available_bytes = statvfs.f_bavail * statvfs.f_frsize |
||||
except OSError: |
||||
available_bytes = default |
||||
|
||||
return available_bytes |
@ -0,0 +1,39 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import shutil |
||||
import threading |
||||
from selfdrive.swaglog import cloudlog |
||||
from selfdrive.loggerd.config import ROOT, get_available_bytes |
||||
from selfdrive.loggerd.uploader import listdir_by_creation |
||||
|
||||
|
||||
def deleter_thread(exit_event): |
||||
while not exit_event.is_set(): |
||||
available_bytes = get_available_bytes() |
||||
|
||||
if available_bytes is not None and available_bytes < (5 * 1024 * 1024 * 1024): |
||||
# remove the earliest directory we can |
||||
dirs = listdir_by_creation(ROOT) |
||||
for delete_dir in dirs: |
||||
delete_path = os.path.join(ROOT, delete_dir) |
||||
|
||||
if any(name.endswith(".lock") for name in os.listdir(delete_path)): |
||||
continue |
||||
|
||||
try: |
||||
cloudlog.info("deleting %s" % delete_path) |
||||
shutil.rmtree(delete_path) |
||||
break |
||||
except OSError: |
||||
cloudlog.exception("issue deleting %s" % delete_path) |
||||
exit_event.wait(.1) |
||||
else: |
||||
exit_event.wait(30) |
||||
|
||||
|
||||
def main(gctx=None): |
||||
deleter_thread(threading.Event()) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -0,0 +1,796 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <unistd.h> |
||||
#include <assert.h> |
||||
|
||||
#include <czmq.h> |
||||
|
||||
#include <pthread.h> |
||||
|
||||
#include <OMX_Component.h> |
||||
#include <OMX_IndexExt.h> |
||||
#include <OMX_VideoExt.h> |
||||
#include <OMX_QCOMExtns.h> |
||||
|
||||
#include <libyuv.h> |
||||
|
||||
#include <android/log.h> |
||||
|
||||
#include <msm_media_info.h> |
||||
|
||||
#include "common/mutex.h" |
||||
#include "common/swaglog.h" |
||||
|
||||
#include "encoder.h" |
||||
|
||||
#define ALOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "omxapp", ##__VA_ARGS__) |
||||
|
||||
// encoder: lossey codec using hardware hevc
|
||||
static void wait_for_state(EncoderState *s, OMX_STATETYPE state) { |
||||
pthread_mutex_lock(&s->state_lock); |
||||
while (s->state != state) { |
||||
pthread_cond_wait(&s->state_cv, &s->state_lock); |
||||
} |
||||
pthread_mutex_unlock(&s->state_lock); |
||||
} |
||||
|
||||
static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, OMX_EVENTTYPE event, |
||||
OMX_U32 data1, OMX_U32 data2, OMX_PTR event_data) { |
||||
EncoderState *s = app_data; |
||||
|
||||
switch (event) { |
||||
case OMX_EventCmdComplete: |
||||
assert(data1 == OMX_CommandStateSet); |
||||
LOG("set state event 0x%x", data2); |
||||
pthread_mutex_lock(&s->state_lock); |
||||
s->state = data2; |
||||
pthread_cond_broadcast(&s->state_cv); |
||||
pthread_mutex_unlock(&s->state_lock); |
||||
break; |
||||
case OMX_EventError: |
||||
LOGE("OMX error 0x%08x", data1); |
||||
// assert(false);
|
||||
break; |
||||
default: |
||||
LOGE("unhandled event %d", event); |
||||
assert(false); |
||||
break; |
||||
} |
||||
|
||||
pthread_mutex_unlock(&s->state_lock); |
||||
|
||||
return OMX_ErrorNone; |
||||
} |
||||
|
||||
static OMX_ERRORTYPE empty_buffer_done(OMX_HANDLETYPE component, OMX_PTR app_data, |
||||
OMX_BUFFERHEADERTYPE *buffer) { |
||||
EncoderState *s = app_data; |
||||
|
||||
// printf("empty_buffer_done\n");
|
||||
|
||||
queue_push(&s->free_in, (void*)buffer); |
||||
|
||||
return OMX_ErrorNone; |
||||
} |
||||
|
||||
|
||||
static OMX_ERRORTYPE fill_buffer_done(OMX_HANDLETYPE component, OMX_PTR app_data, |
||||
OMX_BUFFERHEADERTYPE *buffer) { |
||||
EncoderState *s = app_data; |
||||
|
||||
// printf("fill_buffer_done\n");
|
||||
|
||||
queue_push(&s->done_out, (void*)buffer); |
||||
|
||||
return OMX_ErrorNone; |
||||
} |
||||
|
||||
static OMX_CALLBACKTYPE omx_callbacks = { |
||||
.EventHandler = event_handler, |
||||
.EmptyBufferDone = empty_buffer_done, |
||||
.FillBufferDone = fill_buffer_done, |
||||
}; |
||||
|
||||
#define PORT_INDEX_IN 0 |
||||
#define PORT_INDEX_OUT 1 |
||||
|
||||
static const char* omx_color_fomat_name(uint32_t format) { |
||||
switch (format) { |
||||
case OMX_COLOR_FormatUnused: return "OMX_COLOR_FormatUnused"; |
||||
case OMX_COLOR_FormatMonochrome: return "OMX_COLOR_FormatMonochrome"; |
||||
case OMX_COLOR_Format8bitRGB332: return "OMX_COLOR_Format8bitRGB332"; |
||||
case OMX_COLOR_Format12bitRGB444: return "OMX_COLOR_Format12bitRGB444"; |
||||
case OMX_COLOR_Format16bitARGB4444: return "OMX_COLOR_Format16bitARGB4444"; |
||||
case OMX_COLOR_Format16bitARGB1555: return "OMX_COLOR_Format16bitARGB1555"; |
||||
case OMX_COLOR_Format16bitRGB565: return "OMX_COLOR_Format16bitRGB565"; |
||||
case OMX_COLOR_Format16bitBGR565: return "OMX_COLOR_Format16bitBGR565"; |
||||
case OMX_COLOR_Format18bitRGB666: return "OMX_COLOR_Format18bitRGB666"; |
||||
case OMX_COLOR_Format18bitARGB1665: return "OMX_COLOR_Format18bitARGB1665"; |
||||
case OMX_COLOR_Format19bitARGB1666: return "OMX_COLOR_Format19bitARGB1666"; |
||||
case OMX_COLOR_Format24bitRGB888: return "OMX_COLOR_Format24bitRGB888"; |
||||
case OMX_COLOR_Format24bitBGR888: return "OMX_COLOR_Format24bitBGR888"; |
||||
case OMX_COLOR_Format24bitARGB1887: return "OMX_COLOR_Format24bitARGB1887"; |
||||
case OMX_COLOR_Format25bitARGB1888: return "OMX_COLOR_Format25bitARGB1888"; |
||||
case OMX_COLOR_Format32bitBGRA8888: return "OMX_COLOR_Format32bitBGRA8888"; |
||||
case OMX_COLOR_Format32bitARGB8888: return "OMX_COLOR_Format32bitARGB8888"; |
||||
case OMX_COLOR_FormatYUV411Planar: return "OMX_COLOR_FormatYUV411Planar"; |
||||
case OMX_COLOR_FormatYUV411PackedPlanar: return "OMX_COLOR_FormatYUV411PackedPlanar"; |
||||
case OMX_COLOR_FormatYUV420Planar: return "OMX_COLOR_FormatYUV420Planar"; |
||||
case OMX_COLOR_FormatYUV420PackedPlanar: return "OMX_COLOR_FormatYUV420PackedPlanar"; |
||||
case OMX_COLOR_FormatYUV420SemiPlanar: return "OMX_COLOR_FormatYUV420SemiPlanar"; |
||||
case OMX_COLOR_FormatYUV422Planar: return "OMX_COLOR_FormatYUV422Planar"; |
||||
case OMX_COLOR_FormatYUV422PackedPlanar: return "OMX_COLOR_FormatYUV422PackedPlanar"; |
||||
case OMX_COLOR_FormatYUV422SemiPlanar: return "OMX_COLOR_FormatYUV422SemiPlanar"; |
||||
case OMX_COLOR_FormatYCbYCr: return "OMX_COLOR_FormatYCbYCr"; |
||||
case OMX_COLOR_FormatYCrYCb: return "OMX_COLOR_FormatYCrYCb"; |
||||
case OMX_COLOR_FormatCbYCrY: return "OMX_COLOR_FormatCbYCrY"; |
||||
case OMX_COLOR_FormatCrYCbY: return "OMX_COLOR_FormatCrYCbY"; |
||||
case OMX_COLOR_FormatYUV444Interleaved: return "OMX_COLOR_FormatYUV444Interleaved"; |
||||
case OMX_COLOR_FormatRawBayer8bit: return "OMX_COLOR_FormatRawBayer8bit"; |
||||
case OMX_COLOR_FormatRawBayer10bit: return "OMX_COLOR_FormatRawBayer10bit"; |
||||
case OMX_COLOR_FormatRawBayer8bitcompressed: return "OMX_COLOR_FormatRawBayer8bitcompressed"; |
||||
case OMX_COLOR_FormatL2: return "OMX_COLOR_FormatL2"; |
||||
case OMX_COLOR_FormatL4: return "OMX_COLOR_FormatL4"; |
||||
case OMX_COLOR_FormatL8: return "OMX_COLOR_FormatL8"; |
||||
case OMX_COLOR_FormatL16: return "OMX_COLOR_FormatL16"; |
||||
case OMX_COLOR_FormatL24: return "OMX_COLOR_FormatL24"; |
||||
case OMX_COLOR_FormatL32: return "OMX_COLOR_FormatL32"; |
||||
case OMX_COLOR_FormatYUV420PackedSemiPlanar: return "OMX_COLOR_FormatYUV420PackedSemiPlanar"; |
||||
case OMX_COLOR_FormatYUV422PackedSemiPlanar: return "OMX_COLOR_FormatYUV422PackedSemiPlanar"; |
||||
case OMX_COLOR_Format18BitBGR666: return "OMX_COLOR_Format18BitBGR666"; |
||||
case OMX_COLOR_Format24BitARGB6666: return "OMX_COLOR_Format24BitARGB6666"; |
||||
case OMX_COLOR_Format24BitABGR6666: return "OMX_COLOR_Format24BitABGR6666"; |
||||
|
||||
case OMX_COLOR_FormatAndroidOpaque: return "OMX_COLOR_FormatAndroidOpaque"; |
||||
case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar"; |
||||
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar"; |
||||
case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka: return "OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka"; |
||||
case OMX_SEC_COLOR_FormatNV12Tiled: return "OMX_SEC_COLOR_FormatNV12Tiled"; |
||||
case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m: return "OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m"; |
||||
|
||||
// case QOMX_COLOR_FormatYVU420SemiPlanar: return "QOMX_COLOR_FormatYVU420SemiPlanar";
|
||||
case QOMX_COLOR_FormatYVU420PackedSemiPlanar32m4ka: return "QOMX_COLOR_FormatYVU420PackedSemiPlanar32m4ka"; |
||||
case QOMX_COLOR_FormatYUV420PackedSemiPlanar16m2ka: return "QOMX_COLOR_FormatYUV420PackedSemiPlanar16m2ka"; |
||||
// case QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka: return "QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka";
|
||||
// case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m: return "QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m";
|
||||
case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mMultiView: return "QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mMultiView"; |
||||
case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mCompressed: return "QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mCompressed"; |
||||
case QOMX_COLOR_Format32bitRGBA8888: return "QOMX_COLOR_Format32bitRGBA8888"; |
||||
case QOMX_COLOR_Format32bitRGBA8888Compressed: return "QOMX_COLOR_Format32bitRGBA8888Compressed"; |
||||
|
||||
default: |
||||
return "unkn"; |
||||
} |
||||
} |
||||
|
||||
void encoder_init(EncoderState *s, const char* filename, int width, int height, int fps, int bitrate, bool h265, bool downscale) { |
||||
int err; |
||||
|
||||
memset(s, 0, sizeof(*s)); |
||||
s->filename = filename; |
||||
s->width = width; |
||||
s->height = height; |
||||
s->fps = fps; |
||||
mutex_init_reentrant(&s->lock); |
||||
|
||||
if (!h265) { |
||||
s->remuxing = true; |
||||
} |
||||
|
||||
if (downscale) { |
||||
s->downscale = true; |
||||
s->y_ptr2 = malloc(s->width*s->height); |
||||
s->u_ptr2 = malloc(s->width*s->height/4); |
||||
s->v_ptr2 = malloc(s->width*s->height/4); |
||||
} |
||||
|
||||
s->segment = -1; |
||||
|
||||
s->state = OMX_StateLoaded; |
||||
|
||||
s->codec_config = NULL; |
||||
|
||||
queue_init(&s->free_in); |
||||
queue_init(&s->done_out); |
||||
|
||||
pthread_mutex_init(&s->state_lock, NULL); |
||||
pthread_cond_init(&s->state_cv, NULL); |
||||
|
||||
if (h265) { |
||||
err = OMX_GetHandle(&s->handle, (OMX_STRING)"OMX.qcom.video.encoder.hevc", |
||||
s, &omx_callbacks); |
||||
} else { |
||||
err = OMX_GetHandle(&s->handle, (OMX_STRING)"OMX.qcom.video.encoder.avc", |
||||
s, &omx_callbacks); |
||||
} |
||||
assert(err == OMX_ErrorNone); |
||||
// printf("handle: %p\n", s->handle);
|
||||
|
||||
// setup input port
|
||||
|
||||
OMX_PARAM_PORTDEFINITIONTYPE in_port = {0}; |
||||
in_port.nSize = sizeof(in_port); |
||||
in_port.nPortIndex = (OMX_U32) PORT_INDEX_IN; |
||||
err = OMX_GetParameter(s->handle, OMX_IndexParamPortDefinition, |
||||
(OMX_PTR) &in_port); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
in_port.format.video.nFrameWidth = s->width; |
||||
in_port.format.video.nFrameHeight = s->height; |
||||
in_port.format.video.nStride = VENUS_Y_STRIDE(COLOR_FMT_NV12, s->width); |
||||
in_port.format.video.nSliceHeight = s->height; |
||||
// in_port.nBufferSize = (s->width * s->height * 3) / 2;
|
||||
in_port.nBufferSize = VENUS_BUFFER_SIZE(COLOR_FMT_NV12, s->width, s->height); |
||||
in_port.format.video.xFramerate = (s->fps * 65536); |
||||
in_port.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; |
||||
// in_port.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
|
||||
in_port.format.video.eColorFormat = QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m; |
||||
|
||||
err = OMX_SetParameter(s->handle, OMX_IndexParamPortDefinition, |
||||
(OMX_PTR) &in_port); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
|
||||
err = OMX_GetParameter(s->handle, OMX_IndexParamPortDefinition, |
||||
(OMX_PTR) &in_port); |
||||
assert(err == OMX_ErrorNone); |
||||
s->num_in_bufs = in_port.nBufferCountActual; |
||||
|
||||
// printf("in width: %d, stride: %d\n",
|
||||
// in_port.format.video.nFrameWidth, in_port.format.video.nStride);
|
||||
|
||||
// setup output port
|
||||
|
||||
OMX_PARAM_PORTDEFINITIONTYPE out_port = {0}; |
||||
out_port.nSize = sizeof(out_port); |
||||
out_port.nPortIndex = (OMX_U32) PORT_INDEX_OUT; |
||||
err = OMX_GetParameter(s->handle, OMX_IndexParamPortDefinition, |
||||
(OMX_PTR)&out_port); |
||||
assert(err == OMX_ErrorNone); |
||||
out_port.format.video.nFrameWidth = s->width; |
||||
out_port.format.video.nFrameHeight = s->height; |
||||
out_port.format.video.xFramerate = 0; |
||||
out_port.format.video.nBitrate = bitrate; |
||||
if (h265) { |
||||
out_port.format.video.eCompressionFormat = OMX_VIDEO_CodingHEVC; |
||||
} else { |
||||
out_port.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; |
||||
} |
||||
out_port.format.video.eColorFormat = OMX_COLOR_FormatUnused; |
||||
|
||||
err = OMX_SetParameter(s->handle, OMX_IndexParamPortDefinition, |
||||
(OMX_PTR) &out_port); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
err = OMX_GetParameter(s->handle, OMX_IndexParamPortDefinition, |
||||
(OMX_PTR) &out_port); |
||||
assert(err == OMX_ErrorNone); |
||||
s->num_out_bufs = out_port.nBufferCountActual; |
||||
|
||||
OMX_VIDEO_PARAM_BITRATETYPE bitrate_type = {0}; |
||||
bitrate_type.nSize = sizeof(bitrate_type); |
||||
bitrate_type.nPortIndex = (OMX_U32) PORT_INDEX_OUT; |
||||
err = OMX_GetParameter(s->handle, OMX_IndexParamVideoBitrate, |
||||
(OMX_PTR) &bitrate_type); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
bitrate_type.eControlRate = OMX_Video_ControlRateVariable; |
||||
bitrate_type.nTargetBitrate = bitrate; |
||||
|
||||
err = OMX_SetParameter(s->handle, OMX_IndexParamVideoBitrate, |
||||
(OMX_PTR) &bitrate_type); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
if (h265) { |
||||
// setup HEVC
|
||||
OMX_VIDEO_PARAM_HEVCTYPE hecv_type = {0}; |
||||
hecv_type.nSize = sizeof(hecv_type); |
||||
hecv_type.nPortIndex = (OMX_U32) PORT_INDEX_OUT; |
||||
err = OMX_GetParameter(s->handle, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, |
||||
(OMX_PTR) &hecv_type); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
hecv_type.eProfile = OMX_VIDEO_HEVCProfileMain; |
||||
hecv_type.eLevel = OMX_VIDEO_HEVCHighTierLevel5; |
||||
|
||||
err = OMX_SetParameter(s->handle, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, |
||||
(OMX_PTR) &hecv_type); |
||||
assert(err == OMX_ErrorNone); |
||||
} else { |
||||
// setup h264
|
||||
OMX_VIDEO_PARAM_AVCTYPE avc = { 0 }; |
||||
avc.nSize = sizeof(avc); |
||||
avc.nPortIndex = (OMX_U32) PORT_INDEX_OUT; |
||||
err = OMX_GetParameter(s->handle, OMX_IndexParamVideoAvc, &avc); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
avc.nBFrames = 0; |
||||
avc.nPFrames = 15; |
||||
|
||||
avc.eProfile = OMX_VIDEO_AVCProfileBaseline; |
||||
avc.eLevel = OMX_VIDEO_AVCLevel31; |
||||
|
||||
avc.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB; |
||||
avc.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable; |
||||
|
||||
err = OMX_SetParameter(s->handle, OMX_IndexParamVideoAvc, &avc); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
|
||||
|
||||
// for (int i = 0; ; i++) {
|
||||
// OMX_VIDEO_PARAM_PORTFORMATTYPE video_port_format = {0};
|
||||
// video_port_format.nSize = sizeof(video_port_format);
|
||||
// video_port_format.nIndex = i;
|
||||
// video_port_format.nPortIndex = PORT_INDEX_IN;
|
||||
// if (OMX_GetParameter(s->handle, OMX_IndexParamVideoPortFormat, &video_port_format) != OMX_ErrorNone)
|
||||
// break;
|
||||
// printf("in %d: compression 0x%x format 0x%x %s\n", i,
|
||||
// video_port_format.eCompressionFormat, video_port_format.eColorFormat,
|
||||
// omx_color_fomat_name(video_port_format.eColorFormat));
|
||||
// }
|
||||
|
||||
// for (int i=0; i<32; i++) {
|
||||
// OMX_VIDEO_PARAM_PROFILELEVELTYPE params = {0};
|
||||
// params.nSize = sizeof(params);
|
||||
// params.nPortIndex = PORT_INDEX_OUT;
|
||||
// params.nProfileIndex = i;
|
||||
// if (OMX_GetParameter(s->handle, OMX_IndexParamVideoProfileLevelQuerySupported, ¶ms) != OMX_ErrorNone)
|
||||
// break;
|
||||
// printf("profile %d level 0x%x\n", params.eProfile, params.eLevel);
|
||||
// }
|
||||
|
||||
err = OMX_SendCommand(s->handle, OMX_CommandStateSet, OMX_StateIdle, NULL); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
s->in_buf_headers = calloc(s->num_in_bufs, sizeof(OMX_BUFFERHEADERTYPE*)); |
||||
for (int i=0; i<s->num_in_bufs; i++) { |
||||
err = OMX_AllocateBuffer(s->handle, &s->in_buf_headers[i], PORT_INDEX_IN, s, |
||||
in_port.nBufferSize); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
|
||||
s->out_buf_headers = calloc(s->num_out_bufs, sizeof(OMX_BUFFERHEADERTYPE*)); |
||||
for (int i=0; i<s->num_out_bufs; i++) { |
||||
err = OMX_AllocateBuffer(s->handle, &s->out_buf_headers[i], PORT_INDEX_OUT, s, |
||||
out_port.nBufferSize); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
|
||||
wait_for_state(s, OMX_StateIdle); |
||||
|
||||
err = OMX_SendCommand(s->handle, OMX_CommandStateSet, OMX_StateExecuting, NULL); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
wait_for_state(s, OMX_StateExecuting); |
||||
|
||||
// give omx all the output buffers
|
||||
for (int i = 0; i < s->num_out_bufs; i++) { |
||||
// printf("fill %p\n", s->out_buf_headers[i]);
|
||||
err = OMX_FillThisBuffer(s->handle, s->out_buf_headers[i]); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
|
||||
// fill the input free queue
|
||||
for (int i = 0; i < s->num_in_bufs; i++) { |
||||
queue_push(&s->free_in, (void*)s->in_buf_headers[i]); |
||||
} |
||||
} |
||||
|
||||
static void handle_out_buf(EncoderState *s, OMX_BUFFERHEADERTYPE *out_buf) { |
||||
int err; |
||||
uint8_t *buf_data = out_buf->pBuffer + out_buf->nOffset; |
||||
|
||||
if (out_buf->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { |
||||
if (s->codec_config_len < out_buf->nFilledLen) { |
||||
s->codec_config = realloc(s->codec_config, out_buf->nFilledLen); |
||||
} |
||||
s->codec_config_len = out_buf->nFilledLen; |
||||
memcpy(s->codec_config, buf_data, out_buf->nFilledLen); |
||||
} |
||||
|
||||
if (s->stream_sock_raw) { |
||||
uint64_t current_time = nanos_since_boot(); |
||||
uint64_t diff = current_time - out_buf->nTimeStamp*1000LL; |
||||
double msdiff = (double) diff / 1000000.0; |
||||
// printf("encoded latency to tsEof: %f\n", msdiff);
|
||||
zmq_send(s->stream_sock_raw, &out_buf->nTimeStamp, sizeof(out_buf->nTimeStamp), ZMQ_SNDMORE); |
||||
zmq_send(s->stream_sock_raw, buf_data, out_buf->nFilledLen, 0); |
||||
} |
||||
|
||||
if (s->of) { |
||||
//printf("write %d flags 0x%x\n", out_buf->nFilledLen, out_buf->nFlags);
|
||||
fwrite(buf_data, out_buf->nFilledLen, 1, s->of); |
||||
} |
||||
|
||||
if (s->remuxing) { |
||||
if (!s->wrote_codec_config && s->codec_config_len > 0) { |
||||
if (s->codec_ctx->extradata_size < s->codec_config_len) { |
||||
s->codec_ctx->extradata = realloc(s->codec_ctx->extradata, s->codec_config_len + AV_INPUT_BUFFER_PADDING_SIZE); |
||||
} |
||||
s->codec_ctx->extradata_size = s->codec_config_len; |
||||
memcpy(s->codec_ctx->extradata, s->codec_config, s->codec_config_len); |
||||
memset(s->codec_ctx->extradata + s->codec_ctx->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); |
||||
|
||||
err = avcodec_parameters_from_context(s->out_stream->codecpar, s->codec_ctx); |
||||
assert(err >= 0); |
||||
err = avformat_write_header(s->ofmt_ctx, NULL); |
||||
assert(err >= 0); |
||||
|
||||
s->wrote_codec_config = true; |
||||
} |
||||
|
||||
if (out_buf->nTimeStamp > 0) { |
||||
// input timestamps are in microseconds
|
||||
AVRational in_timebase = {1, 1000000}; |
||||
|
||||
AVPacket pkt; |
||||
av_init_packet(&pkt); |
||||
pkt.data = buf_data; |
||||
pkt.size = out_buf->nFilledLen; |
||||
pkt.pts = pkt.dts = av_rescale_q_rnd(out_buf->nTimeStamp, in_timebase, s->ofmt_ctx->streams[0]->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); |
||||
pkt.duration = av_rescale_q(50*1000, in_timebase, s->ofmt_ctx->streams[0]->time_base); |
||||
|
||||
if (out_buf->nFlags & OMX_BUFFERFLAG_SYNCFRAME) { |
||||
pkt.flags |= AV_PKT_FLAG_KEY; |
||||
} |
||||
|
||||
err = av_write_frame(s->ofmt_ctx, &pkt); |
||||
if (err < 0) { LOGW("ts encoder write issue"); } |
||||
|
||||
av_free_packet(&pkt); |
||||
} |
||||
} |
||||
|
||||
// give omx back the buffer
|
||||
err = OMX_FillThisBuffer(s->handle, out_buf); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
|
||||
int encoder_encode_frame(EncoderState *s, |
||||
const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, |
||||
int in_width, int in_height, |
||||
int *frame_segment, VIPCBufExtra *extra) { |
||||
int err; |
||||
pthread_mutex_lock(&s->lock); |
||||
|
||||
if (s->opening) { |
||||
encoder_open(s, s->next_path); |
||||
s->opening = false; |
||||
} |
||||
|
||||
if (!s->open) { |
||||
pthread_mutex_unlock(&s->lock); |
||||
return -1; |
||||
} |
||||
|
||||
// this sometimes freezes... put it outside the encoder lock so we can still trigger rotates...
|
||||
// THIS IS A REALLY BAD IDEA, but apparently the race has to happen 30 times to trigger this
|
||||
pthread_mutex_unlock(&s->lock); |
||||
OMX_BUFFERHEADERTYPE* in_buf = queue_pop(&s->free_in); |
||||
pthread_mutex_lock(&s->lock); |
||||
|
||||
if (s->rotating) { |
||||
encoder_close(s); |
||||
encoder_open(s, s->next_path); |
||||
s->segment = s->next_segment; |
||||
s->rotating = false; |
||||
} |
||||
|
||||
int ret = s->counter; |
||||
|
||||
uint8_t *in_buf_ptr = in_buf->pBuffer; |
||||
// printf("in_buf ptr %p\n", in_buf_ptr);
|
||||
|
||||
uint8_t *in_y_ptr = in_buf_ptr; |
||||
int in_y_stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, s->width); |
||||
int in_uv_stride = VENUS_UV_STRIDE(COLOR_FMT_NV12, s->width); |
||||
// uint8_t *in_uv_ptr = in_buf_ptr + (s->width * s->height);
|
||||
uint8_t *in_uv_ptr = in_buf_ptr + (in_y_stride * VENUS_Y_SCANLINES(COLOR_FMT_NV12, s->height)); |
||||
|
||||
if (s->downscale) { |
||||
I420Scale(y_ptr, in_width, |
||||
u_ptr, in_width/2, |
||||
v_ptr, in_width/2, |
||||
in_width, in_height, |
||||
s->y_ptr2, s->width, |
||||
s->u_ptr2, s->width/2, |
||||
s->v_ptr2, s->width/2, |
||||
s->width, s->height, |
||||
kFilterNone); |
||||
y_ptr = s->y_ptr2; |
||||
u_ptr = s->u_ptr2; |
||||
v_ptr = s->v_ptr2; |
||||
} |
||||
err = I420ToNV12(y_ptr, s->width, |
||||
u_ptr, s->width/2, |
||||
v_ptr, s->width/2, |
||||
in_y_ptr, in_y_stride, |
||||
in_uv_ptr, in_uv_stride, |
||||
s->width, s->height); |
||||
assert(err == 0); |
||||
|
||||
// in_buf->nFilledLen = (s->width*s->height) + (s->width*s->height/2);
|
||||
in_buf->nFilledLen = VENUS_BUFFER_SIZE(COLOR_FMT_NV12, s->width, s->height); |
||||
in_buf->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; |
||||
in_buf->nOffset = 0; |
||||
in_buf->nTimeStamp = extra->timestamp_eof/1000LL; // OMX_TICKS, in microseconds
|
||||
|
||||
err = OMX_EmptyThisBuffer(s->handle, in_buf); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
// pump output
|
||||
while (true) { |
||||
OMX_BUFFERHEADERTYPE *out_buf = queue_try_pop(&s->done_out); |
||||
if (!out_buf) { |
||||
break; |
||||
} |
||||
handle_out_buf(s, out_buf); |
||||
} |
||||
|
||||
s->dirty = true; |
||||
|
||||
s->counter++; |
||||
|
||||
if (frame_segment) { |
||||
*frame_segment = s->segment; |
||||
} |
||||
|
||||
if (s->closing) { |
||||
encoder_close(s); |
||||
s->closing = false; |
||||
} |
||||
|
||||
pthread_mutex_unlock(&s->lock); |
||||
return ret; |
||||
} |
||||
|
||||
void encoder_open(EncoderState *s, const char* path) { |
||||
int err; |
||||
|
||||
pthread_mutex_lock(&s->lock); |
||||
|
||||
snprintf(s->vid_path, sizeof(s->vid_path), "%s/%s", path, s->filename); |
||||
|
||||
if (s->remuxing) { |
||||
avformat_alloc_output_context2(&s->ofmt_ctx, NULL, NULL, s->vid_path); |
||||
assert(s->ofmt_ctx); |
||||
|
||||
s->out_stream = avformat_new_stream(s->ofmt_ctx, NULL); |
||||
assert(s->out_stream); |
||||
|
||||
// set codec correctly
|
||||
av_register_all(); |
||||
|
||||
AVCodec *codec = NULL; |
||||
codec = avcodec_find_encoder(AV_CODEC_ID_H264); |
||||
assert(codec); |
||||
|
||||
s->codec_ctx = avcodec_alloc_context3(codec); |
||||
assert(s->codec_ctx); |
||||
s->codec_ctx->width = s->width; |
||||
s->codec_ctx->height = s->height; |
||||
s->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; |
||||
s->codec_ctx->time_base = (AVRational){ 1, s->fps }; |
||||
|
||||
err = avio_open(&s->ofmt_ctx->pb, s->vid_path, AVIO_FLAG_WRITE); |
||||
assert(err >= 0); |
||||
|
||||
s->wrote_codec_config = false; |
||||
} else { |
||||
s->of = fopen(s->vid_path, "wb"); |
||||
assert(s->of); |
||||
if (s->codec_config_len > 0) { |
||||
fwrite(s->codec_config, s->codec_config_len, 1, s->of); |
||||
} |
||||
} |
||||
|
||||
// create camera lock file
|
||||
snprintf(s->lock_path, sizeof(s->lock_path), "%s/%s.lock", path, s->filename); |
||||
int lock_fd = open(s->lock_path, O_RDWR | O_CREAT, 0777); |
||||
assert(lock_fd >= 0); |
||||
close(lock_fd); |
||||
|
||||
s->open = true; |
||||
s->counter = 0; |
||||
|
||||
pthread_mutex_unlock(&s->lock); |
||||
} |
||||
|
||||
void encoder_close(EncoderState *s) { |
||||
int err; |
||||
|
||||
pthread_mutex_lock(&s->lock); |
||||
|
||||
if (s->open) { |
||||
if (s->dirty) { |
||||
// drain output only if there could be frames in the encoder
|
||||
|
||||
OMX_BUFFERHEADERTYPE* in_buf = queue_pop(&s->free_in); |
||||
in_buf->nFilledLen = 0; |
||||
in_buf->nOffset = 0; |
||||
in_buf->nFlags = OMX_BUFFERFLAG_EOS; |
||||
in_buf->nTimeStamp = 0; |
||||
|
||||
err = OMX_EmptyThisBuffer(s->handle, in_buf); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
while (true) { |
||||
OMX_BUFFERHEADERTYPE *out_buf = queue_pop(&s->done_out); |
||||
|
||||
handle_out_buf(s, out_buf); |
||||
|
||||
if (out_buf->nFlags & OMX_BUFFERFLAG_EOS) { |
||||
break; |
||||
} |
||||
} |
||||
s->dirty = false; |
||||
} |
||||
|
||||
if (s->remuxing) { |
||||
av_write_trailer(s->ofmt_ctx); |
||||
avio_closep(&s->ofmt_ctx->pb); |
||||
avformat_free_context(s->ofmt_ctx); |
||||
} else { |
||||
fclose(s->of); |
||||
} |
||||
unlink(s->lock_path); |
||||
} |
||||
s->open = false; |
||||
|
||||
pthread_mutex_unlock(&s->lock); |
||||
} |
||||
|
||||
void encoder_rotate(EncoderState *s, const char* new_path, int new_segment) { |
||||
pthread_mutex_lock(&s->lock); |
||||
snprintf(s->next_path, sizeof(s->next_path), "%s", new_path); |
||||
s->next_segment = new_segment; |
||||
if (s->open) { |
||||
if (s->next_segment == -1) { |
||||
s->closing = true; |
||||
} else { |
||||
s->rotating = true; |
||||
} |
||||
} else { |
||||
s->segment = s->next_segment; |
||||
s->opening = true; |
||||
} |
||||
pthread_mutex_unlock(&s->lock); |
||||
} |
||||
|
||||
void encoder_destroy(EncoderState *s) { |
||||
int err; |
||||
|
||||
assert(!s->open); |
||||
|
||||
err = OMX_SendCommand(s->handle, OMX_CommandStateSet, OMX_StateIdle, NULL); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
wait_for_state(s, OMX_StateIdle); |
||||
|
||||
err = OMX_SendCommand(s->handle, OMX_CommandStateSet, OMX_StateLoaded, NULL); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
for (int i=0; i<s->num_in_bufs; i++) { |
||||
err = OMX_FreeBuffer(s->handle, PORT_INDEX_IN, s->in_buf_headers[i]); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
free(s->in_buf_headers); |
||||
|
||||
for (int i=0; i<s->num_out_bufs; i++) { |
||||
err = OMX_FreeBuffer(s->handle, PORT_INDEX_OUT, s->out_buf_headers[i]); |
||||
assert(err == OMX_ErrorNone); |
||||
} |
||||
free(s->out_buf_headers); |
||||
|
||||
wait_for_state(s, OMX_StateLoaded); |
||||
|
||||
err = OMX_FreeHandle(s->handle); |
||||
assert(err == OMX_ErrorNone); |
||||
|
||||
if (s->downscale) { |
||||
free(s->y_ptr2); |
||||
free(s->u_ptr2); |
||||
free(s->v_ptr2); |
||||
} |
||||
} |
||||
|
||||
#if 0 |
||||
|
||||
// cd one/selfdrive/visiond
|
||||
// clang
|
||||
// -fPIC -pie
|
||||
// -std=gnu11 -O2 -g
|
||||
// -o encoder
|
||||
// -I ~/one/selfdrive
|
||||
// -I ~/one/phonelibs/openmax/include
|
||||
// -I ~/one/phonelibs/libyuv/include
|
||||
// -lOmxVenc -lOmxCore -llog
|
||||
// encoder.c
|
||||
// buffering.c
|
||||
// -L ~/one/phonelibs/libyuv/lib -l:libyuv.a
|
||||
|
||||
int main() { |
||||
int err; |
||||
|
||||
EncoderState state; |
||||
EncoderState *s = &state; |
||||
memset(s, 0, sizeof(*s)); |
||||
|
||||
int w = 1164; |
||||
int h = 874; |
||||
|
||||
encoder_init(s, w, h, 20); |
||||
printf("inited\n"); |
||||
|
||||
encoder_open(s, "/sdcard/t1"); |
||||
|
||||
// uint8_t *tmpy = malloc(640*480);
|
||||
// uint8_t *tmpu = malloc((640/2)*(480/2));
|
||||
// uint8_t *tmpv = malloc((640/2)*(480/2));
|
||||
|
||||
// memset(tmpy, 0, 640*480);
|
||||
// // memset(tmpu, 0xff, (640/2)*(480/2));
|
||||
// memset(tmpv, 0, (640/2)*(480/2));
|
||||
|
||||
// #if 0
|
||||
// FILE *infile = fopen("/sdcard/camera_t2.yuv", "rb");
|
||||
uint8_t *inbuf = malloc(w*h*3/2); |
||||
memset(inbuf, 0, w*h*3/2); |
||||
|
||||
for (int i=0; i<20*3+5; i++) { |
||||
|
||||
// fread(inbuf, w*h*3/2, 1, infile);
|
||||
|
||||
uint8_t *tmpy = inbuf; |
||||
uint8_t *tmpu = inbuf + w*h; |
||||
uint8_t *tmpv = inbuf + w*h + (w/2)*(h/2); |
||||
|
||||
for (int y=0; y<h/2; y++) { |
||||
for (int x=0; x<w/2; x++) { |
||||
tmpu[y * (w/2) + x] = (i ^ y ^ x); |
||||
} |
||||
} |
||||
|
||||
encoder_encode_frame(s, 20000*i, tmpy, tmpu, tmpv); |
||||
} |
||||
// #endif
|
||||
|
||||
// while(1);
|
||||
|
||||
printf("done\n"); |
||||
|
||||
// encoder_close(s);
|
||||
|
||||
// printf("restart\n");
|
||||
// fclose(s->of);
|
||||
// s->of = fopen("/sdcard/tmpout2.hevc", "wb");
|
||||
// if (s->codec_config) {
|
||||
// fwrite(s->codec_config, s->codec_config_len, 1, s->of);
|
||||
// }
|
||||
// encoder_open(s, "/sdcard/t1");
|
||||
|
||||
encoder_rotate(s, "/sdcard/t2"); |
||||
|
||||
for (int i=0; i<20*3+5; i++) { |
||||
|
||||
// fread(inbuf, w*h*3/2, 1, infile);
|
||||
|
||||
uint8_t *tmpy = inbuf; |
||||
uint8_t *tmpu = inbuf + w*h; |
||||
uint8_t *tmpv = inbuf + w*h + (w/2)*(h/2); |
||||
|
||||
for (int y=0; y<h/2; y++) { |
||||
for (int x=0; x<w/2; x++) { |
||||
tmpu[y * (w/2) + x] = (i ^ y ^ x); |
||||
} |
||||
} |
||||
|
||||
encoder_encode_frame(s, 20000*i, tmpy, tmpu, tmpv); |
||||
} |
||||
encoder_close(s); |
||||
|
||||
return 0; |
||||
} |
||||
#endif |
@ -0,0 +1,86 @@ |
||||
#ifndef ENCODER_H |
||||
#define ENCODER_H |
||||
|
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include <pthread.h> |
||||
|
||||
#include <OMX_Component.h> |
||||
#include <libavformat/avformat.h> |
||||
|
||||
#include "common/cqueue.h" |
||||
#include "common/visionipc.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct EncoderState { |
||||
pthread_mutex_t lock; |
||||
int width, height, fps; |
||||
const char* path; |
||||
char vid_path[1024]; |
||||
char lock_path[1024]; |
||||
bool open; |
||||
bool dirty; |
||||
int counter; |
||||
int segment; |
||||
|
||||
bool rotating; |
||||
bool closing; |
||||
bool opening; |
||||
char next_path[1024]; |
||||
int next_segment; |
||||
|
||||
const char* filename; |
||||
FILE *of; |
||||
|
||||
size_t codec_config_len; |
||||
uint8_t *codec_config; |
||||
bool wrote_codec_config; |
||||
|
||||
pthread_mutex_t state_lock; |
||||
pthread_cond_t state_cv; |
||||
OMX_STATETYPE state; |
||||
|
||||
OMX_HANDLETYPE handle; |
||||
|
||||
int num_in_bufs; |
||||
OMX_BUFFERHEADERTYPE** in_buf_headers; |
||||
|
||||
int num_out_bufs; |
||||
OMX_BUFFERHEADERTYPE** out_buf_headers; |
||||
|
||||
Queue free_in; |
||||
Queue done_out; |
||||
|
||||
void *stream_sock_raw; |
||||
|
||||
AVFormatContext *ofmt_ctx; |
||||
AVCodecContext *codec_ctx; |
||||
AVStream *out_stream; |
||||
bool remuxing; |
||||
|
||||
void *zmq_ctx; |
||||
|
||||
bool downscale; |
||||
uint8_t *y_ptr2, *u_ptr2, *v_ptr2; |
||||
} EncoderState; |
||||
|
||||
void encoder_init(EncoderState *s, const char* filename, int width, int height, int fps, int bitrate, bool h265, bool downscale); |
||||
int encoder_encode_frame(EncoderState *s, |
||||
const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, |
||||
int in_width, int in_height, |
||||
int *frame_segment, VIPCBufExtra *extra); |
||||
void encoder_open(EncoderState *s, const char* path); |
||||
void encoder_rotate(EncoderState *s, const char* new_path, int new_segment); |
||||
void encoder_close(EncoderState *s); |
||||
void encoder_destroy(EncoderState *s); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,19 @@ |
||||
#!/usr/bin/env python3 |
||||
import zmq |
||||
import cereal.messaging as messaging |
||||
from cereal.services import service_list |
||||
import pcap |
||||
|
||||
def main(gctx=None): |
||||
ethernetData = messaging.pub_sock('ethernetData') |
||||
|
||||
for ts, pkt in pcap.pcap('eth0'): |
||||
dat = messaging.new_message() |
||||
dat.init('ethernetData', 1) |
||||
dat.ethernetData[0].ts = ts |
||||
dat.ethernetData[0].pkt = str(pkt) |
||||
ethernetData.send(dat.to_bytes()) |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
|
@ -0,0 +1,81 @@ |
||||
#ifndef FRAMELOGGER_H |
||||
#define FRAMELOGGER_H |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include <string> |
||||
#include <mutex> |
||||
|
||||
class FrameLogger { |
||||
public: |
||||
virtual ~FrameLogger() {} |
||||
|
||||
virtual void Open(const std::string &path) = 0; |
||||
virtual void Close() = 0; |
||||
|
||||
int LogFrame(uint64_t ts, const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, int *frame_segment) { |
||||
std::lock_guard<std::recursive_mutex> guard(lock); |
||||
|
||||
if (opening) { |
||||
Open(next_path); |
||||
opening = false; |
||||
} |
||||
|
||||
if (!is_open) return -1; |
||||
|
||||
if (rotating) { |
||||
Close(); |
||||
Open(next_path); |
||||
segment = next_segment; |
||||
rotating = false; |
||||
} |
||||
|
||||
int ret = ProcessFrame(ts, y_ptr, u_ptr, v_ptr); |
||||
|
||||
if (ret >= 0 && frame_segment) { |
||||
*frame_segment = segment; |
||||
} |
||||
|
||||
if (closing) { |
||||
Close(); |
||||
closing = false; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void Rotate(const std::string &new_path, int new_segment) { |
||||
std::lock_guard<std::recursive_mutex> guard(lock); |
||||
|
||||
next_path = new_path; |
||||
next_segment = new_segment; |
||||
if (is_open) { |
||||
if (next_segment == -1) { |
||||
closing = true; |
||||
} else { |
||||
rotating = true; |
||||
} |
||||
} else { |
||||
segment = next_segment; |
||||
opening = true; |
||||
} |
||||
} |
||||
|
||||
protected: |
||||
|
||||
virtual int ProcessFrame(uint64_t ts, const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr) = 0; |
||||
|
||||
std::recursive_mutex lock; |
||||
|
||||
bool is_open = false; |
||||
int segment = -1; |
||||
|
||||
std::string vid_path, lock_path; |
||||
|
||||
private: |
||||
int next_segment = -1; |
||||
bool opening = false, closing = false, rotating = false; |
||||
std::string next_path; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,819 @@ |
||||
#ifndef __MEDIA_INFO_H__ |
||||
#define __MEDIA_INFO_H__ |
||||
|
||||
#ifndef MSM_MEDIA_ALIGN |
||||
#define MSM_MEDIA_ALIGN(__sz, __align) (((__sz) + (__align-1)) & (~(__align-1))) |
||||
#endif |
||||
|
||||
#ifndef MSM_MEDIA_ROUNDUP |
||||
#define MSM_MEDIA_ROUNDUP(__sz, __r) (((__sz) + ((__r) - 1)) / (__r)) |
||||
#endif |
||||
|
||||
#ifndef MSM_MEDIA_MAX |
||||
#define MSM_MEDIA_MAX(__a, __b) ((__a) > (__b)?(__a):(__b)) |
||||
#endif |
||||
|
||||
enum color_fmts { |
||||
/* Venus NV12:
|
||||
* YUV 4:2:0 image with a plane of 8 bit Y samples followed |
||||
* by an interleaved U/V plane containing 8 bit 2x2 subsampled |
||||
* colour difference samples. |
||||
* |
||||
* <-------- Y/UV_Stride --------> |
||||
* <------- Width -------> |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* U V U V U V U V U V U V . . . . ^ |
||||
* U V U V U V U V U V U V . . . . | |
||||
* U V U V U V U V U V U V . . . . | |
||||
* U V U V U V U V U V U V . . . . UV_Scanlines |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* . . . . . . . . . . . . . . . . --> Buffer size alignment |
||||
* |
||||
* Y_Stride : Width aligned to 128 |
||||
* UV_Stride : Width aligned to 128 |
||||
* Y_Scanlines: Height aligned to 32 |
||||
* UV_Scanlines: Height/2 aligned to 16 |
||||
* Extradata: Arbitrary (software-imposed) padding |
||||
* Total size = align((Y_Stride * Y_Scanlines |
||||
* + UV_Stride * UV_Scanlines |
||||
* + max(Extradata, Y_Stride * 8), 4096) |
||||
*/ |
||||
COLOR_FMT_NV12, |
||||
|
||||
/* Venus NV21:
|
||||
* YUV 4:2:0 image with a plane of 8 bit Y samples followed |
||||
* by an interleaved V/U plane containing 8 bit 2x2 subsampled |
||||
* colour difference samples. |
||||
* |
||||
* <-------- Y/UV_Stride --------> |
||||
* <------- Width -------> |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* V U V U V U V U V U V U . . . . ^ |
||||
* V U V U V U V U V U V U . . . . | |
||||
* V U V U V U V U V U V U . . . . | |
||||
* V U V U V U V U V U V U . . . . UV_Scanlines |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* . . . . . . . . . . . . . . . . --> Padding & Buffer size alignment |
||||
* |
||||
* Y_Stride : Width aligned to 128 |
||||
* UV_Stride : Width aligned to 128 |
||||
* Y_Scanlines: Height aligned to 32 |
||||
* UV_Scanlines: Height/2 aligned to 16 |
||||
* Extradata: Arbitrary (software-imposed) padding |
||||
* Total size = align((Y_Stride * Y_Scanlines |
||||
* + UV_Stride * UV_Scanlines |
||||
* + max(Extradata, Y_Stride * 8), 4096) |
||||
*/ |
||||
COLOR_FMT_NV21, |
||||
/* Venus NV12_MVTB:
|
||||
* Two YUV 4:2:0 images/views one after the other |
||||
* in a top-bottom layout, same as NV12 |
||||
* with a plane of 8 bit Y samples followed |
||||
* by an interleaved U/V plane containing 8 bit 2x2 subsampled |
||||
* colour difference samples. |
||||
* |
||||
* |
||||
* <-------- Y/UV_Stride --------> |
||||
* <------- Width -------> |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ ^ |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | | |
||||
* . . . . . . . . . . . . . . . . | View_1 |
||||
* . . . . . . . . . . . . . . . . | | |
||||
* . . . . . . . . . . . . . . . . | | |
||||
* . . . . . . . . . . . . . . . . V | |
||||
* U V U V U V U V U V U V . . . . ^ | |
||||
* U V U V U V U V U V U V . . . . | | |
||||
* U V U V U V U V U V U V . . . . | | |
||||
* U V U V U V U V U V U V . . . . UV_Scanlines | |
||||
* . . . . . . . . . . . . . . . . | | |
||||
* . . . . . . . . . . . . . . . . V V |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ ^ |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | |
||||
* Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | | |
||||
* . . . . . . . . . . . . . . . . | View_2 |
||||
* . . . . . . . . . . . . . . . . | | |
||||
* . . . . . . . . . . . . . . . . | | |
||||
* . . . . . . . . . . . . . . . . V | |
||||
* U V U V U V U V U V U V . . . . ^ | |
||||
* U V U V U V U V U V U V . . . . | | |
||||
* U V U V U V U V U V U V . . . . | | |
||||
* U V U V U V U V U V U V . . . . UV_Scanlines | |
||||
* . . . . . . . . . . . . . . . . | | |
||||
* . . . . . . . . . . . . . . . . V V |
||||
* . . . . . . . . . . . . . . . . --> Buffer size alignment |
||||
* |
||||
* Y_Stride : Width aligned to 128 |
||||
* UV_Stride : Width aligned to 128 |
||||
* Y_Scanlines: Height aligned to 32 |
||||
* UV_Scanlines: Height/2 aligned to 16 |
||||
* View_1 begin at: 0 (zero) |
||||
* View_2 begin at: Y_Stride * Y_Scanlines + UV_Stride * UV_Scanlines |
||||
* Extradata: Arbitrary (software-imposed) padding |
||||
* Total size = align((2*(Y_Stride * Y_Scanlines) |
||||
* + 2*(UV_Stride * UV_Scanlines) + Extradata), 4096) |
||||
*/ |
||||
COLOR_FMT_NV12_MVTB, |
||||
/* Venus NV12 UBWC:
|
||||
* Compressed Macro-tile format for NV12. |
||||
* Contains 4 planes in the following order - |
||||
* (A) Y_Meta_Plane |
||||
* (B) Y_UBWC_Plane |
||||
* (C) UV_Meta_Plane |
||||
* (D) UV_UBWC_Plane |
||||
* |
||||
* Y_Meta_Plane consists of meta information to decode compressed |
||||
* tile data in Y_UBWC_Plane. |
||||
* Y_UBWC_Plane consists of Y data in compressed macro-tile format. |
||||
* UBWC decoder block will use the Y_Meta_Plane data together with |
||||
* Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples. |
||||
* |
||||
* UV_Meta_Plane consists of meta information to decode compressed |
||||
* tile data in UV_UBWC_Plane. |
||||
* UV_UBWC_Plane consists of UV data in compressed macro-tile format. |
||||
* UBWC decoder block will use UV_Meta_Plane data together with |
||||
* UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2 |
||||
* subsampled color difference samples. |
||||
* |
||||
* Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable |
||||
* and randomly accessible. There is no dependency between tiles. |
||||
* |
||||
* <----- Y_Meta_Stride ----> |
||||
* <-------- Width ------> |
||||
* M M M M M M M M M M M M . . ^ ^ |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . Height | |
||||
* M M M M M M M M M M M M . . | Meta_Y_Scanlines |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . V | |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* . . . . . . . . . . . . . . V |
||||
* <--Compressed tile Y Stride---> |
||||
* <------- Width -------> |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* . . . . . . . . . . . . . . . . V |
||||
* <----- UV_Meta_Stride ----> |
||||
* M M M M M M M M M M M M . . ^ |
||||
* M M M M M M M M M M M M . . | |
||||
* M M M M M M M M M M M M . . | |
||||
* M M M M M M M M M M M M . . M_UV_Scanlines |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . V |
||||
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* <--Compressed tile UV Stride---> |
||||
* U* V* U* V* U* V* U* V* . . . . ^ |
||||
* U* V* U* V* U* V* U* V* . . . . | |
||||
* U* V* U* V* U* V* U* V* . . . . | |
||||
* U* V* U* V* U* V* U* V* . . . . UV_Scanlines |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* |
||||
* Y_Stride = align(Width, 128) |
||||
* UV_Stride = align(Width, 128) |
||||
* Y_Scanlines = align(Height, 32) |
||||
* UV_Scanlines = align(Height/2, 16) |
||||
* Y_UBWC_Plane_size = align(Y_Stride * Y_Scanlines, 4096) |
||||
* UV_UBWC_Plane_size = align(UV_Stride * UV_Scanlines, 4096) |
||||
* Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) |
||||
* Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16) |
||||
* Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096) |
||||
* UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) |
||||
* UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) |
||||
* UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) |
||||
* Extradata = 8k |
||||
* |
||||
* Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size + |
||||
* Y_Meta_Plane_size + UV_Meta_Plane_size |
||||
* + max(Extradata, Y_Stride * 48), 4096) |
||||
*/ |
||||
COLOR_FMT_NV12_UBWC, |
||||
/* Venus NV12 10-bit UBWC:
|
||||
* Compressed Macro-tile format for NV12. |
||||
* Contains 4 planes in the following order - |
||||
* (A) Y_Meta_Plane |
||||
* (B) Y_UBWC_Plane |
||||
* (C) UV_Meta_Plane |
||||
* (D) UV_UBWC_Plane |
||||
* |
||||
* Y_Meta_Plane consists of meta information to decode compressed |
||||
* tile data in Y_UBWC_Plane. |
||||
* Y_UBWC_Plane consists of Y data in compressed macro-tile format. |
||||
* UBWC decoder block will use the Y_Meta_Plane data together with |
||||
* Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples. |
||||
* |
||||
* UV_Meta_Plane consists of meta information to decode compressed |
||||
* tile data in UV_UBWC_Plane. |
||||
* UV_UBWC_Plane consists of UV data in compressed macro-tile format. |
||||
* UBWC decoder block will use UV_Meta_Plane data together with |
||||
* UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2 |
||||
* subsampled color difference samples. |
||||
* |
||||
* Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable |
||||
* and randomly accessible. There is no dependency between tiles. |
||||
* |
||||
* <----- Y_Meta_Stride -----> |
||||
* <-------- Width ------> |
||||
* M M M M M M M M M M M M . . ^ ^ |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . Height | |
||||
* M M M M M M M M M M M M . . | Meta_Y_Scanlines |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . V | |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* . . . . . . . . . . . . . . V |
||||
* <--Compressed tile Y Stride---> |
||||
* <------- Width -------> |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | |
||||
* Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* . . . . . . . . . . . . . . . . V |
||||
* <----- UV_Meta_Stride ----> |
||||
* M M M M M M M M M M M M . . ^ |
||||
* M M M M M M M M M M M M . . | |
||||
* M M M M M M M M M M M M . . | |
||||
* M M M M M M M M M M M M . . M_UV_Scanlines |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . V |
||||
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* <--Compressed tile UV Stride---> |
||||
* U* V* U* V* U* V* U* V* . . . . ^ |
||||
* U* V* U* V* U* V* U* V* . . . . | |
||||
* U* V* U* V* U* V* U* V* . . . . | |
||||
* U* V* U* V* U* V* U* V* . . . . UV_Scanlines |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* |
||||
* |
||||
* Y_Stride = align(Width * 4/3, 128) |
||||
* UV_Stride = align(Width * 4/3, 128) |
||||
* Y_Scanlines = align(Height, 32) |
||||
* UV_Scanlines = align(Height/2, 16) |
||||
* Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096) |
||||
* UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096) |
||||
* Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) |
||||
* Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16) |
||||
* Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096) |
||||
* UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) |
||||
* UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) |
||||
* UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) |
||||
* Extradata = 8k |
||||
* |
||||
* Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size + |
||||
* Y_Meta_Plane_size + UV_Meta_Plane_size |
||||
* + max(Extradata, Y_Stride * 48), 4096) |
||||
*/ |
||||
COLOR_FMT_NV12_BPP10_UBWC, |
||||
/* Venus RGBA8888 format:
|
||||
* Contains 1 plane in the following order - |
||||
* (A) RGBA plane |
||||
* |
||||
* <-------- RGB_Stride --------> |
||||
* <------- Width -------> |
||||
* R R R R R R R R R R R R . . . . ^ ^ |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . Height | |
||||
* R R R R R R R R R R R R . . . . | RGB_Scanlines |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . V | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . V |
||||
* |
||||
* RGB_Stride = align(Width * 4, 128) |
||||
* RGB_Scanlines = align(Height, 32) |
||||
* RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096) |
||||
* Extradata = 8k |
||||
* |
||||
* Total size = align(RGB_Plane_size + Extradata, 4096) |
||||
*/ |
||||
COLOR_FMT_RGBA8888, |
||||
/* Venus RGBA8888 UBWC format:
|
||||
* Contains 2 planes in the following order - |
||||
* (A) Meta plane |
||||
* (B) RGBA plane |
||||
* |
||||
* <--- RGB_Meta_Stride ----> |
||||
* <-------- Width ------> |
||||
* M M M M M M M M M M M M . . ^ ^ |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . Height | |
||||
* M M M M M M M M M M M M . . | Meta_RGB_Scanlines |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . | | |
||||
* M M M M M M M M M M M M . . V | |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* . . . . . . . . . . . . . . V |
||||
* <-------- RGB_Stride --------> |
||||
* <------- Width -------> |
||||
* R R R R R R R R R R R R . . . . ^ ^ |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . Height | |
||||
* R R R R R R R R R R R R . . . . | RGB_Scanlines |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . | | |
||||
* R R R R R R R R R R R R . . . . V | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . | |
||||
* . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k |
||||
* . . . . . . . . . . . . . . . . V |
||||
* |
||||
* RGB_Stride = align(Width * 4, 128) |
||||
* RGB_Scanlines = align(Height, 32) |
||||
* RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096) |
||||
* RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64) |
||||
* RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16) |
||||
* RGB_Meta_Plane_size = align(RGB_Meta_Stride * |
||||
* RGB_Meta_Scanlines, 4096) |
||||
* Extradata = 8k |
||||
* |
||||
* Total size = align(RGB_Meta_Plane_size + RGB_Plane_size + |
||||
* Extradata, 4096) |
||||
*/ |
||||
COLOR_FMT_RGBA8888_UBWC, |
||||
}; |
||||
|
||||
static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height) |
||||
{ |
||||
(void)height; |
||||
(void)width; |
||||
|
||||
/*
|
||||
* In the future, calculate the size based on the w/h but just |
||||
* hardcode it for now since 16K satisfies all current usecases. |
||||
*/ |
||||
return 16 * 1024; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width) |
||||
{ |
||||
unsigned int alignment, stride = 0; |
||||
if (!width) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV21: |
||||
case COLOR_FMT_NV12: |
||||
case COLOR_FMT_NV12_MVTB: |
||||
case COLOR_FMT_NV12_UBWC: |
||||
alignment = 128; |
||||
stride = MSM_MEDIA_ALIGN(width, alignment); |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
alignment = 256; |
||||
stride = MSM_MEDIA_ALIGN(width, 192); |
||||
stride = MSM_MEDIA_ALIGN(stride * 4/3, alignment); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
invalid_input: |
||||
return stride; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_UV_STRIDE(int color_fmt, int width) |
||||
{ |
||||
unsigned int alignment, stride = 0; |
||||
if (!width) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV21: |
||||
case COLOR_FMT_NV12: |
||||
case COLOR_FMT_NV12_MVTB: |
||||
case COLOR_FMT_NV12_UBWC: |
||||
alignment = 128; |
||||
stride = MSM_MEDIA_ALIGN(width, alignment); |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
alignment = 256; |
||||
stride = MSM_MEDIA_ALIGN(width, 192); |
||||
stride = MSM_MEDIA_ALIGN(stride * 4/3, alignment); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
invalid_input: |
||||
return stride; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_Y_SCANLINES(int color_fmt, int height) |
||||
{ |
||||
unsigned int alignment, sclines = 0; |
||||
if (!height) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV21: |
||||
case COLOR_FMT_NV12: |
||||
case COLOR_FMT_NV12_MVTB: |
||||
case COLOR_FMT_NV12_UBWC: |
||||
alignment = 32; |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
alignment = 16; |
||||
break; |
||||
default: |
||||
return 0; |
||||
} |
||||
sclines = MSM_MEDIA_ALIGN(height, alignment); |
||||
invalid_input: |
||||
return sclines; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_UV_SCANLINES(int color_fmt, int height) |
||||
{ |
||||
unsigned int alignment, sclines = 0; |
||||
if (!height) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV21: |
||||
case COLOR_FMT_NV12: |
||||
case COLOR_FMT_NV12_MVTB: |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
alignment = 16; |
||||
break; |
||||
case COLOR_FMT_NV12_UBWC: |
||||
alignment = 32; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
sclines = MSM_MEDIA_ALIGN(height / 2, alignment); |
||||
|
||||
invalid_input: |
||||
return sclines; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_Y_META_STRIDE(int color_fmt, int width) |
||||
{ |
||||
int y_tile_width = 0, y_meta_stride = 0; |
||||
|
||||
if (!width) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV12_UBWC: |
||||
y_tile_width = 32; |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
y_tile_width = 48; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
y_meta_stride = MSM_MEDIA_ROUNDUP(width, y_tile_width); |
||||
y_meta_stride = MSM_MEDIA_ALIGN(y_meta_stride, 64); |
||||
|
||||
invalid_input: |
||||
return y_meta_stride; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_Y_META_SCANLINES(int color_fmt, int height) |
||||
{ |
||||
int y_tile_height = 0, y_meta_scanlines = 0; |
||||
|
||||
if (!height) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV12_UBWC: |
||||
y_tile_height = 8; |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
y_tile_height = 4; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
y_meta_scanlines = MSM_MEDIA_ROUNDUP(height, y_tile_height); |
||||
y_meta_scanlines = MSM_MEDIA_ALIGN(y_meta_scanlines, 16); |
||||
|
||||
invalid_input: |
||||
return y_meta_scanlines; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_UV_META_STRIDE(int color_fmt, int width) |
||||
{ |
||||
int uv_tile_width = 0, uv_meta_stride = 0; |
||||
|
||||
if (!width) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV12_UBWC: |
||||
uv_tile_width = 16; |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
uv_tile_width = 24; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
uv_meta_stride = MSM_MEDIA_ROUNDUP(width / 2, uv_tile_width); |
||||
uv_meta_stride = MSM_MEDIA_ALIGN(uv_meta_stride, 64); |
||||
|
||||
invalid_input: |
||||
return uv_meta_stride; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_UV_META_SCANLINES(int color_fmt, int height) |
||||
{ |
||||
int uv_tile_height = 0, uv_meta_scanlines = 0; |
||||
|
||||
if (!height) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV12_UBWC: |
||||
uv_tile_height = 8; |
||||
break; |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
uv_tile_height = 4; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
uv_meta_scanlines = MSM_MEDIA_ROUNDUP(height / 2, uv_tile_height); |
||||
uv_meta_scanlines = MSM_MEDIA_ALIGN(uv_meta_scanlines, 16); |
||||
|
||||
invalid_input: |
||||
return uv_meta_scanlines; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_RGB_STRIDE(int color_fmt, int width) |
||||
{ |
||||
unsigned int alignment = 0, stride = 0; |
||||
if (!width) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_RGBA8888: |
||||
alignment = 128; |
||||
break; |
||||
case COLOR_FMT_RGBA8888_UBWC: |
||||
alignment = 256; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
stride = MSM_MEDIA_ALIGN(width * 4, alignment); |
||||
|
||||
invalid_input: |
||||
return stride; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_RGB_SCANLINES(int color_fmt, int height) |
||||
{ |
||||
unsigned int alignment = 0, scanlines = 0; |
||||
|
||||
if (!height) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_RGBA8888: |
||||
alignment = 32; |
||||
break; |
||||
case COLOR_FMT_RGBA8888_UBWC: |
||||
alignment = 16; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
scanlines = MSM_MEDIA_ALIGN(height, alignment); |
||||
|
||||
invalid_input: |
||||
return scanlines; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_RGB_META_STRIDE(int color_fmt, int width) |
||||
{ |
||||
int rgb_tile_width = 0, rgb_meta_stride = 0; |
||||
|
||||
if (!width) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_RGBA8888_UBWC: |
||||
rgb_tile_width = 16; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
rgb_meta_stride = MSM_MEDIA_ROUNDUP(width, rgb_tile_width); |
||||
rgb_meta_stride = MSM_MEDIA_ALIGN(rgb_meta_stride, 64); |
||||
|
||||
invalid_input: |
||||
return rgb_meta_stride; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_RGB_META_SCANLINES(int color_fmt, int height) |
||||
{ |
||||
int rgb_tile_height = 0, rgb_meta_scanlines = 0; |
||||
|
||||
if (!height) |
||||
goto invalid_input; |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_RGBA8888_UBWC: |
||||
rgb_tile_height = 4; |
||||
break; |
||||
default: |
||||
goto invalid_input; |
||||
} |
||||
|
||||
rgb_meta_scanlines = MSM_MEDIA_ROUNDUP(height, rgb_tile_height); |
||||
rgb_meta_scanlines = MSM_MEDIA_ALIGN(rgb_meta_scanlines, 16); |
||||
|
||||
invalid_input: |
||||
return rgb_meta_scanlines; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_BUFFER_SIZE( |
||||
int color_fmt, int width, int height) |
||||
{ |
||||
const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height); |
||||
unsigned int uv_alignment = 0, size = 0; |
||||
unsigned int y_plane, uv_plane, y_stride, |
||||
uv_stride, y_sclines, uv_sclines; |
||||
unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0; |
||||
unsigned int y_meta_stride = 0, y_meta_scanlines = 0; |
||||
unsigned int uv_meta_stride = 0, uv_meta_scanlines = 0; |
||||
unsigned int y_meta_plane = 0, uv_meta_plane = 0; |
||||
unsigned int rgb_stride = 0, rgb_scanlines = 0; |
||||
unsigned int rgb_plane = 0, rgb_ubwc_plane = 0, rgb_meta_plane = 0; |
||||
unsigned int rgb_meta_stride = 0, rgb_meta_scanlines = 0; |
||||
|
||||
if (!width || !height) |
||||
goto invalid_input; |
||||
|
||||
y_stride = VENUS_Y_STRIDE(color_fmt, width); |
||||
uv_stride = VENUS_UV_STRIDE(color_fmt, width); |
||||
y_sclines = VENUS_Y_SCANLINES(color_fmt, height); |
||||
uv_sclines = VENUS_UV_SCANLINES(color_fmt, height); |
||||
rgb_stride = VENUS_RGB_STRIDE(color_fmt, width); |
||||
rgb_scanlines = VENUS_RGB_SCANLINES(color_fmt, height); |
||||
|
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV21: |
||||
case COLOR_FMT_NV12: |
||||
uv_alignment = 4096; |
||||
y_plane = y_stride * y_sclines; |
||||
uv_plane = uv_stride * uv_sclines + uv_alignment; |
||||
size = y_plane + uv_plane + |
||||
MSM_MEDIA_MAX(extra_size, 8 * y_stride); |
||||
size = MSM_MEDIA_ALIGN(size, 4096); |
||||
break; |
||||
case COLOR_FMT_NV12_MVTB: |
||||
uv_alignment = 4096; |
||||
y_plane = y_stride * y_sclines; |
||||
uv_plane = uv_stride * uv_sclines + uv_alignment; |
||||
size = y_plane + uv_plane; |
||||
size = 2 * size + extra_size; |
||||
size = MSM_MEDIA_ALIGN(size, 4096); |
||||
break; |
||||
case COLOR_FMT_NV12_UBWC: |
||||
case COLOR_FMT_NV12_BPP10_UBWC: |
||||
y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); |
||||
uv_ubwc_plane = MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096); |
||||
y_meta_stride = VENUS_Y_META_STRIDE(color_fmt, width); |
||||
y_meta_scanlines = VENUS_Y_META_SCANLINES(color_fmt, height); |
||||
y_meta_plane = MSM_MEDIA_ALIGN( |
||||
y_meta_stride * y_meta_scanlines, 4096); |
||||
uv_meta_stride = VENUS_UV_META_STRIDE(color_fmt, width); |
||||
uv_meta_scanlines = VENUS_UV_META_SCANLINES(color_fmt, height); |
||||
uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride * |
||||
uv_meta_scanlines, 4096); |
||||
|
||||
size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + |
||||
uv_meta_plane + |
||||
MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride); |
||||
size = MSM_MEDIA_ALIGN(size, 4096); |
||||
break; |
||||
case COLOR_FMT_RGBA8888: |
||||
rgb_plane = MSM_MEDIA_ALIGN(rgb_stride * rgb_scanlines, 4096); |
||||
size = rgb_plane; |
||||
size = MSM_MEDIA_ALIGN(size, 4096); |
||||
break; |
||||
case COLOR_FMT_RGBA8888_UBWC: |
||||
rgb_ubwc_plane = MSM_MEDIA_ALIGN(rgb_stride * rgb_scanlines, |
||||
4096); |
||||
rgb_meta_stride = VENUS_RGB_META_STRIDE(color_fmt, width); |
||||
rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color_fmt, |
||||
height); |
||||
rgb_meta_plane = MSM_MEDIA_ALIGN(rgb_meta_stride * |
||||
rgb_meta_scanlines, 4096); |
||||
size = rgb_ubwc_plane + rgb_meta_plane; |
||||
size = MSM_MEDIA_ALIGN(size, 4096); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
invalid_input: |
||||
return size; |
||||
} |
||||
|
||||
static inline unsigned int VENUS_VIEW2_OFFSET( |
||||
int color_fmt, int width, int height) |
||||
{ |
||||
unsigned int offset = 0; |
||||
unsigned int y_plane, uv_plane, y_stride, |
||||
uv_stride, y_sclines, uv_sclines; |
||||
if (!width || !height) |
||||
goto invalid_input; |
||||
|
||||
y_stride = VENUS_Y_STRIDE(color_fmt, width); |
||||
uv_stride = VENUS_UV_STRIDE(color_fmt, width); |
||||
y_sclines = VENUS_Y_SCANLINES(color_fmt, height); |
||||
uv_sclines = VENUS_UV_SCANLINES(color_fmt, height); |
||||
switch (color_fmt) { |
||||
case COLOR_FMT_NV12_MVTB: |
||||
y_plane = y_stride * y_sclines; |
||||
uv_plane = uv_stride * uv_sclines; |
||||
offset = y_plane + uv_plane; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
invalid_input: |
||||
return offset; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,217 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
#include <time.h> |
||||
#include <errno.h> |
||||
|
||||
#include <unistd.h> |
||||
#include <sys/stat.h> |
||||
|
||||
#include <pthread.h> |
||||
#include <bzlib.h> |
||||
|
||||
#include "common/swaglog.h" |
||||
|
||||
#include "logger.h" |
||||
|
||||
static int mkpath(char* file_path) { |
||||
assert(file_path && *file_path); |
||||
char* p; |
||||
for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { |
||||
*p = '\0'; |
||||
if (mkdir(file_path, 0777)==-1) { |
||||
if (errno != EEXIST) { |
||||
*p = '/'; |
||||
return -1; |
||||
} |
||||
} |
||||
*p = '/'; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void logger_init(LoggerState *s, const char* log_name, const uint8_t* init_data, size_t init_data_len, bool has_qlog) { |
||||
memset(s, 0, sizeof(*s)); |
||||
if (init_data) { |
||||
s->init_data = (uint8_t*)malloc(init_data_len); |
||||
assert(s->init_data); |
||||
memcpy(s->init_data, init_data, init_data_len); |
||||
s->init_data_len = init_data_len; |
||||
} |
||||
|
||||
umask(0); |
||||
|
||||
pthread_mutex_init(&s->lock, NULL); |
||||
|
||||
s->part = -1; |
||||
s->has_qlog = has_qlog; |
||||
|
||||
time_t rawtime = time(NULL); |
||||
struct tm timeinfo; |
||||
localtime_r(&rawtime, &timeinfo); |
||||
|
||||
strftime(s->route_name, sizeof(s->route_name), |
||||
"%Y-%m-%d--%H-%M-%S", &timeinfo); |
||||
snprintf(s->log_name, sizeof(s->log_name), "%s", log_name); |
||||
} |
||||
|
||||
static LoggerHandle* logger_open(LoggerState *s, const char* root_path) { |
||||
int err; |
||||
|
||||
LoggerHandle *h = NULL; |
||||
for (int i=0; i<LOGGER_MAX_HANDLES; i++) { |
||||
if (s->handles[i].refcnt == 0) { |
||||
h = &s->handles[i]; |
||||
break; |
||||
} |
||||
} |
||||
assert(h); |
||||
|
||||
snprintf(h->segment_path, sizeof(h->segment_path), |
||||
"%s/%s--%d", root_path, s->route_name, s->part); |
||||
|
||||
snprintf(h->log_path, sizeof(h->log_path), "%s/%s.bz2", h->segment_path, s->log_name); |
||||
snprintf(h->qlog_path, sizeof(h->qlog_path), "%s/qlog.bz2", h->segment_path); |
||||
snprintf(h->lock_path, sizeof(h->lock_path), "%s.lock", h->log_path); |
||||
|
||||
err = mkpath(h->log_path); |
||||
if (err) return NULL; |
||||
|
||||
FILE* lock_file = fopen(h->lock_path, "wb"); |
||||
if (lock_file == NULL) return NULL; |
||||
fclose(lock_file); |
||||
|
||||
h->log_file = fopen(h->log_path, "wb"); |
||||
if (h->log_file == NULL) goto fail; |
||||
|
||||
if (s->has_qlog) { |
||||
h->qlog_file = fopen(h->qlog_path, "wb"); |
||||
if (h->qlog_file == NULL) goto fail; |
||||
} |
||||
|
||||
int bzerror; |
||||
h->bz_file = BZ2_bzWriteOpen(&bzerror, h->log_file, 9, 0, 30); |
||||
if (bzerror != BZ_OK) goto fail; |
||||
|
||||
if (s->has_qlog) { |
||||
h->bz_qlog = BZ2_bzWriteOpen(&bzerror, h->qlog_file, 9, 0, 30); |
||||
if (bzerror != BZ_OK) goto fail; |
||||
} |
||||
|
||||
if (s->init_data) { |
||||
BZ2_bzWrite(&bzerror, h->bz_file, s->init_data, s->init_data_len); |
||||
if (bzerror != BZ_OK) goto fail; |
||||
|
||||
if (s->has_qlog) { |
||||
// init data goes in the qlog too
|
||||
BZ2_bzWrite(&bzerror, h->bz_qlog, s->init_data, s->init_data_len); |
||||
if (bzerror != BZ_OK) goto fail; |
||||
} |
||||
} |
||||
|
||||
pthread_mutex_init(&h->lock, NULL); |
||||
h->refcnt++; |
||||
return h; |
||||
fail: |
||||
LOGE("logger failed to open files"); |
||||
if (h->qlog_file) fclose(h->qlog_file); |
||||
if (h->log_file) fclose(h->log_file); |
||||
return NULL; |
||||
} |
||||
|
||||
int logger_next(LoggerState *s, const char* root_path, |
||||
char* out_segment_path, size_t out_segment_path_len, |
||||
int* out_part) { |
||||
pthread_mutex_lock(&s->lock); |
||||
s->part++; |
||||
|
||||
LoggerHandle* next_h = logger_open(s, root_path); |
||||
if (!next_h) { |
||||
pthread_mutex_unlock(&s->lock); |
||||
return -1; |
||||
} |
||||
|
||||
if (s->cur_handle) { |
||||
lh_close(s->cur_handle); |
||||
} |
||||
s->cur_handle = next_h; |
||||
|
||||
if (out_segment_path) { |
||||
snprintf(out_segment_path, out_segment_path_len, "%s", next_h->segment_path); |
||||
} |
||||
if (out_part) { |
||||
*out_part = s->part; |
||||
} |
||||
|
||||
pthread_mutex_unlock(&s->lock); |
||||
return 0; |
||||
} |
||||
|
||||
LoggerHandle* logger_get_handle(LoggerState *s) { |
||||
pthread_mutex_lock(&s->lock); |
||||
LoggerHandle* h = s->cur_handle; |
||||
if (h) { |
||||
pthread_mutex_lock(&h->lock); |
||||
h->refcnt++; |
||||
pthread_mutex_unlock(&h->lock); |
||||
} |
||||
pthread_mutex_unlock(&s->lock); |
||||
return h; |
||||
} |
||||
|
||||
void logger_log(LoggerState *s, uint8_t* data, size_t data_size, bool in_qlog) { |
||||
pthread_mutex_lock(&s->lock); |
||||
if (s->cur_handle) { |
||||
lh_log(s->cur_handle, data, data_size, in_qlog); |
||||
} |
||||
pthread_mutex_unlock(&s->lock); |
||||
} |
||||
|
||||
void logger_close(LoggerState *s) { |
||||
pthread_mutex_lock(&s->lock); |
||||
free(s->init_data); |
||||
if (s->cur_handle) { |
||||
lh_close(s->cur_handle); |
||||
} |
||||
pthread_mutex_unlock(&s->lock); |
||||
} |
||||
|
||||
void lh_log(LoggerHandle* h, uint8_t* data, size_t data_size, bool in_qlog) { |
||||
pthread_mutex_lock(&h->lock); |
||||
assert(h->refcnt > 0); |
||||
int bzerror; |
||||
BZ2_bzWrite(&bzerror, h->bz_file, data, data_size); |
||||
|
||||
if (in_qlog && h->bz_qlog != NULL) { |
||||
BZ2_bzWrite(&bzerror, h->bz_qlog, data, data_size); |
||||
} |
||||
pthread_mutex_unlock(&h->lock); |
||||
} |
||||
|
||||
void lh_close(LoggerHandle* h) { |
||||
pthread_mutex_lock(&h->lock); |
||||
assert(h->refcnt > 0); |
||||
h->refcnt--; |
||||
if (h->refcnt == 0) { |
||||
if (h->bz_file){ |
||||
int bzerror; |
||||
BZ2_bzWriteClose(&bzerror, h->bz_file, 0, NULL, NULL); |
||||
h->bz_file = NULL; |
||||
} |
||||
if (h->bz_qlog){ |
||||
int bzerror; |
||||
BZ2_bzWriteClose(&bzerror, h->bz_qlog, 0, NULL, NULL); |
||||
h->bz_qlog = NULL; |
||||
} |
||||
if (h->qlog_file) fclose(h->qlog_file); |
||||
fclose(h->log_file); |
||||
unlink(h->lock_path); |
||||
pthread_mutex_unlock(&h->lock); |
||||
pthread_mutex_destroy(&h->lock); |
||||
return; |
||||
} |
||||
pthread_mutex_unlock(&h->lock); |
||||
} |
@ -0,0 +1,59 @@ |
||||
#ifndef LOGGER_H |
||||
#define LOGGER_H |
||||
|
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <pthread.h> |
||||
#include <bzlib.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define LOGGER_MAX_HANDLES 16 |
||||
|
||||
typedef struct LoggerHandle { |
||||
pthread_mutex_t lock; |
||||
int refcnt; |
||||
char segment_path[4096]; |
||||
char log_path[4096]; |
||||
char lock_path[4096]; |
||||
FILE* log_file; |
||||
BZFILE* bz_file; |
||||
|
||||
FILE* qlog_file; |
||||
char qlog_path[4096]; |
||||
BZFILE* bz_qlog; |
||||
} LoggerHandle; |
||||
|
||||
typedef struct LoggerState { |
||||
pthread_mutex_t lock; |
||||
|
||||
uint8_t* init_data; |
||||
size_t init_data_len; |
||||
|
||||
int part; |
||||
char route_name[64]; |
||||
char log_name[64]; |
||||
bool has_qlog; |
||||
|
||||
LoggerHandle handles[LOGGER_MAX_HANDLES]; |
||||
LoggerHandle* cur_handle; |
||||
} LoggerState; |
||||
|
||||
void logger_init(LoggerState *s, const char* log_name, const uint8_t* init_data, size_t init_data_len, bool has_qlog); |
||||
int logger_next(LoggerState *s, const char* root_path, |
||||
char* out_segment_path, size_t out_segment_path_len, |
||||
int* out_part); |
||||
LoggerHandle* logger_get_handle(LoggerState *s); |
||||
void logger_close(LoggerState *s); |
||||
void logger_log(LoggerState *s, uint8_t* data, size_t data_size, bool in_qlog); |
||||
|
||||
void lh_log(LoggerHandle* h, uint8_t* data, size_t data_size, bool in_qlog); |
||||
void lh_close(LoggerHandle* h); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,738 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <cstdint> |
||||
#include <cassert> |
||||
#include <unistd.h> |
||||
#include <signal.h> |
||||
#include <errno.h> |
||||
#include <poll.h> |
||||
#include <string.h> |
||||
#include <inttypes.h> |
||||
#include <libyuv.h> |
||||
#include <sys/resource.h> |
||||
|
||||
#include <string> |
||||
#include <iostream> |
||||
#include <fstream> |
||||
#include <streambuf> |
||||
#include <thread> |
||||
#include <mutex> |
||||
#include <condition_variable> |
||||
#include <random> |
||||
|
||||
#include <ftw.h> |
||||
|
||||
#include <zmq.h> |
||||
#include <yaml-cpp/yaml.h> |
||||
#include <capnp/serialize.h> |
||||
|
||||
#ifdef QCOM |
||||
#include <cutils/properties.h> |
||||
#endif |
||||
|
||||
#include "common/version.h" |
||||
#include "common/timing.h" |
||||
#include "common/params.h" |
||||
#include "common/swaglog.h" |
||||
#include "common/visionipc.h" |
||||
#include "common/utilpp.h" |
||||
#include "common/util.h" |
||||
|
||||
#include "logger.h" |
||||
#include "messaging.hpp" |
||||
|
||||
#ifndef QCOM |
||||
// no encoder on PC
|
||||
#define DISABLE_ENCODER |
||||
#endif |
||||
|
||||
|
||||
#ifndef DISABLE_ENCODER |
||||
#include "encoder.h" |
||||
#include "raw_logger.h" |
||||
#endif |
||||
|
||||
#include "cereal/gen/cpp/log.capnp.h" |
||||
|
||||
#define CAMERA_FPS 20 |
||||
#define SEGMENT_LENGTH 60 |
||||
#define LOG_ROOT "/data/media/0/realdata" |
||||
#define ENABLE_LIDAR 0 |
||||
|
||||
#define RAW_CLIP_LENGTH 100 // 5 seconds at 20fps
|
||||
#define RAW_CLIP_FREQUENCY (randrange(61, 8*60)) // once every ~4 minutes
|
||||
|
||||
namespace { |
||||
|
||||
double randrange(double a, double b) { |
||||
static std::mt19937 gen(millis_since_boot()); |
||||
|
||||
std::uniform_real_distribution<> dist(a, b); |
||||
return dist(gen); |
||||
} |
||||
|
||||
|
||||
volatile sig_atomic_t do_exit = 0; |
||||
static void set_do_exit(int sig) { |
||||
do_exit = 1; |
||||
} |
||||
struct LoggerdState { |
||||
Context *ctx; |
||||
LoggerState logger; |
||||
|
||||
std::mutex lock; |
||||
std::condition_variable cv; |
||||
char segment_path[4096]; |
||||
uint32_t last_frame_id; |
||||
uint32_t rotate_last_frame_id; |
||||
int rotate_segment; |
||||
}; |
||||
LoggerdState s; |
||||
|
||||
#ifndef DISABLE_ENCODER |
||||
void encoder_thread(bool is_streaming, bool raw_clips, bool front) { |
||||
int err; |
||||
|
||||
if (front) { |
||||
char *value; |
||||
const int result = read_db_value(NULL, "RecordFront", &value, NULL); |
||||
if (result != 0) return; |
||||
if (value[0] != '1') { free(value); return; } |
||||
free(value); |
||||
LOGW("recording front camera"); |
||||
|
||||
set_thread_name("FrontCameraEncoder"); |
||||
} else { |
||||
set_thread_name("RearCameraEncoder"); |
||||
} |
||||
|
||||
VisionStream stream; |
||||
|
||||
bool encoder_inited = false; |
||||
EncoderState encoder; |
||||
EncoderState encoder_alt; |
||||
bool has_encoder_alt = false; |
||||
|
||||
int encoder_segment = -1; |
||||
int cnt = 0; |
||||
|
||||
PubSocket *idx_sock = PubSocket::create(s.ctx, front ? "frontEncodeIdx" : "encodeIdx"); |
||||
assert(idx_sock != NULL); |
||||
|
||||
LoggerHandle *lh = NULL; |
||||
|
||||
while (!do_exit) { |
||||
VisionStreamBufs buf_info; |
||||
if (front) { |
||||
err = visionstream_init(&stream, VISION_STREAM_YUV_FRONT, false, &buf_info); |
||||
} else { |
||||
err = visionstream_init(&stream, VISION_STREAM_YUV, false, &buf_info); |
||||
} |
||||
if (err != 0) { |
||||
LOGD("visionstream connect fail"); |
||||
usleep(100000); |
||||
continue; |
||||
} |
||||
|
||||
if (!encoder_inited) { |
||||
LOGD("encoder init %dx%d", buf_info.width, buf_info.height); |
||||
encoder_init(&encoder, front ? "dcamera.hevc" : "fcamera.hevc", buf_info.width, buf_info.height, CAMERA_FPS, front ? 2500000 : 5000000, true, false); |
||||
if (!front) { |
||||
encoder_init(&encoder_alt, "qcamera.ts", 480, 360, CAMERA_FPS, 128000, false, true); |
||||
has_encoder_alt = true; |
||||
} |
||||
encoder_inited = true; |
||||
if (is_streaming) { |
||||
encoder.zmq_ctx = zmq_ctx_new(); |
||||
encoder.stream_sock_raw = zmq_socket(encoder.zmq_ctx, ZMQ_PUB); |
||||
assert(encoder.stream_sock_raw); |
||||
zmq_bind(encoder.stream_sock_raw, "tcp://*:9002"); |
||||
} |
||||
} |
||||
|
||||
// dont log a raw clip in the first minute
|
||||
double rawlogger_start_time = seconds_since_boot()+RAW_CLIP_FREQUENCY; |
||||
int rawlogger_clip_cnt = 0; |
||||
RawLogger *rawlogger = NULL; |
||||
|
||||
if (raw_clips) { |
||||
rawlogger = new RawLogger("prcamera", buf_info.width, buf_info.height, CAMERA_FPS); |
||||
} |
||||
|
||||
while (!do_exit) { |
||||
VIPCBufExtra extra; |
||||
VIPCBuf* buf = visionstream_get(&stream, &extra); |
||||
if (buf == NULL) { |
||||
LOG("visionstream get failed"); |
||||
break; |
||||
} |
||||
|
||||
uint64_t current_time = nanos_since_boot(); |
||||
uint64_t diff = current_time - extra.timestamp_eof; |
||||
double msdiff = (double) diff / 1000000.0; |
||||
// printf("logger latency to tsEof: %f\n", msdiff);
|
||||
|
||||
uint8_t *y = (uint8_t*)buf->addr; |
||||
uint8_t *u = y + (buf_info.width*buf_info.height); |
||||
uint8_t *v = u + (buf_info.width/2)*(buf_info.height/2); |
||||
|
||||
{ |
||||
bool should_rotate = false; |
||||
std::unique_lock<std::mutex> lk(s.lock); |
||||
if (!front) { |
||||
// wait if log camera is older on back camera
|
||||
while ( extra.frame_id > s.last_frame_id //if the log camera is older, wait for it to catch up.
|
||||
&& (extra.frame_id-s.last_frame_id) < 8 // but if its too old then there probably was a discontinuity (visiond restarted)
|
||||
&& !do_exit) { |
||||
s.cv.wait(lk); |
||||
} |
||||
should_rotate = extra.frame_id > s.rotate_last_frame_id && encoder_segment < s.rotate_segment; |
||||
} else { |
||||
// front camera is best effort
|
||||
should_rotate = encoder_segment < s.rotate_segment; |
||||
} |
||||
if (do_exit) break; |
||||
|
||||
// rotate the encoder if the logger is on a newer segment
|
||||
if (should_rotate) { |
||||
LOG("rotate encoder to %s", s.segment_path); |
||||
|
||||
encoder_rotate(&encoder, s.segment_path, s.rotate_segment); |
||||
if (has_encoder_alt) { |
||||
encoder_rotate(&encoder_alt, s.segment_path, s.rotate_segment); |
||||
} |
||||
|
||||
if (raw_clips) { |
||||
rawlogger->Rotate(s.segment_path, s.rotate_segment); |
||||
} |
||||
|
||||
encoder_segment = s.rotate_segment; |
||||
if (lh) { |
||||
lh_close(lh); |
||||
} |
||||
lh = logger_get_handle(&s.logger); |
||||
} |
||||
} |
||||
|
||||
{ |
||||
// encode hevc
|
||||
int out_segment = -1; |
||||
int out_id = encoder_encode_frame(&encoder, |
||||
y, u, v, |
||||
buf_info.width, buf_info.height, |
||||
&out_segment, &extra); |
||||
|
||||
if (has_encoder_alt) { |
||||
int out_segment_alt = -1; |
||||
encoder_encode_frame(&encoder_alt, |
||||
y, u, v, |
||||
buf_info.width, buf_info.height, |
||||
&out_segment_alt, &extra); |
||||
} |
||||
|
||||
// publish encode index
|
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
auto eidx = event.initEncodeIdx(); |
||||
eidx.setFrameId(extra.frame_id); |
||||
eidx.setType(front ? cereal::EncodeIndex::Type::FRONT : cereal::EncodeIndex::Type::FULL_H_E_V_C); |
||||
eidx.setEncodeId(cnt); |
||||
eidx.setSegmentNum(out_segment); |
||||
eidx.setSegmentId(out_id); |
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
if (idx_sock->send((char*)bytes.begin(), bytes.size()) < 0) { |
||||
printf("err sending encodeIdx pkt: %s\n", strerror(errno)); |
||||
} |
||||
if (lh) { |
||||
lh_log(lh, bytes.begin(), bytes.size(), false); |
||||
} |
||||
} |
||||
|
||||
if (raw_clips) { |
||||
double ts = seconds_since_boot(); |
||||
if (ts > rawlogger_start_time) { |
||||
// encode raw if in clip
|
||||
int out_segment = -1; |
||||
int out_id = rawlogger->LogFrame(cnt, y, u, v, &out_segment); |
||||
|
||||
if (rawlogger_clip_cnt == 0) { |
||||
LOG("starting raw clip in seg %d", out_segment); |
||||
} |
||||
|
||||
// publish encode index
|
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
auto eidx = event.initEncodeIdx(); |
||||
eidx.setFrameId(extra.frame_id); |
||||
eidx.setType(cereal::EncodeIndex::Type::FULL_LOSSLESS_CLIP); |
||||
eidx.setEncodeId(cnt); |
||||
eidx.setSegmentNum(out_segment); |
||||
eidx.setSegmentId(out_id); |
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
if (lh) { |
||||
lh_log(lh, bytes.begin(), bytes.size(), false); |
||||
} |
||||
|
||||
// close rawlogger if clip ended
|
||||
rawlogger_clip_cnt++; |
||||
if (rawlogger_clip_cnt >= RAW_CLIP_LENGTH) { |
||||
rawlogger->Close(); |
||||
|
||||
rawlogger_clip_cnt = 0; |
||||
rawlogger_start_time = ts+RAW_CLIP_FREQUENCY; |
||||
|
||||
LOG("ending raw clip in seg %d, next in %.1f sec", out_segment, rawlogger_start_time-ts); |
||||
} |
||||
} |
||||
} |
||||
|
||||
cnt++; |
||||
} |
||||
|
||||
if (lh) { |
||||
lh_close(lh); |
||||
lh = NULL; |
||||
} |
||||
|
||||
if (raw_clips) { |
||||
rawlogger->Close(); |
||||
delete rawlogger; |
||||
} |
||||
|
||||
visionstream_destroy(&stream); |
||||
} |
||||
|
||||
delete idx_sock; |
||||
|
||||
if (encoder_inited) { |
||||
LOG("encoder destroy"); |
||||
encoder_close(&encoder); |
||||
encoder_destroy(&encoder); |
||||
} |
||||
|
||||
if (has_encoder_alt) { |
||||
LOG("encoder alt destroy"); |
||||
encoder_close(&encoder_alt); |
||||
encoder_destroy(&encoder_alt); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if ENABLE_LIDAR |
||||
|
||||
#include <netinet/in.h> |
||||
#include <sys/types.h> |
||||
#include <sys/socket.h> |
||||
#include <arpa/inet.h> |
||||
|
||||
#define VELODYNE_DATA_PORT 2368 |
||||
#define VELODYNE_TELEMETRY_PORT 8308 |
||||
|
||||
#define MAX_LIDAR_PACKET 2048 |
||||
|
||||
int lidar_thread() { |
||||
// increase kernel max buffer size
|
||||
system("sysctl -w net.core.rmem_max=26214400"); |
||||
set_thread_name("lidar"); |
||||
|
||||
int sock; |
||||
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
||||
perror("cannot create socket"); |
||||
return -1; |
||||
} |
||||
|
||||
int a = 26214400; |
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &a, sizeof(int)) == -1) { |
||||
perror("cannot set socket opts"); |
||||
return -1; |
||||
} |
||||
|
||||
struct sockaddr_in addr; |
||||
memset(&addr, 0, sizeof(struct sockaddr_in)); |
||||
addr.sin_family = AF_INET; |
||||
addr.sin_port = htons(VELODYNE_DATA_PORT); |
||||
inet_aton("192.168.5.11", &(addr.sin_addr)); |
||||
|
||||
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
||||
perror("cannot bind LIDAR socket"); |
||||
return -1; |
||||
} |
||||
|
||||
capnp::byte buf[MAX_LIDAR_PACKET]; |
||||
|
||||
while (!do_exit) { |
||||
// receive message
|
||||
struct sockaddr from; |
||||
socklen_t fromlen = sizeof(from); |
||||
int cnt = recvfrom(sock, (void *)buf, MAX_LIDAR_PACKET, 0, &from, &fromlen); |
||||
if (cnt <= 0) { |
||||
printf("bug in lidar recieve!\n"); |
||||
continue; |
||||
} |
||||
|
||||
// create message for log
|
||||
capnp::MallocMessageBuilder msg; |
||||
auto event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
auto lidar_pts = event.initLidarPts(); |
||||
|
||||
// copy in the buffer
|
||||
// TODO: can we remove this copy? does it matter?
|
||||
kj::ArrayPtr<capnp::byte> bufferPtr = kj::arrayPtr(buf, cnt); |
||||
lidar_pts.setPkt(bufferPtr); |
||||
|
||||
// log it
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
logger_log(&s.logger, bytes.begin(), bytes.size()); |
||||
} |
||||
return 0; |
||||
} |
||||
#endif |
||||
|
||||
} |
||||
|
||||
void append_property(const char* key, const char* value, void *cookie) { |
||||
std::vector<std::pair<std::string, std::string> > *properties = |
||||
(std::vector<std::pair<std::string, std::string> > *)cookie; |
||||
|
||||
properties->push_back(std::make_pair(std::string(key), std::string(value))); |
||||
} |
||||
|
||||
kj::Array<capnp::word> gen_init_data() { |
||||
capnp::MallocMessageBuilder msg; |
||||
auto event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
auto init = event.initInitData(); |
||||
|
||||
init.setDeviceType(cereal::InitData::DeviceType::NEO); |
||||
init.setVersion(capnp::Text::Reader(COMMA_VERSION)); |
||||
|
||||
std::ifstream cmdline_stream("/proc/cmdline"); |
||||
std::vector<std::string> kernel_args; |
||||
std::string buf; |
||||
while (cmdline_stream >> buf) { |
||||
kernel_args.push_back(buf); |
||||
} |
||||
|
||||
auto lkernel_args = init.initKernelArgs(kernel_args.size()); |
||||
for (int i=0; i<kernel_args.size(); i++) { |
||||
lkernel_args.set(i, kernel_args[i]); |
||||
} |
||||
|
||||
init.setKernelVersion(util::read_file("/proc/version")); |
||||
|
||||
#ifdef QCOM |
||||
{ |
||||
std::vector<std::pair<std::string, std::string> > properties; |
||||
property_list(append_property, (void*)&properties); |
||||
|
||||
auto lentries = init.initAndroidProperties().initEntries(properties.size()); |
||||
for (int i=0; i<properties.size(); i++) { |
||||
auto lentry = lentries[i]; |
||||
lentry.setKey(properties[i].first); |
||||
lentry.setValue(properties[i].second); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
const char* dongle_id = getenv("DONGLE_ID"); |
||||
if (dongle_id) { |
||||
init.setDongleId(std::string(dongle_id)); |
||||
} |
||||
|
||||
const char* clean = getenv("CLEAN"); |
||||
if (!clean) { |
||||
init.setDirty(true); |
||||
} |
||||
|
||||
char* git_commit = NULL; |
||||
read_db_value(NULL, "GitCommit", &git_commit, NULL); |
||||
if (git_commit) { |
||||
init.setGitCommit(capnp::Text::Reader(git_commit)); |
||||
} |
||||
|
||||
char* git_branch = NULL; |
||||
read_db_value(NULL, "GitBranch", &git_branch, NULL); |
||||
if (git_branch) { |
||||
init.setGitBranch(capnp::Text::Reader(git_branch)); |
||||
} |
||||
|
||||
char* git_remote = NULL; |
||||
read_db_value(NULL, "GitRemote", &git_remote, NULL); |
||||
if (git_remote) { |
||||
init.setGitRemote(capnp::Text::Reader(git_remote)); |
||||
} |
||||
|
||||
char* passive = NULL; |
||||
read_db_value(NULL, "Passive", &passive, NULL); |
||||
init.setPassive(passive && strlen(passive) && passive[0] == '1'); |
||||
|
||||
|
||||
{ |
||||
// log params
|
||||
std::map<std::string, std::string> params; |
||||
read_db_all(NULL, ¶ms); |
||||
auto lparams = init.initParams().initEntries(params.size()); |
||||
int i = 0; |
||||
for (auto& kv : params) { |
||||
auto lentry = lparams[i]; |
||||
lentry.setKey(kv.first); |
||||
lentry.setValue(kv.second); |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
|
||||
if (git_commit) { |
||||
free((void*)git_commit); |
||||
} |
||||
|
||||
if (git_branch) { |
||||
free((void*)git_branch); |
||||
} |
||||
|
||||
if (git_remote) { |
||||
free((void*)git_remote); |
||||
} |
||||
|
||||
if (passive) { |
||||
free((void*)passive); |
||||
} |
||||
|
||||
return words; |
||||
} |
||||
|
||||
static int clear_locks_fn(const char* fpath, const struct stat *sb, int tyupeflag) { |
||||
const char* dot = strrchr(fpath, '.'); |
||||
if (dot && strcmp(dot, ".lock") == 0) { |
||||
unlink(fpath); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static void clear_locks() { |
||||
ftw(LOG_ROOT, clear_locks_fn, 16); |
||||
} |
||||
|
||||
static void bootlog() { |
||||
int err; |
||||
|
||||
{ |
||||
auto words = gen_init_data(); |
||||
auto bytes = words.asBytes(); |
||||
logger_init(&s.logger, "bootlog", bytes.begin(), bytes.size(), false); |
||||
} |
||||
|
||||
err = logger_next(&s.logger, LOG_ROOT, s.segment_path, sizeof(s.segment_path), &s.rotate_segment); |
||||
assert(err == 0); |
||||
LOGW("bootlog to %s", s.segment_path); |
||||
|
||||
{ |
||||
capnp::MallocMessageBuilder msg; |
||||
auto event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
|
||||
auto boot = event.initBoot(); |
||||
|
||||
boot.setWallTimeNanos(nanos_since_epoch()); |
||||
|
||||
std::string lastKmsg = util::read_file("/sys/fs/pstore/console-ramoops"); |
||||
boot.setLastKmsg(capnp::Data::Reader((const kj::byte*)lastKmsg.data(), lastKmsg.size())); |
||||
|
||||
std::string lastPmsg = util::read_file("/sys/fs/pstore/pmsg-ramoops-0"); |
||||
boot.setLastPmsg(capnp::Data::Reader((const kj::byte*)lastPmsg.data(), lastPmsg.size())); |
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
logger_log(&s.logger, bytes.begin(), bytes.size(), false); |
||||
} |
||||
|
||||
logger_close(&s.logger); |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
int err; |
||||
|
||||
if (argc > 1 && strcmp(argv[1], "--bootlog") == 0) { |
||||
bootlog(); |
||||
return 0; |
||||
} |
||||
|
||||
setpriority(PRIO_PROCESS, 0, -12); |
||||
|
||||
clear_locks(); |
||||
|
||||
signal(SIGINT, (sighandler_t)set_do_exit); |
||||
signal(SIGTERM, (sighandler_t)set_do_exit); |
||||
|
||||
s.ctx = Context::create(); |
||||
Poller * poller = Poller::create(); |
||||
|
||||
std::string exe_dir = util::dir_name(util::readlink("/proc/self/exe")); |
||||
std::string service_list_path = exe_dir + "/../../cereal/service_list.yaml"; |
||||
|
||||
// subscribe to all services
|
||||
|
||||
SubSocket *frame_sock = NULL; |
||||
std::vector<SubSocket*> socks; |
||||
|
||||
std::map<SubSocket*, int> qlog_counter; |
||||
std::map<SubSocket*, int> qlog_freqs; |
||||
|
||||
YAML::Node service_list = YAML::LoadFile(service_list_path); |
||||
for (const auto& it : service_list) { |
||||
auto name = it.first.as<std::string>(); |
||||
bool should_log = it.second[1].as<bool>(); |
||||
int qlog_freq = it.second[3] ? it.second[3].as<int>() : 0; |
||||
|
||||
if (should_log) { |
||||
SubSocket * sock = SubSocket::create(s.ctx, name); |
||||
assert(sock != NULL); |
||||
|
||||
poller->registerSocket(sock); |
||||
socks.push_back(sock); |
||||
|
||||
if (name == "frame") { |
||||
frame_sock = sock; |
||||
} |
||||
|
||||
qlog_counter[sock] = (qlog_freq == 0) ? -1 : 0; |
||||
qlog_freqs[sock] = qlog_freq; |
||||
} |
||||
} |
||||
|
||||
|
||||
{ |
||||
auto words = gen_init_data(); |
||||
auto bytes = words.asBytes(); |
||||
logger_init(&s.logger, "rlog", bytes.begin(), bytes.size(), true); |
||||
} |
||||
|
||||
bool is_streaming = false; |
||||
bool is_logging = true; |
||||
|
||||
if (argc > 1 && strcmp(argv[1], "--stream") == 0) { |
||||
is_streaming = true; |
||||
} else if (argc > 1 && strcmp(argv[1], "--only-stream") == 0) { |
||||
is_streaming = true; |
||||
is_logging = false; |
||||
} |
||||
|
||||
if (is_logging) { |
||||
err = logger_next(&s.logger, LOG_ROOT, s.segment_path, sizeof(s.segment_path), &s.rotate_segment); |
||||
assert(err == 0); |
||||
LOGW("logging to %s", s.segment_path); |
||||
} |
||||
|
||||
double start_ts = seconds_since_boot(); |
||||
double last_rotate_ts = start_ts; |
||||
|
||||
#ifndef DISABLE_ENCODER |
||||
// rear camera
|
||||
std::thread encoder_thread_handle(encoder_thread, is_streaming, false, false); |
||||
|
||||
// front camera
|
||||
std::thread front_encoder_thread_handle(encoder_thread, false, false, true); |
||||
#endif |
||||
|
||||
#if ENABLE_LIDAR |
||||
std::thread lidar_thread_handle(lidar_thread); |
||||
#endif |
||||
|
||||
uint64_t msg_count = 0; |
||||
uint64_t bytes_count = 0; |
||||
|
||||
while (!do_exit) { |
||||
for (auto sock : poller->poll(100 * 1000)){ |
||||
while (true) { |
||||
Message * msg = sock->receive(true); |
||||
if (msg == NULL){ |
||||
break; |
||||
} |
||||
|
||||
uint8_t* data = (uint8_t*)msg->getData(); |
||||
size_t len = msg->getSize(); |
||||
|
||||
if (sock == frame_sock) { |
||||
// track camera frames to sync to encoder
|
||||
auto amsg = kj::heapArray<capnp::word>((len / sizeof(capnp::word)) + 1); |
||||
memcpy(amsg.begin(), data, len); |
||||
|
||||
capnp::FlatArrayMessageReader cmsg(amsg); |
||||
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>(); |
||||
if (event.isFrame()) { |
||||
std::unique_lock<std::mutex> lk(s.lock); |
||||
s.last_frame_id = event.getFrame().getFrameId(); |
||||
lk.unlock(); |
||||
s.cv.notify_all(); |
||||
} |
||||
} |
||||
|
||||
logger_log(&s.logger, data, len, qlog_counter[sock] == 0); |
||||
delete msg; |
||||
|
||||
if (qlog_counter[sock] != -1) { |
||||
//printf("%p: %d/%d\n", socks[i], qlog_counter[socks[i]], qlog_freqs[socks[i]]);
|
||||
qlog_counter[sock]++; |
||||
qlog_counter[sock] %= qlog_freqs[sock]; |
||||
} |
||||
|
||||
bytes_count += len; |
||||
msg_count++; |
||||
} |
||||
} |
||||
|
||||
double ts = seconds_since_boot(); |
||||
if (ts - last_rotate_ts > SEGMENT_LENGTH) { |
||||
// rotate the log
|
||||
|
||||
last_rotate_ts += SEGMENT_LENGTH; |
||||
|
||||
std::lock_guard<std::mutex> guard(s.lock); |
||||
s.rotate_last_frame_id = s.last_frame_id; |
||||
|
||||
if (is_logging) { |
||||
err = logger_next(&s.logger, LOG_ROOT, s.segment_path, sizeof(s.segment_path), &s.rotate_segment); |
||||
assert(err == 0); |
||||
LOGW("rotated to %s", s.segment_path); |
||||
} |
||||
} |
||||
|
||||
if ((msg_count%1000) == 0) { |
||||
LOGD("%lu messages, %.2f msg/sec, %.2f KB/sec", msg_count, msg_count*1.0/(ts-start_ts), bytes_count*0.001/(ts-start_ts)); |
||||
} |
||||
} |
||||
|
||||
LOGW("joining threads"); |
||||
s.cv.notify_all(); |
||||
|
||||
|
||||
#ifndef DISABLE_ENCODER |
||||
front_encoder_thread_handle.join(); |
||||
encoder_thread_handle.join(); |
||||
LOGW("encoder joined"); |
||||
#endif |
||||
|
||||
#if ENABLE_LIDAR |
||||
lidar_thread_handle.join(); |
||||
LOGW("lidar joined"); |
||||
#endif |
||||
|
||||
logger_close(&s.logger); |
||||
|
||||
for (auto s : socks){ |
||||
delete s; |
||||
} |
||||
|
||||
delete s.ctx; |
||||
return 0; |
||||
} |
@ -0,0 +1,163 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <cassert> |
||||
|
||||
#include <fcntl.h> |
||||
#include <unistd.h> |
||||
|
||||
#define __STDC_CONSTANT_MACROS |
||||
|
||||
extern "C" { |
||||
#include <libavutil/imgutils.h> |
||||
#include <libavcodec/avcodec.h> |
||||
#include <libavformat/avformat.h> |
||||
} |
||||
|
||||
#include "common/swaglog.h" |
||||
#include "common/utilpp.h" |
||||
|
||||
#include "raw_logger.h" |
||||
|
||||
RawLogger::RawLogger(const std::string &afilename, int awidth, int aheight, int afps) |
||||
: filename(afilename), |
||||
width(awidth), |
||||
height(aheight), |
||||
fps(afps) { |
||||
|
||||
int err = 0; |
||||
|
||||
av_register_all(); |
||||
codec = avcodec_find_encoder(AV_CODEC_ID_FFVHUFF); |
||||
// codec = avcodec_find_encoder(AV_CODEC_ID_FFV1);
|
||||
assert(codec); |
||||
|
||||
codec_ctx = avcodec_alloc_context3(codec); |
||||
assert(codec_ctx); |
||||
codec_ctx->width = width; |
||||
codec_ctx->height = height; |
||||
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; |
||||
|
||||
// codec_ctx->thread_count = 2;
|
||||
|
||||
// ffv1enc doesn't respect AV_PICTURE_TYPE_I. make every frame a key frame for now.
|
||||
// codec_ctx->gop_size = 0;
|
||||
|
||||
codec_ctx->time_base = (AVRational){ 1, fps }; |
||||
|
||||
err = avcodec_open2(codec_ctx, codec, NULL); |
||||
assert(err >= 0); |
||||
|
||||
frame = av_frame_alloc(); |
||||
assert(frame); |
||||
frame->format = codec_ctx->pix_fmt; |
||||
frame->width = width; |
||||
frame->height = height; |
||||
frame->linesize[0] = width; |
||||
frame->linesize[1] = width/2; |
||||
frame->linesize[2] = width/2; |
||||
} |
||||
|
||||
RawLogger::~RawLogger() { |
||||
av_frame_free(&frame); |
||||
avcodec_close(codec_ctx); |
||||
av_free(codec_ctx); |
||||
} |
||||
|
||||
void RawLogger::Open(const std::string &path) { |
||||
int err = 0; |
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(lock); |
||||
|
||||
vid_path = util::string_format("%s/%s.mkv", path.c_str(), filename.c_str()); |
||||
|
||||
// create camera lock file
|
||||
lock_path = util::string_format("%s/%s.lock", path.c_str(), filename.c_str()); |
||||
|
||||
LOG("open %s\n", lock_path.c_str()); |
||||
|
||||
int lock_fd = open(lock_path.c_str(), O_RDWR | O_CREAT, 0777); |
||||
assert(lock_fd >= 0); |
||||
close(lock_fd); |
||||
|
||||
format_ctx = NULL; |
||||
avformat_alloc_output_context2(&format_ctx, NULL, NULL, vid_path.c_str()); |
||||
assert(format_ctx); |
||||
|
||||
stream = avformat_new_stream(format_ctx, codec); |
||||
// AVStream *stream = avformat_new_stream(format_ctx, NULL);
|
||||
assert(stream); |
||||
stream->id = 0; |
||||
stream->time_base = (AVRational){ 1, fps }; |
||||
// codec_ctx->time_base = stream->time_base;
|
||||
|
||||
err = avcodec_parameters_from_context(stream->codecpar, codec_ctx); |
||||
assert(err >= 0); |
||||
|
||||
err = avio_open(&format_ctx->pb, vid_path.c_str(), AVIO_FLAG_WRITE); |
||||
assert(err >= 0); |
||||
|
||||
err = avformat_write_header(format_ctx, NULL); |
||||
assert(err >= 0); |
||||
|
||||
is_open = true; |
||||
counter = 0; |
||||
} |
||||
|
||||
void RawLogger::Close() { |
||||
int err = 0; |
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(lock); |
||||
|
||||
if (!is_open) return; |
||||
|
||||
err = av_write_trailer(format_ctx); |
||||
assert(err == 0); |
||||
|
||||
avcodec_close(stream->codec); |
||||
|
||||
err = avio_closep(&format_ctx->pb); |
||||
assert(err == 0); |
||||
|
||||
avformat_free_context(format_ctx); |
||||
format_ctx = NULL; |
||||
|
||||
unlink(lock_path.c_str()); |
||||
is_open = false; |
||||
} |
||||
|
||||
int RawLogger::ProcessFrame(uint64_t ts, const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr) { |
||||
int err = 0; |
||||
|
||||
AVPacket pkt; |
||||
av_init_packet(&pkt); |
||||
pkt.data = NULL; |
||||
pkt.size = 0; |
||||
|
||||
frame->data[0] = (uint8_t*)y_ptr; |
||||
frame->data[1] = (uint8_t*)u_ptr; |
||||
frame->data[2] = (uint8_t*)v_ptr; |
||||
frame->pts = ts; |
||||
|
||||
int ret = counter; |
||||
|
||||
int got_output = 0; |
||||
err = avcodec_encode_video2(codec_ctx, &pkt, frame, &got_output); |
||||
if (err) { |
||||
LOGE("encoding error\n"); |
||||
ret = -1; |
||||
} else if (got_output) { |
||||
|
||||
av_packet_rescale_ts(&pkt, codec_ctx->time_base, stream->time_base); |
||||
pkt.stream_index = 0; |
||||
|
||||
err = av_interleaved_write_frame(format_ctx, &pkt); |
||||
if (err < 0) { |
||||
LOGE("encoder writer error\n"); |
||||
ret = -1; |
||||
} else { |
||||
counter++; |
||||
} |
||||
} |
||||
|
||||
return ret; |
||||
} |
@ -0,0 +1,43 @@ |
||||
#ifndef FFV1LOGGER_H |
||||
#define FFV1LOGGER_H |
||||
|
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
#include <mutex> |
||||
#include <condition_variable> |
||||
|
||||
extern "C" { |
||||
#include <libavutil/imgutils.h> |
||||
#include <libavcodec/avcodec.h> |
||||
#include <libavformat/avformat.h> |
||||
} |
||||
|
||||
#include "frame_logger.h" |
||||
|
||||
class RawLogger : public FrameLogger { |
||||
public: |
||||
RawLogger(const std::string &filename, int awidth, int aheight, int afps); |
||||
~RawLogger(); |
||||
|
||||
int ProcessFrame(uint64_t ts, const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr); |
||||
void Open(const std::string &path); |
||||
void Close(); |
||||
|
||||
private: |
||||
std::string filename; |
||||
int width, height, fps; |
||||
int counter = 0; |
||||
|
||||
AVCodec *codec = NULL; |
||||
AVCodecContext *codec_ctx = NULL; |
||||
|
||||
AVStream *stream = NULL; |
||||
AVFormatContext *format_ctx = NULL; |
||||
|
||||
AVFrame *frame = NULL; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,35 @@ |
||||
CC = clang
|
||||
CXX = clang++
|
||||
|
||||
PHONELIBS = ../../../phonelibs
|
||||
|
||||
WARN_FLAGS = -Werror=implicit-function-declaration \
|
||||
-Werror=incompatible-pointer-types \
|
||||
-Werror=int-conversion \
|
||||
-Werror=return-type \
|
||||
-Werror=format-extra-args \
|
||||
-Wno-deprecated-declarations
|
||||
|
||||
CFLAGS = -std=gnu11 -g -fPIC -O2 $(WARN_FLAGS)
|
||||
CXXFLAGS = -std=c++11 -g -fPIC -O2 $(WARN_FLAGS)
|
||||
|
||||
FFMPEG_LIBS = -lavformat \
|
||||
-lavcodec \
|
||||
-lswscale \
|
||||
-lavutil \
|
||||
-lz
|
||||
|
||||
OBJS = testraw.o \
|
||||
../RawLogger.o \
|
||||
../../common/visionipc.o
|
||||
|
||||
testraw: $(OBJS) |
||||
$(CXX) -fPIC -o '$@' $^ -L/usr/lib $(FFMPEG_LIBS)
|
||||
|
||||
%.o: %.cc |
||||
@echo "[ CXX ] $@"
|
||||
$(CXX) $(CXXFLAGS) \
|
||||
-I../ \
|
||||
-I../../ \
|
||||
-I../../../ \
|
||||
-c -o '$@' '$<'
|
@ -0,0 +1,26 @@ |
||||
#!/usr/bin/env python3 |
||||
"""Script to fill up EON with fake data""" |
||||
|
||||
import os |
||||
|
||||
from selfdrive.loggerd.config import ROOT, get_available_percent |
||||
from selfdrive.loggerd.tests.loggerd_tests_common import create_random_file |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
segment_idx = 0 |
||||
while True: |
||||
seg_name = "1970-01-01--00-00-00--%d" % segment_idx |
||||
seg_path = os.path.join(ROOT, seg_name) |
||||
|
||||
print(seg_path) |
||||
|
||||
create_random_file(os.path.join(seg_path, 'fcamera.hevc'), 36) |
||||
create_random_file(os.path.join(seg_path, 'rlog.bz2'), 2) |
||||
|
||||
segment_idx += 1 |
||||
|
||||
# Fill up to 99 percent |
||||
available_percent = get_available_percent() |
||||
if available_percent < 1.0: |
||||
break |
@ -0,0 +1,81 @@ |
||||
import os |
||||
import errno |
||||
import shutil |
||||
import random |
||||
import tempfile |
||||
import unittest |
||||
|
||||
import selfdrive.loggerd.uploader as uploader |
||||
|
||||
def create_random_file(file_path, size_mb, lock=False): |
||||
try: |
||||
os.mkdir(os.path.dirname(file_path)) |
||||
except OSError: |
||||
pass |
||||
|
||||
lock_path = file_path + ".lock" |
||||
os.close(os.open(lock_path, os.O_CREAT | os.O_EXCL)) |
||||
|
||||
chunks = 128 |
||||
chunk_bytes = int(size_mb * 1024 * 1024 / chunks) |
||||
data = os.urandom(chunk_bytes) |
||||
|
||||
with open(file_path, 'wb') as f: |
||||
for _ in range(chunks): |
||||
f.write(data) |
||||
|
||||
if not lock: |
||||
os.remove(lock_path) |
||||
|
||||
class MockResponse(): |
||||
def __init__(self, text): |
||||
self.text = text |
||||
|
||||
class MockApi(): |
||||
def __init__(self, dongle_id): |
||||
pass |
||||
|
||||
def get(self, *args, **kwargs): |
||||
return MockResponse('{"url": "http://localhost/does/not/exist", "headers": {}}') |
||||
|
||||
def get_token(self): |
||||
return "fake-token" |
||||
|
||||
class MockParams(): |
||||
def __init__(self): |
||||
self.params = { |
||||
"DongleId": b"0000000000000000", |
||||
"IsUploadRawEnabled": b"1", |
||||
} |
||||
|
||||
def get(self, k): |
||||
return self.params[k] |
||||
|
||||
class UploaderTestCase(unittest.TestCase): |
||||
f_type = "UNKNOWN" |
||||
|
||||
def setUp(self): |
||||
self.root = tempfile.mkdtemp() |
||||
uploader.ROOT = self.root # Monkey patch root dir |
||||
uploader.Api = MockApi |
||||
uploader.Params = MockParams |
||||
uploader.fake_upload = 1 |
||||
uploader.is_on_hotspot = lambda *args: False |
||||
uploader.is_on_wifi = lambda *args: True |
||||
self.seg_num = random.randint(1, 300) |
||||
self.seg_format = "2019-04-18--12-52-54--{}" |
||||
self.seg_format2 = "2019-05-18--11-22-33--{}" |
||||
self.seg_dir = self.seg_format.format(self.seg_num) |
||||
|
||||
def tearDown(self): |
||||
try: |
||||
shutil.rmtree(self.root) |
||||
except OSError as e: |
||||
if e.errno != errno.ENOENT: |
||||
raise |
||||
|
||||
def make_file_with_data(self, f_dir, fn, size_mb=.1, lock=False): |
||||
file_path = os.path.join(self.root, f_dir, fn) |
||||
create_random_file(file_path, size_mb, lock) |
||||
|
||||
return file_path |
@ -0,0 +1,108 @@ |
||||
import os |
||||
import time |
||||
import threading |
||||
import unittest |
||||
from collections import namedtuple |
||||
|
||||
import selfdrive.loggerd.deleter as deleter |
||||
from common.timeout import Timeout, TimeoutException |
||||
|
||||
from selfdrive.loggerd.tests.loggerd_tests_common import UploaderTestCase |
||||
|
||||
Stats = namedtuple("Stats", ['f_bavail', 'f_blocks', 'f_frsize']) |
||||
|
||||
|
||||
class TestDeleter(UploaderTestCase): |
||||
def fake_statvfs(self, d): |
||||
return self.fake_stats |
||||
|
||||
def setUp(self): |
||||
self.f_type = "fcamera.hevc" |
||||
super(TestDeleter, self).setUp() |
||||
self.fake_stats = Stats(f_bavail=0, f_blocks=10, f_frsize=4096) |
||||
deleter.os.statvfs = self.fake_statvfs |
||||
deleter.ROOT = self.root |
||||
|
||||
def tearDown(self): |
||||
super(TestDeleter, self).tearDown() |
||||
|
||||
def start_thread(self): |
||||
self.end_event = threading.Event() |
||||
self.del_thread = threading.Thread(target=deleter.deleter_thread, args=[self.end_event]) |
||||
self.del_thread.daemon = True |
||||
self.del_thread.start() |
||||
|
||||
def join_thread(self): |
||||
self.end_event.set() |
||||
self.del_thread.join() |
||||
|
||||
def test_delete(self): |
||||
f_path = self.make_file_with_data(self.seg_dir, self.f_type, 1) |
||||
|
||||
self.start_thread() |
||||
|
||||
with Timeout(5, "Timeout waiting for file to be deleted"): |
||||
while os.path.exists(f_path): |
||||
time.sleep(0.01) |
||||
self.join_thread() |
||||
|
||||
self.assertFalse(os.path.exists(f_path), "File not deleted") |
||||
|
||||
def test_delete_files_in_create_order(self): |
||||
f_path_1 = self.make_file_with_data(self.seg_dir, self.f_type) |
||||
time.sleep(1) |
||||
self.seg_num += 1 |
||||
self.seg_dir = self.seg_format.format(self.seg_num) |
||||
f_path_2 = self.make_file_with_data(self.seg_dir, self.f_type) |
||||
|
||||
self.start_thread() |
||||
|
||||
with Timeout(5, "Timeout waiting for file to be deleted"): |
||||
while os.path.exists(f_path_1) and os.path.exists(f_path_2): |
||||
time.sleep(0.01) |
||||
|
||||
self.join_thread() |
||||
|
||||
self.assertFalse(os.path.exists(f_path_1), "Older file not deleted") |
||||
|
||||
self.assertTrue(os.path.exists(f_path_2), "Newer file deleted before older file") |
||||
|
||||
def test_no_delete_when_available_space(self): |
||||
f_path = self.make_file_with_data(self.seg_dir, self.f_type) |
||||
|
||||
block_size = 4096 |
||||
available = (10 * 1024 * 1024 * 1024) / block_size # 10GB free |
||||
self.fake_stats = Stats(f_bavail=available, f_blocks=10, f_frsize=block_size) |
||||
|
||||
self.start_thread() |
||||
|
||||
try: |
||||
with Timeout(2, "Timeout waiting for file to be deleted"): |
||||
while os.path.exists(f_path): |
||||
time.sleep(0.01) |
||||
except TimeoutException: |
||||
pass |
||||
finally: |
||||
self.join_thread() |
||||
|
||||
self.assertTrue(os.path.exists(f_path), "File deleted with available space") |
||||
|
||||
def test_no_delete_with_lock_file(self): |
||||
f_path = self.make_file_with_data(self.seg_dir, self.f_type, lock=True) |
||||
|
||||
self.start_thread() |
||||
|
||||
try: |
||||
with Timeout(2, "Timeout waiting for file to be deleted"): |
||||
while os.path.exists(f_path): |
||||
time.sleep(0.01) |
||||
except TimeoutException: |
||||
pass |
||||
finally: |
||||
self.join_thread() |
||||
|
||||
self.assertTrue(os.path.exists(f_path), "File deleted when locked") |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
unittest.main() |
@ -0,0 +1,116 @@ |
||||
import os |
||||
import time |
||||
import threading |
||||
import logging |
||||
import json |
||||
|
||||
from selfdrive.swaglog import cloudlog |
||||
import selfdrive.loggerd.uploader as uploader |
||||
|
||||
from common.timeout import Timeout |
||||
|
||||
from selfdrive.loggerd.tests.loggerd_tests_common import UploaderTestCase |
||||
|
||||
class TestLogHandler(logging.Handler): |
||||
def __init__(self): |
||||
logging.Handler.__init__(self) |
||||
self.reset() |
||||
|
||||
def reset(self): |
||||
self.upload_order = list() |
||||
|
||||
def emit(self, record): |
||||
try: |
||||
j = json.loads(record.message) |
||||
if j["event"] == "upload_success": |
||||
self.upload_order.append(j["key"]) |
||||
except BaseException: |
||||
pass |
||||
|
||||
log_handler = TestLogHandler() |
||||
cloudlog.addHandler(log_handler) |
||||
|
||||
class TestUploader(UploaderTestCase): |
||||
def setUp(self): |
||||
super(TestUploader, self).setUp() |
||||
log_handler.reset() |
||||
|
||||
def tearDown(self): |
||||
super(TestUploader, self).tearDown() |
||||
|
||||
def start_thread(self): |
||||
self.end_event = threading.Event() |
||||
self.up_thread = threading.Thread(target=uploader.uploader_fn, args=[self.end_event]) |
||||
self.up_thread.daemon = True |
||||
self.up_thread.start() |
||||
|
||||
def join_thread(self): |
||||
self.end_event.set() |
||||
self.up_thread.join() |
||||
|
||||
def gen_files(self, lock=False): |
||||
f_paths = list() |
||||
for t in ["bootlog.bz2", "qlog.bz2", "rlog.bz2", "dcamera.hevc", "fcamera.hevc"]: |
||||
f_paths.append(self.make_file_with_data(self.seg_dir, t, 1, lock=lock)) |
||||
return f_paths |
||||
|
||||
def gen_order(self, seg1, seg2): |
||||
keys = [f"{self.seg_format.format(i)}/qlog.bz2" for i in seg1] |
||||
keys += [f"{self.seg_format2.format(i)}/qlog.bz2" for i in seg2] |
||||
for i in seg1: |
||||
keys += [f"{self.seg_format.format(i)}/{f}" for f in ['rlog.bz2','fcamera.hevc','dcamera.hevc']] |
||||
for i in seg2: |
||||
keys += [f"{self.seg_format2.format(i)}/{f}" for f in ['rlog.bz2','fcamera.hevc','dcamera.hevc']] |
||||
keys += [f"{self.seg_format.format(i)}/bootlog.bz2" for i in seg1] |
||||
keys += [f"{self.seg_format2.format(i)}/bootlog.bz2" for i in seg2] |
||||
return keys |
||||
|
||||
def test_upload(self): |
||||
f_paths = self.gen_files(lock=False) |
||||
|
||||
self.start_thread() |
||||
|
||||
with Timeout(5, "Timeout waiting for file to be uploaded"): |
||||
while len(os.listdir(self.root)): |
||||
time.sleep(0.01) |
||||
self.join_thread() |
||||
|
||||
for f_path in f_paths: |
||||
self.assertFalse(os.path.exists(f_path), "All files not uploaded") |
||||
exp_order = self.gen_order([self.seg_num], []) |
||||
self.assertTrue(log_handler.upload_order == exp_order, "Files uploaded in wrong order") |
||||
|
||||
def test_upload_files_in_create_order(self): |
||||
f_paths = list() |
||||
seg1_nums = [0,1,2,10,20] |
||||
for i in seg1_nums: |
||||
self.seg_dir = self.seg_format.format(i) |
||||
f_paths += self.gen_files() |
||||
seg2_nums = [5,50,51] |
||||
for i in seg2_nums: |
||||
self.seg_dir = self.seg_format2.format(i) |
||||
f_paths += self.gen_files() |
||||
|
||||
self.start_thread() |
||||
|
||||
with Timeout(5, "Timeout waiting for file to be upload"): |
||||
while len(os.listdir(self.root)): |
||||
time.sleep(0.01) |
||||
|
||||
self.join_thread() |
||||
|
||||
for f_path in f_paths: |
||||
self.assertFalse(os.path.exists(f_path), "All files not uploaded") |
||||
exp_order = self.gen_order(seg1_nums, seg2_nums) |
||||
self.assertTrue(log_handler.upload_order == exp_order, "Files uploaded in wrong order") |
||||
|
||||
def test_no_upload_with_lock_file(self): |
||||
f_paths = self.gen_files(lock=True) |
||||
|
||||
self.start_thread() |
||||
# allow enough time that files should have been uploaded if they would be uploaded |
||||
time.sleep(5) |
||||
self.join_thread() |
||||
|
||||
for f_path in f_paths: |
||||
self.assertTrue(os.path.exists(f_path), "File upload when locked") |
@ -0,0 +1,57 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <unistd.h> |
||||
|
||||
#include <zmq.h> |
||||
|
||||
#include "common/visionipc.h" |
||||
#include "common/timing.h" |
||||
|
||||
#include "RawLogger.h" |
||||
|
||||
int main() { |
||||
int err; |
||||
|
||||
VisionStream stream; |
||||
|
||||
VisionStreamBufs buf_info; |
||||
while (true) { |
||||
err = visionstream_init(&stream, VISION_STREAM_YUV, false, &buf_info); |
||||
if (err != 0) { |
||||
printf("visionstream fail\n"); |
||||
usleep(100000); |
||||
} |
||||
break; |
||||
} |
||||
|
||||
RawLogger vidlogger("prcamera", buf_info.width, buf_info.height, 20); |
||||
vidlogger.Open("o1"); |
||||
|
||||
for (int cnt=0; cnt<200; cnt++) { |
||||
VIPCBufExtra extra; |
||||
VIPSBuf* buf = visionstream_get(&stream, &extra); |
||||
if (buf == NULL) { |
||||
printf("visionstream get failed\n"); |
||||
break; |
||||
} |
||||
|
||||
if (cnt == 100) { |
||||
vidlogger.Rotate("o2", 2); |
||||
} |
||||
|
||||
uint8_t *y = (uint8_t*)buf->addr; |
||||
uint8_t *u = y + (buf_info.width*buf_info.height); |
||||
uint8_t *v = u + (buf_info.width/2)*(buf_info.height/2); |
||||
|
||||
double t1 = millis_since_boot(); |
||||
vidlogger.LogFrame(cnt, y, u, v, NULL); |
||||
double t2 = millis_since_boot(); |
||||
printf("%d %.2f\n", cnt, (t2-t1)); |
||||
} |
||||
|
||||
vidlogger.Close(); |
||||
|
||||
visionstream_destroy(&stream); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,271 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import re |
||||
import time |
||||
import json |
||||
import random |
||||
import ctypes |
||||
import inspect |
||||
import requests |
||||
import traceback |
||||
import threading |
||||
import subprocess |
||||
|
||||
from selfdrive.swaglog import cloudlog |
||||
from selfdrive.loggerd.config import ROOT |
||||
|
||||
from common import android |
||||
from common.params import Params |
||||
from common.api import Api |
||||
|
||||
fake_upload = os.getenv("FAKEUPLOAD") is not None |
||||
|
||||
def raise_on_thread(t, exctype): |
||||
for ctid, tobj in threading._active.items(): |
||||
if tobj is t: |
||||
tid = ctid |
||||
break |
||||
else: |
||||
raise Exception("Could not find thread") |
||||
|
||||
'''Raises an exception in the threads with id tid''' |
||||
if not inspect.isclass(exctype): |
||||
raise TypeError("Only types can be raised (not instances)") |
||||
|
||||
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), |
||||
ctypes.py_object(exctype)) |
||||
if res == 0: |
||||
raise ValueError("invalid thread id") |
||||
elif res != 1: |
||||
# "if it returns a number greater than one, you're in trouble, |
||||
# and you should call it again with exc=NULL to revert the effect" |
||||
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) |
||||
raise SystemError("PyThreadState_SetAsyncExc failed") |
||||
|
||||
def get_directory_sort(d): |
||||
return list(map(lambda s: s.rjust(10, '0'), d.rsplit('--', 1))) |
||||
|
||||
def listdir_by_creation(d): |
||||
try: |
||||
paths = os.listdir(d) |
||||
paths = sorted(paths, key=get_directory_sort) |
||||
return paths |
||||
except OSError: |
||||
cloudlog.exception("listdir_by_creation failed") |
||||
return list() |
||||
|
||||
def clear_locks(root): |
||||
for logname in os.listdir(root): |
||||
path = os.path.join(root, logname) |
||||
try: |
||||
for fname in os.listdir(path): |
||||
if fname.endswith(".lock"): |
||||
os.unlink(os.path.join(path, fname)) |
||||
except OSError: |
||||
cloudlog.exception("clear_locks failed") |
||||
|
||||
def is_on_wifi(): |
||||
# ConnectivityManager.getActiveNetworkInfo() |
||||
try: |
||||
result = android.parse_service_call_string(android.service_call(["connectivity", "2"])) |
||||
if result is None: |
||||
return True |
||||
return 'WIFI' in result |
||||
except AttributeError: |
||||
return False |
||||
|
||||
def is_on_hotspot(): |
||||
try: |
||||
result = subprocess.check_output(["ifconfig", "wlan0"], stderr=subprocess.STDOUT, encoding='utf8') |
||||
result = re.findall(r"inet addr:((\d+\.){3}\d+)", result)[0][0] |
||||
|
||||
is_android = result.startswith('192.168.43.') |
||||
is_ios = result.startswith('172.20.10.') |
||||
is_entune = result.startswith('10.0.2.') |
||||
|
||||
return (is_android or is_ios or is_entune) |
||||
except: |
||||
return False |
||||
|
||||
class Uploader(): |
||||
def __init__(self, dongle_id, root): |
||||
self.dongle_id = dongle_id |
||||
self.api = Api(dongle_id) |
||||
self.root = root |
||||
|
||||
self.upload_thread = None |
||||
|
||||
self.last_resp = None |
||||
self.last_exc = None |
||||
|
||||
self.immediate_priority = {"qlog.bz2": 0, "qcamera.ts": 1} |
||||
self.high_priority = {"rlog.bz2": 0, "fcamera.hevc": 1, "dcamera.hevc": 2} |
||||
|
||||
def clean_dirs(self): |
||||
try: |
||||
for logname in os.listdir(self.root): |
||||
path = os.path.join(self.root, logname) |
||||
# remove empty directories |
||||
if not os.listdir(path): |
||||
os.rmdir(path) |
||||
except OSError: |
||||
cloudlog.exception("clean_dirs failed") |
||||
|
||||
def get_upload_sort(self, name): |
||||
if name in self.immediate_priority: |
||||
return self.immediate_priority[name] |
||||
if name in self.high_priority: |
||||
return self.high_priority[name] + 100 |
||||
return 1000 |
||||
|
||||
def gen_upload_files(self): |
||||
if not os.path.isdir(self.root): |
||||
return |
||||
for logname in listdir_by_creation(self.root): |
||||
path = os.path.join(self.root, logname) |
||||
try: |
||||
names = os.listdir(path) |
||||
except OSError: |
||||
continue |
||||
if any(name.endswith(".lock") for name in names): |
||||
continue |
||||
|
||||
for name in sorted(names, key=self.get_upload_sort): |
||||
key = os.path.join(logname, name) |
||||
fn = os.path.join(path, name) |
||||
|
||||
yield (name, key, fn) |
||||
|
||||
def next_file_to_upload(self, with_raw): |
||||
upload_files = list(self.gen_upload_files()) |
||||
# try to upload qlog files first |
||||
for name, key, fn in upload_files: |
||||
if name in self.immediate_priority: |
||||
return (key, fn) |
||||
|
||||
if with_raw: |
||||
# then upload the full log files, rear and front camera files |
||||
for name, key, fn in upload_files: |
||||
if name in self.high_priority: |
||||
return (key, fn) |
||||
|
||||
# then upload other files |
||||
for name, key, fn in upload_files: |
||||
if not name.endswith('.lock') and not name.endswith(".tmp"): |
||||
return (key, fn) |
||||
|
||||
return None |
||||
|
||||
def do_upload(self, key, fn): |
||||
try: |
||||
url_resp = self.api.get("v1.3/"+self.dongle_id+"/upload_url/", timeout=10, path=key, access_token=self.api.get_token()) |
||||
url_resp_json = json.loads(url_resp.text) |
||||
url = url_resp_json['url'] |
||||
headers = url_resp_json['headers'] |
||||
cloudlog.info("upload_url v1.3 %s %s", url, str(headers)) |
||||
|
||||
if fake_upload: |
||||
cloudlog.info("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url) |
||||
class FakeResponse(): |
||||
def __init__(self): |
||||
self.status_code = 200 |
||||
self.last_resp = FakeResponse() |
||||
else: |
||||
with open(fn, "rb") as f: |
||||
self.last_resp = requests.put(url, data=f, headers=headers, timeout=10) |
||||
except Exception as e: |
||||
self.last_exc = (e, traceback.format_exc()) |
||||
raise |
||||
|
||||
def normal_upload(self, key, fn): |
||||
self.last_resp = None |
||||
self.last_exc = None |
||||
|
||||
try: |
||||
self.do_upload(key, fn) |
||||
except Exception: |
||||
pass |
||||
|
||||
return self.last_resp |
||||
|
||||
def upload(self, key, fn): |
||||
try: |
||||
sz = os.path.getsize(fn) |
||||
except OSError: |
||||
cloudlog.exception("upload: getsize failed") |
||||
return False |
||||
|
||||
cloudlog.event("upload", key=key, fn=fn, sz=sz) |
||||
|
||||
cloudlog.info("checking %r with size %r", key, sz) |
||||
|
||||
if sz == 0: |
||||
# can't upload files of 0 size |
||||
os.unlink(fn) # delete the file |
||||
success = True |
||||
else: |
||||
cloudlog.info("uploading %r", fn) |
||||
stat = self.normal_upload(key, fn) |
||||
if stat is not None and stat.status_code in (200, 201): |
||||
cloudlog.event("upload_success", key=key, fn=fn, sz=sz) |
||||
|
||||
# delete the file |
||||
try: |
||||
os.unlink(fn) |
||||
except OSError: |
||||
cloudlog.event("delete_failed", stat=stat, exc=self.last_exc, key=key, fn=fn, sz=sz) |
||||
|
||||
success = True |
||||
else: |
||||
cloudlog.event("upload_failed", stat=stat, exc=self.last_exc, key=key, fn=fn, sz=sz) |
||||
success = False |
||||
|
||||
self.clean_dirs() |
||||
|
||||
return success |
||||
|
||||
def uploader_fn(exit_event): |
||||
cloudlog.info("uploader_fn") |
||||
|
||||
params = Params() |
||||
dongle_id = params.get("DongleId").decode('utf8') |
||||
|
||||
if dongle_id is None: |
||||
cloudlog.info("uploader missing dongle_id") |
||||
raise Exception("uploader can't start without dongle id") |
||||
|
||||
uploader = Uploader(dongle_id, ROOT) |
||||
|
||||
backoff = 0.1 |
||||
while True: |
||||
allow_raw_upload = (params.get("IsUploadRawEnabled") != b"0") |
||||
on_hotspot = is_on_hotspot() |
||||
on_wifi = is_on_wifi() |
||||
should_upload = on_wifi and not on_hotspot |
||||
|
||||
if exit_event.is_set(): |
||||
return |
||||
|
||||
d = uploader.next_file_to_upload(with_raw=allow_raw_upload and should_upload) |
||||
if d is None: |
||||
time.sleep(5) |
||||
continue |
||||
|
||||
key, fn = d |
||||
|
||||
cloudlog.event("uploader_netcheck", is_on_hotspot=on_hotspot, is_on_wifi=on_wifi) |
||||
cloudlog.info("to upload %r", d) |
||||
success = uploader.upload(key, fn) |
||||
if success: |
||||
backoff = 0.1 |
||||
else: |
||||
cloudlog.info("backoff %r", backoff) |
||||
time.sleep(backoff + random.uniform(0, backoff)) |
||||
backoff = min(backoff*2, 120) |
||||
cloudlog.info("upload done, success=%r", success) |
||||
|
||||
def main(gctx=None): |
||||
uploader_fn(threading.Event()) |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -0,0 +1,106 @@ |
||||
{ |
||||
"_comment": "These speeds are from https://wiki.openstreetmap.org/wiki/Speed_limits Special cases have been stripped", |
||||
"AR:urban": "40", |
||||
"AR:urban:primary": "60", |
||||
"AR:urban:secondary": "60", |
||||
"AR:rural": "110", |
||||
"AT:urban": "50", |
||||
"AT:rural": "100", |
||||
"AT:trunk": "100", |
||||
"AT:motorway": "130", |
||||
"BE:urban": "50", |
||||
"BE-VLG:rural": "70", |
||||
"BE-WAL:rural": "90", |
||||
"BE:trunk": "120", |
||||
"BE:motorway": "120", |
||||
"CH:urban[1]": "50", |
||||
"CH:rural": "80", |
||||
"CH:trunk": "100", |
||||
"CH:motorway": "120", |
||||
"CZ:pedestrian_zone": "20", |
||||
"CZ:living_street": "20", |
||||
"CZ:urban": "50", |
||||
"CZ:urban_trunk": "80", |
||||
"CZ:urban_motorway": "80", |
||||
"CZ:rural": "90", |
||||
"CZ:trunk": "110", |
||||
"CZ:motorway": "130", |
||||
"DK:urban": "50", |
||||
"DK:rural": "80", |
||||
"DK:motorway": "130", |
||||
"DE:living_street": "7", |
||||
"DE:residential": "30", |
||||
"DE:urban": "50", |
||||
"DE:rural": "100", |
||||
"DE:trunk": "none", |
||||
"DE:motorway": "none", |
||||
"FI:urban": "50", |
||||
"FI:rural": "80", |
||||
"FI:trunk": "100", |
||||
"FI:motorway": "120", |
||||
"FR:urban": "50", |
||||
"FR:rural": "80", |
||||
"FR:trunk": "110", |
||||
"FR:motorway": "130", |
||||
"GR:urban": "50", |
||||
"GR:rural": "90", |
||||
"GR:trunk": "110", |
||||
"GR:motorway": "130", |
||||
"HU:urban": "50", |
||||
"HU:rural": "90", |
||||
"HU:trunk": "110", |
||||
"HU:motorway": "130", |
||||
"IT:urban": "50", |
||||
"IT:rural": "90", |
||||
"IT:trunk": "110", |
||||
"IT:motorway": "130", |
||||
"JP:national": "60", |
||||
"JP:motorway": "100", |
||||
"LT:living_street": "20", |
||||
"LT:urban": "50", |
||||
"LT:rural": "90", |
||||
"LT:trunk": "120", |
||||
"LT:motorway": "130", |
||||
"PL:living_street": "20", |
||||
"PL:urban": "50", |
||||
"PL:rural": "90", |
||||
"PL:trunk": "100", |
||||
"PL:motorway": "140", |
||||
"RO:urban": "50", |
||||
"RO:rural": "90", |
||||
"RO:trunk": "100", |
||||
"RO:motorway": "130", |
||||
"RU:living_street": "20", |
||||
"RU:urban": "60", |
||||
"RU:rural": "90", |
||||
"RU:motorway": "110", |
||||
"SK:urban": "50", |
||||
"SK:rural": "90", |
||||
"SK:trunk": "90", |
||||
"SK:motorway": "90", |
||||
"SI:urban": "50", |
||||
"SI:rural": "90", |
||||
"SI:trunk": "110", |
||||
"SI:motorway": "130", |
||||
"ES:living_street": "20", |
||||
"ES:urban": "50", |
||||
"ES:rural": "50", |
||||
"ES:trunk": "90", |
||||
"ES:motorway": "120", |
||||
"SE:urban": "50", |
||||
"SE:rural": "70", |
||||
"SE:trunk": "90", |
||||
"SE:motorway": "110", |
||||
"GB:nsl_restricted": "30 mph", |
||||
"GB:nsl_single": "60 mph", |
||||
"GB:nsl_dual": "70 mph", |
||||
"GB:motorway": "70 mph", |
||||
"UA:urban": "50", |
||||
"UA:rural": "90", |
||||
"UA:trunk": "110", |
||||
"UA:motorway": "130", |
||||
"UZ:living_street": "30", |
||||
"UZ:urban": "70", |
||||
"UZ:rural": "100", |
||||
"UZ:motorway": "110" |
||||
} |
@ -0,0 +1,240 @@ |
||||
#!/usr/bin/env python3 |
||||
import json |
||||
|
||||
DEFAULT_OUTPUT_FILENAME = "default_speeds_by_region.json" |
||||
|
||||
def main(filename = DEFAULT_OUTPUT_FILENAME): |
||||
countries = [] |
||||
|
||||
""" |
||||
-------------------------------------------------- |
||||
US - United State of America |
||||
-------------------------------------------------- |
||||
""" |
||||
US = Country("US") # First step, create the country using the ISO 3166 two letter code |
||||
countries.append(US) # Second step, add the country to countries list |
||||
|
||||
""" Default rules """ |
||||
# Third step, add some default rules for the country |
||||
# Speed limit rules are based on OpenStreetMaps (OSM) tags. |
||||
# The dictionary {...} defines the tag_name: value |
||||
# if a road in OSM has a tag with the name tag_name and this value, the speed limit listed below will be applied. |
||||
# The text at the end is the speed limit (use no unit for km/h) |
||||
# Rules apply in the order in which they are written for each country |
||||
# Rules for specific regions (states) take priority over country rules |
||||
# If you modify existing country rules, you must update all existing states without that rule to use the old rule |
||||
US.add_rule({"highway": "motorway"}, "65 mph") # On US roads with the tag highway and value motorway, the speed limit will default to 65 mph |
||||
US.add_rule({"highway": "trunk"}, "55 mph") |
||||
US.add_rule({"highway": "primary"}, "55 mph") |
||||
US.add_rule({"highway": "secondary"}, "45 mph") |
||||
US.add_rule({"highway": "tertiary"}, "35 mph") |
||||
US.add_rule({"highway": "unclassified"}, "55 mph") |
||||
US.add_rule({"highway": "residential"}, "25 mph") |
||||
US.add_rule({"highway": "service"}, "25 mph") |
||||
US.add_rule({"highway": "motorway_link"}, "55 mph") |
||||
US.add_rule({"highway": "trunk_link"}, "55 mph") |
||||
US.add_rule({"highway": "primary_link"}, "55 mph") |
||||
US.add_rule({"highway": "secondary_link"}, "45 mph") |
||||
US.add_rule({"highway": "tertiary_link"}, "35 mph") |
||||
US.add_rule({"highway": "living_street"}, "15 mph") |
||||
|
||||
""" States """ |
||||
new_york = US.add_region("New York") # Fourth step, add a state/region to country |
||||
new_york.add_rule({"highway": "primary"}, "45 mph") # Fifth step , add rules to the state. See the text above for how to write rules |
||||
new_york.add_rule({"highway": "secondary"}, "55 mph") |
||||
new_york.add_rule({"highway": "tertiary"}, "55 mph") |
||||
new_york.add_rule({"highway": "residential"}, "30 mph") |
||||
new_york.add_rule({"highway": "primary_link"}, "45 mph") |
||||
new_york.add_rule({"highway": "secondary_link"}, "55 mph") |
||||
new_york.add_rule({"highway": "tertiary_link"}, "55 mph") |
||||
# All if not written by the state, the rules will default to the country rules |
||||
|
||||
#california = US.add_region("California") |
||||
# California uses only the default US rules |
||||
|
||||
michigan = US.add_region("Michigan") |
||||
michigan.add_rule({"highway": "motorway"}, "70 mph") |
||||
|
||||
oregon = US.add_region("Oregon") |
||||
oregon.add_rule({"highway": "motorway"}, "55 mph") |
||||
oregon.add_rule({"highway": "secondary"}, "35 mph") |
||||
oregon.add_rule({"highway": "tertiary"}, "30 mph") |
||||
oregon.add_rule({"highway": "service"}, "15 mph") |
||||
oregon.add_rule({"highway": "secondary_link"}, "35 mph") |
||||
oregon.add_rule({"highway": "tertiary_link"}, "30 mph") |
||||
|
||||
south_dakota = US.add_region("South Dakota") |
||||
south_dakota.add_rule({"highway": "motorway"}, "80 mph") |
||||
south_dakota.add_rule({"highway": "trunk"}, "70 mph") |
||||
south_dakota.add_rule({"highway": "primary"}, "65 mph") |
||||
south_dakota.add_rule({"highway": "trunk_link"}, "70 mph") |
||||
south_dakota.add_rule({"highway": "primary_link"}, "65 mph") |
||||
|
||||
wisconsin = US.add_region("Wisconsin") |
||||
wisconsin.add_rule({"highway": "trunk"}, "65 mph") |
||||
wisconsin.add_rule({"highway": "tertiary"}, "45 mph") |
||||
wisconsin.add_rule({"highway": "unclassified"}, "35 mph") |
||||
wisconsin.add_rule({"highway": "trunk_link"}, "65 mph") |
||||
wisconsin.add_rule({"highway": "tertiary_link"}, "45 mph") |
||||
|
||||
""" |
||||
-------------------------------------------------- |
||||
AU - Australia |
||||
-------------------------------------------------- |
||||
""" |
||||
AU = Country("AU") |
||||
countries.append(AU) |
||||
|
||||
""" Default rules """ |
||||
AU.add_rule({"highway": "motorway"}, "100") |
||||
AU.add_rule({"highway": "trunk"}, "80") |
||||
AU.add_rule({"highway": "primary"}, "80") |
||||
AU.add_rule({"highway": "secondary"}, "50") |
||||
AU.add_rule({"highway": "tertiary"}, "50") |
||||
AU.add_rule({"highway": "unclassified"}, "80") |
||||
AU.add_rule({"highway": "residential"}, "50") |
||||
AU.add_rule({"highway": "service"}, "40") |
||||
AU.add_rule({"highway": "motorway_link"}, "90") |
||||
AU.add_rule({"highway": "trunk_link"}, "80") |
||||
AU.add_rule({"highway": "primary_link"}, "80") |
||||
AU.add_rule({"highway": "secondary_link"}, "50") |
||||
AU.add_rule({"highway": "tertiary_link"}, "50") |
||||
AU.add_rule({"highway": "living_street"}, "30") |
||||
|
||||
""" |
||||
-------------------------------------------------- |
||||
CA - Canada |
||||
-------------------------------------------------- |
||||
""" |
||||
CA = Country("CA") |
||||
countries.append(CA) |
||||
|
||||
""" Default rules """ |
||||
CA.add_rule({"highway": "motorway"}, "100") |
||||
CA.add_rule({"highway": "trunk"}, "80") |
||||
CA.add_rule({"highway": "primary"}, "80") |
||||
CA.add_rule({"highway": "secondary"}, "50") |
||||
CA.add_rule({"highway": "tertiary"}, "50") |
||||
CA.add_rule({"highway": "unclassified"}, "80") |
||||
CA.add_rule({"highway": "residential"}, "40") |
||||
CA.add_rule({"highway": "service"}, "40") |
||||
CA.add_rule({"highway": "motorway_link"}, "90") |
||||
CA.add_rule({"highway": "trunk_link"}, "80") |
||||
CA.add_rule({"highway": "primary_link"}, "80") |
||||
CA.add_rule({"highway": "secondary_link"}, "50") |
||||
CA.add_rule({"highway": "tertiary_link"}, "50") |
||||
CA.add_rule({"highway": "living_street"}, "20") |
||||
|
||||
|
||||
""" |
||||
-------------------------------------------------- |
||||
DE - Germany |
||||
-------------------------------------------------- |
||||
""" |
||||
DE = Country("DE") |
||||
countries.append(DE) |
||||
|
||||
""" Default rules """ |
||||
DE.add_rule({"highway": "motorway"}, "none") |
||||
DE.add_rule({"highway": "living_street"}, "10") |
||||
DE.add_rule({"highway": "residential"}, "30") |
||||
DE.add_rule({"zone:traffic": "DE:rural"}, "100") |
||||
DE.add_rule({"zone:traffic": "DE:urban"}, "50") |
||||
DE.add_rule({"zone:maxspeed": "DE:30"}, "30") |
||||
DE.add_rule({"zone:maxspeed": "DE:urban"}, "50") |
||||
DE.add_rule({"zone:maxspeed": "DE:rural"}, "100") |
||||
DE.add_rule({"zone:maxspeed": "DE:motorway"}, "none") |
||||
DE.add_rule({"bicycle_road": "yes"}, "30") |
||||
|
||||
|
||||
""" |
||||
-------------------------------------------------- |
||||
EE - Estonia |
||||
-------------------------------------------------- |
||||
""" |
||||
EE = Country("EE") |
||||
countries.append(EE) |
||||
|
||||
""" Default rules """ |
||||
EE.add_rule({"highway": "motorway"}, "90") |
||||
EE.add_rule({"highway": "trunk"}, "90") |
||||
EE.add_rule({"highway": "primary"}, "90") |
||||
EE.add_rule({"highway": "secondary"}, "50") |
||||
EE.add_rule({"highway": "tertiary"}, "50") |
||||
EE.add_rule({"highway": "unclassified"}, "90") |
||||
EE.add_rule({"highway": "residential"}, "40") |
||||
EE.add_rule({"highway": "service"}, "40") |
||||
EE.add_rule({"highway": "motorway_link"}, "90") |
||||
EE.add_rule({"highway": "trunk_link"}, "70") |
||||
EE.add_rule({"highway": "primary_link"}, "70") |
||||
EE.add_rule({"highway": "secondary_link"}, "50") |
||||
EE.add_rule({"highway": "tertiary_link"}, "50") |
||||
EE.add_rule({"highway": "living_street"}, "20") |
||||
|
||||
|
||||
""" --- DO NOT MODIFY CODE BELOW THIS LINE --- """ |
||||
""" --- ADD YOUR COUNTRY OR STATE ABOVE --- """ |
||||
|
||||
# Final step |
||||
write_json(countries, filename) |
||||
|
||||
def write_json(countries, filename = DEFAULT_OUTPUT_FILENAME): |
||||
out_dict = {} |
||||
for country in countries: |
||||
out_dict.update(country.jsonify()) |
||||
json_string = json.dumps(out_dict, indent=2) |
||||
with open(filename, "wb") as f: |
||||
f.write(json_string) |
||||
|
||||
|
||||
class Region(): |
||||
ALLOWABLE_TAG_KEYS = ["highway", "zone:traffic", "bicycle_road", "zone:maxspeed"] |
||||
ALLOWABLE_HIGHWAY_TYPES = ["motorway", "trunk", "primary", "secondary", "tertiary", "unclassified", "residential", "service", "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link", "living_street"] |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.rules = [] |
||||
|
||||
def add_rule(self, tag_conditions, speed): |
||||
new_rule = {} |
||||
if not isinstance(tag_conditions, dict): |
||||
raise TypeError("Rule tag conditions must be dictionary") |
||||
if not all(tag_key in self.ALLOWABLE_TAG_KEYS for tag_key in tag_conditions): |
||||
raise ValueError("Rule tag keys must be in allowable tag kesy") # If this is by mistake, please update ALLOWABLE_TAG_KEYS |
||||
if 'highway' in tag_conditions: |
||||
if not tag_conditions['highway'] in self.ALLOWABLE_HIGHWAY_TYPES: |
||||
raise ValueError("Invalid Highway type {}".format(tag_conditions["highway"])) |
||||
new_rule['tags'] = tag_conditions |
||||
try: |
||||
new_rule['speed'] = str(speed) |
||||
except ValueError: |
||||
raise ValueError("Rule speed must be string") |
||||
self.rules.append(new_rule) |
||||
|
||||
def jsonify(self): |
||||
ret_dict = {} |
||||
ret_dict[self.name] = self.rules |
||||
return ret_dict |
||||
|
||||
class Country(Region): |
||||
ALLOWABLE_COUNTRY_CODES = ["AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ","BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR","IO","BN","BG","BF","BI","KH","CM","CA","CV","KY","CF","TD","CL","CN","CX","CC","CO","KM","CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO","EC","EG","SV","GQ","ER","EE","ET","FK","FO","FJ","FI","FR","GF","PF","TF","GA","GM","GE","DE","GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY","HT","HM","VA","HN","HK","HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM","JP","JE","JO","KZ","KE","KI","KP","KR","KW","KG","LA","LV","LB","LS","LR","LY","LI","LT","LU","MO","MK","MG","MW","MY","MV","ML","MT","MH","MQ","MR","MU","YT","MX","FM","MD","MC","MN","ME","MS","MA","MZ","MM","NA","NR","NP","NL","NC","NZ","NI","NE","NG","NU","NF","MP","NO","OM","PK","PW","PS","PA","PG","PY","PE","PH","PN","PL","PT","PR","QA","RE","RO","RU","RW","BL","SH","KN","LC","MF","PM","VC","WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK","SI","SB","SO","ZA","GS","SS","ES","LK","SD","SR","SJ","SZ","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TK","TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","US","UM","UY","UZ","VU","VE","VN","VG","VI","WF","EH","YE","ZM","ZW"] |
||||
def __init__(self, ISO_3166_alpha_2): |
||||
Region.__init__(self, ISO_3166_alpha_2) |
||||
if ISO_3166_alpha_2 not in self.ALLOWABLE_COUNTRY_CODES: |
||||
raise ValueError("Not valid IOS 3166 country code") |
||||
self.regions = {} |
||||
|
||||
def add_region(self, name): |
||||
self.regions[name] = Region(name) |
||||
return self.regions[name] |
||||
|
||||
def jsonify(self): |
||||
ret_dict = {} |
||||
ret_dict[self.name] = {} |
||||
for r_name, region in self.regions.items(): |
||||
ret_dict[self.name].update(region.jsonify()) |
||||
ret_dict[self.name]['Default'] = self.rules |
||||
return ret_dict |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,296 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
# Add phonelibs openblas to LD_LIBRARY_PATH if import fails |
||||
from common.basedir import BASEDIR |
||||
try: |
||||
from scipy import spatial |
||||
except ImportError as e: |
||||
import os |
||||
import sys |
||||
|
||||
|
||||
openblas_path = os.path.join(BASEDIR, "phonelibs/openblas/") |
||||
os.environ['LD_LIBRARY_PATH'] += ':' + openblas_path |
||||
|
||||
args = [sys.executable] |
||||
args.extend(sys.argv) |
||||
os.execv(sys.executable, args) |
||||
|
||||
DEFAULT_SPEEDS_BY_REGION_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds_by_region.json" |
||||
from selfdrive.mapd import default_speeds_generator |
||||
default_speeds_generator.main(DEFAULT_SPEEDS_BY_REGION_JSON_FILE) |
||||
|
||||
import os |
||||
import sys |
||||
import time |
||||
import zmq |
||||
import threading |
||||
import numpy as np |
||||
import overpy |
||||
from collections import defaultdict |
||||
|
||||
from common.params import Params |
||||
from common.transformations.coordinates import geodetic2ecef |
||||
from cereal.services import service_list |
||||
import cereal.messaging as messaging |
||||
from selfdrive.mapd.mapd_helpers import MAPS_LOOKAHEAD_DISTANCE, Way, circle_through_points |
||||
import selfdrive.crash as crash |
||||
from selfdrive.version import version, dirty |
||||
|
||||
|
||||
OVERPASS_API_URL = "https://overpass.kumi.systems/api/interpreter" |
||||
OVERPASS_HEADERS = { |
||||
'User-Agent': 'NEOS (comma.ai)', |
||||
'Accept-Encoding': 'gzip' |
||||
} |
||||
|
||||
last_gps = None |
||||
query_lock = threading.Lock() |
||||
last_query_result = None |
||||
last_query_pos = None |
||||
cache_valid = False |
||||
|
||||
def build_way_query(lat, lon, radius=50): |
||||
"""Builds a query to find all highways within a given radius around a point""" |
||||
pos = " (around:%f,%f,%f)" % (radius, lat, lon) |
||||
lat_lon = "(%f,%f)" % (lat, lon) |
||||
q = """( |
||||
way |
||||
""" + pos + """ |
||||
[highway][highway!~"^(footway|path|bridleway|steps|cycleway|construction|bus_guideway|escape)$"]; |
||||
>;);out;""" + """is_in""" + lat_lon + """;area._[admin_level~"[24]"]; |
||||
convert area ::id = id(), admin_level = t['admin_level'], |
||||
name = t['name'], "ISO3166-1:alpha2" = t['ISO3166-1:alpha2'];out; |
||||
""" |
||||
return q |
||||
|
||||
|
||||
def query_thread(): |
||||
global last_query_result, last_query_pos, cache_valid |
||||
api = overpy.Overpass(url=OVERPASS_API_URL, headers=OVERPASS_HEADERS, timeout=10.) |
||||
|
||||
while True: |
||||
time.sleep(1) |
||||
if last_gps is not None: |
||||
fix_ok = last_gps.flags & 1 |
||||
if not fix_ok: |
||||
continue |
||||
|
||||
if last_query_pos is not None: |
||||
cur_ecef = geodetic2ecef((last_gps.latitude, last_gps.longitude, last_gps.altitude)) |
||||
prev_ecef = geodetic2ecef((last_query_pos.latitude, last_query_pos.longitude, last_query_pos.altitude)) |
||||
dist = np.linalg.norm(cur_ecef - prev_ecef) |
||||
if dist < 1000: #updated when we are 1km from the edge of the downloaded circle |
||||
continue |
||||
|
||||
if dist > 3000: |
||||
cache_valid = False |
||||
|
||||
q = build_way_query(last_gps.latitude, last_gps.longitude, radius=3000) |
||||
try: |
||||
new_result = api.query(q) |
||||
|
||||
# Build kd-tree |
||||
nodes = [] |
||||
real_nodes = [] |
||||
node_to_way = defaultdict(list) |
||||
location_info = {} |
||||
|
||||
for n in new_result.nodes: |
||||
nodes.append((float(n.lat), float(n.lon), 0)) |
||||
real_nodes.append(n) |
||||
|
||||
for way in new_result.ways: |
||||
for n in way.nodes: |
||||
node_to_way[n.id].append(way) |
||||
|
||||
for area in new_result.areas: |
||||
if area.tags.get('admin_level', '') == "2": |
||||
location_info['country'] = area.tags.get('ISO3166-1:alpha2', '') |
||||
if area.tags.get('admin_level', '') == "4": |
||||
location_info['region'] = area.tags.get('name', '') |
||||
|
||||
nodes = np.asarray(nodes) |
||||
nodes = geodetic2ecef(nodes) |
||||
tree = spatial.cKDTree(nodes) |
||||
|
||||
query_lock.acquire() |
||||
last_query_result = new_result, tree, real_nodes, node_to_way, location_info |
||||
last_query_pos = last_gps |
||||
cache_valid = True |
||||
query_lock.release() |
||||
|
||||
except Exception as e: |
||||
print(e) |
||||
query_lock.acquire() |
||||
last_query_result = None |
||||
query_lock.release() |
||||
|
||||
|
||||
def mapsd_thread(): |
||||
global last_gps |
||||
|
||||
gps_sock = messaging.sub_sock('gpsLocation', conflate=True) |
||||
gps_external_sock = messaging.sub_sock('gpsLocationExternal', conflate=True) |
||||
map_data_sock = messaging.pub_sock('liveMapData') |
||||
|
||||
cur_way = None |
||||
curvature_valid = False |
||||
curvature = None |
||||
upcoming_curvature = 0. |
||||
dist_to_turn = 0. |
||||
road_points = None |
||||
|
||||
while True: |
||||
gps = messaging.recv_one(gps_sock) |
||||
gps_ext = messaging.recv_one_or_none(gps_external_sock) |
||||
|
||||
if gps_ext is not None: |
||||
gps = gps_ext.gpsLocationExternal |
||||
else: |
||||
gps = gps.gpsLocation |
||||
|
||||
last_gps = gps |
||||
|
||||
fix_ok = gps.flags & 1 |
||||
if not fix_ok or last_query_result is None or not cache_valid: |
||||
cur_way = None |
||||
curvature = None |
||||
curvature_valid = False |
||||
upcoming_curvature = 0. |
||||
dist_to_turn = 0. |
||||
road_points = None |
||||
map_valid = False |
||||
else: |
||||
map_valid = True |
||||
lat = gps.latitude |
||||
lon = gps.longitude |
||||
heading = gps.bearing |
||||
speed = gps.speed |
||||
|
||||
query_lock.acquire() |
||||
cur_way = Way.closest(last_query_result, lat, lon, heading, cur_way) |
||||
if cur_way is not None: |
||||
pnts, curvature_valid = cur_way.get_lookahead(lat, lon, heading, MAPS_LOOKAHEAD_DISTANCE) |
||||
|
||||
xs = pnts[:, 0] |
||||
ys = pnts[:, 1] |
||||
road_points = [float(x) for x in xs], [float(y) for y in ys] |
||||
|
||||
if speed < 10: |
||||
curvature_valid = False |
||||
if curvature_valid and pnts.shape[0] <= 3: |
||||
curvature_valid = False |
||||
|
||||
# The curvature is valid when at least MAPS_LOOKAHEAD_DISTANCE of road is found |
||||
if curvature_valid: |
||||
# Compute the curvature for each point |
||||
with np.errstate(divide='ignore'): |
||||
circles = [circle_through_points(*p) for p in zip(pnts, pnts[1:], pnts[2:])] |
||||
circles = np.asarray(circles) |
||||
radii = np.nan_to_num(circles[:, 2]) |
||||
radii[radii < 10] = np.inf |
||||
curvature = 1. / radii |
||||
|
||||
# Index of closest point |
||||
closest = np.argmin(np.linalg.norm(pnts, axis=1)) |
||||
dist_to_closest = pnts[closest, 0] # We can use x distance here since it should be close |
||||
|
||||
# Compute distance along path |
||||
dists = list() |
||||
dists.append(0) |
||||
for p, p_prev in zip(pnts, pnts[1:, :]): |
||||
dists.append(dists[-1] + np.linalg.norm(p - p_prev)) |
||||
dists = np.asarray(dists) |
||||
dists = dists - dists[closest] + dist_to_closest |
||||
dists = dists[1:-1] |
||||
|
||||
close_idx = np.logical_and(dists > 0, dists < 500) |
||||
dists = dists[close_idx] |
||||
curvature = curvature[close_idx] |
||||
|
||||
if len(curvature): |
||||
# TODO: Determine left or right turn |
||||
curvature = np.nan_to_num(curvature) |
||||
|
||||
# Outlier rejection |
||||
new_curvature = np.percentile(curvature, 90, interpolation='lower') |
||||
|
||||
k = 0.6 |
||||
upcoming_curvature = k * upcoming_curvature + (1 - k) * new_curvature |
||||
in_turn_indices = curvature > 0.8 * new_curvature |
||||
|
||||
if np.any(in_turn_indices): |
||||
dist_to_turn = np.min(dists[in_turn_indices]) |
||||
else: |
||||
dist_to_turn = 999 |
||||
else: |
||||
upcoming_curvature = 0. |
||||
dist_to_turn = 999 |
||||
|
||||
query_lock.release() |
||||
|
||||
dat = messaging.new_message() |
||||
dat.init('liveMapData') |
||||
|
||||
if last_gps is not None: |
||||
dat.liveMapData.lastGps = last_gps |
||||
|
||||
if cur_way is not None: |
||||
dat.liveMapData.wayId = cur_way.id |
||||
|
||||
# Speed limit |
||||
max_speed = cur_way.max_speed() |
||||
if max_speed is not None: |
||||
dat.liveMapData.speedLimitValid = True |
||||
dat.liveMapData.speedLimit = max_speed |
||||
|
||||
# TODO: use the function below to anticipate upcoming speed limits |
||||
#max_speed_ahead, max_speed_ahead_dist = cur_way.max_speed_ahead(max_speed, lat, lon, heading, MAPS_LOOKAHEAD_DISTANCE) |
||||
#if max_speed_ahead is not None and max_speed_ahead_dist is not None: |
||||
# dat.liveMapData.speedLimitAheadValid = True |
||||
# dat.liveMapData.speedLimitAhead = float(max_speed_ahead) |
||||
# dat.liveMapData.speedLimitAheadDistance = float(max_speed_ahead_dist) |
||||
|
||||
|
||||
advisory_max_speed = cur_way.advisory_max_speed() |
||||
if advisory_max_speed is not None: |
||||
dat.liveMapData.speedAdvisoryValid = True |
||||
dat.liveMapData.speedAdvisory = advisory_max_speed |
||||
|
||||
# Curvature |
||||
dat.liveMapData.curvatureValid = curvature_valid |
||||
dat.liveMapData.curvature = float(upcoming_curvature) |
||||
dat.liveMapData.distToTurn = float(dist_to_turn) |
||||
if road_points is not None: |
||||
dat.liveMapData.roadX, dat.liveMapData.roadY = road_points |
||||
if curvature is not None: |
||||
dat.liveMapData.roadCurvatureX = [float(x) for x in dists] |
||||
dat.liveMapData.roadCurvature = [float(x) for x in curvature] |
||||
|
||||
dat.liveMapData.mapValid = map_valid |
||||
|
||||
map_data_sock.send(dat.to_bytes()) |
||||
|
||||
|
||||
def main(gctx=None): |
||||
params = Params() |
||||
dongle_id = params.get("DongleId") |
||||
crash.bind_user(id=dongle_id) |
||||
crash.bind_extra(version=version, dirty=dirty, is_eon=True) |
||||
crash.install() |
||||
|
||||
main_thread = threading.Thread(target=mapsd_thread) |
||||
main_thread.daemon = True |
||||
main_thread.start() |
||||
|
||||
q_thread = threading.Thread(target=query_thread) |
||||
q_thread.daemon = True |
||||
q_thread.start() |
||||
|
||||
while True: |
||||
time.sleep(0.1) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -0,0 +1,364 @@ |
||||
import math |
||||
import json |
||||
import numpy as np |
||||
from datetime import datetime |
||||
from common.basedir import BASEDIR |
||||
from selfdrive.config import Conversions as CV |
||||
from common.transformations.coordinates import LocalCoord, geodetic2ecef |
||||
|
||||
LOOKAHEAD_TIME = 10. |
||||
MAPS_LOOKAHEAD_DISTANCE = 50 * LOOKAHEAD_TIME |
||||
|
||||
DEFAULT_SPEEDS_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds.json" |
||||
DEFAULT_SPEEDS = {} |
||||
with open(DEFAULT_SPEEDS_JSON_FILE, "rb") as f: |
||||
DEFAULT_SPEEDS = json.loads(f.read()) |
||||
|
||||
DEFAULT_SPEEDS_BY_REGION_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds_by_region.json" |
||||
DEFAULT_SPEEDS_BY_REGION = {} |
||||
with open(DEFAULT_SPEEDS_BY_REGION_JSON_FILE, "rb") as f: |
||||
DEFAULT_SPEEDS_BY_REGION = json.loads(f.read()) |
||||
|
||||
def circle_through_points(p1, p2, p3): |
||||
"""Fits a circle through three points |
||||
Formulas from: http://www.ambrsoft.com/trigocalc/circle3d.htm""" |
||||
x1, y1, _ = p1 |
||||
x2, y2, _ = p2 |
||||
x3, y3, _ = p3 |
||||
|
||||
A = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2 |
||||
B = (x1**2 + y1**2) * (y3 - y2) + (x2**2 + y2**2) * (y1 - y3) + (x3**2 + y3**2) * (y2 - y1) |
||||
C = (x1**2 + y1**2) * (x2 - x3) + (x2**2 + y2**2) * (x3 - x1) + (x3**2 + y3**2) * (x1 - x2) |
||||
D = (x1**2 + y1**2) * (x3 * y2 - x2 * y3) + (x2**2 + y2**2) * (x1 * y3 - x3 * y1) + (x3**2 + y3**2) * (x2 * y1 - x1 * y2) |
||||
|
||||
return (-B / (2 * A), - C / (2 * A), np.sqrt((B**2 + C**2 - 4 * A * D) / (4 * A**2))) |
||||
|
||||
def parse_speed_unit(max_speed): |
||||
"""Converts a maxspeed string to m/s based on the unit present in the input. |
||||
OpenStreetMap defaults to kph if no unit is present. """ |
||||
|
||||
if not max_speed: |
||||
return None |
||||
|
||||
conversion = CV.KPH_TO_MS |
||||
if 'mph' in max_speed: |
||||
max_speed = max_speed.replace(' mph', '') |
||||
conversion = CV.MPH_TO_MS |
||||
try: |
||||
return float(max_speed) * conversion |
||||
except ValueError: |
||||
return None |
||||
|
||||
def parse_speed_tags(tags): |
||||
"""Parses tags on a way to find the maxspeed string""" |
||||
max_speed = None |
||||
|
||||
if 'maxspeed' in tags: |
||||
max_speed = tags['maxspeed'] |
||||
|
||||
if 'maxspeed:conditional' in tags: |
||||
try: |
||||
max_speed_cond, cond = tags['maxspeed:conditional'].split(' @ ') |
||||
cond = cond[1:-1] |
||||
|
||||
start, end = cond.split('-') |
||||
now = datetime.now() # TODO: Get time and timezone from gps fix so this will work correctly on replays |
||||
start = datetime.strptime(start, "%H:%M").replace(year=now.year, month=now.month, day=now.day) |
||||
end = datetime.strptime(end, "%H:%M").replace(year=now.year, month=now.month, day=now.day) |
||||
|
||||
if start <= now <= end: |
||||
max_speed = max_speed_cond |
||||
except ValueError: |
||||
pass |
||||
|
||||
if not max_speed and 'source:maxspeed' in tags: |
||||
max_speed = DEFAULT_SPEEDS.get(tags['source:maxspeed'], None) |
||||
if not max_speed and 'maxspeed:type' in tags: |
||||
max_speed = DEFAULT_SPEEDS.get(tags['maxspeed:type'], None) |
||||
|
||||
max_speed = parse_speed_unit(max_speed) |
||||
return max_speed |
||||
|
||||
def geocode_maxspeed(tags, location_info): |
||||
max_speed = None |
||||
try: |
||||
geocode_country = location_info.get('country', '') |
||||
geocode_region = location_info.get('region', '') |
||||
|
||||
country_rules = DEFAULT_SPEEDS_BY_REGION.get(geocode_country, {}) |
||||
country_defaults = country_rules.get('Default', []) |
||||
for rule in country_defaults: |
||||
rule_valid = all( |
||||
tag_name in tags |
||||
and tags[tag_name] == value |
||||
for tag_name, value in rule['tags'].items() |
||||
) |
||||
if rule_valid: |
||||
max_speed = rule['speed'] |
||||
break #stop searching country |
||||
|
||||
region_rules = country_rules.get(geocode_region, []) |
||||
for rule in region_rules: |
||||
rule_valid = all( |
||||
tag_name in tags |
||||
and tags[tag_name] == value |
||||
for tag_name, value in rule['tags'].items() |
||||
) |
||||
if rule_valid: |
||||
max_speed = rule['speed'] |
||||
break #stop searching region |
||||
except KeyError: |
||||
pass |
||||
max_speed = parse_speed_unit(max_speed) |
||||
return max_speed |
||||
|
||||
class Way: |
||||
def __init__(self, way, query_results): |
||||
self.id = way.id |
||||
self.way = way |
||||
self.query_results = query_results |
||||
|
||||
points = list() |
||||
|
||||
for node in self.way.get_nodes(resolve_missing=False): |
||||
points.append((float(node.lat), float(node.lon), 0.)) |
||||
|
||||
self.points = np.asarray(points) |
||||
|
||||
@classmethod |
||||
def closest(cls, query_results, lat, lon, heading, prev_way=None): |
||||
results, tree, real_nodes, node_to_way, location_info = query_results |
||||
|
||||
cur_pos = geodetic2ecef((lat, lon, 0)) |
||||
nodes = tree.query_ball_point(cur_pos, 500) |
||||
|
||||
# If no nodes within 500m, choose closest one |
||||
if not nodes: |
||||
nodes = [tree.query(cur_pos)[1]] |
||||
|
||||
ways = [] |
||||
for n in nodes: |
||||
real_node = real_nodes[n] |
||||
ways += node_to_way[real_node.id] |
||||
ways = set(ways) |
||||
|
||||
closest_way = None |
||||
best_score = None |
||||
for way in ways: |
||||
way = Way(way, query_results) |
||||
points = way.points_in_car_frame(lat, lon, heading) |
||||
|
||||
on_way = way.on_way(lat, lon, heading, points) |
||||
if not on_way: |
||||
continue |
||||
|
||||
# Create mask of points in front and behind |
||||
x = points[:, 0] |
||||
y = points[:, 1] |
||||
angles = np.arctan2(y, x) |
||||
front = np.logical_and((-np.pi / 2) < angles, |
||||
angles < (np.pi / 2)) |
||||
behind = np.logical_not(front) |
||||
|
||||
dists = np.linalg.norm(points, axis=1) |
||||
|
||||
# Get closest point behind the car |
||||
dists_behind = np.copy(dists) |
||||
dists_behind[front] = np.NaN |
||||
closest_behind = points[np.nanargmin(dists_behind)] |
||||
|
||||
# Get closest point in front of the car |
||||
dists_front = np.copy(dists) |
||||
dists_front[behind] = np.NaN |
||||
closest_front = points[np.nanargmin(dists_front)] |
||||
|
||||
# fit line: y = a*x + b |
||||
x1, y1, _ = closest_behind |
||||
x2, y2, _ = closest_front |
||||
a = (y2 - y1) / max((x2 - x1), 1e-5) |
||||
b = y1 - a * x1 |
||||
|
||||
# With a factor of 60 a 20m offset causes the same error as a 20 degree heading error |
||||
# (A 20 degree heading offset results in an a of about 1/3) |
||||
score = abs(a) * 60. + abs(b) |
||||
|
||||
# Prefer same type of road |
||||
if prev_way is not None: |
||||
if way.way.tags.get('highway', '') == prev_way.way.tags.get('highway', ''): |
||||
score *= 0.5 |
||||
|
||||
if closest_way is None or score < best_score: |
||||
closest_way = way |
||||
best_score = score |
||||
|
||||
# Normal score is < 5 |
||||
if best_score > 50: |
||||
return None |
||||
|
||||
return closest_way |
||||
|
||||
def __str__(self): |
||||
return "%s %s" % (self.id, self.way.tags) |
||||
|
||||
def max_speed(self): |
||||
"""Extracts the (conditional) speed limit from a way""" |
||||
if not self.way: |
||||
return None |
||||
|
||||
max_speed = parse_speed_tags(self.way.tags) |
||||
if not max_speed: |
||||
location_info = self.query_results[4] |
||||
max_speed = geocode_maxspeed(self.way.tags, location_info) |
||||
|
||||
return max_speed |
||||
|
||||
def max_speed_ahead(self, current_speed_limit, lat, lon, heading, lookahead): |
||||
"""Look ahead for a max speed""" |
||||
if not self.way: |
||||
return None |
||||
|
||||
speed_ahead = None |
||||
speed_ahead_dist = None |
||||
lookahead_ways = 5 |
||||
way = self |
||||
for i in range(lookahead_ways): |
||||
way_pts = way.points_in_car_frame(lat, lon, heading) |
||||
|
||||
# Check current lookahead distance |
||||
max_dist = np.linalg.norm(way_pts[-1, :]) |
||||
|
||||
if max_dist > 2 * lookahead: |
||||
break |
||||
|
||||
if 'maxspeed' in way.way.tags: |
||||
spd = parse_speed_tags(way.way.tags) |
||||
if not spd: |
||||
location_info = self.query_results[4] |
||||
spd = geocode_maxspeed(way.way.tags, location_info) |
||||
if spd < current_speed_limit: |
||||
speed_ahead = spd |
||||
min_dist = np.linalg.norm(way_pts[1, :]) |
||||
speed_ahead_dist = min_dist |
||||
break |
||||
# Find next way |
||||
way = way.next_way() |
||||
if not way: |
||||
break |
||||
|
||||
return speed_ahead, speed_ahead_dist |
||||
|
||||
def advisory_max_speed(self): |
||||
if not self.way: |
||||
return None |
||||
|
||||
tags = self.way.tags |
||||
adv_speed = None |
||||
|
||||
if 'maxspeed:advisory' in tags: |
||||
adv_speed = tags['maxspeed:advisory'] |
||||
adv_speed = parse_speed_unit(adv_speed) |
||||
return adv_speed |
||||
|
||||
def on_way(self, lat, lon, heading, points=None): |
||||
if points is None: |
||||
points = self.points_in_car_frame(lat, lon, heading) |
||||
x = points[:, 0] |
||||
return np.min(x) < 0. and np.max(x) > 0. |
||||
|
||||
def closest_point(self, lat, lon, heading, points=None): |
||||
if points is None: |
||||
points = self.points_in_car_frame(lat, lon, heading) |
||||
i = np.argmin(np.linalg.norm(points, axis=1)) |
||||
return points[i] |
||||
|
||||
def distance_to_closest_node(self, lat, lon, heading, points=None): |
||||
if points is None: |
||||
points = self.points_in_car_frame(lat, lon, heading) |
||||
return np.min(np.linalg.norm(points, axis=1)) |
||||
|
||||
def points_in_car_frame(self, lat, lon, heading): |
||||
lc = LocalCoord.from_geodetic([lat, lon, 0.]) |
||||
|
||||
# Build rotation matrix |
||||
heading = math.radians(-heading + 90) |
||||
c, s = np.cos(heading), np.sin(heading) |
||||
rot = np.array([[c, s, 0.], [-s, c, 0.], [0., 0., 1.]]) |
||||
|
||||
# Convert to local coordinates |
||||
points_carframe = lc.geodetic2ned(self.points).T |
||||
|
||||
# Rotate with heading of car |
||||
points_carframe = np.dot(rot, points_carframe[(1, 0, 2), :]).T |
||||
|
||||
return points_carframe |
||||
|
||||
def next_way(self, backwards=False): |
||||
results, tree, real_nodes, node_to_way, location_info = self.query_results |
||||
|
||||
if backwards: |
||||
node = self.way.nodes[0] |
||||
else: |
||||
node = self.way.nodes[-1] |
||||
|
||||
ways = node_to_way[node.id] |
||||
|
||||
way = None |
||||
try: |
||||
# Simple heuristic to find next way |
||||
ways = [w for w in ways if w.id != self.id] |
||||
ways = [w for w in ways if w.nodes[0] == node] |
||||
|
||||
# Filter on highway tag |
||||
acceptable_tags = list() |
||||
cur_tag = self.way.tags['highway'] |
||||
acceptable_tags.append(cur_tag) |
||||
if cur_tag == 'motorway_link': |
||||
acceptable_tags.append('motorway') |
||||
acceptable_tags.append('trunk') |
||||
acceptable_tags.append('primary') |
||||
ways = [w for w in ways if w.tags['highway'] in acceptable_tags] |
||||
|
||||
# Filter on number of lanes |
||||
cur_num_lanes = int(self.way.tags['lanes']) |
||||
if len(ways) > 1: |
||||
ways_same_lanes = [w for w in ways if int(w.tags['lanes']) == cur_num_lanes] |
||||
if len(ways_same_lanes) == 1: |
||||
ways = ways_same_lanes |
||||
if len(ways) > 1: |
||||
ways = [w for w in ways if int(w.tags['lanes']) > cur_num_lanes] |
||||
if len(ways) == 1: |
||||
way = Way(ways[0], self.query_results) |
||||
|
||||
except (KeyError, ValueError): |
||||
pass |
||||
|
||||
return way |
||||
|
||||
def get_lookahead(self, lat, lon, heading, lookahead): |
||||
pnts = None |
||||
way = self |
||||
valid = False |
||||
|
||||
for i in range(5): |
||||
# Get new points and append to list |
||||
new_pnts = way.points_in_car_frame(lat, lon, heading) |
||||
|
||||
if pnts is None: |
||||
pnts = new_pnts |
||||
else: |
||||
pnts = np.vstack([pnts, new_pnts]) |
||||
|
||||
# Check current lookahead distance |
||||
max_dist = np.linalg.norm(pnts[-1, :]) |
||||
if max_dist > lookahead: |
||||
valid = True |
||||
|
||||
if max_dist > 2 * lookahead: |
||||
break |
||||
|
||||
# Find next way |
||||
way = way.next_way() |
||||
if not way: |
||||
break |
||||
|
||||
return pnts, valid |
@ -0,0 +1,36 @@ |
||||
Import('env', 'arch', 'messaging', 'common', 'gpucommon', 'visionipc') |
||||
lenv = env.Clone() |
||||
|
||||
libs = [messaging, common, 'OpenCL', 'SNPE', 'capnp', 'zmq', 'kj', 'yuv', gpucommon, visionipc] |
||||
|
||||
common_src = [ |
||||
"models/commonmodel.c", |
||||
"runners/snpemodel.cc", |
||||
"transforms/loadyuv.c", |
||||
"transforms/transform.c"] |
||||
|
||||
if arch == "aarch64": |
||||
libs += ['gsl', 'CB', 'gnustl_shared'] |
||||
else: |
||||
libs += ['symphony-cpu', 'pthread'] |
||||
|
||||
if FindFile('libtensorflow.so', env['LIBPATH']): |
||||
# for tensorflow support |
||||
common_src += ['runners/tfmodel.cc'] |
||||
libs += ['tensorflow'] |
||||
# tell runners to use it |
||||
lenv['CFLAGS'].append("-DUSE_TF_MODEL") |
||||
lenv['CXXFLAGS'].append("-DUSE_TF_MODEL") |
||||
|
||||
common = lenv.Object(common_src) |
||||
|
||||
lenv.Program('_monitoringd', [ |
||||
"monitoringd.cc", |
||||
"models/monitoring.cc", |
||||
]+common, LIBS=libs) |
||||
|
||||
lenv.Program('_modeld', [ |
||||
"modeld.cc", |
||||
"models/driving.cc", |
||||
]+common, LIBS=libs) |
||||
|
@ -0,0 +1,6 @@ |
||||
MAX_DISTANCE = 140. |
||||
LANE_OFFSET = 1.8 |
||||
MAX_REL_V = 10. |
||||
|
||||
LEAD_X_SCALE = 10 |
||||
LEAD_Y_SCALE = 10 |
@ -0,0 +1,4 @@ |
||||
#!/bin/sh |
||||
export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8/:/home/batman/one/phonelibs/snpe/x86_64-linux-clang:$LD_LIBRARY_PATH" |
||||
exec ./_modeld |
||||
|
@ -0,0 +1,247 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "common/visionbuf.h" |
||||
#include "common/visionipc.h" |
||||
#include "common/swaglog.h" |
||||
|
||||
#include "models/driving.h" |
||||
|
||||
volatile sig_atomic_t do_exit = 0; |
||||
|
||||
static void set_do_exit(int sig) { |
||||
do_exit = 1; |
||||
} |
||||
|
||||
// globals
|
||||
bool run_model; |
||||
mat3 cur_transform; |
||||
pthread_mutex_t transform_lock; |
||||
|
||||
void* live_thread(void *arg) { |
||||
int err; |
||||
set_thread_name("live"); |
||||
|
||||
Context * c = Context::create(); |
||||
SubSocket * live_calibration_sock = SubSocket::create(c, "liveCalibration"); |
||||
assert(live_calibration_sock != NULL); |
||||
|
||||
Poller * poller = Poller::create({live_calibration_sock}); |
||||
|
||||
/*
|
||||
import numpy as np |
||||
from common.transformations.model import medmodel_frame_from_road_frame |
||||
medmodel_frame_from_ground = medmodel_frame_from_road_frame[:, (0, 1, 3)] |
||||
ground_from_medmodel_frame = np.linalg.inv(medmodel_frame_from_ground) |
||||
*/ |
||||
Eigen::Matrix<float, 3, 3> ground_from_medmodel_frame; |
||||
ground_from_medmodel_frame << |
||||
0.00000000e+00, 0.00000000e+00, 1.00000000e+00, |
||||
-1.09890110e-03, 0.00000000e+00, 2.81318681e-01, |
||||
-1.84808520e-20, 9.00738606e-04,-4.28751576e-02; |
||||
|
||||
Eigen::Matrix<float, 3, 3> eon_intrinsics; |
||||
eon_intrinsics << |
||||
910.0, 0.0, 582.0, |
||||
0.0, 910.0, 437.0, |
||||
0.0, 0.0, 1.0; |
||||
|
||||
while (!do_exit) { |
||||
for (auto sock : poller->poll(10)){ |
||||
Message * msg = sock->receive(); |
||||
|
||||
auto amsg = kj::heapArray<capnp::word>((msg->getSize() / sizeof(capnp::word)) + 1); |
||||
memcpy(amsg.begin(), msg->getData(), msg->getSize()); |
||||
|
||||
capnp::FlatArrayMessageReader cmsg(amsg); |
||||
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>(); |
||||
|
||||
if (event.isLiveCalibration()) { |
||||
pthread_mutex_lock(&transform_lock); |
||||
|
||||
auto extrinsic_matrix = event.getLiveCalibration().getExtrinsicMatrix(); |
||||
Eigen::Matrix<float, 3, 4> extrinsic_matrix_eigen; |
||||
for (int i = 0; i < 4*3; i++){ |
||||
extrinsic_matrix_eigen(i / 4, i % 4) = extrinsic_matrix[i]; |
||||
} |
||||
|
||||
auto camera_frame_from_road_frame = eon_intrinsics * extrinsic_matrix_eigen; |
||||
Eigen::Matrix<float, 3, 3> camera_frame_from_ground; |
||||
camera_frame_from_ground.col(0) = camera_frame_from_road_frame.col(0); |
||||
camera_frame_from_ground.col(1) = camera_frame_from_road_frame.col(1); |
||||
camera_frame_from_ground.col(2) = camera_frame_from_road_frame.col(3); |
||||
|
||||
auto warp_matrix = camera_frame_from_ground * ground_from_medmodel_frame; |
||||
|
||||
for (int i=0; i<3*3; i++) { |
||||
cur_transform.v[i] = warp_matrix(i / 3, i % 3); |
||||
} |
||||
|
||||
run_model = true; |
||||
pthread_mutex_unlock(&transform_lock); |
||||
} |
||||
|
||||
delete msg; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
int err; |
||||
set_realtime_priority(1); |
||||
|
||||
// start calibration thread
|
||||
pthread_t live_thread_handle; |
||||
err = pthread_create(&live_thread_handle, NULL, live_thread, NULL); |
||||
assert(err == 0); |
||||
|
||||
// messaging
|
||||
Context *msg_context = Context::create(); |
||||
PubSocket *model_sock = PubSocket::create(msg_context, "model"); |
||||
PubSocket *posenet_sock = PubSocket::create(msg_context, "cameraOdometry"); |
||||
SubSocket *pathplan_sock = SubSocket::create(msg_context, "pathPlan", "127.0.0.1", true); |
||||
|
||||
assert(model_sock != NULL); |
||||
assert(posenet_sock != NULL); |
||||
assert(pathplan_sock != NULL); |
||||
|
||||
// cl init
|
||||
cl_device_id device_id; |
||||
cl_context context; |
||||
cl_command_queue q; |
||||
{ |
||||
// TODO: refactor this
|
||||
cl_platform_id platform_id[2]; |
||||
cl_uint num_devices; |
||||
cl_uint num_platforms; |
||||
|
||||
err = clGetPlatformIDs(sizeof(platform_id)/sizeof(cl_platform_id), platform_id, &num_platforms); |
||||
assert(err == 0); |
||||
|
||||
#ifdef QCOM |
||||
int clPlatform = 0; |
||||
#else |
||||
// don't use nvidia on pc, it's broken
|
||||
// TODO: write this nicely
|
||||
int clPlatform = num_platforms-1; |
||||
#endif |
||||
|
||||
char cBuffer[1024]; |
||||
clGetPlatformInfo(platform_id[clPlatform], CL_PLATFORM_NAME, sizeof(cBuffer), &cBuffer, NULL); |
||||
LOGD("got %d opencl platform(s), using %s", num_platforms, cBuffer); |
||||
|
||||
err = clGetDeviceIDs(platform_id[clPlatform], CL_DEVICE_TYPE_DEFAULT, 1, |
||||
&device_id, &num_devices); |
||||
assert(err == 0); |
||||
|
||||
context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
q = clCreateCommandQueue(context, device_id, 0, &err); |
||||
assert(err == 0); |
||||
} |
||||
|
||||
// init the models
|
||||
ModelState model; |
||||
model_init(&model, device_id, context, true); |
||||
LOGW("models loaded, modeld starting"); |
||||
|
||||
// debayering does a 2x downscale
|
||||
mat3 yuv_transform = transform_scale_buffer((mat3){{ |
||||
1.0, 0.0, 0.0, |
||||
0.0, 1.0, 0.0, |
||||
0.0, 0.0, 1.0, |
||||
}}, 0.5); |
||||
|
||||
// loop
|
||||
VisionStream stream; |
||||
while (!do_exit) { |
||||
VisionStreamBufs buf_info; |
||||
err = visionstream_init(&stream, VISION_STREAM_YUV, true, &buf_info); |
||||
if (err) { |
||||
printf("visionstream connect fail\n"); |
||||
usleep(100000); |
||||
continue; |
||||
} |
||||
LOGW("connected with buffer size: %d", buf_info.buf_len); |
||||
|
||||
// one frame in memory
|
||||
cl_mem yuv_cl; |
||||
VisionBuf yuv_ion = visionbuf_allocate_cl(buf_info.buf_len, device_id, context, &yuv_cl); |
||||
|
||||
double last = 0; |
||||
int desire = -1; |
||||
while (!do_exit) { |
||||
VIPCBuf *buf; |
||||
VIPCBufExtra extra; |
||||
buf = visionstream_get(&stream, &extra); |
||||
if (buf == NULL) { |
||||
printf("visionstream get failed\n"); |
||||
break; |
||||
} |
||||
|
||||
pthread_mutex_lock(&transform_lock); |
||||
mat3 transform = cur_transform; |
||||
const bool run_model_this_iter = run_model; |
||||
pthread_mutex_unlock(&transform_lock); |
||||
|
||||
Message *msg = pathplan_sock->receive(true); |
||||
if (msg != NULL) { |
||||
// TODO: copy and pasted from camerad/main.cc
|
||||
auto amsg = kj::heapArray<capnp::word>((msg->getSize() / sizeof(capnp::word)) + 1); |
||||
memcpy(amsg.begin(), msg->getData(), msg->getSize()); |
||||
|
||||
capnp::FlatArrayMessageReader cmsg(amsg); |
||||
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>(); |
||||
|
||||
// TODO: path planner timeout?
|
||||
desire = ((int)event.getPathPlan().getDesire()) - 1; |
||||
delete msg; |
||||
} |
||||
|
||||
double mt1 = 0, mt2 = 0; |
||||
if (run_model_this_iter) { |
||||
float vec_desire[DESIRE_SIZE] = {0}; |
||||
if (desire >= 0 && desire < DESIRE_SIZE) { |
||||
vec_desire[desire] = 1.0; |
||||
} |
||||
|
||||
mat3 model_transform = matmul3(yuv_transform, transform); |
||||
|
||||
mt1 = millis_since_boot(); |
||||
|
||||
// TODO: don't make copies!
|
||||
memcpy(yuv_ion.addr, buf->addr, buf_info.buf_len); |
||||
|
||||
ModelDataRaw model_buf = |
||||
model_eval_frame(&model, q, yuv_cl, buf_info.width, buf_info.height, |
||||
model_transform, NULL, vec_desire); |
||||
mt2 = millis_since_boot(); |
||||
|
||||
model_publish(model_sock, extra.frame_id, model_buf, extra.timestamp_eof); |
||||
posenet_publish(posenet_sock, extra.frame_id, model_buf, extra.timestamp_eof); |
||||
|
||||
LOGD("model process: %.2fms, from last %.2fms", mt2-mt1, mt1-last); |
||||
last = mt1; |
||||
} |
||||
|
||||
} |
||||
visionbuf_free(&yuv_ion); |
||||
} |
||||
|
||||
visionstream_destroy(&stream); |
||||
|
||||
delete model_sock; |
||||
|
||||
model_free(&model); |
||||
|
||||
LOG("joining live_thread"); |
||||
err = pthread_join(live_thread_handle, NULL); |
||||
assert(err == 0); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,68 @@ |
||||
#include "commonmodel.h" |
||||
|
||||
#include <czmq.h> |
||||
#include "cereal/gen/c/log.capnp.h" |
||||
#include "common/mat.h" |
||||
#include "common/timing.h" |
||||
|
||||
void frame_init(ModelFrame* frame, int width, int height, |
||||
cl_device_id device_id, cl_context context) { |
||||
int err; |
||||
frame->device_id = device_id; |
||||
frame->context = context; |
||||
|
||||
transform_init(&frame->transform, context, device_id); |
||||
frame->transformed_width = width; |
||||
frame->transformed_height = height; |
||||
|
||||
frame->transformed_y_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, |
||||
frame->transformed_width*frame->transformed_height, NULL, &err); |
||||
assert(err == 0); |
||||
frame->transformed_u_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, |
||||
(frame->transformed_width/2)*(frame->transformed_height/2), NULL, &err); |
||||
assert(err == 0); |
||||
frame->transformed_v_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, |
||||
(frame->transformed_width/2)*(frame->transformed_height/2), NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
frame->net_input_size = ((width*height*3)/2)*sizeof(float); |
||||
frame->net_input = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, |
||||
frame->net_input_size, (void*)NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
loadyuv_init(&frame->loadyuv, context, device_id, frame->transformed_width, frame->transformed_height); |
||||
} |
||||
|
||||
float *frame_prepare(ModelFrame* frame, cl_command_queue q, |
||||
cl_mem yuv_cl, int width, int height, |
||||
mat3 transform) { |
||||
int err; |
||||
int i = 0; |
||||
transform_queue(&frame->transform, q, |
||||
yuv_cl, width, height, |
||||
frame->transformed_y_cl, frame->transformed_u_cl, frame->transformed_v_cl, |
||||
frame->transformed_width, frame->transformed_height, |
||||
transform); |
||||
loadyuv_queue(&frame->loadyuv, q, |
||||
frame->transformed_y_cl, frame->transformed_u_cl, frame->transformed_v_cl, |
||||
frame->net_input); |
||||
float *net_input_buf = (float *)clEnqueueMapBuffer(q, frame->net_input, CL_TRUE, |
||||
CL_MAP_READ, 0, frame->net_input_size, |
||||
0, NULL, NULL, &err); |
||||
clFinish(q); |
||||
return net_input_buf; |
||||
} |
||||
|
||||
void frame_free(ModelFrame* frame) { |
||||
transform_destroy(&frame->transform); |
||||
loadyuv_destroy(&frame->loadyuv); |
||||
} |
||||
|
||||
|
||||
float sigmoid(float input) { |
||||
return 1 / (1 + expf(-input)); |
||||
} |
||||
|
||||
float softplus(float input) { |
||||
return log1p(expf(input)); |
||||
} |
@ -0,0 +1,42 @@ |
||||
#ifndef COMMONMODEL_H |
||||
#define COMMONMODEL_H |
||||
|
||||
#include <CL/cl.h> |
||||
|
||||
#include "common/mat.h" |
||||
#include "transforms/transform.h" |
||||
#include "transforms/loadyuv.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
float softplus(float input); |
||||
float sigmoid(float input); |
||||
|
||||
typedef struct ModelFrame { |
||||
cl_device_id device_id; |
||||
cl_context context; |
||||
|
||||
// input
|
||||
Transform transform; |
||||
int transformed_width, transformed_height; |
||||
cl_mem transformed_y_cl, transformed_u_cl, transformed_v_cl; |
||||
LoadYUVState loadyuv; |
||||
cl_mem net_input; |
||||
size_t net_input_size; |
||||
} ModelFrame; |
||||
|
||||
void frame_init(ModelFrame* frame, int width, int height, |
||||
cl_device_id device_id, cl_context context); |
||||
float *frame_prepare(ModelFrame* frame, cl_command_queue q, |
||||
cl_mem yuv_cl, int width, int height, |
||||
mat3 transform); |
||||
void frame_free(ModelFrame* frame); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,287 @@ |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
#include <fcntl.h> |
||||
#include <unistd.h> |
||||
#include "common/timing.h" |
||||
#include "driving.h" |
||||
|
||||
|
||||
#define PATH_IDX 0 |
||||
#define LL_IDX PATH_IDX + MODEL_PATH_DISTANCE*2 |
||||
#define RL_IDX LL_IDX + MODEL_PATH_DISTANCE*2 + 1 |
||||
#define LEAD_IDX RL_IDX + MODEL_PATH_DISTANCE*2 + 1 |
||||
#define LONG_X_IDX LEAD_IDX + MDN_GROUP_SIZE*LEAD_MDN_N + SELECTION |
||||
#define LONG_V_IDX LONG_X_IDX + TIME_DISTANCE*2 |
||||
#define LONG_A_IDX LONG_V_IDX + TIME_DISTANCE*2 |
||||
#define META_IDX LONG_A_IDX + TIME_DISTANCE*2 |
||||
#define POSE_IDX META_IDX + OTHER_META_SIZE + DESIRE_PRED_SIZE |
||||
#define OUTPUT_SIZE POSE_IDX + POSE_SIZE |
||||
#ifdef TEMPORAL |
||||
#define TEMPORAL_SIZE 512 |
||||
#else |
||||
#define TEMPORAL_SIZE 0 |
||||
#endif |
||||
|
||||
// #define DUMP_YUV
|
||||
|
||||
Eigen::Matrix<float, MODEL_PATH_DISTANCE, POLYFIT_DEGREE - 1> vander; |
||||
|
||||
void model_init(ModelState* s, cl_device_id device_id, cl_context context, int temporal) { |
||||
frame_init(&s->frame, MODEL_WIDTH, MODEL_HEIGHT, device_id, context); |
||||
s->input_frames = (float*)calloc(MODEL_FRAME_SIZE * 2, sizeof(float)); |
||||
|
||||
const int output_size = OUTPUT_SIZE + TEMPORAL_SIZE; |
||||
s->output = (float*)calloc(output_size, sizeof(float)); |
||||
|
||||
s->m = new DefaultRunModel("../../models/supercombo.dlc", s->output, output_size, USE_GPU_RUNTIME); |
||||
|
||||
#ifdef TEMPORAL |
||||
assert(temporal); |
||||
s->m->addRecurrent(&s->output[OUTPUT_SIZE], TEMPORAL_SIZE); |
||||
#endif |
||||
|
||||
#ifdef DESIRE |
||||
s->desire = (float*)malloc(DESIRE_SIZE * sizeof(float)); |
||||
for (int i = 0; i < DESIRE_SIZE; i++) s->desire[i] = 0.0; |
||||
s->m->addDesire(s->desire, DESIRE_SIZE); |
||||
#endif |
||||
|
||||
// Build Vandermonde matrix
|
||||
for(int i = 0; i < MODEL_PATH_DISTANCE; i++) { |
||||
for(int j = 0; j < POLYFIT_DEGREE - 1; j++) { |
||||
vander(i, j) = pow(i, POLYFIT_DEGREE-j-1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
ModelDataRaw model_eval_frame(ModelState* s, cl_command_queue q, |
||||
cl_mem yuv_cl, int width, int height, |
||||
mat3 transform, void* sock, float *desire_in) { |
||||
#ifdef DESIRE |
||||
if (desire_in != NULL) { |
||||
for (int i = 0; i < DESIRE_SIZE; i++) s->desire[i] = desire_in[i]; |
||||
} |
||||
#endif |
||||
|
||||
//for (int i = 0; i < OUTPUT_SIZE + TEMPORAL_SIZE; i++) { printf("%f ", s->output[i]); } printf("\n");
|
||||
|
||||
float *new_frame_buf = frame_prepare(&s->frame, q, yuv_cl, width, height, transform); |
||||
memmove(&s->input_frames[0], &s->input_frames[MODEL_FRAME_SIZE], sizeof(float)*MODEL_FRAME_SIZE); |
||||
memmove(&s->input_frames[MODEL_FRAME_SIZE], new_frame_buf, sizeof(float)*MODEL_FRAME_SIZE); |
||||
s->m->execute(s->input_frames); |
||||
|
||||
#ifdef DUMP_YUV |
||||
FILE *dump_yuv_file = fopen("/sdcard/dump.yuv", "wb"); |
||||
fwrite(new_frame_buf, MODEL_HEIGHT*MODEL_WIDTH*3/2, sizeof(float), dump_yuv_file); |
||||
fclose(dump_yuv_file); |
||||
assert(1==2); |
||||
#endif |
||||
|
||||
// net outputs
|
||||
ModelDataRaw net_outputs; |
||||
net_outputs.path = &s->output[PATH_IDX]; |
||||
net_outputs.left_lane = &s->output[LL_IDX]; |
||||
net_outputs.right_lane = &s->output[RL_IDX]; |
||||
net_outputs.lead = &s->output[LEAD_IDX]; |
||||
net_outputs.long_x = &s->output[LONG_X_IDX]; |
||||
net_outputs.long_v = &s->output[LONG_V_IDX]; |
||||
net_outputs.long_a = &s->output[LONG_A_IDX]; |
||||
net_outputs.meta = &s->output[META_IDX]; |
||||
net_outputs.pose = &s->output[POSE_IDX]; |
||||
return net_outputs; |
||||
} |
||||
|
||||
void model_free(ModelState* s) { |
||||
free(s->output); |
||||
free(s->input_frames); |
||||
frame_free(&s->frame); |
||||
delete s->m; |
||||
} |
||||
|
||||
void poly_fit(float *in_pts, float *in_stds, float *out) { |
||||
// References to inputs
|
||||
Eigen::Map<Eigen::Matrix<float, MODEL_PATH_DISTANCE, 1> > pts(in_pts, MODEL_PATH_DISTANCE); |
||||
Eigen::Map<Eigen::Matrix<float, MODEL_PATH_DISTANCE, 1> > std(in_stds, MODEL_PATH_DISTANCE); |
||||
Eigen::Map<Eigen::Matrix<float, POLYFIT_DEGREE - 1, 1> > p(out, POLYFIT_DEGREE - 1); |
||||
|
||||
float y0 = pts[0]; |
||||
pts = pts.array() - y0; |
||||
|
||||
// Build Least Squares equations
|
||||
Eigen::Matrix<float, MODEL_PATH_DISTANCE, POLYFIT_DEGREE - 1> lhs = vander.array().colwise() / std.array(); |
||||
Eigen::Matrix<float, MODEL_PATH_DISTANCE, 1> rhs = pts.array() / std.array(); |
||||
|
||||
// Improve numerical stability
|
||||
Eigen::Matrix<float, POLYFIT_DEGREE - 1, 1> scale = 1. / (lhs.array()*lhs.array()).sqrt().colwise().sum(); |
||||
lhs = lhs * scale.asDiagonal(); |
||||
|
||||
// Solve inplace
|
||||
Eigen::ColPivHouseholderQR<Eigen::Ref<Eigen::MatrixXf> > qr(lhs); |
||||
p = qr.solve(rhs); |
||||
|
||||
// Apply scale to output
|
||||
p = p.transpose() * scale.asDiagonal(); |
||||
out[3] = y0; |
||||
} |
||||
|
||||
|
||||
void fill_path(cereal::ModelData::PathData::Builder path, const float * data, bool has_prob, const float offset) { |
||||
float points_arr[MODEL_PATH_DISTANCE]; |
||||
float stds_arr[MODEL_PATH_DISTANCE]; |
||||
float poly_arr[POLYFIT_DEGREE]; |
||||
float std; |
||||
float prob; |
||||
|
||||
for (int i=0; i<MODEL_PATH_DISTANCE; i++) { |
||||
points_arr[i] = data[i] + offset; |
||||
stds_arr[i] = softplus(data[MODEL_PATH_DISTANCE + i]); |
||||
} |
||||
if (has_prob) { |
||||
prob = sigmoid(data[MODEL_PATH_DISTANCE*2]); |
||||
} else { |
||||
prob = 1.0; |
||||
} |
||||
std = softplus(data[MODEL_PATH_DISTANCE]); |
||||
poly_fit(points_arr, stds_arr, poly_arr); |
||||
|
||||
if (std::getenv("DEBUG")){ |
||||
kj::ArrayPtr<const float> stds(&stds_arr[0], ARRAYSIZE(stds_arr)); |
||||
path.setStds(stds); |
||||
|
||||
kj::ArrayPtr<const float> points(&points_arr[0], ARRAYSIZE(points_arr)); |
||||
path.setPoints(points); |
||||
} |
||||
|
||||
kj::ArrayPtr<const float> poly(&poly_arr[0], ARRAYSIZE(poly_arr)); |
||||
path.setPoly(poly); |
||||
path.setProb(prob); |
||||
path.setStd(std); |
||||
} |
||||
|
||||
void fill_lead(cereal::ModelData::LeadData::Builder lead, const float * data, int mdn_max_idx) { |
||||
const double x_scale = 10.0; |
||||
const double y_scale = 10.0; |
||||
|
||||
lead.setProb(sigmoid(data[LEAD_MDN_N*MDN_GROUP_SIZE])); |
||||
lead.setDist(x_scale * data[mdn_max_idx*MDN_GROUP_SIZE]); |
||||
lead.setStd(x_scale * softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS])); |
||||
lead.setRelY(y_scale * data[mdn_max_idx*MDN_GROUP_SIZE + 1]); |
||||
lead.setRelYStd(y_scale * softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 1])); |
||||
lead.setRelVel(data[mdn_max_idx*MDN_GROUP_SIZE + 2]); |
||||
lead.setRelVelStd(softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 2])); |
||||
lead.setRelA(data[mdn_max_idx*MDN_GROUP_SIZE + 3]); |
||||
lead.setRelAStd(softplus(data[mdn_max_idx*MDN_GROUP_SIZE + MDN_VALS + 3])); |
||||
} |
||||
|
||||
void fill_meta(cereal::ModelData::MetaData::Builder meta, const float * meta_data) { |
||||
meta.setEngagedProb(meta_data[0]); |
||||
meta.setGasDisengageProb(meta_data[1]); |
||||
meta.setBrakeDisengageProb(meta_data[2]); |
||||
meta.setSteerOverrideProb(meta_data[3]); |
||||
kj::ArrayPtr<const float> desire_pred(&meta_data[OTHER_META_SIZE], DESIRE_PRED_SIZE); |
||||
meta.setDesirePrediction(desire_pred); |
||||
} |
||||
|
||||
void fill_longi(cereal::ModelData::LongitudinalData::Builder longi, const float * long_v_data, const float * long_a_data) { |
||||
// just doing 10 vals, 1 every sec for now
|
||||
float speed_arr[TIME_DISTANCE/10]; |
||||
float accel_arr[TIME_DISTANCE/10]; |
||||
for (int i=0; i<TIME_DISTANCE/10; i++) { |
||||
speed_arr[i] = long_v_data[i*10]; |
||||
accel_arr[i] = long_a_data[i*10]; |
||||
} |
||||
kj::ArrayPtr<const float> speed(&speed_arr[0], ARRAYSIZE(speed_arr)); |
||||
longi.setSpeeds(speed); |
||||
kj::ArrayPtr<const float> accel(&accel_arr[0], ARRAYSIZE(accel_arr)); |
||||
longi.setAccelerations(accel); |
||||
} |
||||
|
||||
void model_publish(PubSocket *sock, uint32_t frame_id, |
||||
const ModelDataRaw net_outputs, uint64_t timestamp_eof) { |
||||
// make msg
|
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
|
||||
auto framed = event.initModel(); |
||||
framed.setFrameId(frame_id); |
||||
framed.setTimestampEof(timestamp_eof); |
||||
|
||||
auto lpath = framed.initPath(); |
||||
fill_path(lpath, net_outputs.path, false, 0); |
||||
auto left_lane = framed.initLeftLane(); |
||||
fill_path(left_lane, net_outputs.left_lane, true, 1.8); |
||||
auto right_lane = framed.initRightLane(); |
||||
fill_path(right_lane, net_outputs.right_lane, true, -1.8); |
||||
auto longi = framed.initLongitudinal(); |
||||
fill_longi(longi, net_outputs.long_v, net_outputs.long_a); |
||||
|
||||
|
||||
// Find the distribution that corresponds to the current lead
|
||||
int mdn_max_idx = 0; |
||||
for (int i=1; i<LEAD_MDN_N; i++) { |
||||
if (net_outputs.lead[i*MDN_GROUP_SIZE + 8] > net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 8]) { |
||||
mdn_max_idx = i; |
||||
} |
||||
} |
||||
auto lead = framed.initLead(); |
||||
fill_lead(lead, net_outputs.lead, mdn_max_idx); |
||||
// Find the distribution that corresponds to the lead in 2s
|
||||
mdn_max_idx = 0; |
||||
for (int i=1; i<LEAD_MDN_N; i++) { |
||||
if (net_outputs.lead[i*MDN_GROUP_SIZE + 9] > net_outputs.lead[mdn_max_idx*MDN_GROUP_SIZE + 9]) { |
||||
mdn_max_idx = i; |
||||
} |
||||
} |
||||
auto lead_future = framed.initLeadFuture(); |
||||
fill_lead(lead_future, net_outputs.lead, mdn_max_idx); |
||||
|
||||
|
||||
auto meta = framed.initMeta(); |
||||
fill_meta(meta, net_outputs.meta); |
||||
|
||||
|
||||
// send message
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
sock->send((char*)bytes.begin(), bytes.size()); |
||||
} |
||||
|
||||
void posenet_publish(PubSocket *sock, uint32_t frame_id, |
||||
const ModelDataRaw net_outputs, uint64_t timestamp_eof) { |
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
|
||||
float trans_arr[3]; |
||||
float trans_std_arr[3]; |
||||
float rot_arr[3]; |
||||
float rot_std_arr[3]; |
||||
|
||||
for (int i =0; i < 3; i++) { |
||||
trans_arr[i] = net_outputs.pose[i]; |
||||
trans_std_arr[i] = softplus(net_outputs.pose[6 + i]) + 1e-6; |
||||
|
||||
rot_arr[i] = M_PI * net_outputs.pose[3 + i] / 180.0; |
||||
rot_std_arr[i] = M_PI * (softplus(net_outputs.pose[9 + i]) + 1e-6) / 180.0; |
||||
} |
||||
|
||||
auto posenetd = event.initCameraOdometry(); |
||||
kj::ArrayPtr<const float> trans_vs(&trans_arr[0], 3); |
||||
posenetd.setTrans(trans_vs); |
||||
kj::ArrayPtr<const float> rot_vs(&rot_arr[0], 3); |
||||
posenetd.setRot(rot_vs); |
||||
kj::ArrayPtr<const float> trans_std_vs(&trans_std_arr[0], 3); |
||||
posenetd.setTransStd(trans_std_vs); |
||||
kj::ArrayPtr<const float> rot_std_vs(&rot_std_arr[0], 3); |
||||
posenetd.setRotStd(rot_std_vs); |
||||
|
||||
posenetd.setTimestampEof(timestamp_eof); |
||||
posenetd.setFrameId(frame_id); |
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
sock->send((char*)bytes.begin(), bytes.size()); |
||||
} |
@ -0,0 +1,81 @@ |
||||
#ifndef MODEL_H |
||||
#define MODEL_H |
||||
|
||||
// gate this here
|
||||
#define TEMPORAL |
||||
#define DESIRE |
||||
|
||||
#ifdef DESIRE |
||||
#define DESIRE_SIZE 8 |
||||
#endif |
||||
|
||||
#ifdef QCOM |
||||
#include <eigen3/Eigen/Dense> |
||||
#else |
||||
#include <Eigen/Dense> |
||||
#endif |
||||
|
||||
#include "common/mat.h" |
||||
#include "common/util.h" |
||||
|
||||
#include "commonmodel.h" |
||||
#include "runners/run.h" |
||||
|
||||
#include "cereal/gen/cpp/log.capnp.h" |
||||
#include <czmq.h> |
||||
#include <capnp/serialize.h> |
||||
#include "messaging.hpp" |
||||
|
||||
#define MODEL_WIDTH 512 |
||||
#define MODEL_HEIGHT 256 |
||||
#define MODEL_FRAME_SIZE MODEL_WIDTH * MODEL_HEIGHT * 3 / 2 |
||||
#define MODEL_NAME "supercombo_dlc" |
||||
|
||||
#define MODEL_PATH_DISTANCE 192 |
||||
#define POLYFIT_DEGREE 4 |
||||
#define SPEED_PERCENTILES 10 |
||||
#define DESIRE_PRED_SIZE 32 |
||||
#define OTHER_META_SIZE 4 |
||||
#define LEAD_MDN_N 5 // probs for 5 groups
|
||||
#define MDN_VALS 4 // output xyva for each lead group
|
||||
#define SELECTION 3 //output 3 group (lead now, in 2s and 6s)
|
||||
#define MDN_GROUP_SIZE 11 |
||||
#define TIME_DISTANCE 100 |
||||
#define POSE_SIZE 12 |
||||
|
||||
struct ModelDataRaw { |
||||
float *path; |
||||
float *left_lane; |
||||
float *right_lane; |
||||
float *lead; |
||||
float *long_x; |
||||
float *long_v; |
||||
float *long_a; |
||||
float *meta; |
||||
float *pose; |
||||
}; |
||||
|
||||
|
||||
typedef struct ModelState { |
||||
ModelFrame frame; |
||||
float *output; |
||||
float *input_frames; |
||||
RunModel *m; |
||||
#ifdef DESIRE |
||||
float *desire; |
||||
#endif |
||||
} ModelState; |
||||
|
||||
void model_init(ModelState* s, cl_device_id device_id, |
||||
cl_context context, int temporal); |
||||
ModelDataRaw model_eval_frame(ModelState* s, cl_command_queue q, |
||||
cl_mem yuv_cl, int width, int height, |
||||
mat3 transform, void* sock, float *desire_in); |
||||
void model_free(ModelState* s); |
||||
void poly_fit(float *in_pts, float *in_stds, float *out); |
||||
|
||||
void model_publish(PubSocket* sock, uint32_t frame_id, |
||||
const ModelDataRaw data, uint64_t timestamp_eof); |
||||
void posenet_publish(PubSocket* sock, uint32_t frame_id, |
||||
const ModelDataRaw data, uint64_t timestamp_eof); |
||||
#endif |
@ -0,0 +1,163 @@ |
||||
#include <string.h> |
||||
#include "monitoring.h" |
||||
#include "common/mat.h" |
||||
#include "common/timing.h" |
||||
|
||||
// #include <fastcv.h>
|
||||
#include <libyuv.h> |
||||
|
||||
#define MODEL_WIDTH 160 |
||||
#define MODEL_HEIGHT 320 |
||||
#define FULL_W 426 |
||||
|
||||
void monitoring_init(MonitoringState* s) { |
||||
s->m = new DefaultRunModel("../../models/monitoring_model_q.dlc", (float*)&s->output, OUTPUT_SIZE, USE_DSP_RUNTIME); |
||||
} |
||||
|
||||
MonitoringResult monitoring_eval_frame(MonitoringState* s, void* stream_buf, int width, int height) { |
||||
|
||||
uint8_t *raw_buf = (uint8_t*) stream_buf; |
||||
uint8_t *raw_y_buf = raw_buf; |
||||
uint8_t *raw_u_buf = raw_y_buf + (width * height); |
||||
uint8_t *raw_v_buf = raw_u_buf + ((width/2) * (height/2)); |
||||
|
||||
int cropped_width = height/2; |
||||
int cropped_height = height; |
||||
|
||||
int resized_width = MODEL_WIDTH; |
||||
int resized_height = MODEL_HEIGHT; |
||||
|
||||
uint8_t *cropped_buf = new uint8_t[cropped_width*cropped_height*3/2]; |
||||
uint8_t *cropped_y_buf = cropped_buf; |
||||
uint8_t *cropped_u_buf = cropped_y_buf + (cropped_width * cropped_height); |
||||
uint8_t *cropped_v_buf = cropped_u_buf + ((cropped_width/2) * (cropped_height/2)); |
||||
|
||||
if (true) { |
||||
for (int r = 0; r < height/2; r++) { |
||||
memcpy(cropped_y_buf + 2*r*cropped_width, raw_y_buf + 2*r*width + (width - cropped_width), cropped_width); |
||||
memcpy(cropped_y_buf + (2*r+1)*cropped_width, raw_y_buf + (2*r+1)*width + (width - cropped_width), cropped_width); |
||||
memcpy(cropped_u_buf + r*cropped_width/2, raw_u_buf + r*width/2 + ((width/2) - (cropped_width/2)), cropped_width/2); |
||||
memcpy(cropped_v_buf + r*cropped_width/2, raw_v_buf + r*width/2 + ((width/2) - (cropped_width/2)), cropped_width/2); |
||||
} |
||||
} else { |
||||
// not tested
|
||||
uint8_t *premirror_cropped_buf = new uint8_t[cropped_width*cropped_height*3/2]; |
||||
uint8_t *premirror_cropped_y_buf = premirror_cropped_buf; |
||||
uint8_t *premirror_cropped_u_buf = premirror_cropped_y_buf + (cropped_width * cropped_height); |
||||
uint8_t *premirror_cropped_v_buf = premirror_cropped_u_buf + ((cropped_width/2) * (cropped_height/2)); |
||||
for (int r = 0; r < height/2; r++) { |
||||
memcpy(premirror_cropped_y_buf + 2*r*cropped_width, raw_y_buf + 2*r*width, cropped_width); |
||||
memcpy(premirror_cropped_y_buf + (2*r+1)*cropped_width, raw_y_buf + (2*r+1)*width, cropped_width); |
||||
memcpy(premirror_cropped_u_buf + r*cropped_width/2, raw_u_buf + r*width/2, cropped_width/2); |
||||
memcpy(premirror_cropped_v_buf + r*cropped_width/2, raw_v_buf + r*width/2, cropped_width/2); |
||||
} |
||||
libyuv::I420Mirror(premirror_cropped_y_buf, cropped_width, |
||||
premirror_cropped_u_buf, cropped_width/2, |
||||
premirror_cropped_v_buf, cropped_width/2, |
||||
cropped_y_buf, cropped_width, |
||||
cropped_u_buf, cropped_width/2, |
||||
cropped_v_buf, cropped_width/2, |
||||
cropped_width, cropped_height); |
||||
} |
||||
|
||||
uint8_t *resized_buf = new uint8_t[resized_width*resized_height*3/2]; |
||||
uint8_t *resized_y_buf = resized_buf; |
||||
uint8_t *resized_u_buf = resized_y_buf + (resized_width * resized_height); |
||||
uint8_t *resized_v_buf = resized_u_buf + ((resized_width/2) * (resized_height/2)); |
||||
|
||||
libyuv::FilterMode mode = libyuv::FilterModeEnum::kFilterBilinear; |
||||
libyuv::I420Scale(cropped_y_buf, cropped_width, |
||||
cropped_u_buf, cropped_width/2, |
||||
cropped_v_buf, cropped_width/2, |
||||
cropped_width, cropped_height, |
||||
resized_y_buf, resized_width, |
||||
resized_u_buf, resized_width/2, |
||||
resized_v_buf, resized_width/2, |
||||
resized_width, resized_height, |
||||
mode); |
||||
|
||||
int yuv_buf_len = (MODEL_WIDTH/2) * (MODEL_HEIGHT/2) * 6; // Y|u|v -> y|y|y|y|u|v
|
||||
|
||||
float *net_input_buf = new float[yuv_buf_len]; |
||||
// one shot conversion, O(n) anyway
|
||||
// yuvframe2tensor, normalize
|
||||
for (int r = 0; r < MODEL_HEIGHT/2; r++) { |
||||
for (int c = 0; c < MODEL_WIDTH/2; c++) { |
||||
// Y_ul
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r] = (resized_buf[(2*r*resized_width) + (2*c)] - 128.f) * 0.0078125f; |
||||
// Y_ur
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + ((MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = (resized_buf[(2*r*resized_width) + (2*c+1)] - 128.f) * 0.0078125f; |
||||
// Y_dl
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (2*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = (resized_buf[(2*r*resized_width+1) + (2*c)] - 128.f) * 0.0078125f; |
||||
// Y_dr
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (3*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = (resized_buf[(2*r*resized_width+1) + (2*c+1)] - 128.f) * 0.0078125f; |
||||
// U
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (4*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = (resized_buf[(resized_width*resized_height) + (r*resized_width/2) + c] - 128.f) * 0.0078125f; |
||||
// V
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (5*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = (resized_buf[(resized_width*resized_height) + ((resized_width/2)*(resized_height/2)) + (r*resized_width/2) + c] - 128.f) * 0.0078125f; |
||||
} |
||||
} |
||||
|
||||
// FILE *dump_yuv_file = fopen("/sdcard/rawdump.yuv", "wb");
|
||||
// fwrite(raw_buf, height*width*3/2, sizeof(uint8_t), dump_yuv_file);
|
||||
// fclose(dump_yuv_file);
|
||||
|
||||
// FILE *dump_yuv_file2 = fopen("/sdcard/inputdump.yuv", "wb");
|
||||
// fwrite(net_input_buf, MODEL_HEIGHT*MODEL_WIDTH*3/2, sizeof(float), dump_yuv_file2);
|
||||
// fclose(dump_yuv_file2);
|
||||
|
||||
delete[] cropped_buf; |
||||
delete[] resized_buf; |
||||
s->m->execute(net_input_buf); |
||||
delete[] net_input_buf; |
||||
|
||||
MonitoringResult ret = {0}; |
||||
memcpy(&ret.face_orientation, &s->output[0], sizeof ret.face_orientation); |
||||
memcpy(&ret.face_orientation_meta, &s->output[6], sizeof ret.face_orientation_meta); |
||||
memcpy(&ret.face_position, &s->output[3], sizeof ret.face_position); |
||||
memcpy(&ret.face_position_meta, &s->output[9], sizeof ret.face_position_meta); |
||||
memcpy(&ret.face_prob, &s->output[12], sizeof ret.face_prob); |
||||
memcpy(&ret.left_eye_prob, &s->output[21], sizeof ret.left_eye_prob); |
||||
memcpy(&ret.right_eye_prob, &s->output[30], sizeof ret.right_eye_prob); |
||||
memcpy(&ret.left_blink_prob, &s->output[31], sizeof ret.right_eye_prob); |
||||
memcpy(&ret.right_blink_prob, &s->output[32], sizeof ret.right_eye_prob); |
||||
ret.face_orientation_meta[0] = softplus(ret.face_orientation_meta[0]); |
||||
ret.face_orientation_meta[1] = softplus(ret.face_orientation_meta[1]); |
||||
ret.face_orientation_meta[2] = softplus(ret.face_orientation_meta[2]); |
||||
ret.face_position_meta[0] = softplus(ret.face_position_meta[0]); |
||||
ret.face_position_meta[1] = softplus(ret.face_position_meta[1]); |
||||
return ret; |
||||
} |
||||
|
||||
void monitoring_publish(PubSocket* sock, uint32_t frame_id, const MonitoringResult res) { |
||||
// make msg
|
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
|
||||
auto framed = event.initDriverMonitoring(); |
||||
framed.setFrameId(frame_id); |
||||
|
||||
kj::ArrayPtr<const float> face_orientation(&res.face_orientation[0], ARRAYSIZE(res.face_orientation)); |
||||
kj::ArrayPtr<const float> face_orientation_std(&res.face_orientation_meta[0], ARRAYSIZE(res.face_orientation_meta)); |
||||
kj::ArrayPtr<const float> face_position(&res.face_position[0], ARRAYSIZE(res.face_position)); |
||||
kj::ArrayPtr<const float> face_position_std(&res.face_position_meta[0], ARRAYSIZE(res.face_position_meta)); |
||||
framed.setFaceOrientation(face_orientation); |
||||
framed.setFaceOrientationStd(face_orientation_std); |
||||
framed.setFacePosition(face_position); |
||||
framed.setFacePositionStd(face_position_std); |
||||
framed.setFaceProb(res.face_prob); |
||||
framed.setLeftEyeProb(res.left_eye_prob); |
||||
framed.setRightEyeProb(res.right_eye_prob); |
||||
framed.setLeftBlinkProb(res.left_blink_prob); |
||||
framed.setRightBlinkProb(res.right_blink_prob); |
||||
|
||||
// send message
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
sock->send((char*)bytes.begin(), bytes.size()); |
||||
} |
||||
|
||||
void monitoring_free(MonitoringState* s) { |
||||
delete s->m; |
||||
} |
@ -0,0 +1,44 @@ |
||||
#ifndef MONITORING_H |
||||
#define MONITORING_H |
||||
|
||||
#include "common/util.h" |
||||
#include "commonmodel.h" |
||||
#include "runners/run.h" |
||||
|
||||
#include "cereal/gen/cpp/log.capnp.h" |
||||
#include <capnp/serialize.h> |
||||
#include "messaging.hpp" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define OUTPUT_SIZE 33 |
||||
|
||||
typedef struct MonitoringResult { |
||||
float face_orientation[3]; |
||||
float face_orientation_meta[3]; |
||||
float face_position[2]; |
||||
float face_position_meta[2]; |
||||
float face_prob; |
||||
float left_eye_prob; |
||||
float right_eye_prob; |
||||
float left_blink_prob; |
||||
float right_blink_prob; |
||||
} MonitoringResult; |
||||
|
||||
typedef struct MonitoringState { |
||||
RunModel *m; |
||||
float output[OUTPUT_SIZE]; |
||||
} MonitoringState; |
||||
|
||||
void monitoring_init(MonitoringState* s); |
||||
MonitoringResult monitoring_eval_frame(MonitoringState* s, void* stream_buf, int width, int height); |
||||
void monitoring_publish(PubSocket *sock, uint32_t frame_id, const MonitoringResult res); |
||||
void monitoring_free(MonitoringState* s); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,5 @@ |
||||
#!/bin/sh |
||||
export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8:/home/batman/one/phonelibs/snpe/x86_64-linux-clang:$LD_LIBRARY_PATH" |
||||
export ADSP_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64-android-clang3.8/" |
||||
exec ./_monitoringd |
||||
|
@ -0,0 +1,80 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include <signal.h> |
||||
#include <cassert> |
||||
|
||||
#include "common/visionbuf.h" |
||||
#include "common/visionipc.h" |
||||
#include "common/swaglog.h" |
||||
|
||||
#include "models/monitoring.h" |
||||
|
||||
#ifndef PATH_MAX |
||||
#include <linux/limits.h> |
||||
#endif |
||||
|
||||
|
||||
volatile sig_atomic_t do_exit = 0; |
||||
|
||||
static void set_do_exit(int sig) { |
||||
do_exit = 1; |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
int err; |
||||
set_realtime_priority(1); |
||||
|
||||
// messaging
|
||||
Context *msg_context = Context::create(); |
||||
PubSocket *monitoring_sock = PubSocket::create(msg_context, "driverMonitoring"); |
||||
|
||||
// init the models
|
||||
MonitoringState monitoring; |
||||
monitoring_init(&monitoring); |
||||
|
||||
// loop
|
||||
VisionStream stream; |
||||
while (!do_exit) { |
||||
VisionStreamBufs buf_info; |
||||
err = visionstream_init(&stream, VISION_STREAM_YUV_FRONT, true, &buf_info); |
||||
if (err) { |
||||
printf("visionstream connect fail\n"); |
||||
usleep(100000); |
||||
continue; |
||||
} |
||||
LOGW("connected with buffer size: %d", buf_info.buf_len); |
||||
|
||||
double last = 0; |
||||
while (!do_exit) { |
||||
VIPCBuf *buf; |
||||
VIPCBufExtra extra; |
||||
buf = visionstream_get(&stream, &extra); |
||||
if (buf == NULL) { |
||||
printf("visionstream get failed\n"); |
||||
break; |
||||
} |
||||
//printf("frame_id: %d %dx%d\n", extra.frame_id, buf_info.width, buf_info.height);
|
||||
|
||||
double t1 = millis_since_boot(); |
||||
|
||||
MonitoringResult res = monitoring_eval_frame(&monitoring, buf->addr, buf_info.width, buf_info.height); |
||||
|
||||
double t2 = millis_since_boot(); |
||||
|
||||
// send dm packet
|
||||
monitoring_publish(monitoring_sock, extra.frame_id, res); |
||||
|
||||
LOGD("monitoring process: %.2fms, from last %.2fms", t2-t1, t1-last); |
||||
last = t1; |
||||
} |
||||
|
||||
} |
||||
|
||||
visionstream_destroy(&stream); |
||||
|
||||
delete monitoring_sock; |
||||
monitoring_free(&monitoring); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,18 @@ |
||||
#ifndef RUN_H |
||||
#define RUN_H |
||||
|
||||
#include "runmodel.h" |
||||
#include "snpemodel.h" |
||||
|
||||
#ifdef QCOM |
||||
#define DefaultRunModel SNPEModel |
||||
#else |
||||
#ifdef USE_TF_MODEL |
||||
#include "tfmodel.h" |
||||
#define DefaultRunModel TFModel |
||||
#else |
||||
#define DefaultRunModel SNPEModel |
||||
#endif |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,12 @@ |
||||
#ifndef RUNMODEL_H |
||||
#define RUNMODEL_H |
||||
|
||||
class RunModel { |
||||
public: |
||||
virtual void addRecurrent(float *state, int state_size) {} |
||||
virtual void addDesire(float *state, int state_size) {} |
||||
virtual void execute(float *net_input_buf) {} |
||||
}; |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,126 @@ |
||||
#include <cassert> |
||||
#include <stdlib.h> |
||||
#include "common/util.h" |
||||
#include "snpemodel.h" |
||||
|
||||
void PrintErrorStringAndExit() { |
||||
const char* const errStr = zdl::DlSystem::getLastErrorString(); |
||||
std::cerr << zdl::DlSystem::getLastErrorString() << std::endl; |
||||
std::exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
SNPEModel::SNPEModel(const char *path, float *output, size_t output_size, int runtime) { |
||||
#ifdef QCOM |
||||
zdl::DlSystem::Runtime_t Runtime; |
||||
if (runtime==USE_GPU_RUNTIME) { |
||||
Runtime = zdl::DlSystem::Runtime_t::GPU; |
||||
} else if (runtime==USE_DSP_RUNTIME) { |
||||
Runtime = zdl::DlSystem::Runtime_t::DSP; |
||||
} else { |
||||
Runtime = zdl::DlSystem::Runtime_t::CPU; |
||||
} |
||||
assert(zdl::SNPE::SNPEFactory::isRuntimeAvailable(Runtime)); |
||||
#endif |
||||
size_t model_size; |
||||
model_data = (uint8_t *)read_file(path, &model_size); |
||||
assert(model_data); |
||||
|
||||
// load model
|
||||
std::unique_ptr<zdl::DlContainer::IDlContainer> container = zdl::DlContainer::IDlContainer::open(model_data, model_size); |
||||
if (!container) { PrintErrorStringAndExit(); } |
||||
printf("loaded model with size: %u\n", model_size); |
||||
|
||||
// create model runner
|
||||
zdl::SNPE::SNPEBuilder snpeBuilder(container.get()); |
||||
while (!snpe) { |
||||
#ifdef QCOM |
||||
snpe = snpeBuilder.setOutputLayers({}) |
||||
.setRuntimeProcessor(Runtime) |
||||
.setUseUserSuppliedBuffers(true) |
||||
.setPerformanceProfile(zdl::DlSystem::PerformanceProfile_t::HIGH_PERFORMANCE) |
||||
.build(); |
||||
#else |
||||
snpe = snpeBuilder.setOutputLayers({}) |
||||
.setUseUserSuppliedBuffers(true) |
||||
.setPerformanceProfile(zdl::DlSystem::PerformanceProfile_t::HIGH_PERFORMANCE) |
||||
.build(); |
||||
#endif |
||||
if (!snpe) std::cerr << zdl::DlSystem::getLastErrorString() << std::endl; |
||||
} |
||||
|
||||
// get input and output names
|
||||
const auto &strListi_opt = snpe->getInputTensorNames(); |
||||
if (!strListi_opt) throw std::runtime_error("Error obtaining Input tensor names"); |
||||
const auto &strListi = *strListi_opt; |
||||
//assert(strListi.size() == 1);
|
||||
const char *input_tensor_name = strListi.at(0); |
||||
|
||||
const auto &strListo_opt = snpe->getOutputTensorNames(); |
||||
if (!strListo_opt) throw std::runtime_error("Error obtaining Output tensor names"); |
||||
const auto &strListo = *strListo_opt; |
||||
assert(strListo.size() == 1); |
||||
const char *output_tensor_name = strListo.at(0); |
||||
|
||||
printf("model: %s -> %s\n", input_tensor_name, output_tensor_name); |
||||
|
||||
zdl::DlSystem::UserBufferEncodingFloat userBufferEncodingFloat; |
||||
zdl::DlSystem::IUserBufferFactory& ubFactory = zdl::SNPE::SNPEFactory::getUserBufferFactory(); |
||||
|
||||
// create input buffer
|
||||
{ |
||||
const auto &inputDims_opt = snpe->getInputDimensions(input_tensor_name); |
||||
const zdl::DlSystem::TensorShape& bufferShape = *inputDims_opt; |
||||
std::vector<size_t> strides(bufferShape.rank()); |
||||
strides[strides.size() - 1] = sizeof(float); |
||||
size_t product = 1; |
||||
for (size_t i = 0; i < bufferShape.rank(); i++) product *= bufferShape[i]; |
||||
size_t stride = strides[strides.size() - 1]; |
||||
for (size_t i = bufferShape.rank() - 1; i > 0; i--) { |
||||
stride *= bufferShape[i]; |
||||
strides[i-1] = stride; |
||||
} |
||||
printf("input product is %u\n", product); |
||||
inputBuffer = ubFactory.createUserBuffer(NULL, product*sizeof(float), strides, &userBufferEncodingFloat); |
||||
|
||||
inputMap.add(input_tensor_name, inputBuffer.get()); |
||||
} |
||||
|
||||
// create output buffer
|
||||
{ |
||||
std::vector<size_t> outputStrides = {output_size * sizeof(float), sizeof(float)}; |
||||
outputBuffer = ubFactory.createUserBuffer(output, output_size * sizeof(float), outputStrides, &userBufferEncodingFloat); |
||||
outputMap.add(output_tensor_name, outputBuffer.get()); |
||||
} |
||||
} |
||||
|
||||
void SNPEModel::addRecurrent(float *state, int state_size) { |
||||
recurrentBuffer = this->addExtra(state, state_size, 2); |
||||
} |
||||
|
||||
void SNPEModel::addDesire(float *state, int state_size) { |
||||
desireBuffer = this->addExtra(state, state_size, 1); |
||||
} |
||||
|
||||
std::unique_ptr<zdl::DlSystem::IUserBuffer> SNPEModel::addExtra(float *state, int state_size, int idx) { |
||||
// get input and output names
|
||||
const auto &strListi_opt = snpe->getInputTensorNames(); |
||||
if (!strListi_opt) throw std::runtime_error("Error obtaining Input tensor names"); |
||||
const auto &strListi = *strListi_opt; |
||||
const char *input_tensor_name = strListi.at(idx); |
||||
printf("adding index %d: %s\n", idx, input_tensor_name); |
||||
|
||||
zdl::DlSystem::UserBufferEncodingFloat userBufferEncodingFloat; |
||||
zdl::DlSystem::IUserBufferFactory& ubFactory = zdl::SNPE::SNPEFactory::getUserBufferFactory(); |
||||
std::vector<size_t> retStrides = {state_size * sizeof(float), sizeof(float)}; |
||||
auto ret = ubFactory.createUserBuffer(state, state_size * sizeof(float), retStrides, &userBufferEncodingFloat); |
||||
inputMap.add(input_tensor_name, ret.get()); |
||||
return ret; |
||||
} |
||||
|
||||
void SNPEModel::execute(float *net_input_buf) { |
||||
assert(inputBuffer->setBufferAddress(net_input_buf)); |
||||
if (!snpe->execute(inputMap, outputMap)) { |
||||
PrintErrorStringAndExit(); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,51 @@ |
||||
#ifndef SNPEMODEL_H |
||||
#define SNPEMODEL_H |
||||
|
||||
#include <SNPE/SNPE.hpp> |
||||
#include <SNPE/SNPEBuilder.hpp> |
||||
#include <SNPE/SNPEFactory.hpp> |
||||
#include <DlContainer/IDlContainer.hpp> |
||||
#include <DlSystem/DlError.hpp> |
||||
#include <DlSystem/ITensor.hpp> |
||||
#include <DlSystem/ITensorFactory.hpp> |
||||
#include <DlSystem/IUserBuffer.hpp> |
||||
#include <DlSystem/IUserBufferFactory.hpp> |
||||
|
||||
#include "runmodel.h" |
||||
|
||||
#define USE_CPU_RUNTIME 0 |
||||
#define USE_GPU_RUNTIME 1 |
||||
#define USE_DSP_RUNTIME 2 |
||||
|
||||
class SNPEModel : public RunModel { |
||||
public: |
||||
SNPEModel(const char *path, float *output, size_t output_size, int runtime); |
||||
~SNPEModel() { |
||||
if (model_data) free(model_data); |
||||
} |
||||
void addRecurrent(float *state, int state_size); |
||||
void addDesire(float *state, int state_size); |
||||
void execute(float *net_input_buf); |
||||
private: |
||||
uint8_t *model_data = NULL; |
||||
|
||||
// snpe model stuff
|
||||
std::unique_ptr<zdl::SNPE::SNPE> snpe; |
||||
|
||||
// snpe input stuff
|
||||
zdl::DlSystem::UserBufferMap inputMap; |
||||
std::unique_ptr<zdl::DlSystem::IUserBuffer> inputBuffer; |
||||
|
||||
// snpe output stuff
|
||||
zdl::DlSystem::UserBufferMap outputMap; |
||||
std::unique_ptr<zdl::DlSystem::IUserBuffer> outputBuffer; |
||||
float *output; |
||||
|
||||
// recurrent and desire
|
||||
std::unique_ptr<zdl::DlSystem::IUserBuffer> addExtra(float *state, int state_size, int idx); |
||||
std::unique_ptr<zdl::DlSystem::IUserBuffer> recurrentBuffer; |
||||
std::unique_ptr<zdl::DlSystem::IUserBuffer> desireBuffer; |
||||
}; |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,160 @@ |
||||
#include "tfmodel.h" |
||||
#include <string> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdexcept> |
||||
#include "common/util.h" |
||||
#include "common/swaglog.h" |
||||
#include <cassert> |
||||
|
||||
void TFModel::status_check() const { |
||||
if (TF_GetCode(this->status) != TF_OK) { |
||||
throw std::runtime_error(TF_Message(status)); |
||||
} |
||||
} |
||||
|
||||
TF_Tensor *TFModel::allocate_tensor_for_output(TF_Output out, float *dat) { |
||||
int num_dims = TF_GraphGetTensorNumDims(graph, out, status); |
||||
status_check(); |
||||
int64_t *dims = new int64_t[num_dims]; |
||||
TF_GraphGetTensorShape(graph, out, dims, num_dims, status); |
||||
status_check(); |
||||
dims[0] = 1; |
||||
|
||||
int total = 1; |
||||
for (int i = 0; i < num_dims; i++) total *= dims[i]; |
||||
//printf("dims %d total %d wdat %p\n", num_dims, total, dat);
|
||||
|
||||
// don't deallocate the buffers
|
||||
auto d = [](void* ddata, size_t, void* arg) {}; |
||||
TF_Tensor *ret = TF_NewTensor(TF_FLOAT, dims, num_dims, (void*)dat, sizeof(float)*total, d, NULL); |
||||
|
||||
//TF_Tensor *ret = TF_AllocateTensor(TF_FLOAT, dims, num_dims, sizeof(float)*total);
|
||||
//memcpy(TF_TensorData(ret), dat, sizeof(float)*total);
|
||||
|
||||
assert(ret); |
||||
delete[] dims; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
TFModel::TFModel(const char *path, float *_output, size_t _output_size, int runtime) { |
||||
// load model
|
||||
{ |
||||
TF_Buffer* buf; |
||||
size_t model_size; |
||||
char tmp[1024]; |
||||
snprintf(tmp, sizeof(tmp), "%s.pb", path); |
||||
LOGD("loading model %s", tmp); |
||||
uint8_t *model_data = (uint8_t *)read_file(tmp, &model_size); |
||||
assert(model_data); |
||||
buf = TF_NewBuffer(); |
||||
buf->data = model_data; |
||||
buf->length = model_size; |
||||
buf->data_deallocator = [](void *data, size_t) { free(data); }; |
||||
LOGD("loaded model of size %d", model_size); |
||||
|
||||
// import graph
|
||||
status = TF_NewStatus(); |
||||
graph = TF_NewGraph(); |
||||
TF_ImportGraphDefOptions *opts = TF_NewImportGraphDefOptions(); |
||||
// TODO: fix the GPU, currently it hangs if you set this to /gpu:0
|
||||
//TF_ImportGraphDefOptionsSetDefaultDevice(opts, "/cpu:0");
|
||||
TF_GraphImportGraphDef(graph, buf, opts, status); |
||||
TF_DeleteImportGraphDefOptions(opts); |
||||
TF_DeleteBuffer(buf); |
||||
status_check(); |
||||
LOGD("imported graph"); |
||||
} |
||||
|
||||
// set up session
|
||||
TF_SessionOptions* sess_opts = TF_NewSessionOptions(); |
||||
|
||||
// don't use all GPU memory
|
||||
/*uint8_t config[15] = {0x32, 0xb, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x1, 0x38, 0x1};
|
||||
double gpu_memory_fraction = 0.2; |
||||
auto bytes = reinterpret_cast<std::uint8_t*>(&gpu_memory_fraction); |
||||
for (std::size_t i = 0; i < sizeof(gpu_memory_fraction); ++i) { |
||||
config[i + 3] = bytes[i]; |
||||
} |
||||
TF_SetConfig(sess_opts, config, sizeof(config), status); |
||||
status_check();*/ |
||||
|
||||
// make session
|
||||
session = TF_NewSession(graph, sess_opts, status); |
||||
TF_DeleteSessionOptions(sess_opts); |
||||
status_check(); |
||||
|
||||
// find tensors
|
||||
// TODO: make this generic
|
||||
input_operation = {TF_GraphOperationByName(graph, "lambda/div"), 0}; |
||||
if (input_operation.oper == NULL) { |
||||
input_operation = {TF_GraphOperationByName(graph, "vision_lambda/div"), 0}; |
||||
} |
||||
assert(input_operation.oper != NULL); |
||||
|
||||
output_operation = {TF_GraphOperationByName(graph, "outputs/outputs/Identity"), 0}; |
||||
if (output_operation.oper == NULL) { |
||||
output_operation = {TF_GraphOperationByName(graph, "outputs/concat"), 0}; |
||||
} |
||||
assert(output_operation.oper != NULL); |
||||
|
||||
// output tensor is good to bind now
|
||||
output = _output; |
||||
output_size = _output_size; |
||||
} |
||||
|
||||
TFModel::~TFModel() { |
||||
TF_DeleteSession(session, status); |
||||
status_check(); |
||||
TF_DeleteGraph(graph); |
||||
TF_DeleteStatus(status); |
||||
} |
||||
|
||||
void TFModel::addRecurrent(float *state, int state_size) { |
||||
rnn_operation.oper = TF_GraphOperationByName(graph, "rnn_state"); |
||||
rnn_operation.index = 0; |
||||
assert(rnn_operation.oper != NULL); |
||||
|
||||
rnn_input_buf = state; |
||||
} |
||||
|
||||
void TFModel::addDesire(float *state, int state_size) { |
||||
desire_operation.oper = TF_GraphOperationByName(graph, "desire"); |
||||
desire_operation.index = 0; |
||||
assert(desire_operation.oper != NULL); |
||||
|
||||
desire_input_buf = state; |
||||
} |
||||
|
||||
void TFModel::execute(float *net_input_buf) { |
||||
TF_Tensor *input_tensor = allocate_tensor_for_output(input_operation, net_input_buf); |
||||
assert(input_tensor); |
||||
TF_Tensor *output_tensor = NULL; |
||||
|
||||
if (rnn_input_buf == NULL) { |
||||
TF_SessionRun(session, NULL, |
||||
&input_operation, &input_tensor, 1, |
||||
&output_operation, &output_tensor, 1, |
||||
NULL, 0, NULL, status); |
||||
} else { |
||||
//printf("%f %f %f\n", net_input_buf[0], rnn_input_buf[0], desire_input_buf[0]);
|
||||
TF_Tensor *rnn_tensor = allocate_tensor_for_output(rnn_operation, rnn_input_buf); |
||||
TF_Tensor *desire_tensor = allocate_tensor_for_output(desire_operation, desire_input_buf); |
||||
TF_Output io[] = {input_operation, rnn_operation, desire_operation}; |
||||
TF_Tensor* id[] = {input_tensor, rnn_tensor, desire_tensor}; |
||||
TF_SessionRun(session, NULL, |
||||
io, id, 3, |
||||
&output_operation, &output_tensor, 1, |
||||
NULL, 0, NULL, status); |
||||
TF_DeleteTensor(rnn_tensor); |
||||
TF_DeleteTensor(desire_tensor); |
||||
} |
||||
TF_DeleteTensor(input_tensor); |
||||
status_check(); |
||||
assert(output_tensor); |
||||
memcpy((void*)output, TF_TensorData(output_tensor), output_size*sizeof(float)); |
||||
TF_DeleteTensor(output_tensor); |
||||
} |
||||
|
||||
|
@ -0,0 +1,39 @@ |
||||
#ifndef TFMODEL_H |
||||
#define TFMODEL_H |
||||
|
||||
#include <stdlib.h> |
||||
#include "runmodel.h" |
||||
|
||||
#include "tensorflow/c/c_api.h" |
||||
|
||||
struct TFState; |
||||
|
||||
class TFModel : public RunModel { |
||||
public: |
||||
TFModel(const char *path, float *output, size_t output_size, int runtime); |
||||
~TFModel(); |
||||
void addRecurrent(float *state, int state_size); |
||||
void addDesire(float *state, int state_size); |
||||
void execute(float *net_input_buf); |
||||
private: |
||||
void status_check() const; |
||||
TF_Tensor *allocate_tensor_for_output(TF_Output out, float *dat); |
||||
|
||||
float *output; |
||||
size_t output_size; |
||||
|
||||
TF_Session* session; |
||||
TF_Graph* graph; |
||||
TF_Status* status; |
||||
|
||||
TF_Output input_operation; |
||||
TF_Output rnn_operation; |
||||
TF_Output desire_operation; |
||||
TF_Output output_operation; |
||||
|
||||
float *rnn_input_buf = NULL; |
||||
float *desire_input_buf = NULL; |
||||
}; |
||||
|
||||
#endif |
||||
|
@ -0,0 +1 @@ |
||||
main |
@ -0,0 +1,15 @@ |
||||
PHONELIBS = ../../../../phonelibs
|
||||
|
||||
EIGEN_FLAGS = -I$(PHONELIBS)/eigen
|
||||
|
||||
CXXFLAGS += $(EIGEN_FLAGS)
|
||||
LDFLAGS += -lm
|
||||
|
||||
.PHONY: clean |
||||
|
||||
main: main.cc data.h |
||||
g++ -O2 $(EIGEN_FLAGS) -o main main.cc -lm
|
||||
|
||||
|
||||
clean: |
||||
rm -f main
|
@ -0,0 +1,8 @@ |
||||
#pragma once |
||||
|
||||
#define MODEL_PATH_DISTANCE 192 |
||||
#define POLYFIT_DEGREE 4 |
||||
|
||||
float pts[MODEL_PATH_DISTANCE] = {3.1261718, 3.1642578, 3.0548828, 3.1125, 3.190625, 3.01875, 2.9816406, 3.1222656, 2.9728515, 2.9826171, 3.034375, 3.0392578, 3.1642578, 3.0792968, 3.0011718, 3.0705078, 2.9904296, 3.0089843, 3.0597656, 3.0978515, 2.9210937, 2.9992187, 2.9474609, 2.9621093, 2.9289062, 2.89375, 2.7975585, 2.9015625, 2.8175781, 2.9132812, 2.8175781, 2.7501953, 2.8332031, 2.8166015, 2.7638671, 2.8878906, 2.7599609, 2.6999023, 2.6720703, 2.6398437, 2.7243164, 2.6120117, 2.6588867, 2.5558593, 2.5978515, 2.5485351, 2.4269531, 2.5001953, 2.4855468, 2.4367187, 2.2973144, 2.2812011, 2.2890136, 2.39375, 2.2836425, 2.3815429, 2.2138183, 2.1964843, 2.1840332, 2.1759765, 2.0421875, 2.1034667, 2.0281494, 2.0880859, 1.9706542, 1.9276855, 1.8522155, 1.8991821, 1.7780273, 1.8180053, 1.8326843, 1.8270385, 1.7182128, 1.6439941, 1.5360839, 1.68385, 1.4584472, 1.5955322, 1.6002929, 1.4157226, 1.4704101, 1.2936523, 1.2990234, 1.4281738, 1.4357421, 1.409375, 1.2511718, 1.2194335, 1.1554687, 1.043164, 1.0954101, 1.0392578, 1.0895507, 1.0880859, 0.897168, 0.83369142, 0.86494142, 0.87763673, 0.85322267, 0.72968751, 0.57832032, 0.73066407, 0.78828126, 0.69160157, 0.64375, 0.5919922, 0.5529297, 0.52070314, 0.60957032, 0.51093751, 0.3576172, 0.49921876, 0.284375, 0.21992187, 0.25214845, 0.30683595, 0.30976564, 0.2716797, 0.22089843, 0.25507814, 0.084179685, 0.071484372, 0.1828125, 0.15644531, 0.13789062, 0.054882813, 0.021679688, -0.091601565, -0.0203125, -0.13359375, -0.037890624, -0.29765624, -0.15605469, -0.30351561, -0.055468749, -0.22148438, -0.246875, -0.31718749, -0.25468749, -0.35234374, -0.16484375, -0.56523436, -0.56523436, -0.39921874, -0.58671874, -0.45585936, -0.50859374, -0.44023436, -0.42656249, -0.56328124, -0.70195311, -0.403125, -0.76445311, -0.98710936, -0.7625, -0.75273436, -0.825, -0.996875, -0.86210936, -0.99492186, -0.85625, -0.88359374, -0.97148436, -1.0320313, -1.1609375, -1.1296875, -1.0203125, -1.0691407, -1.2371094, -1.1277344, -1.2214844, -1.1921875, -1.2996094, -1.2917969, -1.3699219, -1.434375, -1.3699219, -1.3601563, -1.5730469, -1.3152344, -1.4851563, -1.48125, -1.5925782, -1.746875, -1.5847657, -1.6003907, -1.5984375, -1.7703125, -1.8328125, -1.8152344, -1.9714844, -1.9421875}; |
||||
|
||||
float stds[MODEL_PATH_DISTANCE] = {1.0945262, 1.156862, 1.0777057, 1.1501777, 1.234844, 1.0140595, 1.2004665, 1.1926303, 1.1269455, 1.0362904, 0.98873031, 0.88530254, 1.0078473, 0.93637651, 0.90959895, 0.86409503, 0.86353016, 0.74534553, 0.78025728, 0.88014913, 0.75756663, 0.77129823, 0.75581717, 0.79222, 0.84098673, 0.79402477, 0.85648865, 0.80315614, 0.77346581, 0.73097658, 0.72557795, 0.72930044, 0.666103, 0.77142948, 0.704379, 0.6806078, 0.67680347, 0.71318036, 0.72244918, 0.66123307, 0.62547487, 0.67786956, 0.68404138, 0.70508122, 0.62400025, 0.72325015, 0.73942852, 0.67811751, 0.70370805, 0.65040058, 0.6870054, 0.66093785, 0.666103, 0.70040214, 0.65300173, 0.69714534, 0.65825552, 0.64833081, 0.6464982, 0.75850725, 0.69627059, 0.71659386, 0.69307244, 0.61554217, 0.62015557, 0.61998636, 0.67650336, 0.68142927, 0.6278621, 0.612294, 0.62592906, 0.63736153, 0.74233508, 0.69297016, 0.69621509, 0.67229682, 0.64879686, 0.72361159, 0.70229048, 0.60928106, 0.62712252, 0.66923952, 0.65802008, 0.68361813, 0.61587888, 0.63348651, 0.60727841, 0.64873856, 0.68847752, 0.58432156, 0.61683363, 0.63311422, 0.64981711, 0.57369792, 0.62604266, 0.62162364, 0.62066346, 0.62808979, 0.58524042, 0.63537884, 0.65367514, 0.63900274, 0.61089778, 0.62513435, 0.6470505, 0.63952166, 0.5937764, 0.64310449, 0.64330715, 0.64322031, 0.64632386, 0.60827911, 0.58887208, 0.61959165, 0.70725286, 0.64287293, 0.62326396, 0.65896219, 0.55610275, 0.6658656, 0.65681434, 0.583188, 0.6311124, 0.559652, 0.71419227, 0.62490743, 0.66699386, 0.62032485, 0.663036, 0.61414057, 0.66179425, 0.59399503, 0.65203643, 0.67839557, 0.63698763, 0.617452, 0.61022842, 0.7398752, 0.65657932, 0.68718743, 0.67901206, 0.66126263, 0.69949967, 0.70709819, 0.713336, 0.68130863, 0.68652785, 0.67028236, 0.7626031, 0.65259206, 0.72977453, 0.66049516, 0.64261246, 0.66906089, 0.69762796, 0.73719794, 0.69081914, 0.69849437, 0.72435051, 0.62354708, 0.68812829, 0.7193296, 0.66211933, 0.69278532, 0.7518425, 0.69661695, 0.672491, 0.71539241, 0.7369433, 0.66120356, 0.79088491, 0.77491313, 0.79442614, 0.7878198, 0.78881842, 0.70690477, 0.80707121, 0.78768665, 0.7215547, 0.75226194, 0.72196257, 0.765799, 0.77267712, 0.75844234, 0.81038833, 0.81188059, 0.79864907, 0.816436, 0.845298, 0.85074174, 0.73668873, 0.83516812}; |
@ -0,0 +1,52 @@ |
||||
#include <iostream> |
||||
#include <cmath> |
||||
|
||||
#include <Eigen/Dense> |
||||
|
||||
#include "data.h" |
||||
|
||||
Eigen::Matrix<float, MODEL_PATH_DISTANCE, POLYFIT_DEGREE> vander; |
||||
|
||||
void poly_init(){ |
||||
// Build Vandermonde matrix
|
||||
for(int i = 0; i < MODEL_PATH_DISTANCE; i++) { |
||||
for(int j = 0; j < POLYFIT_DEGREE; j++) { |
||||
vander(i, j) = pow(i, POLYFIT_DEGREE-j-1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void poly_fit(float *in_pts, float *in_stds, float *out) { |
||||
// References to inputs
|
||||
Eigen::Map<Eigen::Matrix<float, MODEL_PATH_DISTANCE, 1> > pts(in_pts, MODEL_PATH_DISTANCE); |
||||
Eigen::Map<Eigen::Matrix<float, MODEL_PATH_DISTANCE, 1> > std(in_stds, MODEL_PATH_DISTANCE); |
||||
Eigen::Map<Eigen::Matrix<float, POLYFIT_DEGREE, 1> > p(out, POLYFIT_DEGREE); |
||||
|
||||
// Build Least Squares equations
|
||||
Eigen::Matrix<float, MODEL_PATH_DISTANCE, POLYFIT_DEGREE> lhs = vander.array().colwise() / std.array(); |
||||
Eigen::Matrix<float, MODEL_PATH_DISTANCE, 1> rhs = pts.array() / std.array(); |
||||
|
||||
Eigen::Matrix<float, POLYFIT_DEGREE, 1> scale = 1. / (lhs.array()*lhs.array()).sqrt().colwise().sum(); |
||||
lhs = lhs * scale.asDiagonal(); |
||||
|
||||
// Solve inplace
|
||||
Eigen::ColPivHouseholderQR<Eigen::Ref<Eigen::MatrixXf> > qr(lhs); |
||||
p = qr.solve(rhs); |
||||
|
||||
p = p.transpose() * scale.asDiagonal(); |
||||
} |
||||
|
||||
int main(void) { |
||||
poly_init(); |
||||
|
||||
|
||||
float poly[4]; |
||||
poly_fit(pts, stds, poly); |
||||
|
||||
std::cout << "["; |
||||
std::cout << poly[0] << ","; |
||||
std::cout << poly[1] << ","; |
||||
std::cout << poly[2] << ","; |
||||
std::cout << poly[3]; |
||||
std::cout << "]" << std::endl; |
||||
} |
@ -0,0 +1,24 @@ |
||||
import numpy as np |
||||
|
||||
pts = np.array([3.1261718, 3.1642578, 3.0548828, 3.1125, 3.190625, 3.01875, 2.9816406, 3.1222656, 2.9728515, 2.9826171, 3.034375, 3.0392578, 3.1642578, 3.0792968, 3.0011718, 3.0705078, 2.9904296, 3.0089843, 3.0597656, 3.0978515, 2.9210937, 2.9992187, 2.9474609, 2.9621093, 2.9289062, 2.89375, 2.7975585, 2.9015625, 2.8175781, 2.9132812, 2.8175781, 2.7501953, 2.8332031, 2.8166015, 2.7638671, 2.8878906, 2.7599609, 2.6999023, 2.6720703, 2.6398437, 2.7243164, 2.6120117, 2.6588867, 2.5558593, 2.5978515, 2.5485351, 2.4269531, 2.5001953, 2.4855468, 2.4367187, 2.2973144, 2.2812011, 2.2890136, 2.39375, 2.2836425, 2.3815429, 2.2138183, 2.1964843, 2.1840332, 2.1759765, 2.0421875, 2.1034667, 2.0281494, 2.0880859, 1.9706542, 1.9276855, 1.8522155, 1.8991821, 1.7780273, 1.8180053, 1.8326843, 1.8270385, 1.7182128, 1.6439941, 1.5360839, 1.68385, 1.4584472, 1.5955322, 1.6002929, 1.4157226, 1.4704101, 1.2936523, 1.2990234, 1.4281738, 1.4357421, 1.409375, 1.2511718, 1.2194335, 1.1554687, 1.043164, 1.0954101, 1.0392578, 1.0895507, 1.0880859, 0.897168, 0.83369142, 0.86494142, 0.87763673, 0.85322267, 0.72968751, 0.57832032, 0.73066407, 0.78828126, 0.69160157, 0.64375, 0.5919922, 0.5529297, 0.52070314, 0.60957032, 0.51093751, 0.3576172, 0.49921876, 0.284375, 0.21992187, 0.25214845, 0.30683595, 0.30976564, 0.2716797, 0.22089843, 0.25507814, 0.084179685, 0.071484372, 0.1828125, 0.15644531, 0.13789062, 0.054882813, 0.021679688, -0.091601565, -0.0203125, -0.13359375, -0.037890624, -0.29765624, -0.15605469, -0.30351561, -0.055468749, -0.22148438, -0.246875, -0.31718749, -0.25468749, -0.35234374, -0.16484375, -0.56523436, -0.56523436, -0.39921874, -0.58671874, -0.45585936, -0.50859374, -0.44023436, -0.42656249, -0.56328124, -0.70195311, -0.403125, -0.76445311, -0.98710936, -0.7625, -0.75273436, -0.825, -0.996875, -0.86210936, -0.99492186, -0.85625, -0.88359374, -0.97148436, -1.0320313, -1.1609375, -1.1296875, -1.0203125, -1.0691407, -1.2371094, -1.1277344, -1.2214844, -1.1921875, -1.2996094, -1.2917969, -1.3699219, -1.434375, -1.3699219, -1.3601563, -1.5730469, -1.3152344, -1.4851563, -1.48125, -1.5925782, -1.746875, -1.5847657, -1.6003907, -1.5984375, -1.7703125, -1.8328125, -1.8152344, -1.9714844, -1.9421875]) |
||||
|
||||
stds = np.array([1.0945262, 1.156862, 1.0777057, 1.1501777, 1.234844, 1.0140595, 1.2004665, 1.1926303, 1.1269455, 1.0362904, 0.98873031, 0.88530254, 1.0078473, 0.93637651, 0.90959895, 0.86409503, 0.86353016, 0.74534553, 0.78025728, 0.88014913, 0.75756663, 0.77129823, 0.75581717, 0.79222, 0.84098673, 0.79402477, 0.85648865, 0.80315614, 0.77346581, 0.73097658, 0.72557795, 0.72930044, 0.666103, 0.77142948, 0.704379, 0.6806078, 0.67680347, 0.71318036, 0.72244918, 0.66123307, 0.62547487, 0.67786956, 0.68404138, 0.70508122, 0.62400025, 0.72325015, 0.73942852, 0.67811751, 0.70370805, 0.65040058, 0.6870054, 0.66093785, 0.666103, 0.70040214, 0.65300173, 0.69714534, 0.65825552, 0.64833081, 0.6464982, 0.75850725, 0.69627059, 0.71659386, 0.69307244, 0.61554217, 0.62015557, 0.61998636, 0.67650336, 0.68142927, 0.6278621, 0.612294, 0.62592906, 0.63736153, 0.74233508, 0.69297016, 0.69621509, 0.67229682, 0.64879686, 0.72361159, 0.70229048, 0.60928106, 0.62712252, 0.66923952, 0.65802008, 0.68361813, 0.61587888, 0.63348651, 0.60727841, 0.64873856, 0.68847752, 0.58432156, 0.61683363, 0.63311422, 0.64981711, 0.57369792, 0.62604266, 0.62162364, 0.62066346, 0.62808979, 0.58524042, 0.63537884, 0.65367514, 0.63900274, 0.61089778, 0.62513435, 0.6470505, 0.63952166, 0.5937764, 0.64310449, 0.64330715, 0.64322031, 0.64632386, 0.60827911, 0.58887208, 0.61959165, 0.70725286, 0.64287293, 0.62326396, 0.65896219, 0.55610275, 0.6658656, 0.65681434, 0.583188, 0.6311124, 0.559652, 0.71419227, 0.62490743, 0.66699386, 0.62032485, 0.663036, 0.61414057, 0.66179425, 0.59399503, 0.65203643, 0.67839557, 0.63698763, 0.617452, 0.61022842, 0.7398752, 0.65657932, 0.68718743, 0.67901206, 0.66126263, 0.69949967, 0.70709819, 0.713336, 0.68130863, 0.68652785, 0.67028236, 0.7626031, 0.65259206, 0.72977453, 0.66049516, 0.64261246, 0.66906089, 0.69762796, 0.73719794, 0.69081914, 0.69849437, 0.72435051, 0.62354708, 0.68812829, 0.7193296, 0.66211933, 0.69278532, 0.7518425, 0.69661695, 0.672491, 0.71539241, 0.7369433, 0.66120356, 0.79088491, 0.77491313, 0.79442614, 0.7878198, 0.78881842, 0.70690477, 0.80707121, 0.78768665, 0.7215547, 0.75226194, 0.72196257, 0.765799, 0.77267712, 0.75844234, 0.81038833, 0.81188059, 0.79864907, 0.816436, 0.845298, 0.85074174, 0.73668873, 0.83516812]) |
||||
|
||||
order = 3 |
||||
|
||||
x = np.arange(0, len(pts)) |
||||
print(np.polyfit(x, pts, order, w=1/stds)) |
||||
|
||||
# Do polyfit manually |
||||
w = 1.0 / stds |
||||
lhs = np.vander(x, order+1).astype(np.float64) |
||||
rhs = pts |
||||
|
||||
lhs *= np.atleast_2d(w).T |
||||
rhs *= w |
||||
|
||||
scale = np.sqrt((lhs*lhs).sum(axis=0)) |
||||
lhs = lhs / scale |
||||
c, resids, rank, s = np.linalg.lstsq(lhs, rhs, rcond=None) |
||||
c = (c.T/scale).T |
||||
print(c) |
@ -0,0 +1 @@ |
||||
benchmark |
@ -0,0 +1,190 @@ |
||||
#include <SNPE/SNPE.hpp> |
||||
#include <SNPE/SNPEBuilder.hpp> |
||||
#include <SNPE/SNPEFactory.hpp> |
||||
#include <DlContainer/IDlContainer.hpp> |
||||
#include <DlSystem/DlError.hpp> |
||||
#include <DlSystem/ITensor.hpp> |
||||
#include <DlSystem/ITensorFactory.hpp> |
||||
#include <iostream> |
||||
#include <fstream> |
||||
#include <sstream> |
||||
|
||||
using namespace std; |
||||
|
||||
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p) { |
||||
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); |
||||
} |
||||
|
||||
void PrintErrorStringAndExit() { |
||||
cout << "ERROR!" << endl; |
||||
const char* const errStr = zdl::DlSystem::getLastErrorString(); |
||||
std::cerr << errStr << std::endl; |
||||
std::exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
|
||||
zdl::DlSystem::Runtime_t checkRuntime() { |
||||
static zdl::DlSystem::Version_t Version = zdl::SNPE::SNPEFactory::getLibraryVersion(); |
||||
static zdl::DlSystem::Runtime_t Runtime; |
||||
std::cout << "SNPE Version: " << Version.asString().c_str() << std::endl; //Print Version number
|
||||
if (zdl::SNPE::SNPEFactory::isRuntimeAvailable(zdl::DlSystem::Runtime_t::DSP)) { |
||||
std::cout << "Using DSP runtime" << std::endl; |
||||
Runtime = zdl::DlSystem::Runtime_t::DSP; |
||||
} else if (zdl::SNPE::SNPEFactory::isRuntimeAvailable(zdl::DlSystem::Runtime_t::GPU)) { |
||||
std::cout << "Using GPU runtime" << std::endl; |
||||
Runtime = zdl::DlSystem::Runtime_t::GPU; |
||||
} else { |
||||
std::cout << "Using cpu runtime" << std::endl; |
||||
Runtime = zdl::DlSystem::Runtime_t::CPU; |
||||
} |
||||
return Runtime; |
||||
} |
||||
|
||||
void test(char *filename) { |
||||
static zdl::DlSystem::Runtime_t runtime = checkRuntime(); |
||||
std::unique_ptr<zdl::DlContainer::IDlContainer> container; |
||||
container = zdl::DlContainer::IDlContainer::open(filename); |
||||
|
||||
if (!container) { PrintErrorStringAndExit(); } |
||||
cout << "start build" << endl; |
||||
std::unique_ptr<zdl::SNPE::SNPE> snpe; |
||||
{ |
||||
snpe = NULL; |
||||
zdl::SNPE::SNPEBuilder snpeBuilder(container.get()); |
||||
snpe = snpeBuilder.setOutputLayers({}) |
||||
.setRuntimeProcessor(runtime) |
||||
.setUseUserSuppliedBuffers(false) |
||||
//.setDebugMode(true)
|
||||
.build(); |
||||
if (!snpe) { |
||||
cout << "ERROR!" << endl; |
||||
const char* const errStr = zdl::DlSystem::getLastErrorString(); |
||||
std::cerr << errStr << std::endl; |
||||
} |
||||
cout << "ran snpeBuilder" << endl; |
||||
} |
||||
|
||||
const auto &strList_opt = snpe->getInputTensorNames(); |
||||
if (!strList_opt) throw std::runtime_error("Error obtaining input tensor names"); |
||||
|
||||
cout << "get input tensor names done" << endl; |
||||
const auto &strList = *strList_opt; |
||||
static zdl::DlSystem::TensorMap inputTensorMap; |
||||
static zdl::DlSystem::TensorMap outputTensorMap; |
||||
vector<std::unique_ptr<zdl::DlSystem::ITensor> > inputs; |
||||
for (int i = 0; i < strList.size(); i++) { |
||||
cout << "input name: " << strList.at(i) << endl; |
||||
|
||||
const auto &inputDims_opt = snpe->getInputDimensions(strList.at(i)); |
||||
const auto &inputShape = *inputDims_opt; |
||||
inputs.push_back(zdl::SNPE::SNPEFactory::getTensorFactory().createTensor(inputShape)); |
||||
inputTensorMap.add(strList.at(i), inputs[i].get()); |
||||
} |
||||
|
||||
struct timespec start, end; |
||||
cout << "**** starting benchmark ****" << endl; |
||||
for (int i = 0; i < 10; i++) { |
||||
clock_gettime(CLOCK_MONOTONIC, &start); |
||||
assert(snpe->execute(inputTensorMap, outputTensorMap)); |
||||
clock_gettime(CLOCK_MONOTONIC, &end); |
||||
uint64_t timeElapsed = timespecDiff(&end, &start); |
||||
printf("time: %f ms\n", timeElapsed*1.0/1e6); |
||||
} |
||||
} |
||||
|
||||
void get_testframe(int index, std::unique_ptr<zdl::DlSystem::ITensor> &input) { |
||||
FILE * pFile; |
||||
string filepath="/data/ipt/quantize_samples/sample_input_"+std::to_string(index); |
||||
pFile = fopen(filepath.c_str(),"rb"); |
||||
int length = 1*6*160*320*4; |
||||
float * frame_buffer = new float[length/4]; // 32/8
|
||||
fread(frame_buffer, length, 1, pFile); |
||||
// std::cout << *(frame_buffer+length/4-1) << std::endl;
|
||||
std::copy(frame_buffer, frame_buffer+(length/4), input->begin()); |
||||
} |
||||
|
||||
void SaveITensor(const std::string& path, const zdl::DlSystem::ITensor* tensor) |
||||
{ |
||||
std::ofstream os(path, std::ofstream::binary); |
||||
if (!os) |
||||
{ |
||||
std::cerr << "Failed to open output file for writing: " << path << "\n"; |
||||
std::exit(EXIT_FAILURE); |
||||
} |
||||
for ( auto it = tensor->cbegin(); it != tensor->cend(); ++it ) |
||||
{ |
||||
float f = *it; |
||||
if (!os.write(reinterpret_cast<char*>(&f), sizeof(float))) |
||||
{ |
||||
std::cerr << "Failed to write data to: " << path << "\n"; |
||||
std::exit(EXIT_FAILURE); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void testrun(char* modelfile) { |
||||
static zdl::DlSystem::Runtime_t runtime = checkRuntime(); |
||||
std::unique_ptr<zdl::DlContainer::IDlContainer> container; |
||||
container = zdl::DlContainer::IDlContainer::open(modelfile); |
||||
|
||||
if (!container) { PrintErrorStringAndExit(); } |
||||
cout << "start build" << endl; |
||||
std::unique_ptr<zdl::SNPE::SNPE> snpe; |
||||
{ |
||||
snpe = NULL; |
||||
zdl::SNPE::SNPEBuilder snpeBuilder(container.get()); |
||||
snpe = snpeBuilder.setOutputLayers({}) |
||||
.setRuntimeProcessor(runtime) |
||||
.setUseUserSuppliedBuffers(false) |
||||
//.setDebugMode(true)
|
||||
.build(); |
||||
if (!snpe) { |
||||
cout << "ERROR!" << endl; |
||||
const char* const errStr = zdl::DlSystem::getLastErrorString(); |
||||
std::cerr << errStr << std::endl; |
||||
} |
||||
cout << "ran snpeBuilder" << endl; |
||||
} |
||||
|
||||
const auto &strList_opt = snpe->getInputTensorNames(); |
||||
if (!strList_opt) throw std::runtime_error("Error obtaining input tensor names"); |
||||
cout << "get input tensor names done" << endl; |
||||
|
||||
const auto &strList = *strList_opt; |
||||
static zdl::DlSystem::TensorMap inputTensorMap; |
||||
static zdl::DlSystem::TensorMap outputTensorMap; |
||||
|
||||
assert (strList.size() == 1); |
||||
const auto &inputDims_opt = snpe->getInputDimensions(strList.at(0)); |
||||
const auto &inputShape = *inputDims_opt; |
||||
std::cout << "winkwink" << std::endl; |
||||
|
||||
for (int i=0;i<10000;i++) { |
||||
std::unique_ptr<zdl::DlSystem::ITensor> input; |
||||
input = zdl::SNPE::SNPEFactory::getTensorFactory().createTensor(inputShape); |
||||
get_testframe(i,input); |
||||
snpe->execute(input.get(), outputTensorMap); |
||||
zdl::DlSystem::StringList tensorNames = outputTensorMap.getTensorNames(); |
||||
std::for_each( tensorNames.begin(), tensorNames.end(), [&](const char* name) { |
||||
std::ostringstream path; |
||||
path << "/data/opt/Result_" << std::to_string(i) << ".raw"; |
||||
auto tensorPtr = outputTensorMap.getTensor(name); |
||||
SaveITensor(path.str(), tensorPtr); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char* argv[]) { |
||||
if (argc < 2) { |
||||
printf("usage: %s <filename>\n", argv[0]); |
||||
return -1; |
||||
} |
||||
|
||||
if (argc == 2) { |
||||
while(1) test(argv[1]); |
||||
} else if (argc == 3) { |
||||
testrun(argv[1]); |
||||
} |
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,3 @@ |
||||
#!/bin/sh -e |
||||
clang++ -I ~/one/phonelibs/snpe/include/ -lSNPE -lsymphony-cpu -lsymphonypower benchmark.cc -o benchmark |
||||
./benchmark $1 |
@ -0,0 +1,2 @@ |
||||
#!/bin/bash |
||||
clang++ -I /home/batman/one/external/tensorflow/include/ -L /home/batman/one/external/tensorflow/lib -Wl,-rpath=/home/batman/one/external/tensorflow/lib main.cc -ltensorflow |
@ -0,0 +1,69 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <assert.h> |
||||
#include "tensorflow/c/c_api.h" |
||||
|
||||
void* read_file(const char* path, size_t* out_len) { |
||||
FILE* f = fopen(path, "r"); |
||||
if (!f) { |
||||
return NULL; |
||||
} |
||||
fseek(f, 0, SEEK_END); |
||||
long f_len = ftell(f); |
||||
rewind(f); |
||||
|
||||
char* buf = (char*)calloc(f_len, 1); |
||||
assert(buf); |
||||
|
||||
size_t num_read = fread(buf, f_len, 1, f); |
||||
fclose(f); |
||||
|
||||
if (num_read != 1) { |
||||
free(buf); |
||||
return NULL; |
||||
} |
||||
|
||||
if (out_len) { |
||||
*out_len = f_len; |
||||
} |
||||
|
||||
return buf; |
||||
} |
||||
|
||||
static void DeallocateBuffer(void* data, size_t) { |
||||
free(data); |
||||
} |
||||
|
||||
int main(int argc, char* argv[]) { |
||||
TF_Buffer* buf; |
||||
TF_Graph* graph; |
||||
TF_Status* status; |
||||
char *path = argv[1]; |
||||
|
||||
// load model
|
||||
{ |
||||
size_t model_size; |
||||
char tmp[1024]; |
||||
snprintf(tmp, sizeof(tmp), "%s.pb", path); |
||||
printf("loading model %s\n", tmp); |
||||
uint8_t *model_data = (uint8_t *)read_file(tmp, &model_size); |
||||
buf = TF_NewBuffer(); |
||||
buf->data = model_data; |
||||
buf->length = model_size; |
||||
buf->data_deallocator = DeallocateBuffer; |
||||
printf("loaded model of size %d\n", model_size); |
||||
} |
||||
|
||||
// import graph
|
||||
status = TF_NewStatus(); |
||||
graph = TF_NewGraph(); |
||||
TF_ImportGraphDefOptions *opts = TF_NewImportGraphDefOptions(); |
||||
TF_GraphImportGraphDef(graph, buf, opts, status); |
||||
TF_DeleteImportGraphDefOptions(opts); |
||||
TF_DeleteBuffer(buf); |
||||
if (TF_GetCode(status) != TF_OK) { |
||||
printf("FAIL: %s\n", TF_Message(status)); |
||||
} else { |
||||
printf("SUCCESS\n"); |
||||
} |
||||
} |
@ -0,0 +1,9 @@ |
||||
#!/usr/bin/env python3 |
||||
import sys |
||||
import tensorflow as tf |
||||
|
||||
with open(sys.argv[1], "rb") as f: |
||||
graph_def = tf.compat.v1.GraphDef() |
||||
graph_def.ParseFromString(f.read()) |
||||
#tf.io.write_graph(graph_def, '', sys.argv[1]+".try") |
||||
|
@ -0,0 +1,82 @@ |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
|
||||
#include "clutil.h" |
||||
|
||||
#include "loadyuv.h" |
||||
|
||||
void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height) { |
||||
int err = 0; |
||||
memset(s, 0, sizeof(*s)); |
||||
|
||||
s->width = width; |
||||
s->height = height; |
||||
|
||||
char args[1024]; |
||||
snprintf(args, sizeof(args), |
||||
"-cl-fast-relaxed-math -cl-denorms-are-zero " |
||||
"-DTRANSFORMED_WIDTH=%d -DTRANSFORMED_HEIGHT=%d", |
||||
width, height); |
||||
cl_program prg = CLU_LOAD_FROM_FILE(ctx, device_id, "transforms/loadyuv.cl", args); |
||||
|
||||
s->loadys_krnl = clCreateKernel(prg, "loadys", &err); |
||||
assert(err == 0); |
||||
s->loaduv_krnl = clCreateKernel(prg, "loaduv", &err); |
||||
assert(err == 0); |
||||
|
||||
// done with this
|
||||
err = clReleaseProgram(prg); |
||||
assert(err == 0); |
||||
} |
||||
|
||||
void loadyuv_destroy(LoadYUVState* s) { |
||||
int err = 0; |
||||
|
||||
err = clReleaseKernel(s->loadys_krnl); |
||||
assert(err == 0); |
||||
err = clReleaseKernel(s->loaduv_krnl); |
||||
assert(err == 0); |
||||
} |
||||
|
||||
void loadyuv_queue(LoadYUVState* s, cl_command_queue q, |
||||
cl_mem y_cl, cl_mem u_cl, cl_mem v_cl, |
||||
cl_mem out_cl) { |
||||
int err = 0; |
||||
|
||||
err = clSetKernelArg(s->loadys_krnl, 0, sizeof(cl_mem), &y_cl); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->loadys_krnl, 1, sizeof(cl_mem), &out_cl); |
||||
assert(err == 0); |
||||
|
||||
const size_t loadys_work_size = (s->width*s->height)/8; |
||||
err = clEnqueueNDRangeKernel(q, s->loadys_krnl, 1, NULL, |
||||
&loadys_work_size, NULL, 0, 0, NULL); |
||||
assert(err == 0); |
||||
|
||||
const size_t loaduv_work_size = ((s->width/2)*(s->height/2))/8; |
||||
cl_int loaduv_out_off = (s->width*s->height); |
||||
|
||||
err = clSetKernelArg(s->loaduv_krnl, 0, sizeof(cl_mem), &u_cl); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->loaduv_krnl, 1, sizeof(cl_mem), &out_cl); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->loaduv_krnl, 2, sizeof(cl_int), &loaduv_out_off); |
||||
assert(err == 0); |
||||
|
||||
err = clEnqueueNDRangeKernel(q, s->loaduv_krnl, 1, NULL, |
||||
&loaduv_work_size, NULL, 0, 0, NULL); |
||||
assert(err == 0); |
||||
|
||||
loaduv_out_off += (s->width/2)*(s->height/2); |
||||
|
||||
err = clSetKernelArg(s->loaduv_krnl, 0, sizeof(cl_mem), &v_cl); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->loaduv_krnl, 1, sizeof(cl_mem), &out_cl); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->loaduv_krnl, 2, sizeof(cl_int), &loaduv_out_off); |
||||
assert(err == 0); |
||||
|
||||
err = clEnqueueNDRangeKernel(q, s->loaduv_krnl, 1, NULL, |
||||
&loaduv_work_size, NULL, 0, 0, NULL); |
||||
assert(err == 0); |
||||
} |
@ -0,0 +1,43 @@ |
||||
#define UV_SIZE ((TRANSFORMED_WIDTH/2)*(TRANSFORMED_HEIGHT/2)) |
||||
|
||||
__kernel void loadys(__global uchar8 const * const Y, |
||||
__global float * out) |
||||
{ |
||||
const int gid = get_global_id(0); |
||||
const int ois = gid * 8; |
||||
const int oy = ois / TRANSFORMED_WIDTH; |
||||
const int ox = ois % TRANSFORMED_WIDTH; |
||||
|
||||
const uchar8 ys = Y[gid]; |
||||
|
||||
// y = (x - 128) / 128 |
||||
const float8 ysf = (convert_float8(ys) - 128.f) * 0.0078125f; |
||||
|
||||
// 02 |
||||
// 13 |
||||
|
||||
__global float* outy0; |
||||
__global float* outy1; |
||||
if ((oy & 1) == 0) { |
||||
outy0 = out; //y0 |
||||
outy1 = out + UV_SIZE*2; //y2 |
||||
} else { |
||||
outy0 = out + UV_SIZE; //y1 |
||||
outy1 = out + UV_SIZE*3; //y3 |
||||
} |
||||
|
||||
vstore4(ysf.s0246, 0, outy0 + (oy/2) * (TRANSFORMED_WIDTH/2) + ox/2); |
||||
vstore4(ysf.s1357, 0, outy1 + (oy/2) * (TRANSFORMED_WIDTH/2) + ox/2); |
||||
} |
||||
|
||||
__kernel void loaduv(__global uchar8 const * const in, |
||||
__global float8 * out, |
||||
int out_offset) |
||||
{ |
||||
const int gid = get_global_id(0); |
||||
const uchar8 inv = in[gid]; |
||||
|
||||
// y = (x - 128) / 128 |
||||
const float8 outv = (convert_float8(inv) - 128.f) * 0.0078125f; |
||||
out[gid + out_offset / 8] = outv; |
||||
} |
@ -0,0 +1,34 @@ |
||||
#ifndef LOADYUV_H |
||||
#define LOADYUV_H |
||||
|
||||
#include <inttypes.h> |
||||
#include <stdbool.h> |
||||
|
||||
#ifdef __APPLE__ |
||||
#include <OpenCL/cl.h> |
||||
#else |
||||
#include <CL/cl.h> |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct { |
||||
int width, height; |
||||
cl_kernel loadys_krnl, loaduv_krnl; |
||||
} LoadYUVState; |
||||
|
||||
void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height); |
||||
|
||||
void loadyuv_destroy(LoadYUVState* s); |
||||
|
||||
void loadyuv_queue(LoadYUVState* s, cl_command_queue q, |
||||
cl_mem y_cl, cl_mem u_cl, cl_mem v_cl, |
||||
cl_mem out_cl); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif // LOADYUV_H
|
@ -0,0 +1,149 @@ |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
|
||||
#include "clutil.h" |
||||
|
||||
#include "transform.h" |
||||
|
||||
void transform_init(Transform* s, cl_context ctx, cl_device_id device_id) { |
||||
int err = 0; |
||||
memset(s, 0, sizeof(*s)); |
||||
|
||||
cl_program prg = CLU_LOAD_FROM_FILE(ctx, device_id, "transforms/transform.cl", ""); |
||||
|
||||
s->krnl = clCreateKernel(prg, "warpPerspective", &err); |
||||
assert(err == 0); |
||||
|
||||
// done with this
|
||||
err = clReleaseProgram(prg); |
||||
assert(err == 0); |
||||
|
||||
s->m_y_cl = clCreateBuffer(ctx, CL_MEM_READ_WRITE, 3*3*sizeof(float), NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
s->m_uv_cl = clCreateBuffer(ctx, CL_MEM_READ_WRITE, 3*3*sizeof(float), NULL, &err); |
||||
assert(err == 0); |
||||
} |
||||
|
||||
void transform_destroy(Transform* s) { |
||||
int err = 0; |
||||
|
||||
err = clReleaseMemObject(s->m_y_cl); |
||||
assert(err == 0); |
||||
err = clReleaseMemObject(s->m_uv_cl); |
||||
assert(err == 0); |
||||
|
||||
err = clReleaseKernel(s->krnl); |
||||
assert(err == 0); |
||||
} |
||||
|
||||
void transform_queue(Transform* s, |
||||
cl_command_queue q, |
||||
cl_mem in_yuv, int in_width, int in_height, |
||||
cl_mem out_y, cl_mem out_u, cl_mem out_v, |
||||
int out_width, int out_height, |
||||
mat3 projection) { |
||||
int err = 0; |
||||
const int zero = 0; |
||||
|
||||
// sampled using pixel center origin
|
||||
// (because thats how fastcv and opencv does it)
|
||||
|
||||
mat3 projection_y = projection; |
||||
|
||||
// in and out uv is half the size of y.
|
||||
mat3 projection_uv = transform_scale_buffer(projection, 0.5); |
||||
|
||||
err = clEnqueueWriteBuffer(q, s->m_y_cl, CL_TRUE, 0, 3*3*sizeof(float), (void*)projection_y.v, 0, NULL, NULL); |
||||
assert(err == 0); |
||||
err = clEnqueueWriteBuffer(q, s->m_uv_cl, CL_TRUE, 0, 3*3*sizeof(float), (void*)projection_uv.v, 0, NULL, NULL); |
||||
assert(err == 0); |
||||
|
||||
const int in_y_width = in_width; |
||||
const int in_y_height = in_height; |
||||
const int in_uv_width = in_width/2; |
||||
const int in_uv_height = in_height/2; |
||||
const int in_y_offset = 0; |
||||
const int in_u_offset = in_y_offset + in_y_width*in_y_height; |
||||
const int in_v_offset = in_u_offset + in_uv_width*in_uv_height; |
||||
|
||||
const int out_y_width = out_width; |
||||
const int out_y_height = out_height; |
||||
const int out_uv_width = out_width/2; |
||||
const int out_uv_height = out_height/2; |
||||
|
||||
err = clSetKernelArg(s->krnl, 0, sizeof(cl_mem), &in_yuv); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 1, sizeof(cl_int), &in_y_width); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_y_offset); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 3, sizeof(cl_int), &in_y_height); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 4, sizeof(cl_int), &in_y_width); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 5, sizeof(cl_mem), &out_y); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 6, sizeof(cl_int), &out_y_width); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 7, sizeof(cl_int), &zero); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 8, sizeof(cl_int), &out_y_height); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 9, sizeof(cl_int), &out_y_width); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 10, sizeof(cl_mem), &s->m_y_cl); |
||||
assert(err == 0); |
||||
|
||||
const size_t work_size_y[2] = {out_y_width, out_y_height}; |
||||
|
||||
err = clEnqueueNDRangeKernel(q, s->krnl, 2, NULL, |
||||
(const size_t*)&work_size_y, NULL, 0, 0, NULL); |
||||
assert(err == 0); |
||||
|
||||
|
||||
const size_t work_size_uv[2] = {out_uv_width, out_uv_height}; |
||||
|
||||
err = clSetKernelArg(s->krnl, 1, sizeof(cl_int), &in_uv_width); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_u_offset); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 3, sizeof(cl_int), &in_uv_height); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 4, sizeof(cl_int), &in_uv_width); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 5, sizeof(cl_mem), &out_u); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 6, sizeof(cl_int), &out_uv_width); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 7, sizeof(cl_int), &zero); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 8, sizeof(cl_int), &out_uv_height); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 9, sizeof(cl_int), &out_uv_width); |
||||
assert(err == 0); |
||||
|
||||
err = clSetKernelArg(s->krnl, 10, sizeof(cl_mem), &s->m_uv_cl); |
||||
assert(err == 0); |
||||
|
||||
err = clEnqueueNDRangeKernel(q, s->krnl, 2, NULL, |
||||
(const size_t*)&work_size_uv, NULL, 0, 0, NULL); |
||||
assert(err == 0); |
||||
|
||||
|
||||
err = clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_v_offset); |
||||
assert(err == 0); |
||||
err = clSetKernelArg(s->krnl, 5, sizeof(cl_mem), &out_v); |
||||
assert(err == 0); |
||||
|
||||
|
||||
err = clEnqueueNDRangeKernel(q, s->krnl, 2, NULL, |
||||
(const size_t*)&work_size_uv, NULL, 0, 0, NULL); |
||||
assert(err == 0); |
||||
} |
@ -0,0 +1,54 @@ |
||||
#define INTER_BITS 5 |
||||
#define INTER_TAB_SIZE (1 << INTER_BITS) |
||||
#define INTER_SCALE 1.f / INTER_TAB_SIZE |
||||
|
||||
#define INTER_REMAP_COEF_BITS 15 |
||||
#define INTER_REMAP_COEF_SCALE (1 << INTER_REMAP_COEF_BITS) |
||||
|
||||
__kernel void warpPerspective(__global const uchar * src, |
||||
int src_step, int src_offset, int src_rows, int src_cols, |
||||
__global uchar * dst, |
||||
int dst_step, int dst_offset, int dst_rows, int dst_cols, |
||||
__constant float * M) |
||||
{ |
||||
int dx = get_global_id(0); |
||||
int dy = get_global_id(1); |
||||
|
||||
if (dx < dst_cols && dy < dst_rows) |
||||
{ |
||||
float X0 = M[0] * dx + M[1] * dy + M[2]; |
||||
float Y0 = M[3] * dx + M[4] * dy + M[5]; |
||||
float W = M[6] * dx + M[7] * dy + M[8]; |
||||
W = W != 0.0f ? INTER_TAB_SIZE / W : 0.0f; |
||||
int X = rint(X0 * W), Y = rint(Y0 * W); |
||||
|
||||
short sx = convert_short_sat(X >> INTER_BITS); |
||||
short sy = convert_short_sat(Y >> INTER_BITS); |
||||
short ay = (short)(Y & (INTER_TAB_SIZE - 1)); |
||||
short ax = (short)(X & (INTER_TAB_SIZE - 1)); |
||||
|
||||
int v0 = (sx >= 0 && sx < src_cols && sy >= 0 && sy < src_rows) ? |
||||
convert_int(src[mad24(sy, src_step, src_offset + sx)]) : 0; |
||||
int v1 = (sx+1 >= 0 && sx+1 < src_cols && sy >= 0 && sy < src_rows) ? |
||||
convert_int(src[mad24(sy, src_step, src_offset + (sx+1))]) : 0; |
||||
int v2 = (sx >= 0 && sx < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? |
||||
convert_int(src[mad24(sy+1, src_step, src_offset + sx)]) : 0; |
||||
int v3 = (sx+1 >= 0 && sx+1 < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? |
||||
convert_int(src[mad24(sy+1, src_step, src_offset + (sx+1))]) : 0; |
||||
|
||||
float taby = 1.f/INTER_TAB_SIZE*ay; |
||||
float tabx = 1.f/INTER_TAB_SIZE*ax; |
||||
|
||||
int dst_index = mad24(dy, dst_step, dst_offset + dx); |
||||
|
||||
int itab0 = convert_short_sat_rte( (1.0f-taby)*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); |
||||
int itab1 = convert_short_sat_rte( (1.0f-taby)*tabx * INTER_REMAP_COEF_SCALE ); |
||||
int itab2 = convert_short_sat_rte( taby*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); |
||||
int itab3 = convert_short_sat_rte( taby*tabx * INTER_REMAP_COEF_SCALE ); |
||||
|
||||
int val = v0 * itab0 + v1 * itab1 + v2 * itab2 + v3 * itab3; |
||||
|
||||
uchar pix = convert_uchar_sat((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS); |
||||
dst[dst_index] = pix; |
||||
} |
||||
} |
@ -0,0 +1,38 @@ |
||||
#ifndef TRANSFORM_H |
||||
#define TRANSFORM_H |
||||
|
||||
#include <inttypes.h> |
||||
#include <stdbool.h> |
||||
|
||||
#ifdef __APPLE__ |
||||
#include <OpenCL/cl.h> |
||||
#else |
||||
#include <CL/cl.h> |
||||
#endif |
||||
|
||||
#include "common/mat.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct { |
||||
cl_kernel krnl; |
||||
cl_mem m_y_cl, m_uv_cl; |
||||
} Transform; |
||||
|
||||
void transform_init(Transform* s, cl_context ctx, cl_device_id device_id); |
||||
|
||||
void transform_destroy(Transform* transform); |
||||
|
||||
void transform_queue(Transform* s, cl_command_queue q, |
||||
cl_mem yuv, int in_width, int in_height, |
||||
cl_mem out_y, cl_mem out_u, cl_mem out_v, |
||||
int out_width, int out_height, |
||||
mat3 projection); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif // TRANSFORM_H
|
@ -0,0 +1,160 @@ |
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS |
||||
#ifdef __APPLE__ |
||||
#include <OpenCL/cl.h> |
||||
#else |
||||
#include <CL/cl.h> |
||||
#endif |
||||
|
||||
#include "clutil.h" |
||||
#include "transforms/transform.h" |
||||
|
||||
typedef struct { |
||||
int disable_model; |
||||
Transform transform; |
||||
|
||||
int in_width; |
||||
int in_height; |
||||
int out_width; |
||||
int out_height; |
||||
|
||||
cl_context context; |
||||
cl_command_queue command_queue; |
||||
cl_device_id device_id; |
||||
|
||||
size_t in_yuv_size; |
||||
cl_mem in_yuv_cl; |
||||
|
||||
cl_mem out_y_cl, out_u_cl, out_v_cl; |
||||
} VisionTest; |
||||
|
||||
void initialize_opencl(VisionTest* visiontest) { |
||||
// init cl
|
||||
/* Get Platform and Device Info */ |
||||
cl_platform_id platform_ids[16] = {0}; |
||||
cl_uint num_platforms; |
||||
int err = clGetPlatformIDs(16, platform_ids, &num_platforms); |
||||
if (err != 0) { |
||||
fprintf(stderr, "cl error: %d\n", err); |
||||
} |
||||
assert(err == 0); |
||||
|
||||
// try to find a CPU device
|
||||
cl_device_id device_id = NULL; |
||||
for (int i=0; i<num_platforms; i++) { |
||||
cl_uint num_devices_unused; |
||||
err = clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_CPU, 1, &device_id, |
||||
&num_devices_unused); |
||||
if (err == 0) break; |
||||
} |
||||
if (err != 0) { |
||||
fprintf(stderr, "cl error: %d\n", err); |
||||
} |
||||
assert(err == 0); |
||||
|
||||
visiontest->context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
visiontest->device_id = device_id; |
||||
} |
||||
|
||||
VisionTest* visiontest_create(int temporal_model, int disable_model, |
||||
int input_width, int input_height, |
||||
int model_input_width, int model_input_height) { |
||||
int err = 0; |
||||
|
||||
VisionTest* const vt = calloc(1, sizeof(*vt)); |
||||
assert(vt); |
||||
|
||||
vt->disable_model = disable_model; |
||||
vt->in_width = input_width; |
||||
vt->in_height = input_height; |
||||
vt->out_width = model_input_width; |
||||
vt->out_height = model_input_height; |
||||
|
||||
initialize_opencl(vt); |
||||
|
||||
transform_init(&vt->transform, vt->context, vt->device_id); |
||||
|
||||
|
||||
assert((vt->in_width%2) == 0 && (vt->in_height%2) == 0); |
||||
vt->in_yuv_size = vt->in_width*vt->in_height*3/2; |
||||
vt->in_yuv_cl = clCreateBuffer(vt->context, CL_MEM_READ_WRITE, |
||||
vt->in_yuv_size, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
vt->out_y_cl = clCreateBuffer(vt->context, CL_MEM_READ_WRITE, |
||||
vt->out_width*vt->out_width, NULL, &err); |
||||
assert(err == 0); |
||||
vt->out_u_cl = clCreateBuffer(vt->context, CL_MEM_READ_WRITE, |
||||
vt->out_width*vt->out_width/4, NULL, &err); |
||||
assert(err == 0); |
||||
vt->out_v_cl = clCreateBuffer(vt->context, CL_MEM_READ_WRITE, |
||||
vt->out_width*vt->out_width/4, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
vt->command_queue = clCreateCommandQueue(vt->context, vt->device_id, 0, &err); |
||||
assert(err == 0); |
||||
|
||||
return vt; |
||||
} |
||||
|
||||
void visiontest_destroy(VisionTest* vt) { |
||||
transform_destroy(&vt->transform); |
||||
|
||||
int err = 0; |
||||
|
||||
err = clReleaseMemObject(vt->in_yuv_cl); |
||||
assert(err == 0); |
||||
err = clReleaseMemObject(vt->out_y_cl); |
||||
assert(err == 0); |
||||
err = clReleaseMemObject(vt->out_u_cl); |
||||
assert(err == 0); |
||||
err = clReleaseMemObject(vt->out_v_cl); |
||||
assert(err == 0); |
||||
|
||||
err = clReleaseCommandQueue(vt->command_queue); |
||||
assert(err == 0); |
||||
|
||||
err = clReleaseContext(vt->context); |
||||
assert(err == 0); |
||||
|
||||
free(vt); |
||||
} |
||||
|
||||
void visiontest_transform(VisionTest* vt, const uint8_t* yuv_data, |
||||
uint8_t* out_y, uint8_t* out_u, uint8_t* out_v, |
||||
const float* transform) { |
||||
int err = 0; |
||||
|
||||
err = clEnqueueWriteBuffer(vt->command_queue, vt->in_yuv_cl, CL_FALSE, |
||||
0, vt->in_yuv_size, yuv_data, 0, NULL, NULL); |
||||
assert(err == 0); |
||||
|
||||
mat3 transform_m = *(const mat3*)transform; |
||||
|
||||
transform_queue(&vt->transform, vt->command_queue, |
||||
vt->in_yuv_cl, vt->in_width, vt->in_height, |
||||
vt->out_y_cl, vt->out_u_cl, vt->out_v_cl, |
||||
vt->out_width, vt->out_height, |
||||
transform_m); |
||||
|
||||
err = clEnqueueReadBuffer(vt->command_queue, vt->out_y_cl, CL_FALSE, |
||||
0, vt->out_width*vt->out_height, out_y, |
||||
0, NULL, NULL); |
||||
assert(err == 0); |
||||
err = clEnqueueReadBuffer(vt->command_queue, vt->out_u_cl, CL_FALSE, |
||||
0, vt->out_width*vt->out_height/4, out_u, |
||||
0, NULL, NULL); |
||||
assert(err == 0); |
||||
err = clEnqueueReadBuffer(vt->command_queue, vt->out_v_cl, CL_FALSE, |
||||
0, vt->out_width*vt->out_height/4, out_v, |
||||
0, NULL, NULL); |
||||
assert(err == 0); |
||||
|
||||
clFinish(vt->command_queue); |
||||
} |
||||
|
@ -0,0 +1,105 @@ |
||||
CC:=clang
|
||||
CXX:=clang++
|
||||
OPT_FLAGS:=-O2 -g -ggdb3
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux) |
||||
SHARED_FLAGS=-Wl,--whole-archive $^ -Wl,--no-whole-archive
|
||||
endif |
||||
ifeq ($(UNAME_S),Darwin) |
||||
SHARED_FLAGS=-Wl,-force_load $^
|
||||
endif |
||||
|
||||
PHONELIBS := ../../phonelibs
|
||||
BASEDIR := ../..
|
||||
|
||||
WARN_FLAGS = -Werror=implicit-function-declaration \
|
||||
-Werror=incompatible-pointer-types \
|
||||
-Werror=int-conversion \
|
||||
-Werror=return-type \
|
||||
-Werror=format-extra-args
|
||||
|
||||
CFLAGS = -std=gnu11 -g -fPIC $(OPT_FLAGS) $(WARN_FLAGS)
|
||||
CXXFLAGS = -std=c++14 -fPIC $(OPT_FLAGS) $(WARN_FLAGS)
|
||||
|
||||
EIGEN_FLAGS = -I$(PHONELIBS)/eigen
|
||||
|
||||
CEREAL_LIBS = $(BASEDIR)/cereal/libmessaging.a
|
||||
|
||||
OPENCV_LIBS = -lopencv_video -lopencv_core -lopencv_imgproc
|
||||
|
||||
ifeq ($(UNAME_S),Darwin) |
||||
VT_LDFLAGS += $(PHONELIBS)/capnp-c/mac/lib/libcapnp_c.a \
|
||||
$(PHONELIBS)/zmq/mac/lib/libczmq.a \
|
||||
$(PHONELIBS)/zmq/mac/lib/libzmq.a \
|
||||
-framework OpenCL
|
||||
|
||||
OPENCV_LIBS += -L/usr/local/opt/opencv@2/lib
|
||||
OPENCV_FLAGS += -I/usr/local/opt/opencv@2/include
|
||||
|
||||
else |
||||
VT_LDFLAGS += $(CEREAL_LIBS) \
|
||||
-L/system/vendor/lib64 \
|
||||
-L$(BASEDIR)/external/zmq/lib/ \
|
||||
-l:libczmq.a -l:libzmq.a \
|
||||
-lOpenCL
|
||||
endif |
||||
|
||||
.PHONY: all visiontest clean test |
||||
all: visiontest |
||||
|
||||
libvisiontest_inputs := visiontest.c \
|
||||
transforms/transform.c \
|
||||
transforms/loadyuv.c \
|
||||
../common/clutil.c \
|
||||
$(BASEDIR)/selfdrive/common/util.c \
|
||||
$(CEREAL_OBJS)
|
||||
|
||||
visiontest: libvisiontest.so |
||||
all-tests := $(addsuffix .test, $(basename $(wildcard test_*)))
|
||||
|
||||
%.o: %.cc |
||||
@echo "[ CXX ] $@"
|
||||
$(CXX) $(CXXFLAGS) -MMD \
|
||||
-I. -I.. -I../.. \
|
||||
-Wall \
|
||||
-I$(BASEDIR)/ -I$(BASEDIR)/selfdrive -I$(BASEDIR)/selfdrive/common \
|
||||
$(EIGEN_FLAGS) \
|
||||
$(OPENCV_FLAGS) \
|
||||
$(CEREAL_CXXFLAGS) \
|
||||
-c -o '$@' '$<'
|
||||
|
||||
%.o: %.c |
||||
@echo "[ CXX ] $@"
|
||||
$(CC) $(CFLAGS) -MMD \
|
||||
-I. -I.. -I../.. \
|
||||
-Wall \
|
||||
-I$(BASEDIR)/ -I$(BASEDIR)/selfdrive -I$(BASEDIR)/selfdrive/common \
|
||||
$(CEREAL_CFLAGS) \
|
||||
-c -o '$@' '$<'
|
||||
|
||||
libvisiontest.so: $(libvisiontest_inputs) |
||||
$(eval $@_TMP := $(shell mktemp))
|
||||
$(CC) -std=gnu11 -shared -fPIC -O2 -g \
|
||||
-Werror=implicit-function-declaration -Werror=incompatible-pointer-types \
|
||||
-Werror=int-conversion -Wno-pointer-to-int-cast \
|
||||
-I. -DCLU_NO_CACHE \
|
||||
$^ -o $($@_TMP) \
|
||||
-I$(PHONELIBS)/opencl/include \
|
||||
-I$(BASEDIR)/selfdrive/common \
|
||||
$(CEREAL_CXXFLAGS) \
|
||||
$(CEREAL_CFLAGS) \
|
||||
-I$(BASEDIR)/external/zmq/include \
|
||||
-I$(BASEDIR)/ -I$(BASEDIR)/selfdrive \
|
||||
-lstdc++ \
|
||||
$(VT_LDFLAGS) \
|
||||
-lm -lpthread
|
||||
mv $($@_TMP) $@
|
||||
|
||||
test : $(all-tests) |
||||
|
||||
test_%.test : test_% |
||||
@./'$<' || echo FAIL
|
||||
|
||||
clean: |
||||
rm -rf *.o *.so *.a
|
@ -0,0 +1,135 @@ |
||||
import os |
||||
import subprocess |
||||
from cffi import FFI |
||||
from common.basedir import BASEDIR |
||||
|
||||
# Initialize visiontest. Ignore output. |
||||
_visiond_dir = os.path.dirname(os.path.abspath(__file__)) |
||||
_libvisiontest = "libvisiontest.so" |
||||
try: # bacause this crashes somtimes when running pipeline |
||||
subprocess.check_output(["make", "-C", _visiond_dir, "-f", |
||||
os.path.join(_visiond_dir, "visiontest.mk"), |
||||
_libvisiontest]) |
||||
except: |
||||
pass |
||||
|
||||
class VisionTest(): |
||||
"""A version of the vision model that can be run on a desktop. |
||||
|
||||
WARNING: This class is not thread safe. VisionTest objects cannot be |
||||
created or used on multiple threads simultaneously. |
||||
""" |
||||
|
||||
ffi = FFI() |
||||
ffi.cdef(""" |
||||
typedef unsigned char uint8_t; |
||||
|
||||
struct VisionTest; |
||||
typedef struct VisionTest VisionTest; |
||||
|
||||
VisionTest* visiontest_create(int temporal_model, int disable_model, |
||||
int input_width, int input_height, |
||||
int model_input_width, int model_input_height); |
||||
void visiontest_destroy(VisionTest* visiontest); |
||||
|
||||
void visiontest_transform(VisionTest* vt, const uint8_t* yuv_data, |
||||
uint8_t* out_y, uint8_t* out_u, uint8_t* out_v, |
||||
const float* transform); |
||||
""") |
||||
|
||||
clib = ffi.dlopen(os.path.join(_visiond_dir, _libvisiontest)) |
||||
|
||||
def __init__(self, input_size, model_input_size, model): |
||||
"""Create a wrapper around visiond for off-device python code. |
||||
|
||||
Inputs: |
||||
input_size: The size of YUV images passed to transform. |
||||
model_input_size: The size of YUV images passed to the model. |
||||
model: The name of the model to use. "temporal", "yuv", or None to disable the |
||||
model (used to disable OpenCL). |
||||
""" |
||||
self._input_size = input_size |
||||
self._model_input_size = model_input_size |
||||
|
||||
if model is None: |
||||
disable_model = 1 |
||||
temporal_model = 0 |
||||
elif model == "yuv": |
||||
disable_model = 0 |
||||
temporal_model = 0 |
||||
elif model == "temporal": |
||||
disable_model = 0 |
||||
temporal_model = 1 |
||||
else: |
||||
raise ValueError("Bad model name: {}".format(model)) |
||||
|
||||
prevdir = os.getcwd() |
||||
os.chdir(_visiond_dir) # tmp hack to find kernels |
||||
os.environ['BASEDIR'] = BASEDIR |
||||
self._visiontest_c = self.clib.visiontest_create( |
||||
temporal_model, disable_model, self._input_size[0], self._input_size[1], |
||||
self._model_input_size[0], self._model_input_size[1]) |
||||
os.chdir(prevdir) |
||||
|
||||
@property |
||||
def input_size(self): |
||||
return self._input_size |
||||
|
||||
@property |
||||
def model_input_size(self): |
||||
return self._model_input_size |
||||
|
||||
def transform(self, yuv_data, transform): |
||||
y_len = self.model_input_size[0] * self.model_input_size[1] |
||||
t_y_ptr = bytearray(y_len) |
||||
t_u_ptr = bytearray(y_len // 4) |
||||
t_v_ptr = bytearray(y_len // 4) |
||||
|
||||
self.transform_output_buffer(yuv_data, t_y_ptr, t_u_ptr, t_v_ptr, |
||||
transform) |
||||
|
||||
return t_y_ptr, t_u_ptr, t_v_ptr |
||||
|
||||
def transform_contiguous(self, yuv_data, transform): |
||||
y_ol = self.model_input_size[0] * self.model_input_size[1] |
||||
uv_ol = y_ol // 4 |
||||
result = bytearray(y_ol * 3 // 2) |
||||
result_view = memoryview(result) |
||||
t_y_ptr = result_view[:y_ol] |
||||
t_u_ptr = result_view[y_ol:y_ol + uv_ol] |
||||
t_v_ptr = result_view[y_ol + uv_ol:] |
||||
|
||||
self.transform_output_buffer(yuv_data, t_y_ptr, t_u_ptr, t_v_ptr, |
||||
transform) |
||||
return result |
||||
|
||||
|
||||
def transform_output_buffer(self, yuv_data, y_out, u_out, v_out, |
||||
transform): |
||||
assert len(yuv_data) == self.input_size[0] * self.input_size[1] * 3/2 |
||||
|
||||
cast = self.ffi.cast |
||||
from_buffer = self.ffi.from_buffer |
||||
yuv_ptr = cast("unsigned char*", from_buffer(yuv_data)) |
||||
transform_ptr = self.ffi.new("float[]", transform) |
||||
|
||||
y_out_ptr = cast("unsigned char*", from_buffer(y_out)) |
||||
u_out_ptr = cast("unsigned char*", from_buffer(u_out)) |
||||
v_out_ptr = cast("unsigned char*", from_buffer(v_out)) |
||||
|
||||
self.clib.visiontest_transform(self._visiontest_c, yuv_ptr, y_out_ptr, |
||||
u_out_ptr, v_out_ptr, transform_ptr) |
||||
|
||||
def close(self): |
||||
self.clib.visiontest_destroy(self._visiontest_c) |
||||
self._visiontest_c = None |
||||
|
||||
def __enter__(self): |
||||
return self |
||||
|
||||
def __exit__(self, type, value, traceback): |
||||
self.close() |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
VisionTest((560, 304), (320, 160), "temporal") |
@ -0,0 +1,2 @@ |
||||
Import('env', 'messaging') |
||||
env.Program('proclogd.cc', LIBS=[messaging, 'pthread', 'zmq', 'czmq', 'capnp', 'kj']) |
@ -0,0 +1,247 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <climits> |
||||
#include <cassert> |
||||
|
||||
#include <unistd.h> |
||||
#include <dirent.h> |
||||
|
||||
#include <vector> |
||||
#include <string> |
||||
#include <memory> |
||||
#include <utility> |
||||
#include <sstream> |
||||
#include <fstream> |
||||
#include <algorithm> |
||||
#include <functional> |
||||
#include <unordered_map> |
||||
|
||||
#include "messaging.hpp" |
||||
#include <capnp/serialize.h> |
||||
#include "cereal/gen/cpp/log.capnp.h" |
||||
|
||||
#include "common/timing.h" |
||||
#include "common/utilpp.h" |
||||
|
||||
namespace { |
||||
|
||||
struct ProcCache { |
||||
std::string name; |
||||
std::vector<std::string> cmdline; |
||||
std::string exe; |
||||
}; |
||||
|
||||
} |
||||
|
||||
int main() { |
||||
int err; |
||||
|
||||
Context * c = Context::create(); |
||||
PubSocket * publisher = PubSocket::create(c, "procLog"); |
||||
assert(publisher != NULL); |
||||
|
||||
double jiffy = sysconf(_SC_CLK_TCK); |
||||
size_t page_size = sysconf(_SC_PAGE_SIZE); |
||||
|
||||
std::unordered_map<pid_t, ProcCache> proc_cache; |
||||
|
||||
while (1) { |
||||
|
||||
capnp::MallocMessageBuilder msg; |
||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
||||
event.setLogMonoTime(nanos_since_boot()); |
||||
auto procLog = event.initProcLog(); |
||||
|
||||
auto orphanage = msg.getOrphanage(); |
||||
|
||||
// stat
|
||||
{ |
||||
std::vector<capnp::Orphan<cereal::ProcLog::CPUTimes>> otimes; |
||||
|
||||
std::ifstream sstat("/proc/stat"); |
||||
std::string stat_line; |
||||
while (std::getline(sstat, stat_line)) { |
||||
if (util::starts_with(stat_line, "cpu ")) { |
||||
// cpu total
|
||||
} else if (util::starts_with(stat_line, "cpu")) { |
||||
// specific cpu
|
||||
int id; |
||||
unsigned long utime, ntime, stime, itime; |
||||
unsigned long iowtime, irqtime, sirqtime; |
||||
|
||||
int count = sscanf(stat_line.data(), "cpu%d %lu %lu %lu %lu %lu %lu %lu", |
||||
&id, &utime, &ntime, &stime, &itime, &iowtime, &irqtime, &sirqtime); |
||||
|
||||
auto ltimeo = orphanage.newOrphan<cereal::ProcLog::CPUTimes>(); |
||||
auto ltime = ltimeo.get(); |
||||
ltime.setCpuNum(id); |
||||
ltime.setUser(utime / jiffy); |
||||
ltime.setNice(ntime / jiffy); |
||||
ltime.setSystem(stime / jiffy); |
||||
ltime.setIdle(itime / jiffy); |
||||
ltime.setIowait(iowtime / jiffy); |
||||
ltime.setIrq(irqtime / jiffy); |
||||
ltime.setSoftirq(irqtime / jiffy); |
||||
|
||||
otimes.push_back(std::move(ltimeo)); |
||||
|
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
auto ltimes = procLog.initCpuTimes(otimes.size()); |
||||
for (size_t i = 0; i < otimes.size(); i++) { |
||||
ltimes.adoptWithCaveats(i, std::move(otimes[i])); |
||||
} |
||||
} |
||||
|
||||
// meminfo
|
||||
{ |
||||
auto mem = procLog.initMem(); |
||||
|
||||
std::ifstream smem("/proc/meminfo"); |
||||
std::string mem_line; |
||||
|
||||
uint64_t mem_total = 0, mem_free = 0, mem_available = 0, mem_buffers = 0; |
||||
uint64_t mem_cached = 0, mem_active = 0, mem_inactive = 0, mem_shared = 0; |
||||
|
||||
while (std::getline(smem, mem_line)) { |
||||
if (util::starts_with(mem_line, "MemTotal:")) sscanf(mem_line.data(), "MemTotal: %lu kB", &mem_total); |
||||
else if (util::starts_with(mem_line, "MemFree:")) sscanf(mem_line.data(), "MemFree: %lu kB", &mem_free); |
||||
else if (util::starts_with(mem_line, "MemAvailable:")) sscanf(mem_line.data(), "MemAvailable: %lu kB", &mem_available); |
||||
else if (util::starts_with(mem_line, "Buffers:")) sscanf(mem_line.data(), "Buffers: %lu kB", &mem_buffers); |
||||
else if (util::starts_with(mem_line, "Cached:")) sscanf(mem_line.data(), "Cached: %lu kB", &mem_cached); |
||||
else if (util::starts_with(mem_line, "Active:")) sscanf(mem_line.data(), "Active: %lu kB", &mem_active); |
||||
else if (util::starts_with(mem_line, "Inactive:")) sscanf(mem_line.data(), "Inactive: %lu kB", &mem_inactive); |
||||
else if (util::starts_with(mem_line, "Shmem:")) sscanf(mem_line.data(), "Shmem: %lu kB", &mem_shared); |
||||
} |
||||
|
||||
mem.setTotal(mem_total * 1024); |
||||
mem.setFree(mem_free * 1024); |
||||
mem.setAvailable(mem_available * 1024); |
||||
mem.setBuffers(mem_buffers * 1024); |
||||
mem.setCached(mem_cached * 1024); |
||||
mem.setActive(mem_active * 1024); |
||||
mem.setInactive(mem_inactive * 1024); |
||||
mem.setShared(mem_shared * 1024); |
||||
} |
||||
|
||||
// processes
|
||||
{ |
||||
std::vector<capnp::Orphan<cereal::ProcLog::Process>> oprocs; |
||||
struct dirent *de = NULL; |
||||
DIR *d = opendir("/proc"); |
||||
assert(d); |
||||
while ((de = readdir(d))) { |
||||
if (!isdigit(de->d_name[0])) continue; |
||||
pid_t pid = atoi(de->d_name); |
||||
|
||||
|
||||
auto lproco = orphanage.newOrphan<cereal::ProcLog::Process>(); |
||||
auto lproc = lproco.get(); |
||||
|
||||
lproc.setPid(pid); |
||||
|
||||
char tcomm[PATH_MAX] = {0}; |
||||
|
||||
{ |
||||
std::string stat = util::read_file(util::string_format("/proc/%d/stat", pid)); |
||||
|
||||
char state; |
||||
|
||||
int ppid; |
||||
unsigned long utime, stime; |
||||
long cutime, cstime, priority, nice, num_threads; |
||||
unsigned long long starttime; |
||||
unsigned long vms, rss; |
||||
int processor; |
||||
|
||||
int count = sscanf(stat.data(), |
||||
"%*d (%1024[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d " |
||||
"%lu %lu %ld %ld %ld %ld %ld %*d %lld " |
||||
"%lu %lu %*d %*d %*d %*d %*d %*d %*d " |
||||
"%*d %*d %*d %*d %*d %*d %*d %d", |
||||
tcomm, &state, &ppid, |
||||
&utime, &stime, &cutime, &cstime, &priority, &nice, &num_threads, &starttime, |
||||
&vms, &rss, &processor); |
||||
|
||||
if (count != 14) continue; |
||||
|
||||
lproc.setState(state); |
||||
lproc.setPpid(ppid); |
||||
lproc.setCpuUser(utime / jiffy); |
||||
lproc.setCpuSystem(stime / jiffy); |
||||
lproc.setCpuChildrenUser(cutime / jiffy); |
||||
lproc.setCpuChildrenSystem(cstime / jiffy); |
||||
lproc.setPriority(priority); |
||||
lproc.setNice(nice); |
||||
lproc.setNumThreads(num_threads); |
||||
lproc.setStartTime(starttime / jiffy); |
||||
lproc.setMemVms(vms); |
||||
lproc.setMemRss((uint64_t)rss * page_size); |
||||
lproc.setProcessor(processor); |
||||
} |
||||
|
||||
std::string name(tcomm); |
||||
lproc.setName(name); |
||||
|
||||
// populate other things from cache
|
||||
auto cache_it = proc_cache.find(pid); |
||||
ProcCache cache; |
||||
if (cache_it != proc_cache.end()) { |
||||
cache = cache_it->second; |
||||
} |
||||
if (cache_it == proc_cache.end() || cache.name != name) { |
||||
cache = (ProcCache){ |
||||
.name = name, |
||||
.exe = util::readlink(util::string_format("/proc/%d/exe", pid)), |
||||
}; |
||||
|
||||
// null-delimited cmdline arguments to vector
|
||||
std::string cmdline_s = util::read_file(util::string_format("/proc/%d/cmdline", pid)); |
||||
const char* cmdline_p = cmdline_s.c_str(); |
||||
const char* cmdline_ep = cmdline_p + cmdline_s.size(); |
||||
|
||||
// strip trailing null bytes
|
||||
while ((cmdline_ep-1) > cmdline_p && *(cmdline_ep-1) == 0) { |
||||
cmdline_ep--; |
||||
} |
||||
|
||||
while (cmdline_p < cmdline_ep) { |
||||
std::string arg(cmdline_p); |
||||
cache.cmdline.push_back(arg); |
||||
cmdline_p += arg.size() + 1; |
||||
} |
||||
|
||||
proc_cache[pid] = cache; |
||||
} |
||||
|
||||
auto lcmdline = lproc.initCmdline(cache.cmdline.size()); |
||||
for (size_t i = 0; i < lcmdline.size(); i++) { |
||||
lcmdline.set(i, cache.cmdline[i]); |
||||
} |
||||
lproc.setExe(cache.exe); |
||||
|
||||
oprocs.push_back(std::move(lproco)); |
||||
} |
||||
closedir(d); |
||||
|
||||
auto lprocs = procLog.initProcs(oprocs.size()); |
||||
for (size_t i = 0; i < oprocs.size(); i++) { |
||||
lprocs.adoptWithCaveats(i, std::move(oprocs[i])); |
||||
} |
||||
} |
||||
|
||||
auto words = capnp::messageToFlatArray(msg); |
||||
auto bytes = words.asBytes(); |
||||
publisher->send((char*)bytes.begin(), bytes.size()); |
||||
|
||||
usleep(2000000); // 2 secs
|
||||
} |
||||
|
||||
delete c; |
||||
delete publisher; |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue