如何将 opencv mat 写入 gstreamer 管道?

How to write opencv mat to gstreamer pipeline?

我想将一些 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 通过网络发送吗? 如果可以,我该如何做实现这个?

附带问题,有什么方法可以调试 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 工作正常。

好的,评论很长..它不是答案,但有一些提示:

1a,使用网络广播检查接收方收到的内容.. 很简单:

shell> nc -l 5000 -u

然后检查当你向接收者发送东西时打印的内容.. nc 设置为将所有内容转储到屏幕上..

1b,您可以尝试使用 vlc for reciever 并检查调试消息(位于“工具”>“消息”中 - 或按 Ctrl+M)。将日志控制杆设置为 2 debug .. 然后打开网络流并使用 udp://@:5000 作为 URL..

顺便说一句,你可以用带有管道的 rtp 来测试它:

appsrc ! x264enc ! mpegtsmux ! rtpmp2tpay ! udpsink host=localhost port=5000

在vlc中rtp://@:5000然后..

2,使用标识元素检查 appsrc 之后流的是什么(非常有帮助.. 我经常用它来调试管道问题):

将您的管道更改为(注意身份元素和 -v for udpsink):

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之后只用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 );
    }
}

希望这对您有所帮助。 ;)