티스토리 뷰

728x90

재직중인 회사 서비스의 주요개발은 카메라와 WebRTC인데,

이번 프로젝트를 통해 카메라 쪽을 구현하면서 공부했던걸 정리해보고자 기록을 남겨봅니다. 


 

AVCaptureSession

사진 또는 비디오를 이용하기 전에 아래와 같은 설정을 하는 class 입니다.

즉, input과 output을 연결시켜주는 중개자 의 느낌!

AVCaptureSession 공식문서

      카메라 기능을 구현하려면 아래의 3단계 세팅이 필요합니다.



  1. 입력 장치(input device)
  2. 출력  미디어(output media)
  3. preview views

위 그림을 보고 흐름을 예를 들어 표현해보자면 아래처럼 표현할 수 있어요.

* input은 빨간글씨, output은 파란글씨로 적겠습니다.

 

A: 카메라 (후면) 광각 렌즈를 써서 영상을 찍을건데 소리도 녹화 됐음 좋겠어.
     녹화된 영상은 1280*720 해상도로 사진첩에 저장되게!

B: 카메라 (전면)으로 사진을 찍을거고, 
     촬영된 사진은 사진첩말고 파일로 저장하고 싶어. 

'아니.. 진짜 당연하게 될줄 알았던것들이 하나하나 다 세팅을 해줘야 돼..?'

 

네,
카메라 전면/ 후면 세팅,
카메라 렌즈,
영상 녹화,
음성 녹음

심지어 어떻게 저장을 할건지도 

하나하나 다 해줘야 합니다.

 

그리고 captureSession에는 각각 최소 1개의 capture input/ capture output이 있어야 해요.

 

 

capture input이란? 
- iOS나 Mac에 내장된 카메라, 마이크 같은 미디어 소스입니다.

capture output이란? 
- capture input에서 제공하는 데이터를 사용해 이미지/ 동영상을 생성합니다.

 

 

이제 하나씩 세팅을 해봅시다. 먼저 input device

 

1. input device

Input device를 세팅 하려면 아래 흐름대로 세팅을 합니다.

 

import AVFoundation
import UIKit

class CameraViewController: UIViewController {

  @IBOutlet weak var cameraView: PreviewView!

  private let captureSession = AVCaptureSession()

  override func viewDidLoad() {
          super.viewDidLoad()

  }

  override func viewDidAppear(_ animated: Bool) {
          super.viewDidAppear(animated)
          checkCameraAuth() // 권한체크
  }

  func setupCamera() {
        captureSession.beginConfiguration() // 설정시작
        // 1
        let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
                                                  for: .video,
                                                  position: .back)
        // 2
        guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!),
              captureSession.canAddInput(videoDeviceInput)
        else {
            return
        }
        // 3
        captureSession.addInput(videoDeviceInput)
  }
}

 

  1. AVCaptureDevice의 파라미터값을 이용
    • deviceType: 어떤 렌즈를 사용할건지 선택
    • mediaType: 영상을 찍을건지, 음성 녹음할 건지 선택
    • position: 카메라 전면/ 후면 선택
  2. AVCaptureDeviceInput을 이용해 1번에서 등록했던 AVCaptureDevice를 불러옴.
  3. captureSession input에 추가
* iOS 기기마다 지원하는 카메라 렌즈가 각각 다르기 때문에 적절한 분기 처리가 필요해요.
   AVCaptureDevice.DeviceType, 즉 어떤렌즈를 사용할지는 이 링크에 잘 정리 되어있습니다. 

 

 

2. output media

위 CaptureSession에 설정된 input으로 녹화/ 녹음된 데이터를 이용해 출력하는 부분 입니다.

 

1번의 setupCamera() 부분을 이어서 작성해 보면

func setupCamera() {
        // ...
        captureSession.addInput(videoDeviceInput)
        
        // 1
        let videoOutput = AVCaptureMovieFileOutput()
        guard captureSession.canAddOutput(videoOutput) else {
            return
        }
        
        // 2
        captureSession.sessionPreset = .medium
        captureSession.addOutput(videoOutput)
        captureSession.commitConfiguration() // 설정종료
}

 

 

  1. 비디오와 오디오를 녹화 하기 위해 AVCaptureMovieFileOutput()을 사용
  2. sessionPreset의 속성으로 원하는 화질 선택

 

