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

如何从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 19:54:03