Android+Chaquopy:Python接收视频URI路径无法读取文件
问题解决:Chaquopy中Python无法访问Android视频文件
核心原因
- Scoped Storage限制:Android 10及以上版本采用分区存储,
READ_EXTERNAL_STORAGE权限不再赋予直接访问文件路径的权限,MediaStore.MediaColumns.DATA字段可能返回无效路径或为空。 - 路径访问失效:直接传递文件路径给Python后,OpenCV无法通过路径访问受Scoped Storage保护的视频文件,导致权限校验失败。
解决方案
放弃通过文件路径访问视频,改用**文件描述符(File Descriptor)**传递给Python,利用Android ContentResolver打开文件的可读描述符,让OpenCV通过描述符直接读取视频内容。同时适配Android 13+的新权限体系。
步骤1:修改权限配置
在AndroidManifest.xml中添加适配高版本的权限:
<!-- Android 13+ 视频读取权限 --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- 兼容Android 12及以下版本 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
步骤2:更新权限检查逻辑
修改MainActivity中的checkPermission方法,适配Android 13的权限:
private boolean checkPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_VIDEO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_MEDIA_VIDEO}, REQUEST_PERMISSION_READ_EXTERNAL_STORAGE); return false; } } else { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_READ_EXTERNAL_STORAGE); return false; } } return true; }
步骤3:修改VideoProcessor类,传递文件描述符
删除getRealPathFromUri方法,更新processVideo逻辑,通过ContentResolver获取文件描述符并传递给Python:
public class VideoProcessor { private static final String TAG = "APP_TAG"; private static List<byte[]> processedFrames; public static void processVideo(Context context, Uri videoUri) { Python py = Python.getInstance(); if (!Python.isStarted()) { Python.start(new AndroidPlatform(context)); } processedFrames = new ArrayList<>(); // 初始化列表,避免空指针 try (ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(videoUri, "r")) { if (pfd == null) { Log.e(TAG, "Failed to open file descriptor for video"); return; } int fileDescriptor = pfd.getFd(); PyObject videoProcessingModule = py.getModule("script"); // 传递文件描述符给Python处理函数 PyObject processedData = videoProcessingModule.callAttr("process_video", fileDescriptor); PyObject[] processedFramesArray = processedData.toJava(PyObject[].class); for (PyObject frame : processedFramesArray) { processedFrames.add(frame.toJava(byte[].class)); } } catch (IOException e) { Log.e(TAG, "Error accessing video file: " + e.getMessage()); e.printStackTrace(); } } public static List<byte[]> getProcessedFrames() { return processedFrames; } }
步骤4:修改Python脚本,通过文件描述符读取视频
更新Python的process_video函数,接收文件描述符并让OpenCV通过该描述符打开视频:
import cv2 import numpy as np import os from contextlib import closing def process_video(fd): print("Received file descriptor:", fd) # 用文件描述符打开视频,确保资源正确释放 with closing(os.fdopen(fd, 'rb')): cap = cv2.VideoCapture(fd) frames = [] while cap.isOpened(): ret, frame = cap.read() if not ret: break # 帧处理:转为灰度图 processed_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) frames.append(processed_frame) cap.release() print("Number of processed frames:", len(frames)) # 返回转为字节数组的帧列表 return [frame.tobytes() for frame in frames]
关键说明
- 文件描述符是Android系统级的文件访问凭证,不受Scoped Storage路径限制,能让Python直接读取视频内容。
- 使用
try-with-resources和closing上下文管理器确保文件资源正确释放,避免内存泄漏。 - Android 13+必须使用
READ_MEDIA_VIDEO权限,旧版本仍用READ_EXTERNAL_STORAGE。
内容的提问来源于stack exchange,提问作者Matheus Fortunato Alves
相关产品推荐
相关产品推荐

