device_info_avfoundation.mm (7534B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "device_info_avfoundation.h" 8 #include <CoreVideo/CVPixelBuffer.h> 9 10 #include <string> 11 12 #include "components/capturer/RTCCameraVideoCapturer.h" 13 #import "helpers/NSString+StdString.h" 14 #include "media/base/video_common.h" 15 #include "modules/video_capture/video_capture_defines.h" 16 #include "rtc_base/logging.h" 17 18 namespace webrtc::videocapturemodule { 19 /* static */ 20 int32_t DeviceInfoAvFoundation::ConvertAVFrameRateToCapabilityFPS( 21 Float64 aRate) { 22 return static_cast<int32_t>(aRate); 23 } 24 25 /* static */ 26 webrtc::VideoType DeviceInfoAvFoundation::ConvertFourCCToVideoType( 27 FourCharCode aCode) { 28 switch (aCode) { 29 case kCVPixelFormatType_420YpCbCr8Planar: 30 case kCVPixelFormatType_420YpCbCr8PlanarFullRange: 31 return webrtc::VideoType::kI420; 32 case kCVPixelFormatType_24BGR: 33 return webrtc::VideoType::kRGB24; 34 case kCVPixelFormatType_32ABGR: 35 return webrtc::VideoType::kABGR; 36 case kCMPixelFormat_32ARGB: 37 return webrtc::VideoType::kBGRA; 38 case kCMPixelFormat_32BGRA: 39 return webrtc::VideoType::kARGB; 40 case kCMPixelFormat_16LE565: 41 return webrtc::VideoType::kRGB565; 42 case kCMPixelFormat_16LE555: 43 case kCMPixelFormat_16LE5551: 44 return webrtc::VideoType::kARGB1555; 45 case kCMPixelFormat_422YpCbCr8_yuvs: 46 return webrtc::VideoType::kYUY2; 47 case kCMPixelFormat_422YpCbCr8: 48 return webrtc::VideoType::kUYVY; 49 case kCMVideoCodecType_JPEG: 50 case kCMVideoCodecType_JPEG_OpenDML: 51 return webrtc::VideoType::kMJPEG; 52 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: 53 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: 54 return webrtc::VideoType::kNV12; 55 default: 56 RTC_LOG(LS_WARNING) << "Unhandled FourCharCode" << aCode; 57 return webrtc::VideoType::kUnknown; 58 } 59 } 60 61 DeviceInfoAvFoundation::DeviceInfoAvFoundation() 62 : mInvalidateCapabilities(false), 63 mDeviceChangeCaptureInfo([[DeviceInfoIosObjC alloc] init]) { 64 [mDeviceChangeCaptureInfo registerOwner:this]; 65 } 66 67 DeviceInfoAvFoundation::~DeviceInfoAvFoundation() { 68 [mDeviceChangeCaptureInfo registerOwner:nil]; 69 } 70 71 void DeviceInfoAvFoundation::DeviceChange() { 72 mInvalidateCapabilities = true; 73 DeviceInfo::DeviceChange(); 74 } 75 76 uint32_t DeviceInfoAvFoundation::NumberOfDevices() { 77 RTC_DCHECK_RUN_ON(&mChecker); 78 EnsureCapabilitiesMap(); 79 return mDevicesAndCapabilities.size(); 80 } 81 82 int32_t DeviceInfoAvFoundation::GetDeviceName( 83 uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameLength, 84 char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Length, 85 char* /* aProductUniqueIdUTF8 */, uint32_t /* aProductUniqueIdUTF8Length */, 86 pid_t* /* aPid */, bool* /*deviceIsPlaceholder*/) { 87 RTC_DCHECK_RUN_ON(&mChecker); 88 // Don't EnsureCapabilitiesMap() here, since: 89 // 1) That might invalidate the capabilities map 90 // 2) This function depends on the device index 91 92 if (aDeviceNumber >= mDevicesAndCapabilities.size()) { 93 return -1; 94 } 95 96 const auto& [uniqueId, name, _] = mDevicesAndCapabilities[aDeviceNumber]; 97 98 strncpy(aDeviceUniqueIdUTF8, uniqueId.c_str(), aDeviceUniqueIdUTF8Length); 99 aDeviceUniqueIdUTF8[aDeviceUniqueIdUTF8Length - 1] = '\0'; 100 101 strncpy(aDeviceNameUTF8, name.c_str(), aDeviceNameLength); 102 aDeviceNameUTF8[aDeviceNameLength - 1] = '\0'; 103 104 return 0; 105 } 106 107 int32_t DeviceInfoAvFoundation::NumberOfCapabilities( 108 const char* aDeviceUniqueIdUTF8) { 109 RTC_DCHECK_RUN_ON(&mChecker); 110 111 std::string deviceUniqueId(aDeviceUniqueIdUTF8); 112 const auto* tup = FindDeviceAndCapabilities(deviceUniqueId); 113 if (!tup) { 114 return 0; 115 } 116 117 const auto& [_, __, capabilities] = *tup; 118 return static_cast<int32_t>(capabilities.size()); 119 } 120 121 int32_t DeviceInfoAvFoundation::GetCapability( 122 const char* aDeviceUniqueIdUTF8, const uint32_t aDeviceCapabilityNumber, 123 VideoCaptureCapability& aCapability) { 124 RTC_DCHECK_RUN_ON(&mChecker); 125 126 std::string deviceUniqueId(aDeviceUniqueIdUTF8); 127 const auto* tup = FindDeviceAndCapabilities(deviceUniqueId); 128 if (!tup) { 129 return -1; 130 } 131 132 const auto& [_, __, capabilities] = *tup; 133 if (aDeviceCapabilityNumber >= capabilities.size()) { 134 return -1; 135 } 136 137 aCapability = capabilities[aDeviceCapabilityNumber]; 138 return 0; 139 } 140 141 int32_t DeviceInfoAvFoundation::CreateCapabilityMap( 142 const char* aDeviceUniqueIdUTF8) { 143 RTC_DCHECK_RUN_ON(&mChecker); 144 145 const size_t deviceUniqueIdUTF8Length = strlen(aDeviceUniqueIdUTF8); 146 if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) { 147 RTC_LOG(LS_INFO) << "Device name too long"; 148 return -1; 149 } 150 RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device " 151 << aDeviceUniqueIdUTF8; 152 std::string deviceUniqueId(aDeviceUniqueIdUTF8); 153 const auto* tup = FindDeviceAndCapabilities(deviceUniqueId); 154 if (!tup) { 155 RTC_LOG(LS_INFO) << "no matching device found"; 156 return -1; 157 } 158 159 // Store the new used device name 160 _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; 161 _lastUsedDeviceName = static_cast<char*>( 162 realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1)); 163 memcpy(_lastUsedDeviceName, aDeviceUniqueIdUTF8, 164 _lastUsedDeviceNameLength + 1); 165 166 const auto& [_, __, capabilities] = *tup; 167 _captureCapabilities = capabilities; 168 return static_cast<int32_t>(_captureCapabilities.size()); 169 } 170 171 auto DeviceInfoAvFoundation::FindDeviceAndCapabilities( 172 const std::string& aDeviceUniqueId) const 173 -> const std::tuple<std::string, std::string, VideoCaptureCapabilities>* { 174 RTC_DCHECK_RUN_ON(&mChecker); 175 for (const auto& tup : mDevicesAndCapabilities) { 176 if (std::get<0>(tup) == aDeviceUniqueId) { 177 return &tup; 178 } 179 } 180 return nullptr; 181 } 182 183 void DeviceInfoAvFoundation::EnsureCapabilitiesMap() { 184 RTC_DCHECK_RUN_ON(&mChecker); 185 186 if (mInvalidateCapabilities.exchange(false)) { 187 mDevicesAndCapabilities.clear(); 188 } 189 190 if (!mDevicesAndCapabilities.empty()) { 191 return; 192 } 193 194 for (AVCaptureDevice* device in [RTCCameraVideoCapturer 195 captureDevicesWithDeviceTypes:[RTCCameraVideoCapturer 196 defaultCaptureDeviceTypes]]) { 197 std::string uniqueId = [NSString stdStringForString:device.uniqueID]; 198 std::string name = [NSString stdStringForString:device.localizedName]; 199 auto& [_, __, capabilities] = mDevicesAndCapabilities.emplace_back( 200 uniqueId, name, VideoCaptureCapabilities()); 201 202 for (AVCaptureDeviceFormat* format in 203 [RTCCameraVideoCapturer supportedFormatsForDevice:device]) { 204 VideoCaptureCapability cap; 205 FourCharCode fourcc = 206 CMFormatDescriptionGetMediaSubType(format.formatDescription); 207 cap.videoType = ConvertFourCCToVideoType(fourcc); 208 CMVideoDimensions dimensions = 209 CMVideoFormatDescriptionGetDimensions(format.formatDescription); 210 cap.width = dimensions.width; 211 cap.height = dimensions.height; 212 213 for (AVFrameRateRange* range in format.videoSupportedFrameRateRanges) { 214 cap.maxFPS = ConvertAVFrameRateToCapabilityFPS(range.maxFrameRate); 215 capabilities.push_back(cap); 216 } 217 218 if (capabilities.empty()) { 219 cap.maxFPS = 30; 220 capabilities.push_back(cap); 221 } 222 } 223 } 224 } 225 } // namespace webrtc::videocapturemodule