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
相关产品推荐
相关产品推荐

