Android文本转视频应用故障:Assets目录视频无法读取求助
问题排查与解决方案
核心问题定位
你的代码中存在几个关键问题导致无法正确读取assets中的视频:
1. Assets路径错误
在Python代码中,你使用了file_path = f"assets/{word}.mp4"作为AssetManager的打开路径,但Android的AssetManager访问文件不需要前缀assets/,直接传入文件名即可。带前缀的路径会导致AssetManager找不到对应文件。
2. 视频帧处理逻辑错误
- 你仅读取了视频的第一帧,而非完整视频的所有帧
- 使用
np.concatenate(frames)拼接帧的方式错误,三维帧数组直接拼接会破坏视频结构,导致最终生成的文件无法播放 - 未统一不同视频的帧尺寸,拼接时会出现尺寸不匹配问题
3. 临时文件与资源释放不严谨
未在使用后删除临时文件,且未确保VideoCapture资源完全释放,可能导致后续文件读取异常。
修正后的代码
Python代码(displayAnimation.py)
import cv2 import numpy as np import spacy import nltk from nltk.corpus import wordnet from itertools import chain import os import time nltk.download('wordnet') # Load the SpaCy language model nlp = spacy.load("en_core_web_sm") # Function to process text and concatenate videos def process_text_and_generate_video(input_text, asset_manager, context): word = '' # Tokenize using spacy doc = nlp(input_text) # Initialize a dictionary to map POS tags pos_dict = {"NOUN": "noun", "VERB": "verb", "ADJ": "adj", "ADV": "adv", "PRON": "pron", "ADP": "adp"} # 存储所有视频帧,统一尺寸避免拼接错误 video_frames_list = [] target_frame_size = None # 统一视频尺寸 # Iterate through spacy's tokenized results for token in doc: word = token.text pos_tag = token.pos_ video_found = False # 优先按原词查找视频 if pos_tag in pos_dict: try: # 修正:直接使用文件名,无需assets前缀 file_path = f"{word}.mp4" print(f"Trying to open file: {file_path}") with asset_manager.open(file_path) as fd: temp_file_path = os.path.join(context.getCacheDir(), f"{word}.mp4") with open(temp_file_path, 'wb') as out: out.write(fd.read()) # 读取完整视频的所有帧 cap = cv2.VideoCapture(temp_file_path) if cap.isOpened(): # 初始化目标帧尺寸 if not target_frame_size: target_frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) # 逐帧读取并统一尺寸 while True: ret, frame = cap.read() if not ret: break if frame.shape[:2] != target_frame_size: frame = cv2.resize(frame, target_frame_size) video_frames_list.append(frame) video_found = True cap.release() os.remove(temp_file_path) # 清理临时文件 except Exception as e: print(f"Exception for word {word}: {e}") # 原词未找到,尝试同义词 if not video_found: synonyms = wordnet.synsets(word) synonyms = set(chain.from_iterable([word.lemma_names() for word in synonyms])) for synonym in synonyms: try: file_path = f"{synonym}.mp4" print(f"Trying to open synonym file: {file_path}") with asset_manager.open(file_path) as fd: temp_file_path = os.path.join(context.getCacheDir(), f"{synonym}.mp4") with open(temp_file_path, 'wb') as out: out.write(fd.read()) cap = cv2.VideoCapture(temp_file_path) if cap.isOpened(): if not target_frame_size: target_frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) while True: ret, frame = cap.read() if not ret: break if frame.shape[:2] != target_frame_size: frame = cv2.resize(frame, target_frame_size) video_frames_list.append(frame) video_found = True cap.release() os.remove(temp_file_path) break except Exception as e: print(f"Exception for synonym {synonym}: {e}") # 同义词未找到,尝试按字母查找 if not video_found: for letter in word.lower(): try: file_path = f"{letter}.mp4" print(f"Trying to open letter file: {file_path}") with asset_manager.open(file_path) as fd: temp_file_path = os.path.join(context.getCacheDir(), f"{letter}.mp4") with open(temp_file_path, 'wb') as out: out.write(fd.read()) cap = cv2.VideoCapture(temp_file_path) if cap.isOpened(): if not target_frame_size: target_frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) while True: ret, frame = cap.read() if not ret: break if frame.shape[:2] != target_frame_size: frame = cv2.resize(frame, target_frame_size) video_frames_list.append(frame) video_found = True cap.release() os.remove(temp_file_path) except Exception as e: print(f"Exception for letter {letter}: {e}") # 无有效视频帧时返回None if not video_frames_list or not target_frame_size: print("No valid video frames found") return None # 生成最终视频 fourcc = cv2.VideoWriter_fourcc(*'MP4V') output_file_path = os.path.join(context.getFilesDir(), 'my_concatenation.mp4') # 删除已存在的输出文件 if os.path.exists(output_file_path): os.remove(output_file_path) out = cv2.VideoWriter(output_file_path, fourcc, 30.0, target_frame_size) for frame in video_frames_list: out.write(frame) out.release() # 确保文件写入完成 time.sleep(0.5) return output_file_path if os.path.exists(output_file_path) else None
Java代码调整(添加日志辅助排查)
在doInBackground方法中添加Python日志刷新逻辑,方便查看Python端的报错信息:
@Override protected String doInBackground(String... strings) { String input = strings[0]; Python py = Python.getInstance(); PyObject pyf = py.getModule("displayAnimation"); try { String[] files = context.getAssets().list(""); for(String name : files) { System.out.println("Asset file : " + name); } } catch (IOException e) { e.printStackTrace(); } // 刷新Python日志,确保能在Android Studio中看到输出 py.getModule("sys").callAttr("stdout", "flush"); py.getModule("sys").callAttr("stderr", "flush"); PyObject obj = pyf.callAttr("process_text_and_generate_video", input, context.getAssets(), context); return obj == null ? null : obj.toString(); }
额外验证点
- Chaquo环境依赖:确认cv2、numpy等库已正确安装到Chaquo Python环境,可通过
py.getModule("cv2")验证是否能正常加载。 - 视频编码兼容性:Android仅支持特定编码的MP4文件,建议使用H.264编码的视频,避免使用其他编码导致无法读取。
- 权限检查:虽然使用内部存储(
getFilesDir)无需外部存储权限,但仍需确认应用未被限制文件读写权限。
内容的提问来源于stack exchange,提问作者ash
相关产品推荐
相关产品推荐

