개별 녹화 API¶
oasis::startMultiChannelRecording 호출 시 전달하는 oasis::MediaObserver2 객체를 통하여, 녹화와 스냅샷 이벤트를 처리합니다.
헤더 파일¶
OasisMedia.h
MediaObserver2 인터페이스¶
개별 채널 녹화 컴포넌트는 MediaObserver2에서 유도된 사용자 정의 Observer 객체를 통하여 이벤트 등을 처리합니다.
oasis::MediaObserver2는 OasisMedia.h에 아래와 같이 정의되어 있습니다.
class MediaObserver2 : public std::enable_shared_from_this<MediaObserver2>
{
public:
MediaObserver2();
virtual ~MediaObserver2();
//recording started and stopped
virtual void onStarted(int32_t channel_id, void *user_data, const char *file_path);
virtual void onStopped(int32_t channel_id, void *user_data, media_report_reason_t reason, void *details);
//recording a new file
virtual void onFileChanged(int32_t channel_id, void *user_data, const char *new_file_path);
virtual void onError(int32_t channel_id, void *user_data, media_report_reason_t reason, void *details);
//periodic report every 1 second.
virtual void onInfo(int32_t channel_id, void *user_data, MediaInfo *info);
virtual void onInfoEx(int32_t channel_id, void *user_data, MediaInfoEx *info);
//event recording started and completed(aborted)
virtual void onEventRecordingStarted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path);
virtual void onEventRecordingCompleted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path);
//motion recording started and completed(aborted)
virtual void onMotionRecordingStarted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path);
virtual void onMotionRecordingCompleted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path);
virtual void queryNewFilePaths(void *user_data, recording_mode_t file_type, bool sound_on, std::list<MultiChannelRecorderFilePath> &file_paths);
virtual void onSnapshotCompleted(int32_t channel_id, void *user_data, uint32_t snapshot_id, int error, const std::vector<char> &jpeg_image_data, const struct timeval ×tamp);
};
oasis::MediaObserver2와 oasis::MediaObserver 간 다른 점은 콜백 인수로 개별 채널 ID를 정의하고 있으며, queryNewFilePaths 콜백 함수 인수로 채널 정보와 파일 경로로 이루어진 구조체(MultiChannelRecorderFilePath) 목록를 정의하고 있습니다.
아래는 MultiChannelRecorderFilePath 구조체에 대한 설명입니다.
class MultiChannelRecorderFilePath
{
public:
MultiChannelRecorderFilePath() : channel_id_(-1), private_(nullptr) {}
MultiChannelRecorderFilePath(int32_t channel_id, const std::string file_path) : channel_id_(channel_id), file_path_(file_path), private_(nullptr) {}
MultiChannelRecorderFilePath(const MultiChannelRecorderFilePath &src) {
channel_id_ = src.channel_id_;
file_path_ = src.file_path_;
private_ = src.private_;
}
MultiChannelRecorderFilePath &operator=(const MultiChannelRecorderFilePath &src) {
if(this != &src) {
channel_id_ = src.channel_id_;
file_path_ = src.file_path_;
private_ = src.private_;
}
return *this;
}
public:
int32_t channel_id_;
std::string file_path_;
void *private_; //DO NOT MODIFIY private_ on callback!!!
};
녹화 파일 목록 예입니다.
Recording<1> path /tmp/DRIVING/REC_channel1_2000_06_26_01_04_23.mp4
Recording<2> path /tmp/DRIVING/REC_channel2_2000_06_26_01_04_23.mp4
Recording<3> path /tmp/DRIVING/REC_channel3_2000_06_26_01_04_23.mp4
함수¶
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
Note
changeMultiChannelRecorderParameters 호출 전에 MultiChannelRecorder 정지된 상태이어야 합니다. 활성 상태이면 stopMultiChannelRecording을 호출하여 정지 상태로 만듭니다.
- true: MultiChannelRecorder가 동작 상태입니다.
- false: MultiChannelRecorder 동작 상태입니다.
- true: MultiChannelRecorder가 녹화 데이터를 파일에 쓰는 상태입니다.
- false: MultiChannelRecorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- true: MultiChannelRecorder가 지정한 채널의 녹화 데이터를 파일에 쓰는 상태입니다.
- false: MultiChannelRecorder가 지정한 채널의 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 이벤트 녹화가 시작 되었습니다.
- false: 이벤트 녹화가 시작 되지 않았습니다.
- true: 이벤트 녹화 파일 쓰기가 진행 중입니다.
- false: 이벤트 녹화 파일 쓰기가 진행 중이지 않습니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 모션 녹화 시작 후 파일 쓰기 중입니다.
- false: 모션 녹화 시작 후 파일 쓰기 중이지 않습니다.
- true: 이벤트 또는 모션 녹화 파일 쓰기 중입니다.
- false: 이벤트 또는 모션 녹화 파일 쓰기 중이 아닙니다.
- true: 이벤트 녹화가 시작된 후 파일 쓰기 중입니다.
- false: 이벤트 녹화가 시작된 후 파일 쓰기 중이 아닙니다.
- true: 모션 녹화가 시작된 후 파일 쓰기 중입니다.
- false: 모션 녹화가 시작된 후 파일 쓰기 중이 아닙니다.
- true: 이벤트 녹화가 시작 되었습니다.
- false: 이벤트 녹화가 시작 되지 않았습니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 활성화 중
- false: 중단 중
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
예제¶
아래는 3개 영상 채널과 소리를 채널 별 개별 파일로 녹화하는 예입니다.
USE_OFFS가 1이면 Oasis 파일시스템을 사용하고, 0이면 /tmp 폴더 밑에 DRIVING, EVENT 폴더를 생성하여 사용합니다.
각 폴더내 파일 크기는 20MB로 제한하고 총 15개만 녹화됩니다. Oasis 파일 시스템의 경우, 녹화 파일개수 제한은 없으며, 용량이 다 찬 경우 자동으로 오래된 파일을 덮어씁니다.
#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 oasis::MediaObserver2
{
public:
MyRecordingObserver() {}
virtual ~MyRecordingObserver() {}
//recording started and stopped
virtual void onStarted(int32_t channel_id, void *user_data, const char *file_path) {
DLOG(DLOG_THIS, "Recording<%d> started, file path \"%s\"\r\n", channel_id, file_path);
}
virtual void onStopped(int32_t channel_id, void *user_data, media_report_reason_t reason, void *details) {
DLOG(DLOG_THIS, "Recording%d> stopped, reason: %d(%s)\r\n", channel_id, reason, mediaReportReasonString(reason));
if(reason != kMediaReportNoError) {
continue_recording = false;
}
}
//recording a new file
virtual void onFileChanged(int32_t channel_id, void *user_data, const char *new_file_path) {
DLOG(DLOG_THIS, "Recording<%d> new file used: \"%s\"\r\n", channel_id, new_file_path);
}
virtual void onError(int32_t channel_id, void *user_data, media_report_reason_t reason, void *details) {
if(reason == kRecordingErrorMediaDataTimeout) {
int32_t camera_id = (intptr_t)details;
DLOG(DLOG_ERROR, "Recording<%d> error, reason: %d(%s), camera<%d>\r\n", channel_id, reason, mediaReportReasonString(reason), camera_id);
} else if(reason == kRecordingErrorMediaCacheReadErrorBegin) {
DLOG(DLOG_ERROR, "Recording<%d> error, reason: %d(%s) ===> stopping!!!\r\n", channel_id, reason, mediaReportReasonString(reason));
} else if(reason == kRecordingErrorMediaCacheReadErrorEnd) {
DLOG(DLOG_ERROR, "Recording<%d> error, reason: %d(%s)\r\n", channel_id, reason, mediaReportReasonString(reason));
}
continue_recording = false;
}
//periodic report every 1 second.
virtual void onInfo(int32_t channel_id, void *user_data, MediaInfo *info) {
DLOG(DLOG_THIS, "Recording<%d> duration %lld.%06lld sec\r\n", channel_id, info->durationUs / 1000000ll, info->durationUs % 1000000ll);
}
virtual void onInfoEx(int32_t channel_id, void *user_data, MediaInfoEx *info) {
#if 0
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);
TRACE0("Recording<%d> 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", channel_id, 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);
}
#endif
}
//event recording started and completed(aborted)
virtual void onEventRecordingStarted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path) {
DLOG(DLOG_THIS, "Event<%d> Recording started @ \"%s\" (reason: %d).\r\n", channel_id, file_path, reason);
}
virtual void onEventRecordingCompleted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path) {
DLOG(DLOG_THIS, "Event<%d> Recording completed @ \"%s\" (reason: %d).\r\n", channel_id, file_path, reason);
}
//motion recording started and completed(aborted)
virtual void onMotionRecordingStarted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path) {
}
virtual void onMotionRecordingCompleted(int32_t channel_id, void *user_data, media_report_reason_t reason, const char *file_path) {
}
virtual void queryNewFilePaths(void *user_data, recording_mode_t file_type, bool sound_on, std::list<MultiChannelRecorderFilePath> &file_paths) {
DLOG(DLOG_THIS, "queryNewFilePath: mode: %d, sound on %d:\n", file_type, sound_on);
for(auto it = file_paths.begin(); it != file_paths.end(); it++) {
DLOG(DLOG_THIS, " Recording<%d> path %s\n", (*it).channel_id_, (*it).file_path_.c_str());
}
}
virtual void onSnapshotCompleted(int32_t channel_id, void *user_data, uint32_t snapshot_id, int error, const std::vector<char> &jpeg_image_data, const struct timeval ×tamp) {
DLOG(DLOG_THIS, "Snapshot<%d> completed: id %d, error %d, size %zd bytes @ %d sec\n", channel_id, snapshot_id, error, jpeg_image_data.size(), timestamp.tv_sec);
}
};
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"] = "15";
#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/flash/leipzig/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 = createMultiChannelRecorder(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 (isMultiChannelRecorderRecording(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);
addMultiChannelRecordingVideoStreamData(recorder, (void*)stream_data, strlen(stream_data) + 1);
}
if(/*count == 0 &&*/ isMultiChannelRecorderRecording(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);
setMultiChannelRecordingText(recorder, kTextTrackOsd, title, usec);
count++;
}
//30 msec
usleep(40000);
} while (timer_running);
});
err = startMultiChannelRecording(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) {
destroyMultiChannelRecorder(recorder);
}
oasis::finalize();
printf("goodbye.\n");
return 0;
}