处理来自内存缓冲区的视频流
Posted
技术标签:
【中文标题】处理来自内存缓冲区的视频流【英文标题】:process video stream from memory buffer 【发布时间】:2011-07-11 08:46:40 【问题描述】:我需要从专有网络协议(我已经知道该怎么做)解析视频流(mpeg ts),然后我想使用 OpenCV 将视频流处理成帧。我知道如何从文件或标准 URL 中使用 cv::VideoCapture,但我想设置 OpenCV 以从内存中的缓冲区读取,我可以在需要时存储视频流数据。有没有办法设置回调方法(或任何其他接口),以便我仍然可以使用 cv::VideoCapture 对象?有没有更好的方法来完成处理视频而无需将其写入文件然后重新读取它。如果这是一个更好的选择,我也会直接使用 FFMPEG。如果需要,我想我可以将 AVFrames 转换为 Mat。
【问题讨论】:
如何读取和解码缓冲区?我曾经通过初始化 IplImage 对象、分配内存并使用 memcpy 将帧从流复制到 IplImage 来将 OpenCV 与专有流集成。就我而言,我很幸运能够从流中获得正确的原始图像。 @lfagundes 将视频流作为普通的 mpeg ts 流,一旦我从专有封装中提取了它。之后我想使用现有工具(最好是 OpenCV)来解码传输流和视频帧。 这里是如何使用 OpenCV 捕获视频流、编码为 mpeg-4 到缓冲区然后从缓冲区中解码的示例:dimitri-christodoulou.blogspot.com/2012/02/… 【参考方案1】:我最近也有类似的需求。我在 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 = avio_alloc_context(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, probe_data.buf_size);
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);
此外,在这种情况下,请确保在 CvCapture_FFMPEG::close()
函数中调用 av_close_input_stream
而不是 av_close_input_file
。
现在传入avio_alloc_context
的read_buffer
回调函数我定义为:
static int read_buffer(void *opaque, uint8_t *buf, int buf_size)
// This function must fill the buffer with data and return number of bytes copied.
// opaque is the pointer to private_data in the call to avio_alloc_context (4th param)
memcpy(buf, opaque, buf_size);
return buf_size;
此解决方案假定整个视频都包含在内存缓冲区中,并且可能需要进行调整才能处理流数据。
原来如此!顺便说一句,我使用的是 OpenCV 2.1 版所以 YMMV。
【讨论】:
我已经解决了这个问题,但忘记发布我的解决方案。我确实是这样做的。 一些注意事项:memcpy(probe_data.buf, data, 4096)
应该使用 probe_data.buf_size 而不是 4096(否则对于小于 4096 的输入会崩溃)。 av_alloc_put_byte
已重命名为 avio_alloc_context
【参考方案2】:
代码与上面类似,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);
以上代码读取第一帧视频。
【讨论】:
以上是关于处理来自内存缓冲区的视频流的主要内容,如果未能解决你的问题,请参考以下文章