You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt 5.15.2中用gst_parse_launch构建GStreamer qmlglsink管线报错

问题排查与解决方案

你的问题根源有两个:

  1. 使用gst_parse_launch构建管线时,没有给元件指定名字,导致gst_bin_get_by_name无法找到sink和source元件,返回NULL,后续调用g_object_set时触发空对象断言错误。
  2. 你把设置qmlglsink的widget属性的代码放在了app.exec()之后,此时Qt事件循环已经结束,QML元素videoItem可能已经销毁,即使sink有效也无法正确关联。

具体修正步骤

1. 给pipeline中的元件指定名字

修改gst_parse_launch的管线字符串,给videotestsrc和qmlglsink加上name属性,同时可以直接在管线里设置pattern参数:

mPipeline = gst_parse_launch("videotestsrc name=source pattern=18 ! glupload ! qmlglsink name=sink", NULL);

这样后续通过gst_bin_get_by_name就能正确获取到对应的元件。

2. 调整g_object_set的调用时机

把设置sink的widget属性的代码移到找到mItem之后、启动pipeline之前,并且增加空指针检查:

// 找到QML中的videoItem之后
mItem = mView->findChild<QQuickItem*>("videoItem");
if (mItem && mSink) {
    g_object_set(mSink, "widget", mItem, NULL);
}

// 再安排启动pipeline的任务
mView->scheduleRenderJob(new SetPlaying(mPipeline), QQuickView::BeforeSynchronizingStage);

原来的代码中g_object_set在app.exec()之后执行,此时QML界面已经关闭,mItem已经无效,完全起不到关联作用。

3. 增加错误检查

在调用gst_parse_launch和gst_bin_get_by_name后增加空指针检查,避免后续操作空对象:

mPipeline = gst_parse_launch("videotestsrc name=source pattern=18 ! glupload ! qmlglsink name=sink", NULL);
if (!mPipeline) {
    g_printerr("Failed to create pipeline\n");
    return 1;
}

mSink = gst_bin_get_by_name(GST_BIN(mPipeline), "sink");
if (!mSink) {
    g_printerr("Failed to get qmlglsink element\n");
    gst_object_unref(mPipeline);
    return 1;
}

mSource = gst_bin_get_by_name(GST_BIN(mPipeline), "source");
if (!mSource) {
    g_printerr("Failed to get videotestsrc element\n");
    gst_object_unref(mSink);
    gst_object_unref(mPipeline);
    return 1;
}

修正后的完整main.cpp关键部分

int main(int argc, char *argv[])
{
    GstElement* mPipeline = nullptr;
    GstElement* mSource = nullptr;
    GstElement* mSink = nullptr;
    QQuickView* mView = nullptr;
    QWidget* mWidget = nullptr;
    QQuickItem* mItem = nullptr;
    int ret = 0;

    gst_init(argc, argv);

    QApplication app(argc, argv);

    MainWindow* window = new MainWindow;

    // 构建带元件名称的pipeline,同时设置pattern参数
    mPipeline = gst_parse_launch("videotestsrc name=source pattern=18 ! glupload ! qmlglsink name=sink", NULL);
    if (!mPipeline) {
        g_printerr("Failed to create pipeline\n");
        ret = 1;
        goto cleanup;
    }

    mSink = gst_bin_get_by_name(GST_BIN(mPipeline), "sink");
    if (!mSink) {
        g_printerr("Failed to retrieve qmlglsink\n");
        ret = 1;
        goto cleanup;
    }

    mSource = gst_bin_get_by_name(GST_BIN(mPipeline), "source");
    if (!mSource) {
        g_printerr("Failed to retrieve videotestsrc\n");
        ret = 1;
        goto cleanup;
    }

    mView = new QQuickView;
    mView->setSource(QUrl(QStringLiteral("qrc:/video.qml")));

    mWidget = QWidget::createWindowContainer(mView, window);
    mItem = mView->findChild<QQuickItem*>("videoItem");

    // 关联sink和QML元素
    if (mItem && mSink) {
        g_object_set(mSink, "widget", mItem, NULL);
    } else {
        g_printerr("Failed to get videoItem from QML\n");
        ret = 1;
        goto cleanup;
    }

    // 安排启动pipeline的任务
    mView->scheduleRenderJob(new SetPlaying(mPipeline), QQuickView::BeforeSynchronizingStage);

    window->setCentralWidget(mWidget);
    window->show();

    ret = app.exec();

cleanup:
    // 释放资源
    if (mSource) gst_object_unref(mSource);
    if (mSink) gst_object_unref(mSink);
    if (mPipeline) gst_element_set_state(mPipeline, GST_STATE_NULL);
    if (mPipeline) gst_object_unref(mPipeline);
    gst_deinit();
    return ret;
}

额外说明

  • 手动创建元件时能成功,是因为你直接持有了mSink和mSource的指针,不需要通过名字查找,所以没有暴露这个问题。
  • 后续播放H.264视频文件时,只需要把管线字符串替换为对应的解码管线即可,比如:
    filesrc location=/path/to/your/video.mp4 ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! glupload ! qmlglsink name=sink
    
    可根据你的GStreamer插件情况调整解码元件(比如avdec_h264替换为openh264dec等)。

内容的提问来源于stack exchange,提问作者weirdgyn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:55:18