🌐 한국어

    개별 녹화 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 &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 onFileDeleted ( int32_t channel_id , void * user_data , const char * deleted_file_path )
    OasisMedia.h
    녹화 폴더 개수가 제한되어 있는 경우, 가장 오래된 파일을 폴더에서 삭제 한 후 호출됩니다.
    매개변수
    channel_id  1, 2, 3 등 채널 ID 번호입니다.
    user_data  startMultiChannelRecording 호출 시 전달받은 user_data 매개변수 값입니다.
    deleted_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와 녹화 파일의 절대 경로가 포함된 구조체 목록입니다. &lt;녹화 디렉토리>/REC_&lt;채널 번호>_&lt;날짜 시간>.&lt;확장자> 로 자동 생성됩니다. 응용 프로그램은 각 녹화 파일의 절대 경로를 필요에 따라 변경할 수 있습니다.

    아래는 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!!!
    };
    
    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 multiChannelRecorderIsRunning ( 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인 takeRecordingSnapshot를 참고합니다.
    리턴값
    • 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["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;
    }