Individual Recording API#
Processes recording and snapshot events through the oasis::MediaObserver2 object passed when calling oasis::startMultiChannelRecording.
Header File#
OasisMedia.h
MediaObserver2 Interface#
The individual channel recording component processes events and the like through a user-defined Observer object derived from MediaObserver2.
oasis::MediaObserver2 is defined in OasisMedia.h as follows:
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);
};
The differences between oasis::MediaObserver2 and oasis::MediaObserver are that the individual channel ID is defined as a callback argument, and a list of structures (MultiChannelRecorderFilePath) consisting of channel information and file paths is defined as an argument for the queryNewFilePaths callback function.
startMultiChannelRecording.
startMultiChannelRecording.
file-duration-secs elapses during recording or the allocated file size is exceeded, recording automatically restarts with a new file. The new file path can be changed when MediaObserver2::queryNewFilePaths is called.
startMultiChannelRecording.
startMultiChannelRecording.
stopMultiChannelRecording in a separate thread to stop the recording, or restart the recording after resolving the error.
startMultiChannelRecording.
report-media-info-ex is set to "0" and the Recorder object is created via createMultiChannelRecorder.
startMultiChannelRecording.
report-media-info-ex is set to "1" and the Recorder object is created via createMultiChannelRecorder.
startMultiChannelRecording.
startMultiChannelRecording.
startMultiChannelRecording.
startMultiChannelRecording.
startMultiChannelRecording.
startMultiChannelRecording.
kRecordingModeNormal(0) or kRecordingModeSniffing(1). It has the value kRecordingModeSniffing if recording started in sniffing mode.
true if sound is also being recorded.
<Recording Directory>/REC_<Channel Number>_<Date Time>.<Extension>. The application can change the absolute path of each recording file as needed.
Below is the description of the MultiChannelRecorderFilePath structure.
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!!!
};
An example of the recording file list.
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 of the recording component.
startMultiChannelRecording.
snapshot_id value received when calling takeMultiChannelRecordingShapshot.
kMediaReportNoError(0) if the snapshot succeeds.
error is 0. The user can save this data to a file in a separate thread.
Functions#
nullptr on failure.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
Note
The MultiChannelRecorder must be in a stopped state before calling changeMultiChannelRecorderParameters. If it is in an active state, call stopMultiChannelRecording to put it in a stopped state.
true after starting the MultiChannelRecorder object.
- true: MultiChannelRecorder is in a running state.
- false: MultiChannelRecorder is in a stopped state.
- true: MultiChannelRecorder is in a state of writing recording data to a file.
- false: MultiChannelRecorder is in a state of not writing recording data to a file.
- true: MultiChannelRecorder is writing the specified channel's recording data to a file.
- false: MultiChannelRecorder is not writing the specified channel's recording data to a file.
true to operate in Sniffing mode. In Sniffing mode, recording can be started by external factors such as motion detection or event detection, and normal recording does not start.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
event-pre-recording-seconds to the video after the occurrence by the time (seconds) specified in event-post-recording-seconds. The start and completion of event recording can be checked with MediaObserver2::onEventRecordingStarted and MediaObserver2::onEventRecordingCompleted. Event recording proceeds simultaneously with the current recording.
true. Proceeds with event recording if false. The location where the recording file will be saved is set to the event path or motion recording path specified when creating the MultiChannelRecorder.
- 0: Success
- -1: Failure
event-pre-recording-seconds to the video after the occurrence by the time (seconds) specified in event-post-recording-seconds. The start and completion of event recording can be checked with MediaObserver2::onEventRecordingStarted and MediaObserver2::onEventRecordingCompleted.
true. Proceeds with event recording if false. The location where the recording file will be saved is set to the event path or motion recording path specified when creating the MultiChannelRecorder.
true, it stops normal recording and starts event recording. Upon completion of event recording, normal recording resumes.
- 0: Success
- -1: Failure
true. Stops event recording if false.
- 0: Success
- -1: Failure
event-pre-recording-seconds to the video after the occurrence by the time (seconds) specified in event-post-recording-seconds. The start and completion of event recording can be checked with MediaObserver2::onEventRecordingStarted and MediaObserver2::onEventRecordingCompleted.
true, it stops normal recording and starts event recording. Upon completion of event recording, normal recording resumes.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
true, it stops normal recording and starts event recording. Upon completion of event recording, normal recording resumes.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
- true: Event recording has started.
- false: Event recording has not started.
- true: Event recording file writing is in progress.
- false: Event recording file writing is not in progress.
motion-pre-recording-seconds to the video after the occurrence by the time (seconds) specified in motion-post-recording-seconds. The start and completion of motion recording can be checked with MediaObserver2::onMotionRecordingStarted and MediaObserver2::onMotionRecordingCompleted.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
true and current event recording is in progress, it stops the event recording.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
- true: Writing to a file after motion recording has started.
- false: Not writing to a file after motion recording has started.
- true: Event or motion recording file writing is in progress.
- false: Event or motion recording file writing is not in progress.
- true: Writing to a file after event recording has started.
- false: Not writing to a file after event recording has started.
- true: Writing to a file after motion recording has started.
- false: Not writing to a file after motion recording has started.
- true: Event recording has started.
- false: Event recording has not started.
kTextTrackOsd is allowed.
- 0: Success
- -1: Failure
-1, it applies to all video channels.
kTextTrackOsd is allowed.
- 0: Success
- -1: Failure
-1, it applies to all video channels.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
-1, it applies to all video channels.
true, and silence data is stored if false.
- 0: Success
- -1: Failure
- true: Currently enabled
- false: Currently disabled
- 0: Success
- -1: Failure
MediaObserver2::onSnapshotCompleted callback function.
snapshot_id parameter to distinguish each snapshot in the MediaObserver2::onSnapshotCompleted callback function.
takeRecordingSnapshot, the combined recording API, for the parameters.
- 0: Success
- -1: Failure
Example#
Below is an example of recording 3 video channels and sound into individual files per channel.
If USE_OFFS is 1, the Oasis file system is used, and if 0, DRIVING and EVENT folders are created and used under the /tmp folder.
The file size within each folder is limited to 20MB, and only 15 files in total are recorded. In the case of the Oasis file system, there is no limit to the number of recording files, and when the capacity is full, it automatically overwrites the oldest files.
#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);
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";
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["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>();
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(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++;
}
//40 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;
}