尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
FFmpeg 转码 DASH/HLS 实战:从踩坑到生产部署
流媒体这块我踩过的坑不算少但最让我印象深刻的还是第一次用 FFmpeg 做 DASH 和 HLS 转码那会儿。当时手头有一个两小时左右的课程录像想着切成自适应码率让不同网络环境的用户都能流畅播放结果一条命令跑下去出来的分片文件命名乱七八糟m3u8 里的路径全是绝对路径扔到服务器上直接 404。后来翻了不少文档、试了各种参数组合才算把整套流程跑通。这篇文章就把我从零摸索 FFmpeg 转码 DASH/HLS 的完整经验整理出来包括为什么要选这两种格式、参数怎么配、踩过哪些坑、怎么排查问题。不管你是刚接触流媒体转码的新手还是已经用过 FFmpeg 但想搞清楚细节的老手应该都能从里面找到有用的东西。1. 先搞清楚 DASH 和 HLS 到底解决什么问题1.1 为什么不能直接扔一个 MP4 上去很多人一开始会想我直接把 MP4 文件放到服务器上用户点开看不就行了吗。小文件确实没问题但一旦视频超过几百兆、时长超过十几分钟问题就来了。首先是起播速度MP4 的 moov box 如果放在文件末尾浏览器必须把整个文件下载完才能开始播放用户等半天黑屏。其次是自适应码率用户在地铁上用 4G 看 1080p 肯定卡但你没法让同一个 MP4 文件根据网速自动切换清晰度。再就是拖动进度条MP4 的随机访问需要依赖索引大文件拖起来体验很差。HLS 和 DASH 就是为解决这些问题设计的。它们的核心思路是一样的把整个视频切成一个个小分片通常几秒一个然后通过一个索引文件HLS 是 m3u8DASH 是 mpd告诉播放器每个分片的位置和码率信息。播放器根据当前网速动态选择合适码率的分片网速好就拉高码率网速差就切低码率用户几乎感知不到切换过程。同时因为分片很小起播只需要下载前几个分片速度非常快。1.2 HLS 和 DASH 各自的脾气HLS 是苹果主导的协议兼容性极好基本上所有浏览器、移动端、智能电视都支持。它的索引文件是 m3u8 格式本质是一个文本播放列表。HLS 的分片格式通常是 TSMPEG-TS现在也支持 fMP4。HLS 的缺点是延迟相对较高因为苹果规定分片时长不能太短标准直播延迟一般在 6 到 30 秒。DASH 是 MPEG 组织推出的国际标准全称是 Dynamic Adaptive Streaming over HTTP。它更开放、更灵活索引文件是 XML 格式的 mpd。DASH 支持更短的分片和更低的延迟理论上可以做到 2 到 5 秒甚至更低。但 DASH 在苹果生态里的原生支持不如 HLSSafari 对 DASH 的支持一直比较有限。实际项目中我的选择策略是这样的如果目标用户主要是苹果设备或者需要最大兼容性优先 HLS如果追求低延迟、做直播场景或者面向安卓和桌面浏览器为主DASH 更合适。当然很多项目会同时输出两种格式让播放器根据环境自动选择这也是我后面要重点讲的方案。1.3 FFmpeg 在转码流水线里的位置FFmpeg 在这个流程里扮演的是“切片工厂”的角色。它负责把源视频解码然后按照指定的码率、分辨率、编码格式重新编码再切成符合 HLS 或 DASH 规范的分片最后生成索引文件。整个过程中 FFmpeg 要处理的事情包括视频解码、缩放、编码、音频重采样、编码、分片、索引生成。这也是为什么一条 FFmpeg 命令里参数那么多因为每个环节都有讲究。理解了这一点后面看那些参数就不会觉得莫名其妙了。比如-g参数控制关键帧间隔它直接影响分片能否在指定位置切开-hls_time控制分片时长但它不是绝对的实际分片点会落在最近的关键帧上。这些细节我在后面会逐一展开。2. 转码前的准备工作与源文件分析2.1 先摸清源文件的底细动手转码之前我强烈建议先用ffprobe把源文件的信息看一遍。这一步很多人会跳过结果转码时才发现源文件是可变帧率、音频采样率奇怪、或者分辨率不是标准值导致后面各种问题。ffprobe -v error -show_format -show_streams -print_format json input.mp4这条命令会输出源文件的详细信息重点看几个字段codec_name编码格式、width和height分辨率、r_frame_rate帧率、sample_rate音频采样率、channels声道数、duration时长。我一般会把这些信息记下来作为后面设置参数的依据。比如源文件是 1920x1080、30fps、AAC 44100Hz 立体声那我在做多码率输出时就会以这个为基准往下切。如果源文件本身码率就很低硬拉高码率输出只会浪费带宽画质也不会有提升。2.2 确定输出码率阶梯多码率转码的核心是设计一个合理的码率阶梯bitrate ladder。所谓阶梯就是从高到低几个不同码率的版本让播放器根据网速选择。设计阶梯时我遵循几个原则相邻档位的码率差距不要太大一般 1.5 到 2 倍比较合适最高档不要超过源文件的实际质量最低档要保证在弱网下也能流畅播放。下面是我常用的一个 1080p 源文件的码率阶梯供参考档位分辨率视频码率音频码率适用场景11920x10805000 kbps128 kbps光纤/WiFi21280x7202800 kbps128 kbps良好 4G3854x4801400 kbps96 kbps一般 4G4640x360800 kbps96 kbps弱网 3G5426x240400 kbps64 kbps极弱网这个阶梯不是固定的要根据实际内容调整。比如动画类内容压缩率高可以适当降低码率体育赛事、动作片画面变化剧烈码率要给足。我一般会先转一版出来实际看看画质和文件大小再微调。2.3 编码器选型libx264 还是硬件编码FFmpeg 支持的 H.264 编码器有好几个最常用的是libx264软件编码和h264_nvenc、h264_qsv、h264_vaapi等硬件编码器。软件编码画质好、压缩率高但速度慢吃 CPU硬件编码速度快但同码率下画质略差且依赖特定硬件。我的经验是如果是一次性转码、对画质要求高、服务器 CPU 核数够用libx264如果是批量转码、追求速度、有独立显卡或支持 QSV 的 CPU用硬件编码。下面这条命令可以查看当前 FFmpeg 支持哪些编码器ffmpeg -encoders | grep -E 264|265对于libx264我常用的预设是medium或slowslow画质更好但速度慢不少。crf值一般设 20 到 23数值越小画质越好文件越大。不过做多码率转码时我通常用固定码率-b:v而不是 CRF因为要精确控制每个档位的码率方便播放器做码率切换决策。3. HLS 转码实操从单码率到多码率3.1 单码率 HLS 转码的最小命令先从一个最简单的单码率 HLS 转码开始把流程跑通再往上加东西。下面这条命令把 MP4 转成 HLSffmpeg -i input.mp4 \ -c:v libx264 -preset medium -b:v 2800k \ -c:a aac -b:a 128k \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename output/segment_%03d.ts \ output/index.m3u8逐段解释一下。-c:v libx264指定视频编码器-preset medium是编码速度预设-b:v 2800k是视频目标码率。-c:a aac -b:a 128k指定音频用 AAC 编码码率 128k。-hls_time 6表示每个分片目标时长 6 秒。-hls_playlist_type vod表示这是点播内容会在 m3u8 头部加上#EXT-X-PLAYLIST-TYPE:VOD标记。-hls_segment_filename指定分片文件的命名模板%03d会被替换成递增序号。注意-hls_time只是目标值实际分片时长取决于关键帧位置。如果关键帧间隔是 4 秒设-hls_time 6实际可能切出 8 秒的分片。要精确控制需要同时设置-g参数。3.2 关键帧间隔与分片时长的配合这是很多人第一次做 HLS 时最容易忽略的点。HLS 分片必须在关键帧I 帧处切开否则播放器无法独立解码某个分片。所以-hls_time和关键帧间隔必须配合设置。关键帧间隔由-g参数控制单位是帧数。假设视频帧率是 30fps想要每 2 秒一个关键帧那-g就设 60。如果想让分片时长是 6 秒那关键帧间隔最好能整除 6比如 2 秒或 3 秒一个关键帧这样-hls_time 6就能精确切出 6 秒的分片。ffmpeg -i input.mp4 \ -c:v libx264 -preset medium -b:v 2800k \ -g 60 -keyint_min 60 -sc_threshold 0 \ -c:a aac -b:a 128k \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename output/segment_%03d.ts \ output/index.m3u8这里多了三个参数-g 60设关键帧间隔为 60 帧30fps 下即 2 秒-keyint_min 60设最小关键帧间隔-sc_threshold 0禁用场景切换触发的额外关键帧。为什么要禁用场景切换关键帧因为场景切换会插入额外的 I 帧打乱我们精心设计的关键帧间隔导致分片时长不均匀。做流媒体转码时均匀的分片比场景切换优化更重要。3.3 多码率 HLS一条命令搞定单码率只是热身实际项目里基本都是多码率。FFmpeg 可以用一条命令同时输出多个码率靠的是filter_complex里的split滤镜把视频流复制成多路再分别缩放和编码。ffmpeg -i input.mp4 \ -filter_complex \ [0:v]split3[v1][v2][v3]; \ [v1]scale1920:1080[v1out]; \ [v2]scale1280:720[v2out]; \ [v3]scale854:480[v3out] \ -map [v1out] -c:v:0 libx264 -b:v:0 5000k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map [v2out] -c:v:1 libx264 -b:v:1 2800k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map [v3out] -c:v:2 libx264 -b:v:2 1400k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map a:0 -c:a aac -b:a 128k \ -map a:0 -c:a aac -b:a 128k \ -map a:0 -c:a aac -b:a 96k \ -var_stream_map v:0,a:0 v:1,a:1 v:2,a:2 \ -master_pl_name master.m3u8 \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename output/v%v/segment_%03d.ts \ output/v%v/index.m3u8这条命令信息量比较大我拆开讲。filter_complex里先把视频流 split 成三路分别 scale 到三个分辨率。然后-map把每一路映射到输出-c:v:0、-c:v:1、-c:v:2分别指定三路视频的编码参数。音频也映射了三次分别配不同码率。关键是-var_stream_map它定义了视频流和音频流怎么组合成不同的 variant。v:0,a:0表示第一个 variant 用第一路视频和第一路音频以此类推。-master_pl_name master.m3u8指定主播放列表的文件名这个文件里会列出所有 variant 的 m3u8 地址。-hls_segment_filename里的%v会被替换成 variant 的索引这样每个码率的分片放在不同目录里不会互相覆盖。实操心得-var_stream_map里的空格分隔符很容易写错如果报错说 stream map 解析失败先检查这里。另外%v在文件名模板里必须出现否则所有 variant 的分片会写到同一个文件里互相覆盖。3.4 用 fMP4 替代 TS 分片HLS 传统上用 TS 分片但 TS 有一些缺点封装开销大每个分片都有 PAT/PMT 表、不支持现代编码格式的一些特性。fMP4fragmented MP4分片更高效也是苹果现在推荐的方式。切换方法很简单加一个-hls_segment_type fmp4参数分片扩展名改成.m4sffmpeg -i input.mp4 \ -c:v libx264 -preset medium -b:v 2800k -g 60 -keyint_min 60 -sc_threshold 0 \ -c:a aac -b:a 128k \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_type fmp4 \ -hls_fmp4_init_filename init.mp4 \ -hls_segment_filename output/segment_%03d.m4s \ output/index.m3u8fMP4 模式下会多出一个 init 文件初始化分片里面包含编码器的初始化信息播放器需要先加载它才能解码后续分片。m3u8 里会通过#EXT-X-MAP标签引用这个文件。实测下来 fMP4 的分片体积比 TS 小 5% 到 10%对于大文件来说节省的带宽很可观。4. DASH 转码实操与参数详解4.1 DASH 转码的基本命令DASH 的转码命令结构和 HLS 很像主要区别在于输出格式和分片参数。下面是一个单码率 DASH 的例子ffmpeg -i input.mp4 \ -c:v libx264 -preset medium -b:v 2800k -g 60 -keyint_min 60 -sc_threshold 0 \ -c:a aac -b:a 128k \ -f dash \ -seg_duration 6 \ -use_template 1 \ -use_timeline 1 \ -init_seg_name init-$RepresentationID$.m4s \ -media_seg_name chunk-$RepresentationID$-$Number%05d$.m4s \ output/manifest.mpd-f dash指定输出格式为 DASH。-seg_duration 6是分片时长单位秒。-use_template 1表示使用 SegmentTemplate 方式描述分片地址这种方式比 SegmentList 更简洁mpd 文件更小。-use_timeline 1启用 SegmentTimeline对于可变时长分片更友好。-init_seg_name和-media_seg_name定义初始分片和媒体分片的命名模板$RepresentationID$会被替换成码率标识$Number%05d$是分片序号。4.2 多码率 DASH 的写法多码率 DASH 同样用filter_complex做分流但输出参数和 HLS 不同ffmpeg -i input.mp4 \ -filter_complex \ [0:v]split3[v1][v2][v3]; \ [v1]scale1920:1080[v1out]; \ [v2]scale1280:720[v2out]; \ [v3]scale854:480[v3out] \ -map [v1out] -c:v:0 libx264 -b:v:0 5000k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map [v2out] -c:v:1 libx264 -b:v:1 2800k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map [v3out] -c:v:2 libx264 -b:v:2 1400k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map a:0 -c:a aac -b:a 128k \ -map a:0 -c:a aac -b:a 128k \ -map a:0 -c:a aac -b:a 96k \ -f dash \ -seg_duration 6 \ -use_template 1 \ -use_timeline 1 \ -adaptation_sets id0,streamsv id1,streamsa \ -init_seg_name init-$RepresentationID$.m4s \ -media_seg_name chunk-$RepresentationID$-$Number%05d$.m4s \ output/manifest.mpd这里的关键是-adaptation_sets它定义了自适应集。id0,streamsv表示所有视频流归到一个自适应集id1,streamsa表示所有音频流归到另一个。播放器会在同一个自适应集内根据带宽切换码率。视频和音频分开成不同自适应集这样切换视频码率时音频不会中断体验更好。4.3 DASH 的 SegmentTemplate 与 SegmentList 怎么选DASH 的 mpd 文件描述分片地址有两种主要方式SegmentTemplate 和 SegmentList。SegmentTemplate 用一个模板加变量来描述所有分片mpd 文件很小适合分片数量多的场景。SegmentList 把每个分片的地址都列出来mpd 文件会很大但兼容性更好一些老播放器只认这种方式。我一般优先用 SegmentTemplate因为 mpd 小、加载快。只有在遇到播放器兼容问题时才退回 SegmentList。切换方式是-use_template 0 -use_timeline 0但这样 FFmpeg 会生成 SegmentList 形式的 mpd。实测下来主流播放器如 dash.js、ExoPlayer、Shaka Player对 SegmentTemplate 支持都很好不用担心。4.4 HLS 和 DASH 同时输出实际项目里经常需要同时输出 HLS 和 DASH让播放器根据环境选择。FFmpeg 可以在一条命令里同时输出两种格式方法是把输出参数分成两组每组对应一个输出文件ffmpeg -i input.mp4 \ -filter_complex \ [0:v]split2[v1][v2]; \ [v1]scale1280:720[v1out]; \ [v2]scale854:480[v2out] \ -map [v1out] -c:v:0 libx264 -b:v:0 2800k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map [v2out] -c:v:1 libx264 -b:v:1 1400k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map a:0 -c:a aac -b:a 128k \ -map a:0 -c:a aac -b:a 96k \ -var_stream_map v:0,a:0 v:1,a:1 \ -master_pl_name master.m3u8 \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename hls/v%v/seg_%03d.ts \ hls/v%v/index.m3u8 \ -map [v1out] -c:v:0 libx264 -b:v:0 2800k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map [v2out] -c:v:1 libx264 -b:v:1 1400k -preset medium -g 60 -keyint_min 60 -sc_threshold 0 \ -map a:0 -c:a aac -b:a 128k \ -map a:0 -c:a aac -b:a 96k \ -f dash \ -seg_duration 6 \ -use_template 1 \ -use_timeline 1 \ -adaptation_sets id0,streamsv id1,streamsa \ -init_seg_name init-$RepresentationID$.m4s \ -media_seg_name chunk-$RepresentationID$-$Number%05d$.m4s \ dash/manifest.mpd这条命令会同时生成 HLS 和 DASH 两套文件。注意视频流被编码了两次HLS 一次、DASH 一次CPU 开销翻倍。如果源文件很大建议分两次跑或者用硬件编码加速。另外filter_complex里的 split 只需要做一次两套输出共用同一个 filter 结果这样能省一次解码和缩放的开销。5. 转码后的验证与常见问题排查5.1 怎么验证转码结果是否正确转码跑完不代表就完事了必须验证。我一般分三步走。第一步看文件结构确认分片文件、索引文件、init 文件都在预期位置数量对不对。第二步用ffprobe检查分片能否正常解码ffprobe -v error -show_format -show_streams output/segment_000.ts第三步用播放器实际播放。HLS 可以用浏览器直接打开 m3u8 地址测试Chrome 需要装扩展Safari 原生支持DASH 可以用在线播放器或者 dash.js 的参考播放器测试。我习惯用ffplay快速验证ffplay output/index.m3u8 ffplay output/manifest.mpdffplay能播放说明分片和索引基本没问题。如果ffplay报错根据错误信息定位问题比在浏览器里调试方便得多。5.2 常见问题速查表下面这张表是我在实际项目中遇到过的典型问题和解决方法按出现频率排序问题现象可能原因解决方法播放器报 404m3u8/mpd 里是绝对路径用相对路径或加-hls_base_url参数分片时长不均匀关键帧间隔不固定设-g和-keyint_min相等-sc_threshold 0音视频不同步音频采样率或时间基不一致加-async 1或统一音频参数切换码率时卡顿各档位关键帧不对齐所有档位用相同的-g和-keyint_min首屏加载慢分片太大或 moov 位置不对减小-hls_time加-movflags faststart音频有杂音音频码率太低或编码器问题提高音频码率到 128k 以上转码速度极慢用了 slow 预设或软件编码换 medium/fast 预设或启用硬件编码m3u8 无法播放playlist_type 没设或格式错误点播加-hls_playlist_type vod5.3 路径问题的根治方法路径问题是 HLS/DASH 转码里最烦人的因为本地测试好好的一上服务器就 404。根本原因是 FFmpeg 生成索引文件时分片路径的写法取决于你给的输出路径。如果你给的是绝对路径m3u8 里就是绝对路径给相对路径就是相对路径。我的做法是转码时cd到输出目录的父目录然后用相对路径输出。比如输出到output/index.m3u8分片模板写output/segment_%03d.ts这样 m3u8 里就是segment_000.ts这样的相对路径。另外 HLS 有个-hls_base_url参数可以给所有分片路径加一个前缀适合分片和索引不在同一目录的情况。注意-hls_base_url加的前缀会直接拼在分片文件名前面不会自动加斜杠。如果前缀是segments/记得带上末尾斜杠否则会拼成segmentssegment_000.ts。5.4 转码性能优化的几个实操技巧转码是 CPU 密集型任务优化性能能省不少时间和电费。我总结了几条实用的第一能用硬件编码就用硬件编码。NVIDIA 显卡用h264_nvencIntel 核显用h264_qsv实测速度能快 5 到 10 倍。画质损失在同码率下大概 5% 到 10%对于非专业场景完全够用。第二合理设置 preset。medium是速度和画质的平衡点fast更快但画质略差slow更慢但画质更好。批量转码时我用fast精品内容用slow。第三多码率转码时低分辨率档位可以用更快的 preset。因为低分辨率本身细节就少用fast和slow的差别肉眼几乎看不出来但速度能快不少。第四如果服务器有多核FFmpeg 默认会利用多线程但filter_complex里的 scale 滤镜可能是单线程的。可以用-filter_threads参数增加滤镜线程数或者把 scale 操作分散到不同输出里。6. 生产环境部署的几点经验6.1 分片文件的服务端配置转码出来的分片是静态文件用 Nginx 或类似服务托管就行。但有几个配置点要注意。首先是 MIME 类型m3u8 要配成application/vnd.apple.mpegurlmpd 配成application/dashxmlts 配成video/mp2tm4s 配成video/iso.segment。MIME 类型不对某些播放器会拒绝加载。其次是缓存策略。分片文件内容不会变可以设很长的缓存时间比如一年。但索引文件m3u8/mpd在直播场景下会不断更新不能缓存点播场景下内容固定可以缓存但时间短一些方便更新。我一般给索引文件设Cache-Control: no-cache分片设Cache-Control: max-age31536000。再就是 CORS。如果播放器和分片不在同一个域名下必须配置跨域头否则浏览器会拦截。Nginx 里加add_header Access-Control-Allow-Origin *;就行但生产环境建议限制具体域名。6.2 存储与 CDN 的配合分片文件数量多、单个小直接放源站服务器上用户多了带宽扛不住。标准做法是上传到对象存储前面挂 CDN。上传时注意保持目录结构索引文件里的相对路径才能正确解析。CDN 配置里要确保 m3u8 和 mpd 不被缓存或者缓存时间很短分片文件长缓存。我踩过的一个坑是CDN 默认会缓存所有文件结果更新了 m3u8 但 CDN 还在返回旧版本播放器一直拉到已经删除的旧分片报 404。后来给索引文件单独配了不缓存的规则才解决。所以 CDN 配置一定要区分索引文件和分片文件。6.3 批量转码的自动化思路实际项目里往往不是转一个文件而是成百上千个。手动跑命令不现实得写脚本自动化。我的做法是用 Shell 或 Python 写一个批处理脚本遍历输入目录对每个文件调用 FFmpeg转码完成后把输出上传到对象存储最后生成一份转码报告。脚本里要注意几点并发数控制别一次跑太多把 CPU 打满失败重试网络抖动或临时错误导致失败的要能重跑日志记录每个文件的转码耗时、输出大小、是否成功都要记下来方便排查。我用 Python 的subprocess调用 FFmpeg配合concurrent.futures做并发控制跑几百个文件很稳。6.4 监控与告警生产环境跑转码监控不能少。我一般监控几个指标转码队列长度积压太多说明产能不足、单个文件转码耗时突然变长说明有问题、失败率超过阈值告警、磁盘使用率分片文件很占空间。这些指标可以用 Prometheus 采集Grafana 展示超过阈值发告警。转码失败的原因五花八门源文件损坏、磁盘满、内存不足、编码器崩溃都有可能。我的经验是给 FFmpeg 加-loglevel warning把警告和错误输出到日志文件出问题时先看日志。大部分问题日志里都有明确提示比瞎猜快得多。7. 一些容易忽略的细节和进阶技巧7.1 音频处理的门道音频这块很多人不重视但实际体验中音频问题比视频问题更影响观感。几个要点音频采样率统一用 44100Hz 或 48000Hz别用奇怪的采样率声道数根据内容定一般立体声就够语音内容可以转单声道省带宽音频码率别低于 64k否则会有明显压缩感128k 是甜点值。如果源文件音频有问题比如音量忽大忽小可以在转码时加音频滤镜处理。比如-af loudnorm做响度归一化-af volume1.5调音量。不过音频滤镜会增加处理时间非必要不加。7.2 字幕和元数据的处理如果源文件有字幕转码时可以选择烧录进视频或者作为独立字幕流输出。HLS 支持 WebVTT 字幕DASH 支持 TTML 和 WebVTT。烧录字幕用-vf subtitlessub.srt独立字幕流用-c:s webvtt。元数据方面可以用-metadata titlexxx设置标题-metadata commentxxx设置备注这些信息会写进分片和索引文件。7.3 用两遍编码提升画质对画质要求高的场景可以用两遍编码two-pass encoding。第一遍分析视频内容生成统计信息第二遍根据统计信息分配码率画质比单遍好 5% 到 10%。代价是转码时间翻倍。命令上第一遍加-pass 1 -an -f null /dev/null第二遍加-pass 2。多码率场景下每个档位都要跑两遍时间开销很大一般只对最高档位用。7.4 分片时长的取舍分片时长是个需要权衡的参数。分片短起播快、码率切换灵敏但文件数量多、索引文件大、HTTP 请求多。分片长请求少、开销小但起播慢、切换迟钝。点播场景我一般用 6 到 10 秒直播场景用 2 到 4 秒。苹果官方推荐 HLS 分片时长是 6 秒这个值在大多数场景下都是合理的。7.5 测试不同网络环境的播放效果转码完成后最好模拟不同网络环境测试播放效果。Chrome DevTools 的 Network 面板可以限速模拟 3G、4G、WiFi 等环境观察播放器是否正常切换码率、切换时是否卡顿。这一步能发现很多参数配置上的问题比如码率阶梯不合理、切换阈值设置不当等。我一般会重点测弱网环境因为那是最容易出问题的场景。整套流程跑下来从源文件分析、码率阶梯设计、HLS/DASH 转码、到验证和部署每个环节都有细节要注意。我自己的体会是FFmpeg 的参数看着多但理解了每个参数背后的原理配置起来就有章可循了。最关键的是多动手试转一版出来实际播放看看比看十篇文档都管用。
RELATED

