You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置AVCaptureDevice的activeFormat后画面出现阴影及对焦异常

设置AVCaptureDevice.activeFormat后出现画面阴影渐变与对焦跳跃的解决方案

问题分析

你遇到的右到左阴影渐变是**滚动快门(Rolling Shutter)**引发的果冻效应——部分高分辨率高帧率格式采用逐行扫描CMOS传感器,画面快速移动时会出现拉伸、偏移;持续对焦跳跃则是格式选择不严谨、帧率设置逻辑错误,或对焦模式未正确配置导致的。

你的代码存在几个核心问题:

  1. 格式筛选逻辑粗糙:仅以分辨率和帧率为判断标准,未考虑格式的传感器类型(滚动/全局快门)、兼容的对焦模式,也未排除慢动作专用格式(这类格式帧率极高但对焦控制受限)。
  2. 帧率设置逻辑无效:if duration.seconds > 60的判断完全错误——minFrameDuration.seconds是1/帧率,比如60帧仅约0.016秒,永远不会触发分支。强制将最小/最大帧率设为同一值,会打破相机自动对焦、曝光的动态调整机制,导致画面频繁跳跃。
  3. 配置时机不合理:添加输入到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 21:55:35