java如何将图片转为MP4视频并配音

这篇具有很好参考价值的文章主要介绍了java如何将图片转为MP4视频并配音。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.放在前面

最近前端小伙伴给我提了一个难题,让我在后端实现一个图片转MP4视频并配音乐,然后提供一个接口给他下载,我虽然没做过相关的功能,但本着不会就抄的的原则,还是硬着头皮答应了…

话不多说,开整!

2.引入依赖

首先我先在网上搜搜有没有相关demo,还真搜到了

这里附上原文链接 https://cloud.tencent.com/developer/article/1640244

引入相关依赖
<!--图片转MP4-->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv</artifactId>
    <version>1.5.6</version>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>ffmpeg-platform</artifactId>
    <version>4.4-1.5.6</version>
</dependency>
新建类
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 hjljy@outlook.com
 * @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();
        }
    }
}

然后融合音乐

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;

    }

3.开始测试

​ 我根据网上的demo开始写了一个demo,然而不出意外的话就要出意外了…

​ 图片确实转为MP4视频了,也是1秒一张,但是生成的视频像是加了红色滤镜一样…

​ 一通百度加源码(当然看不懂)操作下来,最终在StackOverflow找到了解决方法,更改解析模式就可以了,一次不行就n次

(附上我写方法)

//生成MP4视频
private String createMp4(String mp4SavePath, List<File> list, int width, int height) throws Exception {
    String mp3Name = "";
    //视频宽高最好是按照常见的视频的宽高  16:9  或者 9:16
    FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, width, height);
    // 文件格式
    recorder.setFormat("mp4");
    // 帧率与抓取器一致
    recorder.setFrameRate(25);
    // 编码器类型
    recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
    try {
        recorder.start();
        Java2DFrameConverter converter = new Java2DFrameConverter();
        for (File file : list) {
            if (file.getName().contains(".mp3")) {
                mp3Name = file.getName();
                continue;
            }
            BufferedImage read = null;
            try {
                read = ImageIO.read(file);
                //循环的数量等于帧率 保证每秒一张图片。
                for (int i = 0; i < recorder.getFrameRate(); i++) {
                    //编码格式 ##这里解决生成视频颜色不对问题
                    recorder.record(converter.getFrame(read), avutil.AV_PIX_FMT_RGB32_1);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //最后一定要结束并释放资源
        recorder.stop();
        recorder.release();
    }
    return mp3Name;
}

网上的demo中是这样写的 recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);

我借鉴(抄的)StackOverflow的大神在每张图片解析的时候在指定编码格式:

recorder.record(converter.getFrame(read), avutil.AV_PIX_FMT_RGB32_1);

4.融合音乐

​ 好了进行下一步,配音…

​ 然而根据这个文章的提示:

​ 1 视频长度和音频长度尽量保持一致,如果不一致,合成的视频长度会以最长的为准,音频短,后面就自然缺失音频,视频短,后面的视频会呈现视频的最后一帧。

​ 2 不建议录一帧视频然后录一帧音频,音频的后半段会丢失,比例差不多是1:1.6!!!

确实如这位大哥所写的一样,视频和音频时长不一致会导致生成的MP4出问题,所以我用我聪明的脑袋瓜迅速解决了这个问题…

5.完整示例

5.1 控制层
//上传图片转MP4
    @PostMapping("/upload/mp4/transfer")
    public ResponseEntity<InputStreamResource> uploadMP4(@RequestParam("files") MultipartFile[] files) throws Exception {
        return imgManagerService.uploadMP4(files);
    }
5.2业务层
//将图片转为MP4图片
@Override
public ResponseEntity<InputStreamResource> uploadMP4(MultipartFile[] files) throws Exception {
    log.info("=======生成MP4开始========");
    String uuid = IdGen.uuid();
    for (MultipartFile file : files) {
        getPathAndSaveFile(file, uuid);//上传到服务器的指定目录
    }
    // 图片集合的目录
    String imagesPath = rootPath + uuid + "/";
    String saveMp4name = imagesPath + "source.mp4"; //保存的视频名称
    List<File> list = FileUtil.readFile(imagesPath);
    int width = 1849;
    int height = 932;
    //返回MP3名字
    String mp3 = createMp4(saveMp4name, list, width, height);
    String outPutName = imagesPath + uuid + ".mp4";
    mergeAudioAndVideo(saveMp4name, StringUtils.isBlank(mp3) ? null : imagesPath + mp3, outPutName);
    log.info("=======生成MP4结束========");
    File file = new File(outPutName);
    InputStreamResource inputStreamResource = new InputStreamResource(new FileInputStream(file));
    return ResponseEntity.ok()
            .header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"")
            .contentType(MediaType.valueOf("audio/mp4"))
            .body(inputStreamResource);
}

