1.什么是AVFoundation
AV Foundation是在iOS,macOS,watchOS和tvOS上使用基于時間的視聽媒體的全功能框架。例如磅网,你可以用它來檢查,創(chuàng)建筷屡,編輯或重新編碼媒體文件涧偷。您也可以從設(shè)備得到輸入流和在實時捕捉回放過程中操控視頻。
2.為什么使用AVFoundation
通常情況下速蕊,如果需要播放視頻嫂丙,可以使用AVKit框架,如果需要拍照或者錄制視頻則可以使用UIKit中的UIImagePickerController實現(xiàn),但是如果你想要更多自定義的功能時规哲,例如跟啤,以編程方式更改硬件參數(shù),或者操縱實時預(yù)覽圖,就需要使用到AVFoundation來實現(xiàn),本文介紹如何通過AVFoundation來實現(xiàn)自定義相機唉锌。
3.AVFoundation 相關(guān)類
AVFoundation 框架基于以下幾個類實現(xiàn)圖像捕捉 隅肥,通過這些類可以訪問來自相機設(shè)備的原始數(shù)據(jù)并控制它的組件:
AVCaptureDevice 是關(guān)于相機硬件的接口。它被用于控制硬件特性袄简,諸如鏡頭的位置腥放、曝光、閃光燈等绿语。
AVCaptureDeviceInput 提供來自設(shè)備的數(shù)據(jù)秃症。
AVCaptureOutput 是一個抽象類,描述 capture session 的結(jié)果吕粹。以下是三種關(guān)于靜態(tài)圖片捕捉的具體子類:
AVCaptureStillImageOutput 用于捕捉靜態(tài)圖片
AVCaptureMetadataOutput 啟用檢測人臉和二維碼
AVCaptureVideoOutput 為實時預(yù)覽圖提供原始幀
AVCaptureSession 管理輸入與輸出之間的數(shù)據(jù)流种柑,以及在出現(xiàn)問題時生成運行時錯誤。
AVCaptureVideoPreviewLayer 是 CALayer 的子類匹耕,可被用于自動顯示相機產(chǎn)生的實時圖像聚请。它還有幾個工具性質(zhì)的方法,可將 layer 上的坐標轉(zhuǎn)化到設(shè)備上稳其。它看起來像輸出驶赏,但其實不是。另外既鞠,它擁有 session (outputs 被 session 所擁有)煤傍。
4.代碼實現(xiàn)
??1.初始化AVCaptureSession對象
let session = AVCaptureSession()
??2.現(xiàn)在我們需要一個相機設(shè)備輸入,我們可以選擇后置攝像頭或前置攝像頭。那么我們必須先遍歷所有能提供視頻數(shù)據(jù)的設(shè)備(麥克風也屬于AVCaptureDevice),并檢查position屬性:
let session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
let availableCameraDevices = session.devices.compactMap { $0 }
guard !availableCameraDevices.isEmpty else {throw CameraError.noCamerasAvailable}
for camera in availableCameraDevices{
if camera.position == .back {
backCamera = camera
}else if camera.position == .front {
frontCamera = camera
}
}
3.獲得相關(guān)的AVCaptureDeviceInput對象,將它設(shè)置為session的輸入:
guard let captureSession = self.captureSession else { throw CameraError.captureSessionIsMissing }
if let backCamera = self.backCamera {
self.backCameraInput = try AVCaptureDeviceInput(device:backCamera)
if(captureSession.canAddInput(self.backCameraInput!)){
captureSession.addInput(self.backCameraInput!)
}
self.currentCameraPosition = .back
}else if let frontCamera = self.frontCamera{
self.frontCameraInput = try AVCaptureDeviceInput(device:frontCamera)
if(captureSession.canAddInput(self.frontCameraInput!)){
captureSession.addInput(self.frontCameraInput!)
}
self.currentCameraPosition = .front
}else{throw CameraError.noCamerasAvailable}
4.通過AVCapturePhotoOutput配置照片輸出:
guard let captureSession = self.captureSession else { throw CameraError.captureSessionIsMissing }
self.photoOutput = AVCapturePhotoOutput()
self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil)
if captureSession.canAddOutput(self.photoOutput!) { captureSession.addOutput(self.photoOutput!) }
5.開始運行:
guard let captureSession = self.captureSession else { throw CameraError.captureSessionIsMissing }
captureSession.startRunning()
6.顯示來自相機的輸出:
guard let captureSession = self.captureSession, captureSession.isRunning else {
throw CameraError.captureSessionIsMissing
}
self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer?.connection?.videoOrientation = .landscapeRight
view.layer.insertSublayer(self.previewLayer!, at: 0)
self.previewLayer?.frame = view.frame
7.實現(xiàn)拍照:
let settings = AVCapturePhotoSettings()
settings.flashMode = .off
self.photoOutput?.capturePhoto(with: settings, delegate: self)
8.通過AVCapturePhotoCaptureDelegate獲取圖片:
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
let imageData = photo.fileDataRepresentation()
}
9.切換攝像頭:
guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraError.captureSessionIsMissing }
captureSession.beginConfiguration()
func switchToFrontCamera() throws {
guard let backCameraInput = self.backCameraInput, captureSession.inputs.contains(backCameraInput),
let frontCamera = self.frontCamera else { throw CameraError.invalidOperation }
self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
captureSession.removeInput(backCameraInput)
if captureSession.canAddInput(self.frontCameraInput!) {
captureSession.addInput(self.frontCameraInput!)
self.currentCameraPosition = .front
}
else {
throw CameraError.invalidOperation
}
}
func switchToRearCamera() throws {
guard let frontCameraInput = self.frontCameraInput, captureSession.inputs.contains(frontCameraInput),
let backCamera = self.backCamera else { throw CameraError.invalidOperation }
self.backCameraInput = try AVCaptureDeviceInput(device: backCamera)
captureSession.removeInput(frontCameraInput)
if captureSession.canAddInput(self.backCameraInput!) {
captureSession.addInput(self.backCameraInput!)
self.currentCameraPosition = .back
}
else { throw CameraError.invalidOperation }
}
switch currentCameraPosition {
case .front:
try switchToRearCamera()
case .back:
try switchToFrontCamera()
}
captureSession.commitConfiguration()
}
最后需要在info.plist添加訪問相機的權(quán)限: