diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index c60e0e54..6225dc18 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -60,47 +60,6 @@ API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx); */ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *vhost, const char *app, const char *stream, const char *file_path); - -///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// - -/** - * 获取录制状态 - * @param type 0:hls,1:MP4 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream 流id - * @return 录制状态,0:未录制,1:等待MediaSource注册,注册成功后立即开始录制,2:MediaSource已注册,并且正在录制 - */ -API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const char *app, const char *stream); - -/** - * 开始录制 - * @param type 0:hls,1:MP4 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream 流id - * @param customized_path 录像文件保存自定义目录,默认为空或null则自动生成 - * @param wait_for_record 是否等待流注册后再录制,未注册时,置false将返回失败 - * @param continue_record 流注销时是否继续等待录制还是立即停止录制 - * @return 0代表成功,负数代表失败 - */ -API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream, const char *customized_path, int wait_for_record, int continue_record); - -/** - * 停止录制 - * @param type 0:hls,1:MP4 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream 流id - * @return 1:成功,0:失败 - */ -API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream); - -/** - * 停止所有录制,一般程序退出时调用 - */ -API_EXPORT void API_CALL mk_recorder_stop_all(); - #ifdef __cplusplus } #endif diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index efd29c42..95e283be 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -49,24 +49,4 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *v WarnL << ex.what(); return -1; } -} - -///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// -API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const char *app, const char *stream){ - assert(vhost && app && stream); - return Recorder::getRecordStatus((Recorder::type)type,vhost,app,stream); -} - -API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,const char *customized_path,int wait_for_record, int continue_record){ - assert(vhost && app && stream); - return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path ? customized_path : "",wait_for_record,continue_record); -} - -API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ - assert(vhost && app && stream); - return Recorder::stopRecord((Recorder::type)type,vhost,app,stream); -} - -API_EXPORT void API_CALL mk_recorder_stop_all(){ - Recorder::stopAll(); } \ No newline at end of file diff --git a/server/WebApi.cpp b/server/WebApi.cpp index abd0d57f..862b5c27 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -737,43 +737,6 @@ void installWebApi() { }); #endif//ENABLE_RTPPROXY - // 开始录制hls或MP4 - api_regist1("/index/api/startRecord",[](API_ARGS1){ - CHECK_SECRET(); - CHECK_ARGS("type","vhost","app","stream","wait_for_record","continue_record"); - - int result = Recorder::startRecord((Recorder::type)allArgs["type"].as(), - allArgs["vhost"], - allArgs["app"], - allArgs["stream"], - allArgs["customized_path"], - allArgs["wait_for_record"], - allArgs["continue_record"]); - val["result"] = result; - }); - - // 停止录制hls或MP4 - api_regist1("/index/api/stopRecord",[](API_ARGS1){ - CHECK_SECRET(); - CHECK_ARGS("type","vhost","app","stream"); - int result = Recorder::stopRecord((Recorder::type)allArgs["type"].as(), - allArgs["vhost"], - allArgs["app"], - allArgs["stream"]); - val["result"] = result; - }); - - // 获取hls或MP4录制状态 - api_regist1("/index/api/getRecordStatus",[](API_ARGS1){ - CHECK_SECRET(); - CHECK_ARGS("type","vhost","app","stream"); - auto status = Recorder::getRecordStatus((Recorder::type)allArgs["type"].as(), - allArgs["vhost"], - allArgs["app"], - allArgs["stream"]); - val["status"] = (int)status; - }); - //获取录像文件夹列表或mp4文件列表 //http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01 api_regist1("/index/api/getMp4RecordFile", [](API_ARGS1){ diff --git a/server/main.cpp b/server/main.cpp index dc993d4a..91126273 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> @@ -354,7 +354,6 @@ int start_main(int argc,char *argv[]) { } unInstallWebApi(); unInstallWebHook(); - Recorder::stopAll(); //休眠1秒再退出,防止资源释放顺序错误 InfoL << "程序退出中,请等待..."; sleep(1); diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 3564c28d..a2ff44a5 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -78,7 +78,6 @@ vector MediaSource::getTracks(bool trackReady) const { void MediaSource::setTrackSource(const std::weak_ptr &track_src) { _track_source = track_src; - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, *this); } void MediaSource::setListener(const std::weak_ptr &listener){ diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index c0a79adb..0f9dfe8d 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -57,20 +57,12 @@ public: } if(enable_hls){ - Recorder::startRecord(Recorder::type_hls,vhost, app, stream, "", true, false); + _hls = Recorder::createRecorder(Recorder::type_hls,vhost, app, stream); } if(enable_mp4){ - Recorder::startRecord(Recorder::type_mp4,vhost, app, stream, "", true, false); + _mp4 = Recorder::createRecorder(Recorder::type_mp4,vhost, app, stream); } - - _get_hls_media_source = [vhost,app,stream](){ - auto recorder = dynamic_pointer_cast(Recorder::getRecorder(Recorder::type_hls,vhost,app,stream)); - if(recorder){ - return recorder->getMediaSource(); - } - return MediaSource::Ptr(); - }; } virtual ~MultiMediaSourceMuxer(){} @@ -84,6 +76,12 @@ public: if(_rtsp){ _rtsp->resetTracks(); } + if(_hls){ + _hls->resetTracks(); + } + if(_mp4){ + _mp4->resetTracks(); + } } /** @@ -99,7 +97,7 @@ public: _rtsp->setListener(listener); } - auto hls_src = _get_hls_media_source(); + auto hls_src = getHlsMediaSource(); if(hls_src){ hls_src->setListener(listener); } @@ -110,7 +108,7 @@ public: * @return */ int totalReaderCount() const{ - auto hls_src = _get_hls_media_source(); + auto hls_src = getHlsMediaSource(); return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0); } @@ -118,7 +116,6 @@ public: if(_rtmp){ _rtmp->setTimeStamp(stamp); } - if(_rtsp){ _rtsp->setTimeStamp(stamp); } @@ -139,6 +136,12 @@ protected: if(_rtsp){ _rtsp->addTrack(track); } + if(_hls){ + _hls->addTrack(track); + } + if(_mp4){ + _mp4->addTrack(track); + } } /** @@ -152,6 +155,12 @@ protected: if(_rtsp) { _rtsp->inputFrame(frame); } + if(_hls){ + _hls->inputFrame(frame); + } + if(_mp4){ + _mp4->inputFrame(frame); + } } /** @@ -167,7 +176,7 @@ protected: _rtsp->onAllTrackReady(); } - auto hls_src = _get_hls_media_source(); + auto hls_src = getHlsMediaSource(); if(hls_src){ hls_src->setTrackSource(shared_from_this()); } @@ -176,11 +185,20 @@ protected: _listener->onAllTrackReady(); } } + + MediaSource::Ptr getHlsMediaSource() const{ + auto recorder = dynamic_pointer_cast(_hls); + if(recorder){ + return recorder->getMediaSource(); + } + return nullptr; + } private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; + MediaSinkInterface::Ptr _hls; + MediaSinkInterface::Ptr _mp4; Listener *_listener = nullptr; - function _get_hls_media_source; }; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index eaa4ca1a..87144841 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -55,7 +55,6 @@ bool loadIniConfig(const char *ini_path){ ////////////广播名称/////////// namespace Broadcast { const string kBroadcastMediaChanged = "kBroadcastMediaChanged"; -const string kBroadcastMediaResetTracks = "kBroadcastMediaResetTracks"; const string kBroadcastRecordMP4 = "kBroadcastRecordMP4"; const string kBroadcastHttpRequest = "kBroadcastHttpRequest"; const string kBroadcastHttpAccess = "kBroadcastHttpAccess"; diff --git a/src/Common/config.h b/src/Common/config.h index ba81611c..14c57815 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -72,10 +72,6 @@ namespace Broadcast { extern const string kBroadcastMediaChanged; #define BroadcastMediaChangedArgs const bool &bRegist, MediaSource &sender -//MediaSource重置Track事件 -extern const string kBroadcastMediaResetTracks; -#define BroadcastMediaResetTracksArgs MediaSource &sender - //录制mp4文件成功后广播 extern const string kBroadcastRecordMP4; #define BroadcastRecordMP4Args const MP4Info &info diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index c78c7322..c39d6c8f 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -70,286 +70,6 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s return ""; } } -//////////////////////////////////////////////////////////////////////////////////////// - -class RecorderHelper { -public: - typedef std::shared_ptr Ptr; - - /** - * 构建函数 - * @param bContinueRecord false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除) - */ - RecorderHelper(const MediaSinkInterface::Ptr &recorder, bool bContinueRecord) { - _recorder = recorder; - _continueRecord = bContinueRecord; - } - - ~RecorderHelper() { - resetTracks(); - } - - // 附则于track上 - void attachTracks(vector &&tracks, const string &schema){ - if(isTracksSame(tracks)){ - return; - } - resetTracks(); - _tracks = std::move(tracks); - _schema = schema; - for (auto &track : _tracks) { - _recorder->addTrack(track); - track->addDelegate(_recorder); - } - } - - - // 判断新的tracks是否与之前的一致 - bool isTracksSame(const vector &tracks){ - if(tracks.size() != _tracks.size()) { - return false; - } - int i = 0; - for(auto &track : tracks){ - if(track != _tracks[i++]){ - return false; - } - } - return true; - } - - // 重置所有track - void resetTracks(){ - if(_tracks.empty()){ - return; - } - for (auto &track : _tracks) { - track->delDelegate(_recorder.get()); - } - _tracks.clear(); - _recorder->resetTracks(); - } - - // 返回false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除) - bool continueRecord(){ - return _continueRecord; - } - - bool isRecording() { - return !_tracks.empty(); - } - - const string &getSchema() const{ - return _schema; - } - - const MediaSinkInterface::Ptr& getRecorder() const{ - return _recorder; - } -private: - MediaSinkInterface::Ptr _recorder; - vector _tracks; - bool _continueRecord; - string _schema; -}; - - -template -class MediaSourceWatcher { -public: - static MediaSourceWatcher& Instance(){ - static MediaSourceWatcher instance; - return instance; - } - - Recorder::status getRecordStatus(const string &vhost, const string &app, const string &stream_id) { - return getRecordStatus_l(getRecorderKey(vhost, app, stream_id)); - } - - MediaSinkInterface::Ptr getRecorder(const string &vhost, const string &app, const string &stream_id) const{ - auto key = getRecorderKey(vhost, app, stream_id); - lock_guard lck(_recorder_mtx); - auto it = _recorder_map.find(key); - if (it == _recorder_map.end()) { - return nullptr; - } - return it->second->getRecorder(); - } - - int startRecord(const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) { - auto key = getRecorderKey(vhost, app, stream_id); - lock_guard lck(_recorder_mtx); - if (getRecordStatus_l(key) != Recorder::status_not_record) { - // 已经在录制了 - return 0; - } - - auto src = findMediaSource(vhost, app, stream_id); - if (!waitForRecord && !src) { - // 暂时无法开启录制 - return -1; - } - - auto recorder = Recorder::createRecorder(type, vhost, app, stream_id, customized_path); - if (!recorder) { - // 创建录制器失败 - WarnL << "不支持该录制类型:" << type; - return -2; - } - auto helper = std::make_shared(recorder, continueRecord); - if(src){ - auto tracks = src->getTracks(needTrackReady()); - if(tracks.size()){ - helper->attachTracks(std::move(tracks),src->getSchema()); - } - auto hls_recorder = dynamic_pointer_cast(recorder); - if(hls_recorder){ - hls_recorder->getMediaSource()->setListener(src->getListener()); - } - } - - _recorder_map[key] = std::move(helper); - return 0; - } - - bool stopRecord(const string &vhost, const string &app, const string &stream_id) { - lock_guard lck(_recorder_mtx); - return _recorder_map.erase(getRecorderKey(vhost, app, stream_id)); - } - - void stopAll(){ - lock_guard lck(_recorder_mtx); - _recorder_map.clear(); - } - -private: - MediaSourceWatcher(){ - //保存NoticeCenter的强引用,防止在MediaSourceWatcher单例释放前释放NoticeCenter单例 - _notice_center = NoticeCenter::Instance().shared_from_this(); - _notice_center->addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ - if(!bRegist){ - removeRecorder(sender); - } - }); - _notice_center->addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){ - addRecorder(sender); - }); - } - - ~MediaSourceWatcher(){ - _notice_center->delListener(this,Broadcast::kBroadcastMediaChanged); - _notice_center->delListener(this,Broadcast::kBroadcastMediaResetTracks); - } - - void addRecorder(MediaSource &sender){ - auto tracks = sender.getTracks(needTrackReady()); - auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); - lock_guard lck(_recorder_mtx); - auto it = _recorder_map.find(key); - if(it == _recorder_map.end()){ - // 录像记录不存在 - return; - } - - if(!it->second->isRecording() || it->second->getSchema() == sender.getSchema()){ - // 绑定的协议一致或者并未正在录制则替换tracks - if (!tracks.empty()) { - it->second->attachTracks(std::move(tracks),sender.getSchema()); - } - } - } - - void removeRecorder(MediaSource &sender){ - auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); - lock_guard lck(_recorder_mtx); - auto it = _recorder_map.find(key); - if(it == _recorder_map.end() || it->second->getSchema() != sender.getSchema()){ - // 录像记录不存在或绑定的协议不一致 - return; - } - - if(it->second->continueRecord()){ - // 如果可以继续录制,那么只重置tracks,不删除对象 - it->second->resetTracks(); - }else{ - // 删除对象(意味着可能删除hls临时文件) - _recorder_map.erase(it); - } - } - - Recorder::status getRecordStatus_l(const string &key) { - auto it = _recorder_map.find(key); - if (it == _recorder_map.end()) { - return Recorder::status_not_record; - } - return it->second->isRecording() ? Recorder::status_recording : Recorder::status_wait_record; - } - - // 查找MediaSource以便录制 - MediaSource::Ptr findMediaSource(const string &vhost, const string &app, const string &stream_id) { - bool need_ready = needTrackReady(); - auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); - if (src) { - auto ret = src->getTracks(need_ready); - if (!ret.empty()) { - return std::move(src); - } - } - - src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); - if (src) { - auto ret = src->getTracks(need_ready); - if (!ret.empty()) { - return std::move(src); - } - } - return nullptr; - } - - string getRecorderKey(const string &vhost, const string &app, const string &stream_id) const{ - return vhost + "/" + app + "/" + stream_id; - } - - - /** - * 有些录制类型不需要track就绪即可录制 - */ - bool needTrackReady(){ - switch (type){ - case Recorder::type_hls: - return false; - case Recorder::type_mp4: - return true; - default: - return true; - } - } -private: - mutable recursive_mutex _recorder_mtx; - NoticeCenter::Ptr _notice_center; - unordered_map _recorder_map; -}; - - -Recorder::status Recorder::getRecordStatus(Recorder::type type, const string &vhost, const string &app, const string &stream_id) { - switch (type){ - case type_mp4: - return MediaSourceWatcher::Instance().getRecordStatus(vhost,app,stream_id); - case type_hls: - return MediaSourceWatcher::Instance().getRecordStatus(vhost,app,stream_id); - } - return status_not_record; -} - -std::shared_ptr Recorder::getRecorder(type type, const string &vhost, const string &app, const string &stream_id){ - switch (type){ - case type_mp4: - return MediaSourceWatcher::Instance().getRecorder(vhost,app,stream_id); - case type_hls: - return MediaSourceWatcher::Instance().getRecorder(vhost,app,stream_id); - } - return nullptr; -} std::shared_ptr Recorder::createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path){ auto path = Recorder::getRecordPath(type, vhost, app, stream_id); @@ -375,30 +95,4 @@ std::shared_ptr Recorder::createRecorder(type type, const st } } -int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) { - switch (type){ - case type_mp4: - return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,customized_path,waitForRecord,continueRecord); - case type_hls: - return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,customized_path,waitForRecord,continueRecord); - } - WarnL << "unknown record type: " << type; - return -3; -} - -bool Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) { - switch (type){ - case type_mp4: - return MediaSourceWatcher::Instance().stopRecord(vhost,app,stream_id); - case type_hls: - return MediaSourceWatcher::Instance().stopRecord(vhost,app,stream_id); - } - return false; -} - -void Recorder::stopAll() { - MediaSourceWatcher::Instance().stopAll(); - MediaSourceWatcher::Instance().stopAll(); -} - } /* namespace mediakit */ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 5a072568..cd457c16 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -26,26 +26,14 @@ #ifndef SRC_MEDIAFILE_RECORDER_H_ #define SRC_MEDIAFILE_RECORDER_H_ - #include #include using namespace std; - namespace mediakit { - class MediaSinkInterface; class Recorder{ public: - typedef enum { - // 未录制 - status_not_record = 0, - // 等待MediaSource注册,注册成功后立即开始录制 - status_wait_record = 1, - // MediaSource已注册,并且正在录制 - status_recording = 2, - } status; - typedef enum { // 录制hls type_hls = 0, @@ -64,52 +52,6 @@ public: */ static string getRecordPath(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path = ""); - /** - * 获取录制状态 - * @param type hls还是MP4录制 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream_id 流id - * @return 录制状态 - */ - static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id); - - /** - * 开始录制 - * @param type hls还是MP4录制 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream_id 流id - * @param customized_path 录像文件保存自定义目录,默认为空则自动生成 - * @param waitForRecord 是否等待流注册后再录制,未注册时,置false将返回失败 - * @param continueRecord 流注销时是否继续等待录制还是立即停止录制 - * @return 0代表成功,负数代表失败 - */ - static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path,bool waitForRecord, bool continueRecord); - - /** - * 停止录制 - * @param type hls还是MP4录制 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream_id 流id - */ - static bool stopRecord(type type, const string &vhost, const string &app, const string &stream_id); - - /** - * 停止所有录制,一般程序退出时调用 - */ - static void stopAll(); - - /** - * 获取录制对象 - * @param type hls还是MP4录制 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream_id 流id - */ - static std::shared_ptr getRecorder(type type, const string &vhost, const string &app, const string &stream_id); - /** * 创建录制器对象 * @param type hls还是MP4录制 @@ -119,12 +61,11 @@ public: * @param customized_path 录像文件保存自定义目录,默认为空则自动生成 * @return 对象指针,可能为nullptr */ - static std::shared_ptr createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path); + static std::shared_ptr createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path = ""); private: Recorder() = delete; ~Recorder() = delete; }; } /* namespace mediakit */ - #endif /* SRC_MEDIAFILE_RECORDER_H_ */ diff --git a/tests/test_server.cpp b/tests/test_server.cpp index 297dc8aa..dc9756ba 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -354,7 +354,6 @@ int main(int argc,char *argv[]) { signal(SIGHUP, [](int) { loadIniConfig(); }); sem.wait(); - Recorder::stopAll(); lock_guard lck(s_mtxFlvRecorder); s_mapFlvRecorder.clear(); return 0;