设置AVCaptureDevice的activeFormat后画面出现阴影及对焦异常
设置AVCaptureDevice.activeFormat后出现画面阴影渐变与对焦跳跃的解决方案
问题分析
你遇到的右到左阴影渐变是**滚动快门(Rolling Shutter)**引发的果冻效应——部分高分辨率高帧率格式采用逐行扫描CMOS传感器,画面快速移动时会出现拉伸、偏移;持续对焦跳跃则是格式选择不严谨、帧率设置逻辑错误,或对焦模式未正确配置导致的。
你的代码存在几个核心问题:
- 格式筛选逻辑粗糙:仅以分辨率和帧率为判断标准,未考虑格式的传感器类型(滚动/全局快门)、兼容的对焦模式,也未排除慢动作专用格式(这类格式帧率极高但对焦控制受限)。
- 帧率设置逻辑无效:
if duration.seconds > 60的判断完全错误——minFrameDuration.seconds是1/帧率,比如60帧仅约0.016秒,永远不会触发分支。强制将最小/最大帧率设为同一值,会打破相机自动对焦、曝光的动态调整机制,导致画面频繁跳跃。 - 配置时机不合理:添加输入到session后再修改设备格式,部分设备会出现配置冲突。
修复方案
1. 优化格式筛选逻辑
优先选择兼容性更好的YUV420格式,排除慢动作专用格式,同时综合分辨率和帧率加权评分:
func findBestFormat(for device: AVCaptureDevice) -> (format: AVCaptureDevice.Format, frameRateRange: AVFrameRateRange)? { var bestFormat: AVCaptureDevice.Format? var bestFrameRateRange: AVFrameRateRange? for format in device.formats { let formatDesc = format.formatDescription let dimensions = formatDesc.dimensions // 优先选择YUV420格式,兼容性更强 guard CMFormatDescriptionGetMediaSubType(formatDesc) == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange else { continue } // 排除慢动作格式(通常带像素合并,对焦功能受限) guard format.isVideoBinned == false else { continue } for range in format.videoSupportedFrameRateRanges { // 分辨率×帧率加权评分,优先选择高分高帧格式 let currentScore = Int(dimensions.width * dimensions.height) + Int(range.maxFrameRate * 100) let bestScore = bestFormat != nil ? Int(bestFormat!.formatDescription.dimensions.width * bestFormat!.formatDescription.dimensions.height) + Int(bestFrameRateRange!.maxFrameRate * 100) : 0 if currentScore > bestScore { bestFormat = format bestFrameRateRange = range } } } guard let format = bestFormat, let range = bestFrameRateRange else { return nil } return (format, range) }
2. 修正设备配置逻辑
调整配置顺序(先配置设备再添加到session),修正帧率设置,同时指定稳定的对焦模式:
func setUp() { let audioDevice = AVCaptureDevice.default(for: .audio) let cameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) guard let cameraDevice = cameraDevice, let audioDevice = audioDevice, let bestConfig = findBestFormat(for: cameraDevice) else { return } do { // 先配置设备,再添加到session,避免冲突 try cameraDevice.lockForConfiguration() cameraDevice.activeFormat = bestConfig.format // 保留帧率范围,给相机自动调整的空间 cameraDevice.activeVideoMinFrameDuration = bestConfig.frameRateRange.minFrameDuration cameraDevice.activeVideoMaxFrameDuration = bestConfig.frameRateRange.maxFrameDuration // 设置稳定的自动对焦模式 if cameraDevice.isFocusModeSupported(.continuousAutoFocus) { cameraDevice.focusMode = .continuousAutoFocus } cameraDevice.unlockForConfiguration() self.session.beginConfiguration() let videoInput = try AVCaptureDeviceInput(device: cameraDevice) let audioInput = try AVCaptureDeviceInput(device: audioDevice) if self.session.canAddInput(videoInput) && self.session.canAddInput(audioInput) { self.session.addInput(videoInput) self.session.addInput(audioInput) } if self.session.canAddOutput(self.output) { self.session.addOutput(output) } self.session.commitConfiguration() } catch { print(error.localizedDescription) } }
3. 额外优化建议
- 若设备支持全局快门(
cameraDevice.isGlobalShutterSupported),优先选择对应格式,可彻底解决果冻效应。 - 若对焦仍不稳定,可尝试关闭自动对焦,手动设置对焦位置,或调整
focusPointOfInterest。 - 检查输出的视频尺寸是否与选中格式匹配,避免拉伸导致画面异常。
内容的提问来源于stack exchange,提问作者nickcoding2
相关产品推荐
相关产品推荐