其中涉及的方法:

1.getPathAndSaveFile(file, uuid) #file为上传的文件 #uuid为单次请求的标识

private String getPathAndSaveFile(MultipartFile file, String taskId) throws IOException {
    String path = rootPath + taskId + "/" + file.getOriginalFilename();
    File newFile = new File(path);
    FileUtils.copyInputStreamToFile(file.getInputStream(), newFile);
    return path;
}

2.FileUtil.readFile(imagesPath) #imagesPath 保存到服务器的图片路径

/**
 * 读取文件
 *
 * @param imgUrl 文件路径
 * @throws IOException
 */
public static List<File> readFile(String imgUrl) throws IOException {
    //读取所有图片
    File file = new File(imgUrl);
    return Arrays.stream(Objects.requireNonNull(file.listFiles())).collect(Collectors.toList());
}

3.生成MP4视频的方法为 createMp4(saveMp4name, list, width, height)

#saveMp4name 保存的视频名称

#list 文件列表

#width height生成的视频宽高 最好和图片一致

//生成MP4视频
private String createMp4(String mp4SavePath, List<File> list, int width, int height) throws Exception {
    String mp3Name = "";
    //视频宽高最好是按照常见的视频的宽高  16:9  或者 9:16
    FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, width, height);
    // 文件格式
    recorder.setFormat("mp4");
    // 帧率与抓取器一致
    recorder.setFrameRate(25);
    // 编码器类型
    recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
    try {
        recorder.start();
        Java2DFrameConverter converter = new Java2DFrameConverter();
        for (File file : list) {
            if (file.getName().contains(".mp3")) {
                mp3Name = file.getName();
                continue;
            }
            BufferedImage read = null;
            try {
                read = ImageIO.read(file);
                //循环的数量等于帧率 保证每秒一张图片。
                for (int i = 0; i < recorder.getFrameRate(); i++) {
                    //编码格式
                    recorder.record(converter.getFrame(read), avutil.AV_PIX_FMT_RGB32_1);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //最后一定要结束并释放资源
        recorder.stop();
        recorder.release();
    }
    return mp3Name;
}

4.配音…

mergeAudioAndVideo(saveMp4name, StringUtils.isBlank(mp3) ? null : imagesPath + mp3, outPutName);

saveMp4name #要操作的MP4视频

StringUtils.isBlank(mp3) ? null : imagesPath + mp3 #要操作的MP3音乐 没有MP3就传null

outPutName #输出的目录

//配音
public void mergeAudioAndVideo(String videoPath, String audioPath, String outPut) throws Exception {
    //没有音频文件 直接返回原文件
    if (null == audioPath){
        new File(videoPath).renameTo(new File(outPut));
        return;
    }
    FFmpegFrameRecorder 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());
        // 视频质量,0表示无损
        recorder.setVideoQuality(0);
        recorder.start();
        Frame frame1;
        Frame frame2 = null;
        //先录入视频
        while ((frame1 = grabber1.grabFrame()) != null) {
            recorder.record(frame1);
        }
        //然后录入音频
        audioEntry(frame2, 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();
        }
    }
}

5.录入音频的方法

audioEntry(frame2, grabber1, grabber2, recorder);

#frame2 音频帧

#grabber1 视频的信息

#grabber2 音乐的信息

#recorder 操作工具

private void audioEntry(Frame frame, FrameGrabber grabber1, FrameGrabber grabber2, FrameRecorder recorder) throws Exception {
    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(frame, grabber1, grabber2, recorder);
    }
}

6.写在最后

