当前位置:   article > 正文

SpringBoot 集成 FFmpeg 解析音视频_springboot ffmpeg

springboot ffmpeg

1 摘要

FFmpeg 是最常用的跨平台的音频、视频处理软件,但是其通过命令行的方式进行操作对于普通用户而言上手难度大,同时 FFmpeg 只是函数库,对于不同的编程语言,需要自行适配API接口,以便于操作文件。本文介绍SpringBoot 集成 FFmpeg 实现对音视频文件的解析。

FFmpeg 官网: https://ffmpeg.org

FFmpeg Java 平台常用适配仓库:

JavaCV : https://github.com/bytedeco/javacv

JavaCV 是一个集成第三方函数库的平台,包括 OpenCV、FFmpeg 等知名函数库,提供统一的 API 操作,应用广泛。在不考虑应用程序体积大小的情况下推荐使用 JavaCV 作为集成方案。

JAVE2: https://github.com/a-schild/jave2

JAVE2 是将 FFmpeg 进行封装,并提供 Java API 以供用户操作音视频文件的依赖库。用户可以根据软件运行的操作系统来自由选择所需平台的依赖。

本文是基于 JAVE2 依赖来实现解析音视频文件的功能。

2 核心 Maven 依赖

demo-ffmpeg-media/pom.xml
  • 1
        <!-- ffmpeg 音视频处理 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>${schild-ffmpeg.version}</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-win64</artifactId>
            <version>${schild-ffmpeg.version}</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-linux64</artifactId>
            <version>${schild-ffmpeg.version}</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-osx64</artifactId>
            <version>${schild-ffmpeg.version}</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-osxm1</artifactId>
            <version>${schild-ffmpeg.version}</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

其中 schild-ffmpeg 版本为:

<schild-ffmpeg.version>3.5.0</schild-ffmpeg.version>
  • 1

这里分别引入了 Windows、Linux、macOS 系统的依赖,可根据软件运行环境进行删减。

3 核心代码

3.1 FFmpeg 解析音视频工具类

demo-ffmpeg-media/src/main/java/com/ljq/demo/springboot/ffmpeg/common/util/FFmpegMediaUtil.java
  • 1
package com.ljq.demo.springboot.ffmpeg.common.util;

import com.ljq.demo.springboot.ffmpeg.model.response.AudioInfoResponse;
import com.ljq.demo.springboot.ffmpeg.model.response.VideoInfoResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.AudioInfo;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.info.VideoInfo;

import java.io.File;
import java.nio.file.Files;
import java.util.Objects;

/**
 * @Description: FFmpeg 音视频工具类
 * @Author: junqiang.lu
 * @Date: 2024/5/10
 */
@Slf4j
public class FFmpegMediaUtil {


    /**
     * 获取视频信息
     *
     * @param videoPath 视频路径
     * @return 视频信息
     */
    public static VideoInfoResponse getVideoInfo(String videoPath) {
        VideoInfoResponse response = null;
        try {
            // 解析文件
            File videoFile = new File(videoPath);
            MultimediaObject multimediaObject = new MultimediaObject(videoFile);
            MultimediaInfo multimediaInfo = multimediaObject.getInfo();
            VideoInfo videoInfo = multimediaInfo.getVideo();
            // 判断是否为视频
            if (Objects.isNull(videoInfo) || videoInfo.getBitRate() < 0) {
                return null;
            }
            response = new VideoInfoResponse();
            response.setFormat(multimediaInfo.getFormat())
                    .setDuration(multimediaInfo.getDuration() / 1000)
                    .setSize(videoFile.length())
                    .setMd5(DigestUtils.md5DigestAsHex(Files.readAllBytes(videoFile.toPath())));
            response.setBitRate(videoInfo.getBitRate())
                    .setFrameRate(videoInfo.getFrameRate())
                    .setWidth(videoInfo.getSize().getWidth())
                    .setHeight(videoInfo.getSize().getHeight());
            return response;
        } catch (Exception e) {
            log.warn("Error processing video file", e);
        }
        return response;
    }

