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

Android使用xmpcore库写入视频元数据遇XML解析失败求助

问题:Android中使用XMPCore库写入视频元数据时XML解析失败

问题概述

在Android平台使用com.adobe.xmp:xmpcore:6.1.11库向视频文件写入元数据,测试多个视频文件均触发XML解析失败错误。

报错堆栈信息

FATAL EXCEPTION: main
    Process: com.yzx.testplayer, PID: 2301
    java.lang.RuntimeException: com.adobe.internal.xmp.XMPException: XML parsing failure
        at com.yzx.testplayer.MainActivity.lambda$onResume$0$com-yzx-testplayer-MainActivity(MainActivity.java:73)
        at com.yzx.testplayer.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source:2)
        at android.view.View.performClick(View.java:7448)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1131)
        at android.view.View.performClickInternal(View.java:7425)
        at android.view.View.access$3600(View.java:810)
        at android.view.View$PerformClick.run(View.java:28305)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: com.adobe.internal.xmp.XMPException: XML parsing failure
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseInputSource(XMPMetaParser.java:329)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseXmlFromBytebuffer(XMPMetaParser.java:248)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseXmlFromInputStream(XMPMetaParser.java:184)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseXml(XMPMetaParser.java:147)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parse(XMPMetaParser.java:87)
        at com.adobe.internal.xmp.XMPMetaFactory.parse(XMPMetaFactory.java:109)
        at com.yzx.testplayer.VideoEditingUtils.writeVideoMete(VideoEditingUtils.java:37)
        at com.yzx.testplayer.MainActivity.lambda$onResume$0$com-yzx-testplayer-MainActivity(MainActivity.java:70)
        at com.yzx.testplayer.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source:2)
        at android.view.View.performClick(View.java:7448)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1131)
        at android.view.View.performClickInternal(View.java:7425)
        at android.view.View.access$3600(View.java:810)
        at android.view.View$PerformClick.run(View.java:28305)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: org.xml.sax.SAXParseException: Unexpected token (position:TEXT RIFF¼¢… AVI LIST...@1:555 in com.adobe.internal.xmp.impl.FixASCIIControlsReader@1bd3d36) 
        at org.apache.harmony.xml.parsers.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:147)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseInputSource(XMPMetaParser.java:325)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseXmlFromBytebuffer(XMPMetaParser.java:248)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseXmlFromInputStream(XMPMetaParser.java:184)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parseXml(XMPMetaParser.java:147)
        at com.adobe.internal.xmp.impl.XMPMetaParser.parse(XMPMetaParser.java:87)
        at com.adobe.internal.xmp.XMPMetaFactory.parse(XMPMetaFactory.java:109)
        at com.yzx.testplayer.VideoEditingUtils.writeVideoMete(VideoEditingUtils.java:37)
        at com.yzx.testplayer.MainActivity.lambda$onResume$0$com-yzx-testplayer-MainActivity(MainActivity.java:70)
        at com.yzx.testplayer.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source:2)
        at android.view.View.performClick(View.java:7448)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1131)
        at android.view.View.performClickInternal(View.java:7425)
        at android.view.View.access$3600(View.java:810)
        at android.view.View$PerformClick.run(View.java:28305)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

相关代码

写入元数据工具类代码

public static void writeVideoMete(Context context,String metaData) throws XMPException, IOException {
        File videoFile = getVideoFile(context);
        if(!videoFile.exists()){
            throw new RuntimeException("video does not exist");
        }
        FileInputStream fis = new FileInputStream(videoFile);
        XMPMeta xmp = XMPMetaFactory.parse(fis, null);
        xmp.setProperty(XMPConst.NS_DC, "creator", "John Doe");
        xmp.setProperty(XMPConst.NS_XMP, "CreateDate", "2022-03-21");
        FileOutputStream fos = new FileOutputStream(videoFile);
        XMPMetaFactory.serialize(xmp, fos, null);
        fis.close();
        fos.close();
    }

    private static File getVideoFile(Context context) {
        return new File("/sdcard/test.avi");
    }

