.ts 文件时长问题及 -hls_time 参数详解

😄作者简介: 小曾同学.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.ts

    复制代码
    EXTINF是分片时长标签,格式为#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

相关推荐
Hello.Reader16 天前
用纯 Go 实现一个 AES-128 加密 m3u8 视频下载器(不依赖 ffmpeg)
golang·ffmpeg·音视频·m3u8
加油20191 个月前
音视频处理(三):hls协议和m3u8详解和视频下载爬虫实战
爬虫·音视频·hls·m3u8·mpeg-2·mpeg2-ts·电视迷
eguid_12 个月前
【HLS】Java实现统计HLS的m3u8清单中所有ts切片的视频持续时长
java·音视频·hls·1024程序员节·m3u8·ts时长
humors2212 个月前
批量M3U8转MP4工具
ffmpeg·视频·mp4·多媒体·转换·m3u8
xcg3401233 个月前
SpringBoot结合Vue 播放 m3u8 格式视频
音视频·hls·m3u8·流媒体播放
瘦马3 个月前
如何播放 M3U8 格式的视频
音视频·m3u8·m3u8在线播放
superconvert7 个月前
最快的流媒体服务器搭建 smart_rtmpd
http·webrtc·rtmp·h264·hls·无人直播·dash·rtsp·gb28181·srt·m3u8·vlc·sfu·obs·flv
fengchengwu20129 个月前
python下载m3u8格式视频
开发语言·python·m3u8