新增支持hls-fmp4直播(#2603 #977 #1965)

同时主要优化点包括:
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:
XiaoYan Lin
2023-07-02 12:02:33 +08:00
committed by GitHub
parent 6aa4b741a3
commit cb0579a16d
27 changed files with 413 additions and 224 deletions

View File

@@ -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;
//最小允许设置为00个切片代表点播
_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;

View File

@@ -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;

View File

@@ -21,21 +21,14 @@ using namespace toolkit;
namespace mediakit {
HlsMakerImp::HlsMakerImp(const string &m3u8_file,
const string &params,
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 &params, 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

View File

@@ -19,15 +19,10 @@
namespace mediakit {
class HlsMakerImp : public HlsMaker{
class HlsMakerImp : public HlsMaker {
public:
HlsMakerImp(const std::string &m3u8_file,
const std::string &params,
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 &params, 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;

View File

@@ -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;
/**

View File

@@ -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 &params, const ProtocolOption &option) : MpegMuxer(false) {
HlsRecorderBase(bool is_fmp4, const std::string &m3u8_file, const std::string &params, 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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 {}

View File

@@ -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("未知的录制类型");
}
}

View File

@@ -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;
/**