iPhone端任意视频转码为720P 30fps H264标准参数的实现问题
问题解决完整方案
你现有代码的两个核心问题可通过系统内置API直接修复,无需引入第三方库,完整实现如下:
1. 旋转问题修复逻辑
你之前直接将原视频的preferredTransform赋值给写入器输入,导致导出视频仍携带旋转元数据,播放时触发旋转才会出现内容出框、黑屏。正确处理方式是:
- 先计算原视频应用旋转后的实际自然展示尺寸
- 写入器输入直接使用
.identity变换,导出视频无旋转元数据 - 视频缩放裁剪逻辑直接作用于已旋转的正方向帧,避免适配错误
2. 帧率统一逻辑
系统内置AVMutableVideoComposition原生支持自动帧率转换,只需指定目标帧率的frameDuration,系统会自动处理高帧率抽帧、低帧率补帧,全程不改变视频时长、播放速度无异常。
完整修正代码
import AVFoundation func transcodeVideo(videoUrl: URL, completion: @escaping (Result<URL, Error>) -> Void) { let asset = AVURLAsset(url: videoUrl) guard let videoTrack = asset.tracks(withMediaType: .video).first else { completion(.failure(VideoHelperError.unknown)) return } // 计算原视频应用旋转后的自然尺寸 let naturalSize = videoTrack.naturalSize.applying(videoTrack.preferredTransform) let correctedSize = CGSize(width: abs(naturalSize.width), height: abs(naturalSize.height)) // 配置视频合成规则,自动处理帧率 let videoComposition = AVMutableVideoComposition() videoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30) // 固定30fps videoComposition.renderSize = CGSize(width: 720, height: 1280) // 目标720P竖屏尺寸 // 配置合成层,应用原视频变换保证内容方向正确 let instruction = AVMutableVideoCompositionInstruction() instruction.timeRange = CMTimeRangeMake(start: .zero, duration: asset.duration) let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack) // 计算变换矩阵:先应用原视频旋转,再适配目标尺寸aspect fill let scaleFactor = max(videoComposition.renderSize.width / correctedSize.width, videoComposition.renderSize.height / correctedSize.height) let scaleTransform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor) let finalTransform = videoTrack.preferredTransform.concatenating(scaleTransform) layerInstruction.setTransform(finalTransform, at: .zero) instruction.layerInstructions = [layerInstruction] videoComposition.instructions = [instruction] // 导出配置 guard let outputUrl = try? FileManager.default.temporaryDirectory.appendingPathComponent("\(UUID().uuidString).mp4") else { completion(.failure(VideoHelperError.fileSystemFailure)) return } guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset1280x720) else { completion(.failure(VideoHelperError.unknown)) return } exportSession.outputURL = outputUrl exportSession.outputFileType = .mp4 exportSession.videoComposition = videoComposition exportSession.shouldOptimizeForNetworkUse = true exportSession.videoSettings = [ AVVideoCodecKey: AVVideoCodecType.h264, AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill ] as [String : Any] exportSession.exportAsynchronously { DispatchQueue.main.async { switch exportSession.status { case .completed: completion(.success(outputUrl)) case .failed, .cancelled: completion(.failure(exportSession.error ?? VideoHelperError.unknown)) default: break } } } } // 自定义错误类型,和你原有代码对齐 enum VideoHelperError: Error { case unknown case fileSystemFailure }
对应问题解答
- 帧率转换的系统内置方案:直接使用
AVMutableVideoComposition的frameDuration属性指定目标帧率,系统自动完成抽帧/补帧逻辑,无需手动处理样本时间戳,适配所有原帧率场景,保证视频时长、播放速度完全正常。 - 旋转信息处理最优方案:通过
AVMutableVideoCompositionLayerInstruction的setTransform方法,在合成阶段就将原视频的旋转元数据直接应用到帧内容上,导出的视频使用默认无变换属性,完全去除旋转元数据,aspect fill适配直接作用于正方向的内容,不会出现内容出框、黑屏问题。
内容的提问来源于stack exchange,提问作者Paulo C
相关产品推荐
相关产品推荐

