我正在使用一个android v21设备向javafx应用程序传输数据。它的工作很好,但我有大约2秒的潜伏期。
到目前为止,基本的交通是这样的
一旦它开始,我可以清楚地看到我插入了多少帧,它抓取了多少帧,它总是滞后一些大的数字,如40到200。
另外,FrameGrabber.grab()会阻塞,并且每100ms运行一次,以匹配我的帧速率,不管我告诉它运行多快,所以我永远都赶不上。
你有什么建议吗?
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");
}
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);
}
}
我编辑了上面的问题,因为我在几天内解决了这个问题,但让我为那些可能需要它们的人提供细节。
Android-我最终使用了这个库https://github.com/piasy/videocre,它打开了webrtc函数,并允许你逐帧编码视频。这就是我如何在一部旧的可怕的手机上对16ms的帧进行编码的基准测试。
javacv FFMPEG-解决方案是C++AVCODEC中的一个缓冲问题。为了证明这一点,可以尝试将每个帧输入两次或10次,而不是一次。它以同样的因素减少了延迟,尽管feed也变得无用。它还减少了视频提要的启动时间。但是,在javacv代码的ffmpegframegrabber的第926行,我根据以下链接将thread从(0)设置为(1)https://mailman.videolan.org/pipermail/x264-devel/2009-may/005880.html
您可能会找到无数通过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启动时,有不同的编码器(avcontext、videocontext、audiocontext),它们取不同的值,有不同的api framegrabber和ffply,它们取不同的标志(我相信),所以向墙扔东西是徒劳的。
尝试先将额外的帧添加到流中。另外,如果您只需要一个图像,只需向您的输入流添加一个空数据包,它将刷新缓冲区。
如果你需要为机器人视觉流式视频,请查看我的博客http://cagneymoreau.com/stream-video-android/