Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing FileInputStream and FileOutputStream to ffmpeg to transcode(using JAVE-Java Audio Video Encoding)

I am trying to transcode a *.mov file into a *.mp4 file using JAVE, which calls ffmpeg. Both input file and output file are in InputStream and OutputStream forms. That means I need to pass InputStream and OutputStream as -i and -y parematers for ffmpeg. How do I do that ?

    //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();  

But I keep getting "IOException: pipe is closed" error. Could somebody help me out ? Alternatively if there is any JAVA API that could do this transcoding(on Windows and RedHat Linux), that would be very helpful

Thanks

like image 246
Jagan S Avatar asked Feb 19 '12 00:02

Jagan S


1 Answers

That is not going to work that way.

Remember that JAVE merely acts as a wrapper to an ffmpeg executable, that is you supply parameters, like target encoding, loudness, etc. and then basically tell JAVE to call fmpeg and pass the settings, you entered using Java methods as parameters to the ffmpeg executable.

This step requires that the settings, you specify are 1. Serializable 2. Known to the ffmpeg executable

Now you could argue that at least some InputStreams, like FileInputStream are somehow serializable, as there is a low level File Descriptor that corresponds to this InputStream, but consider a ByteArrayInputStream - I don't know how Java implements on each platform but I somehow doubt, that there is a corresponding File Descriptor.

The crucial point however is, that an ffmpeg executable does not and should not know what a Java object of type InputStream is. The best it could do (at least on posix systems) is take an integer number (the File DescriptorE) and try to read data from it. However, a lot of things can go wrong, when working with a File Descriptor. For example it might be seekable, if it is a file, for example or not, if it actually represents data read from a socket.

Gladly, on Posix systems, for every process there are at least 3 File Descriptors, that is STDIN, STDOUT and STDERR. This corresponds to a concept where you can pipe input/output from one process to another. I don't know if or how this works on Windows, but on OSX or Linux you can pipe data into an ffmpeg executable. This actually means, that you instruct ffmpeg to read from the STDIN File Descriptor.

Sadly, JAVE does not implement this particular feature of ffmpeg, that is, there is no method, that pipes data into ffmpegs STDIN.

FWIW. You could write some native (c/c++) code and pass down a Java Object 'DecodeFeed', using JNI (http://en.wikipedia.org/wiki/Java_Native_Interface) that holds both an Inputstream and an OutputStream

The native code, you have to write could include the ffmpeg sources and use them to decode/transcode input that is read from DecodeFeed.in and then written back to DecodeFeed.out.

I am doing that in an Android project, you might want to look at for reference. https://github.com/fscz/FFmpeg-Android

Alternatively, you could fork JAVE and implement this feature yourself. As you might know Java offers a way to run an executable by calling Runtime.exec. This call will return an instance of Process class that offers Process.getOutputStream. If you write to this Outputstream, you actually write to the just created process's STDIN.

See http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html for documentation on how to spawn and write to a process.

And see http://ffmpeg.org/ffmpeg.html for the available command line options (including reading from STDIN) for ffmpeg.

like image 105
fabian Avatar answered Oct 17 '22 00:10

fabian