GStreamer qmlglsink 管道中的崩溃动态重新绑定到不同的 GstGLVideoItem

Crash in GStreamer qmlglsink pipeline dynamically rebind to different GstGLVideoItem

我使用现有的 qmlglsink 示例之一从 4 个 IP 摄像机流式传输视频源。 4 个管道在引擎加载之前创建。

for(int i = 0; i < maxCameras; ++i) {
    GstElement* pipeline = gst_pipeline_new (NULL);
    GstElement* src = gst_element_factory_make ("udpsrc", NULL);
    GstElement* parse = gst_element_factory_make ("jpegparse", NULL);
    GstElement* decoder = gst_element_factory_make ("jpegdec", NULL);
    GstElement* glcolorconvert = gst_element_factory_make ("glcolorconvert", NULL);
    GstElement* glupload = gst_element_factory_make ("glupload", NULL);
    GstElement *sink = gst_element_factory_make ("qmlglsink", NULL);

    g_assert (src && parse && decoder && glupload && glcolorconvert && sink);

    g_object_set (G_OBJECT (src), "port", startingPort + i, NULL);

    g_object_set (G_OBJECT (sink), "sync", FALSE, NULL);

    gst_bin_add_many (GST_BIN (pipeline), src, parse, decoder, glupload, glcolorconvert, sink, NULL);
    if (!gst_element_link_many ( src, parse, decoder, glupload, glcolorconvert, sink, NULL)) {
        qDebug() << "Linking GStreamer pipeline elements failed";
    }

    sinks.insert(std::make_pair(QString::number(startingPort+i), sink));
    pipelines.insert(std::make_pair(QString::number(startingPort+i), pipeline));
}

在 Qml sink 中连接并处理

import QtQuick 2.15
import QtQuick.Layouts 1.15
import CustomProject 1.0
import org.freedesktop.gstreamer.GLVideoItem 1.0

Item {
    id: root

    signal clicked()
    required property int udpPort
    property var camConnect: undefined
    onUdpPortChanged: { setupConnection(); }

    onVisibleChanged: {
        if (visible) { setupConnection();
        } else { camConnect = undefined }
    }

    GstGLVideoItem {
        id: videoItem
        anchors.fill: parent
        function connect() {
            CameraSinksFactory.connectSink(this, udpPort)
        }
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            CameraSinksFactory.stopPipeline(udpPort)
            root.clicked()
        }
    }

    function setupConnection() {
        if (udpPort <= 0 || !root.visible) return;
        videoItem.connect()
        root.camConnect = CameraSinksFactory.getCamConnection(udpPort);
        root.camConnect.resolutionX =// - 15 root.width
        root.camConnect.resolutionY = root.height
        root.camConnect.bitrate = 15000000
        root.camConnect.streaming = root.visible
        CameraSinksFactory.startPipeline(udpPort)
    }
}

问题:主屏幕使用模型(提供 udpPort 作为唯一 ID)显示 4(2x2 网格)项目。当用户点击一个项目时 - 来自该摄像头的画面应该填满整个屏幕。在示例中,他们使用 4/6 显式项目创建 GridLayout 并仅操纵它们的可见性(实际上,单击的项目只剩下一个并占据整个屏幕)。
就我而言 - 我正在使用单独的项目进行全屏查看。所以我禁用流式传输(CamConnection class 与相机通信并发送命令)并隐藏 GridView。新 GstGLVideoItem 绑定到管道中的 qmlglsink。
一切正常,直到我重复单击序列(返回到 GridView 和全视图)。每次结束时:

Bail out! ERROR:../ext/qt/gstqsgtexture.cc:134:virtual void GstQSGTexture::bind(): code should not be reached

** (KMS:20495): CRITICAL **: 15:47:36.937: gst_video_frame_map_id: assertion 'info->width <= meta->width' failed
** ERROR:../ext/qt/gstqsgtexture.cc:134:virtual void GstQSGTexture::bind(): code should not be reached

根据插件代码分析,当 INFO(从 CAPS 读取的图像大小)大于提供缓冲区中元数据的大小时,就会发生这种情况。这是可以理解的 - 缓冲区太小。
我使用 GST_DEBUG=4/5/6/7 并且日志确认自动检测到的上限与发送到相机的命令中的请求匹配。
我可以使用示例,但项目假设另一个面板带有这些摄像头 - 所以上述问题将在不久的将来出现。

如何使整个设置工作?如何安全地将管道 qmlglsink 重新绑定到新的 QML VideoItem?

两种可能的解决方案:

  • 设置 gst_element_set_state (pipeline, GST_STATE_NULL);,将接收器小部件更改为新项目并启动管道 gst_element_set_state (pipeline, GST_STATE_PLAYING);
  • 使用带有 gst-pipeline 的 Qt 5 MediaPlayer 作为源。当可见时设置 source 并执行 start()。当不可见时,将 source 重置为 empty (重要)并执行 stop().

总的来说 - 当我们将新的 QML 项动态分配给管道接收器时,每次不创建管道可能带来的好处不值得麻烦。