我需要从专有网络协议解析视频流(mpeg ts)(我已经知道该怎么做),然后我想使用OpenCV将视频流处理成帧。我知道如何从文件或标准URL使用cv :: VideoCapture,但我想设置OpenCV从内存中的缓冲区读取,我可以存储视频流数据,直到需要它为止。有没有办法设置回调方法(或任何其他interfrace),以便我仍然可以使用cv :: VideoCapture对象?有没有更好的方法来完成处理视频而不将其写入文件然后重新读取它。如果这是一个更好的选择,我也会直接使用FFMPEG。我想我可以根据需要将AVFrame转换为Mat。
答案 0 :(得分:14)
我最近有类似的需求。我正在寻找一种方法在OpenCV中播放已经在内存中的视频,但无需将视频文件写入磁盘。我发现ffmpeg接口已经通过av_open_input_stream
支持了这一点。与OpenCV中用于打开文件的av_open_input_file
调用相比,只需要更多的准备工作。
在以下两个网站之间,我能够使用ffmpeg调用拼凑出一个有效的解决方案。有关详细信息,请参阅这些网站上的信息:
http://ffmpeg.arrozcru.org/forum/viewtopic.php?f=8&t=1170
http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/
为了让它在OpenCV中运行,我最终向CvCapture_FFMPEG
类添加了一个新函数:
virtual bool openBuffer( unsigned char* pBuffer, unsigned int bufLen );
我通过highgui DLL中的新API调用提供了对它的访问,类似于cvCreateFileCapture
。新的openBuffer
函数与open( const char* _filename )
函数基本相同,但有以下区别:
err = av_open_input_file(&ic, _filename, NULL, 0, NULL);
替换为:
ic = avformat_alloc_context();
ic->pb = av_alloc_put_byte(pBuffer, bufLen, 0, pBuffer, read_buffer, NULL, NULL);
if(!ic->pb) {
// handle error
}
// Need to probe buffer for input format unless you already know it
AVProbeData probe_data;
probe_data.buf_size = (bufLen < 4096) ? bufLen : 4096;
probe_data.filename = "stream";
probe_data.buf = (unsigned char *) malloc(probe_data.buf_size);
memcpy(probe_data.buf, pBuffer, 4096);
AVInputFormat *pAVInputFormat = av_probe_input_format(&probe_data, 1);
if(!pAVInputFormat)
pAVInputFormat = av_probe_input_format(&probe_data, 0);
// cleanup
free(probe_data.buf);
probe_data.buf = NULL;
if(!pAVInputFormat) {
// handle error
}
pAVInputFormat->flags |= AVFMT_NOFILE;
err = av_open_input_stream(&ic , ic->pb, "stream", pAVInputFormat, NULL);
另外,在这种情况下,请务必在av_close_input_stream
函数中调用CvCapture_FFMPEG::close()
而不是av_close_input_file
。
现在传递给read_buffer
的{{1}}回调函数定义为:
av_alloc_put_byte
此解决方案假设整个视频都包含在内存缓冲区中,可能需要进行调整以处理流数据。
这就是它!顺便说一下,我正在使用OpenCV 2.1版,所以YMMV。
答案 1 :(得分:1)
要执行与上面类似的代码,因为opencv 4.2.0已启用: https://github.com/jcdutton/opencv 分支:4.2.0-jcd1
将整个文件加载到缓冲区所指向的RAM中,其大小为buffer_size。 示例代码:
VideoCapture d_reader1;
d_reader1.open_buffer(buffer, buffer_size);
d_reader1.read(input1);
上面的代码读取视频的第一帧。