Pergunta

Estou escrevendo um aplicativo de vídeo em Java executando ffmpeg e captura sua saída para saída padrão. Eu decidi usar o Apache Commons-Exec em vez do Java's Runtime, porque parece melhor. No entanto, tenho dificuldade em capturar toda a saída.

Eu pensei que usar tubos seria o caminho a percorrer, porque é uma maneira padrão de comunicação entre processos. No entanto, minha configuração usando PipedInputStream e PipedOutputStream está errado. Parece funcionar, mas apenas para os primeiros 1042 bytes do fluxo, que curiosamente são o valor de PipedInputStream.PIPE_SIZE.

Não tenho um caso de amor com o uso de tubos, mas quero evitar o uso de E/S de disco (se possível), devido à velocidade e volume de dados (um vídeo de 1m 20s em 512x384 Resolution produz 690M de dados canalizados).

Pensamentos sobre a melhor solução para lidar com grandes quantidades de dados provenientes de um tubo? Meu código para minhas duas classes está abaixo. (sim, sleep é ruim. Pensamentos sobre isso? wait() e 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();
        }
    }
}
Foi útil?

Solução

O problema está no método de execução da classe yuv4mpegpipeparsser. Existem dois loops sucessivos. O segundo loop termina imediatamente se não houver dados atualmente disponíveis no fluxo (por exemplo, todas as entradas até agora foram processadas pelo analisador, e o FFMPEG ou a bomba de fluxo não foram rápidos o suficiente para servir alguns dados novos para ele -> disponível () == 0 -> O loop é encerrado -> acabamentos da rosca da bomba).

Basta livrar -se desses dois loops e dormir e apenas executar uma leitura simples de bloqueio () em vez de verificar se houver algum dado disponível para processamento. Provavelmente também não há necessidade de esperar ()/notify () ou até dormir () porque o código do analisador é iniciado em um encadeamento separado.

Você pode reescrever o método Code of Run () como este:

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 ...
     }
 }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top