commit 2436ba4c963d1d213f26daea70b7ea5361226cfb Author: wangbing Date: Thu Jun 20 00:10:55 2024 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e5dba25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +*.iml +.idea/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..895be7b --- /dev/null +++ b/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + com.example + starter-jpg2mp4 + 0.0.1-SNAPSHOT + jar + + starter-img2mp4 + project for Spring Boot + + + UTF-8 + UTF-8 + 8 + true + + + + + + aliyun + Aliyun Repository + default + https://maven.aliyun.com/repository/public + + + + + + aliyun + Aliyun Repository + https://maven.aliyun.com/repository/public + default + + + + + + + org.bytedeco + javacv + 1.5.6 + + + org.bytedeco + ffmpeg-platform + 4.4-1.5.6 + + + cn.hutool + hutool-all + 5.8.26 + + + + + + ${artifactId}-${version} + + src/main/java + + + src/main/resources + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + org.apache.maven.plugins + maven-war-plugin + + + + ${project.basedir}/src/main/resources/lib + WEB-INF/lib + + *.jar + + + + + + + + diff --git a/src/main/java/com/example/jpg2mp4/Image2Mp4.java b/src/main/java/com/example/jpg2mp4/Image2Mp4.java new file mode 100644 index 0000000..a848013 --- /dev/null +++ b/src/main/java/com/example/jpg2mp4/Image2Mp4.java @@ -0,0 +1,182 @@ +package com.example.jpg2mp4; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.resource.ResourceUtil; +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 org.bytedeco.javacv.Java2DFrameConverter; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.Date; +import java.util.List; + +public class Image2Mp4 { + public static void main(String[] args) throws Exception { + String absolutePath = FileUtil.getAbsolutePath(""); + + // 图片集合目录 + String imgs = FileUtil.getAbsolutePath("data\\album\\"); + String auds = FileUtil.getAbsolutePath("data\\music\\"); + // 图片集合目录 + String target = FileUtil.getAbsolutePath("data\\target\\"); + //合成的MP4 + String mp4 = DateUtil.date().toString("yyyyMMddHHmmss"); + String tempMp4 = target + mp4 + "temp.mp4"; + String outMp4 = target + mp4 + ".mp4"; + // 视频宽度 + int width = 1920; + // 视频高度 + int height = 1080; + createTempMp4(imgs, tempMp4, width, height); + if (FileUtil.loopFiles(auds).size() > 0) { + mergeMp4(tempMp4, auds, outMp4); + } else { + FileUtil.move(new File(tempMp4), new File(mp4), true); + } + } + + /** + * 创建临时文件 + * + * @param tempMp4 + * @param imgs + * @param width + * @param height + * @throws FrameRecorder.Exception + */ + private static void createTempMp4(String imgs, String tempMp4, int width, int height) throws FrameRecorder.Exception { + //视频宽高最好是按照常见的视频的宽高 16:9 或者 9:16 + FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(tempMp4, width, height); + //设置视频编码层模式 + recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4); + // 设置视频图像数据格式 yuv420p 先默认吧,这个应该属于设置视频的处理模式吧 + recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); + recorder.setFormat("mp4"); + //视频帧,一般不能小于24,否则有卡顿 + int frameRate = 25; + recorder.setFrameRate(frameRate); + //这一步没有感觉到有多少影响,暂时不管 + recorder.setGopSize(50); + //8000kb/s 这个说明视频每秒大小,值越大图片转过来的压缩率就越小质量就会越高 + recorder.setVideoBitrate(8000000); + // 不可变(固定)音频比特率 + recorder.setAudioOption("crf", "0"); + // 最高质量 + recorder.setAudioQuality(0); + // 音频比特率 + recorder.setAudioBitrate(192000); + // 音频采样率11025Hz、22050Hz、24000Hz、44100Hz、48000Hz + recorder.setSampleRate(44100); + // 双通道(立体声) + recorder.setAudioChannels(2); + + try { + recorder.start(); + Java2DFrameConverter converter = new Java2DFrameConverter(); + + List files = FileUtil.loopFiles(imgs); + for (int i = 0; i < files.size(); i++) { + BufferedImage read = ImageIO.read(files.get(i)); + //一秒是25帧 所以要记录25次 + for (int j = 0; j < frameRate; j++) { + System.out.println("正在写入,第" + (i + 1) + "张,第" + (j + 1) + "帧"); + //编码格式 ##后面参数用于解决生成视频颜色不对问题 + 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 boolean mergeMp4(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(FileUtil.loopFiles(audioPath).get(0)); + 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()); + // 视频质量,0表示无损 + recorder.setVideoQuality(0); + recorder.start(); + Frame frame1; + //录入视频 + while ((frame1 = grabber1.grabFrame()) != null) { + recorder.record(frame1); + } + //录入音频 + audioEntry(grabber1, grabber2, recorder); + 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; + } + + private static void audioEntry(FrameGrabber grabber1, FrameGrabber grabber2, FrameRecorder recorder) throws Exception { + Frame frame = null; + long grabber1Timestamp = grabber1.getTimestamp(); + while ((frame = grabber2.grabFrame()) != null) { + //如果视频时长小于音频时长 则截断音频帧 + if (grabber1Timestamp <= grabber2.getTimestamp()) break; + recorder.record(frame); + } + long differ = grabber1Timestamp - grabber2.getTimestamp(); + //如果视频时长大于音频时长 则循环录入 + if (differ > 0) { + grabber1.setTimestamp(differ); + audioEntry(grabber1, grabber2, recorder); + } + } +} diff --git a/src/main/resources/data/album/Qiutian001.jpg b/src/main/resources/data/album/Qiutian001.jpg new file mode 100644 index 0000000..b0b167a Binary files /dev/null and b/src/main/resources/data/album/Qiutian001.jpg differ diff --git a/src/main/resources/data/album/Qiutian002.jpg b/src/main/resources/data/album/Qiutian002.jpg new file mode 100644 index 0000000..a874b7d Binary files /dev/null and b/src/main/resources/data/album/Qiutian002.jpg differ diff --git a/src/main/resources/data/album/Qiutian003.jpg b/src/main/resources/data/album/Qiutian003.jpg new file mode 100644 index 0000000..6a35fe6 Binary files /dev/null and b/src/main/resources/data/album/Qiutian003.jpg differ diff --git a/src/main/resources/data/album/Qiutian004.jpg b/src/main/resources/data/album/Qiutian004.jpg new file mode 100644 index 0000000..2a61be2 Binary files /dev/null and b/src/main/resources/data/album/Qiutian004.jpg differ diff --git a/src/main/resources/data/album/Qiutian005.jpg b/src/main/resources/data/album/Qiutian005.jpg new file mode 100644 index 0000000..032820f Binary files /dev/null and b/src/main/resources/data/album/Qiutian005.jpg differ diff --git a/src/main/resources/data/album/Qiutian006.jpg b/src/main/resources/data/album/Qiutian006.jpg new file mode 100644 index 0000000..d89388e Binary files /dev/null and b/src/main/resources/data/album/Qiutian006.jpg differ diff --git a/src/main/resources/data/album/Qiutian007.jpg b/src/main/resources/data/album/Qiutian007.jpg new file mode 100644 index 0000000..5090f55 Binary files /dev/null and b/src/main/resources/data/album/Qiutian007.jpg differ diff --git a/src/main/resources/data/album/Qiutian008.jpg b/src/main/resources/data/album/Qiutian008.jpg new file mode 100644 index 0000000..02b2517 Binary files /dev/null and b/src/main/resources/data/album/Qiutian008.jpg differ diff --git a/src/main/resources/data/album/Qiutian009.jpg b/src/main/resources/data/album/Qiutian009.jpg new file mode 100644 index 0000000..a428969 Binary files /dev/null and b/src/main/resources/data/album/Qiutian009.jpg differ diff --git a/src/main/resources/data/album/Qiutian010.jpg b/src/main/resources/data/album/Qiutian010.jpg new file mode 100644 index 0000000..65ee7fc Binary files /dev/null and b/src/main/resources/data/album/Qiutian010.jpg differ diff --git a/src/main/resources/data/music/ys.mp3 b/src/main/resources/data/music/ys.mp3 new file mode 100644 index 0000000..b9c759e Binary files /dev/null and b/src/main/resources/data/music/ys.mp3 differ