통합 녹화 API¶
oasis::startRecording 호출 시 전달하는 oasis::MediaObserver 객체를 통하여, 녹화와 스냅샷 이벤트를 처리합니다.
헤더 파일¶
OasisMedia.h
함수¶
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
Note
changeRecorderParameters 호출 전에 Recorder은 정지된 상태이어야 합니다. 활성 상태이면 stopRecording을 호출하여 정지 상태로 만듭니다.
- true: Recorder가 동작 상태입니다.
- false: Recorder가 중지 상태입니다.
- true: Recorder가 녹화 데이터를 파일에 쓰는 상태입니다.
- false: Recorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
Note
모션 녹화는 Sniffing 모드에서만 지원됩니다.
- 0: 성공
- -1: 실패
- true: Recorder가 녹화 데이터를 파일에 쓰는 상태입니다.
- false: Recorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- true: Recorder가 녹화 데이터를 파일에 쓰는 상태입니다.
- false: Recorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- true: Recorder가 녹화 데이터를 파일에 쓰는 상태입니다.
- false: Recorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- true: 이벤트 녹화가 동작 상태입니다.
- false: 이벤트 녹화가 중지 상태입니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 활성화 중
- false: 중단 중
- 0: 성공
- -1: 실패
- true: 활성화 중
- false: 중단 중
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
아래는 key-value map 목록입니다.
예제¶
아래는 3개 영상 채널과 소리를 녹화하는 예입니다.
USE_OFFS가 1이면 Oasis 파일시스템을 사용하고, 0이면 /tmp 폴더 밑에 DRIVING, EVENT 폴더를 생성하여 사용합니다.
각 폴더내 파일 크기는 20MB로 제한하고 총 5개만 녹화됩니다. Oasis 파일 시스템의 경우, 녹화 파일개수 제한은 없으며, 용량이 다 찬 경우 자동으로 오래된 파일을 덮어씁니다.
#define USE_OFFS 0
비디오 코덱은 H264이고, 오디오 코덱은 AAC를 사용합니다.
//non-offs, use system fs
#if !USE_OFFS
fs::offsConfigLocalFormatInfo("/tmp/DRIVING", 20*1024*1024);
fs::offsConfigLocalFormatInfo("/tmp/EVENT", 20*1024*1024);
#endif
Oasis를 초기화합니다.
bool offs_disabled = false;
if(!offs_disabled) {
parameters["offs-qsize-max"] = std::to_string(OFFS_QUEUE_SIZE_KBYTES);
parameters["offs-overwrite-if-exist"] = "1";
parameters["offs-cache-size"] = std::to_string(OFFS_CACHE_SIZE_KBYTES);
} else {
parameters["offs-disable"] = "1";
}
parameters["media-cache-size"] = std::to_string(MEDIA_CACHE_SIZE_KBYTES);
//parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
enableLogLocalTime(true);
if(oasis::initialize(parameters) < 0) {
DLOG(DLOG_ERROR, "Oasis init failed\n");
return -1;
}
입력과 출력 오디오 장치를 초기화 합니다.
////////////////////////////////////////////////////////////////////////////////////////////
// audio
parameters.clear();
parameters["types"]="source,sink";
parameters["path"]=SND_PATH;
parameters["always-on"]="0";
parameters["channels"]="2";
parameters["aec-disabled"]="1";
parameters["snd-input-channels"]="2";
parameters["snd-input-sample-size"]="16";
parameters["snd-input-sampling-duration-msec"]="40";
parameters["snd-input-sampling-rate"]="48000";
createAudioDevice(parameters);
총 3개의 카메라 장치를 설정합니다.
////////////////////////////////////////////////////////////////////////////////////////////
// sources
parameters.clear();
parameters["source-count"] = "3";
parameters["source1-camera-id"] = "0";
parameters["source1-isp-id"] = "0";
parameters["source1-isp-wdr-mode"] = "0";
parameters["source1-capture-format"] = "YUV420";
parameters["source1-capture-buffers"] = "5";
parameters["source1-fps"] = "30";
parameters["source1-subchannel-rotation"] = "0";
parameters["source1-loc"] = "front";
parameters["source1-capture-resolution"] = "2160p";
parameters["source1-sensor-config"] = "./Resource_678/VIC/2/imx678_3840x2160_ch2.cfg";
parameters["source1-autoscene-config"] = "./Resource_678/AutoScene/autoscene_conf.cfg";
parameters["source1-resource-dir"] = "./Resource_678/";
parameters["source2-camera-id"] = "1";
parameters["source2-isp-id"] = "0";
parameters["source2-isp-wdr-mode"] = "0";
parameters["source2-capture-format"] = "YUV420";
parameters["source2-capture-buffers"] = "5";
parameters["source2-fps"] = "30";
parameters["source2-subchannel-rotation"] = "0";
parameters["source2-loc"] = "right";
parameters["source2-capture-resolution"] = "1080p";
parameters["source2-sensor-config"] = "./Resource_tp2863/VIC/0/tp2863_1920x1080_ch0.cfg";
parameters["source2-autoscene-config"] = "./Resource_tp2863/AutoScene/autoscene_conf.cfg";
parameters["source2-resource-dir"] = "./Resource_tp2863/";
parameters["source3-camera-id"] = "2";
parameters["source3-isp-id"] = "0";
parameters["source3-isp-wdr-mode"] = "0";
parameters["source3-capture-format"] = "YUV420";
parameters["source3-capture-buffers"] = "5";
parameters["source3-fps"] = "30";
parameters["source3-subchannel-rotation"] = "0";
parameters["source3-loc"] = "left";
parameters["source3-capture-resolution"] = "1080p";
parameters["source3-sensor-config"] = "./Resource_tp2863/VIC/1/tp2863_1920x1080_ch1.cfg";
parameters["source3-autoscene-config"] = "./Resource_tp2863/AutoScene/autoscene_conf.cfg";
parameters["source3-resource-dir"] = "./Resource_tp2863/";
configCameras(parameters);
MediaObserver 로 부터 유도된 사용자 정의 Observer 클래스를 정의합니다.
class MyRecordingObserver : public MediaObserver {
public:
virtual void onStarted(void* user_data, const char* file_path) {
DLOG(DLOG_RECORD|DLOG_INFO, "Recording started, file path \"%s\"\r\n", file_path);
}
virtual void onStopped(void* user_data, media_report_reason_t reason, void* details) {
DLOG(DLOG_RECORD|DLOG_INFO, "Recording stopped, reason: %d(%s)\r\n", reason, mediaReportReasonString(reason));
if(reason != kMediaReportNoError) {
continue_recording = false;
}
}
virtual void onFileChanged(void* user_data, const char* new_file_path) {
/* (새)녹화가 시작되었습니다. */
}
virtual void onError(void* user_data, media_report_reason_t reason, void* details) {
/* 에러를 콘솔에 출력하고 녹화를 종료합니다. */
continue_recording = false;
}
virtual void onInfo(void* user_data, MediaInfo* info) {
/* 사용하지 않습니다. */
}
virtual void onInfoEx(void* user_data, MediaInfoEx* info) {
/* 녹화 경과 시간과 자원 상태를 콘솔에 출력합니다. */
}
//player paused and resumed
virtual void onPaused(void *user_data, MediaInfo *info) {}
virtual void onResumed(void *user_data, MediaInfo *info) {}
virtual void onEventRecordingStarted(void* user_data, media_report_reason_t reason, const char* file_path) {}
virtual void onEventRecordingCompleted(void* user_data, media_report_reason_t reason, const char* file_path) {}
virtual void onMotionRecordingStarted(void *user_data, media_report_reason_t reason, const char *file_path) {}
virtual void onMotionRecordingCompleted(void *user_data, media_report_reason_t reason, const char *file_path) {}
virtual void queryNewFilePath(void* user_data, oasis::recording_mode_t file_type, bool rear_camera_on, bool sound_on, std::string& file_path) {
/* 새 녹화 파일경로를 변경하지 않고 그대로 사용하게 합니다. */
}
virtual void onSnapshotCompleted(void *user_data, uint32_t snapshot_id, int32_t camera_id, int error, const std::vector<char> &image_data, const struct timeval ×tamp) {}
};
녹화 객체를 생성합니다. configCameras에서 설정한 카메라 개수와 순서와 일치하지 않아도 됩니다. configCameras의 source<N>- 과 녹화 객체 생성 시의 channel<N>- 은 별개입니다. 녹화 객체 생성 시 channel<N>-camera-id 값은 source<M>-camera-id 에 일치한 카메라 장치 source<M>을 사용합니다.
////////////////////////////////////////////////////////////////////////////////////////////
//recorer settings
parameters.clear();
parameters["file-prefix"] = "oasis-";
parameters["file-extension"] = "mp4";
parameters["file-duration-secs"] = "60";
#if USE_OFFS
parameters["normal-folder-path"] = "/mnt/sd/DRIVING";
parameters["event-folder-path"] = "/mnt/sd/EVENT";
parameters["motion-folder-path"] = "/mnt/sd/EVENT";
#else
parameters["normal-folder-path"] = "/tmp/DRIVING";
parameters["event-folder-path"] = "/tmp/EVENT";
parameters["motion-folder-path"] = "/tmp/EVENT";
#endif
parameters["event-pre-recording-seconds"] = "10";
parameters["event-post-recording-seconds"] = "10";
parameters["motion-pre-recording-seconds"] = "10";
parameters["motion-post-recording-seconds"] = "30";
parameters["disable-event-recording"] = "0";
parameters["disable-offs-recording"] = "0";
#if USE_OFFS
parameters["max-files"] = "0";
#else
parameters["max-files"] = "5";
#endif
parameters["delete-oldest-file-on-max-files"] = "1";
parameters["recording-size-limit-threshold-seconds"] = "1";
parameters["report-media-info-ex"] = "1";
parameters["avi-strd-size-max"] = "65536";
parameters["mp4-udta-size-max"] = "65536";
parameters["enable-persistent-cache"] = "0";
parameters["recording-file-header-write-interval-secs"] = "1";
parameters["snd-path"] = "hw:0,0";
parameters["snd-input-channels"] = "2";
parameters["snd-input-sample-size"] = "16";
parameters["snd-input-sampling-duration-msec"] = "120";
parameters["snd-input-sampling-rate"] = "44100";
//parameters["snd-input-sampling-rate"] = "48000";
parameters["aencoder-type"] = "aac"; //aac, raw, mp3
parameters["aencoder-bitrate"] = "128000";
parameters["osd-font-size"] = "0"; // disable (default)
parameters["osd-font-face"] = "Consolas";
parameters["osd-text-color"] = "255,255,255";
parameters["osd-use-text-color"] = "1";
parameters["osd-use-bg-color"] = "0";
parameters["osd-bg-color"] = "0,0,0";
parameters["osd-use-outline-color"] = "1";
parameters["osd-outline-color"] = "255,255,255";
parameters["osd-horz-align"] = "left";
parameters["osd-vert-align"] = "bottom";
parameters["osd-font-path"] = "/mnt/sd/consola.ttf";
parameters["osd-use-fixed-size"] = "0";
parameters["channel-count"] = "3";
parameters["channel1-camera-id"] = "0";
parameters["channel1-ise-id"] = "-1";
parameters["channel1-resolution"] = "2160p";
parameters["channel1-bitrate"] = "8000000";
parameters["channel1-fps"] = "30";
parameters["channel1-file-framerate"] = "30";
parameters["channel1-vencoder-type"] = "h264";
parameters["channel1-venc-framerate"] = "30";
parameters["channel1-venc-keyframe-interval"] = "30";
parameters["channel1-h264-profile"] = "high";
parameters["channel1-h264-level"] = "level51";
parameters["channel1-h264-enable-cabac"] = "1";
parameters["channel1-h264-min-qp"] = "10";
parameters["channel1-h264-max-qp"] = "31";
parameters["channel1-h264-enable-fixqp"] = "0";
parameters["channel1-h264-fix-iqp"] = "10";
parameters["channel1-h264-fix-pqp"] = "20";
parameters["channel1-media-wait-timeout-secs"] = "3";
parameters["channel1-media-wait-timeout-notify-oneshot"] = "1";
parameters["channel1-osd-font-size"] = "12";
parameters["channel2-camera-id"] = "1";
parameters["channel2-ise-id"] = "-1";
parameters["channel2-resolution"] = "1080p";
parameters["channel2-bitrate"] = "8000000";
parameters["channel2-fps"] = "30";
parameters["channel2-file-framerate"] = "30";
parameters["channel2-vencoder-type"] = "h264";
parameters["channel2-venc-framerate"] = "30";
parameters["channel2-venc-keyframe-interval"] = "30";
parameters["channel2-h264-profile"] = "high";
parameters["channel2-h264-level"] = "level51";
parameters["channel2-h264-enable-cabac"] = "1";
parameters["channel2-h264-min-qp"] = "10";
parameters["channel2-h264-max-qp"] = "31";
parameters["channel2-h264-enable-fixqp"] = "0";
parameters["channel2-h264-fix-iqp"] = "10";
parameters["channel2-h264-fix-pqp"] = "20";
parameters["channel2-media-wait-timeout-secs"] = "3";
parameters["channel2-media-wait-timeout-notify-oneshot"] = "1";
parameters["channel2-osd-font-size"] = "12";
parameters["channel3-camera-id"] = "2";
parameters["channel3-ise-id"] = "-1";
parameters["channel3-resolution"] = "1080p";
parameters["channel3-bitrate"] = "8000000";
parameters["channel3-fps"] = "30";
parameters["channel3-file-framerate"] = "30";
parameters["channel3-vencoder-type"] = "h264";
parameters["channel3-venc-framerate"] = "30";
parameters["channel3-venc-keyframe-interval"] = "30";
parameters["channel3-h264-profile"] = "high";
parameters["channel3-h264-level"] = "level51";
parameters["channel3-h264-enable-cabac"] = "1";
parameters["channel3-h264-min-qp"] = "10";
parameters["channel3-h264-max-qp"] = "31";
parameters["channel3-h264-enable-fixqp"] = "0";
parameters["channel3-h264-fix-iqp"] = "10";
parameters["channel3-h264-fix-pqp"] = "20";
parameters["channel3-media-wait-timeout-secs"] = "3";
parameters["channel3-media-wait-timeout-notify-oneshot"] = "1";
parameters["channel3-osd-font-size"] = "12";
std::shared_ptr<MyRecordingObserver> recording_observer = std::make_shared<MyRecordingObserver>();
//dumpParameters("recorder", recorder_parameters);
RecorderRef recorder = createRecorder(parameters);
if(recorder == nullptr) {
DLOG(DLOG_ERROR, "recorder creation failed!\r\n");
goto done;
}
타이머 쓰레드를 생성하여 메타 데이터와 OSD 텍스트를 출력하도록 합니다.
t2 = std::thread([&]() {
time_t t;
struct tm tm_t;
uint64_t usec, preview_usec;
std::string title;
uint32_t count = 0;
char stream_data[512] = { 0, };
preview_usec = systemTime();
do {
usec = systemTime();
time(&t);
localtime_r(&t, &tm_t);
if (isRecorderRecording(recorder)) {
sprintf(stream_data, "Main %4d/%02d/%02d %02d:%02d:%02d", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
addRecordingVideoStreamData(recorder, (void*)stream_data, strlen(stream_data) + 1);
}
if(/*count == 0 &&*/ isRecorderRecording(recorder)) {
title = oasis::format("OASIS %4d/%02d/%02d %02d:%02d:%02d\n", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
setRecordingText(recorder, kTextTrackOsd, title, usec);
count++;
}
//30 msec
usleep(40000);
} while (timer_running);
});
녹화를 시작합니다.
err = startRecording(recorder, recording_observer, nullptr, false, &strd_data_header_, sizeof(strd_data_header_));
if(err < 0) {
DLOG(DLOG_ERROR, "start recording failed\r\n");
goto done;
}
아래는 전체 코드입니다.
#include "OasisAPI.h"
#include "OasisLog.h"
#include "OasisMedia.h"
#include "OasisFS.h"
#include "OasisUtil.h"
#include <thread>
#include <mutex>
#include <memory>
#include <condition_variable>
#include <signal.h>
#define DLOG_THIS 0x00010000
#define DLOG_RECORD 0x00020000
#define DLOG_FRAME 0x00040000
#define DLOG_TRACE 0x00080000
#undef DLOG_FLAGS
#define DLOG_FLAGS (DLOG_FLAGS_DEFAULT|DLOG_RECORD|DLOG_TRACE|DLOG_THIS/*|DLOG_FRAME*/)
#undef DLOG_TAG
#define DLOG_TAG "RECORDER"
#define OFFS_QUEUE_SIZE_KBYTES (20*1024)
#define OFFS_CACHE_SIZE_KBYTES (1024)
#define OFFS_WRITE_ALIGNMENT_BYTES 8192
#define MEDIA_CACHE_SIZE_KBYTES (40*1024)
#define USE_OFFS 0
using namespace oasis;
static bool timer_running = true;
static bool continue_recording = true;
static void bytesToString(size_t size, char *size_string)
{
if(size >= 1024*1024) {
sprintf(size_string, "%.2fMB", (double)size/1048576);
} else if(size >= 1024) {
sprintf(size_string, "%.2fKB", (double)size/1024);
} else {
sprintf(size_string, "%dB", size);
}
}
static uint8_t strd_data_header_[24] = {0, };
class MyRecordingObserver : public MediaObserver {
public:
virtual void onStarted(void* user_data, const char* file_path) {
DLOG(DLOG_RECORD|DLOG_INFO, "Recording started, file path \"%s\"\r\n", file_path);
}
virtual void onStopped(void* user_data, media_report_reason_t reason, void* details) {
DLOG(DLOG_RECORD|DLOG_INFO, "Recording stopped, reason: %d(%s)\r\n", reason, mediaReportReasonString(reason));
if(reason != kMediaReportNoError) {
continue_recording = false;
}
}
virtual void onFileChanged(void* user_data, const char* new_file_path) {
DLOG(DLOG_RECORD|DLOG_INFO, "New file used: \"%s\"\r\n", new_file_path);
}
virtual void onError(void* user_data, media_report_reason_t reason, void* details) {
if(reason == kRecordingErrorMediaDataTimeout) {
int32_t camera_id = (intptr_t)details;
DLOG(DLOG_RECORD|DLOG_ERROR, "Recording error, reason: %d(%s), camera<%d>\r\n", reason, mediaReportReasonString(reason), camera_id);
} else if(reason == kRecordingErrorMediaCacheReadErrorBegin) {
DLOG(DLOG_RECORD|DLOG_ERROR, "Recording error, reason: %d(%s) ===> stopping\r\n", reason, mediaReportReasonString(reason));
} else if(reason == kRecordingErrorMediaCacheReadErrorEnd) {
DLOG(DLOG_RECORD|DLOG_ERROR, "Recording error, reason: %d(%s)\r\n", reason, mediaReportReasonString(reason));
}
continue_recording = false;
}
virtual void onInfo(void* user_data, MediaInfo* info) {
DLOG(DLOG_RECORD, "Recording duration %lld.%06lld sec\r\n", info->durationUs / 1000000ll, info->durationUs % 1000000ll);
}
virtual void onInfoEx(void* user_data, MediaInfoEx* info) {
int h, m, s, u;
char normal_qsize[128], event_qsize[128], in_size[128], out_size[128], cache_in_size[128];
char video1_size[128], video2_size[128], meta_size[128], audio_size[128], file_size[128];
parseUsec(info->durationUs, h, m, s, u);
DLOG0(DLOG_RECORD, "Recording duration \033[33m%02d:%02d:%02d.%06d\033[0m, wq#%d(%d), eq#%d(%d), meq#%d(%d), nohits#%u, mb.avail#%d/%d (free mem %.3fMB, cpu %.2f%%)\r\n", h, m, s, u, info->writerStat.curSamples, info->writerStat.maxSamples, info->timeshiftStat.curSamples, info->timeshiftStat.maxSamples, info->writerStat.motionStat.curSamples, info->writerStat.motionStat.maxSamples, info->mediaCacheStat.nohits_, info->mediaCacheStat.free_buffer_count_, info->mediaCacheStat.total_buffer_count_, (double)oasis::getFreeMemorySize()/1024.0/1024.0, getCPUUsage());
//offs state
bytesToString(info->offsStat.qNormalSize, normal_qsize);
bytesToString(info->offsStat.qEventSize, event_qsize);
bytesToString(info->offsStat.inSize, in_size);
bytesToString(info->offsStat.outSize, out_size);
bytesToString(info->mediaCacheStat.in_size_, cache_in_size);
TRACE0(" cache: during %d msec, in %s\r\n", info->mediaCacheStat.check_duration_, cache_in_size);
TRACE0(" offs: n#%d, e#%d, nz#%s, ez#%s, during %d msec: in %s out %s elapsed %d msec \r\n", info->offsStat.qNormalCount, info->offsStat.qEventCount, normal_qsize, event_qsize, info->offsStat.checkDuration, in_size, out_size, info->offsStat.elapsedSum);
//TRACE0(" cache: hits#%u, nohits#%u, free#%u, gets#%u, puts#%u\r\n", info->mediaCacheStat.hits_, info->mediaCacheStat.nohits_, info->mediaCacheStat.free_buffer_count_, info->mediaCacheStat.get_buffer_count_, info->mediaCacheStat.put_buffer_count_);
//print recording stat in details
bytesToString(info->writerStat.video1Length, video1_size);
bytesToString(info->writerStat.video2Length, video2_size);
bytesToString(info->writerStat.metaLength, meta_size);
bytesToString(info->writerStat.audioLength, audio_size);
bytesToString(info->writerStat.fileLength, file_size);
TRACE0(" %s: video1 %s, video2 %s, meta %s, audio %s, file %s\n", info->sniffing?"sniffing":"recording", video1_size, video2_size, meta_size, audio_size, file_size);
if(info->writerStat.motionStat.recording) {
bytesToString(info->writerStat.motionStat.video1Length, video1_size);
bytesToString(info->writerStat.motionStat.video2Length, video2_size);
bytesToString(info->writerStat.motionStat.metaLength, meta_size);
bytesToString(info->writerStat.motionStat.audioLength, audio_size);
bytesToString(info->writerStat.motionStat.fileLength, file_size);
TRACE0(" motion: video1 %s, video2 %s, meta %s, audio %s, file %s\n", video1_size, video2_size, meta_size, audio_size, file_size);
}
if(info->timeshiftStat.recording) {
bytesToString(info->timeshiftStat.video1Length, video1_size);
bytesToString(info->timeshiftStat.video2Length, video2_size);
bytesToString(info->timeshiftStat.metaLength, meta_size);
bytesToString(info->timeshiftStat.audioLength, audio_size);
bytesToString(info->timeshiftStat.fileLength, file_size);
TRACE0(" event: video1 %s, video2 %s, meta %s, audio %s, file %s\n", video1_size, video2_size, meta_size, audio_size, file_size);
}
}
//player paused and resumed
virtual void onPaused(void *user_data, MediaInfo *info) {}
virtual void onResumed(void *user_data, MediaInfo *info) {}
virtual void onEventRecordingStarted(void* user_data, media_report_reason_t reason, const char* file_path) {}
virtual void onEventRecordingCompleted(void* user_data, media_report_reason_t reason, const char* file_path) {}
virtual void onMotionRecordingStarted(void *user_data, media_report_reason_t reason, const char *file_path) {}
virtual void onMotionRecordingCompleted(void *user_data, media_report_reason_t reason, const char *file_path) {}
virtual void queryNewFilePath(void* user_data, oasis::recording_mode_t file_type, bool rear_camera_on, bool sound_on, std::string& file_path) {
// use as it is
}
virtual void onSnapshotCompleted(void *user_data, uint32_t snapshot_id, int32_t camera_id, int error, const std::vector<char> &image_data, const struct timeval ×tamp) {}
};
void print_usage(const char *pname)
{
DLOG0(DLOG_INFO, "USAGE: %s\n", pname);
}
void cancel_handler(int signum)
{
timer_running = false;
continue_recording = false;
}
#if 0
#define SND_PATH "default"
#else
#define SND_PATH "hw:0,0"
#endif
int main(int argc, char* argv[])
{
int32_t err;
std::thread t2;
oasis::key_value_map_t parameters;
srand(time(NULL));
signal(SIGINT, cancel_handler);
////////////////////////////////////////////////////////////////////////////////////////////
// init
bool offs_disabled = false;
if(!offs_disabled) {
parameters["offs-qsize-max"] = std::to_string(OFFS_QUEUE_SIZE_KBYTES);
parameters["offs-overwrite-if-exist"] = "1";
parameters["offs-cache-size"] = std::to_string(OFFS_CACHE_SIZE_KBYTES);
} else {
parameters["offs-disable"] = "1";
}
parameters["media-cache-size"] = std::to_string(MEDIA_CACHE_SIZE_KBYTES);
//parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
enableLogLocalTime(true);
if(oasis::initialize(parameters) < 0) {
DLOG(DLOG_ERROR, "Oasis init failed\n");
return -1;
}
//non-offs, use system fs
#if !USE_OFFS
fs::offsConfigLocalFormatInfo("/tmp/DRIVING", 20*1024*1024);
fs::offsConfigLocalFormatInfo("/tmp/EVENT", 20*1024*1024);
#endif
////////////////////////////////////////////////////////////////////////////////////////////
// audio
parameters.clear();
parameters["types"]="source,sink";
parameters["path"]=SND_PATH;
parameters["always-on"]="0";
parameters["channels"]="2";
parameters["aec-disabled"]="1";
parameters["snd-input-channels"]="2";
parameters["snd-input-sample-size"]="16";
parameters["snd-input-sampling-duration-msec"]="40";
parameters["snd-input-sampling-rate"]="48000";
//parameters["snd-input-sampling-rate"]="22050";
createAudioDevice(parameters);
////////////////////////////////////////////////////////////////////////////////////////////
// sources
parameters.clear();
parameters["source-count"] = "3";
parameters["source1-camera-id"] = "0";
parameters["source1-isp-id"] = "0";
parameters["source1-isp-wdr-mode"] = "0";
parameters["source1-capture-format"] = "YUV420";
parameters["source1-capture-buffers"] = "5";
parameters["source1-fps"] = "30";
parameters["source1-subchannel-rotation"] = "0";
parameters["source1-loc"] = "front";
parameters["source1-capture-resolution"] = "2160p";
parameters["source1-sensor-config"] = "./Resource_678/VIC/2/imx678_3840x2160_ch2.cfg";
parameters["source1-autoscene-config"] = "./Resource_678/AutoScene/autoscene_conf.cfg";
parameters["source1-resource-dir"] = "./Resource_678/";
parameters["source2-camera-id"] = "1";
parameters["source2-isp-id"] = "0";
parameters["source2-isp-wdr-mode"] = "0";
parameters["source2-capture-format"] = "YUV420";
parameters["source2-capture-buffers"] = "5";
parameters["source2-fps"] = "30";
parameters["source2-subchannel-rotation"] = "0";
parameters["source2-loc"] = "right";
parameters["source2-capture-resolution"] = "1080p";
parameters["source2-sensor-config"] = "./Resource_tp2863/VIC/0/tp2863_1920x1080_ch0.cfg";
parameters["source2-autoscene-config"] = "./Resource_tp2863/AutoScene/autoscene_conf.cfg";
parameters["source2-resource-dir"] = "./Resource_tp2863/";
parameters["source3-camera-id"] = "2";
parameters["source3-isp-id"] = "0";
parameters["source3-isp-wdr-mode"] = "0";
parameters["source3-capture-format"] = "YUV420";
parameters["source3-capture-buffers"] = "5";
parameters["source3-fps"] = "30";
parameters["source3-subchannel-rotation"] = "0";
parameters["source3-loc"] = "left";
parameters["source3-capture-resolution"] = "1080p";
parameters["source3-sensor-config"] = "./Resource_tp2863/VIC/1/tp2863_1920x1080_ch1.cfg";
parameters["source3-autoscene-config"] = "./Resource_tp2863/AutoScene/autoscene_conf.cfg";
parameters["source3-resource-dir"] = "./Resource_tp2863/";
configCameras(parameters);
////////////////////////////////////////////////////////////////////////////////////////////
//recorer settings
parameters.clear();
parameters["file-prefix"] = "oasis-";
parameters["file-extension"] = "mp4";
parameters["file-duration-secs"] = "60";
#if USE_OFFS
parameters["normal-folder-path"] = "/mnt/sd/DRIVING";
parameters["event-folder-path"] = "/mnt/sd/EVENT";
parameters["motion-folder-path"] = "/mnt/sd/EVENT";
#else
parameters["normal-folder-path"] = "/tmp/DRIVING";
parameters["event-folder-path"] = "/tmp/EVENT";
parameters["motion-folder-path"] = "/tmp/EVENT";
#endif
parameters["event-pre-recording-seconds"] = "10";
parameters["event-post-recording-seconds"] = "10";
parameters["motion-pre-recording-seconds"] = "10";
parameters["motion-post-recording-seconds"] = "30";
parameters["disable-event-recording"] = "0";
parameters["disable-offs-recording"] = "0";
#if USE_OFFS
parameters["max-files"] = "0";
#else
parameters["max-files"] = "5";
#endif
parameters["delete-oldest-file-on-max-files"] = "1";
parameters["recording-size-limit-threshold-seconds"] = "1";
parameters["report-media-info-ex"] = "1";
parameters["avi-strd-size-max"] = "65536";
parameters["mp4-udta-size-max"] = "65536";
parameters["enable-persistent-cache"] = "0";
parameters["recording-file-header-write-interval-secs"] = "1";
parameters["snd-path"] = "hw:0,0";
parameters["snd-input-channels"] = "2";
parameters["snd-input-sample-size"] = "16";
parameters["snd-input-sampling-duration-msec"] = "120";
parameters["snd-input-sampling-rate"] = "44100";
//parameters["snd-input-sampling-rate"] = "48000";
parameters["aencoder-type"] = "aac"; //aac, raw, mp3
parameters["aencoder-bitrate"] = "128000";
parameters["osd-font-size"] = "0"; // disable (default)
parameters["osd-font-face"] = "Consolas";
parameters["osd-text-color"] = "255,255,255";
parameters["osd-use-text-color"] = "1";
parameters["osd-use-bg-color"] = "0";
parameters["osd-bg-color"] = "0,0,0";
parameters["osd-use-outline-color"] = "1";
parameters["osd-outline-color"] = "255,255,255";
parameters["osd-horz-align"] = "left";
parameters["osd-vert-align"] = "bottom";
parameters["osd-font-path"] = "/mnt/sd/consola.ttf";
parameters["osd-use-fixed-size"] = "0";
parameters["channel-count"] = "3";
parameters["channel1-camera-id"] = "0";
parameters["channel1-ise-id"] = "-1";
parameters["channel1-resolution"] = "2160p";
parameters["channel1-bitrate"] = "8000000";
parameters["channel1-fps"] = "30";
parameters["channel1-file-framerate"] = "30";
parameters["channel1-vencoder-type"] = "h264";
parameters["channel1-venc-framerate"] = "30";
parameters["channel1-venc-keyframe-interval"] = "30";
parameters["channel1-h264-profile"] = "high";
parameters["channel1-h264-level"] = "level51";
parameters["channel1-h264-enable-cabac"] = "1";
parameters["channel1-h264-min-qp"] = "10";
parameters["channel1-h264-max-qp"] = "31";
parameters["channel1-h264-enable-fixqp"] = "0";
parameters["channel1-h264-fix-iqp"] = "10";
parameters["channel1-h264-fix-pqp"] = "20";
parameters["channel1-media-wait-timeout-secs"] = "3";
parameters["channel1-media-wait-timeout-notify-oneshot"] = "1";
parameters["channel1-osd-font-size"] = "12";
parameters["channel2-camera-id"] = "1";
parameters["channel2-ise-id"] = "-1";
parameters["channel2-resolution"] = "1080p";
parameters["channel2-bitrate"] = "8000000";
parameters["channel2-fps"] = "30";
parameters["channel2-file-framerate"] = "30";
parameters["channel2-vencoder-type"] = "h264";
parameters["channel2-venc-framerate"] = "30";
parameters["channel2-venc-keyframe-interval"] = "30";
parameters["channel2-h264-profile"] = "high";
parameters["channel2-h264-level"] = "level51";
parameters["channel2-h264-enable-cabac"] = "1";
parameters["channel2-h264-min-qp"] = "10";
parameters["channel2-h264-max-qp"] = "31";
parameters["channel2-h264-enable-fixqp"] = "0";
parameters["channel2-h264-fix-iqp"] = "10";
parameters["channel2-h264-fix-pqp"] = "20";
parameters["channel2-media-wait-timeout-secs"] = "3";
parameters["channel2-media-wait-timeout-notify-oneshot"] = "1";
parameters["channel2-osd-font-size"] = "12";
parameters["channel3-camera-id"] = "2";
parameters["channel3-ise-id"] = "-1";
parameters["channel3-resolution"] = "1080p";
parameters["channel3-bitrate"] = "8000000";
parameters["channel3-fps"] = "30";
parameters["channel3-file-framerate"] = "30";
parameters["channel3-vencoder-type"] = "h264";
parameters["channel3-venc-framerate"] = "30";
parameters["channel3-venc-keyframe-interval"] = "30";
parameters["channel3-h264-profile"] = "high";
parameters["channel3-h264-level"] = "level51";
parameters["channel3-h264-enable-cabac"] = "1";
parameters["channel3-h264-min-qp"] = "10";
parameters["channel3-h264-max-qp"] = "31";
parameters["channel3-h264-enable-fixqp"] = "0";
parameters["channel3-h264-fix-iqp"] = "10";
parameters["channel3-h264-fix-pqp"] = "20";
parameters["channel3-media-wait-timeout-secs"] = "3";
parameters["channel3-media-wait-timeout-notify-oneshot"] = "1";
parameters["channel3-osd-font-size"] = "12";
std::shared_ptr<MyRecordingObserver> recording_observer = std::make_shared<MyRecordingObserver>();
//dumpParameters("recorder", recorder_parameters);
RecorderRef recorder = createRecorder(parameters);
if(recorder == nullptr) {
DLOG(DLOG_ERROR, "recorder creation failed!\r\n");
goto done;
}
memset(&strd_data_header_, 0, sizeof(strd_data_header_));
////////////////////////////////////////////////////////////////////////////////////////////
// timer thread
t2 = std::thread([&]() {
time_t t;
struct tm tm_t;
uint64_t usec, preview_usec;
std::string title;
uint32_t count = 0;
char stream_data[512] = { 0, };
preview_usec = systemTime();
do {
usec = systemTime();
time(&t);
localtime_r(&t, &tm_t);
if (isRecorderRecording(recorder)) {
sprintf(stream_data, "Main %4d/%02d/%02d %02d:%02d:%02d", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
addRecordingVideoStreamData(recorder, (void*)stream_data, strlen(stream_data) + 1);
}
if(/*count == 0 &&*/ isRecorderRecording(recorder)) {
title = oasis::format("OASIS %4d/%02d/%02d %02d:%02d:%02d\n", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
//title += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.";
setRecordingText(recorder, kTextTrackOsd, title, usec);
count++;
}
//30 msec
usleep(40000);
} while (timer_running);
});
err = startRecording(recorder, recording_observer, nullptr, false, &strd_data_header_, sizeof(strd_data_header_));
if(err < 0) {
DLOG(DLOG_ERROR, "start recording failed\r\n");
goto done;
}
printf("Ctrl+C to exit...\n");
do {
usleep(100000);
} while (continue_recording == true);
done:
timer_running = false;
if (t2.joinable()) {
t2.join();
}
if(recorder) {
destroyRecorder(recorder);
}
oasis::finalize();
printf("goodbye.\n");
return 0;
}