Как установить уровень масштабирования задней камеры на 0,5x с помощью Swift?

У меня работает функция масштабирования (1x и далее) для пользовательской камеры, реализованной с использованием AVFoundation. Это нормально до моделей iPhone X. Но я хотел иметь 0,5-кратный зум на устройствах iPhone 11 и iPhone 11 Pro.

Код, который я написал, не работает для увеличения 0,5x. Я перепробовал все возможные комбинации [.builtInTripleCamera, .builtInDualWideCamera, .builtInUltraWideCamera]. Устройство захвата с типом устройства .builtinUltraWideCamera не дает 0,5 для minAvailableVideoZoomFactor.

Во время тестирования на iPhone 11 я также удалил [.builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera, .builtInTrueDepthCamera] из deviceTypes.

Благодарим за любую помощь в решении этой проблемы. Ниже приведен код, который работает для увеличения на 1х.

/// Called from -handlePinchGesture
private func zoom(_ scale: CGFloat) {
    let captureDevice = cameraDevice(.back)
            
    do {
        try captureDevice?.lockForConfiguration()

        var minZoomFactor: CGFloat = captureDevice?.minAvailableVideoZoomFactor ?? 1.0
        let maxZoomFactor: CGFloat = captureDevice?.maxAvailableVideoZoomFactor ?? 1.0
        
        if #available(iOS 13.0, *) {
            if captureDevice?.deviceType == .builtInDualWideCamera || captureDevice?.deviceType == .builtInTripleCamera || captureDevice?.deviceType == .builtInUltraWideCamera {
                minZoomFactor = 0.5
            }
        }
        zoomScale = max(minZoomFactor, min(beginZoomScale * scale, maxZoomFactor))
        captureDevice?.videoZoomFactor = zoomScale

        captureDevice?.unlockForConfiguration()
    } catch {
        print("ERROR: locking configuration")
    }
}

@objc private func handlePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
    var allTouchesOnPreviewLayer = true
    let numTouch = recognizer.numberOfTouches
    
    for i in 0 ..< numTouch {
        let location = recognizer.location(ofTouch: i, in: view)
        let convertedTouch = previewLayer.convert(location, from: previewLayer.superlayer)
        if !previewLayer.contains(convertedTouch) {
            allTouchesOnPreviewLayer = false
            break
        }
    }
    if allTouchesOnPreviewLayer {
        zoom(recognizer.scale)
    }
}

func cameraDevice(_ position: AVCaptureDevice.Position) -> AVCaptureDevice? {
    var deviceTypes = [AVCaptureDevice.DeviceType]()
    deviceTypes.append(contentsOf: [.builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera, .builtInTrueDepthCamera])
    
    if #available(iOS 13.0, *) {
        deviceTypes.append(contentsOf: [.builtInTripleCamera, .builtInDualWideCamera, .builtInUltraWideCamera])
    }

    let availableCameraDevices = AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: .video, position: position).devices

    guard availableCameraDevices.isEmpty == false else {
        debugPrint("ERROR: No camera devices found!!!")
        return nil
    }

    for device in availableCameraDevices {
        if device.position == position {
            return device
        }
    }

    guard let defaultDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
        debugPrint("ERROR: Can't initialize default back camera!!!")
        return nil
    }
    return defaultDevice
}

4 ответа

Минимальное свойство zoomFactor для AVCaptureDevice не может быть меньше 1.0 согласно Apple Docs. Это немного сбивает с толку, потому что в зависимости от того, какую камеру вы выбрали, коэффициент масштабирования 1 будет другим полем зрения или оптическим углом обзора. В приложении камеры iPhone по умолчанию отображается метка «0,5», но это просто метка для сверхширокоугольного объектива по отношению к коэффициенту масштабирования стандартной камеры.

