我正在尝试使用javacv
来无延迟地抓取帧,而且我有点困惑如何做以及javacv
和其他东西如何在后台正常工作。
在我的示例中,我运行的RTSP流具有以下配置:
Codec: H.264
Frame Size: 1280x720
Maximum Frame Rate: 60 fps
为了实现这一目标,我创建了如下线程:
public class TakeFrameFromStreamingThread implements Runnable {
private CircularFifoQueue<Frame> queue;
private Camera camera;
private FFmpegFrameGrabber grabber = null;
public TakeFrameFromStreamingThread(CircularFifoQueue<Frame> queue, Camera camera) {
try {
this.queue = queue;
this.camera = camera;
this.initGrabber(camera);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true) {
if (grabber == null) {
initGrabber(camera); // connect
}
Frame frame = null;
frame = grabber.grabImage();
if (frame != null) {
this.queue.add(frame);
} else { // when frame == null then connection has been lost
initGrabber(camera); // reconnect
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void initGrabber(Camera camera) throws Exception {
grabber = new FFmpegFrameGrabber(camera.getURL()); // rtsp url
grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);
grabber.setOption("rtsp_transport", "tcp");
grabber.setFrameRate(60);
grabber.setImageWidth(camera.getResolution().getWidth());
grabber.setImageHeight(camera.getResolution().getHeight());
grabber.start();
}
}
似乎可行。每当我需要一个框架时,就从我的主线程中pool
queue
。
我最终用这个解决方案解决了另一个问题。我很困惑,为什么每次需要一个帧时调用grabImage()
都会返回下一帧,而不是流式传输的实时帧。
通过这种解决方案,我猜想有一个javacv
(或ffmpeg
idk)用帧填充的缓冲区,然后grabImage()
只是从该缓冲区中获取一个帧。所以这是我的第一个问题:
1)是吗? ffmpeg
是否依靠缓冲区存储帧,然后grab()
只是从那里获取帧?
好吧,如果这是事实,那么必须以某种速率填充此缓冲区,当然,如果该速率大于我的grabImage()
调用率,最终我将失去实时功能一旦缓冲区完全填满,我什至会丢失帧。
在这种情况下,我的grabImage()
花费了大约 50毫秒,这使我从该缓冲区中以 20 fps 的速率获取帧。因此,我需要确保ffmpeg
接收的帧最多为 20 fps 。所以这是我的第二个问题:
2)如何知道和更改ffmpeg
缓冲速率?我猜它正在以相同的流速率( 60 fps )填充或来自媒体资源grabber.setFrameRate()
。完全不确定我是否应该使用源流中具有相同值的采集器设置器。