Java FFmpeg的音视频处理

实现原理是,使用FFmpeg命令对音视频文件,按照需求进行重新编解码并存储。
注意:代码重点是方法formatConverter(File fileInput, File fileOutPut, boolean isVideo),方法任务是FFmpeg命令拼接,每一行都有注释帮助使用者理解并使用,按需求进行更改,然后命令执行方法不用过多关注!

第一步
复制下方工具类代码放入项目

public class FormatConverterUtils {

    /**
     * FFmpeg程序执行路径
     * 当前系统安装好ffmpeg程序并配置好相应的环境变量后,值为ffmpeg.exe可执行程序文件在实际系统中的绝对路径
     */
    private static String FFMPEG_PATH;

    static {
        Properties props = new Properties();
        try (
                InputStream in = FormatConverterUtils.class.getClassLoader().getResourceAsStream("FFmpeg.properties");
        ) {
            props.load(in);
            String osName = System.getProperties().getProperty("os.name");
            if (osName.startsWith("Windows")) {
                FFMPEG_PATH = props.getProperty("FFmpeg.exe.path");
            } else {
                FFMPEG_PATH = props.getProperty("FFmpeg.path");
            }
        } catch (Exception e) {
            log.error("静态配置文件内容读取失败!");
        }
    }


    /**
     * 音频转换器
     * @param resourcePath 需要被转换的音频文件全路径带文件名
     * @param targetPath 转换之后的音频文件全路径带文件名
     */
    public static void audioConverter(String resourcePath, String targetPath) {
        formatConverter(new File(resourcePath), new File(targetPath), false);
    }

    /**
     * 视频转换器
     * @param resourcePath 需要被转换的视频文件全路径带文件名
     * @param targetPath 转换之后的视频文件全路径带文件名
     */
    public static void videoConverter(String resourcePath, String targetPath) {
        formatConverter(new File(resourcePath), new File(targetPath), true);
    }

    /**
     * 文件格式转换器   
     * 注意!此方法为按照需求进行拼接命令来完成音频视频文件的处理 命令拼接需要根据自己需求进行更改
     * 视频 或 音频
     * @param fileInput 源文件路径
     * @param fileOutPut 转换后的文件路径
     * @param isVideo 源文件是视频文件
     *
     */
    public static void formatConverter(File fileInput, File fileOutPut, boolean isVideo) {
        if (null == fileInput || !fileInput.exists()) {
            throw new RuntimeException("源文件不存在,请检查源路径");
        }
        if (null == fileOutPut) {
            throw new RuntimeException("转换后的路径为空,请检查转换后的存放路径是否正确");
        }

        if (!fileOutPut.exists()) {
            try {
                fileOutPut.createNewFile();
            } catch (IOException e) {
                log.error("转换时新建输出文件失败");
            }
        }
        List<String> commond = new ArrayList<String>();
        //输出直接覆盖文件
        commond.add("-y");
        commond.add("-i");
        commond.add(fileInput.getAbsolutePath());
        if (isVideo) {
            //为视频流设置编码器
            commond.add("-c:v");
            //编码格式处理为H.264
            commond.add("libx264");
            //设置比特率
            commond.add("-b:v");
            //bit/s
            commond.add("1M");
            //设置分辨率
            commond.add("-s");
            commond.add("1280x720");
            //设置帧率
            commond.add("-r");
            //ftps
            commond.add("25");
        }
        //设置音频编码器
        commond.add("-c:a");
        commond.add("aac");
        //音频采样率
        commond.add("-ar");
        //Hz
        commond.add("44.1K");
        //音频比特率
        commond.add("-b:a");
        //bps
        commond.add("96K");
        //音频声道
        commond.add("-ac");
        //stereo 立体声
        commond.add("2");
        commond.add(fileOutPut.getAbsolutePath());
        //执行命令
        executeCommand(commond);
    }