Вы уже получаете minZoomFactor от устройства (который, вероятно, будет равен 1), поэтому вам следует использовать считываемые вами минимальные и максимальные значения устройства, чтобы установить границы фактора, вводимого вами в "captureDevice.videoZoomFactor". Затем, когда вы выбрали сверхширокоугольный объектив, установка коэффициента масштабирования на 1 будет настолько широкой, насколько это возможно! (коэффициент 0,5 по отношению к полю зрения стандартного объектива).

Обновление для людей, которые хотят установить уровень оптического увеличения 0,5x

любезно предоставлено: https://github.com/NextLevel/NextLevel/issues/187

      public class func primaryVideoDevice(forPosition position: AVCaptureDevice.Position) -> AVCaptureDevice? {
    
    // -- Changes begun
    if #available(iOS 13.0, *) {

        let hasUltraWideCamera: Bool = true // Set this variable to true if your device is one of the following - iPhone 11, iPhone 11 Pro, & iPhone 11 Pro Max
        
        if hasUltraWideCamera {

            // Your iPhone has UltraWideCamera.
            let deviceTypes: [AVCaptureDevice.DeviceType] = [AVCaptureDevice.DeviceType.builtInUltraWideCamera]
            let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: AVMediaType.video, position: position)
            return discoverySession.devices.first
            
        }
        
    }
    // -- Changes end
    

    var deviceTypes: [AVCaptureDevice.DeviceType] = [AVCaptureDevice.DeviceType.builtInWideAngleCamera] // builtInWideAngleCamera // builtInUltraWideCamera
    if #available(iOS 11.0, *) {
        deviceTypes.append(.builtInDualCamera)
    } else {
        deviceTypes.append(.builtInDuoCamera)
    }
    
    // prioritize duo camera systems before wide angle
    let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: AVMediaType.video, position: position)
    for device in discoverySession.devices {
        if #available(iOS 11.0, *) {
            if (device.deviceType == AVCaptureDevice.DeviceType.builtInDualCamera) {
                return device
            }
        } else {
            if (device.deviceType == AVCaptureDevice.DeviceType.builtInDuoCamera) {
                return device
            }
        }
    }
    
    return discoverySession.devices.first
    
}

Мы не можем установить коэффициент масштабирования меньше 1.

Я решаю эту проблему, используя ".builtInDualWideCamera".

В этом случае мы используем «сверхширокоугольную камеру» с коэффициентом масштабирования 2,0 (будет значением по умолчанию), равным нормальному коэффициенту масштабирования «широкоугольной камеры». (минимальное значение будет 1.0)

Если ваш iPhone не поддерживает «.builtInDualWideCamera», мы будем использовать «.builtInWideAngleCamera» как обычно, а коэффициент масштабирования равен 1.0 (минимальное значение).

       func getCameraDevices() -> [AVCaptureDevice] {
        var deviceTypes = [AVCaptureDevice.DeviceType]()
   
        if #available(iOS 13.0, *) {
            deviceTypes.append(contentsOf: [.builtInDualWideCamera])
            self.isUltraWideCamera = true
            self.defaultZoomFactor = 2.0
        }
        
        if(deviceTypes.isEmpty){
            deviceTypes.append(contentsOf: [.builtInWideAngleCamera])
            self.isUltraWideCamera = false
            self.defaultZoomFactor = 1.0
        }

        return AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: .video, position: .unspecified).devices
    }

Проблема в том, что вы пытаетесь достать какой-нибудь девайс из он возвращает устройство по умолчанию, которое может не поддерживать необходимый вам сверхширокий диапазон.

Так было для меня с iPhone 12Pro Max, возвращая только одно устройство для позиции «Назад», тип отчета. Но это была просто камера, это была средняя камера, не широкоугольная, не телеобъектив. Не знаю, почему разработчики Apple сделали это так, похоже на устаревшую устаревшую архитектуру.

Решение не было очевидным: используйте чтобы получить реальное устройство, способное увеличивать масштаб от 1 (ваш логический 0,5).

Другие вопросы по тегам