最近有一个需求需要调用linux上的ffmpeg将视频转换为MP4,切片,然后保存,在了解了ffmpeg后,下一步就是如何用java调用linux命令行了

一、概述

java要执行系统命令的话,其实是支持的,不需要添加其他的jar包或者依赖,就可以,java8API https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html#Process

主要涉及的类就是公共抽象类Process,使用ProcessBuilder.start(),Runtime.exec方法来得到一个process对象,用来控制进程,他可以获取命令的执行结果,可以获得打印的日志以及杀死进程

注意:创建进程的方法可能不适用于某些本机平台上的特殊进程,例如本机窗口进程,守护进程,Microsoft Windows上的Win16 / DOS进程或Shell脚本

 

二、使用

方式一:ProcessBuilder

目录

一、概述

二、使用

方式一:ProcessBuilder

2.1流程

2.2. waitfor 问题描述分析

2.3代码

方式二、Runtime.exec


2.1流程

创建processBuilder对象,设置需要执行的command,设置合并输出流(按需合并),调用start()开始执行,并获取process对象,获取输出流并处理,调用process.waitFor();获取执行结果,根据结果返回

2.2. waitfor 问题描述分析

  1. 主进程中调用pb.start会创建一个子进程,用于执行shell /exe 脚本。子进程创建后会和主进程分别独立运行。
  2. 因为主进程需要等待脚本执行完成,然后对脚本返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成。
  3. 子进程执行过程就是不断的打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
  4. 这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
  5. 这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。

参考博客:

2.3代码

private static ExecutorService executor;
static {
    //创建线程池
    executor = Executors.newFixedThreadPool(10);
}

public String processMp4(String path){
    StringBuilder commend = new StringBuilder();
    commend.append("ffmpeg ")
        .append(" -i ")
        .append(path)
        .append(" -movflags faststart ") //将视频的moov移动到最前方
        .append(" -c:v libx264 ")
        .append(" -c:a copy ")
        .append(" -n ")//如果文件已存在,则直接退出
        .append(newPath);
        Process process = null;
    try{
        ProcessBuilder pb = new ProcessBuilder();
        //因为process执行命令并不是像窗口中执行shell一样,所以需要添加参数,用于执行脚本
        pb.command(new String[]{"sh","-c",commend.toString()});
        //processBuilder支持将inputStream与ErrorStream合并为一个Stream,即所有的输出信息都合并到inputStream中,这样做可以减少一个线程
        pb.redirectErrorStream(true);
        process = pb.start();
        //由于process机制原因会导致死锁,所以需要在waitfor方法之前,创建线程用于处理inputstream中缓冲区的数据,这也是为什么要合并inputstream和errorstream的原因,在这里可以少创建一个线程
        readInputStream(process.getInputStream());
        //返回0则表示输出正常
        int resultCode = process.waitFor();
        System.out.println("resultCode:"+resultCode);
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        try{
            process.getErrorStream().close();
            process.getInputStream().close();
            process.getOutputStream().close();
        }
        catch(Exception ee){}
    }
    return newPath;
}

//创建线程处理输出流
private void readInputStream(InputStream in){
    executor.execute(new Runnable() {
        @Override
        public void run() {
            InputStreamReader reader = null;
            try {
                reader = new InputStreamReader(in);
                LineNumberReader line = new LineNumberReader(reader);
                String str = null;
                while((str=line.readLine())!=null){
                    System.out.println(str);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if (reader!= null ){
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    });
}

 

方式二、Runtime.exec

参考博客: 

public String processMp42(String path){
    File file = new File(path);
    if (!file.isFile()){
        System.out.println(path+"不是文件");
        return "不是文件";
    }

    String fileName = this.getFileName(path);
    String path1 = path.substring(0, path.lastIndexOf("/")+1);
    String newPath = path1+fileName+".mp4";
    StringBuilder commend = new StringBuilder();
    commend.append("ffmpeg ")
            .append(" -i ")
            .append(path)
            .append(" -movflags faststart ") //将视频的moov移动到最前方
            .append(" -c:v libx264 ")
            .append(" -c:a copy ")
            .append(" -n ")//如果文件已存在,则直接退出
            .append(newPath);
    try{
        //和processBuilder一样,某些命令不可以直接以string的形式作为参数执行,需要以String[]的形式,当前命令经过测试,可以直接执行
        Process process=Runtime.getRuntime().exec(commend.toString());
        //处理输出流
        readInputStream(process.getInputStream());
        //返回0则表示输出正常
        int resultCode = process.waitFor();
        System.out.println("执行返回码:"+resultCode);
        System.out.println("exitValue:"+process.exitValue());
        System.out.println("执行完毕");
    }catch (Exception e){
        e.printStackTrace();
    }
    return newPath;
}

 

如有理解不对的地方,劳烦指正