    /**
     * 执行FFmpeg命令
     * @param commonds 要执行的FFmpeg命令
     * @return FFmpeg程序在执行命令过程中产生的各信息,执行出错时返回null
     */
    public static String executeCommand(List<String> commonds) {
        if (CollectionUtils.isEmpty(commonds)) {
            log.error("--- 指令执行失败,因为要执行的FFmpeg指令为空! ---");
            return null;
        }
        LinkedList<String> ffmpegCmds = new LinkedList<>(commonds);
        ffmpegCmds.addFirst(FFMPEG_PATH); // 设置ffmpeg程序所在路径
        log.info("--- 待执行的FFmpeg指令为:---" + ffmpegCmds);

        Runtime runtime = Runtime.getRuntime();
        Process ffmpeg = null;
        try {
            // 执行ffmpeg指令
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(ffmpegCmds);
            ffmpeg = builder.start();
            log.info("--- 开始执行FFmpeg指令:--- 执行线程名:" + builder.toString());

            // 取出输出流和错误流的信息
            // 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,如果不取的话当输出流信息填满jvm存储输出留信息的缓冲区时,线程就回阻塞住
            PrintStream errorStream = new PrintStream(ffmpeg.getErrorStream());
            PrintStream inputStream = new PrintStream(ffmpeg.getInputStream());
            errorStream.start();
            inputStream.start();
            // 等待ffmpeg命令执行完
            ffmpeg.waitFor();

            // 获取执行结果字符串
            String result = errorStream.stringBuffer.append(inputStream.stringBuffer).toString();

            // 输出执行的命令信息
            String cmdStr = Arrays.toString(ffmpegCmds.toArray()).replace(",", "");
            String resultStr = StringUtils.isBlank(result) ? "【异常】" : "正常";
            log.info("--- 已执行的FFmepg命令: ---" + cmdStr + " 已执行完毕,执行结果: " + resultStr);
            return result;

        } catch (Exception e) {
            log.error("--- FFmpeg命令执行出错! --- 出错信息: " + e.getMessage());
            return null;

        } finally {
            if (null != ffmpeg) {
                ProcessKiller ffmpegKiller = new ProcessKiller(ffmpeg);
                // JVM退出时,先通过钩子关闭FFmepg进程
                runtime.addShutdownHook(ffmpegKiller);
            }
        }
    }

    /**
     * 用于取出ffmpeg线程执行过程中产生的各种输出和错误流的信息
     */
    static class PrintStream extends Thread {

        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        StringBuffer stringBuffer = new StringBuffer();

        public PrintStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public void run() {
            try {
                if (null == inputStream) {
                    log.error("--- 读取输出流出错!因为当前输出流为空!---");
                }
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    log.info(line);
                    stringBuffer.append(line);
                }
            } catch (Exception e) {
                log.error("--- 读取输入流出错了!--- 错误信息:" + e.getMessage());
            } finally {
                try {
                    if (null != bufferedReader) {
                        bufferedReader.close();
                    }
                    if (null != inputStream) {
                        inputStream.close();
                    }
                } catch (IOException e) {
                    log.error("--- 调用PrintStream读取输出流后,关闭流时出错!---");
                }
            }
        }
    }

    /**
     * 在程序退出前结束已有的FFmpeg进程
     */
    private static class ProcessKiller extends Thread {

        private Process process;

        public ProcessKiller(Process process) {
            this.process = process;
        }

        @Override
        public void run() {
            this.process.destroy();
            log.info("--- 已销毁FFmpeg进程 --- 进程名: " + process.toString());
        }
    }

}

第二步
将下方配置文件下载放入项目resource文件下,具体配置内容修改,配置文件中有具体注释
配置文件链接:https://pan.baidu.com/s/15Lk4uU17oqBmz9MuQPbdnQ?pwd=jjjj
提取码:jjjj
第三步
下载下方文件(内含Linux和Windows的FFmpeg软件安装包(绿色免安装版,解压就能用)),按文件记录步骤进行解压,修改配置文件,就完事儿!
按照配置说明文件链接:https://pan.baidu.com/s/1yPZBSusCAIv2RFk7BCQgcQ?pwd=jjjj
提取码:jjjj
小助手:配置说明文件包含两个压缩包,鼠标右击包含的那个文件,选择“保存到文件”选项,如果还不行就下载WPS有此功能
愿君享用愉快!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值