개별 녹화 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 onFileDeleted(int32_t camera_id, void *user_data, const char *deleted_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) 목록를 정의하고 있습니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
file-duration-secs 만큼 경과하거나 할당된 파일 크기를 초과할 경우, 자동으로 새 파일로 녹화를 재시작합니다. 새 파일 경로는 MediaObserver2::queryNewFilePaths 호출 될 때 변경할 수 있습니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
stopMultiChannelRecording을 호출하여 녹화를 중지하거나 오류 문제를 해결한 후 녹화를 재시작할 수 있습니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
를 "0" 로 설정하고createMultiChannelRecorder`로 Recoder 객체를 생성할 경우, 녹화 시작 후 1초 단위로 호출됩니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
report-media-info-ex를 "1" 로 설정하고 createMultiChannelRecorder로 Recoder 객체를 생성할 경우, 녹화 시작 후 1초 단위로 호출됩니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
kRecordingModeNormal(0) 이나 kRecordingModeSniffing(1) 값 중 하나입니다. 녹화를 sniffing 모드로 시작하였을 경우, kRecordingModeSniffing 값을 갖습니다.
true 입니다.
<녹화 디렉토리>/REC_<채널 번호>_<날짜 시간>.<확장자> 로 자동 생성됩니다. 응용 프로그램은 각 녹화 파일의 절대 경로를 필요에 따라 변경할 수 있습니다.
아래는 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
takeMultiChannelRecordingShapshot 호출 후 스냅샷 생성이 종료되거나 오류가 발생할 경우, 호출됩니다.
startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
takeMultiChannelRecordingShapshot 호출 시 전달받은 snapshot_id 값입니다.
kMediaReportNoError(0) 값을 갖습니다.
error가 0인 경우에만 유효합니다. 사용자는 별도 쓰레드에서 이 데이터를 파일에 저장할 수 있습니다.
함수#
nullptr을 리턴합니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
Note
changeMultiChannelRecorderParameters 호출 전에 MultiChannelRecorder 정지된 상태이어야 합니다. 활성 상태이면 stopMultiChannelRecording을 호출하여 정지 상태로 만듭니다.
true를 리턴합니다.
- true: MultiChannelRecorder가 동작 상태입니다.
- false: MultiChannelRecorder 동작 상태입니다.
- true: MultiChannelRecorder가 녹화 데이터를 파일에 쓰는 상태입니다.
- false: MultiChannelRecorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
- true: MultiChannelRecorder가 지정한 채널의 녹화 데이터를 파일에 쓰는 상태입니다.
- false: MultiChannelRecorder가 지정한 채널의 녹화 데이터를 파일에 쓰지 않는 상태입니다.
true로 설정합니다. Sniffing 모드에서는 모션 감지나 이벤트 감지 등 외부 요인으로 녹화를 시작할 수 있고, 일반 녹화는 시작하지 않습니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
event-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 event-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onEventRecordingStarted 와 MediaObserver2::onEventRecordingCompleted로 이벤트 녹화 시작과 종료 여부를 알 수 있습니다. 이벤트 녹화는 현재 녹화와 동시에 진행됩니다.
true이면 모션 녹화를 진행합니다. false이면 이벤트 녹화를 진행합니다. 녹화 파일이 저장될 위치가 MultiChannelRecorder 생성시 지정한 이벤트 경로나 모션 녹화 경로로 설정됩니다.
- 0: 성공
- -1: 실패
event-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 event-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onEventRecordingStarted 와 MediaObserver2::onEventRecordingCompleted로 이벤트 녹화 시작과 종료 여부를 알 수 있습니다.
true이면 모션 녹화를 진행합니다. false이면 이벤트 녹화를 진행합니다. 녹화 파일이 저장될 위치가 MultiChannelRecorder 생성시 지정한 이벤트 경로나 모션 녹화 경로로 설정됩니다.
true인 경우, 일반 녹화를 중지하고 이벤트 녹화를 시작합니다. 이벤트 녹화가 끝나면 일반 녹화를 다시 시작합니다.
- 0: 성공
- -1: 실패
true이면 모션 녹화를 중지합니다. false이면 이벤트 녹화를 중지합니다.
- 0: 성공
- -1: 실패
event-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 event-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onEventRecordingStarted 와 MediaObserver2::onEventRecordingCompleted로 이벤트 녹화 시작과 종료 여부를 알 수 있습니다.
true인 경우, 일반 녹화를 중지하고 이벤트 녹화를 시작합니다. 이벤트 녹화가 끝나면 일반 녹화를 다시 시작합니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
true인 경우, 일반 녹화를 중지하고 이벤트 녹화를 시작합니다. 이벤트 녹화가 끝나면 일반 녹화를 다시 시작합니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 이벤트 녹화가 시작 되었습니다.
- false: 이벤트 녹화가 시작 되지 않았습니다.
- true: 이벤트 녹화 파일 쓰기가 진행 중입니다.
- false: 이벤트 녹화 파일 쓰기가 진행 중이지 않습니다.
motion-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 motion-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onMotionRecordingStarted 와 MediaObserver2::onMotionRecordingCompleted로 모션 녹화 시작과 종료 여부를 알 수 있습니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
true이고 현재 이벤트 녹화가 진행 중이면, 이벤트 녹화를 중지합니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 모션 녹화 시작 후 파일 쓰기 중입니다.
- false: 모션 녹화 시작 후 파일 쓰기 중이지 않습니다.
- true: 이벤트 또는 모션 녹화 파일 쓰기 중입니다.
- false: 이벤트 또는 모션 녹화 파일 쓰기 중이 아닙니다.
- true: 이벤트 녹화가 시작된 후 파일 쓰기 중입니다.
- false: 이벤트 녹화가 시작된 후 파일 쓰기 중이 아닙니다.
- true: 모션 녹화가 시작된 후 파일 쓰기 중입니다.
- false: 모션 녹화가 시작된 후 파일 쓰기 중이 아닙니다.
- true: 이벤트 녹화가 시작 되었습니다.
- false: 이벤트 녹화가 시작 되지 않았습니다.
kTextTrackOsd 만 허용됩니다.
- 0: 성공
- -1: 실패
-1인 경우, 전 영상 채널에 적용됩니다.
kTextTrackOsd 만 허용됩니다.
- 0: 성공
- -1: 실패
-1인 경우, 전 영상 채널에 적용됩니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
-1인 경우, 전 영상 채널에 적용됩니다.
true이면 활성화되고, false이면 묵음 데이터가 저장됩니다.
- 0: 성공
- -1: 실패
- true: 활성화 중
- false: 중단 중
- 0: 성공
- -1: 실패
MediaObserver2::onSnapshotCompleted 콜백함수에 JPEG 이미지 데이터를 전달합니다.
MediaObserver2::onSnapshotCompleted 콜백함수에서 각 snapshot을 구분하는 snapshot_id 매개변수로 사용됩니다.
takeRecordingSnapshot를 참고합니다.
- 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["denoise-enabled"]="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;
}