diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index 5b1b8b71..2653fd50 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -63,9 +63,10 @@ API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, co * @param app 应用名 * @param stream 流id * @param customized_path 录像文件保存自定义目录,默认为空或null则自动生成 + * @param max_second mp4录制最大切片时间,单位秒,置0则采用配置文件配置 * @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); +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream, const char *customized_path, size_t max_second); /** * 停止录制 diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index 550179ec..ea0abe4c 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -41,9 +41,9 @@ API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, co 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){ +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,const char *customized_path, size_t max_second){ assert(vhost && app && stream); - return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path ? customized_path : ""); + return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path ? customized_path : "", max_second); } API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index a06d22fb..75d9cbb9 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -818,7 +818,14 @@ { "key": "customized_path", "value": null, + "disabled": true, "description": "录像文件保存自定义根目录,为空则采用配置文件设置" + }, + { + "key": "max_second", + "value": "1000", + "disabled": true, + "description": "MP4录制的切片时间大小,单位秒" } ] } diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 145787b1..ac0c1a26 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -280,10 +280,10 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) { setDelegate(listener); src->setListener(shared_from_this()); if (_enable_hls) { - src->setupRecord(Recorder::type_hls, true, ""); + src->setupRecord(Recorder::type_hls, true, "", 0); } if (_enable_mp4) { - src->setupRecord(Recorder::type_mp4, true, ""); + src->setupRecord(Recorder::type_mp4, true, "", 0); } } } diff --git a/server/WebApi.cpp b/server/WebApi.cpp index b29a136c..39ecb6b1 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -877,10 +877,11 @@ void installWebApi() { CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); auto result = Recorder::startRecord((Recorder::type) allArgs["type"].as(), - allArgs["vhost"], - allArgs["app"], - allArgs["stream"], - allArgs["customized_path"]); + allArgs["vhost"], + allArgs["app"], + allArgs["stream"], + allArgs["customized_path"], + allArgs["max_second"].as()); val["result"] = result; val["code"] = result ? API::Success : API::OtherFailed; val["msg"] = result ? "success" : "start record failed"; diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index a783f29d..ae4a47c8 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -171,13 +171,13 @@ void MediaSource::onReaderChanged(int size) { } } -bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path){ +bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path, size_t max_second){ auto listener = _listener.lock(); if (!listener) { WarnL << "未设置MediaSource的事件监听者,setupRecord失败:" << getSchema() << "/" << getVhost() << "/" << getApp() << "/" << getId(); return false; } - return listener->setupRecord(*this, type, start, custom_path); + return listener->setupRecord(*this, type, start, custom_path, max_second); } bool MediaSource::isRecording(Recorder::type type){ @@ -626,12 +626,12 @@ void MediaSourceEventInterceptor::onRegist(MediaSource &sender, bool regist) { } } -bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { +bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) { auto listener = _listener.lock(); if (!listener) { return false; } - return listener->setupRecord(sender, type, start, custom_path); + return listener->setupRecord(sender, type, start, custom_path, max_second); } bool MediaSourceEventInterceptor::isRecording(MediaSource &sender, Recorder::type type) { diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 029b5f87..b85415fc 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -77,7 +77,7 @@ public: ////////////////////////仅供MultiMediaSourceMuxer对象继承//////////////////////// // 开启或关闭录制 - virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { return false; }; + virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) { return false; }; // 获取录制状态 virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }; // 获取所有track相关信息 @@ -109,7 +109,7 @@ public: int totalReaderCount(MediaSource &sender) override; void onReaderChanged(MediaSource &sender, int size) override; void onRegist(MediaSource &sender, bool regist) override; - bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) override; + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) override; bool isRecording(MediaSource &sender, Recorder::type type) override; vector getTracks(MediaSource &sender, bool trackReady = true) const override; void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, const string &ssrc, bool is_udp, uint16_t src_port, const function &cb) override; @@ -252,7 +252,7 @@ public: // 该流观看人数变化 void onReaderChanged(int size); // 开启或关闭录制 - bool setupRecord(Recorder::type type, bool start, const string &custom_path); + bool setupRecord(Recorder::type type, bool start, const string &custom_path, size_t max_second); // 获取录制状态 bool isRecording(Recorder::type type); // 开始发送ps-rtp diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index f176e88e..1a86083d 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -107,8 +107,8 @@ int MultiMuxerPrivate::totalReaderCount() const { (hls ? hls->readerCount() : 0); } -static std::shared_ptr makeRecorder(const vector &tracks, Recorder::type type, const string &custom_path, MediaSource &sender){ - auto recorder = Recorder::createRecorder(type, sender.getVhost(), sender.getApp(), sender.getId(), custom_path); +static std::shared_ptr makeRecorder(MediaSource &sender, const vector &tracks, Recorder::type type, const string &custom_path, size_t max_second){ + auto recorder = Recorder::createRecorder(type, sender.getVhost(), sender.getApp(), sender.getId(), custom_path, max_second); for (auto &track : tracks) { recorder->addTrack(track); } @@ -116,12 +116,12 @@ static std::shared_ptr makeRecorder(const vector } //此函数可能跨线程调用 -bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path){ +bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second){ switch (type) { case Recorder::type_hls : { if (start && !_hls) { //开始录制 - auto hls = dynamic_pointer_cast(makeRecorder(getTracks(true), type, custom_path, sender)); + auto hls = dynamic_pointer_cast(makeRecorder(sender, getTracks(true), type, custom_path, max_second)); if (hls) { //设置HlsMediaSource的事件监听器 hls->setListener(_listener); @@ -136,7 +136,7 @@ bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bo case Recorder::type_mp4 : { if (start && !_mp4) { //开始录制 - _mp4 = makeRecorder(getTracks(true), type, custom_path, sender); + _mp4 = makeRecorder(sender, getTracks(true), type, custom_path, max_second); } else if (!start && _mp4) { //停止录制 _mp4 = nullptr; @@ -326,8 +326,8 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) { 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::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) { + return _muxer->setupRecord(sender, type, start, custom_path, max_second); } bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) { diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 422c9e30..e37cde9f 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -44,7 +44,7 @@ private: 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 setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second); bool isRecording(MediaSource &sender, Recorder::type type); bool isEnabled(); void onTrackReady(const Track::Ptr & track) override; @@ -125,7 +125,7 @@ public: * @param custom_path 开启录制时,指定自定义路径 * @return 是否设置成功 */ - bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) override; + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) override; /** * 获取录制状态 diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index c357d047..6b33cd7b 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -20,16 +20,19 @@ using namespace toolkit; namespace mediakit { -MP4Recorder::MP4Recorder(const string& strPath, - const string &strVhost, - const string &strApp, - const string &strStreamId) { +MP4Recorder::MP4Recorder(const string &strPath, + const string &strVhost, + const string &strApp, + const string &strStreamId, + size_t max_second) { _strPath = strPath; /////record 业务逻辑////// _info.app = strApp; _info.stream = strStreamId; _info.vhost = strVhost; _info.folder = strPath; + GET_CONFIG(size_t ,recordSec,Record::kFileSecond); + _max_second = max_second ? max_second : recordSec; } MP4Recorder::~MP4Recorder() { closeFile(); @@ -104,8 +107,7 @@ void MP4Recorder::closeFile() { } void MP4Recorder::inputFrame(const Frame::Ptr &frame) { - GET_CONFIG(uint32_t,recordSec,Record::kFileSecond); - if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) && + if(!_muxer || ((_createFileTicker.elapsedTime() > _max_second * 1000) && (!_haveVideo || (_haveVideo && frame->keyFrame()))) ){ //成立条件 //1、_muxer为空 diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index cf862889..dfeac3d7 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -33,7 +33,8 @@ public: MP4Recorder(const string &strPath, const string &strVhost, const string &strApp, - const string &strStreamId); + const string &strStreamId, + size_t max_second); virtual ~MP4Recorder(); /** @@ -55,12 +56,13 @@ private: void closeFile(); void asyncClose(); private: + bool _haveVideo = false; + size_t _max_second; string _strPath; string _strFile; string _strFileTmp; Ticker _createFileTicker; RecordInfo _info; - bool _haveVideo = false; MP4Muxer::Ptr _muxer; list _tracks; }; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index c281e228..cba7b319 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -55,7 +55,7 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s } } -std::shared_ptr Recorder::createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path){ +std::shared_ptr Recorder::createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path, size_t max_second){ auto path = Recorder::getRecordPath(type, vhost, app, stream_id, customized_path); switch (type) { case Recorder::type_hls: { @@ -72,7 +72,7 @@ std::shared_ptr Recorder::createRecorder(type type, const st case Recorder::type_mp4: { #if defined(ENABLE_MP4) - return std::make_shared(path, vhost, app, stream_id); + return std::make_shared(path, vhost, app, stream_id, max_second); #else throw std::invalid_argument("mp4相关功能未打开,请开启ENABLE_MP4宏后编译再测试"); #endif @@ -90,13 +90,13 @@ bool Recorder::isRecording(type type, const string &vhost, const string &app, co return src->isRecording(type); } -bool Recorder::startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path){ +bool Recorder::startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path, size_t max_second){ auto src = MediaSource::find(vhost, app, stream_id); if (!src) { WarnL << "未找到相关的MediaSource,startRecord失败:" << vhost << "/" << app << "/" << stream_id; return false; } - return src->setupRecord(type, true, customized_path); + return src->setupRecord(type, true, customized_path, max_second); } bool Recorder::stopRecord(type type, const string &vhost, const string &app, const string &stream_id){ @@ -104,7 +104,7 @@ bool Recorder::stopRecord(type type, const string &vhost, const string &app, con if(!src){ return false; } - return src->setupRecord(type, false, ""); + return src->setupRecord(type, false, "", 0); } } /* namespace mediakit */ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index d535ae1a..ade8654c 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -57,9 +57,10 @@ public: * @param app 应用名 * @param stream_id 流id * @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置 + * @param max_second mp4录制最大切片时间,单位秒,置0则采用配置文件配置 * @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 = "", size_t max_second = 0); /** * 获取录制状态 @@ -80,7 +81,7 @@ public: * @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置 * @return 成功与否 */ - static bool startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path); + static bool startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path, size_t max_second); /** * 停止录制