카메라 어플 만들기 3편

1 답변 글타래를 보이고 있습니다
  • 글쓴이
    • 광현
      참가자
      • 글작성 : 15
      • 답글작성 : 26

      오늘은
      0. 기본적인 AVCaptureSession 생성하기
      1. 전면 카메라와 후면 카메라 전환
      2. 사진 찍기 및 저장 기능 구현
      3. previewView 만들기 (MTKView를 이용하는 방법은 추후에 다루겠습니다.)

      기본적인 AVCaptureSession 생성하기

      • AVCaptureSession
        • capture activity 를 다루며 input device 에서 outputs을 capture 할 수 있도록 데이터의 흐름을 관리하는 object 이다.
          let captureSession = AVCaptureSession()
          // captureSession에 대한 설정.
          captureSession.beginConfiguration()
          captureSession.sessionPreset = AVCaptureSession.Preset.photo
        
        • AVCaptureDevice
          • 물리적인 capture device와 그와 관계 속성을 나타내는 object
          • capture device는 AVCaptureSession object에 연결할 수 있는 input data(audio나 video)를 제공
          • class func default(for:)class func default(_:for:position) 을 사용해서 device object를 생성
          guard let captureDevice = AVCaptureDevice.default(.default) else { return }
          
          //후면 카메라
          guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
          
          // 전면 카메라
          guard let frontCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
          
        • AVCaptureDeviceInput
          • capture session에 input data를 제공하는 object 입니다.
        • AVCapturePhotoOutput
          • 스틸 이미지, 라이브 포토, 기타 사진 워크 플로우에 대한 capture output
        • AVCapturePhotoSettings
          • 단일 사진 캡쳐 요청에 사용할 기능 및 설정의 사양을 정의하는 object 입니다.
          // 생성된 captureSession에 device input을 생성 및 연결하고,
          // device output을 연결하는 코드입니다.
        
          var photoOutput : AVCapturePhotoOutput = {
            let output = AVCapturePhotoOutput()
            // 고해상도의 이미지 캡쳐 가능 설정
            output.isHighResolutionCaptureEnabled = true
            return output
          }()
        
          var photoSetting : AVCapturePhotoSettings?
        
          do{
            let backCameraInput = try AVCaptureDeviceInput(device: backCamera)
            let frontCameraInput = try AVCaptureDevice(device: frontCamera)
        
            // captureSession에 camearInput을 받도록 설정.
            captureSession.addInput(backCameraInput)
            captureSession.addOutput(photoOutput)
        
            // photoOutput 의 codec의 hevc 가능시 photoSettings의 codec을 hevc 로 설정하는 코드입니다.
            // hevc 불가능한 경우에는 jpeg codec을 사용하도록 합니다.
            if photoOutput.availablePhotoCodecTypes.contains(.hevc)
              photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
            }else{
              photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
            }
          } catch {
            print(error)
          }
          // captureSession 을 시작시킵니다.
          captureSession.startRunning()
        

      카메라 방향 전환( 전면 및 후면 카메라)

          @IBOutlet func changeCameraPosition(_ sender: UIButton) {
            // beginConfiguration : captureSession의 설정 변경의 시작을 알리는 함수.
            captureSession.beginConfiguration()
            if captureSession.inputs[0] == backCameraInput {
              // 후면에서 전면으로 전환
              captureSession.removeInput(backCameraInput)
              captureSession.addInput(frontCameraInput)
            }else if captureSession.inputs[0] == frontCameraInput {
              // 전면에서 후면으로 전환
              captureSession.removeInput(frontCameraInput)
              captureSession.addInput(backCameraInput)
            }
            // commitConfiguration : captureSession 의 설정 변경이 완료되었음을 알리는 함수.
            captureSession.commitConfiguration()
          }
      

      사진 찍기 및 앨범에 저장 기능 구현.

      • 사진 찍기는 AVCapturePhotoOutput object 의 capturePhoto(with:delegate) 를 이용하면 된다.
      • 찍은 사진을 저장하기 위해서는 AVCapturePhotoCaptureDelegate 프로토코을 채택해야 합니다.
      • AVCapturePhotoCaptureDelegate 프로토콜은 photo capture output의 진행을 감ㅅ히하고 결과를 받아오는 메쏘드들을 제공합니다.
      • capture process를 감시하는 methods
        • func photoOutput(_:willBeginCaptureFor) capture output 설정을 확인했으며, capture process가 시작될 것임 delegate에 알리는 메쏘드
        • func photoOutput(_:willCapturePhotoFor) photo capture 가 곧 일어날 것임을 delegate 에 알리는 메쏘드
        • func photoOutput(_:didCapturePhotoFor) photo capture 가 일어났음을 delegate 에 알리는 메쏘드
        • func photoOutput(_:didFinishCaptureFor) capture process 가 완료되었음을 delegate에 알리는 메쏘드
      • capture results를 받는 methods (저장 기능을 위해 구현해야 하는 부분.)
        • func photoOutput(_:didFinishProcessingPhoto:error) delegate 에 capture 이미지와 관련 metadata를 제공하는 함수.
        @IBAction func capturePhotoBtnTouchUp(_ sender: UIButton){
          photoOutput.capturePhoto(with: photoSettings, delegate: self)
        }
        capturePhoto 이후에 capture process 가 완료된 이미지를 저장하는 메쏘드
        func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
           guard error == nil else {print("Error capturing photo: \(error!)"); return}
           // 사진 앨범에 접근 권한을 요청
           PHPhotoLibrary.requestAuthorization { status in
               guard status == .authorized else {return}
      
               // 사진 앨범에 저장.
               PHPhotoLibrary.shared().performChanges({
                   let creationRequest = PHAssetCreationRequest.forAsset()
                   creationRequest.addResource(with: .photo, data: photo.fileDataRepresentation()!, options: nil)
               }, completionHandler: nil)
           }
         }
      

      previewView 구현

      • 2편에 설명했던 AVCaptureVideoPreviewLayer를 활용해서 만들 수 있습니다.
          @IBOutlet weak var previewView: UIView!
        
          override func viewDidLoad(){
          ...
          guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {return}
          previewLayer.frame = self.previewView.layer.bounds
          self.previewView.layer.addSublayer(previewLayer)
          ...
          }
        
      • 이 게시글은 광현에 의해 3 years, 9 months 전에 수정됐습니다. 이유: 오타 및 소제목 수정
      • 이 게시글은 광현에 의해 3 years, 9 months 전에 수정됐습니다.
    • 야곰
      키 마스터
      • 글작성 : 37
      • 답글작성 : 580

      오오, 마크다운 완전 좋아요! 훨씬 보기 좋네요!

      코드도 더 좋아지고 있는 것 같아요. 변수이름이나 메서드 이름도 더 좋아지고 있고요.
      띄어쓰기도 일관성있게 해주면 더 보기 좋을것 같습니다~
      guard let captureDevice = AVCaptureDevice.default(.default) else { return }
      여기서는 띄어 썼는데
      guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {return}
      여기서는 붙여쓰고 하는것도 보이고요

      }else{
            photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
      }
      

      이거랑

      } catch {
          print(error)
      }
      

      이런 차이도 보이네요.

      사소한 차이가 코드를 훨씬 좋게 만들어줄 것 같아요 ㅎㅎㅎ
      언제나 멋진글 고맙습니다!

      • 광현
        참가자
        • 글작성 : 15
        • 답글작성 : 26

        꼼꼼히 읽어주셔서 감사합니다. 🙂

        아톰 에디터로 쓴 것을 붙여온 건데 비슷한 부분에서 띄어쓰기 상에서 저런 차이가 있는 점은 발견하지 못했었는데

        앞으로 좀 더 신경써서 코드 작성을 해야겠네요!

1 답변 글타래를 보이고 있습니다
  • 답변은 로그인 후 가능합니다.

logo landscape small

사업자번호 : 743-81-02195
통신판매업 신고번호 : 제 2022-충북청주-1278 호
고객센터 : 카카오톡채널 @yagom