开源 C++ QT QML 开发(二十一)多媒体--视频播放

文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C++ QT QML 开发(一)基本介绍

开源 C++ QT QML 开发(二)工程结构

开源 C++ QT QML 开发(三)常用控件

开源 C++ QT QML 开发(四)复杂控件--Listview

开源 C++ QT QML 开发(五)复杂控件--Gridview

开源 C++ QT QML 开发(六)自定义控件--波形图

开源 C++ QT QML 开发(七)自定义控件--仪表盘

推荐链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

本章节主要内容是:使用qml编写了媒体播放器,使用了K-Lite Codec Pack Mega作为解码包,实现mp4的播放。

1.代码分析

2.所有源码

3.效果演示

一、代码分析1. 主窗口和属性定义

复制代码
ApplicationWindow {
    id: mainWindow
    width: 1024
    height: 720
    visible: true
    title: "MP4播放器 - 完全兼容版"
    minimumWidth: 800
    minimumHeight: 600

    property string currentVideo: ""  // 当前视频文件路径
    property bool hasVideo: videoPlayer.hasVideo  // 绑定到Video组件的hasVideo属性
    property bool isSeekable: videoPlayer.seekable  // 是否支持跳转
    property string currentStatus: "等待选择文件"  // 当前状态显示文本
}
  1. 核心视频播放组件

Video组件及其信号处理

复制代码
Video {
    id: videoPlayer
    anchors.fill: parent
    anchors.margins: 2
    source: currentVideo  // 绑定到currentVideo属性
    fillMode: VideoOutput.PreserveAspectFit  // 保持宽高比
    volume: volumeSlider.value  // 绑定音量滑块
    autoPlay: true  // 自动播放

    // 状态变化信号处理
    onStatusChanged: {
        console.log("视频状态变化:", status)
        updateStatusDisplay()  // 更新状态显示
    }

    // 播放状态变化信号处理
    onPlaybackStateChanged: {
        console.log("播放状态变化:", playbackState)
        updatePlayButton()  // 更新播放按钮文本
    }

    // 视频时长变化信号处理
    onDurationChanged: {
        console.log("视频时长:", duration, "ms")
        progressSlider.to = Math.max(1, duration)  // 设置进度条最大值
    }

    // 播放位置变化信号处理
    onPositionChanged: {
        if (!progressSlider.pressed) {  // 避免拖动时冲突
            progressSlider.value = position  // 更新进度条位置
        }
    }
}
  1. 主要控制函数

播放/暂停切换函数

复制代码
function togglePlayPause() {
    if (!hasVideo || currentVideo === "") return  // 安全检查

    if (videoPlayer.playbackState === MediaPlayer.PlayingState) {
        videoPlayer.pause()  // 如果正在播放,则暂停
    } else {
        videoPlayer.play()  // 否则开始播放
    }
}

状态显示更新函数

复制代码
function updateStatusDisplay() {
    var status = videoPlayer.status  // 获取当前视频状态

    switch(status) {
        case MediaPlayer.NoMedia:
            currentStatus = "无媒体文件"
            break
        case MediaPlayer.Loading:
            currentStatus = "加载中..."
            break
        case MediaPlayer.Loaded:
            currentStatus = "已加载"
            break
        case MediaPlayer.Stalling:
            currentStatus = "缓冲中..."
            break
        case MediaPlayer.Buffering:
            currentStatus = "缓冲中"
            break
        case MediaPlayer.Buffered:
            currentStatus = "就绪"
            break
        case MediaPlayer.EndOfMedia:
            currentStatus = "播放结束"
            break
        case MediaPlayer.InvalidMedia:
            currentStatus = "格式不支持"
            break
        case MediaPlayer.UnknownStatus:
            currentStatus = "未知状态"
            break
        default:
            currentStatus = "就绪"
    }

    console.log("状态更新:", currentStatus)
}

播放按钮更新函数

复制代码
function updatePlayButton() {
    switch(videoPlayer.playbackState) {
        case MediaPlayer.PlayingState:
            playButton.text = "⏸️ 暂停"  // 播放中显示暂停
            break
        case MediaPlayer.PausedState:
            playButton.text = "▶️ 继续"  // 暂停中显示继续
            break
        default:
            playButton.text = "▶️ 播放"  // 默认显示播放
    }
}
  1. UI状态显示函数

