音视频处理工具FFmpeg与Java结合的简单使用
一、什么是FFmpeg
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。项目的名称来自MPEG 视频编码标准,前面的"FF"代表"Fast Forward"。 -- 引用自百度百科
二、项目组成
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它包括了领先的音/视频编码库libavcodec等。
libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;
libavcodec:用于各种类型声音/图像编解码;
libavutil:包含一些公共的工具函数;
libswscale:用于视频场景比例缩放、色彩映射转换;
libpostproc:用于后期效果处理;
ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;
ffsever:一个 HTTP 多媒体即时广播串流服务器;
ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;
三、FFmpeg使用场景
3.1、以下列举几个开发中常用的功能:
ffmpeg在Linux下和windows下的视频采集;
视频格式转换;
视频截图功能;
音视频合成功能;
视频添加水印功能;
3.2、谁在使用:
使用FFmpeg作为内核视频播放器:Mplayer,ffplay,射手播放器,暴风影音,KMPlayer,QQ影音等
使用FFmpeg作为内核的Directshow Filter:ffdshow,lav filters等
使用FFmpeg作为内核的转码工具:ffmpeg,格式工厂等
四、如何使用
4.1、FFmpeg如何下载和安装
FFmpeg可以在Linux、windows以及Mac系统中使用,官网提供了各种系统的安装包,下载安装包请移步FFmpeg官网:FFmpeg
安装包下载完成后,对其进行解压,由于我的开发环境为Windows系统,就已Windows安装包为例进行演示。解压后的目录结构如下图所示:
以上目录中,我们使用最多的就是bin目录下的ffmpeg.exe文件了,官网给出了一个简单的例子,我们先来看一下:
转换视频和音频从未如此简单。
图中的命令是Linux下进行操作的,Windows下并无差异,下面我们分别来使用命令行以及Java代码来进行演示视频格式转换、音视频合并以及视频按帧截图功能。
4.2、视频格式转换
首先我们要来到FFmpeg的解压目录,进入到bin目录中,在此目录中打开cmd工具。如下图所示
注:在地址栏中输入“cmd”,按回车,即可在此目录直接打开cmd工具。此处我准备了一个视频文件以及一个音频文件用于下面功能演示。
在cmd工具中键入以下命令即可完成视频格式的转换:
ffmpeg.exe -i test1.mp4 newVideo.avi
具体使用请参考官方文档:ffmpeg Documentation
具体效果如下图所示:
以上是使用命令行进行操作,也可在Java中进行操作:
视频格式转换代码:
/**
* 简易视频处理 -- (cmd(windows): ffmpeg.exe -i test1.mp4 newVideo.avi)
*
* @param ffmpegPath ffmpeg.exe文件路径,可在rest或者admin中进行配置,使用配置文件进行读取
* @param videoInputPath 视频文件路径(输入)
* @param videoOutputPath 转换完成的文件路径(输出)
* @throws IOException
*/
public static void videoConvert(String ffmpegPath, String videoInputPath, String videoOutputPath) throws IOException {
// 构建命令
List<String> command = Lists.newArrayList();
command.add(ffmpegPath);
command.add("-i");
command.add(videoInputPath);
command.add(videoOutputPath);
// 执行操作
ProcessBuilder builder = new ProcessBuilder(command);
Process process = builder.start();
InputStream errorStream = process.getErrorStream();
InputStreamReader isr = new InputStreamReader(errorStream);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
}
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (errorStream != null) {
errorStream.close();
}
}
测试代码:
@Test
public void testVideoConvert() {
FfmpegProperties ffmpegProperties = SpringContextHolder.getBean(FfmpegProperties.class);
try {
FfmpegUtil.videoConvert(ffmpegProperties.getFfmpegFile(), "D:\\tools\\ffmpeg\\bin\\test1.mp4", "D:\\tools\\ffmpeg\\bin\\newVideo.avi");
} catch (IOException e) {
e.printStackTrace();
}
}
由于使用的是SpringBoot,ffmpeg.exe文件路径配置在了appliction.yml中,所以使用了自定义的FfmpegProperties对其进行读取,下述所有例子均使用相同配置,如下所示:
ffmpeg:
ffmpegFile: D:/tools/ffmpeg/bin/ffmpeg.exe #ffmpeg可执行文件路径
outputVideoSuffix: .mp4 #输出视频的后缀名
outputCoverSuffix: .jpg #输出缩略图的后缀名
4.3、音视频合并
音视频合并会使用到两个输入资源,一个音频资源和一个视频资源,此处的例子为一个10s的视频与一个20s的音频,我要达到的效果是视频播放完毕,便退出播放,剩余音频不再进行播放。
ffmpeg.exe -i test2.mp3 -i test1.mp4 -t 10 -y newVideo.mp4
此处:-t代表的是视频的时间,而-y代表覆盖输出文件,直接在cmd工具中执行命令即可
注:在编写命令是,音频文件一定要在视频文件的前面,否则合成出来的视频是有问题的,你会发现音频并没有合成进来。
以上是使用命令行进行操作,也可在Java中进行操作:
/**
* 音视频合并,视频结束,音频结束 -- (cmd(windows): ffmpeg.exe -i test2.mp3 -i test1.mp4 -t 10 -y newVideo.mp4)
*
* @param ffmpegPath ffmpeg.exe文件路径,可在rest或者admin中进行配置,使用配置文件进行读取
* @param audioInputPath 音频文件路径(输入)
* @param videoInputPath 视频文件路径(输入)
* @param time 文件时长
* @param videoOutputPath 转换完成的文件路径(输出)
* @throws IOException
*/
public static void audioVideoMerge(String ffmpegPath, String audioInputPath, String videoInputPath, double time, String videoOutputPath) throws IOException {
// 构建命令
List<String> command = Lists.newArrayList();
command.add(ffmpegPath);
command.add("-i");
command.add(audioInputPath);
command.add("-i");
command.add(videoInputPath);
command.add("-t");
command.add(String.valueOf(time));
command.add("-y");
command.add(videoOutputPath);
// 执行操作
ProcessBuilder builder = new ProcessBuilder(command);
Process process = builder.start();
InputStream errorStream = process.getErrorStream();
InputStreamReader isr = new InputStreamReader(errorStream);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
}
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (errorStream != null) {
errorStream.close();
}
}
测试代码:
@Test
public void testAudioVideoMerge() {
FfmpegProperties ffmpegProperties = SpringContextHolder.getBean(FfmpegProperties.class);
try {
FfmpegUtil.audioVideoMerge(ffmpegProperties.getFfmpegFile(), "D:\\tools\\ffmpeg\\bin\\test2.mp3", "D:\\tools\\ffmpeg\\bin\\test1.mp4", 10, "D:\\tools\\ffmpeg\\bin\\newVideo.mp4");
} catch (IOException e) {
e.printStackTrace();
}
}
4.4、视频按帧截图
这个功能没什么好说的,主要用于用户手动截图或者上传视频生成缩略图。
ffmpeg.exe -ss 00:00:01 -y -i test1.mp4 -vframes 1 new.jpg
此处:-ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持,-vframes 设置转换多少桢(frame)的视频,此命令是获取第一秒第一帧的截图。
注:生成的截图最好使用jpg格式,占用空间较小,如果需要使用其他格式,根据业务需求自行设置即可。
以上是使用命令行进行操作,也可在Java中进行操作:
/**
* 获取第一秒第一帧的缩略图 -- (cmd(windows): ffmpeg.exe -ss 00:00:01 -y -i test1.mp4 -vframes 1 new.jpg)
*
* @param ffmpegPath ffmpeg.exe文件路径,可在rest或者admin中进行配置,使用配置文件进行读取
* @param videoInputPath 视频文件路径(输入)
* @param coverOutputPath 缩略图输出路径
* @throws IOException
*/
public static void getVideoCover(String ffmpegPath, String videoInputPath, String coverOutputPath) throws IOException {
// 构建命令
List<String> command = Lists.newArrayList();
command.add(ffmpegPath);
command.add("-ss");
command.add("00:00:01");
command.add("-y");
command.add("-i");
command.add(videoInputPath);
command.add("-vframes");
command.add("1");
command.add(coverOutputPath);
// 执行操作
ProcessBuilder builder = new ProcessBuilder(command);
Process process = builder.start();
InputStream errorStream = process.getErrorStream();
InputStreamReader isr = new InputStreamReader(errorStream);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
}
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (errorStream != null) {
errorStream.close();
}
}
测试代码:
@Test
public void testGetVideoCover() {
FfmpegProperties ffmpegProperties = SpringContextHolder.getBean(FfmpegProperties.class);
try {
FfmpegUtil.getVideoCover(ffmpegProperties.getFfmpegFile(), "D:\\tools\\ffmpeg\\bin\\test1.mp4", "D:\\tools\\ffmpeg\\bin\\new.jpg");
} catch (IOException e) {
e.printStackTrace();
}
}
以上就是FFmpeg的简单使用,如果有更为复杂的需求,直接参考官方文档进行使用即可。