티스토리 뷰
이 글은 레코딩 기능을 하는 화면을 구현하면서 정리한 내용입니다.
1. captureSession 생성
세션은 입력에서 출력 장치로의 데이터 흐름을 제어하는데 사용됩니다.
간단히 아래와 같이 초기화 할 수 있습니다.
let captureSession = AVCaptureSession()
captureSession.sessionPreset = .high
sessionPreset은 비디오 및 오디오의 녹화 품질을 설정하는 속성입니다.
저품질로 할 경우 배터리 소비량이 낮아집니다.
2. captureDevice 생성
세션을 생성되면 이제 사용하려는 장치(카메라 or 마이크)를 정의합니다.
영상을 녹화할 것이기 때문에 AVMediaType은 video로 설정하고, DeviceType은 원하는 타입을 선택하여 설정해줍니다.
let videoDevice = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)
하지만 device 별로 카메라 타입의 사용여부가 다르기 때문에 사용 가능한 타입을 찾아서 리턴해주는 메서드를 만들었습니다.
func bestDevice(in position: AVCaptureDevice.Position) -> AVCaptureDevice {
var deviceTypes: [AVCaptureDevice.DeviceType]!
if #available(iOS 11.1, *) {
deviceTypes = [.builtInTrueDepthCamera, .builtInDualCamera, .builtInWideAngleCamera]
} else {
deviceTypes = [.builtInDualCamera, .builtInWideAngleCamera]
}
let discoverySession = AVCaptureDevice.DiscoverySession(
deviceTypes: deviceTypes,
mediaType: .video,
position: .unspecified
)
let devices = discoverySession.devices
guard !devices.isEmpty else { fatalError("Missing capture devices.")}
return devices.first(where: { device in device.position == position })!
}
let videoDevice = bestDevice(in: .back)
- DeviceType에 대한 Apple Document
https://developer.apple.com/documentation/avfoundation/avcapturedevice/devicetype
3. captureDeviceInput 생성
위에서 만든 videoDevice를 이용하여 session에 입력 장치(captureDeviceInput)를 추가해 줍니다.
private func setupSession() {
do {
captureSession.beginConfiguration() // 1
// 2
let videoInput = try AVCaptureDeviceInput(device: videoDevice!)
if captureSession.canAddInput(deviceInput) {
captureSession.addInput(deviceInput)
}
// 3
let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)!
let audioInput = try AVCaptureDeviceInput(device: audioDevice)
if captureSession.canAddInput(audioInput) {
captureSession.addInput(audioInput)
}
// 4
videoOutput = AVCaptureMovieFileOutput()
if captureSession.canAddOutput(videoOutput) {
captureSession.addOutput(videoOutput)
}
captureSession.commitConfiguration() // 5
}
catch let error as NSError {
NSLog("\(error), \(error.localizedDescription)")
}
}
-
세션 구성의 시작을 나타냅니다.
-
비디오 장치에 대한 입력을 만들어 세션에 추가합니다.
-
오디오 장치에 대한 입력을 만들어 세션에 추가합니다.
-
비디오 및 오디오를 파일로 출력하기 위한 인스턴스를 만들어 세션에 추가합니다.
-
세션 구성의 완료를 나타냅니다.
4. View Rendering
이제 비디오의 입력이 UI에서 보여지도록 해야 합니다.
lazy var previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession).then {
$0.bounds = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)
$0.position = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
$0.videoGravity = .resizeAspectFill
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
생성된 previewLayer를 뷰에 추가하고 세션을 실행시켜주면, 레코딩을 위한 화면이 출력되는 것을 볼 수 있을 것입니다.
코딩을 끝내고 실행해보면 화면이 안나올텐데, info.plist에 권한 설정을 해주는 것도 잊지 말고 해줘야 합니다!
5. Recording
이제 출력되는 화면을 녹화해야 하는데, 그러기 위해서는 출력되는 비디오/오디오를 파일에 기록하는 AVCaptureMovieFileOutput 를 구현합니다.
코드를 보면 금방 이해할 것입니다.
// Propertie
var videoOutput: AVCaptureMovieFileOutput!
private func setupSession() {
...
videoOutput = AVCaptureMovieFileOutput()
if captureSession.canAddOutput(videoOutput) {
captureSession.addOutput(videoOutput)
}
...
}
// Recording Methods
private func startRecording() {
outputURL = tempURL() // 파일이 저장될 경로
videoOutput.startRecording(to: outputURL!, recordingDelegate: self)
}
private func stopRecording() {
if videoOutput.isRecording {
videoOutput.stopRecording()
}
}
마지막으로 녹화 종료 후 작업입니다.
AVCaptureFileOutputRecordingDelegate를 준수하면 녹화의 시작/종료 시점에 따른 라이프사이클 메서드를 제공받아 사용할 수 있습니다.
녹화 종료를 하면 아래 함수중 fileOutPut(didFinishRecordingTo:)가 실행될 것이고 이 부분에서 받은 URL을 기반으로 저장하는 작업을 하면 됩니다.
extension RecordingViewController: AVCaptureFileOutputRecordingDelegate {
// 레코딩이 시작되면 호출
func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
// do someting..
}
// 레코딩이 끝나면 호출
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if (error != nil) {
print("Error recording movie: \(error!.localizedDescription)")
} else {
let videoRecorded = outputURL! as URL
UISaveVideoAtPathToSavedPhotosAlbum(videoRecorded.path, nil, nil, nil)
}
}
}
orientation 처리, 레코딩 타이머 등의 부가적인 처리는 전체 코드에서 확인할 수 있습니다.
'Swift' 카테고리의 다른 글
[Swift] HTTP Live Streaming을 위한 재생 목록 (m3u8) (0) | 2020.06.09 |
---|---|
[Swift] Documentation Comments (0) | 2020.05.29 |
[swift] escaping closure (0) | 2019.04.14 |
[swift] Optional (0) | 2019.03.29 |
[swift] Enum (0) | 2019.03.29 |
- Total
- Today
- Yesterday
- RECORDING
- permission error
- NIB
- customAlertView
- HLS
- Cleancode
- AssociatedObject
- xib
- Closure
- ssh
- http live streaming
- UIControl
- Realm
- Coordinator
- Design Pattern
- Swift
- Video
- m3u8
- database
- AVFoundation
- TDD
- pagingView
- IOS
- CollectionView
- UIBarButtonItem
- BaseViewController
- AVKit
- testing
- UIButton
- carousel
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |