Swift使用AVFoundation實現(xiàn)自定義相機

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)限:


camera.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嘱蛋,一起剝皮案震驚了整個濱河市蚯姆,隨后出現(xiàn)的幾起案子椅寺,更是在濱河造成了極大的恐慌,老刑警劉巖蒋失,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異桐玻,居然都是意外死亡篙挽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門镊靴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铣卡,“玉大人,你說我怎么就攤上這事偏竟≈舐洌” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵踊谋,是天一觀的道長蝉仇。 經(jīng)常有香客問我,道長殖蚕,這世上最難降的妖魔是什么轿衔? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮睦疫,結(jié)果婚禮上害驹,老公的妹妹穿的比我還像新娘。我一直安慰自己蛤育,他們只是感情好宛官,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓦糕,像睡著了一般底洗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刻坊,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天枷恕,我揣著相機與錄音,去河邊找鬼谭胚。 笑死徐块,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的灾而。 我是一名探鬼主播胡控,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼旁趟!你這毒婦竟也來了昼激?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎橙困,沒想到半個月后瞧掺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡凡傅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年辟狈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夏跷。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡哼转,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出槽华,到底是詐尸還是另有隱情壹蔓,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布猫态,位于F島的核電站佣蓉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏亲雪。R本人自食惡果不足惜偏螺,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匆光。 院中可真熱鬧套像,春花似錦、人聲如沸终息。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽周崭。三九已至柳譬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間续镇,已是汗流浹背美澳。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留摸航,地道東北人制跟。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像酱虎,于是被迫代替她去往敵國和親雨膨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容