首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >FFmpeg Javacv -延迟问题

FFmpeg Javacv -延迟问题
EN

Stack Overflow用户
提问于 2019-12-04 21:44:49
回答 1查看 1.5K关注 0票数 0

我正在使用android v21设备将数据流到javafx应用程序。它可以正常工作,但我有大约2秒的延迟时间。

到目前为止,基本的交通方式是这样的

buffers

  • windows latency

  • javafx
  1. webrtc/自定义实现16 ms
  2. android packetizer(udp) 6 ms
  3. udp传输假设在< 5ms
  4. windows depacketizer上没有建立数据在 ffmpeg framgrabber中显示latency
    1. javafx图像视图<1ms

    G 213

我的数据流到我的桌面和我的包装机比我的帧速率快得多,而且经常只是等待。在任何其他地方都没有数据的积累,因此我认为我的任何代码都不会延迟。

我测试了我的安卓设备,从摄像头到纹理和定时写了yuv,android设备可以在多长时间内将帧编码到h264中,然后还要多久才能发送。so 16 +6=22 16

我觉得问题在于Javacv ffmpeg框架抓取器。我学习这个api是为了了解为什么会发生这种情况。

我主要关心的是,帧抓取器会占用start...around 4秒的时间。

一旦启动,我就可以清楚地看到我插入了多少帧,以及它的抓取数,它总是落后于一些大的数字,比如40到200。

此外,Framegrabber.grab()是阻塞的,每100 my运行一次,以匹配我的帧速率,不管我告诉它运行的速度有多快,所以我永远赶不上它。

你有什么意见建议?

我开始认为javacv不是一个可行的解决方案,因为似乎很多人都在努力解决这个延迟问题。如果你有其他的建议,请提出建议。

我的框架抓取器

代码语言:javascript
运行
复制
    public RapidDecoder(final InputStream inputStream, final ImageView view)
{
    System.out.println(TAG + " starting");

     grabber = new FFmpegFrameGrabber(inputStream, 0);
     converter = new Java2DFrameConverter();
     mView = view;


    emptyBuffer = new Runnable() {
        @Override
        public void run() {
            System.out.println(TAG + " emptybuffer thread running");
            try {

                grabber.setFrameRate(12);
                grabber.setVideoBitrate(10000);

                //grabber.setOption("g", "2");
               // grabber.setOption("bufsize", "10000");
                //grabber.setOption("af", "delay 20");
                //grabber.setNumBuffers(0);
                //grabber.setOption("flush_packets", "1");
                //grabber.setOption("probsize", "32");
                //grabber.setOption("analyzeduration", "0");
                grabber.setOption("preset", "ultrafast");

                grabber.setOption("fflags", "nobuffer");
                //grabber.setVideoOption("nobuffer", "1");
                //grabber.setOption("fflags", "discardcorrupt");
                //grabber.setOption("framedrop", "\\");
               //grabber.setOption("flags","low_delay");
                grabber.setOption("strict","experimental");
                //grabber.setOption("avioflags", "direct");
                //grabber.setOption("filter:v", "fps=fps=30");
                grabber.setVideoOption("tune", "zerolatency");
                //grabber.setFrameNumber(60);


                grabber.start();
            }catch (Exception e)
            {
                System.out.println(TAG + e);
            }

            while (true)
            {

                try{
                    grabFrame();
                    Thread.sleep(1);
                }catch (Exception e)
                {
                    System.out.println(TAG + " emptybuffer " + e);
                }

            }



        }
    };

    display = new Runnable() {
        @Override
        public void run() {

            System.out.println(TAG + " display thread running ");

            while(true)
            {

                try{
                    displayImage();
                    Thread.sleep(10);
                }catch (Exception e)
                {
                    System.out.println(TAG + " display " + e);
                }

            }


        }
    };




}


public void generateVideo()
{
    System.out.println(TAG + " genvid ");




    new Thread(emptyBuffer).start();
    new Thread(display).start();



}



public synchronized void grabFrame() throws FrameGrabber.Exception
{
           //frame = grabber.grabFrame();
        frame = grabber.grab();
    //System.out.println("grab");


}

public synchronized void displayImage()
{


    bufferedImage = converter.convert(frame);
    frame = null;
    if (bufferedImage == null) return;
    mView.setImage(SwingFXUtils.toFXImage(bufferedImage, null));
    //System.out.println("display");
}

在这里,您可以看到我用图像绘制纹理并发送到h264编码器

