我正在使用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)
{
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:->");
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");
//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/
https://stackoverflow.com/questions/59185079
复制相似问题