背景
最近有一个需求,需要将一个mp4的视频转换为avi的视频,为此,查找资料找到了ffmpeg、Javacv、Xuggler等方法,其中Xuggler我导不入依赖,就放弃了,这里简单记录一下FFmpeg和Javacv的使用。
实现
FFmpeg
FFmpeg(Fast Forward MPEG)是一个强大的开源多媒体框架和工具集,用于处理音频、视频、多媒体流以及多种多媒体文件格式。它是一个跨平台工具,可在多个操作系统上运行,包括Linux、Windows、MacOS X等。
一:准备工作
1、安装FFmpeg。找到FFmpeg的官网,选择自己对应的系统的FFmpeg压缩包,下载并解压到自己的电脑。
2、将FFmpeg运行路径添加到环境变量中。
二:代码实现
/** Windows系统* */
public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
/** Linux系统* */
public static final boolean IS_LINUX = System.getProperty("os.name").toLowerCase().contains("linux");
/**
* ffmpeg -i input.mp4 -c:v mjpeg output.avi
* 使用ffmpeg转码
* @return
*/
public void ffmpegDecode(String path,String newFilePath,Integer width,Integer height) {
/**
* -an 表示不保留音频流。
* -codec(-c) 指定输入输出的解码编码器 codec指定为copy则将输入流直接复制到输出流不进行编解码操作。
* -r 设置某个流的帧率
*-f 强制设置输入输出的文件格式,默认情况下ffmpeg会根据文件后缀名判断文件格式.
*
*/
String cmd;
/*如果是windows系统**/
if (IS_WINDOWS) {
log.info("Windows系统");
cmd="ffmpeg -i "+path;
if (null!=width&&null!=height){
cmd=cmd+" -s "+width+"x"+height;
}
cmd=cmd+" -c:v mjpeg "+newFilePath;
winExec(cmd, newFilePath, () -> dosomething);
}
/*如果是Linux系统**/
if (IS_LINUX){
log.info("Linux系统");
cmd = "/usr/local/ffmpeg/bin/ffmpeg -i " + path;
if (null!=width&&null!=height){
cmd=cmd+" -s "+width+"x"+height;
}
cmd=cmd+" -c:v mjpeg "+newFilePath;
linuxExec(cmd,newFilePath,()-> dosomething);
}
}
Windows
/**
* 在Windows上执行ffmpeg转码命令
* @param cmd 命令
*/
public void winExec(String cmd ,String newPath, Runnable callback ) {
log.info("执行命令[ " + cmd + "]");
Runtime runtime = Runtime.getRuntime();
try {
Process process = runtime.exec(cmd);
new DealProcessStream(process.getInputStream()).start();
new DealProcessStream(process.getErrorStream()).start();
// 等待ffmpeg进程执行完毕
int exitCode = process.waitFor();
if (exitCode == 0) {
//do something.
} else {
// 处理ffmpeg执行失败的情况
log.error("转换失败");
}
callback.run();
} catch (Exception e) {
e.printStackTrace();
}
}
Linux
/**
* 在Linux上执行ffmpeg转码命令
* @param cmd 命令
*/
public void linuxExec(String cmd,String newPath, Runnable callback) {
log.info("执行命令[" + cmd + "]");
String[] cmds = {"/bin/sh","-c",cmd};
Process process;
Runtime run = Runtime.getRuntime();
try {
process = run.exec(cmds);
// Process process = run.exec(new String[]{"sh", "-c", cmd});
Field field = process.getClass().getDeclaredField("pid");
field.setAccessible(true);
new DealProcessStream(process.getInputStream()).start();
new DealProcessStream(process.getErrorStream()).start();
// 等待ffmpeg进程执行完毕
int exitCode = process.waitFor();
if (exitCode == 0) {
//do something.
} else {
// 处理ffmpeg执行失败的情况
log.error("转换失败");
}
callback.run();
} catch (Exception e) {
e.printStackTrace();
}
}
处理流信息的线程
public class DealProcessStream extends Thread {
private InputStream inputStream;
public DealProcessStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
InputStreamReader inputStreamReader = null;
BufferedReader br = null;
try {
inputStreamReader = new InputStreamReader(inputStream);
br = new BufferedReader(inputStreamReader);
// 不打印信息
while (br.readLine() != null);
} catch (IOException ioe) {
ioe.printStackTrace();
}finally {
try {
if (br != null) {
br.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我这里只是将mp4视频简单的转换为指定大小的avi视频,ffmpeg的功能非常强大,如果想要了解更多功能,需要详细去了解一下。
Javacv
JavaCV(Java Computer Vision)是一个用于计算机视觉和多媒体处理的开源Java库,它基于多个计算机视觉和多媒体处理库,如OpenCV、FFmpeg、FFtw和其他一些库。JavaCV提供了对这些库的Java绑定,使Java开发人员能够利用这些底层库的功能,以便进行图像处理、视频处理、计算机视觉任务等。
导入依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.21-1.5.8</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.21-1.5.8</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>5.1.2-1.5.8</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>5.1.2-1.5.8</version>
<classifier>windows-x86_64</classifier>
</dependency>
实现
public static void convertMp4ToAvi(String inputMp4Path, String outputAviPath,Integer width,Integer height){
FFmpegLogCallback.set();
// 设置日志级别为 AV_LOG_QUIET,禁用所有日志信息
avutil.av_log_set_level(avutil.AV_LOG_QUIET);
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputMp4Path);
try {
grabber.start();
if (null==width||null==height){
width = grabber.getImageWidth();
height = grabber.getImageHeight();
}
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputAviPath, width, height);
recorder.setFormat("avi");
recorder.setVideoCodec(avcodec.AV_CODEC_ID_MJPEG); //设置视频解码器
recorder.setVideoBitrate(grabber.getVideoBitrate()); // 设置视频比特率,根据需要进行调整
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 设置音频编解码器
recorder.setFrameRate(grabber.getFrameRate());
// recorder.setAudioCodec(avcodec.AV_CODEC_ID_NONE); //去掉音频
recorder.setAudioChannels(2); // 设置音频通道数
recorder.start();
while (true) {
org.bytedeco.javacv.Frame frame = grabber.grab();
if (frame == null) {
break;
}
recorder.record(frame);
}
grabber.stop();
grabber.release();
recorder.stop();
recorder.release();
} catch (FFmpegFrameGrabber.Exception | FFmpegFrameRecorder.Exception e) {
e.printStackTrace();
}
}
使用JavaCV来进行视频转码,其实就是使用JavaCV的FFmpeg部分来进行视频编解码和转换,本质上还是FFmpeg。使用JavaCV后,打包的文件会变得特别大,介意这点的需要注意一下。