AVCaptureStillImageOutput vs AVCapturePhotoOutput в Swift 3

Я пытаюсь просто поставить Camera View в свой контроллер просмотра.

Я импортировал AVFoundation вверху, а также классы UIImagePickerControllerDelegate и UINavigationControllerDelegate.

Однако, когда я пытаюсь использовать AVCaptureStillImageOutput, Xcode сообщает мне, что он устарел в iOS10, и я должен использовать AVCapturePhotoOutput. Это вполне нормально, однако, как только я хочу позвонить stillImageOutput.outputSettings, .outputSettings сам по себе недоступен. Таким образом, я должен использовать AVAVCaptureStillImageOutput для его работы, но у меня есть несколько предупреждений, потому что эта функция была устарела в iOS10.

Я искал и искал, но не мог найти решение вокруг него. Я бы очень признателен вам за вашу помощь. Я учусь, поэтому любое объяснение будет замечательным! Код ниже.

import UIKit
import AVFoundation

class CameraView: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    var captureSession : AVCaptureSession?
    var stillImageOutput : AVCaptureStillImageOutput?
    var previewLayer : AVCaptureVideoPreviewLayer?

    @IBOutlet var cameraView: UIView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        captureSession = AVCaptureSession()
        captureSession?.sessionPreset = AVCaptureSessionPreset1920x1080

        var backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        var error : NSError?

        do {
            var input = try! AVCaptureDeviceInput (device: backCamera)
            if (error == nil && captureSession?.canAddInput(input) != nil) {

                captureSession?.addInput(input)

                stillImageOutput = AVCaptureStillImageOutput()
                stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

                if (captureSession?.canAddOutput(stillImageOutput) != nil) {
                    captureSession?.addOutput(stillImageOutput)

                    previewLayer = AVCaptureVideoPreviewLayer (session: captureSession)
                    previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect
                    previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
                    cameraView.layer.addSublayer(previewLayer!)
                    captureSession?.startRunning()
                }
            }
        } catch {

        }
    }
}

Ответ 1

AVCaptureStillImageOutput устаревший означает, что вы можете продолжать использовать его в iOS 10, но:

  • Apple не делает promises относительно того, как долго IOS 10 останется доступным.
  • по мере добавления новых функций аппаратного и программного обеспечения в iOS 10 и далее, вы не получите доступ ко всем из них. Например, вы можете настроить AVCaptureStillImageOutput для широкого цвета, но гораздо проще сделать широкий цвет с помощью AVCapturePhotoOutput. А для съемки RAW или Live Photos AVCapturePhotoOutput - единственная игра в городе.

Если вы счастливы, несмотря на устаревание, ваша проблема заключается не в том, что outputSettings удален - он все еще существует.

Что-то, о чем следует знать для бета-версии 6 и выше (хотя это и не проблема): API, которые используют NSDictionary без явных ключей и типов значений, входят в Swift 3 как [AnyHashable: Any], а Foundation или Типы CoreFoundation, которые вы можете использовать в словаре, более неявно соединяются с типами Swift. (Некоторые из других вопросов о конверсиях из бета-версии могут указывать на вас в правильном направлении.)

Однако при настройке outputSettings я не получаю ошибок компиляции. В вашем полном коде или путем сокращения его до необходимых частей для этой линии:

var stillImageOutput : AVCaptureStillImageOutput?
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

... только предупреждения, о которых я вижу, относятся к устареванию.

Ответ 2

Есть моя полная реализация

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {

var captureSesssion : AVCaptureSession!
var cameraOutput : AVCapturePhotoOutput!
var previewLayer : AVCaptureVideoPreviewLayer!

@IBOutlet weak var capturedImage: UIImageView!
@IBOutlet weak var previewView: UIView!

override func viewDidLoad() {
super.viewDidLoad()
captureSesssion = AVCaptureSession()
captureSesssion.sessionPreset = AVCaptureSessionPresetPhoto
cameraOutput = AVCapturePhotoOutput()

let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

if let input = try? AVCaptureDeviceInput(device: device) {
    if (captureSesssion.canAddInput(input)) {
        captureSesssion.addInput(input)
        if (captureSesssion.canAddOutput(cameraOutput)) {
            captureSesssion.addOutput(cameraOutput)
            previewLayer = AVCaptureVideoPreviewLayer(session: captureSesssion)
            previewLayer.frame = previewView.bounds
            previewView.layer.addSublayer(previewLayer)
            captureSesssion.startRunning()
        }
    } else {
        print("issue here : captureSesssion.canAddInput")
    }
} else {
    print("some problem here")
}
}

// Take picture button
@IBAction func didPressTakePhoto(_ sender: UIButton) {
let settings = AVCapturePhotoSettings()
let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
let previewFormat = [
     kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
     kCVPixelBufferWidthKey as String: 160,
     kCVPixelBufferHeightKey as String: 160
]
settings.previewPhotoFormat = previewFormat
cameraOutput.capturePhoto(with: settings, delegate: self)
}

// callBack from take picture
func capture(_ captureOutput: AVCapturePhotoOutput,  didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,  previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings:  AVCaptureResolvedPhotoSettings, bracketSettings:   AVCaptureBracketedStillImageSettings?, error: Error?) {

if let error = error {
    print("error occure : \(error.localizedDescription)")
}

if  let sampleBuffer = photoSampleBuffer,
    let previewBuffer = previewPhotoSampleBuffer,
    let dataImage =  AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer:  sampleBuffer, previewPhotoSampleBuffer: previewBuffer) {
    print(UIImage(data: dataImage)?.size as Any)

    let dataProvider = CGDataProvider(data: dataImage as CFData)
    let cgImageRef: CGImage! = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
    let image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.right)

    self.capturedImage.image = image
} else {
    print("some error here")
}
}

// This method you can use somewhere you need to know camera permission   state
func askPermission() {
print("here")
let cameraPermissionStatus =  AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)

switch cameraPermissionStatus {
case .authorized:
    print("Already Authorized")
case .denied:
    print("denied")

    let alert = UIAlertController(title: "Sorry :(" , message: "But  could you please grant permission for camera within device settings",  preferredStyle: .alert)
    let action = UIAlertAction(title: "Ok", style: .cancel,  handler: nil)
    alert.addAction(action)
    present(alert, animated: true, completion: nil)

case .restricted:
    print("restricted")
default:
    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: {
        [weak self]
        (granted :Bool) -> Void in

        if granted == true {
            // User granted
            print("User granted")
 DispatchQueue.main.async(){
        //Do smth that you need in main thread   
        } 
        }
        else {
            // User Rejected
            print("User Rejected")

DispatchQueue.main.async(){
        let alert = UIAlertController(title: "WHY?" , message:  "Camera it is the main feature of our application", preferredStyle: .alert)
            let action = UIAlertAction(title: "Ok", style: .cancel, handler: nil)
            alert.addAction(action)
            self?.present(alert, animated: true, completion: nil)  
        } 
        }
    });
}
}
}