如何从QVideoWidget迁移至QML:实现C++ QMediaPlayer对接QML VideoOutput
C++侧QVideoWidget替换为QML VideoOutput的实现方案
核心逻辑是利用Qt5媒体框架的通用输出接口,QML的VideoOutput本质是对QAbstractVideoSurface的封装,完全可以直接对接C++侧的QMediaPlayer,不需要改动现有媒体流读写的核心逻辑。
第一步:精简C++类的成员定义
移除原有类中对QVideoWidget的依赖:
- 头文件中删除
std::unique_ptr<QVideoWidget> videoWidget_成员 - 新增可安全跟踪QML组件的指针成员
QPointer<QAbstractVideoSurface> videoSurface_,避免QML组件销毁后出现野指针 - 保留现有
mediaPlayer_、videoBuffer_等核心媒体处理成员不变
修改后的createVideoOutput函数不再创建QVideoWidget:
void QtVideoOutput::createVideoOutput() { OPENAUTO_LOG(debug) << "[QtVideoOutput] create."; mediaPlayer_ = std::make_unique<QMediaPlayer>(nullptr, QMediaPlayer::StreamPlayback); }
第二步:开放C++侧的绑定接口
给QtVideoOutput类新增供QML调用的公共槽,用来完成QML侧VideoOutput和C++媒体播放器的关联:
public slots: // 绑定QML传入的VideoOutput对象作为视频输出 void bindVideoSurface(QObject* videoOutputObj) { // QML侧的VideoOutput对象本身支持直接作为QMediaPlayer的输出源 videoSurface_ = qobject_cast<QAbstractVideoSurface*>(videoOutputObj); if (mediaPlayer_ && videoSurface_) { mediaPlayer_->setVideoOutput(videoSurface_.get()); } } // 解绑现有视频输出,避免组件销毁后的访问异常 void unbindVideoSurface() { if (mediaPlayer_) { mediaPlayer_->setVideoOutput(nullptr); } videoSurface_.clear(); }
第三步:调整播放流程的实现逻辑
把原有onStartPlayback、onStopPlayback函数中所有和QVideoWidget相关的代码全部移除,保留核心的媒体加载、播放控制逻辑:
void QtVideoOutput::onStartPlayback() { // 移除QVideoWidget的比例设置、焦点设置、窗口显示、全屏设置等逻辑 if (!videoSurface_) { OPENAUTO_LOG(warning) << "未完成QML侧视频输出绑定,无法启动播放"; return; } mediaPlayer_->setMedia(QMediaContent(), &videoBuffer_); mediaPlayer_->play(); OPENAUTO_LOG(debug) << "Player error state -> " << mediaPlayer_->errorString().toStdString(); } void QtVideoOutput::onStopPlayback() { // 移除QVideoWidget的hide操作 mediaPlayer_->stop(); }
第四步:QML侧完成组件绑定
首先在程序初始化阶段,把QtVideoOutput的实例注册为QML可访问的上下文属性:
// main.cpp 初始化阶段的注册逻辑,qtVideoOutputInstance为工厂创建的有效实例 QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("openautoVideoOutput", qtVideoOutputInstance);
然后在QML的VideoOutput组件中,在组件生命周期节点主动完成绑定,不需要手动设置source属性:
VideoOutput { id: vidOutput anchors.fill: parent anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: 640 height: 480 objectName: "vidout" // 对应原QVideoWidget的忽略比例设置 fillMode: VideoOutput.Stretch // 组件加载完成后绑定C++侧的播放器 Component.onCompleted: { openautoVideoOutput.bindVideoSurface(this) } // 组件销毁前解绑,避免内存异常 Component.onDestruction: { openautoVideoOutput.unbindVideoSurface() } }
开发注意事项
- 原来在C++侧QVideoWidget上实现的窗口控制逻辑(全屏、置顶、焦点获取、比例调整)全部挪到QML侧管理,和其他QML组件的控制逻辑保持一致即可
- 不要使用
QWidget::createWindowContainer把原有QVideoWidget嵌进QML,这种方式会带来渲染层级冲突、性能损耗,直接绑定surface的方案是Qt原生支持的,渲染性能和原QVideoWidget方案无差异 - 保持原有线程模型不变:因为
mediaPlayer_本身在GUI线程创建,绑定、播放操作都在GUI线程执行,和原实现的线程安全要求完全一致,不需要调整跨线程的连接方式
内容的提问来源于stack exchange,提问作者mozcelikors
相关产品推荐
相关产品推荐

