使用AVAssetReader读取iPad视频帧时图像方向异常的技术问询
我之前也踩过AVAssetReader处理视频帧方向的坑,结合你描述的情况——iPhone(iOS10.3/11.x)和iPad模拟器都正常,唯独真机iPad(iOS11)读取的帧要么横屏倒置、要么竖屏旋转90度,且视频本身显示正常——核心问题几乎可以锁定为:iPad真机上AVAssetReader没有自动应用视频轨道的方向变换元数据,而iPhone和模拟器因为默认屏幕方向的适配,隐性处理了这个逻辑。
问题根源
视频文件的轨道元数据里,AVAssetTrack的preferredTransform属性存储了视频拍摄时的方向信息(比如竖屏拍摄的视频会带有90度旋转的变换)。在iPhone上,系统会自动根据设备默认方向适配这个变换;iPad模拟器因为和Mac的屏幕方向联动,也会自动处理;但iPad真机的默认屏幕方向(横屏)和视频拍摄方向(比如竖屏)的差异,导致AVAssetReader输出的原始帧没有被自动修正方向。
具体解决方案
你需要手动读取视频轨道的preferredTransform,并将其应用到读取到的视频帧上,步骤如下:
获取视频轨道的方向变换
先从目标AVAsset中拿到视频轨道,提取它的preferredTransform:import AVFoundation guard let asset = AVAsset(url: yourVideoURL) else { // 处理资源加载失败 return } guard let videoTrack = asset.tracks(withMediaType: .video).first else { // 没有找到视频轨道 return } let videoTransform = videoTrack.preferredTransform读取帧时应用变换
当你通过AVAssetReaderOutput拿到CMSampleBuffer后,将变换应用到图像上。这里以转成UIImage为例:func convertSampleBufferToImage(_ sampleBuffer: CMSampleBuffer, transform: CGAffineTransform) -> UIImage? { guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } // 转成CIImage并应用变换 let ciImage = CIImage(cvImageBuffer: imageBuffer).transformed(by: transform) // 创建CIContext生成CGImage let context = CIContext(options: [CIContextOption.useSoftwareRenderer: false]) guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else { return nil } return UIImage(cgImage: cgImage) }额外注意事项
- 不要在
AVAssetReaderOutput的outputSettings中强制设置固定的图像方向或尺寸,否则会丢失原始的方向元数据; - 如果你的视频存在镜像翻转的情况,
preferredTransform也会包含对应的翻转信息,上述代码会自动处理; - 可以在iPad真机上打印
videoTransform的值,对比iPhone上的输出,确认变换参数的差异(比如是否是.pi/2或.pi的旋转)。
- 不要在
验证方法
在读取帧后,分别对比应用变换前后的图像,确认方向是否恢复正常。如果还是有问题,可以检查videoTrack.naturalSize结合transform计算最终的图像尺寸,避免出现拉伸或裁剪的情况。
内容的提问来源于stack exchange,提问作者CodeBender

