Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java execute ffmpeg commands with (pipe) "... -f nut - | ffmpeg -i - ..." just hangs

I can't get this to run,because java just waits for ffmpeg. But ffmpeg doesn't give an input- nor an error stream. It just runs, but doing nothing.

The output of "System.out.println("command:.." insert into bash just runs fine as expected.So there is nothing wrong with the ffmpeg syntax.

Here's the code.

package mypackage;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;

/**
 *
 * @author test
 */
public class ffmpeg_hang {

        /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        String INPUT_FILE="/path/to/media";
        String FFMPEG_PATH="/path/to/ffmpegFolder/";

            for(int i=0;(i+4)<40;i+=4){                
            String[] ffmpeg_pipe = new String[]{
                FFMPEG_PATH + "ffmpeg_4.1.1",
                "-ss",(i+""),"-t", "4",             
                "-i", INPUT_FILE,                                        
                "-ac", "1", "-acodec", "pcm_s16le", "-ar", "16000", 
                "-f","nut","-","|",
                FFMPEG_PATH + "ffmpeg_4.1.1",
                "-i","-",
                "-lavfi", "showspectrumpic=s=128x75:legend=disabled:saturation=0:stop=8000",
                "-f","image2pipe","pipe:1"};

            System.out.println("command: "+String.join(" ", ffmpeg_pipe));

            Process p;
            //ffmpe wav->pipe->spectrogra->pipe->java
            p = Runtime.getRuntime().exec(ffmpeg_pipe);


            StringBuilder Boxbuffer = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            String line = "";

            while ((line = reader.readLine()) != null) {
                Boxbuffer.append(line);
            }


            System.out.println("ffmpeg errors->> "+Boxbuffer.toString());
            p.waitFor();


            BufferedImage image = ImageIO.read(p.getInputStream());
            //do stuff with image
            }

    }

}
like image 519
user3776738 Avatar asked Mar 13 '19 19:03

user3776738


2 Answers

The pipe will not be interpreted when passed directly in a command this way, it will be just another argument to the first ffmpeg at the beginning of your command. Consider using /bin/sh -c "command1 | command2" as wrapper (assuming non-Windows OS...).

Consider adding -nostdin to first ffmpeg command to avoid a number of issues with ffmpeg trying to read stdin when you're not expecting it to (not in second one obviously).

Consider using String.format to build complex strings with variables.

Consider using ProcessBuilder for easier process creation. Here I'm redirecting errors so they end up to your java process stderr, so you can read your child process's stdout without using a thread. See alternatives

So here is a suggestion:

public static void main(String[] args) throws IOException, InterruptedException {
    String INPUT_FILE = "/path/to/media";
    String FFMPEG_PATH = "/path/to/ffmpegFolder";

    for (int i = 0; (i + 4) < 40; i += 4) {

        String command1 = String.format(
                "%s/ffmpeg_4.1.1 -nostdin -ss %d -t 4 -i '%s' -ac 1 -acodec pcm_s16le -ar 16000 -f nut -",
                FFMPEG_PATH, i, INPUT_FILE);

        String command2 = String.format(
                "%s/ffmpeg_4.1.1 -i - -lavfi showspectrumpic=s=128x75:legend=disabled:saturation=0:stop=8000",
                FFMPEG_PATH);

        Process process = new ProcessBuilder("sh", "-c", command1 + " | " + command2)
                .redirectError(ProcessBuilder.Redirect.INHERIT)
                .start();

        BufferedImage image = ImageIO.read(process.getInputStream());
        // ...
    }
}
like image 157
Hugues M. Avatar answered Nov 05 '22 17:11

Hugues M.


It looks to me like you are blocking on the stderr stream being closed. If ffmpeg does not close its stderr before it exits (and I wouldn't expect it to do that), then your program will just deadlock.

Use java.lang.ProcessBuilder with pb.redirectErrorStream(true); instead.

It is also a good idea to read the process outputs on a different thread to the one that has called waitFor();, otherwise you risk deadlock.

like image 1
Rich Avatar answered Nov 05 '22 17:11

Rich