如何在Xuggler中写入视频旋转元数据?缩放后元数据丢失问题
解决Xuggler视频缩放后丢失旋转元数据的问题
我明白你遇到的困扰:用Xuggler处理竖屏手机视频时,虽然尺寸缩放正确,但视频的旋转元数据(Rotation)丢失了,导致播放器无法正确识别竖屏显示模式。结合你提供的MediaInfo信息和代码,我来帮你解决这个问题。
问题根源
你的代码目前只处理了视频尺寸的缩放,但没有将原视频的旋转元数据传递到输出视频中。Xuggler的IMediaWriter不会自动复制容器或流的元数据,需要我们手动提取并设置到输出文件里。
解决方案步骤
我们需要分三步操作:
- 从原视频中提取旋转角度元数据
- 将旋转元数据写入输出视频的容器元数据中
- 确保视频缩放尺寸与旋转角度匹配(保证显示比例正确)
修改后的完整代码
1. 主类 MediaConverter 修改
package test.video; import com.xuggle.mediatool.IMediaReader; import com.xuggle.mediatool.IMediaWriter; import com.xuggle.mediatool.ToolFactory; import com.xuggle.xuggler.IContainer; import com.xuggle.xuggler.IMetaData; import com.xuggle.xuggler.IStream; import java.io.IOException; public class MediaConverter { private static final String INPUT_FILE = "/home/user/input_video.mp4"; private static final String OUTPUT_FILE = "/home/user/output_video.mp4"; public static void main(String[] args) throws IOException { // 原视频旋转90°后实际是1080x1920(9:16比例),所以目标尺寸设为360x640保持竖屏比例 convertVideo(INPUT_FILE, OUTPUT_FILE, 360, 640); } private static void convertVideo(String inputFile, String outputFile, int width, int height) { // 第一步:提取原视频的旋转元数据 int rotation = getVideoRotation(inputFile); // 初始化Reader IMediaReader reader = ToolFactory.makeReader(inputFile); Resizer resizer = new Resizer(width, height); reader.addListener(resizer); // 初始化Writer并设置旋转元数据 IMediaWriter writer = ToolFactory.makeWriter(outputFile, reader); setRotationMetadata(writer, rotation); resizer.addListener(writer); // 处理视频数据包 while (reader.readPacket() == null) { // 空循环,让reader自动处理所有数据包 } // 释放资源 writer.flush(); writer.close(); reader.close(); } // 从原视频中提取旋转角度(兼容容器/流两种元数据存储方式) private static int getVideoRotation(String inputFile) { IContainer container = IContainer.make(); int rotation = 0; try { if (container.open(inputFile, IContainer.Type.READ, null) < 0) { throw new IllegalArgumentException("无法打开输入文件: " + inputFile); } // 先尝试从容器元数据获取旋转信息 IMetaData containerMetaData = container.getMetaData(); if (containerMetaData != null) { String rotationStr = containerMetaData.getValue("rotation"); if (rotationStr != null) { rotation = Integer.parseInt(rotationStr); } } // 如果容器元数据没有,尝试从视频流元数据获取 if (rotation == 0) { IStream videoStream = container.getStream(0); if (videoStream != null) { IMetaData streamMetaData = videoStream.getMetaData(); if (streamMetaData != null) { String rotationStr = streamMetaData.getValue("rotation"); if (rotationStr != null) { rotation = Integer.parseInt(rotationStr); } } } } } finally { if (container.isOpen()) { container.close(); } } return rotation; } // 将旋转元数据设置到输出视频容器 private static void setRotationMetadata(IMediaWriter writer, int rotation) { IMetaData outputMetaData = writer.getContainer().getMetaData(); if (outputMetaData == null) { outputMetaData = IMetaData.make(); writer.getContainer().setMetaData(outputMetaData); } outputMetaData.setValue("rotation", String.valueOf(rotation)); } }
2. Resizer 类修改
移除onAddStream中无效的元数据操作,只保留视频重采样核心逻辑:
package test.video; import com.xuggle.mediatool.MediaToolAdapter; import com.xuggle.mediatool.event.IVideoPictureEvent; import com.xuggle.mediatool.event.VideoPictureEvent; import com.xuggle.xuggler.IVideoPicture; import com.xuggle.xuggler.IVideoResampler; public class Resizer extends MediaToolAdapter { private Integer width; private Integer height; private IVideoResampler videoResampler = null; public Resizer(Integer aWidth, Integer aHeight) { this.width = aWidth; this.height = aHeight; } @Override public void onVideoPicture(IVideoPictureEvent event) { IVideoPicture pic = event.getPicture(); if (videoResampler == null) { // 初始化视频重采样器,匹配原视频像素格式 videoResampler = IVideoResampler.make(width, height, pic.getPixelType(), pic.getWidth(), pic.getHeight(), pic.getPixelType()); } // 创建输出视频帧并完成重采样 IVideoPicture out = IVideoPicture.make(pic.getPixelType(), width, height); videoResampler.resample(out, pic); // 将处理后的帧传递给Writer super.onVideoPicture(new VideoPictureEvent(event.getSource(), out, event.getStreamIndex())); // 释放资源 out.delete(); } }
关键说明
- 旋转元数据的兼容性:有些视频的旋转信息存在容器元数据中,有些存在视频流元数据中,所以我们两种方式都尝试获取,确保不会遗漏。
- 尺寸比例匹配:原视频是1920x1080旋转90°,实际显示比例为9:16(竖屏),所以缩放时目标尺寸设为360x640(保持相同比例),避免旋转后显示变形。
- 资源管理:添加了
reader.close()和writer.close(),避免Xuggler的资源泄漏问题。
验证方法
修改完成后,用MediaInfo查看输出视频的元数据,应该能看到Rotation : 90°字段,然后在手机和电脑播放器中打开视频,会正常以竖屏模式显示。
内容的提问来源于stack exchange,提问作者ulanBator
相关产品推荐
相关产品推荐

