使用java实现ffmpeg的各种操作

时间:2024-09-30 07:31:49

以实现如下功能

  • 1、支持音频文件转mp3;
  • 2、支持视频文件转mp4;
  • 3、支持视频提取音频;
  • 4、支持视频中提取缩略图;
  • 5、支持按时长拆分音频文件;

1、工具类

由于部分原因,没有将FfmpegUtil中的静态的命令行与Type枚举类结合使用。


import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/***
 *
 * @author xuancg
 * 要求系统内置ffmpeg工具环境
 * @date 2024/9/23
 */
@Slf4j
public class FfmpegUtil {

    private static final String CONVERT_MP3 = "ffmpeg -i %s -y %s";

    private static final String CONVERT_MP4 = "ffmpeg -i %s -c:v libx264 -c:a copy -y %s";

    private static final String EXTRACT_MP3 = "ffmpeg -i %s -q:a 0 -map a -y %s";

    private static final String EXTRACT_ICON = "ffmpeg -i %s -ss 0.5 -vframes 1 -r 1 -ac 2 -ab 128k   -y -f mjpeg %s";

    private static final String SPLIT_AUDIO_BY_SIZE = "ffmpeg -i %s  -f segment -segment_time  %d  -c copy -y  %s";


    private static final Set<String> MP3_TYPE = new HashSet<>(Arrays.asList("mp3", "wav", "aac", "flac"));

    private static final Set<String> MP4_TYPE = new HashSet<>(Arrays.asList("mp4", "avi", "flv", "mpeg", "wmv"));


    /***
     * 音视频文件格式化,如果存在目标文件会强制覆盖
     * 1、支持音频文件转mp3;
     * 2、支持视频文件转mp4;
     * 3、支持视频提取音频;
     * 4、支持视频中提取缩略图;
     * 5、支持按时长拆分音频文件;
     */
    public static boolean convertMedia(MediaConvertBo convertBo) {
        File src = convertBo.getSrc();
        File dest = convertBo.getDest();
        if (null == src || !src.isFile()) {
            log.error("原始文件不存在");
            return false;
        }
        if (null != dest && dest.isFile()) {
            log.info("目标文件已存在");
        }
        long start = System.currentTimeMillis();
        Process process = null;
        BufferedReader reader = null;
        try {
            String cmd = createCmd(convertBo);
            if(null == cmd){
                return false;
            }
            log.info("ffmpeg执行命令=" + cmd);
            // 执行命令
            process = Runtime.getRuntime().exec(cmd);
            // 获取命令输出结果
            reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                log.debug(line);
            }
            // 明确自己的命令需要执行多长时间,否则可以一直等待
            int timeout = convertBo.getTimeout();
            if (timeout <= 0) {
                process.waitFor();
            } else {
                process.waitFor(timeout, TimeUnit.SECONDS);
            }
            return dest.isFile() && dest.length() > 10;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            log.error("剪裁视频超时source=" + src.getAbsolutePath());
        } finally {
            if (null != process) {
                process.destroy();
            }
            if (null != reader) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("关闭流失败" + e.getMessage());
                }
            }
            log.info("耗时ms=" + (System.currentTimeMillis() - start));
        }
        return false;

    }


    public static boolean isMp4File(File file){
        String name = file.getName();
        String suffix = name.substring(name.lastIndexOf(".") + 1);
        return MP4_TYPE.contains(suffix);
    }

    public static boolean isMp3File(File file){
        String name = file.getName();
        String suffix = name.substring(name.lastIndexOf(".") + 1);
        return MP3_TYPE.contains(suffix);
    }


    private static final String createCmd(MediaConvertBo bo) {
        File src = bo.getSrc();
        String srcPath = src.getAbsolutePath().replace("\\", "/");
        String destPath = bo.getDest().getAbsolutePath().replace("\\", "/");;
        if (bo.isConvertMp3()) {
            if(!isMp3File(src)){
                log.error("错误的mp3格式");
                return null;
            }
            return String.format(CONVERT_MP3, srcPath, destPath);
        } else if (bo.isConvertMp4()) {
            if(!isMp4File(src)){
                log.error("错误的mp4格式");
                return null;
            }
            return String.format(CONVERT_MP4, srcPath, destPath);
        } else if(bo.getType() == MediaConvertBo.Type.EXTRACT_MP3){
            if(!isMp4File(src)){
                log.error("错误的mp4格式");
                return null;
            }
            return String.format(EXTRACT_MP3, srcPath, destPath);
        } else if(bo.getType() == MediaConvertBo.Type.EXTRACT_ICON) {
            if(!isMp4File(src)){
                log.error("错误的mp4格式");
                return null;
            }
            return String.format(EXTRACT_ICON, srcPath , destPath);

        } else if(bo.getType() == MediaConvertBo.Type.SPLIT_AUDIO_BY_SIZE){
            bo.getDest().mkdirs();
            String name = src.getName();
            String suffix = name.substring(name.lastIndexOf(".") + 1);
            // 保持输入输出一致性
            return String.format(SPLIT_AUDIO_BY_SIZE, srcPath, bo.getSplitSize(), destPath + "/output_%03d." + suffix);
        }

        log.error("错误的type");
        return null;
    }

}

