依赖
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.6</version> </dependency>
实现
import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.FrameGrabber; import org.bytedeco.javacv.FrameRecorder; import java.io.File; import java.util.Map; import java.util.Optional; /** */ public class Test { public static final String SEPARATOR = System.getProperty("file.separator"); public static final String USERDIR = System.getProperty("user.dir"); /** * 视频配音 * @param videoPath 视频目录 * @param audioPath 声音目录 * @param outPut 输出目录 * @return * @throws Exception */ public static boolean mergeAudioAndVideo(String videoPath, String audioPath, String outPut, Integer imageWidth, Integer imageHeight) throws Exception { File file = new File(videoPath); if (!file.exists()) { return false; } FrameRecorder recorder = null; FrameGrabber grabber1 = null; FrameGrabber grabber2 = null; try { //抓取视频帧 grabber1 = new FFmpegFrameGrabber(videoPath); grabber1.start(); //抓取音频帧 grabber2 = new FFmpegFrameGrabber(audioPath); grabber2.start(); //创建录制 recorder = new FFmpegFrameRecorder(outPut, Optional.ofNullable(imageWidth).orElse(grabber1.getImageWidth()), Optional.ofNullable(imageHeight).orElse(grabber1.getImageHeight()), grabber2.getAudioChannels()); recorder.setFormat("mp4"); recorder.setFrameRate(grabber1.getFrameRate()); recorder.setSampleRate(grabber2.getSampleRate()); recorder.setAudioChannels(2); recorder.setVideoOption("preset", "faster"); recorder.setVideoOption("strict", "experimental"); recorder.setOption("movflags", " faststart"); // yuv420p,像素 recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); recorder.start(); Frame frame1; Frame frame2 = null; //先录入视频 long t1 = 0; Map<String, String> map = grabber1.getAudioOptions(); for(String key : map.keySet()){ System.out.println(key + "--" + map.get(key)); } while ((frame1 = grabber1.grabFrame()) != null) { recorder.record(frame1); t1 = frame1.timestamp; } //然后录入音频 long t2 = 0; while ((frame2 = grabber2.grabFrame()) != null) { recorder.record(frame2); t2 = frame2.timestamp; if(t2 == t1) break; } 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(); } } File existFile = new File(outPut); if (existFile.exists()) { return true; } return false; } /** * 测试 * @param args */ public static void main(String[] args) { //视频+音频 String mp4SavePath = "D:\\video\\output.mp4"; String audioPath = "D:\\audio\\audio.mp3"; String outputFile2 = USERDIR+SEPARATOR+"output2.mp4"; try { mergeAudioAndVideo(mp4SavePath, audioPath, outputFile2,null,null); } catch (Exception e) { throw new RuntimeException(e); } } }
注意
视频资源不能有声道等音频设置,不然会将视频和音频直接拼接,时长会是视频+音频的时长