​ 这样这个接口写出来,前端小伙伴直接夸我666,当然后这里代码还优化的空间…

​ 1.生成MP4视频和配音的解码器是否可以共用(当然我没想出来)

​ 2.如果存在多个音频文件则需要再次修改代码

​ 3.不管了,摸鱼去了

7.记录

用以前的方法生成视频会导致视频无法播放所以更新了一下代码文章来源地址https://www.toymoban.com/news/detail-559687.html

//生成MP4视频
    private void createMp4(String mp4SavePath, List<File> list, int width, int height) throws Exception {
        String mp3Name = "";
        //视频宽高最好是按照常见的视频的宽高  16:9  或者 9:16
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, width, height);

        //MP4格式设置成H264,才能在html上播放
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        // 设置格式mp4
        recorder.setFormat("mp4");
        // 此处说明每一秒多少帧,即说明1秒会录多少张照片
        recorder.setFrameRate(1); //0.01 代表100秒一张图
        // 8000kb/s 这个说明视频每秒大小,值越大图片转过来的压缩率就越小质量就会越高
        recorder.setVideoBitrate(80000);//80000000
        // yuv420p
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        // 先默认吧,这个应该属于设置视频的处理模式  不可变(固定)音频比特率
        recorder.setAudioOption("crf", "0");
        // 最高质量
        recorder.setAudioQuality(0);
        // 音频比特率
        recorder.setAudioBitrate(192000);
        // 音频采样率
        recorder.setSampleRate(44100);
        // 双通道(立体声)
        recorder.setAudioChannels(2);
        // ------------------->end 初始化视频录制器

        try {
            recorder.start();// 开始录制
            // ------------------->begin 图片处理开始
            OpenCVFrameConverter.ToIplImage conveter = new OpenCVFrameConverter.ToIplImage(); // 申明一个图片处理的变量

            for (File file : list) {
                if (file.getName().contains(".mp3")) {
                    mp3Name = file.getPath();
                    continue;
                }
                IplImage image = opencv_imgcodecs.cvLoadImage(file.getPath()); // 非常吃内存!!
                Frame frame = conveter.convert(image);
                recorder.record(frame); // 录制
                // 释放内存 非常吃内存!!
                opencv_core.cvReleaseImage(image);
            }
            // ------------------->end 图片处理开始

            // ------------------->begin 开始录制音频
            if (StringUtils.isNotBlank(mp3Name)) {
                FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(mp3Name);
                grabber.start();// 开始录制音频
                audioEntry(null, grabber, recorder, recorder.getTimestamp());
                // ------------------->end 开始录制音频
                grabber.stop();
                grabber.release();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //最后一定要结束并释放资源
            recorder.stop();
            recorder.release();
        }
    }

    //录入音频 mp3Grabber 音频帧 frame音频源  recorder 解码器 recorderTimestamp 视频总长
    private void audioEntry(Frame frame, FFmpegFrameGrabber mp3Grabber, FFmpegFrameRecorder recorder, long recorderTimestamp) throws Exception {
        while ((frame = mp3Grabber.grabFrame()) != null) {
            //如果视频时长小于音频时长 则截断音频帧
            long timestamp = mp3Grabber.getTimestamp();
            if (recorderTimestamp <= mp3Grabber.getTimestamp()) break;
            recorder.record(frame);
        }
        long differ = recorderTimestamp - mp3Grabber.getTimestamp();
        mp3Grabber.setTimestamp(0);
        //如果视频时长大于音频时长 则循环录入
        if (differ > 0) {
            audioEntry(frame, mp3Grabber, recorder, differ);
        }
    }

