سؤال

أنا أكتب تطبيقا فيديو في جافا عن طريق التنفيذ ffmpeg والتقاط إخراجها إلى الإخراج القياسي. قررت استخدام Apache Commons-EXEC بدلا من جافا Runtime, ، لأنه يبدو أفضل. ومع ذلك، لدي صعوبة في التقاط كل الإخراج.

اعتقدت أن استخدام الأنابيب ستكون هي الطريقة التي يجب أن تذهب، لأنها طريقة قياسية للاتصالات بين العملية. ومع ذلك، فإن الإعداد الخاص بي باستخدام PipedInputStream و PipedOutputStream خطأ. يبدو أن العمل، ولكن فقط لأول 1042 بايت من الدفق، والتي تحدث بفضول هي قيمة PipedInputStream.PIPE_SIZE.

ليس لدي علاقة بحب مع استخدام الأنابيب، ولكن أريد تجنب استخدام القرص I / O (إذا كان ذلك ممكنا)، بسبب السرعة وحجم البيانات (فيديو رقم 20s عند 512 × 384 القرار ينتج 690M من البيانات الأنابيب).

الأفكار حول أفضل حل للتعامل مع كميات كبيرة من البيانات القادمة من أنبوب؟ الرمز الخاص بي لفي الطبقتين أدناه. (نعم، sleep سيء. الأفكار على ذلك؟ wait() و 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();
        }
    }
}
هل كانت مفيدة؟

المحلول

المشكلة هي في طريقة تشغيل فئة yuv4mpegpippippiperser. هناك اثنين من الحلقات المتعاقبة. ينتهي الحلقة الثانية على الفور إذا لم تكن هناك بيانات متوفرة حاليا على دفق (على سبيل المثال، لم تكن جميع الإدخال حتى الآن عن طريق المحلل، ولم تكن مضخة FFMPEG أو Stream سريعة بما يكفي لخدمة بعض البيانات الجديدة الخاصة به -> متاح () == 0 -> يتم إنهاء الحلقة -> التشطيبات الخيط المضخة).

فقط تخلص من هاتين الحلقتين والنوم فقط وأداء قراءة حظر بسيط () بدلا من التحقق مما إذا كانت أي بيانات متاحة للمعالجة. من المحتمل أن تكون هناك حاجة أيضا للانتظار () / إعلام () أو حتى النوم () لأن رمز المحلل يتم تشغيل رمز المحلل في مؤشر ترابط منفصل.

يمكنك إعادة كتابة طريقة رمز التشغيل () مثل هذا:

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 ...
     }
 }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top