diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index d5cb4e78..c14563ed 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -44,6 +44,39 @@ 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:正在录制 + */ +API_EXPORT int API_CALL mk_recorder_is_recording(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则自动生成 + * @return 1代表成功,0代表失败 + */ +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream, const char *customized_path); + +/** + * 停止录制 + * @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); + #ifdef __cplusplus } #endif diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index a36e2545..e6bb8481 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -26,7 +26,7 @@ public: ~MediaHelper(){} void attachEvent(){ - _channel->setListener(shared_from_this()); + _channel->setMediaListener(shared_from_this()); } DevChannel::Ptr &getChannel(){ diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index 6171a6b2..087bb3e9 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -33,4 +33,20 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *v WarnL << ex.what(); return -1; } -} \ No newline at end of file +} + +///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// +API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, const char *app, const char *stream){ + assert(vhost && app && stream); + return Recorder::isRecording((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){ + assert(vhost && app && stream); + return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path ? customized_path : ""); +} + +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); +} diff --git a/server/WebApi.cpp b/server/WebApi.cpp index e5e7bff5..6afad9b8 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -721,6 +721,37 @@ void installWebApi() { }); #endif//ENABLE_RTPPROXY + // 开始录制hls或MP4 + api_regist1("/index/api/startRecord",[](API_ARGS1){ + CHECK_SECRET(); + CHECK_ARGS("type","vhost","app","stream"); + val["result"] = Recorder::startRecord((Recorder::type) allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"], + allArgs["customized_path"]); + }); + + // 停止录制hls或MP4 + api_regist1("/index/api/stopRecord",[](API_ARGS1){ + CHECK_SECRET(); + CHECK_ARGS("type","vhost","app","stream"); + val["result"] = Recorder::stopRecord((Recorder::type) allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"]); + }); + + // 获取hls或MP4录制状态 + api_regist1("/index/api/isRecording",[](API_ARGS1){ + CHECK_SECRET(); + CHECK_ARGS("type","vhost","app","stream"); + val["status"] = Recorder::isRecording((Recorder::type) allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"]); + }); + //获取录像文件夹列表或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/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 90b9a917..f20a2a2c 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -13,9 +13,7 @@ #include "Util/util.h" #include "Network/sockutil.h" #include "Network/TcpSession.h" - using namespace toolkit; - namespace mediakit { recursive_mutex MediaSource::g_mtxMediaSrc; @@ -104,6 +102,22 @@ void MediaSource::onNoneReader(){ } } +bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path){ + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->setupRecord(*this, type, start, custom_path); +} + +bool MediaSource::isRecording(Recorder::type type){ + auto listener = _listener.lock(); + if(!listener){ + return false; + } + return listener->isRecording(*this, type); +} + void MediaSource::for_each_media(const function &cb) { lock_guard lock(g_mtxMediaSrc); for (auto &pr0 : g_mapMediaSrc) { diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 1b1b829e..36a0ea59 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -22,6 +22,7 @@ #include "Util/TimeTicker.h" #include "Util/NoticeCenter.h" #include "Extension/Track.h" +#include "Record/Recorder.h" using namespace std; using namespace toolkit; @@ -45,6 +46,10 @@ public: virtual bool close(MediaSource &sender,bool force) { return false;} // 观看总人数 virtual int totalReaderCount(MediaSource &sender) = 0; + // 开启或关闭录制 + virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { return false; }; + // 获取录制状态 + virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }; private: // 通知无人观看 void onNoneReader(MediaSource &sender); @@ -104,7 +109,6 @@ public: // 获取监听者 const std::weak_ptr& getListener() const; - // 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数 virtual int readerCount() = 0; // 观看者个数,包括(hls/rtsp/rtmp) @@ -121,6 +125,10 @@ public: bool close(bool force); // 该流无人观看 void onNoneReader(); + // 开启或关闭录制 + bool setupRecord(Recorder::type type, bool start, const string &custom_path); + // 获取录制状态 + bool isRecording(Recorder::type type); // 同步查找流 static Ptr find(const string &schema, const string &vhost, const string &app, const string &id, bool bMake = true) ; diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp new file mode 100644 index 00000000..1348a0e5 --- /dev/null +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -0,0 +1,295 @@ +/* +* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Use of this source code is governed by MIT license that can be found in the +* LICENSE file in the root of the source tree. All contributing project authors +* may be found in the AUTHORS file in the root of the source tree. +*/ + +#include "MultiMediaSourceMuxer.h" +namespace mediakit { + +MultiMuxerPrivate::~MultiMuxerPrivate() {} +MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost, + const string &app, + const string &stream, + float dur_sec, + bool enable_rtsp, + bool enable_rtmp, + bool enable_hls, + bool enable_mp4) { + if (enable_rtmp) { + _rtmp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); + } + if (enable_rtsp) { + _rtsp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); + } + + if (enable_hls) { + _hls = Recorder::createRecorder(Recorder::type_hls, vhost, app, stream); + } + + if (enable_mp4) { + _mp4 = Recorder::createRecorder(Recorder::type_mp4, vhost, app, stream); + } +} + +void MultiMuxerPrivate::resetTracks() { + if (_rtmp) { + _rtmp->resetTracks(); + } + if (_rtsp) { + _rtsp->resetTracks(); + } + + //拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题 + auto hls = _hls; + if (hls) { + hls->resetTracks(); + } + + auto mp4 = _mp4; + if (mp4) { + mp4->resetTracks(); + } +} + +void MultiMuxerPrivate::setMediaListener(const std::weak_ptr &listener) { + if (_rtmp) { + _rtmp->setListener(listener); + } + + if (_rtsp) { + _rtsp->setListener(listener); + } + + auto hls_src = getHlsMediaSource(); + if (hls_src) { + hls_src->setListener(listener); + } + _meida_listener = listener; +} + +int MultiMuxerPrivate::totalReaderCount() const { + auto hls_src = getHlsMediaSource(); + return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0); +} + +static std::shared_ptr makeRecorder(const vector &tracks, Recorder::type type, MediaSource &sender){ + auto recorder = Recorder::createRecorder(type, sender.getVhost(), sender.getApp(), sender.getId()); + for (auto &track : tracks) { + recorder->addTrack(track); + } + return recorder; +} + +//此函数可能跨线程调用 +bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path){ + switch (type) { + case Recorder::type_hls : { + if (start && !_hls) { + //开始录制 + _hls = makeRecorder(getTracks(true), type, sender); + auto hls_src = getHlsMediaSource(); + if (hls_src) { + //设置HlsMediaSource的事件监听器 + hls_src->setListener(_meida_listener); + } + } else if (!start && _hls) { + //停止录制 + _hls = nullptr; + } + return true; + } + case Recorder::type_mp4 : { + if (start && !_mp4) { + //开始录制 + _mp4 = makeRecorder(getTracks(true), type, sender);; + } else if (!start && _mp4) { + //停止录制 + _mp4 = nullptr; + } + return true; + } + default: + return false; + } +} + +//此函数可能跨线程调用 +bool MultiMuxerPrivate::isRecording(MediaSource &sender, Recorder::type type){ + switch (type){ + case Recorder::type_hls : + return _hls ? true : false; + case Recorder::type_mp4 : + return _mp4 ? true : false; + default: + return false; + } +} + +void MultiMuxerPrivate::setTimeStamp(uint32_t stamp) { + if (_rtmp) { + _rtmp->setTimeStamp(stamp); + } + if (_rtsp) { + _rtsp->setTimeStamp(stamp); + } +} + +void MultiMuxerPrivate::setTrackListener(Listener *listener) { + _listener = listener; +} + +void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) { + if (_rtmp) { + _rtmp->addTrack(track); + } + if (_rtsp) { + _rtsp->addTrack(track); + } + + //拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题 + auto hls = _hls; + if (hls) { + hls->addTrack(track); + } + auto mp4 = _mp4; + if (mp4) { + mp4->addTrack(track); + } +} + +void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) { + if (_rtmp) { + _rtmp->inputFrame(frame); + } + if (_rtsp) { + _rtsp->inputFrame(frame); + } + //拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题 + //此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优 + auto hls = _hls; + if (hls) { + hls->inputFrame(frame); + } + auto mp4 = _mp4; + if (mp4) { + mp4->inputFrame(frame); + } +} + +void MultiMuxerPrivate::onAllTrackReady() { + if (_rtmp) { + _rtmp->setTrackSource(shared_from_this()); + _rtmp->onAllTrackReady(); + } + if (_rtsp) { + _rtsp->setTrackSource(shared_from_this()); + _rtsp->onAllTrackReady(); + } + + auto hls_src = getHlsMediaSource(); + if (hls_src) { + hls_src->setTrackSource(shared_from_this()); + } + + if (_listener) { + _listener->onAllTrackReady(); + } +} + +MediaSource::Ptr MultiMuxerPrivate::getHlsMediaSource() const { + auto recorder = dynamic_pointer_cast(_hls); + if (recorder) { + return recorder->getMediaSource(); + } + return nullptr; +} + +///////////////////////////////////////////////////////////////// + +MultiMediaSourceMuxer::~MultiMediaSourceMuxer() {} +MultiMediaSourceMuxer::MultiMediaSourceMuxer(const string &vhost, + const string &app, + const string &stream, + float dur_sec, + bool enable_rtsp, + bool enable_rtmp, + bool enable_hls, + bool enable_mp4) { + _muxer.reset(new MultiMuxerPrivate(vhost, app, stream, dur_sec, enable_rtsp, enable_rtmp, enable_hls, enable_mp4)); +} + +void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr &listener) { + _muxer->setMediaListener(shared_from_this()); + _listener = listener; +} + +int MultiMediaSourceMuxer::totalReaderCount() const { + return _muxer->totalReaderCount(); +} + +void MultiMediaSourceMuxer::setTimeStamp(uint32_t stamp) { + _muxer->setTimeStamp(stamp); +} + +void MultiMediaSourceMuxer::setTrackListener(Listener *listener) { + _muxer->setTrackListener(listener); +} + +vector MultiMediaSourceMuxer::getTracks(bool trackReady) const { + return _muxer->getTracks(trackReady); +} + +bool MultiMediaSourceMuxer::seekTo(MediaSource &sender, uint32_t ui32Stamp) { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->seekTo(sender, ui32Stamp); +} + +bool MultiMediaSourceMuxer::close(MediaSource &sender, bool force) { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->close(sender, force); +} + +int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) { + auto listener = _listener.lock(); + if (!listener) { + return _muxer->totalReaderCount(); + } + return listener->totalReaderCount(sender); +} + +bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { + return _muxer->setupRecord(sender,type,start,custom_path); +} + +bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) { + return _muxer->isRecording(sender,type); +} + +void MultiMediaSourceMuxer::addTrack(const Track::Ptr &track) { + _muxer->addTrack(track); +} + +void MultiMediaSourceMuxer::addTrackCompleted() { + _muxer->addTrackCompleted(); +} + +void MultiMediaSourceMuxer::resetTracks() { + _muxer->resetTracks(); +} + +void MultiMediaSourceMuxer::inputFrame(const Frame::Ptr &frame) { + _muxer->inputFrame(frame); +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index d3f8ebe7..5ba6b8fe 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -10,180 +10,167 @@ #ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H #define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H - #include "Rtsp/RtspMediaSourceMuxer.h" #include "Rtmp/RtmpMediaSourceMuxer.h" #include "Record/Recorder.h" #include "Record/HlsMediaSource.h" #include "Record/HlsRecorder.h" +namespace mediakit{ -/** - * 使用该对象时,应该使用setListener方法来绑定MediaSource相关的事件 - * 否则多种不同类型的MediaSource(rtsp/rtmp/hls)将无法产生关联 - */ -class MultiMediaSourceMuxer : public MediaSink , public std::enable_shared_from_this{ +class MultiMuxerPrivate : public MediaSink , public std::enable_shared_from_this{ public: + friend class MultiMediaSourceMuxer; + typedef std::shared_ptr Ptr; class Listener{ public: Listener() = default; virtual ~Listener() = default; virtual void onAllTrackReady() = 0; }; + ~MultiMuxerPrivate() override ; +private: + MultiMuxerPrivate(const string &vhost, + const string &app, + const string &stream, + float dur_sec, + bool enable_rtsp, + bool enable_rtmp, + bool enable_hls, + bool enable_mp4); - typedef std::shared_ptr Ptr; - MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec = 0.0, - bool enable_rtsp = true, bool enable_rtmp = true, bool enable_hls = true, bool enable_mp4 = false){ - if (enable_rtmp) { - _rtmp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); - } - if (enable_rtsp) { - _rtsp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); - } - - if(enable_hls){ - _hls = Recorder::createRecorder(Recorder::type_hls,vhost, app, stream); - } - - if(enable_mp4){ - _mp4 = Recorder::createRecorder(Recorder::type_mp4,vhost, app, stream); - } - } - virtual ~MultiMediaSourceMuxer(){} - - /** - * 重置音视频媒体 - */ - void resetTracks() override{ - if(_rtmp){ - _rtmp->resetTracks(); - } - if(_rtsp){ - _rtsp->resetTracks(); - } - if(_hls){ - _hls->resetTracks(); - } - if(_mp4){ - _mp4->resetTracks(); - } - } - - /** - * 设置事件监听器 - * @param listener - */ - void setListener(const std::weak_ptr &listener){ - if(_rtmp) { - _rtmp->setListener(listener); - } - - if(_rtsp) { - _rtsp->setListener(listener); - } - - auto hls_src = getHlsMediaSource(); - if(hls_src){ - hls_src->setListener(listener); - } - } - - /** - * 返回总的消费者个数 - * @return - */ - int totalReaderCount() const{ - auto hls_src = getHlsMediaSource(); - return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0); - } - - void setTimeStamp(uint32_t stamp){ - if(_rtmp){ - _rtmp->setTimeStamp(stamp); - } - if(_rtsp){ - _rtsp->setTimeStamp(stamp); - } - } - - void setTrackListener(Listener *listener){ - _listener = listener; - } -protected: - /** - * 添加音视频媒体 - * @param track 媒体描述 - */ - void onTrackReady(const Track::Ptr & track) override { - if(_rtmp){ - _rtmp->addTrack(track); - } - if(_rtsp){ - _rtsp->addTrack(track); - } - if(_hls){ - _hls->addTrack(track); - } - if(_mp4){ - _mp4->addTrack(track); - } - } - - /** - * 写入帧数据然后打包rtmp - * @param frame 帧数据 - */ - void onTrackFrame(const Frame::Ptr &frame) override { - if(_rtmp) { - _rtmp->inputFrame(frame); - } - if(_rtsp) { - _rtsp->inputFrame(frame); - } - if(_hls){ - _hls->inputFrame(frame); - } - if(_mp4){ - _mp4->inputFrame(frame); - } - } - - /** - * 所有Track都准备就绪,触发媒体注册事件 - */ - void onAllTrackReady() override{ - if(_rtmp) { - _rtmp->setTrackSource(shared_from_this()); - _rtmp->onAllTrackReady(); - } - if(_rtsp) { - _rtsp->setTrackSource(shared_from_this()); - _rtsp->onAllTrackReady(); - } - - auto hls_src = getHlsMediaSource(); - if(hls_src){ - hls_src->setTrackSource(shared_from_this()); - } - - if(_listener){ - _listener->onAllTrackReady(); - } - } - - MediaSource::Ptr getHlsMediaSource() const{ - auto recorder = dynamic_pointer_cast(_hls); - if(recorder){ - return recorder->getMediaSource(); - } - return nullptr; - } + void resetTracks() override; + void setMediaListener(const std::weak_ptr &listener); + int totalReaderCount() const; + void setTimeStamp(uint32_t stamp); + void setTrackListener(Listener *listener); + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path); + bool isRecording(MediaSource &sender, Recorder::type type); +private: + void onTrackReady(const Track::Ptr & track) override; + void onTrackFrame(const Frame::Ptr &frame) override; + void onAllTrackReady() override; + MediaSource::Ptr getHlsMediaSource() const; private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; MediaSinkInterface::Ptr _hls; MediaSinkInterface::Ptr _mp4; Listener *_listener = nullptr; + std::weak_ptr _meida_listener; }; +class MultiMediaSourceMuxer : public MediaSourceEvent, public MediaSinkInterface, public TrackSource, public std::enable_shared_from_this{ +public: + typedef MultiMuxerPrivate::Listener Listener; + typedef std::shared_ptr Ptr; + ~MultiMediaSourceMuxer() override; + MultiMediaSourceMuxer(const string &vhost, + const string &app, + const string &stream, + float dur_sec = 0.0, + bool enable_rtsp = true, + bool enable_rtmp = true, + bool enable_hls = true, + bool enable_mp4 = false); + + /** + * 设置事件监听器 + * @param listener + */ + void setMediaListener(const std::weak_ptr &listener); + + /** + * 返回总的消费者个数 + * @return + */ + int totalReaderCount() const; + + /** + * 设置MediaSource时间戳 + * @param stamp 时间戳 + */ + void setTimeStamp(uint32_t stamp); + + /** + * 随着Track就绪事件监听器 + * @param listener 事件监听器 + */ + void setTrackListener(Listener *listener); + + /** + * 获取所有Track + * @param trackReady 是否筛选过滤未就绪的track + * @return 所有Track + */ + vector getTracks(bool trackReady = true) const override; + + /** + * 通知拖动进度条 + * @param sender 事件发送者 + * @param ui32Stamp 目标时间戳 + * @return 是否成功 + */ + bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; + + /** + * 通知停止流生成 + * @param sender 事件发送者 + * @param force 是否强制关闭 + * @return 成功与否 + */ + bool close(MediaSource &sender,bool force) override; + + /** + * 观看总人数 + * @param sender 事件发送者 + * @return 观看总人数 + */ + int totalReaderCount(MediaSource &sender) override; + + /** + * 设置录制状态 + * @param type 录制类型 + * @param start 开始或停止 + * @param custom_path 开启录制时,指定自定义路径 + * @return 是否设置成功 + */ + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) override; + + /** + * 获取录制状态 + * @param type 录制类型 + * @return 录制状态 + */ + bool isRecording(MediaSource &sender, Recorder::type type) override; + + /** + * 添加track,内部会调用Track的clone方法 + * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 + * @param track + */ + void addTrack(const Track::Ptr & track) override; + + /** + * 添加track完毕 + * @param track + */ + void addTrackCompleted(); + + /** + * 重置track + */ + void resetTracks() override; + + /** + * 写入帧数据 + * @param frame 帧 + */ + void inputFrame(const Frame::Ptr &frame) override; +private: + MultiMuxerPrivate::Ptr _muxer; + std::weak_ptr _listener; +}; + +}//namespace mediakit #endif //ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index e77e4634..6b726228 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -238,7 +238,7 @@ void PlayerProxy::onPlaySuccess() { _mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, _bEnableRtmp, _bEnableHls, _bEnableMp4)); } } - _mediaMuxer->setListener(shared_from_this()); + _mediaMuxer->setMediaListener(shared_from_this()); auto videoTrack = getTrack(TrackVideo,false); if(videoTrack){ diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 15079f57..025a9c39 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -73,7 +73,7 @@ bool MP4Reader::readSample() { void MP4Reader::startReadMP4() { GET_CONFIG(uint32_t, sampleMS, Record::kSampleMS); auto strongSelf = shared_from_this(); - _mediaMuxer->setListener(strongSelf); + _mediaMuxer->setMediaListener(strongSelf); //先获取关键帧 seekTo(0); diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 8a7a2d16..38d6b98a 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -79,4 +79,36 @@ std::shared_ptr Recorder::createRecorder(type type, const st } } +static MediaSource::Ptr getMediaSource(const string &vhost, const string &app, const string &stream_id){ + auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id, false); + if(src){ + return src; + } + return MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id, false); +} + +bool Recorder::isRecording(type type, const string &vhost, const string &app, const string &stream_id){ + auto src = getMediaSource(vhost, app, stream_id); + if(!src){ + return false; + } + return src->isRecording(type); +} + +bool Recorder::startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path){ + auto src = getMediaSource(vhost, app, stream_id); + if(!src){ + return false; + } + return src->setupRecord(type,true,customized_path); +} + +bool Recorder::stopRecord(type type, const string &vhost, const string &app, const string &stream_id){ + auto src = getMediaSource(vhost, app, stream_id); + if(!src){ + return false; + } + return src->setupRecord(type, false, ""); +} + } /* namespace mediakit */ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index dff4acd0..670de9f8 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -46,6 +46,37 @@ public: * @return 对象指针,可能为nullptr */ static std::shared_ptr createRecorder(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 bool isRecording(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 录像文件保存自定义目录,默认为空则自动生成 + * @return 成功与否 + */ + static bool startRecord(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 + */ + static bool stopRecord(type type, const string &vhost, const string &app, const string &stream_id); + private: Recorder() = delete; ~Recorder() = delete; diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index 027dec54..e3c74963 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -68,7 +68,7 @@ public: void setListener(const std::weak_ptr &listener) override { RtmpMediaSource::setListener(listener); if(_muxer){ - _muxer->setListener(listener); + _muxer->setMediaListener(listener); } } @@ -88,7 +88,7 @@ public: void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) { //不重复生成rtmp _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4); - _muxer->setListener(getListener()); + _muxer->setMediaListener(getListener()); _muxer->setTrackListener(this); for(auto &track : _demuxer->getTracks(false)){ _muxer->addTrack(track); diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 5b5aa561..fd37904d 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -298,7 +298,7 @@ int RtpProcess::totalReaderCount(){ } void RtpProcess::setListener(const std::weak_ptr &listener){ - _muxer->setListener(listener); + _muxer->setMediaListener(listener); } diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index a43a0688..57913235 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -59,7 +59,7 @@ public: void setListener(const std::weak_ptr &listener) override { RtspMediaSource::setListener(listener); if(_muxer){ - _muxer->setListener(listener); + _muxer->setMediaListener(listener); } } @@ -79,7 +79,7 @@ public: void setProtocolTranslation(bool enableRtmp,bool enableHls,bool enableMP4){ //不重复生成rtsp _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), false, enableRtmp, enableHls, enableMP4); - _muxer->setListener(getListener()); + _muxer->setMediaListener(getListener()); _muxer->setTrackListener(this); for(auto &track : _demuxer->getTracks(false)){ _muxer->addTrack(track);