    /**
     * 获取音频信息
     *
     * @param audioPath 音频路径
     * @return 音频信息
     */
    public static AudioInfoResponse getAudioInfo(String audioPath) {
        AudioInfoResponse response = null;
        try {
            // 解析文件
            File videoFile = new File(audioPath);
            MultimediaObject multimediaObject = new MultimediaObject(videoFile);
            MultimediaInfo multimediaInfo = multimediaObject.getInfo();
            AudioInfo audioInfo = multimediaInfo.getAudio();
            // 判断是否为音频
            if (Objects.isNull(audioInfo) || Objects.nonNull(multimediaInfo.getVideo())) {
                return null;
            }
            response = new AudioInfoResponse();
            response.setFormat(multimediaInfo.getFormat())
                    .setDuration(multimediaInfo.getDuration() / 1000)
                    .setSize(videoFile.length())
                    .setMd5(DigestUtils.md5DigestAsHex(Files.readAllBytes(videoFile.toPath())));
            response.setSamplingRate(audioInfo.getSamplingRate())
                    .setBitRate(audioInfo.getBitRate())
                    .setChannels(audioInfo.getChannels())
                    .setBitDepth(audioInfo.getBitDepth());
            return response;
        } catch (Exception e) {
            log.warn("Error processing audio file", e);
        }
        return response;
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

3.2 音视频文件信息参数

音视频文件公共信息参数

demo-ffmpeg-media/src/main/java/com/ljq/demo/springboot/ffmpeg/model/response/MediaInfoResponse.java
  • 1
package com.ljq.demo.springboot.ffmpeg.model.response;

import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * @Description: 多媒体信息
 * @Author: junqiang.lu
 * @Date: 2024/5/10
 */
@Data
@Accessors(chain = true)
public class MediaInfoResponse implements Serializable {

    private static final long serialVersionUID = -326368230008457941L;

    /**
     * 文件格式
     */
    private String format;

    /**
     * 时长,单位:秒
     */
    private Long duration;

    /**
     * 文件大小,单位:字节数
     */
    private Long size;

    /**
     * 文件md5值
     */
    private String md5;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

视频文件参数信息对象

demo-ffmpeg-media/src/main/java/com/ljq/demo/springboot/ffmpeg/model/response/VideoInfoResponse.java
  • 1
package com.ljq.demo.springboot.ffmpeg.model.response;

import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * @Description: 视频文件信息返回对象
 * @Author: junqiang.lu
 * @Date: 2024/5/10
 */
@Data
@Accessors(chain = true)
@ToString(callSuper = true)
public class VideoInfoResponse extends MediaInfoResponse {

    private static final long serialVersionUID = -9016123624628502571L;

    /**
     * 比特率,单位: bps
     */
    private Integer bitRate;

    /**
     * 帧率,单位: FPS
     */
    private Float frameRate;

    /**
     * 宽度,单位: px
     */
    private Integer width;

    /**
     * 高度,单位: px
     */
    private Integer height;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

音频文件参数信息对象

demo-ffmpeg-media/src/main/java/com/ljq/demo/springboot/ffmpeg/model/response/AudioInfoResponse.java
  • 1
package com.ljq.demo.springboot.ffmpeg.model.response;

import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * @Description: 音频文件信息返回对象
 * @Author: junqiang.lu
 * @Date: 2024/5/10
 */
@Data
@Accessors(chain = true)
@ToString(callSuper = true)
public class AudioInfoResponse extends MediaInfoResponse {

    private static final long serialVersionUID = 3573655613715240188L;


    /**
     * 采样率
     */
    private Integer samplingRate;

    /**
     * 音频通道数量,1-单声道,2-立体声
     */
    private Integer channels;

    /**
     * 比特率,单位: bps
     */
    private Integer bitRate;

    /**
     * 位深度
     */
    private String bitDepth;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

3.3 音视频文件上传Controller

demo-ffmpeg-media/src/main/java/com/ljq/demo/springboot/ffmpeg/controller/FFmpegMediaController.java
  • 1
package com.ljq.demo.springboot.ffmpeg.controller;

import com.ljq.demo.springboot.ffmpeg.common.config.UploadConfig;
import com.ljq.demo.springboot.ffmpeg.common.util.FFmpegMediaUtil;
import com.ljq.demo.springboot.ffmpeg.model.response.AudioInfoResponse;
import com.ljq.demo.springboot.ffmpeg.model.response.VideoInfoResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;

/**
 * @Description: FFmpeg 媒体文件处理控制层
 * @Author: junqiang.lu
 * @Date: 2024/5/10
 */
@Slf4j
@RestController
@RequestMapping(value = "/api/ffmpeg/media")
public class FFmpegMediaController {

    @Resource
    private UploadConfig uploadConfig;


    /**
     * 视频上传
     *
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping(value = "/upload/video", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<VideoInfoResponse> uploadVideo(MultipartFile file) throws IOException {
        // 文件上传保存
        String videoFilePath = uploadConfig.getUploadPath() + File.separator + file.getOriginalFilename();
        log.info("videoFilePath: {}", videoFilePath);
        File videoFile = new File(videoFilePath);
        if (!videoFile.getParentFile().exists()) {
            videoFile.getParentFile().mkdirs();
        }
        file.transferTo(videoFile);
        // 获取视频信息
        VideoInfoResponse videoInfoResponse = FFmpegMediaUtil.getVideoInfo(videoFilePath);
        return ResponseEntity.ok(videoInfoResponse);
    }

    /**
     * 音频上传
     *
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping(value = "/upload/audio", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<AudioInfoResponse> uploadAudio(MultipartFile file) throws IOException {
        // 文件上传保存
        String audioFilePath = uploadConfig.getUploadPath() + File.separator + file.getOriginalFilename();
        log.info("audioFilePath: {}", audioFilePath);
        File audioFile = new File(audioFilePath);
        if (!audioFile.getParentFile().exists()) {
            audioFile.getParentFile().mkdirs();
        }
        file.transferTo(audioFile);
        // 获取音频信息
        AudioInfoResponse audioInfoResponse = FFmpegMediaUtil.getAudioInfo(audioFilePath);
        return ResponseEntity.ok(audioInfoResponse);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

3.4 application 配置文件

# config
server:
  port: 9250


# spring
spring:
  application:
    name: demo-ffmpeg-media
  servlet:
    multipart:
      max-file-size: 1000MB
      max-request-size: 1000MB

# uploadConfig
upload:
  path: D:\\upload  # linux/macOS 路径 /opt/upload
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

4 测试数据

示例文件下载: https://zh.getsamplefiles.com

4.1 视频文件解析

测试文件:

https://zh.getsamplefiles.com/download/mp4/sample-4.mp4
  • 1

测试结果:

{
    "format": "mov",
    "duration": 30,
    "size": 7588608,
    "md5": "d7b5155ee54ee9c7dcd8cbb5395823dc",
    "bitRate": 2015000,
    "frameRate": 25.0,
    "width": 1280,
    "height": 720
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4.2 音频文件解析

测试文件:

https://zh.getsamplefiles.com/download/mp3/sample-5.mp3
  • 1

测试结果:

{
    "format": "mp3",
    "duration": 45,
    "size": 1830660,
    "md5": "843e2916b1c552fb5e8ee3d83faddb8c",
    "samplingRate": 44100,
    "channels": 2,
    "bitRate": 320000,
    "bitDepth": "fltp"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5 注意事项

5.1 文件必须在本地

在实际项目中,一般会将文件保存到专门的文件服务器,但是 FFmpeg 解析文件必须是本地文件,因此在上传至文件服务器之前需要将文件在本地服务器做中转,解析完毕后再上传,然后删除本地文件。

6 推荐参考文档

SpringBoot集成ffmpeg实现视频转码播放

Convert video to Another Format in Spring Boot(Java-based apps)

springboot如何获取视频文件的视频时间长度

java 视频识别 java 视频转码

JAVE2 官方 Github

JAVE2 官方文档 Getting informations about a multimedia file

javacv-ffmpeg(八)视频文件信息获取

7 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo/tree/master/demo-ffmpeg-media

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/931595
推荐阅读
相关标签
  

闽ICP备14008679号