😄作者简介: 小曾同学.com,一个致力于测试开发的博主⛽️,主要职责:测试开发、CI/CD
如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。
😊 座右铭:不想当开发的测试,不是一个好测试✌️。
如果感觉博主的文章还不错的话,还请点赞、收藏哦!👍
在这篇文章中 HLS协议中m3u8列表及ts文件的由来我们留了一个小问题:切片时长是指定的,怎么最终的切片时长 长短不一
在命令中指定-hls_time 5,但是有的切片时长是4s,有的是5s 、6s,这是为什么?-hls_time 是目标分片时长,不是强制固定时长,FFmpeg 会按视频关键帧对齐、音频采样对齐、文件末尾这几个规则拆分,所以时长会波动。另外ffmpeg 会优先保证分片的可独立解码性,而非严格遵守5s。
- 关键帧对齐:每个TS 片段必须以 关键帧开头,否则播放器无法独立解码该片段。若源视频的关键帧间隔不是 5s 整数倍(比如关键帧在 4s、9s、14s 处),FFmpeg 会在最近的关键帧处拆分,导致片段时长为 4s、5s 交替。例如:源视频关键帧间隔 4s → 拆出 4s 片段;关键帧间隔 5s → 拆出 5s 片段。
- 音频帧完整对齐:音频帧是固定采样率的(如 44100Hz),单帧时长固定(如 1024 采样≈23ms);FFmpeg 不会拆分音频帧,会凑整完整音频帧后再拆分,导致时长微调(比如多 / 少几十毫秒,最终显示为 4s/5s)。例如:5s 时长需要 217 个音频帧(217×23ms≈5s),若差 1 帧凑不齐,就会拆出 4.977s(≈4s)的片段。
- 文件末尾截断:最后一个片段是视频剩余时长,必然无法刚好凑齐 5s。
补充:FFmpeg 对 hls_time 的定义是「最大目标时长」,片段时长只会≤该值(如 5s),很少会超过,因此 4s、5s 是正常波动。
强制关键帧间隔匹配 5s(推荐,兼顾解码性)
ffmpeg -i ../bbb_30fps_gop_60_3mbps.mp4 -c:v libx264 -c:a aac -force_key_frames "expr:gte(t,n_forced*5)" -g 150 -f hls -hls_time 5 -start_number 0 output.m3u8
但是最终的结果 前段切片文件时长基本都是4s。FFmpeg 的时间戳是浮点型 (如 4.999999s 而非严格 5.0s),gte(t,n_forced*5) 表达式在浮点误差下,会把关键帧生成在 4.999s 而非 5.0s,切片时 FFmpeg 会按这个 "近似 5s" 的关键帧拆分,最终显示为 4s(播放器 /ffprobe 会向下取整或保留整数显示)。
另外,AAC 音频帧是固定时长(如 44100Hz 采样→每帧≈23.22ms),5s 需要≈215.33 个音频帧,FFmpeg 只能凑整 215 帧(总时长≈4.992s),切片时长会被识别为 4s(整数显示)。
所以如果切片仍显示 4s(如4.999s),是显示精度问题而非实际时长问题。
或者执行命令:ffmpeg -i ../bbb_30fps_gop_60_3mbps.mp4 -c:v libx264 -c:a aac -force_key_frames "expr:gte(round(t*1000),n_forced*5000)" -g 150 -sc_threshold 0 -hls_flags split_by_time -f hls -hls_time 5 -start_number 0 output.m3u8
- -force_key_frames :
优化关键帧表达式:用毫秒级整数避免浮点误差 - -g 150 :
强制GOP严格5s(30fps×5=150),禁用场景自适应关键帧

另外我们还没怎么看 M3U8 列表中的内容都是什么含义。
#EXTM3U:必选文件头,标识该文件是符合 M3U8 规范的 HLS 播放列表(不是普通的 M3U 音乐列表),所有合法的 m3u8 文件必须以该行开头,由 FFmpeg 自动生成。#EXT-X-VERSION:3:指定 HLS 协议的版本号为 3#EXT-X-TARGETDURATION:5:声明所有 TS 分片的最大时长(秒) ,播放器会按这个值预留缓冲时间,对应命令中的-hls_time 5,表示所有分片时长≤5 秒,如果设置的-hls_time 6,结果如下

-
#EXT-X-MEDIA-SEQUENCE:0:指定播放列表中第一个 TS 分片的序列号为 0,播放器从序号 0 开始播放 -
#EXTINF:5.000000,output.tsEXTINF是分片时长标签,格式为#EXTINF:<时长>,<分片文件名>: - 5.000000:该分片时长为 5 秒(高精度浮点表示); - output.ts:分片文件名为 output.ts(序列号 0 的分片,FFmpeg 默认省略 0,逻辑上等价于 output0.ts) -
#EXT-X-ENDLIST:播放列表结束标签,标识这是 "点播(VOD)" 类型的 HLS(非直播)
另外我们会发现上述的命令中m3u8文件列表中包含的ts文件太少,导致播放十几秒后就停止播放,主要原因是 m3u8 列表截断
FFmpeg 默认hls_list_size=5(仅保留 5 个分片条目),需强制设置hls_list_size=0(保留所有分片条目),重新生成完整的 m3u8:
ffmpeg -i ../bbb_30fps_gop_60_3mbps.mp4 -c copy -f hls -hls_time 5 -start_number 0 -hls_list_size 0 -hls_playlist_type vod output.m3u8
-hls_list_size 0:保留所有分片条目,不截断-hls_playlist_type vod:标记为点播,确保生成完整列表
可以通过命令查看参数的默认值
ffmpeg -h muxer=hls
