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

Android 13下Qt QML视频播放器如何访问USB存储播放视频?

解决Android 13下Qt视频播放器无法播放USB存储视频的问题

你遇到的核心问题是Android 13(API 33)对可移动存储访问权限的限制变更,以及Qt访问USB存储的特殊处理要求,以下是具体的解决步骤:

一、权限配置与申请调整

Android 13对存储权限做了大幅调整,原有的READ_EXTERNAL_STORAGE权限已无法覆盖USB可移动存储的访问需求,需要补充以下配置:

1. 更新AndroidManifest.xml权限

添加针对可移动存储的管理权限及USB相关权限:

<!-- 管理所有外部存储权限,适用于API 30+ -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
                 android:minSdkVersion="30" />
<!-- USB主机权限,用于识别USB设备 -->
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<!-- 保留媒体权限用于兼容媒体库索引的视频 -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

2. 处理MANAGE_EXTERNAL_STORAGE权限

该权限无法通过常规运行时请求直接获取,必须引导用户到应用设置页面手动开启:

#include <QAndroidJniObject>
#include <QAndroidIntent>

// 检查是否已获取管理外部存储权限
bool hasManageStoragePermission() {
    return QAndroidJniObject::callStaticMethod<jboolean>(
        "android/os/Environment",
        "isExternalStorageManager"
    );
}

// 引导用户到设置页面开启权限
void requestManageStoragePermission() {
    QAndroidJniObject activity = QtAndroidPrivate::activity();
    QAndroidJniObject intentAction = QAndroidJniObject::getStaticObjectField(
        "android/provider/Settings",
        "ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION",
        "Ljava/lang/String;"
    );
    QAndroidJniObject packageName = activity.callObjectMethod(
        "getPackageName",
        "()Ljava/lang/String;"
    );
    QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod(
        "android/net/Uri",
        "fromParts",
        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;",
        QAndroidJniObject::fromString("package").object(),
        packageName.object(),
        nullptr
    );
    QAndroidJniObject intent = QAndroidJniObject("android/content/Intent",
        "(Ljava/lang/String;)V", intentAction.object());
    intent.callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", uri.object());
    QtAndroidPrivate::startActivity(intent.object(), 1001);
}

二、使用存储访问框架(SAF)访问USB视频

Android 13推荐使用SAF让用户主动选择文件,避免权限限制问题,同时Qt 6.4支持直接播放Content URI格式的视频:

1. 调用系统文件选择器

#include <QAndroidJniObject>
#include <QAndroidIntent>
#include <QString>

// 打开文件选择器,指定视频类型
void openVideoPicker() {
    QAndroidJniObject intent = QAndroidJniObject("android/content/Intent",
        "(Ljava/lang/String;)V", QAndroidJniObject::fromString("android.intent.action.GET_CONTENT").object());
    intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;",
        QAndroidJniObject::fromString("video/*").object());
    intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;",
        QAndroidJniObject::fromString("android.intent.category.OPENABLE").object());
    QtAndroidPrivate::startActivity(intent.object(), 1002);
}

// 处理文件选择结果(需重写Activity结果回调)
void onActivityResult(int requestCode, int resultCode, const QAndroidJniObject &data) {
    if (requestCode == 1002 && resultCode == -1) { // RESULT_OK = -1
        QAndroidJniObject uriObj = data.callObjectMethod("getData", "()Landroid/net/Uri;");
        QString videoUri = uriObj.callObjectMethod("toString", "()Ljava/lang/String;").toString();
        // 将URI传递给QML的Video组件
        emit videoFileSelected(videoUri);
    }
}

2. QML中播放Content URI

直接将获取到的content://开头的URI设置给Video组件:

Video {
    id: videoPlayer
    anchors.fill: parent
    source: ""
    onSourceChanged: {
        if (source) play()
    }
}

Connections {
    target: backend // 对应你的C++后端对象
    function onVideoFileSelected(uri) {
        videoPlayer.source = uri
    }
}

三、直接访问USB挂载路径的注意事项

如果需要直接访问USB的挂载路径(如/storage/XXXX-XXXX),需确保:

  1. 已获取MANAGE_EXTERNAL_STORAGE权限
  2. 验证Qt能否识别该路径:
#include <QDir>
#include <QDebug>

void checkUSBMounts() {
    QDir storageDir("/storage");
    QStringList dirs = storageDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    qDebug() << "已挂载的存储目录:" << dirs;
    // 遍历目录尝试打开视频文件,验证权限
    for (const QString &dir : dirs) {
        QDir usbDir("/storage/" + dir);
        QStringList videos = usbDir.entryList({"*.mp4", "*.mkv"}, QDir::Files);
        if (!videos.isEmpty()) {
            QFile testFile(usbDir.filePath(videos.first()));
            qDebug() << "能否访问" << testFile.fileName() << ":" << testFile.open(QIODevice::ReadOnly);
        }
    }
}

四、原代码的问题点

你原本的权限检查仅针对READ_EXTERNAL_STORAGE,但在Android 13中:

  • 该权限仅能访问媒体库中的文件,无法覆盖USB存储的非索引文件
  • QtAndroidPrivate::Storage对应的权限已不符合Android 13的权限模型,需替换为MANAGE_EXTERNAL_STORAGE的检查逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:53:10