2、入参对象


import lombok.Data;

import java.io.File;

/***
 *
 * @author xuancg
 * @date 2024/9/23
 */
@Data
public class MediaConvertBo {


    private File src;
    private File dest;

    /**0表示持续等待,单位秒*/
    private int timeout = 0;

    /** 拆分时长,单位秒*/
    private int splitSize = 60;

    /**处理类型,必传*/
    private Type type;


    public boolean isConvertMp3(){
        return null != type && type == Type.CONVERT_MP3;
    }

    public boolean isConvertMp4(){
        return null != type && type == Type.CONVERT_MP4;
    }

    public enum Type {
        /**将视频转码成mp4*/
        CONVERT_MP4,
        /**将音频转码成mp3*/
        CONVERT_MP3,
        /**从视频中提取音频*/
        EXTRACT_MP3,
        /**从视频中提取缩略图*/
        EXTRACT_ICON,
        /**按时长拆分音频文件*/
        SPLIT_AUDIO_BY_SIZE,
        ;
    }


}

3、junit测试


import org.junit.Test;

import java.io.File;

/***
 *
 * @author xuancg
 * @date 2024/9/23
 */
public class ConvertTest {

    /**
     * 1分10秒的wav2M大小=转成mp3耗时858ms,200kb大小
     */
    @Test
    public void convertmp3(){
        File src = new File("C:\\Users\\Desktop\\音视频素材\\example.wav");
        File dest = new File("C:\\Users\\Desktop\\音视频素材\\example.mp3");

        MediaConvertBo bo = new MediaConvertBo();
        bo.setType(MediaConvertBo.Type.CONVERT_MP3);
        bo.setSrc(src);
        bo.setDest(dest);

        System.out.println(FfmpegUtil.convertMedia(bo));
    }


    /**
     * 4分13秒视频50M大小=提取音频耗时7秒,4M大小
     */
    @Test
    public void extractMp3(){
        File src = new File("C:\\User\\Desktop\\音视频素材\\202002041032546186.mp4");
        File dest = new File("C:\\User\\Desktop\\音视频素材\\202002041032546186.mp3");

        MediaConvertBo bo = new MediaConvertBo();
        bo.setType(MediaConvertBo.Type.EXTRACT_MP3);
        bo.setSrc(src);
        bo.setDest(dest);
        System.out.println(FfmpegUtil.convertMedia(bo));
    }

    /**
     * 耗时500ms,保持原始视频尺寸
     */
    @Test
    public void extractIcon(){
        File src = new File("C:\\Users\\Desktop\\音视频素材\\202002041032546186.mp4");
        File dest = new File("C:\\Users\\Desktop\\音视频素材\\202002041032546186.jpg");

        MediaConvertBo bo = new MediaConvertBo();
        bo.setType(MediaConvertBo.Type.EXTRACT_ICON);
        bo.setSrc(src);
        bo.setDest(dest);
        System.out.println(FfmpegUtil.convertMedia(bo));
    }


    /**
     * 1分钟2秒
     */
    @Test
    public void splitAudio(){
        File src = new File("C:\\Users\\Desktop\\音视频素材\\example.wav");
        File dest = new File("C:\\Users\\Desktop\\音视频素材\\example");

        MediaConvertBo bo = new MediaConvertBo();
        bo.setType(MediaConvertBo.Type.SPLIT_AUDIO_BY_SIZE);
        bo.setSrc(src);
        bo.setDest(dest);
        bo.setSplitSize(60);
        System.out.println(FfmpegUtil.convertMedia(bo));
    }

}

5、maven依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version> <!-- 版本号可替换为最新版本 -->
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv</artifactId>
    <version>1.5.8</version>
</dependency>

<!-- 此版本中主要兼容linux和windows系统,如需兼容其他系统平台,请引入对应依赖即可 -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>opencv</artifactId>
    <version>4.6.0-1.5.8</version>
    <classifier>linux-x86_64</classifier>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>opencv</artifactId>
    <version>4.6.0-1.5.8</version>
    <classifier>windows-x86_64</classifier>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>openblas</artifactId>
    <version>0.3.21-1.5.8</version>
    <classifier>linux-x86_64</classifier>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>openblas</artifactId>
    <version>0.3.21-1.5.8</version>
    <classifier>windows-x86_64</classifier>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>ffmpeg</artifactId>
    <version>5.1.2-1.5.8</version>
    <classifier>linux-x86_64</classifier>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>ffmpeg</artifactId>
    <version>5.1.2-1.5.8</version>
    <classifier>windows-x86_64</classifier>
</dependency>