@Override void onTextureFrameCaptured(int宽度、int高度、int texId、float[] tranformMatrix、int旋转、长时间戳){//Log.d(标记,"onTextureFrameCaptured:->");

代码语言:javascript
运行
复制
            VideoRenderer.I420Frame frame = new VideoRenderer.I420Frame(width, height, rotation, texId, tranformMatrix, 0,timestamp);
            avccEncoder.renderFrame(frame);
            videoView.renderFrame(frame);
            surfaceTextureHelper.returnTextureFrame();

        }

在这里您可以看到webrtc编码的发生。

代码语言:javascript
运行
复制
 @Override
    public void renderFrame(VideoRenderer.I420Frame i420Frame) {
        start = System.nanoTime();
        bufferque++;

        mediaCodecHandler.post(new Runnable() {
            @Override
            public void run() {
                videoEncoder.encodeTexture(false, i420Frame.textureId, i420Frame.samplingMatrix, TimeUnit.NANOSECONDS.toMicros(i420Frame.timestamp));
            }
        });


    }

    /**
     * Called to retrieve an encoded frame
     */
    @Override
    public void onEncodedFrame(MediaCodecVideoEncoder.OutputBufferInfo frame, MediaCodec.BufferInfo bufferInfo) {

        b = new byte[frame.buffer().remaining()];
        frame.buffer().get(b);
        synchronized (lock)
        {
            encodedBuffer.add(b);
            lock.notifyAll();
            if(encodedBuffer.size() > 1)
            {
                Log.e(TAG, "drainEncoder: too big: " + encodedBuffer.size(),null );

            }
        }
        duration = System.nanoTime() - start;
        bufferque--;
        calcAverage();
        if (bufferque > 0)
        {
        Log.d(TAG, "onEncodedFrame: bufferque size: " + bufferque);


    }

}
EN

回答 1

Stack Overflow用户

发布于 2019-12-24 20:42:54

我编辑了上面的问题,因为我解决了问题在几天的过程中,但让我给那些可能需要他们的细节。

安卓--我最终使用了这个库https://github.com/Piasy/VideoCRE,它撕开了webrtc功能,允许你逐帧编码视频。这就是我如何在一个旧的可怕的手机上编码的16毫秒的基准帧。

javacv解决方案是c++ avcodec中的一个缓冲问题。为了证明它,试着用两次或十次而不是一次给每个帧喂食。它将延迟降低了同样的因素,尽管提要也变得无用了。它还减少了视频提要的启动时间。但是,在javacv代码中ffmpegframegrabber的第926行中,我将线程设置为(0)到(1),每个链接为https://mailman.videolan.org/pipermail/x264-devel/2009-May/005880.html

thread_count =0指示x264使用足够多的线程在编码期间加载所有CPU核心。因此,您可能会在双核机器上运行测试(2个内核将有3个线程)。要立即获得x264编码,请设置thread_count = 1。

您可能会发现关于通过javacv设置选项的无数建议,但是我从来没有过javacv,拒绝我设置的选项,并多次了解到我影响了错误的因素。这是我尝试过的事情的清单;

代码语言:javascript
运行
复制
                //grabber.setFrameRate(12);
                //grabber.setVideoBitrate(10000);

                //grabber.setOption("g", "2");
               // grabber.setOption("bufsize", "10000");
                //grabber.setOption("af", "delay 20");
                //grabber.setNumBuffers(0);
                //grabber.setOption("flush_packets", "1");
                //grabber.setOption("probsize", "32");
                //grabber.setOption("analyzeduration", "0");
                //grabber.setOption("preset", "ultrafast");

                //grabber.setOption("fflags", "nobuffer");
                //grabber.setVideoOption("nobuffer", "1");
                //grabber.setOption("fflags", "discardcorrupt");
                //grabber.setOption("framedrop", "\\");
               //grabber.setOption("flags","low_delay");
                //grabber.setOption("strict","experimental");
                //grabber.setOption("avioflags", "direct");
                //grabber.setOption("filter:v", "fps=fps=30");
                //grabber.setOptions("look_ahead", "0");
                //Map options = new HashMap();
                //options.put("tune", "zerolatency");
                grabber.setVideoOption("look_ahead", "0");
                //grabber.setFrameNumber(60);

它们都不起作用,当您阅读文档时,您将了解到,当ffmpeg启动时,有不同的编码器(如上下文、视频上下文、音频上下文),它们采用不同的值,并且有不同的api框架、抓取和使用不同的标志(我相信),因此向墙上扔东西是非常徒劳的。

先尝试将额外的帧添加到流中。另外,如果您只需要一个图像,只需向输入流中添加一个空数据包,它就会刷新缓冲区。

如果你需要观看机器人视觉的视频,请查看我的博客文章http://cagneymoreau.com/stream-video-android/

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59185079

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档