All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.lishaoshuai.utils.FFMUtils Maven / Gradle / Ivy

The newest version!
package cn.lishaoshuai.utils;

import org.bytedeco.javacpp.Loader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author lss
 * @version 1.0
 * @date 2022/4/5 5:49 下午
 * @description 执行ffmpeg命令的工具类
 */
public class FFMUtils {

    static final Logger logger = LoggerFactory.getLogger(FFMUtils.class);

    /**
     * 执行ffmpeg命令
     *
     * @param commands     命令集合
     * @param tolerateTime 允许执行最大耗时,单位秒
     * @return 返回结果:
     * 成功则返回:"ok"
     * 失败则返回:"error"
     */
    public static String executeCmd(List commands,
                                    Integer tolerateTime) {
        long startTime = System.currentTimeMillis();
        if (CollectionUtils.isEmpty(commands)) {
            throw new RuntimeException("ffmpeg指令为空");
        }
        LinkedList ffmpegCommands = new LinkedList<>(commands);
        ffmpegCommands.addFirst(Loader.load(org.bytedeco.ffmpeg.ffmpeg.class));
        Process process = null;
        // 创建操作系统进程
        ProcessBuilder builder = new ProcessBuilder();
        try {
            builder.redirectErrorStream(false);
            // 执行ffmpeg指令,每个ProcessBuilder 实例管理一个进程属性集,start()方法利用这些属性创建一个新的Process实例
            // start()方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程
            process = builder.command(ffmpegCommands)
                    //使用与当前Java进程相同的IO
                    .inheritIO()
                    .start();
            // 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,
            // 如果不取出的话,当输出流信息填满jvm存储输出流信息的缓冲区时,线程会阻塞
            PrintStream inputStream = new PrintStream(process.getInputStream());
            PrintStream errorStream = new PrintStream(process.getErrorStream());
            inputStream.start();
            errorStream.start();
            //等待ffmpeg命令执行完
            if (Objects.isNull(tolerateTime)) {
                // 设置默认值 60秒,如果当前进程60秒没执行完,则退出
                tolerateTime = 60;
            }
            process.waitFor(tolerateTime, TimeUnit.SECONDS);

            String errorInfo = errorStream.stringBuffer.append(inputStream.stringBuffer).toString();
            if (!StringUtils.isEmpty(errorInfo)) {
                logger.error("-- 执行ffmpeg命令,获取错误信息为:{} --", errorInfo);
            }
            //输出执行的命令信息
            String cmd = Arrays.toString(ffmpegCommands.toArray()).replace(",", "");
            logger.info("-- 执行ffmpeg命令: {} --", cmd);
            //执行结果
            return process.exitValue() == 0 ? "ok" : "error";
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new RuntimeException("ffmpeg命令执行错误: " + e.getMessage());
        } finally {
            Runtime runtime = Runtime.getRuntime();
            if (!ObjectUtils.isEmpty(process)) {
                ProcessKiller processKiller = new ProcessKiller(process);
                //JVM退出时,先通过钩子关闭FFMPEG进程
                runtime.addShutdownHook(processKiller);

            }
            long costTime = System.currentTimeMillis() - startTime;
            logger.info("execute cost : {} milliseconds", costTime);
        }
    }

    /**
     * 在程序退出前结束已有的FFMPEG进程
     */
    private static class ProcessKiller extends Thread {
        private final Process process;

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

        @Override
        public void run() {
            this.process.destroyForcibly();
        }
    }

    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) {
                    logger.error("读取输出流出错,当前输出流为空");
                }
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuffer.append(line);
                }
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("读取输入流出错,错误信息为:{}", e.getMessage());
            } finally {
                try {
                    if (null != bufferedReader) {
                        bufferedReader.close();
                    }
                    if (null != inputStream) {
                        inputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.error("关闭流出错:{}", e.getMessage());
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy