summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/objc_video_capture/device_info_avfoundation.mm
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/objc_video_capture/device_info_avfoundation.mm')
-rw-r--r--dom/media/systemservices/objc_video_capture/device_info_avfoundation.mm213
1 files changed, 213 insertions, 0 deletions
diff --git a/dom/media/systemservices/objc_video_capture/device_info_avfoundation.mm b/dom/media/systemservices/objc_video_capture/device_info_avfoundation.mm
new file mode 100644
index 0000000000..fae65ff343
--- /dev/null
+++ b/dom/media/systemservices/objc_video_capture/device_info_avfoundation.mm
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "device_info_avfoundation.h"
+#include <CoreVideo/CVPixelBuffer.h>
+
+#include <string>
+
+#include "components/capturer/RTCCameraVideoCapturer.h"
+#import "helpers/NSString+StdString.h"
+#include "media/base/video_common.h"
+#include "modules/video_capture/video_capture_defines.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc::videocapturemodule {
+/* static */
+int32_t DeviceInfoAvFoundation::ConvertAVFrameRateToCapabilityFPS(Float64 aRate) {
+ return static_cast<int32_t>(aRate);
+}
+
+/* static */
+webrtc::VideoType DeviceInfoAvFoundation::ConvertFourCCToVideoType(FourCharCode aCode) {
+ switch (aCode) {
+ case kCVPixelFormatType_420YpCbCr8Planar:
+ case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
+ return webrtc::VideoType::kI420;
+ case kCVPixelFormatType_24BGR:
+ return webrtc::VideoType::kRGB24;
+ case kCVPixelFormatType_32ABGR:
+ return webrtc::VideoType::kABGR;
+ case kCMPixelFormat_32ARGB:
+ return webrtc::VideoType::kBGRA;
+ case kCMPixelFormat_32BGRA:
+ return webrtc::VideoType::kARGB;
+ case kCMPixelFormat_16LE565:
+ return webrtc::VideoType::kRGB565;
+ case kCMPixelFormat_16LE555:
+ case kCMPixelFormat_16LE5551:
+ return webrtc::VideoType::kARGB1555;
+ case kCMPixelFormat_422YpCbCr8_yuvs:
+ return webrtc::VideoType::kYUY2;
+ case kCMPixelFormat_422YpCbCr8:
+ return webrtc::VideoType::kUYVY;
+ case kCMVideoCodecType_JPEG:
+ case kCMVideoCodecType_JPEG_OpenDML:
+ return webrtc::VideoType::kMJPEG;
+ case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+ case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
+ return webrtc::VideoType::kNV12;
+ default:
+ RTC_LOG(LS_WARNING) << "Unhandled FourCharCode" << aCode;
+ return webrtc::VideoType::kUnknown;
+ }
+}
+
+DeviceInfoAvFoundation::DeviceInfoAvFoundation()
+ : mInvalidateCapabilities(false), mDeviceChangeCaptureInfo([[DeviceInfoIosObjC alloc] init]) {
+ [mDeviceChangeCaptureInfo registerOwner:this];
+}
+
+DeviceInfoAvFoundation::~DeviceInfoAvFoundation() { [mDeviceChangeCaptureInfo registerOwner:nil]; }
+
+void DeviceInfoAvFoundation::DeviceChange() {
+ mInvalidateCapabilities = true;
+ DeviceInfo::DeviceChange();
+}
+
+uint32_t DeviceInfoAvFoundation::NumberOfDevices() {
+ RTC_DCHECK_RUN_ON(&mChecker);
+ EnsureCapabilitiesMap();
+ return mDevicesAndCapabilities.size();
+}
+
+int32_t DeviceInfoAvFoundation::GetDeviceName(uint32_t aDeviceNumber, char* aDeviceNameUTF8,
+ uint32_t aDeviceNameLength, char* aDeviceUniqueIdUTF8,
+ uint32_t aDeviceUniqueIdUTF8Length,
+ char* /* aProductUniqueIdUTF8 */,
+ uint32_t /* aProductUniqueIdUTF8Length */,
+ pid_t* /* aPid */) {
+ RTC_DCHECK_RUN_ON(&mChecker);
+ // Don't EnsureCapabilitiesMap() here, since:
+ // 1) That might invalidate the capabilities map
+ // 2) This function depends on the device index
+
+ if (aDeviceNumber >= mDevicesAndCapabilities.size()) {
+ return -1;
+ }
+
+ const auto& [uniqueId, name, _] = mDevicesAndCapabilities[aDeviceNumber];
+
+ strncpy(aDeviceUniqueIdUTF8, uniqueId.c_str(), aDeviceUniqueIdUTF8Length);
+ aDeviceUniqueIdUTF8[aDeviceUniqueIdUTF8Length - 1] = '\0';
+
+ strncpy(aDeviceNameUTF8, name.c_str(), aDeviceNameLength);
+ aDeviceNameUTF8[aDeviceNameLength - 1] = '\0';
+
+ return 0;
+}
+
+int32_t DeviceInfoAvFoundation::NumberOfCapabilities(const char* aDeviceUniqueIdUTF8) {
+ RTC_DCHECK_RUN_ON(&mChecker);
+
+ std::string deviceUniqueId(aDeviceUniqueIdUTF8);
+ const auto* tup = FindDeviceAndCapabilities(deviceUniqueId);
+ if (!tup) {
+ return 0;
+ }
+
+ const auto& [_, __, capabilities] = *tup;
+ return static_cast<int32_t>(capabilities.size());
+}
+
+int32_t DeviceInfoAvFoundation::GetCapability(const char* aDeviceUniqueIdUTF8,
+ const uint32_t aDeviceCapabilityNumber,
+ VideoCaptureCapability& aCapability) {
+ RTC_DCHECK_RUN_ON(&mChecker);
+
+ std::string deviceUniqueId(aDeviceUniqueIdUTF8);
+ const auto* tup = FindDeviceAndCapabilities(deviceUniqueId);
+ if (!tup) {
+ return -1;
+ }
+
+ const auto& [_, __, capabilities] = *tup;
+ if (aDeviceCapabilityNumber >= capabilities.size()) {
+ return -1;
+ }
+
+ aCapability = capabilities[aDeviceCapabilityNumber];
+ return 0;
+}
+
+int32_t DeviceInfoAvFoundation::CreateCapabilityMap(const char* aDeviceUniqueIdUTF8) {
+ RTC_DCHECK_RUN_ON(&mChecker);
+
+ const size_t deviceUniqueIdUTF8Length = strlen(aDeviceUniqueIdUTF8);
+ if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) {
+ RTC_LOG(LS_INFO) << "Device name too long";
+ return -1;
+ }
+ RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device " << aDeviceUniqueIdUTF8;
+ std::string deviceUniqueId(aDeviceUniqueIdUTF8);
+ const auto* tup = FindDeviceAndCapabilities(deviceUniqueId);
+ if (!tup) {
+ RTC_LOG(LS_INFO) << "no matching device found";
+ return -1;
+ }
+
+ // Store the new used device name
+ _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
+ _lastUsedDeviceName =
+ static_cast<char*>(realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1));
+ memcpy(_lastUsedDeviceName, aDeviceUniqueIdUTF8, _lastUsedDeviceNameLength + 1);
+
+ const auto& [_, __, capabilities] = *tup;
+ _captureCapabilities = capabilities;
+ return static_cast<int32_t>(_captureCapabilities.size());
+}
+
+auto DeviceInfoAvFoundation::FindDeviceAndCapabilities(const std::string& aDeviceUniqueId) const
+ -> const std::tuple<std::string, std::string, VideoCaptureCapabilities>* {
+ RTC_DCHECK_RUN_ON(&mChecker);
+ for (const auto& tup : mDevicesAndCapabilities) {
+ if (std::get<0>(tup) == aDeviceUniqueId) {
+ return &tup;
+ }
+ }
+ return nullptr;
+}
+
+void DeviceInfoAvFoundation::EnsureCapabilitiesMap() {
+ RTC_DCHECK_RUN_ON(&mChecker);
+
+ if (mInvalidateCapabilities.exchange(false)) {
+ mDevicesAndCapabilities.clear();
+ }
+
+ if (!mDevicesAndCapabilities.empty()) {
+ return;
+ }
+
+ for (AVCaptureDevice* device in [RTCCameraVideoCapturer captureDevices]) {
+ std::string uniqueId = [NSString stdStringForString:device.uniqueID];
+ std::string name = [NSString stdStringForString:device.localizedName];
+ auto& [_, __, capabilities] =
+ mDevicesAndCapabilities.emplace_back(uniqueId, name, VideoCaptureCapabilities());
+
+ for (AVCaptureDeviceFormat* format in
+ [RTCCameraVideoCapturer supportedFormatsForDevice:device]) {
+ VideoCaptureCapability cap;
+ FourCharCode fourcc = CMFormatDescriptionGetMediaSubType(format.formatDescription);
+ cap.videoType = ConvertFourCCToVideoType(fourcc);
+ CMVideoDimensions dimensions =
+ CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ cap.width = dimensions.width;
+ cap.height = dimensions.height;
+
+ for (AVFrameRateRange* range in format.videoSupportedFrameRateRanges) {
+ cap.maxFPS = ConvertAVFrameRateToCapabilityFPS(range.maxFrameRate);
+ capabilities.push_back(cap);
+ }
+
+ if (capabilities.empty()) {
+ cap.maxFPS = 30;
+ capabilities.push_back(cap);
+ }
+ }
+ }
+}
+} // namespace webrtc::videocapturemodule