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

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();
}

额外验证点

  1. Chaquo环境依赖:确认cv2、numpy等库已正确安装到Chaquo Python环境,可通过py.getModule("cv2")验证是否能正常加载。
  2. 视频编码兼容性:Android仅支持特定编码的MP4文件,建议使用H.264编码的视频,避免使用其他编码导致无法读取。
  3. 权限检查:虽然使用内部存储(getFilesDir)无需外部存储权限,但仍需确认应用未被限制文件读写权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 15:19:50