From fddb6a13caa91bdd5fcd561fb416b5a52ce34986 Mon Sep 17 00:00:00 2001 From: monktan Date: Thu, 16 Dec 2021 20:47:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hls=20=E8=90=BD=E7=9B=98?= =?UTF-8?q?=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 + server/WebHook.cpp | 16 ++++++ src/Common/MultiMediaSourceMuxer.cpp | 51 ++++++++++++++++-- src/Common/MultiMediaSourceMuxer.h | 1 + src/Common/config.cpp | 1 + src/Common/config.h | 4 ++ src/Record/HlsMaker.cpp | 36 +++++++++---- src/Record/HlsMaker.h | 11 +++- src/Record/HlsMakerImp.cpp | 70 +++++++++++++++++++++++-- src/Record/HlsMakerImp.h | 12 ++++- src/Record/HlsRecorderDisk.cpp | 25 +++++++++ src/Record/HlsRecorderDisk.h | 77 ++++++++++++++++++++++++++++ src/Record/Recorder.cpp | 26 ++++++++++ src/Record/Recorder.h | 4 +- 14 files changed, 312 insertions(+), 24 deletions(-) create mode 100644 src/Record/HlsRecorderDisk.cpp create mode 100644 src/Record/HlsRecorderDisk.h diff --git a/conf/config.ini b/conf/config.ini index a92210fa..cb491cd0 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -124,6 +124,8 @@ on_publish=https://127.0.0.1/index/hook/on_publish on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4 # 录制 hls ts 切片完成事件 on_record_ts=https://127.0.0.1/index/hook/on_record_ts +#录制hls切片完成事件,http api接口录制类型为2 +on_record_hls=http://127.0.0.1/index/hook/on_record_hls #rtsp播放鉴权事件,此事件中比对rtsp的用户名密码 on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth #rtsp播放是否开启专属鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权 diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 3e84e5bf..8065e75c 100755 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -43,6 +43,7 @@ const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader"; const string kOnHttpAccess = HOOK_FIELD"on_http_access"; const string kOnServerStarted = HOOK_FIELD"on_server_started"; const string kOnServerKeepalive = HOOK_FIELD"on_server_keepalive"; +const string kOnRecordHls = HOOK_FIELD"on_record_hls"; const string kAdminParams = HOOK_FIELD"admin_params"; const string kAliveInterval = HOOK_FIELD"alive_interval"; @@ -64,6 +65,7 @@ onceToken token([](){ mINI::Instance()[kOnHttpAccess] = ""; mINI::Instance()[kOnServerStarted] = ""; mINI::Instance()[kOnServerKeepalive] = ""; + mINI::Instance()[kOnRecordHls] = ""; mINI::Instance()[kAdminParams] = "secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc"; mINI::Instance()[kAliveInterval] = 30.0; },nullptr); @@ -401,6 +403,20 @@ void installWebHook(){ }); #endif //ENABLE_MP4 +#ifdef ENABLE_HLS + //录制hls文件落盘成功后广播 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordHlsDisk,[](BroadcastRecordHlsDiskArgs){ + GET_CONFIG(bool, hook_enable, Hook::kEnable) + GET_CONFIG(string, hook_record_hls, Hook::kOnRecordHls); + if(!hook_enable || hook_record_hls.empty()){ + return; + } + + //执行hook + do_http_hook(hook_record_hls, getRecordInfo(info), nullptr); + }); +#endif + NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastRecordTs, [](BroadcastRecordTsArgs) { GET_CONFIG(string,hook_record_ts,Hook::kOnRecordTs); if (!hook_enable || hook_record_ts.empty()) { diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index f60829da..7acc5502 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -119,13 +119,16 @@ void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr &list int MultiMediaSourceMuxer::totalReaderCount() const { auto hls = _hls; + auto hls_disk = _hls_disk; auto ret = (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (_ts ? _ts->readerCount() : 0) + #if defined(ENABLE_MP4) (_fmp4 ? _fmp4->readerCount() : 0) + + (_mp4 ? 1 : 0) + #endif - (hls ? hls->readerCount() : 0); + (hls ? hls->readerCount() : 0) + + (hls_disk ? 1 : 0); #if defined(ENABLE_RTPPROXY) return ret + (int)_rtp_sender.size(); @@ -153,6 +156,7 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) { //此函数可能跨线程调用 bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) { + bool ret = false; switch (type) { case Recorder::type_hls : { if (start && !_hls) { @@ -167,7 +171,8 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type //停止录制 _hls = nullptr; } - return true; + ret = true; + goto ret; } case Recorder::type_mp4 : { if (start && !_mp4) { @@ -177,10 +182,28 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type //停止录制 _mp4 = nullptr; } - return true; + ret = true; + goto ret; + } + case Recorder::type_hls_disk: { + if (start && !_hls_disk) { + // 开始录制 + _hls_disk = makeRecorder(sender, getTracks(), type, custom_path, max_second); + } else if (!start && _hls_disk) { + // 停止录制 + _hls_disk = nullptr; + } + ret = true; + goto ret; + } + default : { + ret = false; + goto ret; } - default : return false; } +ret: + MediaSourceEventInterceptor::onReaderChanged(sender, totalReaderCount()); + return ret; } //此函数可能跨线程调用 @@ -190,6 +213,8 @@ bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type return !!_hls; case Recorder::type_mp4 : return !!_mp4; + case Recorder::type_hls_disk: + return !!_hls_disk; default: return false; } @@ -272,10 +297,17 @@ bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) { if (hls) { ret = hls->addTrack(track) ? true : ret; } + auto mp4 = _mp4; if (mp4) { ret = mp4->addTrack(track) ? true : ret; } + + auto hls_disk = _hls_disk; + if (hls_disk) { + ret = hls_disk->addTrack(track) ? true : ret; + } + return ret; } @@ -335,6 +367,11 @@ void MultiMediaSourceMuxer::resetTracks() { if (mp4) { mp4->resetTracks(); } + + auto hls_disk = _hls_disk; + if (hls_disk) { + hls_disk->resetTracks(); + } } //该类实现frame级别的时间戳覆盖 @@ -414,11 +451,17 @@ bool MultiMediaSourceMuxer::onTrackFrame(const Frame::Ptr &frame_in) { if (hls) { ret = hls->inputFrame(frame) ? true : ret; } + auto mp4 = _mp4; if (mp4) { ret = mp4->inputFrame(frame) ? true : ret; } + auto hls_disk = _hls_disk; + if (hls_disk) { + ret = hls_disk->inputFrame(frame) ? true : ret; + } + #if defined(ENABLE_MP4) if (_fmp4) { ret = _fmp4->inputFrame(frame) ? true : ret; diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 70853ea3..f0853f61 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -159,6 +159,7 @@ private: TSMediaSourceMuxer::Ptr _ts; MediaSinkInterface::Ptr _mp4; HlsRecorder::Ptr _hls; + MediaSinkInterface::Ptr _hls_disk; //对象个数统计 ObjectStatistic _statistic; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 98ce831e..328fe52d 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -53,6 +53,7 @@ const string kBroadcastShellLogin = "kBroadcastShellLogin"; const string kBroadcastNotFoundStream = "kBroadcastNotFoundStream"; const string kBroadcastStreamNoneReader = "kBroadcastStreamNoneReader"; const string kBroadcastHttpBeforeAccess = "kBroadcastHttpBeforeAccess"; +const string kBroadcastRecordHlsDisk = "kBroadcastRecordHlsDisk"; } //namespace Broadcast //通用配置项目 diff --git a/src/Common/config.h b/src/Common/config.h index ab35a566..bc814371 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -45,6 +45,10 @@ extern const string kBroadcastRecordMP4; extern const string kBroadcastRecordTs; #define BroadcastRecordTsArgs const RecordInfo &info +//录制hls文件成功后广播 +extern const string kBroadcastRecordHlsDisk; +#define BroadcastRecordHlsDiskArgs const RecordInfo &info + //收到http api请求广播 extern const string kBroadcastHttpRequest; #define BroadcastHttpRequestArgs const Parser &parser,const HttpSession::HttpResponseInvoker &invoker,bool &consumed,SockInfo &sender diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index 84cd1e4d..5832991b 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -9,18 +9,19 @@ */ #include "HlsMaker.h" + namespace mediakit { -HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number) { +HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number, Recorder::type type) { //最小允许设置为0,0个切片代表点播 _seg_number = seg_number; _seg_duration = seg_duration; + _type = type; } HlsMaker::~HlsMaker() { } - void HlsMaker::makeIndexFile(bool eof) { char file_content[1024]; int maxSegmentDuration = 0; @@ -32,7 +33,7 @@ void HlsMaker::makeIndexFile(bool eof) { } } - auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL; + auto sequence = _type == Recorder::type_hls ? ( _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL): 0LL; string m3u8; if (_seg_number == 0) { @@ -57,19 +58,32 @@ void HlsMaker::makeIndexFile(bool eof) { } m3u8.assign(file_content); + string rm3u8 = m3u8; + string rcontent; - for (auto &tp : _seg_dur_list) { + if (_type == Recorder::type_hls) {; + for (auto &tp : _seg_dur_list) { + snprintf(file_content, sizeof(file_content), "#EXTINF:%.3f,\n%s?\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); + m3u8.append(file_content); + } + } else if (_type == Recorder::type_hls_disk) { + auto &tp = _seg_dur_list.back(); snprintf(file_content, sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); - m3u8.append(file_content); + rcontent.assign(file_content); } if (eof) { snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n"); m3u8.append(file_content); + rcontent.append(file_content); } - onWriteHls(m3u8.data(), m3u8.size()); -} + if (_type == Recorder::type_hls) { + onWriteHls(m3u8.data(), m3u8.size()); + } else if (_type == Recorder::type_hls_disk) { + onWriteRecordM3u8(rm3u8.data(), rm3u8.size(), rcontent.data(), rcontent.size()); + } +} void HlsMaker::inputData(void *data, size_t len, uint32_t timestamp, bool is_idr_fast_packet) { if (data && len) { @@ -89,7 +103,7 @@ void HlsMaker::inputData(void *data, size_t len, uint32_t timestamp, bool is_idr } void HlsMaker::delOldSegment() { - if (_seg_number == 0) { + if (_seg_number == 0 || _type == Recorder::type_hls_disk) { //如果设置为保留0个切片,则认为是保存为点播 return; } @@ -112,7 +126,7 @@ void HlsMaker::addNewSegment(uint32_t stamp) { } //关闭并保存上一个切片,如果_seg_number==0,那么是点播。 - flushLastSegment(false); + flushLastSegment(_seg_number == 0 || _type == Recorder::type_hls_disk); //新增切片 _last_file_name = onOpenSegment(_file_index++); //记录本次切片的起始时间戳 @@ -129,7 +143,7 @@ void HlsMaker::flushLastSegment(bool eof){ if (seg_dur <= 0) { seg_dur = 100; } - _seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name))); + _seg_dur_list.emplace_back(seg_dur, std::move(_last_file_name)); _last_file_name.clear(); delOldSegment(); makeIndexFile(eof); @@ -137,7 +151,7 @@ void HlsMaker::flushLastSegment(bool eof){ } bool HlsMaker::isLive() { - return _seg_number != 0; + return _seg_number != 0 && _type == Recorder::type_hls; } void HlsMaker::clear() { diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index dcd96c4a..1f9e217b 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -18,6 +18,8 @@ #include "Util/File.h" #include "Util/util.h" #include "Util/logger.h" +#include "Recorder.h" + using namespace toolkit; namespace mediakit { @@ -28,7 +30,7 @@ public: * @param seg_duration 切片文件长度 * @param seg_number 切片个数 */ - HlsMaker(float seg_duration = 5, uint32_t seg_number = 3); + HlsMaker(float seg_duration = 5, uint32_t seg_number = 3, Recorder::type type = Recorder::type_hls); virtual ~HlsMaker(); /** @@ -78,6 +80,12 @@ protected: */ virtual void onWriteHls(const char *data, size_t len) = 0; + /** + * 写m3u8文件回调,hls落盘使用 + * @param data + * @param len + */ + virtual void onWriteRecordM3u8(const char *header, size_t hlen, const char *body, size_t blen) = 0; /** * 上一个 ts 切片写入完成, 可在这里进行通知处理 * @param duration_ms 上一个 ts 切片的时长, 单位为毫秒 @@ -116,6 +124,7 @@ private: uint64_t _file_index = 0; string _last_file_name; std::deque > _seg_dur_list; + Recorder::type _type{Recorder::type_hls}; }; }//namespace mediakit diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index 569dbac0..b98373e4 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -22,7 +22,8 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file, const string ¶ms, uint32_t bufSize, float seg_duration, - uint32_t seg_number) : HlsMaker(seg_duration, seg_number) { + uint32_t seg_number, + Recorder::type type) : HlsMaker(seg_duration, seg_number, type) { _poller = EventPollerPool::Instance().getPoller(); _path_prefix = m3u8_file.substr(0, m3u8_file.rfind('/')); _path_hls = m3u8_file; @@ -33,16 +34,37 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file, }); _info.folder = _path_prefix; + + _start_time = ::time(nullptr); + _type = type; } HlsMakerImp::~HlsMakerImp() { - clearCache(false); + clearCache(false, false); } -void HlsMakerImp::clearCache(bool immediately) { +void HlsMakerImp::clearCache(bool immediately, bool first) { //录制完了 flushLastSegment(true); if (!isLive()) { + if (first) return; //第一次创建清除cache不需要上报 + + //hook接口,hls落盘录制,触发hook + auto info = _info; + if (_media_src) { + info.app = _media_src.get()->getApp(); + info.stream = _media_src.get()->getId(); + info.vhost = _media_src.get()->getVhost(); + info.file_path = _path_hls; + info.start_time = _start_time; + info.time_len = ::time(nullptr) - _start_time; + info.folder = _info.folder; + info.file_name = _path_hls; + info.url = _path_hls; + info.file_size = 0; + } + + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordHlsDisk, info); return; } @@ -115,13 +137,13 @@ void HlsMakerImp::onWriteHls(const char *data, size_t len) { if (hls) { fwrite(data, len, 1, hls.get()); hls.reset(); - if (_media_src) { + // 只有直播才注册 + if (_media_src && _type == Recorder::type_hls) { _media_src->registHls(true); } } else { WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg(); } - //DebugL << "\r\n" << string(data,len); } void HlsMakerImp::onFlushLastSegment(uint32_t duration_ms) { @@ -161,4 +183,42 @@ HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const { return _media_src; } +void HlsMakerImp::onWriteRecordM3u8(const char *header, size_t hlen, const char *body, size_t blen) { + bool exist = true; + string mode = "rb+"; + if (access(_path_hls.c_str(), 0) == -1) { + exist = false; + mode = "wb+"; + } + + auto hls_file = makeRecordM3u8(_path_hls, mode); + if (hls_file) { + fwrite(header, hlen, 1, hls_file.get()); + if (exist) { + fseek(hls_file.get(), -15L, SEEK_END); + } + + fwrite(body, blen,1, hls_file.get()); + hls_file.reset(); + if(_media_src && _type == Recorder::type_hls){ + _media_src->registHls(true); + } + } else { + WarnL << "create hls_file file failed, " << _path_hls << " " << get_uv_errmsg(); + } +} + +std::shared_ptr HlsMakerImp::makeRecordM3u8(const string &file, const string &mode, bool setbuf) { + auto file_buf = _file_buf; + auto ret= shared_ptr(File::create_file(file.data(), mode.data()), [file_buf](FILE *fp) { + if (fp) { + fclose(fp); + } + }); + if (ret && setbuf) { + setvbuf(ret.get(), _file_buf.get(), _IOFBF, _buf_size); + } + return ret; +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index 93d73356..bc6ced2e 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -27,7 +27,8 @@ public: const string ¶ms, uint32_t bufSize = 64 * 1024, float seg_duration = 5, - uint32_t seg_number = 3); + uint32_t seg_number = 3, + Recorder::type type = Recorder::type_hls); ~HlsMakerImp() override; @@ -49,17 +50,21 @@ public: * 清空缓存 * @param immediately 时候立即删除 */ - void clearCache(bool immediately = true); + void clearCache(bool immediately = true, bool first = false); protected: string onOpenSegment(uint64_t index) override ; void onDelSegment(uint64_t index) override; void onWriteSegment(const char *data, size_t len) override; void onWriteHls(const char *data, size_t len) override; + // hls 落盘使用 + void onWriteRecordM3u8(const char *header, size_t hlen, const char *body, size_t blen) override; void onFlushLastSegment(uint32_t duration_ms) override; private: std::shared_ptr makeFile(const string &file,bool setbuf = false); + // hls 落盘使用 + std::shared_ptr makeRecordM3u8(const string &file, const string &mode, bool setbuf = false); private: int _buf_size; @@ -72,6 +77,9 @@ private: HlsMediaSource::Ptr _media_src; EventPoller::Ptr _poller; map _segment_file_paths; + + time_t _start_time {0}; + Recorder::type _type{Recorder::type_hls}; }; }//namespace mediakit diff --git a/src/Record/HlsRecorderDisk.cpp b/src/Record/HlsRecorderDisk.cpp new file mode 100644 index 00000000..73bd4867 --- /dev/null +++ b/src/Record/HlsRecorderDisk.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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 "HlsRecorderDisk.h" + +namespace mediakit { + + +void HlsRecorderDisk::resetTracks() { + TsMuxer::resetTracks(); +} + +bool HlsRecorderDisk::addTrack(const Track::Ptr & track) { + return TsMuxer::addTrack(track); +} + +}//namespace mediakit + diff --git a/src/Record/HlsRecorderDisk.h b/src/Record/HlsRecorderDisk.h new file mode 100644 index 00000000..c2961a8b --- /dev/null +++ b/src/Record/HlsRecorderDisk.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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. + */ + +#ifndef HLSRECORDER_DISK_H +#define HLSRECORDER_DISK_H + +#include "Common/MediaSink.h" +#include "HlsMakerImp.h" +#include "TsMuxer.h" + +namespace mediakit { + +class HlsRecorderDisk : public TsMuxer, public std::enable_shared_from_this { +public: + typedef std::shared_ptr Ptr; + HlsRecorderDisk(const string &m3u8_file, const string ¶ms){ + GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum); + GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize); + GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration); + _hls = std::make_shared(m3u8_file, params, hlsBufSize, hlsDuration, + hlsNum, Recorder::type_hls_disk); + //清空上次的残余文件 + _hls->clearCache(false, true); + } + + ~HlsRecorderDisk() override= default; + + /** + * 重置所有Track + */ + void resetTracks() override; + + /** + * 输入frame + */ + //bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 添加ready状态的track + */ + bool addTrack(const Track::Ptr & track) override; + + void setMediaSource(const string &vhost, const string &app, const string &stream_id) { + _hls->setMediaSource(vhost, app, stream_id); + } + + bool inputFrame(const Frame::Ptr &frame) override {; + if (_clear_cache) { + _clear_cache = false; + _hls->clearCache(); + } + + return TsMuxer::inputFrame(frame); + } + +private: + void onTs(std::shared_ptr buffer, uint32_t timestamp, bool is_idr_fast_packet) override { + if (!buffer) { + _hls->inputData(nullptr, 0, timestamp, is_idr_fast_packet); + } else { + _hls->inputData(buffer->data(), buffer->size(), timestamp, is_idr_fast_packet); + } + } + +private: + bool _clear_cache = false; + std::shared_ptr _hls; +}; +}//namespace mediakit +#endif //HLSRECORDER_DISK_H diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index cba7b319..1b962b61 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -13,6 +13,7 @@ #include "Common/MediaSource.h" #include "MP4Recorder.h" #include "HlsRecorder.h" +#include "HlsRecorderDisk.h" using namespace toolkit; @@ -50,6 +51,21 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s } return File::absolutePath(mp4FilePath, recordPath); } + case Recorder::type_hls_disk: { + GET_CONFIG(string, hlsPath, Record::kFilePath); + string m3u8FilePath; + if (enableVhost) { + m3u8FilePath = "hls_record/" + vhost + "/" + app + "/" + stream_id + "/record.m3u8"; + } else { + m3u8FilePath = "hls_record/" + app + "/" + stream_id + "/record.m3u8"; + } + //Here we use the customized file path. + if (!customized_path.empty()) { + m3u8FilePath = "/" + stream_id + "/record.m3u8"; + return File::absolutePath(m3u8FilePath, customized_path); + } + return File::absolutePath(m3u8FilePath, hlsPath); + } default: return ""; } @@ -77,6 +93,16 @@ std::shared_ptr Recorder::createRecorder(type type, const st throw std::invalid_argument("mp4相关功能未打开,请开启ENABLE_MP4宏后编译再测试"); #endif } + case Recorder::type_hls_disk: { +#if defined(ENABLE_HLS) + GET_CONFIG(bool, enable_vhost, General::kEnableVhost); + auto ret = std::make_shared(path, enable_vhost ? string(VHOST_KEY) + "=" + vhost : ""); + ret->setMediaSource(vhost, app, stream_id); + return ret; +#else + throw std::invalid_argument("hls相关功能未打开,请开启ENABLE_HLS宏后编译再测试"); +#endif + } default: throw std::invalid_argument("未知的录制类型"); } diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index ade8654c..f4acb4b3 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -36,7 +36,9 @@ public: // 录制hls type_hls = 0, // 录制MP4 - type_mp4 = 1 + type_mp4 = 1, + // 录制hls落盘 + type_hls_disk = 2, } type; /**