web-dev-qa-db-ja.com

opencvマットをgstreamerパイプラインに書き込む方法は?

いくつかの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が正常に機能しているため、何が問題なのかわかりません。

9
j0e1in

何時間もの検索とテストの後、ようやく答えがわかりました。重要なのは、videoconvertの後にappsrcのみを使用することです。キャップを設定する必要はありません。したがって、ライターパイプラインは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 );
    }
}

お役に立てれば。 ;)

15
j0e1in

わかりました。コメントは長めです。答えはありませんが、いくつかのヒント:

1a、netcastを使用して、受信側で何が受信されているかを確認します。

Shell> nc -l 5000 -u

受信機に何かを送信するときに、何が印刷されているかを確認するよりも、ncはすべてを画面にダンプするように設定されています。

1b、受信者のvlcを試し、デバッグメッセージを確認できます([ツール]> [メッセージ]にあるか、Ctrl + Mを押します)。ログレバーを2デバッグに設定します。次に、ネットワークストリームを開き、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);
2
nayana