mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-24 19:50:38 +08:00
同时主要优化点包括: 1、编译宏特性开关优化。 2、转协议复用器相关创建代码移动至Recorder类。 3、转协议复用器onAllTrackReady函数修改为addTrackCompleted。 4、startRecord/stopRecord/isRecording接口新增支持ts/fmp4/hls-fmp4协议。 Co-authored-by: xia-chu <771730766@qq.com> Co-authored-by: linxiaoyan87 <linxiaoyan87@foxmail.com>
This commit is contained in:
parent
6aa4b741a3
commit
cb0579a16d
@ -47,44 +47,44 @@ set(MediaServer_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/media-server")
|
||||
# TODO: 补一个函数处理各种库
|
||||
|
||||
# 添加 mov、flv 库用于 MP4 录制
|
||||
if(ENABLE_MP4)
|
||||
message(STATUS "ENABLE_MP4 defined")
|
||||
|
||||
if (ENABLE_MP4 OR ENABLE_HLS_FMP4)
|
||||
# MOV
|
||||
set(MediaServer_MOV_ROOT ${MediaServer_ROOT}/libmov)
|
||||
aux_source_directory(${MediaServer_MOV_ROOT}/include MOV_SRC_LIST)
|
||||
aux_source_directory(${MediaServer_MOV_ROOT}/source MOV_SRC_LIST)
|
||||
aux_source_directory(${MediaServer_MOV_ROOT}/source MOV_SRC_LIST)
|
||||
add_library(mov STATIC ${MOV_SRC_LIST})
|
||||
add_library(MediaServer::mov ALIAS mov)
|
||||
target_compile_definitions(mov
|
||||
PUBLIC -DENABLE_MP4)
|
||||
target_compile_options(mov
|
||||
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
|
||||
target_compile_options(mov PRIVATE ${COMPILE_OPTIONS_DEFAULT})
|
||||
target_include_directories(mov
|
||||
PRIVATE
|
||||
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>"
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>")
|
||||
PRIVATE
|
||||
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>"
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>")
|
||||
|
||||
# FLV
|
||||
set(MediaServer_FLV_ROOT ${MediaServer_ROOT}/libflv)
|
||||
aux_source_directory(${MediaServer_FLV_ROOT}/include FLV_SRC_LIST)
|
||||
aux_source_directory(${MediaServer_FLV_ROOT}/source FLV_SRC_LIST)
|
||||
aux_source_directory(${MediaServer_FLV_ROOT}/source FLV_SRC_LIST)
|
||||
add_library(flv STATIC ${FLV_SRC_LIST})
|
||||
add_library(MediaServer::flv ALIAS flv)
|
||||
target_compile_options(flv
|
||||
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
|
||||
target_compile_options(flv PRIVATE ${COMPILE_OPTIONS_DEFAULT})
|
||||
target_include_directories(flv
|
||||
PRIVATE
|
||||
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>"
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>")
|
||||
PRIVATE
|
||||
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>"
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>")
|
||||
|
||||
update_cached_list(MK_LINK_LIBRARIES
|
||||
MediaServer::flv MediaServer::mov)
|
||||
update_cached_list(MK_COMPILE_DEFINITIONS
|
||||
ENABLE_MP4)
|
||||
endif()
|
||||
update_cached_list(MK_LINK_LIBRARIES MediaServer::flv MediaServer::mov)
|
||||
|
||||
if (ENABLE_MP4)
|
||||
message(STATUS "ENABLE_MP4 defined")
|
||||
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_MP4)
|
||||
endif ()
|
||||
if (ENABLE_HLS_FMP4)
|
||||
message(STATUS "ENABLE_HLS_FMP4 defined")
|
||||
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_HLS_FMP4)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# 添加 mpeg 用于支持 ts 生成
|
||||
if(ENABLE_RTPPROXY OR ENABLE_HLS)
|
||||
@ -108,9 +108,11 @@ if(ENABLE_RTPPROXY OR ENABLE_HLS)
|
||||
|
||||
update_cached_list(MK_LINK_LIBRARIES MediaServer::mpeg)
|
||||
if(ENABLE_RTPPROXY)
|
||||
message(STATUS "ENABLE_RTPPROXY defined")
|
||||
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_RTPPROXY)
|
||||
endif()
|
||||
if(ENABLE_HLS)
|
||||
message(STATUS "ENABLE_HLS defined")
|
||||
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_HLS)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -41,6 +41,7 @@ option(ENABLE_HLS "Enable HLS" ON)
|
||||
option(ENABLE_JEMALLOC_STATIC "Enable static linking to the jemalloc library" OFF)
|
||||
option(ENABLE_MEM_DEBUG "Enable Memory Debug" OFF)
|
||||
option(ENABLE_MP4 "Enable MP4" ON)
|
||||
option(ENABLE_HLS_FMP4 "Enable HLS-FMP4" ON)
|
||||
option(ENABLE_MSVC_MT "Enable MSVC Mt/Mtd lib" ON)
|
||||
option(ENABLE_MYSQL "Enable MySQL" OFF)
|
||||
option(ENABLE_OPENSSL "Enable OpenSSL" ON)
|
||||
|
@ -45,8 +45,10 @@ add_mute_audio=1
|
||||
#此参数不应大于播放器超时时间;单位毫秒
|
||||
continue_push_ms=15000
|
||||
|
||||
#是否开启转换为hls
|
||||
#是否开启转换为hls(mpegts)
|
||||
enable_hls=1
|
||||
#是否开启转换为hls(fmp4)
|
||||
enable_hls_fmp4=0
|
||||
#是否开启MP4录制
|
||||
enable_mp4=0
|
||||
#是否开启转换为rtsp/webrtc
|
||||
@ -124,7 +126,7 @@ segDur=2
|
||||
segNum=3
|
||||
#HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数
|
||||
segRetain=5
|
||||
#是否广播 ts 切片完成通知
|
||||
#是否广播 hls切片(ts/fmp4)完成通知(on_record_ts)
|
||||
broadcastRecordTs=0
|
||||
#直播hls文件删除延时,单位秒,issue: #913
|
||||
deleteDelaySec=10
|
||||
@ -150,7 +152,7 @@ on_play=https://127.0.0.1/index/hook/on_play
|
||||
on_publish=https://127.0.0.1/index/hook/on_publish
|
||||
#录制mp4切片完成事件
|
||||
on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4
|
||||
# 录制 hls ts 切片完成事件
|
||||
# 录制 hls ts(或fmp4) 切片完成事件
|
||||
on_record_ts=https://127.0.0.1/index/hook/on_record_ts
|
||||
#rtsp播放鉴权事件,此事件中比对rtsp的用户名密码
|
||||
on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth
|
||||
|
@ -59,6 +59,7 @@ ProtocolOption::ProtocolOption() {
|
||||
GET_CONFIG(uint32_t, s_continue_push_ms, Protocol::kContinuePushMS);
|
||||
|
||||
GET_CONFIG(bool, s_enable_hls, Protocol::kEnableHls);
|
||||
GET_CONFIG(bool, s_enable_hls_fmp4, Protocol::kEnableHlsFmp4);
|
||||
GET_CONFIG(bool, s_enable_mp4, Protocol::kEnableMP4);
|
||||
GET_CONFIG(bool, s_enable_rtsp, Protocol::kEnableRtsp);
|
||||
GET_CONFIG(bool, s_enable_rtmp, Protocol::kEnableRtmp);
|
||||
@ -83,6 +84,7 @@ ProtocolOption::ProtocolOption() {
|
||||
continue_push_ms = s_continue_push_ms;
|
||||
|
||||
enable_hls = s_enable_hls;
|
||||
enable_hls_fmp4 = s_enable_hls_fmp4;
|
||||
enable_mp4 = s_enable_mp4;
|
||||
enable_rtsp = s_enable_rtsp;
|
||||
enable_rtmp = s_enable_rtmp;
|
||||
|
@ -151,8 +151,10 @@ public:
|
||||
//断连续推延时,单位毫秒,默认采用配置文件
|
||||
uint32_t continue_push_ms;
|
||||
|
||||
//是否开启转换为hls
|
||||
//是否开启转换为hls(mpegts)
|
||||
bool enable_hls;
|
||||
//是否开启转换为hls(fmp4)
|
||||
bool enable_hls_fmp4;
|
||||
//是否开启MP4录制
|
||||
bool enable_mp4;
|
||||
//是否开启转换为rtsp/webrtc
|
||||
@ -194,6 +196,7 @@ public:
|
||||
GET_OPT_VALUE(continue_push_ms);
|
||||
|
||||
GET_OPT_VALUE(enable_hls);
|
||||
GET_OPT_VALUE(enable_hls_fmp4);
|
||||
GET_OPT_VALUE(enable_mp4);
|
||||
GET_OPT_VALUE(enable_rtsp);
|
||||
GET_OPT_VALUE(enable_rtmp);
|
||||
|
@ -37,6 +37,7 @@ static std::shared_ptr<MediaSinkInterface> makeRecorder(MediaSource &sender, con
|
||||
for (auto &track : tracks) {
|
||||
recorder->addTrack(track);
|
||||
}
|
||||
recorder->addTrackCompleted();
|
||||
return recorder;
|
||||
}
|
||||
|
||||
@ -97,17 +98,18 @@ MultiMediaSourceMuxer::MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_
|
||||
if (option.enable_hls) {
|
||||
_hls = dynamic_pointer_cast<HlsRecorder>(Recorder::createRecorder(Recorder::type_hls, _tuple, option));
|
||||
}
|
||||
if (option.enable_hls_fmp4) {
|
||||
_hls_fmp4 = dynamic_pointer_cast<HlsFMP4Recorder>(Recorder::createRecorder(Recorder::type_hls_fmp4, _tuple, option));
|
||||
}
|
||||
if (option.enable_mp4) {
|
||||
_mp4 = Recorder::createRecorder(Recorder::type_mp4, _tuple, option);
|
||||
}
|
||||
if (option.enable_ts) {
|
||||
_ts = std::make_shared<TSMediaSourceMuxer>(_tuple, option);
|
||||
_ts = dynamic_pointer_cast<TSMediaSourceMuxer>(Recorder::createRecorder(Recorder::type_ts, _tuple, option));
|
||||
}
|
||||
#if defined(ENABLE_MP4)
|
||||
if (option.enable_fmp4) {
|
||||
_fmp4 = std::make_shared<FMP4MediaSourceMuxer>(_tuple, option);
|
||||
_fmp4 = dynamic_pointer_cast<FMP4MediaSourceMuxer>(Recorder::createRecorder(Recorder::type_fmp4, _tuple, option));
|
||||
}
|
||||
#endif
|
||||
|
||||
//音频相关设置
|
||||
enableAudio(option.enable_audio);
|
||||
@ -128,14 +130,14 @@ void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr<MediaSourceEven
|
||||
if (_ts) {
|
||||
_ts->setListener(self);
|
||||
}
|
||||
#if defined(ENABLE_MP4)
|
||||
if (_fmp4) {
|
||||
_fmp4->setListener(self);
|
||||
}
|
||||
#endif
|
||||
auto hls = _hls;
|
||||
if (hls) {
|
||||
hls->setListener(self);
|
||||
if (_hls_fmp4) {
|
||||
_hls_fmp4->setListener(self);
|
||||
}
|
||||
if (_hls) {
|
||||
_hls->setListener(self);
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,15 +146,13 @@ void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr<Listener> &list
|
||||
}
|
||||
|
||||
int MultiMediaSourceMuxer::totalReaderCount() const {
|
||||
auto hls = _hls;
|
||||
return (_rtsp ? _rtsp->readerCount() : 0) +
|
||||
(_rtmp ? _rtmp->readerCount() : 0) +
|
||||
(_ts ? _ts->readerCount() : 0) +
|
||||
#if defined(ENABLE_MP4)
|
||||
(_fmp4 ? _fmp4->readerCount() : 0) +
|
||||
#endif
|
||||
(_mp4 ? _option.mp4_as_player : 0) +
|
||||
(hls ? hls->readerCount() : 0) +
|
||||
(_hls ? _hls->readerCount() : 0) +
|
||||
(_hls_fmp4 ? _hls_fmp4->readerCount() : 0) +
|
||||
(_ring ? _ring->readerCount() : 0);
|
||||
}
|
||||
|
||||
@ -180,6 +180,7 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
|
||||
|
||||
//此函数可能跨线程调用
|
||||
bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) {
|
||||
CHECK(getOwnerPoller(MediaSource::NullMediaSource())->isCurrentThread(), "Can only call setupRecord in it's owner poller");
|
||||
onceToken token(nullptr, [&]() {
|
||||
if (_option.mp4_as_player && type == Recorder::type_mp4) {
|
||||
//开启关闭mp4录制,触发观看人数变化相关事件
|
||||
@ -215,19 +216,59 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Recorder::type_hls_fmp4: {
|
||||
if (start && !_hls_fmp4) {
|
||||
//开始录制
|
||||
_option.hls_save_path = custom_path;
|
||||
auto hls = dynamic_pointer_cast<HlsFMP4Recorder>(makeRecorder(sender, getTracks(), type, _option));
|
||||
if (hls) {
|
||||
//设置HlsMediaSource的事件监听器
|
||||
hls->setListener(shared_from_this());
|
||||
}
|
||||
_hls_fmp4 = hls;
|
||||
} else if (!start && _hls_fmp4) {
|
||||
//停止录制
|
||||
_hls_fmp4 = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Recorder::type_fmp4: {
|
||||
if (start && !_fmp4) {
|
||||
auto fmp4 = dynamic_pointer_cast<FMP4MediaSourceMuxer>(makeRecorder(sender, getTracks(), type, _option));
|
||||
if (fmp4) {
|
||||
fmp4->setListener(shared_from_this());
|
||||
}
|
||||
_fmp4 = fmp4;
|
||||
} else if (!start && _fmp4) {
|
||||
_fmp4 = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Recorder::type_ts: {
|
||||
if (start && !_ts) {
|
||||
auto ts = dynamic_pointer_cast<TSMediaSourceMuxer>(makeRecorder(sender, getTracks(), type, _option));
|
||||
if (ts) {
|
||||
ts->setListener(shared_from_this());
|
||||
}
|
||||
_ts = ts;
|
||||
} else if (!start && _ts) {
|
||||
_ts = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default : return false;
|
||||
}
|
||||
}
|
||||
|
||||
//此函数可能跨线程调用
|
||||
bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) {
|
||||
switch (type){
|
||||
case Recorder::type_hls :
|
||||
return !!_hls;
|
||||
case Recorder::type_mp4 :
|
||||
return !!_mp4;
|
||||
default:
|
||||
return false;
|
||||
switch (type) {
|
||||
case Recorder::type_hls: return !!_hls;
|
||||
case Recorder::type_mp4: return !!_mp4;
|
||||
case Recorder::type_hls_fmp4: return !!_hls_fmp4;
|
||||
case Recorder::type_fmp4: return !!_fmp4;
|
||||
case Recorder::type_ts: return !!_ts;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,20 +368,17 @@ bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
|
||||
if (_ts) {
|
||||
ret = _ts->addTrack(track) ? true : ret;
|
||||
}
|
||||
#if defined(ENABLE_MP4)
|
||||
if (_fmp4) {
|
||||
ret = _fmp4->addTrack(track) ? true : ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
|
||||
auto hls = _hls;
|
||||
if (hls) {
|
||||
ret = hls->addTrack(track) ? true : ret;
|
||||
if (_hls) {
|
||||
ret = _hls->addTrack(track) ? true : ret;
|
||||
}
|
||||
auto mp4 = _mp4;
|
||||
if (mp4) {
|
||||
ret = mp4->addTrack(track) ? true : ret;
|
||||
if (_hls_fmp4) {
|
||||
ret = _hls_fmp4->addTrack(track) ? true : ret;
|
||||
}
|
||||
if (_mp4) {
|
||||
ret = _mp4->addTrack(track) ? true : ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -350,16 +388,27 @@ void MultiMediaSourceMuxer::onAllTrackReady() {
|
||||
setMediaListener(getDelegate());
|
||||
|
||||
if (_rtmp) {
|
||||
_rtmp->onAllTrackReady();
|
||||
_rtmp->addTrackCompleted();
|
||||
}
|
||||
if (_rtsp) {
|
||||
_rtsp->onAllTrackReady();
|
||||
_rtsp->addTrackCompleted();
|
||||
}
|
||||
if (_ts) {
|
||||
_ts->addTrackCompleted();
|
||||
}
|
||||
if (_mp4) {
|
||||
_mp4->addTrackCompleted();
|
||||
}
|
||||
#if defined(ENABLE_MP4)
|
||||
if (_fmp4) {
|
||||
_fmp4->onAllTrackReady();
|
||||
_fmp4->addTrackCompleted();
|
||||
}
|
||||
#endif
|
||||
if (_hls) {
|
||||
_hls->addTrackCompleted();
|
||||
}
|
||||
if (_hls_fmp4) {
|
||||
_hls_fmp4->addTrackCompleted();
|
||||
}
|
||||
|
||||
auto listener = _track_listener.lock();
|
||||
if (listener) {
|
||||
listener->onAllTrackReady();
|
||||
@ -407,21 +456,17 @@ void MultiMediaSourceMuxer::resetTracks() {
|
||||
if (_ts) {
|
||||
_ts->resetTracks();
|
||||
}
|
||||
#if defined(ENABLE_MP4)
|
||||
if (_fmp4) {
|
||||
_fmp4->resetTracks();
|
||||
}
|
||||
#endif
|
||||
|
||||
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
|
||||
auto hls = _hls;
|
||||
if (hls) {
|
||||
hls->resetTracks();
|
||||
if (_hls_fmp4) {
|
||||
_hls_fmp4->resetTracks();
|
||||
}
|
||||
|
||||
auto mp4 = _mp4;
|
||||
if (mp4) {
|
||||
mp4->resetTracks();
|
||||
if (_hls) {
|
||||
_hls->resetTracks();
|
||||
}
|
||||
if (_mp4) {
|
||||
_mp4->resetTracks();
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,23 +488,20 @@ bool MultiMediaSourceMuxer::onTrackFrame(const Frame::Ptr &frame_in) {
|
||||
ret = _ts->inputFrame(frame) ? true : ret;
|
||||
}
|
||||
|
||||
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
|
||||
//此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优
|
||||
auto hls = _hls;
|
||||
if (hls) {
|
||||
ret = hls->inputFrame(frame) ? true : ret;
|
||||
}
|
||||
auto mp4 = _mp4;
|
||||
if (mp4) {
|
||||
ret = mp4->inputFrame(frame) ? true : ret;
|
||||
if (_hls) {
|
||||
ret = _hls->inputFrame(frame) ? true : ret;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_MP4)
|
||||
if (_hls_fmp4) {
|
||||
ret = _hls_fmp4->inputFrame(frame) ? true : ret;
|
||||
}
|
||||
|
||||
if (_mp4) {
|
||||
ret = _mp4->inputFrame(frame) ? true : ret;
|
||||
}
|
||||
if (_fmp4) {
|
||||
ret = _fmp4->inputFrame(frame) ? true : ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_ring) {
|
||||
if (frame->getTrackType() == TrackVideo) {
|
||||
// 视频时,遇到第一帧配置帧或关键帧则标记为gop开始处
|
||||
@ -481,15 +523,14 @@ bool MultiMediaSourceMuxer::isEnabled(){
|
||||
if (!_is_enable || _last_check.elapsedTime() > stream_none_reader_delay_ms) {
|
||||
//无人观看时,每次检查是否真的无人观看
|
||||
//有人观看时,则延迟一定时间检查一遍是否无人观看了(节省性能)
|
||||
auto hls = _hls;
|
||||
_is_enable = (_rtmp ? _rtmp->isEnabled() : false) ||
|
||||
(_rtsp ? _rtsp->isEnabled() : false) ||
|
||||
(_ts ? _ts->isEnabled() : false) ||
|
||||
#if defined(ENABLE_MP4)
|
||||
(_fmp4 ? _fmp4->isEnabled() : false) ||
|
||||
#endif
|
||||
(_ring ? (bool)_ring->readerCount() : false) ||
|
||||
(hls ? hls->isEnabled() : false) || _mp4;
|
||||
(_hls ? _hls->isEnabled() : false) ||
|
||||
(_hls_fmp4 ? _hls_fmp4->isEnabled() : false) ||
|
||||
_mp4;
|
||||
|
||||
if (_is_enable) {
|
||||
//无人观看时,不刷新计时器,因为无人观看时每次都会检查一遍,所以刷新计数器无意义且浪费cpu
|
||||
|
@ -164,18 +164,14 @@ private:
|
||||
toolkit::Ticker _last_check;
|
||||
Stamp _stamp[2];
|
||||
std::weak_ptr<Listener> _track_listener;
|
||||
#if defined(ENABLE_RTPPROXY)
|
||||
std::unordered_map<std::string, RingType::RingReader::Ptr> _rtp_sender;
|
||||
#endif //ENABLE_RTPPROXY
|
||||
|
||||
#if defined(ENABLE_MP4)
|
||||
FMP4MediaSourceMuxer::Ptr _fmp4;
|
||||
#endif
|
||||
RtmpMediaSourceMuxer::Ptr _rtmp;
|
||||
RtspMediaSourceMuxer::Ptr _rtsp;
|
||||
TSMediaSourceMuxer::Ptr _ts;
|
||||
MediaSinkInterface::Ptr _mp4;
|
||||
HlsRecorder::Ptr _hls;
|
||||
HlsFMP4Recorder::Ptr _hls_fmp4;
|
||||
toolkit::EventPoller::Ptr _poller;
|
||||
RingType::Ptr _ring;
|
||||
|
||||
|
@ -102,6 +102,7 @@ const string kAddMuteAudio = PROTOCOL_FIELD "add_mute_audio";
|
||||
const string kContinuePushMS = PROTOCOL_FIELD "continue_push_ms";
|
||||
|
||||
const string kEnableHls = PROTOCOL_FIELD "enable_hls";
|
||||
const string kEnableHlsFmp4 = PROTOCOL_FIELD "enable_hls_fmp4";
|
||||
const string kEnableMP4 = PROTOCOL_FIELD "enable_mp4";
|
||||
const string kEnableRtsp = PROTOCOL_FIELD "enable_rtsp";
|
||||
const string kEnableRtmp = PROTOCOL_FIELD "enable_rtmp";
|
||||
@ -127,6 +128,7 @@ static onceToken token([]() {
|
||||
mINI::Instance()[kContinuePushMS] = 15000;
|
||||
|
||||
mINI::Instance()[kEnableHls] = 1;
|
||||
mINI::Instance()[kEnableHlsFmp4] = 0;
|
||||
mINI::Instance()[kEnableMP4] = 0;
|
||||
mINI::Instance()[kEnableRtsp] = 1;
|
||||
mINI::Instance()[kEnableRtmp] = 1;
|
||||
|
@ -193,8 +193,10 @@ extern const std::string kAddMuteAudio;
|
||||
//断连续推延时,单位毫秒,默认采用配置文件
|
||||
extern const std::string kContinuePushMS;
|
||||
|
||||
//是否开启转换为hls
|
||||
//是否开启转换为hls(mpegts)
|
||||
extern const std::string kEnableHls;
|
||||
//是否开启转换为hls(fmp4)
|
||||
extern const std::string kEnableHlsFmp4;
|
||||
//是否开启MP4录制
|
||||
extern const std::string kEnableMP4;
|
||||
//是否开启转换为rtsp/webrtc
|
||||
|
@ -67,6 +67,7 @@
|
||||
#define HLS_SCHEMA "hls"
|
||||
#define TS_SCHEMA "ts"
|
||||
#define FMP4_SCHEMA "fmp4"
|
||||
#define HLS_FMP4_SCHEMA "hls.fmp4"
|
||||
#define SRT_SCHEMA "srt"
|
||||
#define DEFAULT_VHOST "__defaultVhost__"
|
||||
|
||||
|
@ -11,8 +11,6 @@
|
||||
#ifndef ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
|
||||
#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
|
||||
|
||||
#if defined(ENABLE_MP4)
|
||||
|
||||
#include "FMP4MediaSource.h"
|
||||
#include "Record/MP4Muxer.h"
|
||||
|
||||
@ -63,7 +61,8 @@ public:
|
||||
return _option.fmp4_demand ? (_clear_cache ? true : _enabled) : true;
|
||||
}
|
||||
|
||||
void onAllTrackReady() {
|
||||
void addTrackCompleted() override {
|
||||
MP4MuxerMemory::addTrackCompleted();
|
||||
_media_src->setInitSegment(getInitSegment());
|
||||
}
|
||||
|
||||
@ -86,5 +85,4 @@ private:
|
||||
|
||||
}//namespace mediakit
|
||||
|
||||
#endif// defined(ENABLE_MP4)
|
||||
#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
|
||||
|
@ -33,6 +33,7 @@ namespace mediakit {
|
||||
static int kHlsCookieSecond = 60;
|
||||
static const string kCookieName = "ZL_COOKIE";
|
||||
static const string kHlsSuffix = "/hls.m3u8";
|
||||
static const string kHlsFMP4Suffix = "/hls.fmp4.m3u8";
|
||||
|
||||
struct HttpCookieAttachment {
|
||||
//是否已经查找到过MediaSource
|
||||
@ -278,7 +279,7 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo
|
||||
HttpCookieManager::Instance().delCookie(cookie);
|
||||
}
|
||||
|
||||
bool is_hls = media_info.schema == HLS_SCHEMA;
|
||||
bool is_hls = media_info.schema == HLS_SCHEMA || media_info.schema == HLS_FMP4_SCHEMA;
|
||||
|
||||
SockInfoImp::Ptr info = std::make_shared<SockInfoImp>();
|
||||
info->_identifier = sender.getIdentifier();
|
||||
@ -355,7 +356,7 @@ static string pathCat(const string &a, const string &b){
|
||||
* @param cb 回调对象
|
||||
*/
|
||||
static void accessFile(Session &sender, const Parser &parser, const MediaInfo &media_info, const string &file_path, const HttpFileManager::invoker &cb) {
|
||||
bool is_hls = end_with(file_path, kHlsSuffix);
|
||||
bool is_hls = end_with(file_path, kHlsSuffix) || end_with(file_path, kHlsFMP4Suffix);
|
||||
if (!is_hls && !File::fileExist(file_path.data())) {
|
||||
//文件不存在且不是hls,那么直接返回404
|
||||
sendNotFound(cb);
|
||||
@ -363,8 +364,13 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m
|
||||
}
|
||||
if (is_hls) {
|
||||
// hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS
|
||||
const_cast<string &>(media_info.schema) = HLS_SCHEMA;
|
||||
replace(const_cast<string &>(media_info.stream), kHlsSuffix, "");
|
||||
if (end_with(file_path, kHlsSuffix)) {
|
||||
const_cast<string &>(media_info.schema) = HLS_SCHEMA;
|
||||
replace(const_cast<string &>(media_info.stream), kHlsSuffix, "");
|
||||
} else {
|
||||
const_cast<string &>(media_info.schema) = HLS_FMP4_SCHEMA;
|
||||
replace(const_cast<string &>(media_info.stream), kHlsFMP4Suffix, "");
|
||||
}
|
||||
}
|
||||
|
||||
weak_ptr<Session> weakSession = static_pointer_cast<Session>(sender.shared_from_this());
|
||||
@ -621,4 +627,4 @@ HttpResponseInvokerImp::operator bool(){
|
||||
}
|
||||
|
||||
|
||||
}//namespace mediakit
|
||||
}//namespace mediakit
|
||||
|
@ -8,6 +8,7 @@
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include "HlsMaker.h"
|
||||
#include "Common/config.h"
|
||||
|
||||
@ -15,83 +16,76 @@ using namespace std;
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number, bool seg_keep) {
|
||||
HlsMaker::HlsMaker(bool is_fmp4, float seg_duration, uint32_t seg_number, bool seg_keep) {
|
||||
_is_fmp4 = is_fmp4;
|
||||
//最小允许设置为0,0个切片代表点播
|
||||
_seg_number = seg_number;
|
||||
_seg_duration = seg_duration;
|
||||
_seg_keep = seg_keep;
|
||||
}
|
||||
|
||||
HlsMaker::~HlsMaker() = default;
|
||||
|
||||
void HlsMaker::makeIndexFile(bool eof) {
|
||||
char file_content[1024];
|
||||
int maxSegmentDuration = 0;
|
||||
|
||||
for (auto &tp : _seg_dur_list) {
|
||||
int dur = std::get<0>(tp);
|
||||
if (dur > maxSegmentDuration) {
|
||||
maxSegmentDuration = dur;
|
||||
}
|
||||
}
|
||||
auto index_seq = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL;
|
||||
|
||||
auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL;
|
||||
|
||||
string m3u8;
|
||||
if (_seg_number == 0) {
|
||||
// 录像点播支持时移
|
||||
snprintf(file_content, sizeof(file_content),
|
||||
"#EXTM3U\n"
|
||||
"#EXT-X-PLAYLIST-TYPE:EVENT\n"
|
||||
"#EXT-X-VERSION:4\n"
|
||||
"#EXT-X-TARGETDURATION:%u\n"
|
||||
"#EXT-X-MEDIA-SEQUENCE:%llu\n",
|
||||
(maxSegmentDuration + 999) / 1000,
|
||||
sequence);
|
||||
string index_str;
|
||||
index_str.reserve(2048);
|
||||
index_str += "#EXTM3U\n";
|
||||
index_str += (_is_fmp4 ? "#EXT-X-VERSION:7\n" : "#EXT-X-VERSION:4\n");
|
||||
if (_seg_number == 0) {
|
||||
index_str += "#EXT-X-PLAYLIST-TYPE:EVENT\n";
|
||||
} else {
|
||||
snprintf(file_content, sizeof(file_content),
|
||||
"#EXTM3U\n"
|
||||
"#EXT-X-VERSION:3\n"
|
||||
"#EXT-X-ALLOW-CACHE:NO\n"
|
||||
"#EXT-X-TARGETDURATION:%u\n"
|
||||
"#EXT-X-MEDIA-SEQUENCE:%llu\n",
|
||||
(maxSegmentDuration + 999) / 1000,
|
||||
sequence);
|
||||
index_str += "#EXT-X-ALLOW-CACHE:NO\n";
|
||||
}
|
||||
index_str += "#EXT-X-TARGETDURATION:" + std::to_string((maxSegmentDuration + 999) / 1000) + "\n";
|
||||
index_str += "#EXT-X-MEDIA-SEQUENCE:" + std::to_string(index_seq) + "\n";
|
||||
if (_is_fmp4) {
|
||||
index_str += "#EXT-X-MAP:URI=\"init.mp4\"\n";
|
||||
}
|
||||
|
||||
m3u8.assign(file_content);
|
||||
|
||||
stringstream ss;
|
||||
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);
|
||||
ss << "#EXTINF:" << std::setprecision(3) << std::get<0>(tp) / 1000.0 << ",\n" << std::get<1>(tp) << "\n";
|
||||
}
|
||||
index_str += ss.str();
|
||||
|
||||
if (eof) {
|
||||
snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n");
|
||||
m3u8.append(file_content);
|
||||
index_str += "#EXT-X-ENDLIST\n";
|
||||
}
|
||||
onWriteHls(m3u8);
|
||||
onWriteHls(index_str);
|
||||
}
|
||||
|
||||
void HlsMaker::inputInitSegment(const char *data, size_t len) {
|
||||
if (!_is_fmp4) {
|
||||
throw std::invalid_argument("Only fmp4-hls can input init segment");
|
||||
}
|
||||
onWriteInitSegment(data, len);
|
||||
}
|
||||
|
||||
void HlsMaker::inputData(void *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet) {
|
||||
void HlsMaker::inputData(const char *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet) {
|
||||
if (data && len) {
|
||||
if (timestamp < _last_timestamp) {
|
||||
//时间戳回退了,切片时长重新计时
|
||||
WarnL << "stamp reduce: " << _last_timestamp << " -> " << timestamp;
|
||||
// 时间戳回退了,切片时长重新计时
|
||||
WarnL << "Timestamp reduce: " << _last_timestamp << " -> " << timestamp;
|
||||
_last_seg_timestamp = _last_timestamp = timestamp;
|
||||
}
|
||||
if (is_idr_fast_packet) {
|
||||
//尝试切片ts
|
||||
// 尝试切片ts
|
||||
addNewSegment(timestamp);
|
||||
}
|
||||
if (!_last_file_name.empty()) {
|
||||
//存在切片才写入ts数据
|
||||
onWriteSegment((char *) data, len);
|
||||
// 存在切片才写入ts数据
|
||||
onWriteSegment(data, len);
|
||||
_last_timestamp = timestamp;
|
||||
}
|
||||
} else {
|
||||
//resetTracks时触发此逻辑
|
||||
// resetTracks时触发此逻辑
|
||||
flushLastSegment(false);
|
||||
}
|
||||
}
|
||||
@ -148,14 +142,18 @@ void HlsMaker::flushLastSegment(bool eof){
|
||||
makeIndexFile(eof);
|
||||
}
|
||||
|
||||
bool HlsMaker::isLive() {
|
||||
bool HlsMaker::isLive() const {
|
||||
return _seg_number != 0;
|
||||
}
|
||||
|
||||
bool HlsMaker::isKeep() {
|
||||
bool HlsMaker::isKeep() const {
|
||||
return _seg_keep;
|
||||
}
|
||||
|
||||
bool HlsMaker::isFmp4() const {
|
||||
return _is_fmp4;
|
||||
}
|
||||
|
||||
void HlsMaker::clear() {
|
||||
_file_index = 0;
|
||||
_last_timestamp = 0;
|
||||
|
@ -21,12 +21,13 @@ namespace mediakit {
|
||||
class HlsMaker {
|
||||
public:
|
||||
/**
|
||||
* @param is_fmp4 使用fmp4还是mpegts
|
||||
* @param seg_duration 切片文件长度
|
||||
* @param seg_number 切片个数
|
||||
* @param seg_keep 是否保留切片文件
|
||||
*/
|
||||
HlsMaker(float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false);
|
||||
virtual ~HlsMaker();
|
||||
HlsMaker(bool is_fmp4 = false, float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false);
|
||||
virtual ~HlsMaker() = default;
|
||||
|
||||
/**
|
||||
* 写入ts数据
|
||||
@ -35,17 +36,29 @@ public:
|
||||
* @param timestamp 毫秒时间戳
|
||||
* @param is_idr_fast_packet 是否为关键帧第一个包
|
||||
*/
|
||||
void inputData(void *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet);
|
||||
void inputData(const char *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet);
|
||||
|
||||
/**
|
||||
* 输入fmp4 init segment
|
||||
* @param data 数据
|
||||
* @param len 数据长度
|
||||
*/
|
||||
void inputInitSegment(const char *data, size_t len);
|
||||
|
||||
/**
|
||||
* 是否为直播
|
||||
*/
|
||||
bool isLive();
|
||||
bool isLive() const;
|
||||
|
||||
/**
|
||||
* 是否保留切片文件
|
||||
*/
|
||||
bool isKeep();
|
||||
bool isKeep() const;
|
||||
|
||||
/**
|
||||
* 是否采用fmp4切片还是mpegts
|
||||
*/
|
||||
bool isFmp4() const;
|
||||
|
||||
/**
|
||||
* 清空记录
|
||||
@ -66,6 +79,13 @@ protected:
|
||||
*/
|
||||
virtual void onDelSegment(uint64_t index) = 0;
|
||||
|
||||
/**
|
||||
* 写init.mp4切片文件回调
|
||||
* @param data
|
||||
* @param len
|
||||
*/
|
||||
virtual void onWriteInitSegment(const char *data, size_t len) = 0;
|
||||
|
||||
/**
|
||||
* 写ts切片文件回调
|
||||
* @param data
|
||||
@ -109,6 +129,7 @@ private:
|
||||
void addNewSegment(uint64_t timestamp);
|
||||
|
||||
private:
|
||||
bool _is_fmp4 = false;
|
||||
float _seg_duration = 0;
|
||||
uint32_t _seg_number = 0;
|
||||
bool _seg_keep = false;
|
||||
|
@ -21,21 +21,14 @@ using namespace toolkit;
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
HlsMakerImp::HlsMakerImp(const string &m3u8_file,
|
||||
const string ¶ms,
|
||||
uint32_t bufSize,
|
||||
float seg_duration,
|
||||
uint32_t seg_number,
|
||||
bool seg_keep):HlsMaker(seg_duration, seg_number, seg_keep) {
|
||||
HlsMakerImp::HlsMakerImp(bool is_fmp4, const string &m3u8_file, const string ¶ms, uint32_t bufSize, float seg_duration,
|
||||
uint32_t seg_number, bool seg_keep) : HlsMaker(is_fmp4, seg_duration, seg_number, seg_keep) {
|
||||
_poller = EventPollerPool::Instance().getPoller();
|
||||
_path_prefix = m3u8_file.substr(0, m3u8_file.rfind('/'));
|
||||
_path_hls = m3u8_file;
|
||||
_params = params;
|
||||
_buf_size = bufSize;
|
||||
_file_buf.reset(new char[bufSize], [](char *ptr) {
|
||||
delete[] ptr;
|
||||
});
|
||||
|
||||
_file_buf.reset(new char[bufSize], [](char *ptr) { delete[] ptr; });
|
||||
_info.folder = _path_prefix;
|
||||
}
|
||||
|
||||
@ -53,9 +46,9 @@ void HlsMakerImp::clearCache() {
|
||||
}
|
||||
|
||||
void HlsMakerImp::clearCache(bool immediately, bool eof) {
|
||||
//录制完了
|
||||
// 录制完了
|
||||
flushLastSegment(eof);
|
||||
if (!isLive()||isKeep()) {
|
||||
if (!isLive() || isKeep()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -63,7 +56,7 @@ void HlsMakerImp::clearCache(bool immediately, bool eof) {
|
||||
_file = nullptr;
|
||||
_segment_file_paths.clear();
|
||||
|
||||
//hls直播才删除文件
|
||||
// hls直播才删除文件
|
||||
GET_CONFIG(uint32_t, delay, Hls::kDeleteDelaySec);
|
||||
if (!delay || immediately) {
|
||||
File::delete_file(_path_prefix.data());
|
||||
@ -82,7 +75,7 @@ string HlsMakerImp::onOpenSegment(uint64_t index) {
|
||||
auto strDate = getTimeStr("%Y-%m-%d");
|
||||
auto strHour = getTimeStr("%H");
|
||||
auto strTime = getTimeStr("%M-%S");
|
||||
segment_name = StrPrinter << strDate + "/" + strHour + "/" + strTime << "_" << index << ".ts";
|
||||
segment_name = StrPrinter << strDate + "/" + strHour + "/" + strTime << "_" << index << (isFmp4() ? ".mp4" : ".ts");
|
||||
segment_path = _path_prefix + "/" + segment_name;
|
||||
if (isLive()) {
|
||||
_segment_file_paths.emplace(index, segment_path);
|
||||
@ -90,14 +83,14 @@ string HlsMakerImp::onOpenSegment(uint64_t index) {
|
||||
}
|
||||
_file = makeFile(segment_path, true);
|
||||
|
||||
//保存本切片的元数据
|
||||
// 保存本切片的元数据
|
||||
_info.start_time = ::time(NULL);
|
||||
_info.file_name = segment_name;
|
||||
_info.file_path = segment_path;
|
||||
_info.url = _info.app + "/" + _info.stream + "/" + segment_name;
|
||||
|
||||
if (!_file) {
|
||||
WarnL << "create file failed," << segment_path << " " << get_uv_errmsg();
|
||||
WarnL << "Create file failed," << segment_path << " " << get_uv_errmsg();
|
||||
}
|
||||
if (_params.empty()) {
|
||||
return segment_name;
|
||||
@ -114,6 +107,18 @@ void HlsMakerImp::onDelSegment(uint64_t index) {
|
||||
_segment_file_paths.erase(it);
|
||||
}
|
||||
|
||||
void HlsMakerImp::onWriteInitSegment(const char *data, size_t len) {
|
||||
string init_seg_path = _path_prefix + "/init.mp4";
|
||||
_file = makeFile(init_seg_path, true);
|
||||
|
||||
if (_file) {
|
||||
fwrite(data, len, 1, _file.get());
|
||||
_file = nullptr;
|
||||
} else {
|
||||
WarnL << "Create file failed," << init_seg_path << " " << get_uv_errmsg();
|
||||
}
|
||||
}
|
||||
|
||||
void HlsMakerImp::onWriteSegment(const char *data, size_t len) {
|
||||
if (_file) {
|
||||
fwrite(data, len, 1, _file.get());
|
||||
@ -132,13 +137,12 @@ void HlsMakerImp::onWriteHls(const std::string &data) {
|
||||
_media_src->setIndexFile(data);
|
||||
}
|
||||
} else {
|
||||
WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg();
|
||||
WarnL << "Create hls file failed," << _path_hls << " " << get_uv_errmsg();
|
||||
}
|
||||
//DebugL << "\r\n" << string(data,len);
|
||||
}
|
||||
|
||||
void HlsMakerImp::onFlushLastSegment(uint64_t duration_ms) {
|
||||
//关闭并flush文件到磁盘
|
||||
// 关闭并flush文件到磁盘
|
||||
_file = nullptr;
|
||||
|
||||
GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs);
|
||||
@ -166,11 +170,11 @@ void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const s
|
||||
_info.app = app;
|
||||
_info.stream = stream_id;
|
||||
_info.vhost = vhost;
|
||||
_media_src = std::make_shared<HlsMediaSource>(_info);
|
||||
_media_src = std::make_shared<HlsMediaSource>(isFmp4() ? HLS_FMP4_SCHEMA : HLS_SCHEMA, _info);
|
||||
}
|
||||
|
||||
HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {
|
||||
return _media_src;
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
} // namespace mediakit
|
@ -19,15 +19,10 @@
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class HlsMakerImp : public HlsMaker{
|
||||
class HlsMakerImp : public HlsMaker {
|
||||
public:
|
||||
HlsMakerImp(const std::string &m3u8_file,
|
||||
const std::string ¶ms,
|
||||
uint32_t bufSize = 64 * 1024,
|
||||
float seg_duration = 5,
|
||||
uint32_t seg_number = 3,
|
||||
bool seg_keep = false);
|
||||
|
||||
HlsMakerImp(bool is_fmp4, const std::string &m3u8_file, const std::string ¶ms, uint32_t bufSize = 64 * 1024,
|
||||
float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false);
|
||||
~HlsMakerImp() override;
|
||||
|
||||
/**
|
||||
@ -52,6 +47,7 @@ public:
|
||||
protected:
|
||||
std::string onOpenSegment(uint64_t index) override ;
|
||||
void onDelSegment(uint64_t index) override;
|
||||
void onWriteInitSegment(const char *data, size_t len) override;
|
||||
void onWriteSegment(const char *data, size_t len) override;
|
||||
void onWriteHls(const std::string &data) override;
|
||||
void onFlushLastSegment(uint64_t duration_ms) override;
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
using RingType = toolkit::RingBuffer<std::string>;
|
||||
using Ptr = std::shared_ptr<HlsMediaSource>;
|
||||
|
||||
HlsMediaSource(const MediaTuple& tuple): MediaSource(HLS_SCHEMA, tuple) {}
|
||||
HlsMediaSource(const std::string &schema, const MediaTuple &tuple) : MediaSource(schema, tuple) {}
|
||||
~HlsMediaSource() override = default;
|
||||
|
||||
/**
|
||||
|
@ -13,27 +13,27 @@
|
||||
|
||||
#include "HlsMakerImp.h"
|
||||
#include "MPEG.h"
|
||||
#include "MP4Muxer.h"
|
||||
#include "Common/config.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class HlsRecorder final : public MediaSourceEventInterceptor, public MpegMuxer, public std::enable_shared_from_this<HlsRecorder> {
|
||||
template <typename Muxer>
|
||||
class HlsRecorderBase : public MediaSourceEventInterceptor, public Muxer, public std::enable_shared_from_this<HlsRecorderBase<Muxer> > {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HlsRecorder>;
|
||||
|
||||
HlsRecorder(const std::string &m3u8_file, const std::string ¶ms, const ProtocolOption &option) : MpegMuxer(false) {
|
||||
HlsRecorderBase(bool is_fmp4, const std::string &m3u8_file, const std::string ¶ms, const ProtocolOption &option) {
|
||||
GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum);
|
||||
GET_CONFIG(bool, hlsKeep, Hls::kSegmentKeep);
|
||||
GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize);
|
||||
GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration);
|
||||
|
||||
_option = option;
|
||||
_hls = std::make_shared<HlsMakerImp>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
|
||||
//清空上次的残余文件
|
||||
_hls = std::make_shared<HlsMakerImp>(is_fmp4, m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
|
||||
// 清空上次的残余文件
|
||||
_hls->clearCache();
|
||||
}
|
||||
|
||||
~HlsRecorder() { MpegMuxer::flush(); };
|
||||
~HlsRecorderBase() override = default;
|
||||
|
||||
void setMediaSource(const MediaTuple& tuple) {
|
||||
_hls->setMediaSource(tuple.vhost, tuple.app, tuple.stream);
|
||||
@ -41,7 +41,7 @@ public:
|
||||
|
||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) {
|
||||
setDelegate(listener);
|
||||
_hls->getMediaSource()->setListener(shared_from_this());
|
||||
_hls->getMediaSource()->setListener(this->shared_from_this());
|
||||
}
|
||||
|
||||
int readerCount() { return _hls->getMediaSource()->readerCount(); }
|
||||
@ -64,7 +64,7 @@ public:
|
||||
_hls->getMediaSource()->setIndexFile("");
|
||||
}
|
||||
if (_enabled || !_option.hls_demand) {
|
||||
return MpegMuxer::inputFrame(frame);
|
||||
return Muxer::inputFrame(frame);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -74,20 +74,54 @@ public:
|
||||
return _option.hls_demand ? (_clear_cache ? true : _enabled) : true;
|
||||
}
|
||||
|
||||
private:
|
||||
void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override {
|
||||
if (!buffer) {
|
||||
_hls->inputData(nullptr, 0, timestamp, key_pos);
|
||||
} else {
|
||||
_hls->inputData(buffer->data(), buffer->size(), timestamp, key_pos);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool _enabled = true;
|
||||
bool _clear_cache = false;
|
||||
ProtocolOption _option;
|
||||
std::shared_ptr<HlsMakerImp> _hls;
|
||||
};
|
||||
|
||||
class HlsRecorder final : public HlsRecorderBase<MpegMuxer> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HlsRecorder>;
|
||||
template <typename ...ARGS>
|
||||
HlsRecorder(ARGS && ...args) : HlsRecorderBase<MpegMuxer>(false, std::forward<ARGS>(args)...) {}
|
||||
~HlsRecorder() override { this->flush(); }
|
||||
|
||||
private:
|
||||
void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override {
|
||||
if (!buffer) {
|
||||
// reset tracks
|
||||
_hls->inputData(nullptr, 0, timestamp, key_pos);
|
||||
} else {
|
||||
_hls->inputData(buffer->data(), buffer->size(), timestamp, key_pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HlsFMP4Recorder final : public HlsRecorderBase<MP4MuxerMemory> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HlsFMP4Recorder>;
|
||||
template <typename ...ARGS>
|
||||
HlsFMP4Recorder(ARGS && ...args) : HlsRecorderBase<MP4MuxerMemory>(true, std::forward<ARGS>(args)...) {}
|
||||
~HlsFMP4Recorder() override { this->flush(); }
|
||||
|
||||
void addTrackCompleted() override {
|
||||
HlsRecorderBase<MP4MuxerMemory>::addTrackCompleted();
|
||||
auto data = getInitSegment();
|
||||
_hls->inputInitSegment(data.data(), data.size());
|
||||
}
|
||||
|
||||
private:
|
||||
void onSegmentData(std::string buffer, uint64_t timestamp, bool key_pos) override {
|
||||
if (buffer.empty()) {
|
||||
// reset tracks
|
||||
_hls->inputData(nullptr, 0, timestamp, key_pos);
|
||||
} else {
|
||||
_hls->inputData((char *)buffer.data(), buffer.size(), timestamp, key_pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
#endif //HLSRECORDER_H
|
||||
|
@ -8,7 +8,8 @@
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_MP4
|
||||
#if defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
|
||||
#include "MP4.h"
|
||||
#include "Util/File.h"
|
||||
#include "Util/logger.h"
|
||||
@ -176,4 +177,4 @@ int MP4FileMemory::onWrite(const void *data, size_t bytes){
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
#endif //NABLE_MP4RECORD
|
||||
#endif // defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
|
@ -11,7 +11,7 @@
|
||||
#ifndef ZLMEDIAKIT_MP4_H
|
||||
#define ZLMEDIAKIT_MP4_H
|
||||
|
||||
#ifdef ENABLE_MP4
|
||||
#if defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -136,5 +136,5 @@ private:
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
#endif //NABLE_MP4RECORD
|
||||
#endif //defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
#endif //ZLMEDIAKIT_MP4_H
|
||||
|
@ -8,7 +8,7 @@
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_MP4
|
||||
#if defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
|
||||
#include "MP4Muxer.h"
|
||||
#include "Extension/AAC.h"
|
||||
@ -377,24 +377,24 @@ bool MP4MuxerMemory::inputFrame(const Frame::Ptr &frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool key_frame = frame->keyFrame();
|
||||
|
||||
//flush切片
|
||||
// flush切片
|
||||
saveSegment();
|
||||
|
||||
auto data = _memory_file->getAndClearMemory();
|
||||
if (!data.empty()) {
|
||||
//输出切片数据
|
||||
onSegmentData(std::move(data), frame->dts(), _key_frame);
|
||||
// 输出切片数据
|
||||
onSegmentData(std::move(data), _last_dst, _key_frame);
|
||||
_key_frame = false;
|
||||
}
|
||||
|
||||
if (key_frame) {
|
||||
if (frame->keyFrame()) {
|
||||
_key_frame = true;
|
||||
}
|
||||
|
||||
if (frame->getTrackType() == TrackVideo || !haveVideo()) {
|
||||
_last_dst = frame->dts();
|
||||
}
|
||||
return MP4MuxerInterface::inputFrame(frame);
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
#endif//#ifdef ENABLE_MP4
|
||||
#endif //defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
|
@ -11,13 +11,13 @@
|
||||
#ifndef ZLMEDIAKIT_MP4MUXER_H
|
||||
#define ZLMEDIAKIT_MP4MUXER_H
|
||||
|
||||
#ifdef ENABLE_MP4
|
||||
#if defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
|
||||
#include "Common/MediaSink.h"
|
||||
#include "Common/Stamp.h"
|
||||
#include "MP4.h"
|
||||
|
||||
namespace mediakit{
|
||||
namespace mediakit {
|
||||
|
||||
class MP4MuxerInterface : public MediaSinkInterface {
|
||||
public:
|
||||
@ -147,11 +147,39 @@ protected:
|
||||
|
||||
private:
|
||||
bool _key_frame = false;
|
||||
uint64_t _last_dst = 0;
|
||||
std::string _init_segment;
|
||||
MP4FileMemory::Ptr _memory_file;
|
||||
};
|
||||
|
||||
} // namespace mediakit
|
||||
|
||||
}//namespace mediakit
|
||||
#endif//#ifdef ENABLE_MP4
|
||||
#else
|
||||
|
||||
#include "Common/MediaSink.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class MP4MuxerMemory : public MediaSinkInterface {
|
||||
public:
|
||||
MP4MuxerMemory() = default;
|
||||
~MP4MuxerMemory() override = default;
|
||||
|
||||
bool addTrack(const Track::Ptr & track) override { return false; }
|
||||
bool inputFrame(const Frame::Ptr &frame) override { return false; }
|
||||
const std::string &getInitSegment() { static std::string kNull; return kNull; };
|
||||
|
||||
protected:
|
||||
/**
|
||||
* 输出fmp4切片回调函数
|
||||
* @param std::string 切片内容
|
||||
* @param stamp 切片末尾时间戳
|
||||
* @param key_frame 是否有关键帧
|
||||
*/
|
||||
virtual void onSegmentData(std::string string, uint64_t stamp, bool key_frame) = 0;
|
||||
};
|
||||
|
||||
} // namespace mediakit
|
||||
|
||||
#endif //defined(ENABLE_MP4) || defined(ENABLE_HLS_FMP4)
|
||||
#endif //ZLMEDIAKIT_MP4MUXER_H
|
||||
|
@ -25,7 +25,7 @@ namespace mediakit {
|
||||
//该类用于产生MPEG-TS/MPEG-PS
|
||||
class MpegMuxer : public MediaSinkInterface {
|
||||
public:
|
||||
MpegMuxer(bool is_ps);
|
||||
MpegMuxer(bool is_ps = false);
|
||||
~MpegMuxer() override;
|
||||
|
||||
/**
|
||||
@ -86,7 +86,7 @@ namespace mediakit {
|
||||
|
||||
class MpegMuxer : public MediaSinkInterface {
|
||||
public:
|
||||
MpegMuxer(bool is_ps) {}
|
||||
MpegMuxer(bool is_ps = false) {}
|
||||
~MpegMuxer() override = default;
|
||||
bool addTrack(const Track::Ptr &track) override { return false; }
|
||||
void resetTracks() override {}
|
||||
|
@ -14,7 +14,8 @@
|
||||
#include "Common/MediaSource.h"
|
||||
#include "MP4Recorder.h"
|
||||
#include "HlsRecorder.h"
|
||||
#include "Util/File.h"
|
||||
#include "FMP4/FMP4MediaSourceMuxer.h"
|
||||
#include "TS/TSMediaSourceMuxer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
@ -53,6 +54,20 @@ string Recorder::getRecordPath(Recorder::type type, const MediaTuple& tuple, con
|
||||
}
|
||||
return File::absolutePath(mp4FilePath, recordPath);
|
||||
}
|
||||
case Recorder::type_hls_fmp4: {
|
||||
GET_CONFIG(string, hlsPath, Protocol::kHlsSavePath);
|
||||
string m3u8FilePath;
|
||||
if (enableVhost) {
|
||||
m3u8FilePath = tuple.shortUrl() + "/hls.fmp4.m3u8";
|
||||
} else {
|
||||
m3u8FilePath = tuple.app + "/" + tuple.stream + "/hls.fmp4.m3u8";
|
||||
}
|
||||
// Here we use the customized file path.
|
||||
if (!customized_path.empty()) {
|
||||
return File::absolutePath(m3u8FilePath, customized_path);
|
||||
}
|
||||
return File::absolutePath(m3u8FilePath, hlsPath);
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@ -82,6 +97,34 @@ std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const Me
|
||||
#endif
|
||||
}
|
||||
|
||||
case Recorder::type_hls_fmp4: {
|
||||
#if defined(ENABLE_HLS_FMP4)
|
||||
auto path = Recorder::getRecordPath(type, tuple, option.hls_save_path);
|
||||
GET_CONFIG(bool, enable_vhost, General::kEnableVhost);
|
||||
auto ret = std::make_shared<HlsFMP4Recorder>(path, enable_vhost ? string(VHOST_KEY) + "=" + tuple.vhost : "", option);
|
||||
ret->setMediaSource(tuple);
|
||||
return ret;
|
||||
#else
|
||||
throw std::invalid_argument("hls.fmp4相关功能未打开,请开启ENABLE_HLS_FMP4宏后编译再测试");
|
||||
#endif
|
||||
}
|
||||
|
||||
case Recorder::type_fmp4: {
|
||||
#if defined(ENABLE_HLS_FMP4) || defined(ENABLE_MP4)
|
||||
return std::make_shared<FMP4MediaSourceMuxer>(tuple, option);
|
||||
#else
|
||||
throw std::invalid_argument("fmp4相关功能未打开,请开启ENABLE_HLS_FMP4或ENABLE_MP4宏后编译再测试");
|
||||
#endif
|
||||
}
|
||||
|
||||
case Recorder::type_ts: {
|
||||
#if defined(ENABLE_HLS) || defined(ENABLE_RTPPROXY)
|
||||
return std::make_shared<TSMediaSourceMuxer>(tuple, option);
|
||||
#else
|
||||
throw std::invalid_argument("mpegts相关功能未打开,请开启ENABLE_HLS或ENABLE_RTPPROXY宏后编译再测试");
|
||||
#endif
|
||||
}
|
||||
|
||||
default: throw std::invalid_argument("未知的录制类型");
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,13 @@ public:
|
||||
// 录制hls
|
||||
type_hls = 0,
|
||||
// 录制MP4
|
||||
type_mp4 = 1
|
||||
type_mp4 = 1,
|
||||
// 录制hls.fmp4
|
||||
type_hls_fmp4 = 2,
|
||||
// fmp4直播
|
||||
type_fmp4 = 3,
|
||||
// ts直播
|
||||
type_ts = 4,
|
||||
} type;
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,8 @@ public:
|
||||
return _media_src->readerCount();
|
||||
}
|
||||
|
||||
void onAllTrackReady(){
|
||||
void addTrackCompleted() override {
|
||||
RtmpMuxer::addTrackCompleted();
|
||||
makeConfigPacket();
|
||||
_media_src->setMetaData(getMetadata());
|
||||
}
|
||||
|
@ -44,7 +44,8 @@ public:
|
||||
_media_src->setTimeStamp(stamp);
|
||||
}
|
||||
|
||||
void onAllTrackReady(){
|
||||
void addTrackCompleted() override {
|
||||
RtspMuxer::addTrackCompleted();
|
||||
_media_src->setSdp(getSdp());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user