mirror of
https://gitee.com/xia-chu/ZLMediaKit.git
synced 2026-06-11 01:57:49 +08:00
支持配置 fmp4 HLS 切片文件扩展名 (.mp4/.m4s) (#4746)
Some checks failed
Android / build (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (push) Has been cancelled
DockerPy / build (push) Has been cancelled
Linux / build (push) Has been cancelled
Linux_Python / build (push) Has been cancelled
macOS / build (push) Has been cancelled
macOS_Python / build (push) Has been cancelled
Windows / build (push) Has been cancelled
Windows_Python / build (push) Has been cancelled
Some checks failed
Android / build (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (push) Has been cancelled
DockerPy / build (push) Has been cancelled
Linux / build (push) Has been cancelled
Linux_Python / build (push) Has been cancelled
macOS / build (push) Has been cancelled
macOS_Python / build (push) Has been cancelled
Windows / build (push) Has been cancelled
Windows_Python / build (push) Has been cancelled
## 背景 / 问题 fmp4 模式下,HLS 切片文件名被硬编码为 `.mp4`(见 `HlsMakerImp::onOpenSegment`),无法改用 fmp4 媒体段更规范、更常见的 `.m4s` 扩展名(`init` 段用 `init.mp4`、媒体段用 `.m4s` 是 CMAF/DASH 的通行约定)。 ## 改动 - 新增配置项 `hls.fmp4SegExt`,默认 `.mp4`,**完全向后兼容**;用户可设为 `.m4s`。 - `init` 段仍固定为 `init.mp4`,mpegts 切片仍为 `.ts`,相关行为不变。 - 切片文件名同时用作 m3u8 中的分片 URI,因此扩展名变更后 playlist 自动保持一致,无需额外同步。 - 在 HTTP MIME 表中注册 `.m4s` -> `video/mp4`;否则会以 `text/plain` 返回,导致 Safari 等播放器无法播放 HLS-fmp4。 - 配置值不带前导点(如 `m4s`)时自动规范化为 `.m4s`。 ## 涉及文件 `src/Common/config.h`、`src/Common/config.cpp`、`src/Record/HlsRecorder.h`、`src/Record/HlsMakerImp.h`、`src/Record/HlsMakerImp.cpp`、`src/Http/HttpConst.cpp`、`conf/config.ini` ## 测试 - **默认(未配置)**:切片仍为 `*.mp4`,`init` 为 `init.mp4`,行为与改动前一致(回归正常)。 - **`hls.fmp4SegExt=.m4s`**:切片文件为 `*_N.m4s`,m3u8 中 `#EXTINF` 的 URI 指向 `.m4s`,`#EXT-X-MAP` 仍为 `init.mp4`;HTTP 拉取切片返回 `Content-Type: video/mp4`;hls.js 与 Safari 播放正常。 - **`hls.fmp4SegExt=m4s`(无前导点)**:自动规范化,仍生成 `*.m4s`。 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eeb2693f22
commit
64ae6e43c5
@ -284,6 +284,11 @@ segKeep=0
|
||||
# If set to 1, the length of the first segment is forcibly set to exactly 1 GOP.
|
||||
# When the GOP is smaller than `segDur`, this can improve the initial startup (instant playback) speed.
|
||||
fastRegister=0
|
||||
|
||||
# fmp4 HLS切片文件的扩展名,例如 .mp4 或 .m4s(fmp4媒体段的标准扩展名);init段始终为init.mp4,mpegts切片始终为.ts
|
||||
# File extension for fMP4 HLS segment files, e.g. .mp4 or .m4s (the standard extension for fMP4 media segments).
|
||||
# The init segment is always init.mp4, and mpegts segments are always .ts.
|
||||
fmp4SegExt=.mp4
|
||||
[hook]
|
||||
# 是否启用hook事件,启用后,推拉流都将进行鉴权
|
||||
# Whether to enable webhook events. When enabled, pushing and pulling streams requires authentication.
|
||||
|
||||
@ -355,6 +355,7 @@ const string kFileBufSize = HLS_FIELD "fileBufSize";
|
||||
const string kBroadcastRecordTs = HLS_FIELD "broadcastRecordTs";
|
||||
const string kDeleteDelaySec = HLS_FIELD "deleteDelaySec";
|
||||
const string kFastRegister = HLS_FIELD "fastRegister";
|
||||
const string kFmp4SegExt = HLS_FIELD "fmp4SegExt";
|
||||
|
||||
static onceToken token([]() {
|
||||
mINI::Instance()[kSegmentDuration] = 2;
|
||||
@ -366,6 +367,7 @@ static onceToken token([]() {
|
||||
mINI::Instance()[kBroadcastRecordTs] = false;
|
||||
mINI::Instance()[kDeleteDelaySec] = 10;
|
||||
mINI::Instance()[kFastRegister] = false;
|
||||
mINI::Instance()[kFmp4SegExt] = ".mp4";
|
||||
});
|
||||
} // namespace Hls
|
||||
|
||||
|
||||
@ -531,6 +531,9 @@ extern const std::string kDeleteDelaySec;
|
||||
// 如果设置为1,则第一个切片长度强制设置为1个GOP [AUTO-TRANSLATED:fbbb651d]
|
||||
// If set to 1, the length of the first slice is forced to be 1 GOP
|
||||
extern const std::string kFastRegister;
|
||||
// fmp4 HLS切片文件的扩展名(例如 .mp4 或 .m4s);mpegts切片始终为.ts
|
||||
// File extension for fMP4 HLS segment files (e.g. .mp4 or .m4s); mpegts segments are always .ts
|
||||
extern const std::string kFmp4SegExt;
|
||||
} // namespace Hls
|
||||
|
||||
// //////////Rtp代理相关配置/////////// [AUTO-TRANSLATED:7b285587]
|
||||
|
||||
@ -183,6 +183,7 @@ static const char *s_mime_src[][2] = {
|
||||
{"3gp", "video/3gpp"},
|
||||
{"ts", "video/mp2t"},
|
||||
{"mp4", "video/mp4"},
|
||||
{"m4s", "video/mp4"},
|
||||
{"mpeg", "video/mpeg"},
|
||||
{"mpg", "video/mpeg"},
|
||||
{"mov", "video/quicktime"},
|
||||
|
||||
@ -31,13 +31,16 @@ std::string getDelayPath(const std::string& originalPath) {
|
||||
}
|
||||
|
||||
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) {
|
||||
uint32_t seg_number, bool seg_keep, const string &fmp4_seg_ext) : 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;
|
||||
_path_hls_delay = getDelayPath(m3u8_file);
|
||||
_params = params;
|
||||
_buf_size = bufSize;
|
||||
// 兼容用户配置不带前导点的扩展名(例如 m4s),统一补上"." [AUTO-TRANSLATED]
|
||||
// Tolerate user-configured extensions without a leading dot (e.g. m4s) by normalizing to ".m4s"
|
||||
_fmp4_seg_ext = fmp4_seg_ext.empty() ? ".mp4" : (fmp4_seg_ext.front() == '.' ? fmp4_seg_ext : "." + fmp4_seg_ext);
|
||||
_file_buf.reset(new char[bufSize], [](char *ptr) { delete[] ptr; });
|
||||
_info.folder = _path_prefix;
|
||||
}
|
||||
@ -151,7 +154,7 @@ string HlsMakerImp::onOpenSegment(uint64_t index) {
|
||||
auto strHour = getTimeStr("%H");
|
||||
auto strTime = getTimeStr("%M-%S");
|
||||
auto current_dir = strDate + "/" + strHour + "/";
|
||||
segment_name = current_dir + strTime + "_" + std::to_string(index) + (isFmp4() ? ".mp4" : ".ts");
|
||||
segment_name = current_dir + strTime + "_" + std::to_string(index) + (isFmp4() ? _fmp4_seg_ext : ".ts");
|
||||
segment_path = _path_prefix + "/" + segment_name;
|
||||
if (isLive()) {
|
||||
// 直播
|
||||
|
||||
@ -22,7 +22,8 @@ namespace mediakit {
|
||||
class HlsMakerImp : public HlsMaker {
|
||||
public:
|
||||
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);
|
||||
float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false,
|
||||
const std::string &fmp4_seg_ext = ".mp4");
|
||||
~HlsMakerImp() override;
|
||||
|
||||
/**
|
||||
@ -68,6 +69,7 @@ private:
|
||||
private:
|
||||
int _buf_size;
|
||||
std::string _params;
|
||||
std::string _fmp4_seg_ext;
|
||||
std::string _path_hls;
|
||||
std::string _path_hls_delay;
|
||||
std::string _path_init;
|
||||
|
||||
@ -26,9 +26,10 @@ public:
|
||||
GET_CONFIG(bool, hlsKeep, Hls::kSegmentKeep);
|
||||
GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize);
|
||||
GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration);
|
||||
GET_CONFIG(std::string, hlsFmp4SegExt, Hls::kFmp4SegExt);
|
||||
|
||||
_option = option;
|
||||
_hls = std::make_shared<HlsMakerImp>(is_fmp4, m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
|
||||
_hls = std::make_shared<HlsMakerImp>(is_fmp4, m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep, hlsFmp4SegExt);
|
||||
// 清空上次的残余文件 [AUTO-TRANSLATED:e16122be]
|
||||
// Clear the residual files from the last time
|
||||
_hls->clearCache();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user