개별 녹화 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 &timestamp);
};

oasis::MediaObserver2oasis::MediaObserver 간 다른 점은 콜백 인수로 개별 채널 ID를 정의하고 있으며, queryNewFilePaths 콜백 함수 인수로 채널 정보와 파일 경로로 이루어진 구조체(MultiChannelRecorderFilePath) 목록를 정의하고 있습니다.

void onStarted ( int32_t channel_id , void * user_data , const char * file_path )
OasisMedia.h
녹화가 시작된 후 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
file_path  녹화 파일 경로입니다.
void onStopped ( int32_t channel_id , void * user_data , media_report_reason_t reason , void * details )
OasisMedia.h
녹화가 중지된 후 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
reason  중지 이유를 나타내는 상수 값입니다.
details  중지 이유에 해당하는 상세 데이터입니다. 중지 이유 값에 따라 타입이 정의됩니다.
void onFileChanged ( int32_t channel_id , void * user_data , const char * new_file_path )
OasisMedia.h
새 파일로 녹화가 재시작될 때 호출됩니다. 녹화 중 file-duration-secs 만큼 경과하거나 할당된 파일 크기를 초과할 경우, 자동으로 새 파일로 녹화를 재시작합니다. 새 파일 경로는 MediaObserver2::queryNewFilePaths 호출 될 때 변경할 수 있습니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
new_file_path  새 녹화 파일 경로입니다.
void onError ( int32_t channel_id , void * user_data , media_report_reason_t reason , void * details )
OasisMedia.h
녹화 도중 오류가 발생하였을 때 호출됩니다. 사용자는 별도 쓰레드에서 stopMultiChannelRecording을 호출하여 녹화를 중지하거나 오류 문제를 해결한 후 녹화를 재시작할 수 있습니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
reason  중지 이유를 나타내는 상수 값입니다.
details  중지 이유에 해당하는 상세 데이터입니다. 중지 이유 값에 따라 타입이 정의됩니다.
void onInfo ( int32_t channel_id , void * user_data , MediaInfo * info )
OasisMedia.h
report-media-info-ex를 "0" 로 설정하고 createMultiChannelRecorder`로 Recoder 객체를 생성할 경우, 녹화 시작 후 1초 단위로 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
info  상태 정보입니다. 구조체에 대한 설명은 통합 녹화 API를 참고합니다.
void onInfoEx ( int32_t channel_id , void * user_data , MediaInfoEx * info )
OasisMedia.h
report-media-info-ex를 "1" 로 설정하고 createMultiChannelRecorder로 Recoder 객체를 생성할 경우, 녹화 시작 후 1초 단위로 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
info  상태 정보입니다. 구조체에 대한 설명은 통합 녹화 API를 참고합니다.
void onEventRecordingStarted ( int32_t channel_id , void * user_data , media_report_reason_t reason , const char * file_path )
OasisMedia.h
이벤트 녹화가 시작되었을 때 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
reason  녹화 시작시 발생한 이유를 나타내는 상수 값입니다.
file_path  이벤트 녹화 파일 절대 경로입니다.
void onEventRecordingCompleted ( int32_t channel_id , void * user_data , media_report_reason_t reason , const char * file_path )
OasisMedia.h
이벤트 녹화가 끝났을 때 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
reason  녹화 완료시 발생한 이유를 나타내는 상수 값입니다.
file_path  이벤트 녹화 파일 절대 경로입니다.
void onMotionRecordingStarted ( int32_t channel_id , void * user_data , media_report_reason_t reason , const char * file_path )
OasisMedia.h
모션 녹화가 시작되었을 때 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
reason  녹화 시작시 발생한 이유를 나타내는 상수 값입니다.
file_path  모션 녹화 파일 절대 경로입니다.
void onMotionRecordingCompleted ( int32_t channel_id , void * user_data , media_report_reason_t reason , const char * file_path )
OasisMedia.h
모션 녹화가 끝났을 때 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
reason  녹화 완료시 발생한 이유를 나타내는 상수 값입니다.
file_path  모션 녹화 파일 절대 경로입니다.
void queryNewFilePaths ( void * user_data , recording_mode_t file_type , bool sound_on , std::list<MultiChannelRecorderFilePath> & file_paths )
OasisMedia.h
새 녹화를 시작하기전 새 녹화파일 경로 확인을 위해 호출됩니다. 사용자는 필요할 경우 새 녹화파일명이나 경로를 변경하여 리턴합니다.
매개변수
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
file_type  kRecordingModeNormal(0) 이나 kRecordingModeSniffing(1) 값 중 하나입니다. 녹화를 sniffing 모드로 시작하였을 경우, kRecordingModeSniffing 값을 갖습니다.
sound_on  소리도 녹화 중인 경우, true 입니다.
file_paths  IN OUT 채널 ID와 녹화 파일의 절대 경로가 포함된 구조체 목록입니다. <녹화 디렉토리>/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!!!
};
struct MultiChannelRecorderFilePath
OasisMedia.h
타입
이름
설명
int32_t
channel_id_
채널 번호입니다.
std::string
file_path_
파일의 절대 경로입니다.
void *
private_
내부에서 사용하는 포인터로 변경하지 않아야 합니다.

녹화 파일 목록 예입니다.

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
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 & timestamp )
OasisMedia.h
녹화 컴포넌트의 takeMultiChannelRecordingShapshot 호출 후 스냅샷 생성이 종료되거나 오류가 발생할 경우, 호출됩니다.
매개변수
channel_id  1, 2, 3 등 채널 ID 번호입니다.
user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
snapshot_id  takeMultiChannelRecordingShapshot 호출 시 전달받은 snapshot_id 값입니다.
error  오류 값입니다. 스냅샷이 성공한 경우, kMediaReportNoError(0) 값을 갖습니다.
jpeg_image_data  스냅샷으로 생성된 JPEG 파일 데이터입니다. error가 0인 경우에만 유효합니다. 사용자는 별도 쓰레드에서 이 데이터를 파일에 저장할 수 있습니다.
timestamp  스냅샷 생성 시 timestamp 입니다.

함수

MultiChannelRecorderRef createMultiChannelRecorder ( key_value_map_t & parameters )
OasisMedia.h
MultiChannelRecorder 객체를 생성합니다.
매개변수
parameters  MultiChannelRecorder 객체 생성에 필요한 key-value map 입니다. 패러미터 절을 참고합니다.
리턴값
성공할 경우, MultiChannelRecorder 객체를 리턴하고, 실패하면 nullptr을 리턴합니다.
int32_t destroyMultiChannelRecorder ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
매개변수
multi_channel_recorder  해제할 MultiChannelRecorder 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t changeMultiChannelRecorderParameters ( MultiChannelRecorderRef multi_channel_recorder , key_value_map_t & parameters )
OasisMedia.h
MultiChannelRecorder 객체의 설정값을 변경합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
parameters  변경할 key-value map 입니다.
리턴값
  • 0: 성공
  • -1: 실패

Note

changeMultiChannelRecorderParameters 호출 전에 MultiChannelRecorder 정지된 상태이어야 합니다. 활성 상태이면 stopMultiChannelRecording을 호출하여 정지 상태로 만듭니다.

bool mutiChannelRecorderIsRunning ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
녹화가 동작 중인지 판단합니다. MultiChannelRecorder 객체를 시작한 이후에 true를 리턴합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • true: MultiChannelRecorder가 동작 상태입니다.
  • false: MultiChannelRecorder 동작 상태입니다.
bool isMultiChannelRecorderRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
녹화가 실제 파일 쓰기 중인지 판단합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • true: MultiChannelRecorder가 녹화 데이터를 파일에 쓰는 상태입니다.
  • false: MultiChannelRecorder가 녹화 데이터를 파일에 쓰지 않는 상태입니다.
bool isMultiChannelRecorderRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
채널 별로 녹화 파일 쓰기가 진행 중인지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • true: MultiChannelRecorder가 지정한 채널의 녹화 데이터를 파일에 쓰는 상태입니다.
  • false: MultiChannelRecorder가 지정한 채널의 녹화 데이터를 파일에 쓰지 않는 상태입니다.
int32_t startMultiChannelRecording ( MultiChannelRecorderRef multi_channel_recorder , const std::shared_ptr<MediaObserver2> & observer , void * user_data , bool sniffing , void * stream_data_header , size_t stream_data_header_size )
OasisMedia.h
녹화를 시작합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
observer  녹화 상태 Observer 객체입니다.
user_data  observer에 전달될 사용자 정의 데이터입니다.
sniffing  Sniffing 모드로 동작 할 경우에 true로 설정합니다. Sniffing 모드에서는 모션 감지나 이벤트 감지 등 외부 요인으로 녹화를 시작할 수 있고, 일반 녹화는 시작하지 않습니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t stopMultiChannelRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
녹화를 중지합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t restartMutiChannelRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
정지된 녹화를 재시작합니다. Sniffing 모드에서는 지원되지 않습니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t startMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , const char * event_recording_file_path , void * stream_data_header , size_t stream_data_header_size , bool is_type_of_motion )
OasisMedia.h
지정된 채널의 이벤트 또는 모션 녹화를 시작합니다. 이벤트 녹화는 event-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 event-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onEventRecordingStartedMediaObserver2::onEventRecordingCompleted로 이벤트 녹화 시작과 종료 여부를 알 수 있습니다. 이벤트 녹화는 현재 녹화와 동시에 진행됩니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
event_recording_file_path  저장할 이벤트 또는 모션 녹화 파일의 절대 경로입니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
is_type_of_motion  true이면 모션 녹화를 진행합니다. false이면 이벤트 녹화를 진행합니다. 녹화 파일이 저장될 위치가 MultiChannelRecorder 생성시 지정한 이벤트 경로나 모션 녹화 경로로 설정됩니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t startMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , const char * event_recording_file_path , void * stream_data_header , size_t stream_data_header_size , bool is_type_of_motion , bool do_single_recording )
OasisMedia.h
지정된 채널의 이벤트 또는 모션 녹화를 시작합니다. 이벤트 녹화는 event-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 event-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onEventRecordingStartedMediaObserver2::onEventRecordingCompleted로 이벤트 녹화 시작과 종료 여부를 알 수 있습니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
event_recording_file_path  저장할 이벤트 또는 모션 녹화 파일의 절대 경로입니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
is_type_of_motion  true이면 모션 녹화를 진행합니다. false이면 이벤트 녹화를 진행합니다. 녹화 파일이 저장될 위치가 MultiChannelRecorder 생성시 지정한 이벤트 경로나 모션 녹화 경로로 설정됩니다.
do_single_recording  true인 경우, 일반 녹화를 중지하고 이벤트 녹화를 시작합니다. 이벤트 녹화가 끝나면 일반 녹화를 다시 시작합니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t stopMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , bool is_type_of_motion )
OasisMedia.h
지정된 채널의 이벤트 또는 모션 녹화를 중지합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
is_type_of_motion  true이면 모션 녹화를 중지합니다. false이면 이벤트 녹화를 중지합니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t startMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , const char * event_recording_file_path , void * stream_data_header , size_t stream_data_header_size , bool do_single_recording )
OasisMedia.h
지정된 채널의 이벤트 녹화를 시작합니다. 이벤트 녹화는 event-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 event-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onEventRecordingStartedMediaObserver2::onEventRecordingCompleted로 이벤트 녹화 시작과 종료 여부를 알 수 있습니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
event_recording_file_path  저장할 이벤트 녹화 파일의 절대 경로입니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
do_single_recording  true인 경우, 일반 녹화를 중지하고 이벤트 녹화를 시작합니다. 이벤트 녹화가 끝나면 일반 녹화를 다시 시작합니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t stopMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
지정된 채널의 이벤트 녹화를 중지합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t startMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , const std::list<MultiChannelRecorderFilePath> & file_paths , void * stream_data_header , size_t stream_data_header_size , bool do_single_recording )
OasisMedia.h
전체 채널의 이벤트 녹화를 시작합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
file_paths  이벤트 녹화 파일 목록입니다. 각 엔트리는 채널 번호와 녹화 파일의 절대 경로가 포함됩니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
do_single_recording  true인 경우, 일반 녹화를 중지하고 이벤트 녹화를 시작합니다. 이벤트 녹화가 끝나면 일반 녹화를 다시 시작합니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t stopMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
전체 채널의 이벤트 녹화를 중지합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
bool isMultiChannelEventRunning ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
이벤트 녹화가 시작 되었는지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • true: 이벤트 녹화가 시작 되었습니다.
  • false: 이벤트 녹화가 시작 되지 않았습니다.
bool isMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
이벤트 녹화로 파일 쓰기가 진행 중인지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • true: 이벤트 녹화 파일 쓰기가 진행 중입니다.
  • false: 이벤트 녹화 파일 쓰기가 진행 중이지 않습니다.
int32_t startMultiChannelMotionRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , const char * event_recording_file_path , void * stream_data_header , size_t stream_data_header_size )
OasisMedia.h
지정된 채널의 모션 녹화를 시작합니다. 모션 녹화는 motion-pre-recording-seconds에 지정된 시간(초)만큼 이벤트 발생 이전 영상부터 발생 이후 motion-post-recording-seconds에 지정된 시간(초) 만큼 녹화를 합니다. MediaObserver2::onMotionRecordingStartedMediaObserver2::onMotionRecordingCompleted로 모션 녹화 시작과 종료 여부를 알 수 있습니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
event_recording_file_path  저장할 모션 녹화 파일의 절대 경로입니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t stopMultiChannelMotionRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
지정된 채널의 모션 녹화를 중지합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t startMultiChannelMotionRecording ( MultiChannelRecorderRef multi_channel_recorder , const std::list<MultiChannelRecorderFilePath> & file_paths , void * stream_data_header , size_t stream_data_header_size , bool stop_current_event_recording )
OasisMedia.h
전체 채널의 모션 녹화를 시작합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
file_paths  모션 녹화 파일 목록입니다. 각 엔트리는 채널 번호와 녹화 파일의 절대 경로가 포함됩니다.
stream_data_header  녹화 파일에 저장될 meta data header 입니다. 사용하지 않을 경우, nullptr을 지정합니다.
stream_data_header_size  녹화 파일에 저장될 meta data header 크기입니다. 크기는 바이트 단위입니다.
stop_current_event_recording  true이고 현재 이벤트 녹화가 진행 중이면, 이벤트 녹화를 중지합니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t stopMultiChannelMotionRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
전체 채널의 모션 녹화를 중지합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
bool isMultiChannelMotionRecording ( MultiChannelRecorderRef multi_channel_recorder )
OasisMedia.h
전체 채널의 모션 녹화가 실제 파일 쓰기 중인지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
리턴값
  • true: 모션 녹화 시작 후 파일 쓰기 중입니다.
  • false: 모션 녹화 시작 후 파일 쓰기 중이지 않습니다.
bool isMultiChannelEventOrMotionRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
지정된 채널의 이벤트 또는 모션 녹화가 시작되고 파일 쓰기 중인지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • true: 이벤트 또는 모션 녹화 파일 쓰기 중입니다.
  • false: 이벤트 또는 모션 녹화 파일 쓰기 중이 아닙니다.
bool isMultiChannelEventRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
지정된 채널의 이벤트 녹화가 시작되고 파일 쓰기 중인지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • true: 이벤트 녹화가 시작된 후 파일 쓰기 중입니다.
  • false: 이벤트 녹화가 시작된 후 파일 쓰기 중이 아닙니다.
bool isMultiChannelMotionRecording ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
지정된 채널의 모션 녹화가 시작되고 파일 쓰기 중인지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • true: 모션 녹화가 시작된 후 파일 쓰기 중입니다.
  • false: 모션 녹화가 시작된 후 파일 쓰기 중이 아닙니다.
bool isMultiChannelEventRunning ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id )
OasisMedia.h
지정된 채널의 이벤트 녹화가 시작되었는지 확인합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • true: 이벤트 녹화가 시작 되었습니다.
  • false: 이벤트 녹화가 시작 되지 않았습니다.
int32_t setMultiChannelRecordingText ( MultiChannelRecorderRef multi_channel_recorder , text_track_id_t text_id , const std::string & text , uint64_t timestampUs = 0ull )
OasisMedia.h
OSD 텍스트를 설정합니다. OSD 텍스트는 영상의 좌측 하단에 배치됩니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
text_id  OSD 텍스트 타입을 지정합니다. kTextTrackOsd 만 허용됩니다.
text  기록할 OSD 텍스트 입니다.
timestampUs  기록 타임스탬프로 마이크로초 단위입니다. 실제 녹화 파일에 저장되지 않습니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t setMultiChannelRecordingText ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , text_track_id_t text_id , const std::string & text , uint64_t timestampUs = 0ull )
OasisMedia.h
지정된 채널에 OSD 텍스트를 설정합니다. OSD 텍스트는 영상의 좌측 하단에 배치됩니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다. -1인 경우, 전 영상 채널에 적용됩니다.
text_id  OSD 텍스트 타입을 지정합니다. kTextTrackOsd 만 허용됩니다.
text  기록할 OSD 텍스트 입니다.
timestampUs  기록 타임스탬프로 마이크로초 단위입니다. 실제 녹화 파일에 저장되지 않습니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t setMultiChannelRecordingOsdTextAt ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , int32_t x , int32_t y , const std::string & osd_text , osd_text_flag_t flags )
OasisMedia.h
지정된 채널에 OSD 텍스트를 영상 이미지의 특정 위치에 기록합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다. -1인 경우, 전 영상 채널에 적용됩니다.
x  OSD 텍스트의 x 시작 위치입니다.
y  OSD 텍스트의 y 시작 위치입니다.
osd_text  기록할 OSD 텍스트 입니다.
flags  (사용하지 않음)
리턴값
  • 0: 성공
  • -1: 실패
int32_t addMultiChannelRecordingVideoStreamData ( MultiChannelRecorderRef multi_channel_recorder , void * stream_data , size_t stream_data_length , uint64_t timestampUs = 0ull )
OasisMedia.h
메타 데이터를 저장합니다. 사용자는 주기적으로 또는 필요에 따라서 메타 데이터를 녹화 데이터와 함께 저장할 수 있습니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
stream_data  메타 데이터 포인터입니다. 메타 데이터 포맷은 사용자가 정의합니다.
stream_data_length  메타 데이터 크기입니다. 바이트 단위입니다.
timestampUs  타임스탬프로 마이크초 단위입니다. 녹화 파일에 저장되지 않습니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t enableSoundOfMultiChannelRecording ( MultiChannelRecorderRef multi_channel_recorder , int channel_id , bool enable )
OasisMedia.h
소리 녹화 활성화 및 중지 여부를 지정합니다. 소리 녹화를 중지할 경우, 소리 트랙은 묵음 데이터가 저장됩니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다. -1인 경우, 전 영상 채널에 적용됩니다.
enable  true이면 활성화되고, false이면 묵음 데이터가 저장됩니다.
리턴값
  • 0: 성공
  • -1: 실패
bool isSoundOfMultiChannelRecordingEnabled ( MultiChannelRecorderRef multi_channel_recorder , int channel_id )
OasisMedia.h
소리 녹화 활성화 여부를 판단합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
리턴값
  • true: 활성화 중
  • false: 중단 중
int32_t getCurrentMultiChannelRecordingFilePath ( MultiChannelRecorderRef multi_channel_recorder , int32_t channel_id , std::string & file_path )
OasisMedia.h
지정된 채널의 현재 녹화 중인 파일 경로를 얻습니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
file_path  OUT 성공 시 녹화 파일 경로가 저장됩니다.
리턴값
  • 0: 성공
  • -1: 실패
int32_t takeMultiChannelRecordingShapshot ( MultiChannelRecorderRef multi_channel_recorder , uint32_t snapshot_id , int32_t channel_id , key_value_map_t & parameters )
OasisMedia.h
현재 녹화 중 영상 스냅샷을 찍습니다. 스냅샷 데이터 생성이 완료되면 MediaObserver2::onSnapshotCompleted 콜백함수에 JPEG 이미지 데이터를 전달합니다.
매개변수
multi_channel_recorder  MultiChannelRecorder 객체입니다.
snapshot_id  MediaObserver2::onSnapshotCompleted 콜백함수에서 각 snapshot을 구분하는 snapshot_id 매개변수로 사용됩니다.
channel_id  1, 2, 3 등 채널 ID 번호입니다.
parameters  JPEG 스냅샷 생성에 필요한 key-value map입니다. 패러미터는 통합 녹화 API인 takeRecordingShapshot를 참고합니다.
리턴값
  • 0: 성공
  • -1: 실패

예제

아래는 3개 영상 채널과 소리를 채널 별 개별 파일로 녹화하는 예입니다.

USE_OFFS1이면 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 &timestamp) {
    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;
}