mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
Bug 1451394 - Integrate with the libwebrtc camera backend for Mac. r=webrtc-reviewers,jib
Differential Revision: https://phabricator.services.mozilla.com/D163684
This commit is contained in:
parent
ace3a99c63
commit
47c24b8bab
@ -36,9 +36,15 @@ if CONFIG["MOZ_WEBRTC"]:
|
||||
REQUIRES_UNIFIED_BUILD = True
|
||||
UNIFIED_SOURCES += [
|
||||
"objc_video_capture/device_info.mm",
|
||||
"objc_video_capture/device_info_avfoundation.mm",
|
||||
"objc_video_capture/device_info_objc.mm",
|
||||
"objc_video_capture/rtc_video_capture_objc.mm",
|
||||
"objc_video_capture/video_capture.mm",
|
||||
"objc_video_capture/video_capture_avfoundation.mm",
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
"/third_party/libwebrtc/sdk/objc",
|
||||
"/third_party/libwebrtc/sdk/objc/base",
|
||||
]
|
||||
CMMFLAGS += [
|
||||
"-fobjc-arc",
|
||||
|
@ -18,8 +18,7 @@
|
||||
|
||||
@class DeviceInfoIosObjC;
|
||||
|
||||
namespace webrtc {
|
||||
namespace videocapturemodule {
|
||||
namespace webrtc::videocapturemodule {
|
||||
class DeviceInfoIos : public DeviceInfoImpl {
|
||||
public:
|
||||
DeviceInfoIos();
|
||||
@ -52,7 +51,6 @@ class DeviceInfoIos : public DeviceInfoImpl {
|
||||
DeviceInfoIosObjC* _captureInfo;
|
||||
};
|
||||
|
||||
} // namespace videocapturemodule
|
||||
} // namespace webrtc
|
||||
} // namespace webrtc::videocapturemodule
|
||||
|
||||
#endif // MODULES_VIDEO_CAPTURE_OBJC_DEVICE_INFO_H_
|
||||
|
@ -19,8 +19,11 @@
|
||||
#include "device_info.h"
|
||||
#include "device_info_objc.h"
|
||||
#include "modules/video_capture/video_capture_impl.h"
|
||||
#include "mozilla/StaticPrefs_media.h"
|
||||
#include "objc_video_capture/device_info_avfoundation.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace webrtc;
|
||||
using namespace videocapturemodule;
|
||||
|
||||
@ -32,7 +35,12 @@ static NSArray* camera_presets = @[
|
||||
RTC_LOG(LS_ERROR) << __FUNCTION__ << " is not supported on the iOS platform."; \
|
||||
return -1;
|
||||
|
||||
VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() { return new DeviceInfoIos(); }
|
||||
VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() {
|
||||
if (StaticPrefs::media_getusermedia_camera_macavf_enabled_AtStartup()) {
|
||||
return new DeviceInfoAvFoundation();
|
||||
}
|
||||
return new DeviceInfoIos();
|
||||
}
|
||||
|
||||
DeviceInfoIos::DeviceInfoIos() { this->Init(); }
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef DOM_MEDIA_SYSTEMSERVICES_OBJC_VIDEO_CAPTURE_DEVICE_INFO_AVFOUNDATION_H_
|
||||
#define DOM_MEDIA_SYSTEMSERVICES_OBJC_VIDEO_CAPTURE_DEVICE_INFO_AVFOUNDATION_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "device_info_objc.h"
|
||||
#include "modules/video_capture/device_info_impl.h"
|
||||
|
||||
namespace webrtc::videocapturemodule {
|
||||
|
||||
/**
|
||||
* DeviceInfo implementation for the libwebrtc ios/mac sdk camera backend.
|
||||
* Single threaded except for DeviceChange() that happens on a platform callback
|
||||
* thread.
|
||||
*/
|
||||
class DeviceInfoAvFoundation : public DeviceInfoImpl {
|
||||
public:
|
||||
static int32_t ConvertAVFrameRateToCapabilityFPS(Float64 aRate);
|
||||
static webrtc::VideoType ConvertFourCCToVideoType(FourCharCode aCode);
|
||||
|
||||
DeviceInfoAvFoundation();
|
||||
virtual ~DeviceInfoAvFoundation();
|
||||
|
||||
// Implementation of DeviceInfoImpl.
|
||||
int32_t Init() override { return 0; }
|
||||
void DeviceChange() override;
|
||||
uint32_t NumberOfDevices() override;
|
||||
int32_t GetDeviceName(uint32_t aDeviceNumber, char* aDeviceNameUTF8,
|
||||
uint32_t aDeviceNameLength, char* aDeviceUniqueIdUTF8,
|
||||
uint32_t aDeviceUniqueIdUTF8Length,
|
||||
char* aProductUniqueIdUTF8 = nullptr,
|
||||
uint32_t aProductUniqueIdUTF8Length = 0,
|
||||
pid_t* aPid = nullptr) override;
|
||||
int32_t NumberOfCapabilities(const char* aDeviceUniqueIdUTF8) override;
|
||||
int32_t GetCapability(const char* aDeviceUniqueIdUTF8,
|
||||
const uint32_t aDeviceCapabilityNumber,
|
||||
VideoCaptureCapability& aCapability) override;
|
||||
int32_t DisplayCaptureSettingsDialogBox(const char* aDeviceUniqueIdUTF8,
|
||||
const char* aDialogTitleUTF8,
|
||||
void* aParentWindow,
|
||||
uint32_t aPositionX,
|
||||
uint32_t aPositionY) override {
|
||||
return -1;
|
||||
}
|
||||
int32_t CreateCapabilityMap(const char* aDeviceUniqueIdUTF8) override
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(_apiLock);
|
||||
|
||||
private:
|
||||
const std::tuple<std::string, std::string, VideoCaptureCapabilities>*
|
||||
FindDeviceAndCapabilities(const std::string& aDeviceUniqueId) const;
|
||||
void EnsureCapabilitiesMap();
|
||||
|
||||
SequenceChecker mChecker;
|
||||
std::atomic<bool> mInvalidateCapabilities;
|
||||
// [{uniqueId, name, capabilities}]
|
||||
std::vector<std::tuple<std::string, std::string, VideoCaptureCapabilities>>
|
||||
mDevicesAndCapabilities RTC_GUARDED_BY(mChecker);
|
||||
const DeviceInfoIosObjC* mDeviceChangeCaptureInfo RTC_GUARDED_BY(mChecker);
|
||||
};
|
||||
|
||||
} // namespace webrtc::videocapturemodule
|
||||
|
||||
#endif
|
@ -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
|
@ -14,11 +14,12 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#include "modules/video_capture/video_capture_defines.h"
|
||||
#include "device_info.h"
|
||||
|
||||
@interface DeviceInfoIosObjC : NSObject {
|
||||
NSArray* _observers;
|
||||
NSLock* _lock;
|
||||
webrtc::videocapturemodule::DeviceInfoIos* _owner;
|
||||
webrtc::VideoCaptureModule::DeviceInfo* _owner;
|
||||
}
|
||||
|
||||
+ (int)captureDeviceCount;
|
||||
@ -29,7 +30,7 @@
|
||||
+ (NSString*)deviceNameForUniqueId:(NSString*)uniqueId;
|
||||
+ (webrtc::VideoCaptureCapability)capabilityForPreset:(NSString*)preset;
|
||||
|
||||
- (void)registerOwner:(webrtc::videocapturemodule::DeviceInfoIos*)owner;
|
||||
- (void)registerOwner:(webrtc::VideoCaptureModule::DeviceInfo*)owner;
|
||||
- (void)configureObservers;
|
||||
|
||||
@end
|
||||
|
@ -15,7 +15,6 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import "device_info_objc.h"
|
||||
#include "modules/video_capture/video_capture_config.h"
|
||||
|
||||
@implementation DeviceInfoIosObjC
|
||||
|
||||
@ -30,7 +29,7 @@
|
||||
- (void)dealloc {
|
||||
}
|
||||
|
||||
- (void)registerOwner:(DeviceInfoIos*)owner {
|
||||
- (void)registerOwner:(webrtc::VideoCaptureModule::DeviceInfo*)owner {
|
||||
[_lock lock];
|
||||
if (!_owner && owner) {
|
||||
[self configureObservers];
|
||||
|
@ -11,6 +11,7 @@
|
||||
#ifndef MODULES_VIDEO_CAPTURE_OBJC_RTC_VIDEO_CAPTURE_OBJC_H_
|
||||
#define MODULES_VIDEO_CAPTURE_OBJC_RTC_VIDEO_CAPTURE_OBJC_H_
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#ifdef WEBRTC_IOS
|
||||
# import <UIKit/UIKit.h>
|
||||
|
@ -16,8 +16,7 @@
|
||||
|
||||
@class RTCVideoCaptureIosObjC;
|
||||
|
||||
namespace webrtc {
|
||||
namespace videocapturemodule {
|
||||
namespace webrtc::videocapturemodule {
|
||||
class VideoCaptureIos : public VideoCaptureImpl {
|
||||
public:
|
||||
VideoCaptureIos();
|
||||
@ -37,7 +36,6 @@ class VideoCaptureIos : public VideoCaptureImpl {
|
||||
VideoCaptureCapability capability_;
|
||||
};
|
||||
|
||||
} // namespace videocapturemodule
|
||||
} // namespace webrtc
|
||||
} // namespace webrtc::videocapturemodule
|
||||
|
||||
#endif // MODULES_VIDEO_CAPTURE_OBJC_VIDEO_CAPTURE_H_
|
||||
|
@ -16,11 +16,17 @@
|
||||
#include "rtc_video_capture_objc.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "video_capture_avfoundation.h"
|
||||
#include "mozilla/StaticPrefs_media.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace webrtc;
|
||||
using namespace videocapturemodule;
|
||||
|
||||
rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create(const char* deviceUniqueIdUTF8) {
|
||||
if (StaticPrefs::media_getusermedia_camera_macavf_enabled_AtStartup()) {
|
||||
return VideoCaptureAvFoundation::Create(deviceUniqueIdUTF8);
|
||||
}
|
||||
return VideoCaptureIos::Create(deviceUniqueIdUTF8);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef DOM_MEDIA_SYSTEMSERVICES_OBJC_VIDEO_CAPTURE_VIDEO_CAPTURE2_H_
|
||||
#define DOM_MEDIA_SYSTEMSERVICES_OBJC_VIDEO_CAPTURE_VIDEO_CAPTURE2_H_
|
||||
|
||||
#import "components/capturer/RTCCameraVideoCapturer.h"
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "modules/video_capture/video_capture_impl.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
@class VideoCaptureAdapter;
|
||||
|
||||
namespace webrtc::videocapturemodule {
|
||||
|
||||
/**
|
||||
* VideoCaptureImpl implementation of the libwebrtc ios/mac sdk camera backend.
|
||||
* Single threaded except for OnFrame() that happens on a platform callback thread.
|
||||
*/
|
||||
class VideoCaptureAvFoundation : public VideoCaptureImpl {
|
||||
public:
|
||||
VideoCaptureAvFoundation(AVCaptureDevice* _Nonnull aDevice);
|
||||
virtual ~VideoCaptureAvFoundation();
|
||||
|
||||
static rtc::scoped_refptr<VideoCaptureModule> Create(const char* _Nullable aDeviceUniqueIdUTF8);
|
||||
|
||||
// Implementation of VideoCaptureImpl. Single threaded.
|
||||
int32_t StartCapture(const VideoCaptureCapability& aCapability) override;
|
||||
int32_t StopCapture() override;
|
||||
bool CaptureStarted() override;
|
||||
int32_t CaptureSettings(VideoCaptureCapability& aSettings) override;
|
||||
|
||||
// Callback. This can be called on any thread.
|
||||
int32_t OnFrame(webrtc::VideoFrame& aFrame) MOZ_EXCLUDES(api_lock_);
|
||||
|
||||
private:
|
||||
SequenceChecker mChecker;
|
||||
AVCaptureDevice* _Nonnull mDevice RTC_GUARDED_BY(mChecker);
|
||||
VideoCaptureAdapter* _Nonnull mAdapter RTC_GUARDED_BY(mChecker);
|
||||
RTC_OBJC_TYPE(RTCCameraVideoCapturer) * _Nullable mCapturer RTC_GUARDED_BY(mChecker);
|
||||
mozilla::Maybe<VideoCaptureCapability> mCapability RTC_GUARDED_BY(mChecker);
|
||||
};
|
||||
|
||||
} // namespace webrtc::videocapturemodule
|
||||
|
||||
@interface VideoCaptureAdapter : NSObject <RTC_OBJC_TYPE (RTCVideoCapturerDelegate)>
|
||||
@property(nonatomic) webrtc::videocapturemodule::VideoCaptureAvFoundation* _Nullable capturer;
|
||||
@end
|
||||
|
||||
#endif
|
@ -0,0 +1,158 @@
|
||||
/* -*- 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 "video_capture_avfoundation.h"
|
||||
|
||||
#import "api/video_frame_buffer/RTCNativeI420Buffer+Private.h"
|
||||
#import "base/RTCI420Buffer.h"
|
||||
#import "base/RTCVideoFrame.h"
|
||||
#import "base/RTCVideoFrameBuffer.h"
|
||||
#import "components/capturer/RTCCameraVideoCapturer.h"
|
||||
#import "helpers/NSString+StdString.h"
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "device_info_avfoundation.h"
|
||||
#include "modules/video_capture/video_capture_defines.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace webrtc::videocapturemodule;
|
||||
|
||||
namespace {
|
||||
webrtc::VideoRotation ToNativeRotation(RTCVideoRotation aRotation) {
|
||||
switch (aRotation) {
|
||||
case RTCVideoRotation_0:
|
||||
return webrtc::kVideoRotation_0;
|
||||
case RTCVideoRotation_90:
|
||||
return webrtc::kVideoRotation_90;
|
||||
case RTCVideoRotation_180:
|
||||
return webrtc::kVideoRotation_180;
|
||||
case RTCVideoRotation_270:
|
||||
return webrtc::kVideoRotation_270;
|
||||
default:
|
||||
MOZ_CRASH_UNSAFE_PRINTF("Unexpected rotation %d", static_cast<int>(aRotation));
|
||||
return webrtc::kVideoRotation_0;
|
||||
}
|
||||
}
|
||||
|
||||
AVCaptureDeviceFormat* _Nullable FindFormat(AVCaptureDevice* _Nonnull aDevice,
|
||||
webrtc::VideoCaptureCapability aCapability) {
|
||||
for (AVCaptureDeviceFormat* format in [aDevice formats]) {
|
||||
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
|
||||
if (dimensions.width != aCapability.width) {
|
||||
continue;
|
||||
}
|
||||
if (dimensions.height != aCapability.height) {
|
||||
continue;
|
||||
}
|
||||
FourCharCode fourcc = CMFormatDescriptionGetMediaSubType(format.formatDescription);
|
||||
if (aCapability.videoType != DeviceInfoAvFoundation::ConvertFourCCToVideoType(fourcc)) {
|
||||
continue;
|
||||
}
|
||||
if ([format.videoSupportedFrameRateRanges
|
||||
indexOfObjectPassingTest:^BOOL(AVFrameRateRange* _Nonnull obj, NSUInteger idx,
|
||||
BOOL* _Nonnull stop) {
|
||||
return static_cast<BOOL>(DeviceInfoAvFoundation::ConvertAVFrameRateToCapabilityFPS(
|
||||
obj.maxFrameRate) == aCapability.maxFPS);
|
||||
}] == NSNotFound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@implementation VideoCaptureAdapter
|
||||
@synthesize capturer = _capturer;
|
||||
|
||||
- (void)capturer:(RTC_OBJC_TYPE(RTCVideoCapturer) * _Nonnull)capturer
|
||||
didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) * _Nonnull)frame {
|
||||
const int64_t timestamp_us = frame.timeStampNs / rtc::kNumNanosecsPerMicrosec;
|
||||
RTC_OBJC_TYPE(RTCI420Buffer)* buffer = [[frame buffer] toI420];
|
||||
// Accessing the (intended-to-be-private) native buffer directly is hacky but lets us skip two
|
||||
// copies
|
||||
rtc::scoped_refptr<webrtc::I420BufferInterface> nativeBuffer = [buffer nativeI420Buffer];
|
||||
webrtc::VideoFrame nativeFrame = webrtc::VideoFrame::Builder()
|
||||
.set_video_frame_buffer(nativeBuffer)
|
||||
.set_rotation(ToNativeRotation(frame.rotation))
|
||||
.set_timestamp_us(timestamp_us)
|
||||
.build();
|
||||
_capturer->OnFrame(nativeFrame);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace webrtc::videocapturemodule {
|
||||
VideoCaptureAvFoundation::VideoCaptureAvFoundation(AVCaptureDevice* _Nonnull aDevice)
|
||||
: mDevice(aDevice), mAdapter([[VideoCaptureAdapter alloc] init]), mCapturer(nullptr) {
|
||||
{
|
||||
const char* uniqueId = [[aDevice uniqueID] UTF8String];
|
||||
size_t len = strlen(uniqueId);
|
||||
_deviceUniqueId = new (std::nothrow) char[len + 1];
|
||||
if (_deviceUniqueId) {
|
||||
memcpy(_deviceUniqueId, uniqueId, len + 1);
|
||||
}
|
||||
}
|
||||
|
||||
mAdapter.capturer = this;
|
||||
mCapturer = [[RTC_OBJC_TYPE(RTCCameraVideoCapturer) alloc] initWithDelegate:mAdapter];
|
||||
}
|
||||
|
||||
VideoCaptureAvFoundation::~VideoCaptureAvFoundation() = default;
|
||||
|
||||
/* static */
|
||||
rtc::scoped_refptr<VideoCaptureModule> VideoCaptureAvFoundation::Create(
|
||||
const char* _Nullable aDeviceUniqueIdUTF8) {
|
||||
std::string uniqueId(aDeviceUniqueIdUTF8);
|
||||
for (AVCaptureDevice* device in [RTCCameraVideoCapturer captureDevices]) {
|
||||
if ([NSString stdStringForString:device.uniqueID] == uniqueId) {
|
||||
rtc::scoped_refptr<VideoCaptureModule> module(
|
||||
new rtc::RefCountedObject<VideoCaptureAvFoundation>(device));
|
||||
return module;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t VideoCaptureAvFoundation::StartCapture(const VideoCaptureCapability& aCapability) {
|
||||
RTC_DCHECK_RUN_ON(&mChecker);
|
||||
if (AVCaptureDeviceFormat* format = FindFormat(mDevice, aCapability)) {
|
||||
mCapability = Some(aCapability);
|
||||
[mCapturer startCaptureWithDevice:mDevice
|
||||
format:format
|
||||
fps:aCapability.maxFPS
|
||||
completionHandler:nullptr];
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t VideoCaptureAvFoundation::StopCapture() {
|
||||
RTC_DCHECK_RUN_ON(&mChecker);
|
||||
mCapability = Nothing();
|
||||
[mCapturer stopCapture];
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VideoCaptureAvFoundation::CaptureStarted() {
|
||||
RTC_DCHECK_RUN_ON(&mChecker);
|
||||
return mCapability.isSome();
|
||||
}
|
||||
|
||||
int32_t VideoCaptureAvFoundation::CaptureSettings(VideoCaptureCapability& aSettings) {
|
||||
MOZ_CRASH("Unexpected call");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t VideoCaptureAvFoundation::OnFrame(webrtc::VideoFrame& aFrame) {
|
||||
MutexLock lock(&api_lock_);
|
||||
return DeliverCapturedFrame(aFrame);
|
||||
}
|
||||
} // namespace webrtc::videocapturemodule
|
@ -10355,6 +10355,13 @@
|
||||
value: @IS_ANDROID@
|
||||
mirror: always
|
||||
|
||||
# Use the libwebrtc AVFoundation camera backend on Mac by default. When
|
||||
# disabled, an older forked capture module is used.
|
||||
- name: media.getusermedia.camera.macavf.enabled
|
||||
type: bool
|
||||
value: false
|
||||
mirror: once
|
||||
|
||||
# WebRTC prefs follow
|
||||
|
||||
# Enables RTCPeerConnection support. Note that, when true, this pref enables
|
||||
|
Loading…
Reference in New Issue
Block a user