如何让FFmpeg按顺序编码分辨率并实时更新HLS主播放列表?
解决方案:顺序编码HLS分辨率并实时更新主播放列表
要实现按顺序编码分辨率、完成一个就更新主播放列表的需求,核心思路是放弃-var_stream_map的并行编码逻辑,改为分批次单独处理每个分辨率,编码完成后立即将该流的信息追加到主播放列表中。以下是具体实现步骤和注意事项:
关键前提
所有分辨率的编码必须保证关键帧对齐,否则播放时切换分辨率会出现卡顿。需统一设置以下参数:
-g 48 -keyint_min 48:固定关键帧间隔-sc_threshold 0:禁用场景切换自动插入关键帧-hls_time 4:统一切片时长
步骤1:编码第一个分辨率(240p)并生成初始主播放列表
先编码最低分辨率,完成后立即生成可播放的主播放列表:
# 编码240p流 /home/test/ffmpeg -i 1.mp4 \ -map 0:v:0 -map 0:a:0 \ -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 \ -c:a aac -ar 48000 \ -filter:v scale="trunc(oh*a/2)*2:240" -maxrate:v 856k -bufsize:v 1200k -b:a 96k \ -hls_time 4 -hls_list_size 0 -hls_segment_filename 240p_%03d.ts 240p.m3u8 # 生成初始主播放列表 cat > master.m3u8 << EOF #EXTM3U #EXT-X-VERSION:3 #EXT-X-STREAM-INF:BANDWIDTH=952000,RESOLUTION=426x240,CODECS="avc1.4D401E,mp4a.40.2" 240p.m3u8 EOF
此时用户已经可以通过master.m3u8播放240p流。
步骤2:后台编码后续分辨率(480p/720p)并更新主播放列表
将后续分辨率的编码放到后台执行,完成后自动将流信息追加到主播放列表:
# 后台编码480p流(不占用终端) nohup /home/test/ffmpeg -i 1.mp4 \ -map 0:v:0 -map 0:a:0 \ -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 \ -c:a aac -ar 48000 \ -filter:v scale="trunc(oh*a/2)*2:480" -maxrate:v 1498k -bufsize:v 2100k -b:a 128k \ -hls_time 4 -hls_list_size 0 -hls_segment_filename 480p_%03d.ts 480p.m3u8 > /dev/null 2>&1 & # 等待480p编码完成后,更新主播放列表(可通过脚本自动检测,或手动执行) echo -e "\n#EXT-X-STREAM-INF:BANDWIDTH=1626000,RESOLUTION=854x480,CODECS=\"avc1.4D401E,mp4a.40.2\"" >> master.m3u8 echo "480p.m3u8" >> master.m3u8 # 720p编码同理,重复上述逻辑 nohup /home/test/ffmpeg -i 1.mp4 \ -map 0:v:0 -map 0:a:0 \ -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 \ -c:a aac -ar 48000 \ -filter:v scale="trunc(oh*a/2)*2:720" -maxrate:v 2800k -bufsize:v 5600k -b:a 192k \ -hls_time 4 -hls_list_size 0 -hls_segment_filename 720p_%03d.ts 720p.m3u8 > /dev/null 2>&1 & # 720p完成后更新主播放列表 echo -e "\n#EXT-X-STREAM-INF:BANDWIDTH=2992000,RESOLUTION=1280x720,CODECS=\"avc1.4D401E,mp4a.40.2\"" >> master.m3u8 echo "720p.m3u8" >> master.m3u8
自动化脚本(推荐)
可以写一个Shell脚本自动完成顺序编码和主列表更新,避免手动操作:
#!/bin/bash INPUT_FILE="1.mp4" MASTER_PL="master.m3u8" # 初始化主播放列表 cat > $MASTER_PL << EOF #EXTM3U #EXT-X-VERSION:3 EOF # 定义分辨率配置:名称 高度 视频码率 音频码率 RESOLUTIONS=( "240p 240 856k 96k" "480p 480 1498k 128k" "720p 720 2800k 192k" ) for res in "${RESOLUTIONS[@]}"; do read name height v_bitrate a_bitrate <<< "$res" # 计算符合比例的宽度(保证偶数) width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 $INPUT_FILE | awk -F'x' -v h=$height '{print int(h*$1/$2/2)*2}') # 计算总带宽(视频+音频,转成bps) total_bps=$(( ${v_bitrate%k}*1000 + ${a_bitrate%k}*1000 )) # 编码当前分辨率 echo "开始编码${name}..." /home/test/ffmpeg -i $INPUT_FILE \ -map 0:v:0 -map 0:a:0 \ -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 \ -c:a aac -ar 48000 \ -filter:v scale="${width}:${height}" -maxrate:v $v_bitrate -bufsize:v $(( ${v_bitrate%k}*2 ))k -b:a $a_bitrate \ -hls_time 4 -hls_list_size 0 -hls_segment_filename "${name}_%03d.ts" "${name}.m3u8" # 更新主播放列表 echo "已完成${name}编码,更新主播放列表..." echo -e "\n#EXT-X-STREAM-INF:BANDWIDTH=${total_bps},RESOLUTION=${width}x${height},CODECS=\"avc1.4D401E,mp4a.40.2\"" >> $MASTER_PL echo "${name}.m3u8" >> $MASTER_PL done echo "所有分辨率编码完成"
执行脚本后,会按顺序编码每个分辨率,完成一个就立即更新主播放列表,用户可随时播放已完成的流。
内容的提问来源于stack exchange,提问作者user994461
相关产品推荐
相关产品推荐

