如何将 opencv mat 写入 gstreamer 管道?
Posted
技术标签:
【中文标题】如何将 opencv mat 写入 gstreamer 管道?【英文标题】:How to write opencv mat to gstreamer pipeline? 【发布时间】:2016-05-20 06:14:06 【问题描述】:我想将一些 opencv 进程添加到 gstreamer 管道,然后通过 udpsink 发送。
我可以像这样从 gstreamer 读取帧:
// may add some plugins to the pipeline later
cv::VideoCapture cap("v4l2src ! video/x-raw, framerate=30/1, width=640, height=480, format=RGB ! videoconvert ! appsink");
cv::Mat frame;
while(ture)
cap >> frame;
// do some processing to the frame
但想不通的是如何将处理后的帧传递到以下管道:appsrc ! x264enc ! mpegtsmux ! udpsink host=localhost port=5000
我试过了
cv::VideoWriter writer = cv::VideoWriter("appsrc ! x264enc ! mpegtsmux ! udpsink host=localhost port=5000", 0, (double)30, cv::Size(640, 480), true);
writer << processedFrame;
但是,接收方什么也没有收到。 (我使用管道$gst-launch-1.0 udpsrc port=5000 ! tsparse ! tsdemux ! h264parse ! avdec_h264 ! videoconvert ! ximagesink sync=false
作为接收器)
我的问题是,我可以将处理后的 opencv Mat 传递给 gstreamer 管道并让它进行一些编码,然后通过 udpsink 通过网络发送吗?如果可以,我该如何实现? p>
附带问题,有什么方法可以调试 VideoWriter?比如检查帧是否真的写入其中。
请注意,我在 ubuntu 14.04 上使用的是 opencv 2.4.12 和 gstreamer 1.2。
任何帮助都很棒!
编辑:
为了提供更多信息,我测试了以下代码,它给出了GStreamer Plugin: Embedded video playback halted; module appsrc0 reported: Internal data flow error.
#include <stdio.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
int main(int argc, char *argv[])
cv::VideoCapture cap("v4l2src ! video/x-raw, framerate=30/1, width=640, height=480, format=RGB ! videoconvert ! appsink");
if (!cap.isOpened())
printf("=ERR= can't create capture\n");
return -1;
cv::VideoWriter writer;
// problem here
writer.open("appsrc ! video/x-raw, framerate=30/1, width=640, height=480, format=RGB ! autovideoconvert ! ximagesink sync=false", 0, (double)30, cv::Size(640, 480), true);
if (!writer.isOpened())
printf("=ERR= can't create writer\n");
return -1;
cv::Mat frame;
int key;
while (true)
cap >> frame;
if (frame.empty())
printf("no frame\n");
break;
writer << frame;
key = cv::waitKey( 30 );
cv::destroyWindow( "video" );
appsrc 管道显然有问题,但我不知道出了什么问题,因为管道 gst-launch-1.0 v4l2src ! video/x-raw, framerate=30/1, width=640, height=480, format=RGB ! videoconvert ! ximagesink sync=false
工作正常。
【问题讨论】:
你需要为appsrc元素设置适当的大写(capabilities - 它的appsrc参数叫做caps) - 你知道processedFrame
s是什么格式的吗?
我尝试将video/x-raw, framerate=30/1, width=640, height=480, format=RGB
(与源相同)设置为appsrc,但没有成功。有什么方法可以检查 opencv Mat 的 gstreamer caps 格式吗?
【参考方案1】:
经过几个小时的搜索和测试,我终于得到了答案。
关键是在appsrc
之后只使用videoconvert
,不需要设置大写。因此,写入器管道看起来像appsrc ! videoconvert ! x264enc ! mpegtsmux ! udpsink host=localhost port=5000
。
以下是从 gstreamer 管道读取图像、进行一些 opencv 图像处理并将其写回管道的示例代码。
使用此方法,您可以轻松地将任何 opencv 进程添加到 gstreamer 管道中。
// Compile with: $ g++ opencv_gst.cpp -o opencv_gst `pkg-config --cflags --libs opencv`
#include <stdio.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
int main(int argc, char** argv)
// Original gstreamer pipeline:
// == Sender ==
// gst-launch-1.0 v4l2src
// ! video/x-raw, framerate=30/1, width=640, height=480, format=RGB
// ! videoconvert
// ! x264enc noise-reduction=10000 tune=zerolatency byte-stream=true threads=4
// ! mpegtsmux
// ! udpsink host=localhost port=5000
//
// == Receiver ==
// gst-launch-1.0 -ve udpsrc port=5000
// ! tsparse ! tsdemux
// ! h264parse ! avdec_h264
// ! videoconvert
// ! ximagesink sync=false
// first part of sender pipeline
cv::VideoCapture cap("v4l2src ! video/x-raw, framerate=30/1, width=640, height=480, format=RGB ! videoconvert ! appsink");
if (!cap.isOpened())
printf("=ERR= can't create video capture\n");
return -1;
// second part of sender pipeline
cv::VideoWriter writer;
writer.open("appsrc ! videoconvert ! x264enc noise-reduction=10000 tune=zerolatency byte-stream=true threads=4 ! mpegtsmux ! udpsink host=localhost port=9999"
, 0, (double)30, cv::Size(640, 480), true);
if (!writer.isOpened())
printf("=ERR= can't create video writer\n");
return -1;
cv::Mat frame;
int key;
while (true)
cap >> frame;
if (frame.empty())
break;
/* Process the frame here */
writer << frame;
key = cv::waitKey( 30 );
希望这会有所帮助。 ;)
【讨论】:
您好,先生!我也有同样的问题,我想将帧推送到作为 nginx 媒体服务器的 RTMP 流,但它不起作用,而且我尝试直接在 html 页面中打开,也不起作用。 试过了,不行。尝试玩 ffmpeg,我收到错误消息:收到不支持的 RTP 版本数据包 @StepanYakovenko 您的 Gstreamer 管道是否可以在不使用 OpenCV 的情况下工作?可能是版本兼容性问题。 感谢您分享您的解决方案!不过,我有一个简单的问题,我没有使用 VideoCapture 设备,是否仍然可以使用 appsrc 将 OpenCV Mat 直接写入 VideoWriter 管道?基本上我正在尝试扩展此代码,以便将结果写入 UDP 接收器,但接收端没有收到任何数据,所以我认为这是因为 github.com/IntelRealSense/librealsense/blob/master/wrappers/…【参考方案2】:好的,这很长一段时间的评论..它不是答案,但很少提示:
1a, 使用 netcast 检查接收方收到的内容.. 很简单:
shell> nc -l 5000 -u
当您向接收者发送某些内容时,检查正在打印的内容.. nc 设置为将所有内容转储到屏幕上..
1b,您可以尝试使用 vlc 作为接收方并检查调试消息(位于工具 > 消息中 - 或按 Ctrl+M)。将日志杠杆设置为 2 debug .. 然后打开网络流并使用 udp://@:5000
作为 URL..
顺便说一句,您可以使用带有管道的 rtp 对其进行测试:
appsrc ! x264enc ! mpegtsmux ! rtpmp2tpay ! udpsink host=localhost port=5000
然后在 vlc rtp://@:5000
中..
2,使用标识元素检查appsrc之后的内容(非常有用..我经常使用它来调试管道问题):
将管道更改为(注意标识元素和 udpsink 的 -v):
cv::VideoWriter writer = cv::VideoWriter("appsrc ! identity silent=false ! x264enc ! mpegtsmux ! udpsink -v host=localhost port=5000", 0, (double)30, cv::Size(640, 480), true);
然后运行你的代码并检查它的输出。它应该列出来自appsrc的传入缓冲区
3,对于您作为更新发布的代码 - 不,我的意思是使用 caps=
属性作为大写字母,但也许没有区别:
writer.open("appsrc caps="video/x-raw, framerate=30/1, width=640, height=480, format=RGB" ! autovideoconvert ! ximagesink sync=false", 0, (double)30, cv::Size(640, 480), true);
【讨论】:
感谢您的建议。抱歉回复晚了。我尝试了所有方法,但仍然在 appsrc 管道上获得Internal data flow error
。我想我错过了一些必要的东西。以上是关于如何将 opencv mat 写入 gstreamer 管道?的主要内容,如果未能解决你的问题,请参考以下文章