FFmpeg 的使用,图片合成视频,视频音频合并,视频合并
Java调用FFmpeg的方式
参考:JAVA代码调用ffmpeg程序进行视频转码和推流
有两种方式实现Java代码调用FFmpeg
1、采用第三方封装调用ffmpeg接口的jar包实现
优点:封装好了ffmpeg调用功能,代码实现简单易用
缺点:没有提供停止ffmpeg程序的方法,无法实现手动终止任务。就算手动杀死JAVA程序也无法终止ffmpeg程序的执行,只有等ffmpeg转流任务完成了程序自行停止。
2、采用ProcessBuilder类创建进程的方式调用ffmpeg实现
优点:可以手动调用destory()方法随时停止ffmpeg程序的执行
缺点:需要手动写代码去主动接收任务进程产生的正常输出信息和错误信息,不然进程会被卡住。
Windows安装FFmpeg
参考:windows电脑FFmpeg安装教程手把手详解
下载特变慢
这是我下载的安装包
jar包依赖
JDK:1.8
不要用1.5.10版本,会报错误:
参考:2024年9月 解决报错Could not initialize class org.bytedeco.ffmpeg.global.avutil
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform-gpu</artifactId>
<version>4.7.0-1.5.9</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg-platform-gpl</artifactId>
<version>6.0-1.5.9</version>
</dependency>
代码
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class FFmpegTest{
public static void main(String[] args) throws Exception {
//图片合成为视频
imgMergeToVideo("C:\\Users\\Desktop\\各类文件\\多个图片文件");
//视频音频合成
mergeAudioAndVideo(
"C:\\Users\\Desktop\\各类文件\\output.mp4",
"C:\\Users\\Desktop\\各类文件\\bbbb.mp3",
"C:\\Users\\Desktop\\各类文件\\merge.mp4");
//视频拼接
mergeVideos("C:\\Users\\Desktop\\各类文件\\多个图片文件\\","C:\\Users\\Desktop\\各类文件\\多个图片文件\\cccc.mp4");
}
private static void imgMergeToVideo(String imgFolder) throws IOException {
//要合成视频的图片的目录
Map<Integer, File> integerFileMap = readImg(imgFolder);
//视频输出地址
String outPath = "C:\\Users\\Desktop\\各类文件\\output.mp4";
//视频宽高
int width = 1600;
int height = 900;
createMp4(outPath, integerFileMap, width, height);
}
/**
* 读取图片
*
* @param imgFolderPath 图片目录
* @return
*/
private static Map<Integer, File> readImg(String imgFolderPath) {
//图片存放的地址路径
String img = imgFolderPath;
//读取所有图片
File file = new File(img);
File[] files = file.listFiles();
Map<Integer, File> imgMap = new HashMap<Integer, File>();
int num = 0;
for (File imgFile : files) {
imgMap.put(num, imgFile);
num++;
}
return imgMap;
}
/**
* 图片合成为视频
*
* @param outPath 输出地址
* @param imgMap 图片map
* @param width 宽
* @param height 高
* @throws IOException
*/
private static void createMp4(String outPath, Map<Integer, File> imgMap, int width, int height) throws IOException {
File file = new File(outPath);
if (!file.exists()) {
file.createNewFile();
}
//视频宽高最好是按照常见的视频的宽高 16:9 或者 9:16
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outPath, width, height);
//设置视频编码层模式
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
//设置视频为25帧每秒
recorder.setFrameRate(25);
//设置视频图像数据格式
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.setFormat("mp4");
try {
recorder.start();
Java2DFrameConverter converter = new Java2DFrameConverter();
//录制一个22秒的视频,22秒为自定义的一个视频时间长度,图片少则在22秒内,多则到22秒停止
for (int i = 0; i < 22; i++) {
BufferedImage read = ImageIO.read(imgMap.get(i));
//一秒是25帧 所以要记录25次
for (int j = 0; j < 25; j++) {
recorder.record(converter.getFrame(read));
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//最后一定要结束并释放资源
recorder.stop();
recorder.release();
}
}
/**
* 视频音频合并
* @param videoPath 视频地址
* @param audioPath 音频地址
* @param outPut 输出地址
* @return
* @throws Exception
*/
public static void mergeAudioAndVideo(String videoPath, String audioPath, String outPut) throws Exception {
//校验文件是否存在
File file = new File(videoPath);
if (!file.exists()) {
file.createNewFile();
}
//帧记录
FrameRecorder recorder = null;
//读取视频帧
FrameGrabber grabber1 = null;
//读取音频帧
FrameGrabber grabber2 = null;
try{
//抓取视频帧
grabber1 = new FFmpegFrameGrabber(videoPath);
//抓取音频帧
grabber2 = new FFmpegFrameGrabber(audioPath);
grabber1.start();
grabber2.start();
//创建录制
recorder = new FFmpegFrameRecorder(outPut,
grabber1.getImageWidth(), grabber1.getImageHeight(),
grabber2.getAudioChannels());
recorder.setFormat("mp4");
recorder.setFrameRate(grabber1.getFrameRate());
recorder.setSampleRate(grabber2.getSampleRate());
recorder.start();
Frame frame1;
Frame frame2 ;
//录入视频
while ((frame1 = grabber1.grabFrame()) != null ){
recorder.record(frame1);
}
//录入音频
while ((frame2 = grabber2.grabFrame()) != null) {
recorder.record(frame2);
}
grabber1.stop();
grabber2.stop();
recorder.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (recorder != null) {
recorder.release();
}
if (grabber1 != null) {
grabber1.release();
}
if (grabber2 != null) {
grabber2.release();
}
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
}
}
}
/**
* 视频合并
* @param videoFolderPath 视频文件夹
* @param outPut 输出地址
* @return
* @throws Exception
*/
public static void mergeVideos(String videoFolderPath, String outPut) throws Exception {
Map<Integer, File> videoMap = readMp4(videoFolderPath);
File file = new File(outPut);
if (!file.exists()) {
file.createNewFile();
}
//帧记录
FrameRecorder recorder = null;
try{
//设置视频格式,audioChannels 音轨数量,为0的时候报错,猜测有声视频不能为0;
recorder = new FFmpegFrameRecorder(outPut, 1600, 900,1);
recorder.setFormat("mp4");
recorder.setFrameRate(25);
recorder.start();
for (Map.Entry<Integer, File> entry : videoMap.entrySet()) {
//读取视频帧
FrameGrabber grabber = null;
try {
grabber = new FFmpegFrameGrabber(entry.getValue());
grabber.start();
Frame frame;
//录入视频
while ((frame = grabber.grabFrame()) != null ){
//录入视频帧
recorder.record(frame);
}
grabber.stop();
}catch (Exception e){
e.printStackTrace();
}finally {
if (grabber != null) {
grabber.release();
}
}
}
recorder.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (recorder != null) {
recorder.release();
}
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
}
}
}
/**
* 读取图片
*
* @param videoFolderPath 视频存放路径
* @return
*/
private static Map<Integer, File> readMp4(String videoFolderPath) {
//读取所有图片
File file = new File(videoFolderPath);
File[] files = file.listFiles();
Map<Integer, File> imgMap = new HashMap<Integer, File>();
int num = 0;
for (File imgFile : files) {
imgMap.put(num, imgFile);
num++;
}
return imgMap;
}
}