如何利用FFMPEG Java包装库创建HLS主播放列表及分段元数据
我完全懂你现在的困扰——不少Java FFmpeg包装库能搞定基础编码和单码率HLS分段,但要生成带多分辨率、多带宽的主播放列表和对应变体流,确实没那么容易。结合你的需求,我给你两个靠谱的解决方案:
一、用活跃的Java FFmpeg包装库实现:Jaffree
Jaffree是目前维护最活跃的Java FFmpeg包装库之一,它既可以封装FFmpeg底层API,也能灵活构建FFmpeg命令并执行,对HLS多变体流的支持非常到位,完全能满足你生成主元数据文件+多码率分段的需求。
代码示例(生成多码率HLS主播放列表)
import io.github.kokorin.jaffree.ffmpeg.FFmpeg; import io.github.kokorin.jaffree.ffmpeg.Input; import io.github.kokorin.jaffree.ffmpeg.Output; public class MultiBitrateHlsGenerator { public static void main(String[] args) { FFmpeg.atPath() // 输入源文件 .addInput(Input.fromPath("input.mp4")) // 生成720p变体流:码率2M,带宽2500k .addOutput(Output.toPath("hls/720p.m3u8") .addArguments("-c:v", "libx264", "-crf", "23", "-preset", "medium") .addArguments("-c:a", "aac", "-b:a", "128k") .addArguments("-s", "1280x720", "-b:v", "2000k") .addArguments("-hls_time", "10", "-hls_list_size", "0")) // 生成480p变体流:码率1M,带宽1200k .addOutput(Output.toPath("hls/480p.m3u8") .addArguments("-c:v", "libx264", "-crf", "23", "-preset", "medium") .addArguments("-c:a", "aac", "-b:a", "128k") .addArguments("-s", "854x480", "-b:v", "1000k") .addArguments("-hls_time", "10", "-hls_list_size", "0")) // 生成主播放列表,并映射变体流的元数据 .addArguments("-master_pl_name", "master.m3u8") .addArguments("-var_stream_map", "v:0,a:0,bandwidth=2500000,resolution=1280x720,name:720p " + "v:1,a:1,bandwidth=1200000,resolution=854x480,name:480p") .execute(); } }
这段代码会自动生成:
master.m3u8主播放列表(包含两个变体流的引用及带宽、分辨率元数据)720p.m3u8/480p.m3u8媒体播放列表- 对应分辨率的TS分段文件
二、直接调用FFmpeg命令行(你提到的方案)
如果包装库的灵活性达不到预期,直接执行FFmpeg命令是最稳妥的选择——毕竟FFmpeg本身对HLS多码率的支持非常成熟,能覆盖所有自定义需求。
示例FFmpeg命令
ffmpeg -i input.mp4 \ # 720p变体流配置 -c:v libx264 -crf 23 -preset medium -s 1280x720 -b:v 2000k \ -c:a aac -b:a 128k \ -hls_time 10 -hls_list_size 0 -hls_segment_filename "hls/720p_%03d.ts" hls/720p.m3u8 \ # 480p变体流配置 -c:v libx264 -crf 23 -preset medium -s 854x480 -b:v 1000k \ -c:a aac -b:a 128k \ -hls_time 10 -hls_list_size 0 -hls_segment_filename "hls/480p_%03d.ts" hls/480p.m3u8 \ # 生成主播放列表 -master_pl_name master.m3u8 \ -var_stream_map "v:0,a:0,bandwidth=2500000,resolution=1280x720,name:720p v:1,a:1,bandwidth=1200000,resolution=854x480,name:480p"
Java中执行命令的示例
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class FFmpegCommandRunner { public static void main(String[] args) throws IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder( "ffmpeg", "-i", "input.mp4", // 720p参数 "-c:v", "libx264", "-crf", "23", "-preset", "medium", "-s", "1280x720", "-b:v", "2000k", "-c:a", "aac", "-b:a", "128k", "-hls_time", "10", "-hls_list_size", "0", "-hls_segment_filename", "hls/720p_%03d.ts", "hls/720p.m3u8", // 480p参数 "-c:v", "libx264", "-crf", "23", "-preset", "medium", "-s", "854x480", "-b:v", "1000k", "-c:a", "aac", "-b:a", "128k", "-hls_time", "10", "-hls_list_size", "0", "-hls_segment_filename", "hls/480p_%03d.ts", "hls/480p.m3u8", // 主播放列表参数 "-master_pl_name", "master.m3u8", "-var_stream_map", "v:0,a:0,bandwidth=2500000,resolution=1280x720,name:720p v:1,a:1,bandwidth=1200000,resolution=854x480,name:480p" ); // 重定向错误流到输出流,方便排查问题 pb.redirectErrorStream(true); Process process = pb.start(); // 启动线程读取输出,避免进程阻塞 new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 等待命令执行完成 int exitCode = process.waitFor(); System.out.println("FFmpeg执行完成,退出码:" + exitCode); } }
关键参数说明
-master_pl_name: 指定生成的主播放列表文件名-var_stream_map: 核心参数,用于映射变体流并指定带宽、分辨率、名称等元数据(如果不显式指定带宽,FFmpeg会自动计算)-hls_time: 每个媒体分段的时长(单位:秒)-hls_list_size: 设置为0表示保留所有分段文件,不自动清理旧分段
注意事项
- 确保使用FFmpeg 4.0及以上版本,旧版本可能不支持部分HLS多变体流参数
- 用Java包装库时,注意依赖的FFmpeg平台包要适配你的操作系统(比如Jaffree的
ffmpeg-platform会自动适配) - 处理大文件时,一定要用线程读取输出流,避免缓冲区满导致进程阻塞
内容的提问来源于stack exchange,提问作者Caffeinated Coder
相关产品推荐
相关产品推荐

