iOS 응용 (swift)
[iOS - swift] AVFoundation, Camera 다루는 방법
jake-kim
2021. 1. 26. 01:55
핵심 타입
- AVCaptureSession
- AVCaptureDevice
- AVCaptureDeviceInput, AVCapturePhotoOutput
- AVCaptureVideoPreviewLayer
AVCaptureSession
- 개념: 세션이란 입력에서 출력 장치로의 데이터 흐름을 제어하는데 사용
- 해상도 설정: captureSession.sessionPreset
captureSession = AVCaptureSession()
captureSession.sessionPreset = .photo // set resoultion
AVCaptureDevice
- 개념: 말 그래로 "capture"하는 물리적인 device를 참조하는 데이터형
guard let backCamera = AVCaptureDevice.default(for: AVMediaType.video) else {
return
}
captureDevice = backCamera
AVCaptureDeviceInput, AVCapturePhotoOutput
- AVCaptureDeviceInput: device의 입력
- AVCapturePhotoOutput: device의 출력
- DeviceInput으로 input을 참조, PhotoOutput으로 output을 참조하여 captureSession에 등록
do {
let input = try AVCaptureDeviceInput(device: backCamera)
stillIamgeOutput = AVCapturePhotoOutput()
if captureSession = captureSession.canAddInput(input), captureSession.canAddOutput(stillImageOutput) {
captureSession.addInput(input)
captureSession.addOutput(stillImageOutput)
// 여기에서 preview 세팅하는 함수 호출
}
} catch {
print(error.localizedDescription)
}
AVCaptureVideoPreviewLayer
- 개념: input, output이 설정된 AVCaptureSession의 객체를 받아서 미리보기 화면의 정보를 갖는 Layer 데이터형
- Layer이므로 따로 UIView를 만들어서 이곳에 부착하는 형태로 사용
private func setupLivePreview() {
// previewLayer 세팅
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer.videoGravity = .resizeAspectFill
videoPreviewLayer.connection?.videoOrientation = .portrait
// UIView객체인 preView 위 객체 입힘
preView.layer.insertSublayer(videoPreviewLayer, at: 0) // 맨 앞(0번쨰로)으로 가져와서 보이게끔 설정
DispatchQueue.main.async { in
self.videoPreviewLayer.frame = self.preView.bounds
}
// preview까지 준비되었으니 captureSession을 시작하도록 설정
}
- captureSession에 시작을 알림
private func startCaptureSession() {
DispatchQueue.global(qos: .userInitiated).async {
self.captureSession.startRunning()
}
}
기능 구현
- focus
- 카메라 전환 (앞, 뒤)
- flash 버튼
Focus
- preView에서 tap한 위치를 찾아내어 그 좌표를 focus시키는 방식
- tap 이벤트 바인딩
preView.rx.tapGesture()
.asDriverOnErrorNever()
.drive(onNext: { [weak self] (gesture) in
self?.tapFocus(gesture)
}).disposed(by: bag)
- focus : captureDevice를 가지고 focus 처리
private func tapFocus(_ sender: UITapGestureRecognizer) {
if (sender.state == .ended) {
let thisFocusPoint = sender.location(in: preView)
focusAnimationAt(thisFocusPoint)
let focus_x = thisFocusPoint.x / preView.frame.size.width
let focus_y = thisFocusPoint.y / preView.frame.size.height
if (captureDevice!.isFocusModeSupported(.autoFocus) && captureDevice!.isFocusPointOfInterestSupported) {
do {
try captureDevice?.lockForConfiguration()
captureDevice?.focusMode = .autoFocus
captureDevice?.focusPointOfInterest = CGPoint(x: focus_x, y: focus_y)
if (captureDevice!.isExposureModeSupported(.autoExpose) && captureDevice!.isExposurePointOfInterestSupported) {
captureDevice?.exposureMode = .autoExpose;
captureDevice?.exposurePointOfInterest = CGPoint(x: focus_x, y: focus_y);
}
captureDevice?.unlockForConfiguration()
} catch {
print(error)
}
}
}
}
- focus 받은 부분 애니메이션 처리
fileprivate func focusAnimationAt(_ point: CGPoint) {
let focusView = UIImageView(image: UIImage(named: "aim"))
focusView.center = point
preView.addSubview(focusView)
focusView.transform = CGAffineTransform(scaleX: 2, y: 2)
UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
focusView.transform = CGAffineTransform(scaleX: 0.85, y: 0.85)
}) { (success) in
UIView.animate(withDuration: 0.15, delay: 1, options: .curveEaseInOut, animations: {
focusView.alpha = 0.0
}) { (success) in
focusView.removeFromSuperview()
}
}
}
카메라 전환
- 새로운 device를 생성한 후 captureSession에 등록
private func switchCamera(captureSession: AVCaptureSession?) {
captureSession?.beginConfiguration()
let currentInput = captureSession?.inputs.first as? AVCaptureDeviceInput
captureSession?.removeInput(currentInput!)
let newCameraDevice = currentInput?.device.position == .back ? camera(with: .front) : camera(with: .back)
let newVideoInput = try? AVCaptureDeviceInput(device: newCameraDevice!)
captureSession?.addInput(newVideoInput!)
captureSession?.commitConfiguration()
}
private func camera(with position: AVCaptureDevice.Position) -> AVCaptureDevice? {
let devices = AVCaptureDevice.devices(for: AVMediaType.video)
return devices.filter { $0.position == position }.first
}
- flashMode의 flag값을 가지고 있다가, 사진을 찍을 때, AVCapturePhotoSetting의 객체를 가지고 stillImageOutput에 설정
func didTapBtnTakePicture(stillImageOutput: AVCapturePhotoOutput) {
let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
settings.flashMode = flashMode.value == .off ? .off : .on
stillImageOutput.capturePhoto(with: settings, delegate: self)
}