状态图标获取函数

复制代码
function getStatusIcon() {
    if (currentVideo === "") return "📁"  // 无文件
    if (videoPlayer.status === MediaPlayer.InvalidMedia) return "❌"  // 格式错误
    if (videoPlayer.status === MediaPlayer.Loading) return "⏳"  // 加载中
    if (!hasVideo) return "🎬"  // 无视频流
    return "✅"  // 正常状态
}

状态消息获取函数

复制代码
function getStatusMessage() {
    if (currentVideo === "") return "请选择视频文件开始播放"  // 初始提示
    if (videoPlayer.status === MediaPlayer.InvalidMedia) return "无法播放此文件"  // 错误提示
    if (videoPlayer.status === MediaPlayer.Loading) return "正在加载..."  // 加载提示
    if (!hasVideo) return "准备播放"  // 准备状态
    return "正在播放: " + getFileName(currentVideo)  // 播放状态显示文件名
}

错误详情获取函数

复制代码
function getErrorDetails() {
    if (videoPlayer.status === MediaPlayer.InvalidMedia) {
        return "此文件需要H.264解码器\n请安装K-Lite Codec Pack\n下载: https://codecguide.com/download_kl.htm"
    }
    return ""  // 无错误时返回空字符串
}
  1. 工具函数

状态颜色获取函数

复制代码
function getStatusColor() {
    if (videoPlayer.status === MediaPlayer.InvalidMedia) return "#e74c3c"  // 红色-错误
    if (videoPlayer.status === MediaPlayer.Buffered) return "#2ecc71"  // 绿色-就绪
    if (videoPlayer.status === MediaPlayer.Loading) return "#f39c12"  // 橙色-加载中
    return "#3498db"  // 蓝色-默认
}

文件名提取函数

复制代码
function getFileName(filePath) {
    var path = filePath.toString()  // 转换为字符串
    var fileName = path.split('/').pop() || path.split('\\').pop()  // 从路径中提取文件名
    return fileName || "未知文件"  // 返回文件名或默认值
}

时间格式化函数

复制代码
function formatTime(milliseconds) {
    if (!milliseconds || isNaN(milliseconds)) return "00:00"  // 无效时间处理

    var totalSeconds = Math.floor(milliseconds / 1000)  // 转换为秒
    var hours = Math.floor(totalSeconds / 3600)  // 计算小时
    var minutes = Math.floor((totalSeconds % 3600) / 60)  // 计算分钟
    var seconds = totalSeconds % 60  // 计算秒数

    if (hours > 0) {
        // 有时长格式:HH:MM:SS
        return hours.toString().padStart(2, '0') + ":" +
               minutes.toString().padStart(2, '0') + ":" +
               seconds.toString().padStart(2, '0')
    } else {
        // 无时长格式:MM:SS
        return minutes.toString().padStart(2, '0') + ":" +
               seconds.toString().padStart(2, '0')
    }
}
  1. 进度条控制函数

进度条拖动处理

复制代码
Slider {
    id: progressSlider
    // ... 其他属性
    
    onMoved: {
        if (isSeekable) {  // 检查是否支持跳转
            videoPlayer.seek(value)  // 跳转到指定位置
        }
    }
}
  1. C++主程序函数

环境设置函数

复制代码
int main(int argc, char *argv[])
{
    // 关键环境变量设置
    qputenv("QT_MEDIA_BACKEND", "windows");  // 强制使用Windows媒体后端
    qputenv("QT_LOGGING_RULES", "qt.multimedia.*=true");  // 启用多媒体调试日志
    qputenv("MF_DEBUG", "1");  // 启用Media Foundation调试
    
    // 应用程序属性设置
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);  // 高DPI支持
    QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);  // 软件渲染
    
    QGuiApplication app(argc, argv);
    
    // 应用程序信息
    app.setApplicationName("MP4播放器");
    app.setApplicationVersion("1.0");
    app.setOrganizationName("MyCompany");
}

系统诊断函数

