Qt 5.15.2中用gst_parse_launch构建GStreamer qmlglsink管线报错
问题排查与解决方案
你的问题根源有两个:
- 使用
gst_parse_launch构建管线时,没有给元件指定名字,导致gst_bin_get_by_name无法找到sink和source元件,返回NULL,后续调用g_object_set时触发空对象断言错误。 - 你把设置
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视频文件时,只需要把管线字符串替换为对应的解码管线即可,比如:
可根据你的GStreamer插件情况调整解码元件(比如filesrc location=/path/to/your/video.mp4 ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! glupload ! qmlglsink name=sinkavdec_h264替换为openh264dec等)。
内容的提问来源于stack exchange,提问作者weirdgyn
相关产品推荐
相关产品推荐

