使用ProcessBuilder执行FFmpeg命令,进程一直处于阻塞状态,一直没有返回执行结果

昨晚我在尝试使用Java的ProcessBuilder开辟一个进程执行FFmpeg相关命令对视频进行HLS切片处理的时候,遇到了一个进程阻塞的问题。

我使用的命令 ffmpeg -i D:\\pythonWorkspace\\bilibili-script\\data\\甚至还没说一句谢谢:懂王称愿自掏腰包付宇航员加班工资_哔哩哔哩_bilibili.mp4 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 D:\\shuzhiworkspace\\Mindhaven\\output\\output.m3u8 在终端命令行执行的时候是没有问题的,一下子就给我出结果了,但是我使用ProcessBuilder开辟的进程,等了半天一直出不来结果,进程陷入了阻塞状态。

问题代码

java 复制代码
@Test  
void test16(){  
  String inputPath = "D:\\pythonWorkspace\\bilibili-script\\data\\甚至还没说一句谢谢:懂王称愿自掏腰包付宇航员加班工资_哔哩哔哩_bilibili.mp4";  
    String outputFolder = "/output";  
    //ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 output.m3u8  
    String commend = "ffmpeg -i D:\\pythonWorkspace\\bilibili-script\\data\\甚至还没说一句谢谢:懂王称愿自掏腰包付宇航员加班工资_哔哩哔哩_bilibili.mp4 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 D:\\shuzhiworkspace\\Mindhaven\\output\\output.m3u8";  
    //构建FFmpeg命令参数  
    String[] cmd = {  
            "ffmpeg",  
            "-i",inputPath,  
            "-c:v", "libx264",  
            "-c:a", "aac",  
            "-f", "hls",  
            "-hls_time", "10",  
            "-hls_list_size", "0",  
            outputFolder+"/output.m3u8"  
    };  
    //检查目录是否存在  
    File outputDir = new File(outputFolder);  
    if(!outputDir.exists()){  
        boolean mkdirs = outputDir.mkdirs();  
        if(!mkdirs){  
            System.out.println("创建目录失败");  
            return;  
        }  
    }  
    ProcessBuilder processBuilder = new ProcessBuilder(cmd);  
    System.out.println("工作路径:"+processBuilder.directory());  
  
    try {  
        System.out.println("开始执行FFmpeg命令...");  
        long l = System.currentTimeMillis();  
        Process process = processBuilder.start();  
        System.out.println("pid:"+process.pid());  
        int exitCode = process.waitFor();  
        if(exitCode == 0){  
            System.out.println("视频切片成功!");  
        }  
        System.out.println("FFmpeg命令执行完成,耗时:"+(System.currentTimeMillis()-l)+"ms");  
    } catch (IOException | InterruptedException e) {  
        e.printStackTrace();  
    }}

问题解决方案

今天早上,我询问了一下AI大模型,在它的思维链中发现了问题所在。原来FFmpeg在执行转码操作的时候,会持续的输出日志信息到 stdoutstderr这两个流中,而流数据又会先输入到缓冲区中。如果父进程(Java进程)没有主动读取这些流,当缓冲区满了的时候,子进程就会暂停执行并等待父进程(Java进程)消费数据,然而我的Java程序并没有做消费数据的处理,因而形成了死锁。从而表现在我的 int exitCode = process.waitFor(); 无限卡住,无法返回退出码。

而我在终端命令行直接执行时可以成功很快的返回之处在于,终端命令行的进程回去消费stdoutstderr 中的日志信息并输出在终端上,也就是下图所示的输出。

因此,问题的解决方法就是在Java程序中再开两个子线程,去读取消费stdoutstderr 中的日志信息,避免缓存区阻塞,这样我们的问题就基本解决了。

同时,我们还可以waitFor()方法设置一个超时中断机制,避免因其他未知的原因导致进程无限阻塞。

问题解决后的代码

java 复制代码
@Test  
void test17(){  
    String inputPath = "D:\\pythonWorkspace\\bilibili-script\\data\\甚至还没说一句谢谢:懂王称愿自掏腰包付宇航员加班工资_哔哩哔哩_bilibili.mp4";  
    String outputFolder = "/output";  
    //ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 output.m3u8  
    String commend = "ffmpeg -i D:\\pythonWorkspace\\bilibili-script\\data\\甚至还没说一句谢谢:懂王称愿自掏腰包付宇航员加班工资_哔哩哔哩_bilibili.mp4 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0 D:\\shuzhiworkspace\\Mindhaven\\output\\output.m3u8";  
    //构建FFmpeg命令参数  
    String[] cmd = {  
            "ffmpeg",  
            "-i",inputPath,  
            "-c:v", "libx264",  
            "-c:a", "aac",  
            "-f", "hls",  
            "-hls_time", "10",  
            "-hls_list_size", "0",  
            outputFolder+"/output.m3u8"  
    };  
    //检查目录是否存在  
    File outputDir = new File(outputFolder);  
    if(!outputDir.exists()){  
        boolean mkdirs = outputDir.mkdirs();  
        if(!mkdirs){  
            System.out.println("创建目录失败");  
            return;  
        }  
    }  
    ProcessBuilder processBuilder = new ProcessBuilder(cmd);  
    System.out.println("工作路径:"+processBuilder.directory());  
  
    try {  
        System.out.println("开始执行FFmpeg命令...");  
        long l = System.currentTimeMillis();  
        Process process = processBuilder.start();  
        // 启动线程读取 stdout        Thread stdoutThread = new Thread(() -> {  
            try (BufferedReader reader = new BufferedReader(  
                    new InputStreamReader(process.getInputStream()))) {  
                String line;  
                while ((line = reader.readLine()) != null) {  
                    System.out.println("[FFmpeg] " + line);  
                }  
            } catch (IOException e) { e.printStackTrace(); }  
        });  
  
        // 启动线程读取 stderr(关键!FFmpeg 错误信息在此流)  
        Thread stderrThread = new Thread(() -> {  
            try (BufferedReader reader = new BufferedReader(  
                    new InputStreamReader(process.getErrorStream()))) {  
                String line;  
                while ((line = reader.readLine()) != null) {  
                    System.err.println("[FFmpeg-Error] " + line);  
                }  
            } catch (IOException e) { e.printStackTrace(); }  
        });  
  
        stdoutThread.start();  
        stderrThread.start();  
        System.out.println("pid:"+process.pid());  
        boolean exitCode = process.waitFor(60l,TimeUnit.SECONDS);  
        if(exitCode){  
            System.out.println("视频切片成功!");  
        }  
        System.out.println("FFmpeg命令执行完成,耗时:"+(System.currentTimeMillis()-l)+"ms");  
    } catch (IOException | InterruptedException e) {  
        e.printStackTrace();  
    }}

至此,使用ProcessBuilder执行FFmpeg命令,进程一直处于阻塞状态,一直没有返回执行结果的这个问题就解决了。拜拜~

相关推荐
四谎真好看25 分钟前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程31 分钟前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t31 分钟前
ZIP工具类
java·zip
lang201509281 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
报错小能手2 小时前
linux学习笔记(45)git详解
linux·笔记·学习
pengzhuofan2 小时前
第10章 Maven
java·maven
百锦再2 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说2 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
Larry_Yanan2 小时前
QML学习笔记(四十四)QML与C++交互:对QML对象设置objectName
开发语言·c++·笔记·qt·学习·ui·交互
壹佰大多3 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring