玩抖音的時候,發現可以根據圖片生成視頻,並添加音頻,同時剛好在項目當中也遇到需要利用多張圖片生成視頻的操作,特此記錄下實現的過程!!!
JAVA來實現圖片合成視頻這個需求,想想還是非常少見的,在網上找了很久資料,基本只找到一個開源庫:JAVACV 可以進行操作。並且在網上查找資料的時候也是發現,這方面的資料也是非常少的。有點難受哎!!!
什麼是JAVACV?
JavaCV 是一款開源的視覺處理庫,基於Apache License Version 2.0協議和GPLv2兩種協議 [1] ,對各種常用計算機視覺庫封裝後的一組jar包,封裝了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等計算機視覺編程人員常用庫的接口。
JavaCV通過其中的utility類方便的在包括Android在內的Java平臺上調用這些接口。
GITHUB項目地址:https://github.com/bytedeco/javacv
GITEE地址:https://gitee.com/hjljy/javacv (非官方,自己fork的一份)
最重要的是這個項目現在還在維護當中:無論是GITHUB地址,還是Maven倉庫,都可以看到代碼或者JAR包近期有過更新!!!
Maven倉庫地址:https://mvnrepository.com/search?q=javacv
相關JAR包
下載這個jar非常耗時。難受!!! 建議切換到阿里雲倉庫,下載要快很多
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.2</version>
</dependency>
圖片合成視頻
視頻都是一張一張圖片組成的,每秒的視頻都是由25張以上的圖片組成的,這個在視頻術語裏面叫做幀!!! 具體的合成代碼如下:
package cn.hjljy.javacv;
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.util.HashMap;
import java.util.Map;
/**
* @author 海加爾金鷹 www.hjljy.cn
* @version V1.0
* @email [email protected]
* @description: 圖片合成MP4
* @since 2020/5/16 18:00
**/
public class Image2Mp4 {
public static void main(String[] args) throws Exception {
//合成的MP4
String mp4SavePath = "D:\\javacv\\mp4\\img.mp4";
//圖片地址 這裏面放了22張圖片
String img = "D:\\javacv\\img";
int width = 1600;
int height = 900;
//讀取所有圖片
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++;
}
createMp4(mp4SavePath, imgMap, width, height);
}
private static void createMp4(String mp4SavePath, Map<Integer, File> imgMap, int width, int height) throws FrameRecorder.Exception {
//視頻寬高最好是按照常見的視頻的寬高 16:9 或者 9:16
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, 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秒的視頻
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();
}
}
}
在合成完畢之後,正常打開可以看到一個22秒的視頻,可以正常播放,裏面的畫面也是圖片文件夾裏面的圖片。
幾個需要注意的點:
1 建議合成的圖片寬高要一致,並且視頻的寬高還是要符合一定比例,不然會合成失敗!!!
2 一定要釋放資源,這個非常佔內存
3 H264和YUV420P 都是視頻的一些屬性,具體作用百度一下你就知道。反正我不是很清楚!!!
4 合成完畢後,會打印合成信息,裏面有合成的視頻的詳細信息,可以仔細看看!!!
視頻融合音頻
上面合成的視頻沒有聲音,需要將音頻融合到視頻裏面。形成一個完整的視頻!!!
public static boolean mergeAudioAndVideo(String videoPath, String audioPath, String outPut) throws Exception {
boolean isCreated = true;
File file = new File(videoPath);
if (!file.exists()) {
return false;
}
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();
}
}
return isCreated;
}
到這裏一個完整的視頻就合成出來了!!!。但是在視頻融合音頻的過程當中還是有一些比較需要注意的點:
1 視頻長度和音頻長度儘量保持一致,如果不一致,合成的視頻長度會以最長的爲準,音頻短,後面就自然缺失音頻,視頻短,後面的視頻會呈現視頻的最後一幀。
2 不建議錄一幀視頻然後錄一幀音頻,音頻的後半段會丟失,比例差不多是1:1.6!!!
最後總結
這個功能是非常耗時與耗內存的一個操作,所以一定要注意服務器的內存問題。
推薦一些其他人的操作文章:
音頻與視頻合成技術
javaCV入門指南:序章
javacv opencv 多圖片合成視頻 並加入mp3的音頻 控制視頻秒數