Swift中如何使AVCaptureFileOutput输出无旋转EXIF的正确方向视频?
我之前帮不少开发者解决过这个问题——AVFoundation默认录制的横屏视频会带EXIF旋转标记,导致后期用AVComposition导出时方向错乱。要让AVCaptureFileOutput直接录制出像素方向正确、不带旋转EXIF参数的横屏视频,核心是让采集会话在录制阶段就把视频帧旋转到目标方向,而不是依赖后期的EXIF标记。下面是完整的Swift实现方案和关键细节:
核心思路
默认情况下,摄像头采集的原始帧方向和UI横屏方向不匹配,系统会自动添加EXIF旋转参数来让播放器正确显示。我们需要通过配置AVCaptureConnection的视频方向,告诉录制输出直接将帧旋转为目标方向,这样输出的视频本身的像素排列就是正确的,无需EXIF辅助。
完整Swift实现代码
1. 定义录制器类
import AVFoundation import UIKit class VideoRecorder: NSObject { // 采集会话 private let captureSession = AVCaptureSession() // 视频文件输出 private let movieFileOutput = AVCaptureMovieFileOutput() // 当前摄像头位置 private var currentCameraPosition: AVCaptureDevice.Position = .back // 预览层(如果需要实时预览) private(set) var previewLayer: AVCaptureVideoPreviewLayer? // 初始化时配置会话 override init() { super.init() setupCaptureSession() setupPreviewLayer() } }
2. 配置采集会话(关键部分)
extension VideoRecorder { private func setupCaptureSession() { captureSession.beginConfiguration() // 选择支持横屏分辨率的会话预设(比如1080P) captureSession.sessionPreset = .hd1920x1080 // 添加摄像头输入 guard let cameraDevice = getCamera(for: currentCameraPosition), let videoInput = try? AVCaptureDeviceInput(device: cameraDevice), captureSession.canAddInput(videoInput) else { print("摄像头输入配置失败") return } captureSession.addInput(videoInput) // 添加视频文件输出 guard captureSession.canAddOutput(movieFileOutput) else { print("无法添加视频输出") return } captureSession.addOutput(movieFileOutput) // 配置视频连接的方向(核心!) if let videoConnection = movieFileOutput.connection(with: .video) { // 设置目标横屏方向,支持landscapeRight或landscapeLeft let targetOrientation: AVCaptureVideoOrientation = .landscapeRight // 检查连接是否支持该方向 if videoConnection.isVideoOrientationSupported { videoConnection.videoOrientation = targetOrientation } // 按需开启视频稳定(不影响方向设置) if videoConnection.isVideoStabilizationSupported { videoConnection.preferredVideoStabilizationMode = .auto } } captureSession.commitConfiguration() } // 获取指定位置的摄像头 private func getCamera(for position: AVCaptureDevice.Position) -> AVCaptureDevice? { let discoverySession = AVCaptureDevice.DiscoverySession( deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .unspecified ) return discoverySession.devices.first { $0.position == position } } }
3. 预览层配置(可选,用于实时预览)
extension VideoRecorder { private func setupPreviewLayer() { previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer?.videoGravity = .resizeAspectFill // 预览层方向会自动跟随视频连接的设置,无需额外调整 } // 启动/停止采集会话 func startSession() { if !captureSession.isRunning { captureSession.startRunning() } } func stopSession() { if captureSession.isRunning { captureSession.stopRunning() } } }
4. 录制控制与代理实现
extension VideoRecorder: AVCaptureFileOutputRecordingDelegate { func startRecording(to outputURL: URL) { guard !movieFileOutput.isRecording else { return } movieFileOutput.startRecording(to: outputURL, recordingDelegate: self) } func stopRecording() { guard movieFileOutput.isRecording else { return } movieFileOutput.stopRecording() } // 录制完成回调 func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) { if let error = error { print("录制出错: \(error.localizedDescription)") return } print("视频录制完成,路径: \(outputFileURL.path)") // 可通过exiftool工具验证视频是否无Rotation参数 } }
5. 动态调整方向(如果需要支持录制中切换横屏方向)
如果需要在录制过程中响应设备横屏方向变化,可以添加以下方法:
extension VideoRecorder { func updateOrientation(for interfaceOrientation: UIInterfaceOrientation) { guard let videoConnection = movieFileOutput.connection(with: .video), videoConnection.isVideoOrientationSupported else { return } let captureOrientation: AVCaptureVideoOrientation switch interfaceOrientation { case .landscapeLeft: captureOrientation = .landscapeLeft case .landscapeRight: captureOrientation = .landscapeRight default: // 非横屏时默认 fallback 到目标横屏方向 captureOrientation = .landscapeRight } videoConnection.videoOrientation = captureOrientation } }
关键细节说明
为什么设置
videoOrientation有效?
这个属性会告诉AVCaptureConnection在将帧传递给输出前,直接旋转到指定方向。这样录制的视频像素本身就是正确的横屏排列,系统不会添加EXIF旋转标记。前置摄像头的适配
前置摄像头的传感器原始方向和后置相反,但AVCaptureVideoOrientation是基于UI方向的,无需额外处理——系统会自动适配前置摄像头的镜像和方向差异。会话预设的选择
确保选择的sessionPreset支持横屏分辨率,比如.hd1920x1080是16:9的横屏预设,不要选.photo这类方形预设,否则方向设置可能无效。验证输出视频
录制完成后,可以用exiftool命令行工具查看视频的EXIF信息:exiftool your_video.mp4如果没有
Rotation字段,说明视频方向正确且无EXIF旋转标记。
内容的提问来源于stack exchange,提问作者DevStarlight

