java使用ffmpeg拼接音频

2023-05-04  本文已影响0人  给代码点颜色

简介

本文基于ffmpeg获取音视频时长及对音视频进行拼接。

Java项目

依赖

<dependency>
    <groupId>ws.schild</groupId>
    <artifactId>jave-all-deps</artifactId>
    <version>3.3.1</version>
</dependency>

获取音视频基础信息

public static MultimediaInfo parseInfo(String filePath) {
    File media = new File(filePath);
    MultimediaObject multimediaObject = new MultimediaObject(media);
    MultimediaInfo info = null;
    try {
        return multimediaObject.getInfo();
    } catch (EncoderException e) {
        e.printStackTrace();
    }
    return null;
}

只对mp3、mp4进行了验证,其他格式文件请自行测试。

返回信息

{
    "audio": {
        "bitRate": 64000, 
        "channels": 2, 
        "decoder": "mp3", 
        "metadata": { }, 
        "samplingRate": 22050
    }, 
    "duration": 7630, 
    "format": "mp3", 
    "metadata": {
        "date": "2015-02-04T12:55:32", 
        "artist": "Bauchamp", 
        "album_artist": "Bauchamp", 
        "genre": "Electronic", 
        "track": "0", 
        "encoder": "Lavf58.23.101"
    }
}

其中duration就是音视频时长,单位毫秒。

音频拼接

ffmpeg拼接音频支持两种方式。

1. 直接进行文件拼接

原始命令

ffmpeg64 -i "concat:1.mp3|2.mp3" -acodec copy output.mp3

拼接1.mp3和2.mp3,重新编码输出为output.mp3

注意这种方式不会改变原数据格式,只是进行文件拼接。

java实现代码

public static boolean concat(String toMediaPath, String... fromMediaPaths) throws IOException {
    // ffmpeg64 -i "concat:1.mp3|2.mp3" -acodec copy output.mp3
    File to = new File(toMediaPath);
    if (to.exists()) {
        to.delete();
    }

    DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();
    ProcessWrapper ffmpeg = locator.createExecutor();
    // -i代表输入参数
    ffmpeg.addArgument("-i");
    // 需要连接到一起的音频文件
    String fromMedias = Arrays.stream(fromMediaPaths).collect(Collectors.joining("|"));
    ffmpeg.addArgument("concat:" + fromMedias);
    // 重新编码并复制到新文件中
    ffmpeg.addArgument("-acodec");
    ffmpeg.addArgument("copy");
    ffmpeg.addArgument(toMediaPath);

    ffmpeg.execute();
    BufferedReader reader = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()));
    String msg = reader.lines().collect(Collectors.joining("\n"));
    System.out.println(msg);

    File newFile = new File(toMediaPath);
    if (newFile.exists()) {
        return true;
    }
    return false;
}

注:
这种方式拼接得音频可能在浏览器上无法完整播放。猜测原因可能是拼接得音频格式不完全统一导致的。

2. 清洗文件格式并重新编码

原始命令

ffmpeg -i input1.mp4 -i input2.webm -i input3.avi -filter_complex '[0:0] [0:1] [1:0] [1:1] [2:0] [2:1] concat=n=3:v=1:a=1 [v] [a]' -map '[v]' -map '[a]' <编码器选项> output.mkv
  1. FFmpeg concat 过滤器会重新编码它们。注意这是有损压缩。
  2. [0:0][0:1] [1:0][1:1] [2:0][2:1] 分别表示第一个输入文件的视频、音频、第二个输入文件的视频、音频、第三个输入文件的视频、音频。
  3. concat=n=3:v=1:a=1 表示有三个输入文件,输出一条视频流和一条音频流。[v] [a] 就是得到的视频流和音频流的名字,注意在 bash 等 shell 中需要用引号,防止通配符扩展。

java实现代码

public static boolean concat(String toMediaPath, String... fromMediaPaths) throws IOException {
    // ffmpeg -i input1.mp4 -i input2.webm -i input3.avi -filter_complex '[0:0] [0:1] [1:0] [1:1] [2:0] [2:1] concat=n=3:v=1:a=1 [v] [a]' -map '[v]' -map '[a]' <编码器选项> output.mkv
    File to = new File(toMediaPath);
    if (to.exists()) {
        to.delete();
    }

    DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();
    ProcessWrapper ffmpeg = locator.createExecutor();
    for (String from : fromMediaPaths) {
        ffmpeg.addArgument("-i");
        ffmpeg.addArgument(from);
    }
    ffmpeg.addArgument("-filter_complex");
    //ffmpeg.addArgument("\"[0:a] [1:a] [2:a] concat=n=3:v=0:a=1 [out]\"");
    ffmpeg.addArgument("[0:a] [1:a] [2:a] concat=n=3:v=0:a=2 [out]");
    ffmpeg.addArgument("-map");
    //ffmpeg.addArgument("\"[out]\"");
    ffmpeg.addArgument("[out]");
    ffmpeg.addArgument(toMediaPath);

    ffmpeg.execute();
    BufferedReader reader = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()));
    String msg = reader.lines().collect(Collectors.joining("\n"));
    log.info("[concat] msg: {}", msg);

    ffmpeg.close();
    File newFile = new File(toMediaPath);
    if (newFile.exists()) {
        return true;
    }
    return false;
}

Java代码里只实现了音频得拼接,视频需要自行调整命令实现。

需要注意的是,windows和Linux格式不一样可能导致此方法不能同时运行在两个环境下。

上一篇 下一篇

猜你喜欢

热点阅读