1 Reply Latest reply: Aug 4, 2008 6:19 PM by 796365 RSS

    Reading InputStream from Runtime.exec() and ffmpeg?

    807589
      I've got a lot of things going on here and I'm having trouble debugging. I'm working on a streaming music player, and the platform only handles MP3 natively. Specifically, the method that handles incoming requests has to return an InputStream for further processing upstream (out of my visibility).

      I'm trying to extend the music player to play AAC files with the extension ".m4a". To do this, my plan is to transcode from MP3 to AAC using Runtime.exec() and ffmpeg, and then return an InputStream that contains the MP3. This works fine if I use ffmpeg to transcode to a temp file, and then return a FileInputStream constructed from that temp file. But like I said, this is supposed to be a steaming music player, and the blocking for the ~30 seconds it takes to completely transcode to a file is too much of a delay.

      So what I'm trying to do is have ffmpeg transcode to stdout, and then use Process.getInputStream() to return the InputStream that contains ffmpeg's stdout while the transcoding is still going on. This doesn't work and I'm not sure why. (I'm fairly new to java, and this is the first time I've used Runtime.) My player just hangs there. I know Runtime is picky about exhausting the stdout and stderr streams, so I'm not sure if it's something related to that, or if I somehow need to buffer the stdout before returning it, or if I need to run something in a different thread, or if I'm just completely barking up the wrong tree.

      If anyone has any experience with something like this, or can point me at some code that implements something similar, I'd appreciate the help.

      Below a sample of the code in question. Also, for what it's worth, all of the console messages that I put in there are printing, but the music doesn't play.
         public InputStream getStream(String uri) throws IOException 
              {
                   System.out.println("Entering Factory.getStream()");
                    System.out.flush();
                   
                  File file = new File(URLDecoder.decode(uri, "UTF-8"));
                  if (file.exists()) 
                  {
                       if(file.getPath().toLowerCase().endsWith(".mp3")) {
                              // This code for playing MP3 files works correctly
                            System.out.println("Playing MP3");
                            System.out.flush();
                            InputStream in = new FileInputStream(file);
                            return in;
                       }
                       else if(file.getPath().toLowerCase().endsWith(".m4a")){
                            
                            System.out.println("Playing M4A");
                            System.out.flush();
                            
                            // Create array for ffmpeg command line
                              // This command line transcodes to STDOUT
                            String[] command = { this.ffmpeg_path, 
                                                               "-i",  
                                                               "input.m4a", 
                                                               "-acodec",
                                                               "libmp3lame",
                                                               "-ac", 
                                                               "2", 
                                                               "-ab", 
                                                               "256", 
                                                               "-f",
                                                               "mp3",
                                                               "-"
                               };
                  
                            // Begin transcoding with ffmpeg
                            System.out.println("Transcoding...");
                            System.out.flush();
                            Runtime runtime = Runtime.getRuntime();
                            Process ffmpeg = runtime.exec(command);
                            
                            // Must exhaust error stream, or the application can become deadlocked.
                            System.out.println("Exhausting stderr...");
                            System.out.flush();
                            this.exhaustInputStream(ffmpeg.getErrorStream());
                            
                            // Return ffmpeg's stdout as an input stream
                            System.out.println("Returning stdout...");
                            System.out.flush();
                            return ffmpeg.getInputStream();
                       }
                       else {
                            System.out.println("Unsupported Audio File");
                            System.out.flush();
                            return null;
                       }
                  }
                  else
                  {
                      // We aren't requesting a file, so let the API handle the request upstream...
                      return super.getStream(uri);
                  }
              }
      
           private void exhaustInputStream(final InputStream inputStream) {
                    
                    // Since InputStream.read() blocks, exhast the stream in a separate thread
                    new Thread() {
                         public void run() {
                              try {
                                   while(inputStream.read() >= 0) {
                                        // Just throw the bytes away
                                   }
                              }
                              catch(IOException e) {
                                   e.printStackTrace();
                              }
                         }
                    }.start();
                    
               }