调用代码

@Override
    protected void onResume() {
        super.onResume();
        editVideoButton.setOnClickListener(i-> {
            try {
                VideoEditingUtils.writeVideoMete(this,"");   //70
                VideoEditingUtils.readVideoMete(this);
            } catch (XMPException | IOException e) {
                throw new RuntimeException(e);          //73
            }
        });
    }

解决方案

核心问题分析

报错信息中的Unexpected token (position:TEXT RIFF¼¢… AVI LIST...明确指向错误根源:代码直接将整个AVI视频的二进制内容传入XMPMetaFactory.parse()方法,而该方法仅能解析纯XMP XML数据,无法处理视频这类二进制媒体容器文件。

修正方案

需借助支持媒体容器解析的库来提取/写入视频中的XMP元数据,以下是具体实现步骤:

1. 添加依赖

在项目build.gradle中添加Metadata Extractor(用于处理媒体元数据)和XMPCore的依赖:

implementation 'com.drewnoakes:metadata-extractor:2.18.0'
implementation 'com.adobe.xmp:xmpcore:6.1.11'

2. 修正写入元数据代码

import com.adobe.xmp.XMPConst;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
import com.adobe.xmp.XMPMetaFactory;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.xmp.XmpDirectory;

import android.content.Context;

import java.io.File;
import java.io.IOException;

public class VideoEditingUtils {

    public static void writeVideoMeta(Context context) throws IOException, ImageProcessingException, XMPException {
        File videoFile = getVideoFile(context);
        if (!videoFile.exists()) {
            throw new RuntimeException("视频文件不存在");
        }

        XMPMeta xmpMeta;
        // 尝试从视频中提取已有XMP元数据,无则创建新实例
        try {
            Metadata metadata = ImageMetadataReader.readMetadata(videoFile);
            XmpDirectory xmpDirectory = metadata.getFirstDirectoryOfType(XmpDirectory.class);
            xmpMeta = xmpDirectory != null && xmpDirectory.getXmpMeta() != null 
                      ? xmpDirectory.getXmpMeta() 
                      : XMPMetaFactory.create();
        } catch (ImageProcessingException e) {
            // 若视频格式不支持提取,直接创建新XMP实例
            xmpMeta = XMPMetaFactory.create();
        }

        // 修改XMP元数据内容
        xmpMeta.setProperty(XMPConst.NS_DC, "creator", "John Doe");
        xmpMeta.setProperty(XMPConst.NS_XMP, "CreateDate", "2022-03-21");

        // 将修改后的XMP写回视频文件(注:Metadata Extractor仅支持部分视频格式的写入)
        XmpDirectory xmpDirectory = new XmpDirectory();
        xmpDirectory.setXmpMeta(xmpMeta);
        Metadata metadata = new Metadata();
        metadata.addDirectory(xmpDirectory);
        // 若此方法无法生效,建议使用FFmpeg相关库处理,示例命令:
        // ffmpeg -i input.avi -metadata creator="John Doe" -metadata creation_time="2022-03-21" -codec copy output.avi
    }

    private static File getVideoFile(Context context) {
        return new File("/sdcard/test.avi");
    }
}

3. 调用代码调整

@Override
protected void onResume() {
    super.onResume();
    editVideoButton.setOnClickListener(i-> {
        try {
            VideoEditingUtils.writeVideoMeta(this);
            // VideoEditingUtils.readVideoMete(this);  // 若读取方法也需修正,同步调整逻辑
        } catch (IOException | ImageProcessingException | XMPException e) {
            throw new RuntimeException(e);
        }
    });
}

补充说明

若Metadata Extractor无法满足特定视频格式的写入需求,可使用FFmpeg工具或其Android封装库,通过命令行方式修改视频元数据,兼容性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:44:57