到了这里,关于java如何将图片转为MP4视频并配音的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt/C++音视频开发46-音视频同步保存到MP4

    用ffmpeg单独做视频保存不难,单独做音频保存也不难,难的是音视频同步保存到MP4中,重点是音视频要同步,其实这也不难,只要播放那边音视频同步后的数据,写入到文件即可。最难的是在播放过程中不断随机的切换播放进度,而且还会暂停播放、暂停录制的情况出现,这

    2024年02月17日
    浏览(71)
  • 【音视频 ffmpeg 学习】 RTMP推流 mp4文件

    1.RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议。 2.RTMP协议中基本的数据单元称为消息(Message)。 3.当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。 (1). linux 环境准备 安装nginx 和 rtmp模块 下载nginx安装包 下载

    2024年02月03日
    浏览(67)
  • 视频剪辑:视频转码实用技巧,批量将MP4转为MP3音频

    随着数字媒体设备的普及,视频和音频文件已成为日常生活中的重要组成部分。有时,可能要将MP4视频文件转换为MP3音频文件,以提取其中的音频内容或者进行其他处理。这是耗费时间的任务,那要如何操作呢?本文详解云炫AI智剪如何将视频批量转码的技巧,批量将MP4文件

    2024年02月05日
    浏览(62)
  • 音视频编码实战-------pcm+yuv数据转成MP4

    avcodec_find_encoder: 根据编码器ID查找编码器 avcodec_alloc_context3:创建编码器上下文 avcodec_open2:打开编码器 avformat_alloc_output_context2:为输出格式创建复用器上下文 avformat_new_stream:创建音视频流 avcodec_parameters_from_context:将编码器上下文中的参数拷贝到音视频流中的编码器参数中AVCodec

    2024年02月15日
    浏览(66)
  • 【音视频开发】FFmpeg转换与封装 I - MP4格式

    1 FFmpeg转换与封装 1.1 MP4格式转换 1.1.1 MP4格式标准         FFmpeg支持的媒体封装格式具有多样性与全面性,与此, 我们还可以使用FFmpeg来对媒体格式进行转换与封装 。 在互联网常见的格式中,跨平台最好的应该是 MP4 文件,因为 MP4 文件既可以在PC 平台的Flashplayer中播放,

    2024年02月08日
    浏览(83)
  • 用ffmpeg解析mp4文件得到时长、比特率、音视频信息

    以下是使用C++语言调用FFmpeg获取视频流和音频流信息的示例代码: 上述代码通过 AVFormatContext 结构体和FFmpeg库函数 avformat_open_input 、 avformat_find_stream_info 等,获取MP4文件的视频流和音频流信息,并将结果存储到 MediaInfo 类中。在实际应用中,可以将上述代码封装成一个函数,

    2024年02月12日
    浏览(67)
  • 视频转码实例:把MP4转为MKV视频,一键批量转换的操作方法

    在数字媒体时代,视频格式的多样性是不可避免的。经常把MP4格式的视频转换为MKV格式。MKV格式有较高的音频和视频质量,能在其他设备或软件上播放视频。以下是云炫AI智剪如何把MP4视频转为MKV格式的一键批量转换操作方法。 已转码的mkv视频效果缩略图展示。   Mp4视频批量

    2024年01月18日
    浏览(51)
  • Java实现视频(mp4/flv/..)及图片(jpg/jpeg/png/..)给前端调用

    本期内容总结: 在做后端开发的过程中,经常会处理到将视频或者图片返回给前端。下面将封装一种可以简单的方法,前端只需要拼接接口地址+地址链接,即可播放下载。

    2024年02月16日
    浏览(78)
  • 音视频知识:MPEG-4、H264、MP4、AAC之间的关系

    MPEG-4 一种编码标准。是国际标准化组织 (ISO) 主要针对消费类应用,已经针对运动图像压缩定义的标准。MPEG(Moving Picture Experts Group)标准包括 MPEG1、MPEG2与 MPEG4。 MPEG-4标准目前分为27个部分,统称为ISO/IEC14496国际标准。其中第10部分(ISO/IEC 14496-10)就是熟悉的高级视频编码

    2024年02月14日
    浏览(68)
  • 【音视频笔记】Mediacodec+Muxer生成mp4,浏览器无法播放问题处理

    最近在测试视频录制功能时发现,AudioRecord + MediaCodec + MediaMuxer生成的MP4,PC浏览器无法播放 ,但是Android、Windows、Mac的播放器应用都能正常播放。虽然不禁想吐槽浏览器视频组件的容错性差,但我也意识生成的文件格式肯定也是有问题的。 然后尝试了合成MP4视频时,只保留视

    2024年02月07日
    浏览(99)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包