我正在使用android v21设备将数据流到javafx应用程序。它可以正常工作,但我有大约2秒的延迟时间。
到目前为止,基本的交通方式是这样的
buffers
G 213
我的数据流到我的桌面和我的包装机比我的帧速率快得多,而且经常只是等待。在任何其他地方都没有数据的积累,因此我认为我的任何代码都不会延迟。
我测试了我的安卓设备,从摄像头到纹理和定时写了yuv,android设备可以在多长时间内将帧编码到h264中,然后还要多久才能发送。so 16 +6=22 16
我觉得问题在于Javacv ffmpeg框架抓取器。我学习这个api是为了了解为什么会发生这种情况。
我主要关心的是,帧抓取器会占用start...around 4秒的时间。
一旦启动,我就可以清楚地看到我插入了多少帧,以及它的抓取数,它总是落后于一些大的数字,比如40到200。
此外,Framegrabber.grab()是阻塞的,每100 my运行一次,以匹配我的帧速率,不管我告诉它运行的速度有多快,所以我永远赶不上它。
你有什么意见建议?
我开始认为javacv不是一个可行的解决方案,因为似乎很多人都在努力解决这个延迟问题。如果你有其他的建议,请提出建议。
我的框架抓取器
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) 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) displayImage(); Thread.sleep(10); }catch (Exception e) System.out.println(TAG + " display " + e); 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:->");
VideoRenderer.I420Frame frame = new VideoRenderer.I420Frame(width, height, rotation, texId, tranformMatrix, 0,timestamp); avccEncoder.renderFrame(frame); videoView.renderFrame(frame); surfaceTextureHelper.returnTextureFrame(); }
在这里您可以看到webrtc编码的发生。
@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); }
发布于 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,拒绝我设置的选项,并多次了解到我影响了错误的因素。这是我尝试过的事情的清单;
//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");