* 중요!! 

input, output의 설정값을 바꾸기 전에 
beginConfiguration()을 호출하고,

설정값을 다 바꾼 후엔
commitConfiguration()을 호출해야 합니다.

 

3. previewLayer 

마지막입니다.

위에서 카메라 설정 했으니, 사용자가 볼 수 있게 카메라 화면을 나타내줘야겠죠!

애플공식문서에 보면 친절하게 카메라화면을 표시해주는 layer 코드가 있습니다.

 

먼저 코드부터 봅시다.

class PreviewView: UIView {

    // Use AVCaptureVideoPreviewLayer as the view's backing layer.
    override class var layerClass: AnyClass {
        AVCaptureVideoPreviewLayer.self
    }
    
    var previewLayer: AVCaptureVideoPreviewLayer {
        layer as! AVCaptureVideoPreviewLayer
    }
    
    // Connect the layer to a capture session.
    var session: AVCaptureSession? {
        get { previewLayer.session }
        set { previewLayer.session = newValue }
    }
}

 

PreviewView내부에 애플에서 제공해주는 AVCaptureVideoPreviewLayer를 이용해 preview를 만들어 준 다음.

captureSession을 만들던 ViewController에 preview를 추가해줍니다.

 

func setupCamera() {
        // ...
        captureSession.commitConfiguration() // 설정종료
        
        cameraView.videoPreviewLayer.session = captureSession // 추가
}

 

이제 captureSession의 모든 세팅이 끝났으니 startRunning()을 호출해 실행시켜 봅시다.

func setupCamera() {
        // ...
        cameraView.videoPreviewLayer.session = captureSession
        
        DispatchQueue.global(qos: .background).async {
            self.captureSession.startRunning()
        }
}

startRunning()background에서 실행해줍니다.

UI와 관련되지 않은건 굳이굳이 main에서 돌릴 필요가 없어, background에서 실행하라는것 같아요. (이 부분은 공부좀해봐야겠네요..)

main thread에서 실행할 경우 뜨는 보라색 에러

 

전체 예제코드는 Github에 올려놓을게요.

 

+ 추가로
View와 Layer의 차이를 모르겠다면 여기 참고해보시면 도움이 될거에요!

 


중요한걸 안적었네요.권한!!

카메라에 접근하기 위해서 권한이 필요합니다.

 

1. Info.plist

Privacy - Camera Usage Description

 

  1. 좌측 네비게이터 - info 
  2. '+' 클릭
  3. Key 값에 'Privacy - Camera Usage Description' 입력 후 추가

Privacy - Camera Usage Description

 

2. 권한 체크

 

화면이 띄워질때마다 체크를 하고 싶기 때문에 viewDidAppear 라이프사이클에 적용.

class CameraViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        checkCameraAuth()
    }
}


// MARK: Check Camera Auth
extension CameraViewController {
    func checkCameraAuth() {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:  // 2
            setupCamera()  
        case .notDetermined: // 1
            sessionQueue.suspend()
            AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
                if granted {
                    DispatchQueue.main.async {
                        self.setupCamera()
                    }
                }
                self.sessionQueue.resume()
            })

        default:
            break
        }
    }
}

 

  1. 권한체크시 아직 권한체크를 안했으면 (.notDetermined) 권한체크 요청 팝업 띄우기 (requestAccess(for:completionhandler:))
    '허용'시 카메라세션 셋업시작 (setupCamera())
  2. 권한허용되어있는 상태면 카메라세션 셋업시작
  3. 나머지 (.denied/ .restricted) 일 경우 때에 따라 처리하면 될것 같습니다 :) 

 

여기까지 세팅해주면!?

 

 

고생하셨습니다 !!

추후 영상 촬영 시작, 종료도 천천히 정리해보겠습니다 :) 

감사합니다 


참고

 

https://developer.apple.com/documentation/avfoundation/capture_setup/setting_up_a_capture_session

 

Setting Up a Capture Session | Apple Developer Documentation

Configure input devices, output media, preview views, and basic settings before capturing photos or video.

developer.apple.com

 

 

 

728x90