일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- JSONParser
- 프로그래머스
- parse
- ios외부카메라연결
- IOS
- 2018 KAKAO BLIND RECRUITMENT
- ios캡처감지
- Xcode
- 위클리챌린지
- swift
- cocoapods
- AVFoundation
- JSON
- 정보처리기사 실기 요약본
- Pod
- UITableView
- 정보처리기사
- usb카메라연결
- CustomCode
- 정보처리기사 실기
- cancelstouchesinview
- ios외부디바이스연결
- ios카메라유선연결
- programmers
- ios캡처방지
- avcapturesession
- 카메라유선연결
- Decodable
- 외부카메라감지
- Codable
- Today
- Total
iOS 개발일기
[iOS] USB 카메라 연결(1) 본문
진행 중인 프로젝트의 작업 중 하나가 외부 카메라를 유선(USB)으로 연결하고 영상을 기기에서 확인하면서 캡처하는 기능이 있었는데, 내부 카메라를 통해서 하는 작업은 해봤지만 USB 카메라를 연결하는 프로젝트는 처음 진행하는 것이었다. iOS는 기본적으로 UVC를 지원하지 않기 때문에 생각보다 외부 기기 연결에는 제약이 많았다.
USB 카메라를 사용하기 위해서는 크게는 세 가지의 연결 방법이 있었다.
- MFi 인증 카메라
- Apple에서 공식적으로 지원하는 방법이기 때문에 AVFoundation을 통해 카메라 접근 및 제어 가능
- 별도의 SDK가 제공되는 카메라
- 고프로나 소니와 같은 기업에서 별도의 SDK를 제공하는 경우
- UVC(USB Video Class) 카메라
- iOS에서는 기본적으로 UVC를 지원하지 않기 때문에 MFi 인증을 받지 않으면 기능이 매우 제한적
- iOS 17부터는 일부 UVC 카메라 장치 연결 가능 ( 단, USB-C 포트일 경우에만 )
- 라이트닝(8핀)의 경우에는 MFi 인증이 되어 있어야 사용 가능
프로젝트에서 사용되는 USB 카메라의 경우에는 UVC 카메라이긴 하나 MFi 인증 카메라나 별도의 SDK를 지원하는 카메라도 아니었다.
따라서, iOS 17 이상에서 USB-C 포트인 기기에서만 테스트가 가능했다.
왜 이런 조건에서만 일부 UVC 카메라 사용이 허용되었을까?
USB-C 포트로 변경되면서 라이트닝(8핀)에서 필수 요소였던 MFi 인증이 사라졌기 때문이다.
(EU 선생님께서 USB-C 포트에 MFi 적용을 하지 말라 하셨기 때문,,,)
그러한 이유로 iOS 17 에서 UVC와 MFi 인증이 필요했던 일부 기능을 USB-C 포트일 경우에는 풀어준 게 아닐까 싶기도 하고
덕분에 iOS 17 이상 & USB-C 포트의 기기일 경우에는 UVC 카메라가 AVFoundation을 통해 접근 및 제어가 가능하다.
기기에 연결된 외부 카메라 목록 가져오기
그럼 AVFoundation으로 어떻게 외부 카메라 목록을 가져올 수 있을까?
improt AVFoundation
let discoverySession = AVCaptureDevice.DiscoverySession(
deviceTypes: [.external],
mediaType: .video,
position: .unspecified
)
let device = discoverySession.devices.first
//외부 카메라 이름
print("camera name:", device.localizedName)
위 코드를 통해 외부에서 연결된 기기 중 카메라 형태의 기기들만 찾아 첫 번째 카메라 디바이스를 가져오게 된다. 처음에는 단순하게 생각해서 라이트닝 젠더를 이용해서 아이폰에 연결해봤었는데 인식이 되지 않았다.(MFi 인증 이슈) 여기서 외부 카메라란 USB 뿐만 아니라 HDMI, 어댑터 등도 포함이 된다.
이제 이 디바이스 정보를 가지고 카메라를 설정할 수 있다.
외부 카메라 포맷 상세정보
외부 카메라가 지원하는 해상도와 프레임을 확인하고 설정할 수 있는데, 우선 해상도 목록을 확인하기 위해서는 카메라의 포맷 목록을 가지고 크기 정보를 반환해 주는 `CMVideoFormatDescriptionGetDimensions` 메서드를 이용하면 목록을 확인할 수 있다.
device.formats.forEach { format in
let dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
print("해상도: \(dimensions.width)x\(dimensions.height)")
}
그런데 출력해보면
왜 해상도가 중복되어 뜨는 것일까?
카메라가 지원하는 포맷에는 해상도뿐만 아니라 다른 포맷 형식, 프레임(FPS) 등의 다양한 정보들이 존재했고, 해상도가 같더라도 다른 정보들이 달라 출력이 되는 것이었다.
device.formats.forEach { format in
//해상도
let dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
//프레임
let frame = format.videoSupportedFrameRateRanges
let min = Int(frame.map { $0.minFrameRate }.min() ?? 0)
let max = Int(frame.map { $0.maxFrameRate }.max() ?? 0)
//픽셀 포맷 타입
let pixelFormat = CMFormatDescriptionGetMediaSubType(format.formatDescription)
let formatString = String(
format: "%c%c%c%c",
(pixelFormat >> 24) & 0xFF,
(pixelFormat >> 16) & 0xFF,
(pixelFormat >> 8) & 0xFF,
pixelFormat & 0xFF
)
print("해상도: \(dimensions.width)x\(dimensions.height)")
print("프레임: \(min)-\(max)")
print("픽셀 포맷: \(formatString)")
}
이러한 속성들을 가지고 중복되는 해상도 중에서 원하는 프레임 속도나 픽셀 포맷 등을 선택하여 중복을 제거할 수 있다. 출력을 해보게 되면
![]() |
![]() |
같은 해상도여도 프레임과 픽셀 포맷과 같은 상세 정보가 다르기 때문에 중복으로 출력이 되는 것이었다. 이 외에도 최대 줌 비율, 멀티 캠 지원 등등 다양한 속성이 존재하며 포맷의 전체적인 정보는 `description` 또는 `formatDescription` 파라미터를 통해 확인할 수 있다.
외부 디바이스 연결 감지
사용자가 USB 카메라를 연결한 상태에서 뷰 컨트롤러로 올수도 있지만 연결하지 않은 상태에서도 연결 및 해제가 일어날 수 있기 때문에 감지할 수 있는 방법이 필요했고 옵저버를 이용해 해결할 수 있었다.
새로운 디바이스만 가져오는 것이 아니라 이전 디바이스의 정보도 확인할 수 있다.
let discoverySession: AVCaptureDevice.DiscoverySession
private var observation: NSKeyValueObservation?
func setupObservation() {
observation = discoverySession.observe(\.devices, options: [.new, .old]) { [weak self] _, change in
guard let self else {return}
//새로 연결된 디바이스 중 카메라만 확인
let newDevices = (change.newValue ?? []).filter { $0.deviceType == .external && $0.hasMediaType(.video) }
if newDevices.isEmpty {
//외부 카메라 연결이 해제되었거나 연결 X
} else {
//새로운 외부 카메라 연결
}
}
}
USB 카메라를 사용하는 뷰 컨트롤러에서만 옵저버를 사용했고 deinit이 되는 순간 옵저버도 제거해주었다.
deinit {
observation?.invalidate()
}
이 외 나머지 카메라 화면 송출 및 포맷 설정 방법은 다음 글에 작성해 보도록 할 예정,,,
'iOS' 카테고리의 다른 글
[iOS] USB 카메라 연결(2) (0) | 2025.02.19 |
---|---|
[iOS] 화면 캡처 감지 및 방지 방법 (0) | 2024.12.31 |
[iOS] 오픈소스 라이선스 표시 및 라이브러리 (0) | 2024.12.04 |
[iOS] Realm 사용법 (0) | 2024.11.24 |
[iOS] SwiftGen 사용법 (1) | 2024.10.20 |