mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 10:40:05 +08:00
添加hls 落盘录制
This commit is contained in:
parent
b3dd440151
commit
fddb6a13ca
@ -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方式鉴权
|
||||
|
@ -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()) {
|
||||
|
@ -119,13 +119,16 @@ void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr<Listener> &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;
|
||||
|
@ -159,6 +159,7 @@ private:
|
||||
TSMediaSourceMuxer::Ptr _ts;
|
||||
MediaSinkInterface::Ptr _mp4;
|
||||
HlsRecorder::Ptr _hls;
|
||||
MediaSinkInterface::Ptr _hls_disk;
|
||||
|
||||
//对象个数统计
|
||||
ObjectStatistic<MultiMediaSourceMuxer> _statistic;
|
||||
|
@ -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
|
||||
|
||||
//通用配置项目
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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<tuple<int,string> > _seg_dur_list;
|
||||
Recorder::type _type{Recorder::type_hls};
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
@ -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<FILE> HlsMakerImp::makeRecordM3u8(const string &file, const string &mode, bool setbuf) {
|
||||
auto file_buf = _file_buf;
|
||||
auto ret= shared_ptr<FILE>(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
|
@ -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<FILE> makeFile(const string &file,bool setbuf = false);
|
||||
// hls 落盘使用
|
||||
std::shared_ptr<FILE> 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<uint64_t/*index*/,string/*file_path*/> _segment_file_paths;
|
||||
|
||||
time_t _start_time {0};
|
||||
Recorder::type _type{Recorder::type_hls};
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
25
src/Record/HlsRecorderDisk.cpp
Normal file
25
src/Record/HlsRecorderDisk.cpp
Normal file
@ -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
|
||||
|
77
src/Record/HlsRecorderDisk.h
Normal file
77
src/Record/HlsRecorderDisk.h
Normal file
@ -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<HlsRecorderDisk> {
|
||||
public:
|
||||
typedef std::shared_ptr<HlsRecorderDisk> 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<HlsMakerImp>(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> 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<HlsMakerImp> _hls;
|
||||
};
|
||||
}//namespace mediakit
|
||||
#endif //HLSRECORDER_DISK_H
|
@ -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<MediaSinkInterface> 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<HlsRecorderDisk>(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("未知的录制类型");
|
||||
}
|
||||
|
@ -36,7 +36,9 @@ public:
|
||||
// 录制hls
|
||||
type_hls = 0,
|
||||
// 录制MP4
|
||||
type_mp4 = 1
|
||||
type_mp4 = 1,
|
||||
// 录制hls落盘
|
||||
type_hls_disk = 2,
|
||||
} type;
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user