c++ - Gstreamer 流不适用于 OpenCV

标签 c++ opencv gstreamer

我想直接将 Gstreamer 管道与 OpenCV 结合使用来管理来自相机的图像采集。目前我没有相机,所以我一直在尝试从 URI 和本地文件获取视频。我正在使用带有 L4T 的 Jetson AGX Xavier(ubuntu 18.04),我的 OpenCV 版本包括 Gstreamer,并且两个库似乎都可以独立工作。

我遇到的问题是,当我使用 cv2.CAP_GSTREAMER 将定义管道的字符串传递给 VideoCapture 类时,我收到一些如下警告:

[ WARN:0] global /home/nvidia/opencv/modules/videoio/src/cap_gstreamer.cpp (854) open OpenCV | GStreamer warning: Error opening bin: could not link playbin0 to whatever sink I've defined

[ WARN:0] global /home/nvidia/opencv/modules/videoio/src/cap_gstreamer.cpp (597) isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created

我尝试了几个选项,您可以在下面的代码中看到它们:

bool receiver(const char* context)
{   
    VideoCapture cap(context, CAP_GSTREAMER);
    int fail = 0;

    while(!cap.isOpened())
    {
        cout<<"VideoCapture not opened"<<endl;
        fail ++;
        if (fail > 10){
                return false;
            }
        continue;
    }

    Mat frame;
    while(true) {

        cap.read(frame);

        if(frame.empty())
            return true;

        imshow("Receiver", frame);
        if(waitKey(1) == 'r')
            return false;
    }
    destroyWindow("Receiver");
    return true;
}

int main(int argc, char *argv[])
{
    GstElement *pipeline;
    const char* context = "gstlaunch v udpsrc port=5000 caps=\"application/xrtp\" ! rtph264depay ! ffdec_h264 ! ffmpegcolorspace ! ximagesink sync=false"; //Command for the camera that I don't have yet
    const char* test_context = "gstlaunch playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
    const char* thermal_context = "playbin uri=file:///home/nvidia/repos/vidtest/thermalVideo.avi ! appsink name=thermalsink";
    const char* local_context = "playbin uri=file:///home/nvidia/repos/flir/Video.avi";
    
    // gst_init(&argc, &argv);
    // pipeline = gst_parse_launch(test_context, NULL);
    bool correct_execution = receiver(thermal_context);
    if(correct_execution){
        cout << "openCV - gstreamer works!" << endl;
    } else {
        cout << "openCV - gstreamer FAILED" << endl;
    }
}

对于我测试过的命令,错误isPipelinePlaying OpenCV | GStreamer 警告:GStreamer:尚未创建管道 是持久的,如果我没有定义 AppSink,则上面显示的错误将针对 open OpenCV | 进行更改。 GStreamer 警告:在手动管道中找不到 appsink。 从警告中我可以了解到管道不完整或未正确创建,但我不知道为什么,我按照我在网上找到的示例进行操作,它们不包含任何其他步骤。

此外,当直接使用 Gstreamer 管道可视化流时,当我尝试打开本地视频时,一切似乎都工作正常,但第一帧被卡住并且不显示视频,它只是停留在第一帧框架。你知道为什么会发生这种情况吗? playbin uri 指向互联网地址,一切正常...代码如下:

#include <gst/gst.h>
#include <unistd.h> // for sleep function
#include <iostream>

using namespace std;

int main (int argc, char *argv[])
{
    GstElement *pipeline;
    GstBus *bus;
    GstMessage *msg;
    
    const char* context = "gstlaunch v udpsrc port=5000 caps=\"application/xrtp\" ! rtph264depay ! ffdec_h264 ! ffmpegcolorspace ! ximagesink sync=false";
    const char* local_context = "gst-launch-1.0 -v playbin uri=file:///home/nvidia/repos/APPIDE/vidtest/THERMAL/thermalVideo.avi";
    const char* test_context = "gstlaunch playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
    // Initialize gstreamer
    gst_init (&argc, &argv);

    // Create C pipeline from terminal command (context)
    pipeline = gst_parse_launch(local_context, NULL);
        
    // Start the pipeline
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    // Wait until error or EOS
    bus = gst_element_get_bus (pipeline);

    gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));

    /* Free resources */

    if (msg != NULL)

        gst_message_unref (msg);
        // g_print(msg);

    gst_object_unref (bus);

    gst_element_set_state (pipeline, GST_STATE_NULL);

    gst_object_unref (pipeline);
}

最佳答案

为了使用 gstreamer 后端,opencv VideoCapture 需要从源到 appsink 的有效管道字符串(BGR 颜色格式)。

您的管道字符串不正确,主要是因为它们以二进制命令(gSTLaunch for gst-launch-1.0、playbin)开头,您将在 shell 中使用该命令来运行这些命令。

您可以尝试使用此管道从 RTP/UDP 读取 H264 编码视频,使用专用 HW NVDEC 进行解码,然后从 NVMM 内存复制到系统内存,同时转换为 BGRx 格式,然后使用基于 CPU 的 videoconvert 转换 BGR 格式正如 opencv appsink 所期望的:

    const char* context = "udpsrc port=5000 caps=application/x-rtp,media=video,encoding-name=H264 ! rtph264depay ! h264parse ! nvv4l2decoder ! nvvidconv ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=BGR ! appsink drop=1";

或者对于 uridecodebin,如果选择了 NV 解码器,输出可能位于 NVMM 内存中,否则位于系统内存中,因此以下 nvvidconv 实例首先复制到 NVMM 内存,然后第二个 nvvidconv 转换为带有 HW 的 BGRx 并输出进入系统内存:

    const char* local_context = "uridecodebin uri=file:///home/nvidia/repos/APPIDE/vidtest/THERMAL/thermalVideo.avi ! nvvidconv ! video/x-raw(memory:NVMM) ! nvvidconv ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=BGR ! appsink drop=1";

请注意高分辨率:

  • 基于 CPU 的 videoconvert 可能是一个瓶颈。启用所有内核并提升时钟。
  • OpenCv imshow 可能不会那么快,具体取决于您的 OpenCv 构建的图形后端(GTK、QT4、QT5..)。在这种情况下,解决方案是使用 OpenCv Videowriter,使用 gstreamer 后端输出到 gstreamer 视频接收器。

关于c++ - Gstreamer 流不适用于 OpenCV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70753630/

相关文章:

c++ - 从 vector<point2f> 创建 Mat

c++ - OpenCV 3.0 中 ACCESS_FAST 的用途是什么?

java - 错误: undefined reference to 'libiconv_close' in Ndk Android Studio

c++ - 单字节字符代码

c++ - 如果手册中未提及,如何查找 PinPad XFS 的逻辑名称

java - 以编程方式切换 API 命名约定

webcam - 如何列出网络摄像头的可用配置?

c++ - 如何解决链接器错误 "cannot find -lgcc_s"

opencv - 特征检测/提取方法的指标

debugging - 如何在 gst_object_unref() 之后使用泄漏的文件描述符调试 gstreamer 管道?