借助三方工具ffmpeg.exe实现音频文件和背景音乐合成

借助三方工具ffmpeg.exe实现音频文件和背景音乐合成
/**
 * 借助三方工具ffmpeg.exe实现音频文件和背景音乐合成
 *
 * @author tums
 * @2019年5月11日14:29:57
 */
public class FFmpegBuilder {
    private static Logger logger = LoggerFactory.getLogger(FFmpegBuilder.class);
    /**
     * 音频文件最长不能超过10分钟
     */
    private static final int MAX_SECONDS = 600;
    /**
     * 执行命令 循环+混音(音量以前面的为准)
     * ffmpeg -i /usr/local/ffmpeg/永恒瞬间.m4a -i /usr/local/ffmpeg/灵动线条.m4a -filter_complex '[1:a]aloop=loop=-1:size=2e+09[out];[out][0:a]amix' -ss 0 -t 126 -y /usr/local/ffmpeg/x.m4a
     */
//    private static final String COMMAND_STR = "%s -i %s -i %s -filter_complex '[1:a]aloop=loop=-1:size=2e+09[bg];[0:a][bg]amix' -ss 0 -t %s -y %s";
    private static final String COMMAND_STR = "%s -i %s -i %s -filter_complex '[1:a]aloop=loop=-1:size=2e+09[bg];[0:a][bg]amix[out];[out]volume=volume=1.5' -ar 44100 -ss 0 -t %s -y %s";
    private static final String COMMAND_MP32M4A_STR = "%s -i %s %s";
    private static final String COMMAND_AUDIO_LENGTH_STR = "ffprobe %s";

    /**
     * mp3 to m4a
     *
     * @param ffmpeg
     * @param input
     * @param output
     * @throws RuntimeException
     */
    public static void mp32m4a(String ffmpeg, String input, String output) throws RuntimeException {
        execute(String.format(COMMAND_MP32M4A_STR, ffmpeg, input, output));
    }

    private static void execute(String cmd) throws RuntimeException {
        logger.info("cmd start {}", cmd);
        try {
            Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", cmd});
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
            errorGobbler.start();
            StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
            outGobbler.start();
            process.waitFor();
            logger.info("cmd completed");
        } catch (Exception e) {
            logger.error("cmd fail ", e);
            throw new RuntimeException("音频处理失败");
        }
    }

    /**
     * Duration: 00:00:11.55, start: 0.050113, bitrate: 68 kb/s
     *
     * @param filename
     * @return
     * @throws RuntimeException
     */
    public static long getAudioLength(String filename) throws RuntimeException {
        String info = getExecuteInfo(String.format(COMMAND_AUDIO_LENGTH_STR, filename));
        String strDuration = "Duration:";
        int startIndex = info.indexOf(strDuration);
        if (startIndex <= 0) {
            return 0;
        }
        int endIndex = info.indexOf(",", startIndex);
        String durationInfo = info.substring(startIndex + strDuration.length(), endIndex).trim();
        String[] arrs = durationInfo.split("[:.]");
        int length = arrs.length;
        if (length >= 3) {
            int h = Integer.parseInt(arrs[0]) * 3600;
            int m = Integer.parseInt(arrs[1]) * 60;
            int s = Integer.parseInt(arrs[2]);
            return h + m + s;
        }
        return 0;
    }

    private static String getExecuteInfo(String cmd) throws RuntimeException {
        logger.info("cmd start {}", cmd);
        Runtime runtime = Runtime.getRuntime();
        InputStream errorStream = null;
        try {
            Process process = runtime.exec(cmd);
            errorStream = process.getErrorStream();
            process.waitFor();
            String respBody = IOUtils.toString(errorStream, "utf-8");
            System.out.println(respBody);
            return respBody;
        } catch (Exception e) {
            logger.error("cmd fail ", e);
            throw new RuntimeException("音频处理失败");
        } finally {
            logger.info("cmd completed");
            try {
                if (errorStream != null) {
                    errorStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param inputA  输入音频
     * @param inputB  背景音乐
     * @param seconds 限制时长-单位s
     * @param outputC 输出音频
     * @throws RuntimeException
     */
    public static void process(String ffmpeg, String inputA, String inputB, int seconds, String outputC) throws RuntimeException {
        if (seconds <= 0) {
            throw new RuntimeException("音频时长不能小于0");
        } else if (seconds > MAX_SECONDS) {
            seconds = MAX_SECONDS;
        }
        execute(String.format(COMMAND_STR, ffmpeg, inputA, inputB, seconds, outputC));
    }

    /**
     * @param inputA  输入音频
     * @param inputB  背景音乐
     * @param seconds 限制时长-单位s
     * @param outputC 输出音频
     * @throws RuntimeException
     */
    public static void processWindows(String ffmpeg, String inputA, String inputB, int seconds, String outputC) throws RuntimeException {
        long start = System.currentTimeMillis();
        if (seconds <= 0) {
            throw new RuntimeException("音频时长不能小于0");
        } else if (seconds > MAX_SECONDS) {
            seconds = MAX_SECONDS;
        }
        List<String> command = new ArrayList<>();
        command.add(ffmpeg);
        command.add("-i");
        command.add(inputA);
        command.add("-i");
        command.add(inputB);
        command.add("-filter_complex");
        //linux 必须加双引号,windows 可以不加
        command.add("'[1:a]aloop=loop=-1:size=2e+09[out];[out][0:a]amix'");
        command.add("-ss");
        command.add("0");
        command.add("-t");
        command.add(String.valueOf(seconds));
        command.add("-y");
        command.add(outputC);
        logger.info("cmd {}", Arrays.toString(command.toArray()));
        try {
            new ProcessBuilder(command).start();
            long use = System.currentTimeMillis() - start;
            logger.info("use {}ms", use);
        } catch (IOException e) {
            logger.error("音频合成失败", e);
            throw new RuntimeException("音频合成失败");
        }
    }

    public static void main(String[] args) {
        String cmd = "%s %s";
        String exe = "ffprobe";
        String file = "D:\\tmp\\a.mp3";
        try {
            long audioLength = FFmpegBuilder.getAudioLength(String.format(cmd, exe, file));
            System.out.println(audioLength);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class StreamGobbler extends Thread {
    private InputStream is;
    private String type;
    private OutputStream os;

    public StreamGobbler(InputStream is, String type) {
        this(is, type, null);
    }

    public StreamGobbler(InputStream is, String type, OutputStream redirect) {
        this.is = is;
        this.type = type;
        this.os = redirect;
    }

    @Override
    public void run() {
        try {
            PrintWriter pw = null;
            if (os != null) {
                pw = new PrintWriter(os);
            }
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line;
            while ((line = br.readLine()) != null) {
                if (pw != null) {
                    pw.println(line);
                }
            }
            if (pw != null)
                pw.flush();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值