mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-07-04 01:37:33 +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:
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user