C# HTTP Live Streaming(HLS) C#如何将视频文件切片成.ts文件并生成.m3u8播放列表

来源:这里教程网 时间:2026-02-21 17:42:12 作者:

用 FFmpeg 命令行切片,不是 C# 原生能干的事

C# 本身没有内置的 HLS 切片能力,

System.IO
MediaFoundation
都不支持生成标准
.ts
片段和
.m3u8
清单。硬写解析 H.264/AVC 流、按 GOP 对齐切片、封装 MPEG-TS、生成带
#EXT-X-VERSION
和时间戳的 playlist,不仅工作量大,还极易产出非标准或播放失败的文件。

实际项目里,99% 的做法是调用外部工具——最可靠、最通用的是

ffmpeg
。它支持完整 HLS 工作流,参数稳定,兼容主流播放器(如 iOS Safari、VLC、hls.js)。

必须确保目标机器已安装
ffmpeg
,且在 PATH 中可执行;否则
Process.Start("ffmpeg", ...)
会直接抛
FileNotFoundException
不要尝试用
MemoryStream
把视频喂给 ffmpeg stdin——TS 封装依赖随机访问和帧对齐,从管道输入常导致
moov atom not found
或切片时间错乱
输入视频最好已是 H.264 编码 + AAC 音频;若源为 MP4/MKV,ffmpeg 会自动转封装,但耗时增加,且可能引入音画不同步

关键 ffmpeg 参数组合:-f hls -hls_time -hls_list_size

生成合规 HLS 的核心不是“能不能切”,而是“切得是否被播放器接受”。以下是最小可行命令模板:

ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 -hls_segment_filename "chunk_%03d.ts" output.m3u8

注意几个易错点:

-hls_time 10
表示每个
.ts
片段约 10 秒——但实际长度受 GOP 影响,若关键帧间隔 >10s(如某些监控录像),ffmpeg 会拉长到下一个 I 帧,导致片段偏长;可加
-force_key_frames "expr:gte(t,n_forced*10)"
强制插关键帧
-hls_list_size 0
表示不限制 playlist 中保留多少条
#EXTINF
记录;设为正数(如
5
)会滚动覆盖旧片段,适合直播场景,但点播应设为
0
-hls_segment_filename
路径需为相对或绝对路径,若含目录(如
"./out/chunk_%03d.ts"
),必须确保目录已存在,否则 ffmpeg 静默失败,只生成空
.m3u8
默认生成的
.m3u8
是 v3 版本(无
#EXT-X-MAP
),若需支持低版本 hls.js 或 Apple TV,可加
-hls_version 4

从 C# 启动 ffmpeg 并捕获错误,别只看 ExitCode

Process.Start
调用 ffmpeg 后,仅检查
ExitCode == 0
不够——ffmpeg 在部分失败时仍返回 0(例如只生成了前几个
.ts
就卡住,但没报错)。真正可靠的判断依据是 stderr 输出和输出文件是否存在。

务必重定向
StandardError
,并监听其中是否含
"Error"
"failed"
"Invalid"
等关键词;ffmpeg 错误通常不走 stdout
切片完成后,检查
output.m3u8
文件是否非空,且内容包含至少两条
#EXTINF
行;可用
File.ReadAllLines
快速验证
避免在 UI 线程同步等待 ffmpeg 结束——用
await Process.WaitForExitAsync()
(.NET 6+)或
Task.Run(() => process.WaitForExit())
Windows 下若路径含中文或空格,
ffmpeg
参数必须用双引号包裹,但 C# 传参时需手动加,例如:
new string[] { "-i", $"\"{inputPath}\"", "-f", "hls", ... }

生成的 .m3u8 被拒载?先查 CORS 和 MIME 类型

即使切片完全正确,前端用

hls.js
加载时仍可能报
"failed to load manifest"
或黑屏。问题往往不在 C# 或 ffmpeg,而在服务端配置。

HTTP 服务器必须返回
.m3u8
的 MIME 类型为
application/vnd.apple.mpegurl
,而非
text/plain
;IIS、Nginx、Kestrel 默认都不认这个后缀
若静态文件由 ASP.NET Core 提供,需在
Program.cs
中显式注册:
services.Configure<staticfileoptions>(o => o.ServeUnknownFileTypes = true);</staticfileoptions>
并在
UseStaticFiles
前加
app.UseCors
允许跨域(开发时常见坑)
.ts
文件也需正确 MIME:
video/MP2T
;某些 CDN 会强制改写,导致浏览器拒绝解析
用浏览器 DevTools 的 Network 面板直接打开
.m3u8
URL,确认响应头含
Content-Type: application/vnd.apple.mpegurl
,且响应体是纯文本、每行无 BOM、无 HTML 标签

HLS 的麻烦从来不在切片本身,而在于整个交付链路里任意一环的隐式假设——比如你以为 ffmpeg 生成了标准文件,其实播放器只认特定 EXT-X-VERSION;你以为服务端配好了 MIME,其实 CDN 拦截重写了 header。动手前,先用 VLC 打开生成的

.m3u8
,能播通,再往下一步走。

相关推荐