相关推荐

RS485 Modbus组网避坑:多主一从与一主多从怎么选?

RS485 Modbus组网避坑:多主一从与一主多从怎么选?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/16 1:31:28
两串电池被动均衡方案:MP2642GR-Z与PIC18F87J50实战

两串电池被动均衡方案:MP2642GR-Z与PIC18F87J50实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/16 1:31:28
直播观看时长统计的C语言实现:CSV解析与哈希表聚合实战

直播观看时长统计的C语言实现:CSV解析与哈希表聚合实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/16 1:26:28
MORE NEWS

更多资讯

📰

Docker Desktop 数据迁移完整流程(Windows 10/11 x64)

前提 两台机器都是 Windows 10 / 11 x64 拥有管理员权限 知道旧机器 Docker Desktop 版本(例如4.31.1),很重要!!!! 旧机器已安装 WSL 功能 WSL功能检查和准备 如果安装过Docker Desktop会自动…

📰

Python本地实现横向联邦学习FedAvg全流程

简介:本资源是一份面向机器学习与分布式系统初学者的横向联邦学习本地模拟实践项目,聚焦Python生态下的端到端实现,解决隐私敏感场景下多设备协同建模的核心问题。资源共25个文件,含5个核心Python源码(server.py、clie…

📰

【图像去噪】论文精读:TM-BSN: Triangular-Masked Blind-Spot Network for Real-World Self-Supervised Image Denoisi

请先看【专栏介绍文章】:【图像去噪(Image Denoising)】关于【图像去噪】专栏的相关说明,包含适配人群、专栏简介、专栏亮点、阅读方法、定价理由、品质承诺、关于更新、去噪概述、文章目录、资料汇总、问题汇总(更新中) 文章目录 前言 摘要详解 1. Introduction 引言详解…

📰

YOLO12+A2C2f+DFFN:水果新鲜度检测系统的实战全解析

1. YOLO12-A2C2f-DFFN这套组合,一开始是怎么定的产品端给的诉求很直接:在分拣线或者农户收货的现场,拍一张番石榴的照片,系统能同时告诉我三个信息——画面里的果在哪里、这个果目前是什么状态、这种状态该归到哪一档。这个需求拆…

📰

PyTorch原型网络实战:少样本分类的轻量级方案与代码详解

简介:这是一个基于PyTorch实现的Prototypical Networks(原型网络)少样本学习项目,对应arXiv论文1703.05175,主要面向机器学习初学者、算法研究人员以及需要在少量标注样本条件下完成图像分类任务的开发者。项目以Omnig…

📰

2S电池组主动均衡系统:ATmega644+MP2642GR-Z设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