6

我正在尝试使用调用 ffmpeg 的 JAVE 将 *.mov 文件转码为 *.mp4 文件。输入文件和输出文件都是 InputStream 和 OutputStream 的形式。这意味着我需要将 InputStream 和 OutputStream 作为 -i 和 -y 参数传递给 ffmpeg。我怎么做 ?

    //Read a movfile.mov converted into a FileInputStream  
    InputStream fileInputStream = getFileInputStream();  
    OutputStream fileOutputStream = new FileOutputStrea(outputMP4File) //Output        
    Process p = Runtime.exec("ffmpeg -i - -y -");  
    InputStream pInStrm = p.getInputStream();  
    OutputStream pOutStrm = p.getOutputStream();  
    int vin = 0, vout = 0;   

    Thread read = new Thread() {  
         byte[] buff = new byte[4096];  
          void run() {   
            while ((vin=fileInputStream.read(buf))!=-1) {   
                 pOutStrm.write(buf, 0, vin);   
            }   
         }   
      }; read.start();

    Thread write = new Thread() {  
        byte[] buff = new byte[4096];  
        void run() {  
             while ((vout=pInStrm.read(buf))!=-1) {  
                 fileOutputStream.write(buf, 0, vout);  
            }  
         }  
      }; write.start();  

但我不断收到“IOException:管道已关闭”错误。有人可以帮帮我吗?或者,如果有任何 JAVA API 可以进行这种转码(在 Windows 和 RedHat Linux 上),那将非常有帮助

谢谢

4

1 回答 1

4

那是行不通的。

请记住,JAVE 仅充当 ffmpeg 可执行文件的包装器,即您提供参数,如目标编码、响度等,然后基本上告诉 JAVE 调用 fmpeg 并传递设置,您使用 Java 方法作为参数输入到 ffmpeg可执行。

此步骤要求您指定的设置为 1. 可序列化 2. ffmpeg 可执行文件已知

现在您可能会争辩说,至少某些 InputStreams,例如 FileInputStream 是可序列化的,因为有一个与此 InputStream 相对应的低级文件描述符,但考虑一个 ByteArrayInputStream - 我不知道 Java 如何在每个平台上实现,但我有点怀疑,即有对应的文件描述符。

然而,关键点是,ffmpeg 可执行文件不知道也不应该知道 InputStream 类型的 Java 对象是什么。它所能做的最好的事情(至少在posix系统上)是取一个整数(文件描述符E)并尝试从中读取数据。但是,在使用文件描述符时,很多事情都可能出错。例如,它可能是可搜索的,如果它是一个文件,例如或不是,如果它实际上表示从套接字读取的数据。

很高兴,在 Posix 系统上,每个进程至少有 3 个文件描述符,即 STDIN、STDOUT 和 STDERR。这对应于一个概念,您可以将输入/输出从一个进程传送到另一个进程。我不知道这是否或如何在 Windows 上工作,但在 OSX 或 Linux 上,您可以将数据通过管道传输到 ffmpeg 可执行文件中。这实际上意味着,您指示 ffmpeg 从 STDIN 文件描述符中读取。

遗憾的是,JAVE 没有实现 ffmpeg 的这个特殊功能,也就是说,没有方法可以将数据通过管道传输到 ffmpeg 的 STDIN。

FWIW。您可以使用同时包含 Inputstream 和 OutputStream 的JNI ( http://en.wikipedia.org/wiki/Java_Native_Interface )编写一些本机 (c/c++) 代码并传递 Java 对象“DecodeFeed”

您必须编写的本机代码可以包含 ffmpeg 源代码,并使用它们对从 DecodeFeed.in 读取的输入进行解码/转码,然后再写回 DecodeFeed.out。

我在一个 Android 项目中这样做,您可能想查看以供参考。 https://github.com/fscz/FFmpeg-Android

或者,您可以分叉 JAVE 并自己实现此功能。正如您可能知道的那样,Java 提供了一种通过调用 Runtime.exec 来运行可执行文件的方法。此调用将返回提供 Process.getOutputStream 的 Process 类的实例。如果你写到这个Outputstream,你实际上是写到刚刚创建的进程的STDIN。

有关如何生成和写入进程的文档,请参阅http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html 。

有关 ffmpeg 的可用命令行选项(包括从 STDIN 读取),请参见http://ffmpeg.org/ffmpeg.html

于 2014-02-17T14:20:03.407 回答