复制代码
// 详细的多媒体支持信息
qDebug() << "=== 多媒体系统诊断信息 ===";
qDebug() << "应用程序目录:" << QDir::currentPath();
qDebug() << "临时目录:" << QStandardPaths::writableLocation(QStandardPaths::TempLocation);
qDebug() << "支持的MIME类型数量:" << QMediaPlayer::supportedMimeTypes().count();

// 输出支持的格式
auto mimeTypes = QMediaPlayer::supportedMimeTypes();
for (int i = 0; i < qMin(10, mimeTypes.count()); ++i) {
    qDebug() << "支持格式:" << mimeTypes[i];
}
  1. 文件对话框处理

    FileDialog {
    id: fileDialog
    title: "选择视频文件"
    nameFilters: [ /* 文件过滤器列表 */ ]

    复制代码
     onAccepted: {
         currentVideo = fileDialog.fileUrl  // 获取选择的文件URL
         console.log("加载文件:", currentVideo)  // 调试日志
     }

    }

二、所有源码

1.安装正确的解码器包

推荐方案:K-Lite Codec Pack Mega

下载地址:https://codecguide.com/download_kl.htm

选择版本:下载 Mega 版本(包含所有解码器)

安装步骤:运行安装程序

2.pro文件

复制代码
QT += quick quickcontrols2 multimedia multimediawidgets

3.main.qml文件源码

复制代码
import QtQuick 2.14
import QtQuick.Window 2.14
import QtMultimedia 5.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.14

