5

我正在通过执行ffmpeg并将其输出捕获到标准输出来用 Java 编写视频应用程序。我决定使用 Apache Commons-Exec 而不是 Java Runtime,因为它看起来更好。但是,我很难捕获所有输出。

我认为使用管道将是可行的方法,因为它是进程间通信的标准方式。但是,我使用PipedInputStreamand的设置PipedOutputStream是错误的。它似乎有效,但仅适用于流的前 1042 个字节,奇怪的是恰好是PipedInputStream.PIPE_SIZE.

我不喜欢使用管道,但我想避免使用磁盘 I/O(如果可能的话),因为速度和数据量(512x384 分辨率的 1m 20s 视频会产生 690M个管道数据)。

关于处理来自管道的大量数据的最佳解决方案的想法?我的两个课程的代码如下。(是的,sleep很糟糕。对此有什么想法? wait()notifyAll()?)

WriteFrames.java

public class WriteFrames {
    public static void main(String[] args) {
        String commandName = "ffmpeg";
        CommandLine commandLine = new CommandLine(commandName);
        File filename = new File(args[0]);
        String[] options = new String[] { 
                "-i",
                filename.getAbsolutePath(),
                "-an",
                "-f",
                "yuv4mpegpipe",
                "-"};

        for (String s : options) {
            commandLine.addArgument(s);
        }



        PipedOutputStream output = new PipedOutputStream();
        PumpStreamHandler streamHandler = new PumpStreamHandler(output, System.err);
        DefaultExecutor executor = new DefaultExecutor();

        try {
            DataInputStream is = new DataInputStream(new PipedInputStream(output));
            YUV4MPEGPipeParser p = new YUV4MPEGPipeParser(is);
            p.start();

            executor.setStreamHandler(streamHandler);
            executor.execute(commandLine);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

YUV4MPEGPipeParser.java

public class YUV4MPEGPipeParser extends Thread {

    private InputStream is;
    int width, height;

    public YUV4MPEGPipeParser(InputStream is) {
        this.is = is;
    }

    public void run() {
        try {
            while (is.available() == 0) {
                Thread.sleep(100);
            }

            while (is.available() != 0) {
                // do stuff.... like write out YUV frames
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
4

1 回答 1

4

问题出在 YUV4MPEGPipeParser 类的 run 方法中。有两个连续的循环。如果流上当前没有可用数据,则第二个循环立即终止(例如,到目前为止,所有输入都由解析器处理,并且 ffmpeg 或流泵速度不够快,无法为其提供一些新数据 -> available() == 0 -> 循环终止 -> 泵线程完成)。

只需摆脱这两个循环并休眠并执行简单的阻塞 read() 而不是检查是否有任何数据可用于处理。也可能不需要 wait()/notify() 甚至 sleep(),因为解析器代码是在单独的线程上启动的。

您可以像这样重写 run() 方法的代码:

public class YUV4MPEGPipeParser extends Thread {

    ...

    // optimal size of buffer for reading from pipe stream :-)
    private static final int BUFSIZE = PipedInputStream.PIPE_SIZE; 

    public void run() {
        try {
            byte buffer[] = new byte[BUFSIZE];
            int len = 0;
            while ((len = is.read(buffer, 0, BUFSIZE) != -1) {
                // we have valid data available 
                // in first 'len' bytes of 'buffer' array.

                // do stuff.... like write out YUV frames
            }
         } catch ...
     }
 }
于 2009-06-05T22:11:09.897 回答