ApplicationWindow {
    id: mainWindow
    width: 1024
    height: 720
    visible: true
    title: "MP4播放器 - 完全兼容版"
    minimumWidth: 800
    minimumHeight: 600

    property string currentVideo: ""
    property bool hasVideo: videoPlayer.hasVideo
    property bool isSeekable: videoPlayer.seekable
    property string currentStatus: "等待选择文件"

    // 背景渐变
    Rectangle {
        anchors.fill: parent
        gradient: Gradient {
            GradientStop { position: 0.0; color: "#2c3e50" }
            GradientStop { position: 1.0; color: "#3498db" }
        }
    }

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 10

        // 标题栏
        Rectangle {
            Layout.fillWidth: true
            height: 60
            color: "transparent"

            RowLayout {
                anchors.fill: parent

                Text {
                    text: "🎬 MP4播放器"
                    color: "white"
                    font.pixelSize: 24
                    font.bold: true
                }

                Item { Layout.fillWidth: true }

                Text {
                    text: "Powered by Qt5.14"
                    color: "lightgray"
                    font.pixelSize: 12
                }
            }
        }

        // 视频显示区域
        Rectangle {
            id: videoContainer
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: "black"
            radius: 8
            clip: true

            Video {
                id: videoPlayer
                anchors.fill: parent
                anchors.margins: 2
                source: currentVideo
                fillMode: VideoOutput.PreserveAspectFit
                volume: volumeSlider.value
                autoPlay: true

                // 状态变化处理 - 使用正确的信号
                onStatusChanged: {
                    console.log("视频状态变化:", status)
                    updateStatusDisplay()
                }

                onPlaybackStateChanged: {
                    console.log("播放状态变化:", playbackState)
                    updatePlayButton()
                }

                onDurationChanged: {
                    console.log("视频时长:", duration, "ms")
                    progressSlider.to = Math.max(1, duration)
                }

                onPositionChanged: {
                    if (!progressSlider.pressed) {
                        progressSlider.value = position
                    }
                }
            }

            // 加载指示器
            BusyIndicator {
                id: loadingIndicator
                anchors.centerIn: parent
                running: videoPlayer.status === MediaPlayer.Loading ||
                         videoPlayer.status === MediaPlayer.Stalling ||
                         videoPlayer.status === MediaPlayer.Buffering
                visible: running
            }

            // 居中状态信息
            Column {
                id: centerInfo
                anchors.centerIn: parent
                spacing: 10
                visible: currentVideo === "" || videoPlayer.status === MediaPlayer.InvalidMedia || !hasVideo

                Text {
                    id: statusIcon
                    anchors.horizontalCenter: parent.horizontalCenter
                    color: "white"
                    font.pixelSize: 48
                    text: getStatusIcon()
                }

                Text {
                    id: statusMessage
                    anchors.horizontalCenter: parent.horizontalCenter
                    color: "white"
                    font.pixelSize: 18
                    text: getStatusMessage()
                    horizontalAlignment: Text.AlignHCenter
                }

                Text {
                    id: errorDetails
                    anchors.horizontalCenter: parent.horizontalCenter
                    color: "yellow"
                    font.pixelSize: 12
                    text: getErrorDetails()
                    visible: text !== ""
                    horizontalAlignment: Text.AlignHCenter
                    width: 400
                    wrapMode: Text.WordWrap
                }
            }

            // 点击控制播放/暂停
            MouseArea {
                anchors.fill: parent
                onClicked: togglePlayPause()
                enabled: hasVideo && currentVideo !== ""
            }
        }

        // 进度控制区域
        ColumnLayout {
            Layout.fillWidth: true
            spacing: 5

            // 进度条
            Slider {
                id: progressSlider
                Layout.fillWidth: true
                from: 0
                to: 100
                value: 0
                enabled: isSeekable && hasVideo

                onMoved: {
                    if (isSeekable) {
                        videoPlayer.seek(value)
                    }
                }

                // 自定义样式
                background: Rectangle {
                    implicitHeight: 6
                    color: "#5a5a5a"
                    radius: 3
                }

                handle: Rectangle {
                    x: progressSlider.leftPadding + progressSlider.visualPosition * (progressSlider.availableWidth - width)
                    y: progressSlider.topPadding + progressSlider.availableHeight / 2 - height / 2
                    implicitWidth: 16
                    implicitHeight: 16
                    radius: 8
                    color: progressSlider.pressed ? "#f0f0f0" : "#ffffff"
                    border.color: "#bdbebf"
                }
            }

            // 时间信息
            RowLayout {
                Layout.fillWidth: true

                Text {
                    color: "white"
                    text: formatTime(videoPlayer.position)
                    font.pixelSize: 12
                }

                Item { Layout.fillWidth: true }

                Text {
                    color: "white"
                    text: formatTime(videoPlayer.duration)
                    font.pixelSize: 12
                }
            }
        }

        // 控制按钮区域
        Rectangle {
            Layout.fillWidth: true
            height: 70
            color: "transparent"

            RowLayout {
                anchors.fill: parent
                spacing: 10

                // 文件操作按钮
                Button {
                    text: "📁 打开文件"
                    onClicked: fileDialog.open()

                    background: Rectangle {
                        color: "#27ae60"
                        radius: 5
                    }

                    contentItem: Text {
                        text: parent.text
                        color: "white"
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        font.bold: true
                    }
                }

                // 播放控制按钮
                Button {
                    id: playButton
                    text: "▶️ 播放"
                    onClicked: togglePlayPause()
                    enabled: hasVideo && currentVideo !== ""

                    background: Rectangle {
                        color: enabled ? "#2980b9" : "#7f8c8d"
                        radius: 5
                    }

                    contentItem: Text {
                        text: parent.text
                        color: "white"
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        font.bold: true
                    }
                }

                Button {
                    text: "⏹️ 停止"
                    onClicked: {
                        videoPlayer.stop()
                        progressSlider.value = 0
                    }
                    enabled: hasVideo && currentVideo !== ""

                    background: Rectangle {
                        color: enabled ? "#e74c3c" : "#7f8c8d"
                        radius: 5
                    }

                    contentItem: Text {
                        text: parent.text
                        color: "white"
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                    }
                }

                Item { Layout.fillWidth: true }

                // 音量控制
                RowLayout {
                    spacing: 5

                    Text {
                        text: "🔊"
                        color: "white"
                        font.pixelSize: 16
                    }

                    Slider {
                        id: volumeSlider
                        from: 0
                        to: 1
                        value: 0.8
                        stepSize: 0.1
                        Layout.preferredWidth: 100

                        background: Rectangle {
                            implicitHeight: 4
                            color: "#5a5a5a"
                            radius: 2
                        }
                    }

                    Text {
                        color: "white"
                        text: Math.round(volumeSlider.value * 100) + "%"
                        font.pixelSize: 12
                        Layout.preferredWidth: 40
                    }
                }
            }
        }

        // 状态信息栏
        Rectangle {
            Layout.fillWidth: true
            height: 30
            color: "#34495e"
            radius: 4

            RowLayout {
                anchors.fill: parent
                anchors.margins: 5

                Text {
                    color: "white"
                    text: "文件: " + (currentVideo ? getFileName(currentVideo) : "未选择")
                    font.pixelSize: 11
                    elide: Text.ElideMiddle
                    Layout.fillWidth: true
                }

                Text {
                    color: getStatusColor()
                    text: currentStatus
                    font.pixelSize: 11
                    font.bold: true
                }

                Text {
                    color: hasVideo ? "lightgreen" : "red"
                    text: hasVideo ? "🎥 有视频" : "❌ 无视频"
                    font.pixelSize: 11
                }
            }
        }
    }

    FileDialog {
        id: fileDialog
        title: "选择视频文件"
        nameFilters: [
            "MP4文件 (*.mp4)",
            "AVI文件 (*.avi)",
            "MKV文件 (*.mkv)",
            "MOV文件 (*.mov)",
            "WMV文件 (*.wmv)",
            "所有文件 (*.*)"
        ]

        onAccepted: {
            currentVideo = fileDialog.fileUrl
            console.log("加载文件:", currentVideo)
        }
    }

    // 功能函数
    function togglePlayPause() {
        if (!hasVideo || currentVideo === "") return

        if (videoPlayer.playbackState === MediaPlayer.PlayingState) {
            videoPlayer.pause()
        } else {
            videoPlayer.play()
        }
    }

    function updateStatusDisplay() {
        var status = videoPlayer.status

        switch(status) {
            case MediaPlayer.NoMedia:
                currentStatus = "无媒体文件"
                break
            case MediaPlayer.Loading:
                currentStatus = "加载中..."
                break
            case MediaPlayer.Loaded:
                currentStatus = "已加载"
                break
            case MediaPlayer.Stalling:
                currentStatus = "缓冲中..."
                break
            case MediaPlayer.Buffering:
                currentStatus = "缓冲中"
                break
            case MediaPlayer.Buffered:
                currentStatus = "就绪"
                break
            case MediaPlayer.EndOfMedia:
                currentStatus = "播放结束"
                break
            case MediaPlayer.InvalidMedia:
                currentStatus = "格式不支持"
                break
            case MediaPlayer.UnknownStatus:
                currentStatus = "未知状态"
                break
            default:
                currentStatus = "就绪"
        }

        console.log("状态更新:", currentStatus)
    }

    function updatePlayButton() {
        switch(videoPlayer.playbackState) {
            case MediaPlayer.PlayingState:
                playButton.text = "⏸️ 暂停"
                break
            case MediaPlayer.PausedState:
                playButton.text = "▶️ 继续"
                break
            default:
                playButton.text = "▶️ 播放"
        }
    }

    function getStatusIcon() {
        if (currentVideo === "") return "📁"
        if (videoPlayer.status === MediaPlayer.InvalidMedia) return "❌"
        if (videoPlayer.status === MediaPlayer.Loading) return "⏳"
        if (!hasVideo) return "🎬"
        return "✅"
    }

    function getStatusMessage() {
        if (currentVideo === "") return "请选择视频文件开始播放"
        if (videoPlayer.status === MediaPlayer.InvalidMedia) return "无法播放此文件"
        if (videoPlayer.status === MediaPlayer.Loading) return "正在加载..."
        if (!hasVideo) return "准备播放"
        return "正在播放: " + getFileName(currentVideo)
    }

    function getErrorDetails() {
        if (videoPlayer.status === MediaPlayer.InvalidMedia) {
            return "此文件需要H.264解码器\n请安装K-Lite Codec Pack\n下载: https://codecguide.com/download_kl.htm"
        }
        return ""
    }

    function getStatusColor() {
        if (videoPlayer.status === MediaPlayer.InvalidMedia) return "#e74c3c"
        if (videoPlayer.status === MediaPlayer.Buffered) return "#2ecc71"
        if (videoPlayer.status === MediaPlayer.Loading) return "#f39c12"
        return "#3498db"
    }

    function getFileName(filePath) {
        var path = filePath.toString()
        var fileName = path.split('/').pop() || path.split('\\').pop()
        return fileName || "未知文件"
    }

    function formatTime(milliseconds) {
        if (!milliseconds || isNaN(milliseconds)) return "00:00"

        var totalSeconds = Math.floor(milliseconds / 1000)
        var hours = Math.floor(totalSeconds / 3600)
        var minutes = Math.floor((totalSeconds % 3600) / 60)
        var seconds = totalSeconds % 60

        if (hours > 0) {
            return hours.toString().padStart(2, '0') + ":" +
                   minutes.toString().padStart(2, '0') + ":" +
                   seconds.toString().padStart(2, '0')
        } else {
            return minutes.toString().padStart(2, '0') + ":" +
                   seconds.toString().padStart(2, '0')
        }
    }

    Component.onCompleted: {
        console.log("播放器初始化完成")
        console.log("视频输出支持:", videoPlayer.availability)
        console.log("是否有视频:", videoPlayer.hasVideo)
        console.log("是否可跳转:", videoPlayer.seekable)
    }
}
  1. main.cpp文件源码

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QMediaPlayer>
    #include <QDebug>
    #include <QDir>
    #include <QStandardPaths>

    int main(int argc, char argv[])
    {
    // 设置环境变量 - 在创建QGuiApplication之前
    qputenv("QT_MEDIA_BACKEND", "windows");
    qputenv("QT_LOGGING_RULES", "qt.multimedia.
    =true");
    qputenv("MF_DEBUG", "1");

    复制代码
     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
     QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
    
     QGuiApplication app(argc, argv);
    
     // 设置应用程序信息
     app.setApplicationName("MP4播放器");
     app.setApplicationVersion("1.0");
     app.setOrganizationName("MyCompany");
    
     // 详细的多媒体支持信息
     qDebug() << "=== 多媒体系统诊断信息 ===";
     qDebug() << "应用程序目录:" << QDir::currentPath();
     qDebug() << "临时目录:" << QStandardPaths::writableLocation(QStandardPaths::TempLocation);
     qDebug() << "支持的MIME类型数量:" << QMediaPlayer::supportedMimeTypes().count();
    
     // 输出前10个支持的格式
     auto mimeTypes = QMediaPlayer::supportedMimeTypes();
     for (int i = 0; i < qMin(10, mimeTypes.count()); ++i) {
         qDebug() << "支持格式:" << mimeTypes[i];
     }
    
     qDebug() << "环境变量:";
     qDebug() << "QT_MEDIA_BACKEND =" << qgetenv("QT_MEDIA_BACKEND");
     qDebug() << "PATH =" << qgetenv("PATH");
    
     QQmlApplicationEngine engine;
    
     // 连接加载失败信号
     QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                      &app, [](QObject *obj, const QUrl &objUrl) {
         if (!obj) {
             qCritical() << "QML加载失败:" << objUrl;
             QCoreApplication::exit(-1);
         } else {
             qDebug() << "QML加载成功";
         }
     }, Qt::QueuedConnection);
    
     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
     return app.exec();

    }

三、效果演示

选择MP4文件,进行播放,可以拖动和暂停。需要注意的是必须要安装解码器才行。

相关推荐
qq_479875436 小时前
C++ std::Set<std::pair>
开发语言·c++
云知谷9 小时前
【C++基本功】C++适合做什么,哪些领域适合哪些领域不适合?
c语言·开发语言·c++·人工智能·团队开发
仰泳的熊猫9 小时前
LeetCode:785. 判断二分图
数据结构·c++·算法·leetcode
^Moon^9 小时前
CycloneDDS:跨主机多进程通信全解析
c++·分布式·dds
C_Liu_11 小时前
C++:list
开发语言·c++
my rainy days11 小时前
C++:友元
开发语言·c++·算法
鄃鳕12 小时前
python 字典 列表 类比c++【python】
c++·python
保持低旋律节奏13 小时前
C++——list链表
c++·链表·list
2401_8414956414 小时前
【数据结构】基于Floyd算法的最短路径求解
java·数据结构·c++·python·算法··floyd
Algebraaaaa14 小时前
什么是前端、后端与全栈开发,Qt属于什么?
开发语言·前端·qt