diff options
Diffstat (limited to 'third_party/libwebrtc/sdk/objc')
359 files changed, 34112 insertions, 0 deletions
diff --git a/third_party/libwebrtc/sdk/objc/DEPS b/third_party/libwebrtc/sdk/objc/DEPS new file mode 100644 index 0000000000..4cff92caf1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/DEPS @@ -0,0 +1,18 @@ +include_rules = [ + "+base", + "+components", + "+helpers", + "+sdk", + "+common_video/h264", + "+common_video/include", + "+common_video/libyuv/include", + "+logging/rtc_event_log/rtc_event_log_factory.h", + "+media", + "+modules/video_coding", + "+pc", + "+system_wrappers", + "+modules/audio_device", + "+modules/audio_processing", + "+native", + "+third_party/libyuv", +] diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/Common/NSString+StdString.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/Common/NSString+StdString.h new file mode 100644 index 0000000000..3ec1b613ef --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/Common/NSString+StdString.h @@ -0,0 +1,11 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "helpers/NSString+StdString.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/Common/scoped_cftyperef.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/Common/scoped_cftyperef.h new file mode 100644 index 0000000000..e5e376b0bc --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/Common/scoped_cftyperef.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import "helpers/scoped_cftyperef.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Native.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Native.h new file mode 100644 index 0000000000..529aa8dcf5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Native.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "api/peerconnection/RTCConfiguration+Native.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnectionFactory+Native.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnectionFactory+Native.h new file mode 100644 index 0000000000..222e06ef33 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnectionFactory+Native.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "api/peerconnection/RTCPeerConnectionFactory+Native.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/Video/RTCDefaultShader.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/Video/RTCDefaultShader.h new file mode 100644 index 0000000000..136d7003c6 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/Video/RTCDefaultShader.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "components/renderer/opengl/RTCDefaultShader.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/Video/RTCNV12TextureCache.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/Video/RTCNV12TextureCache.h new file mode 100644 index 0000000000..4ba1caa41d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/Video/RTCNV12TextureCache.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "components/renderer/opengl/RTCNV12TextureCache.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter.h b/third_party/libwebrtc/sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter.h new file mode 100644 index 0000000000..21281f36ac --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "components/video_codec/nalu_rewriter.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Native/api/audio_device_module.h b/third_party/libwebrtc/sdk/objc/Framework/Native/api/audio_device_module.h new file mode 100644 index 0000000000..7b448024de --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Native/api/audio_device_module.h @@ -0,0 +1,11 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "native/api/audio_device_module.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_decoder_factory.h b/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_decoder_factory.h new file mode 100644 index 0000000000..ca9371c54d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_decoder_factory.h @@ -0,0 +1,11 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "native/api/video_decoder_factory.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_encoder_factory.h b/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_encoder_factory.h new file mode 100644 index 0000000000..35e1e6c99f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_encoder_factory.h @@ -0,0 +1,11 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "native/api/video_encoder_factory.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_frame_buffer.h b/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_frame_buffer.h new file mode 100644 index 0000000000..0e862cfa07 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Native/api/video_frame_buffer.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "native/api/video_frame_buffer.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Native/src/objc_video_decoder_factory.h b/third_party/libwebrtc/sdk/objc/Framework/Native/src/objc_video_decoder_factory.h new file mode 100644 index 0000000000..bd8513c342 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Native/src/objc_video_decoder_factory.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "native/src/objc_video_decoder_factory.h" diff --git a/third_party/libwebrtc/sdk/objc/Framework/Native/src/objc_video_encoder_factory.h b/third_party/libwebrtc/sdk/objc/Framework/Native/src/objc_video_encoder_factory.h new file mode 100644 index 0000000000..b6bd650a4f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Framework/Native/src/objc_video_encoder_factory.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "native/src/objc_video_encoder_factory.h" diff --git a/third_party/libwebrtc/sdk/objc/Info.plist b/third_party/libwebrtc/sdk/objc/Info.plist new file mode 100644 index 0000000000..38c437e7fe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/Info.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>WebRTC</string> + <key>CFBundleIdentifier</key> + <string>org.webrtc.WebRTC</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>WebRTC</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>NSPrincipalClass</key> + <string></string> +</dict> +</plist> diff --git a/third_party/libwebrtc/sdk/objc/OWNERS b/third_party/libwebrtc/sdk/objc/OWNERS new file mode 100644 index 0000000000..6af9062b2d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/OWNERS @@ -0,0 +1,9 @@ +# Normal code changes. +kthelgason@webrtc.org +andersc@webrtc.org +peterhanspers@webrtc.org +denicija@webrtc.org + +# Rubberstamps of e.g. reverts and critical bug fixes. +magjed@webrtc.org +tkchin@webrtc.org diff --git a/third_party/libwebrtc/sdk/objc/README.md b/third_party/libwebrtc/sdk/objc/README.md new file mode 100644 index 0000000000..ff294a266f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/README.md @@ -0,0 +1,37 @@ +# WebRTC Obj-C SDK + +This directory contains the Obj-C SDK for WebRTC. This includes wrappers for the +C++ PeerConnection API and some platform specific components for iOS and macOS. + +## Organization + +- api/ + + Wrappers around classes and functions in the C++ API for creating and + configuring peer connections, etc. + +- base/ + + This directory contains some base protocols and classes that are used by both + the platform specific components and the SDK wrappers. + +- components/ + + These are the platform specific components. Contains components for handling + audio, capturing and rendering video, encoding and decoding using the + platform's hardware codec implementation and for representing video frames + in the platform's native format. + +- helpers/ + + These files are not WebRTC specific, but are general helper classes and + utilities for the Cocoa platforms. + +- native/ + + APIs for wrapping the platform specific components and using them with the + C++ API. + +- unittests/ + + This directory contains the tests. diff --git a/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter+Private.h b/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter+Private.h new file mode 100644 index 0000000000..9b123d2d05 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter+Private.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoRendererAdapter.h" + +#import "base/RTCVideoRenderer.h" + +#include "api/media_stream_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCVideoRendererAdapter () + +/** + * The Objective-C video renderer passed to this adapter during construction. + * Calls made to the webrtc::VideoRenderInterface will be adapted and passed to + * this video renderer. + */ +@property(nonatomic, readonly) id<RTC_OBJC_TYPE(RTCVideoRenderer)> videoRenderer; + +/** + * The native VideoSinkInterface surface exposed by this adapter. Calls made + * to this interface will be adapted and passed to the RTCVideoRenderer supplied + * during construction. This pointer is unsafe and owned by this class. + */ +@property(nonatomic, readonly) rtc::VideoSinkInterface<webrtc::VideoFrame> *nativeVideoRenderer; + +/** Initialize an RTCVideoRendererAdapter with an RTCVideoRenderer. */ +- (instancetype)initWithNativeRenderer:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)videoRenderer + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter.h b/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter.h new file mode 100644 index 0000000000..b0b6f04488 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter.h @@ -0,0 +1,27 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/* + * Creates a rtc::VideoSinkInterface surface for an RTCVideoRenderer. The + * rtc::VideoSinkInterface is used by WebRTC rendering code - this + * adapter adapts calls made to that interface to the RTCVideoRenderer supplied + * during construction. + */ +@interface RTCVideoRendererAdapter : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter.mm b/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter.mm new file mode 100644 index 0000000000..ef02f72f60 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/RTCVideoRendererAdapter.mm @@ -0,0 +1,67 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoRendererAdapter+Private.h" +#import "base/RTCVideoFrame.h" + +#include <memory> + +#include "sdk/objc/native/api/video_frame.h" + +namespace webrtc { + +class VideoRendererAdapter + : public rtc::VideoSinkInterface<webrtc::VideoFrame> { + public: + VideoRendererAdapter(RTCVideoRendererAdapter* adapter) { + adapter_ = adapter; + size_ = CGSizeZero; + } + + void OnFrame(const webrtc::VideoFrame& nativeVideoFrame) override { + RTC_OBJC_TYPE(RTCVideoFrame)* videoFrame = NativeToObjCVideoFrame(nativeVideoFrame); + + CGSize current_size = (videoFrame.rotation % 180 == 0) + ? CGSizeMake(videoFrame.width, videoFrame.height) + : CGSizeMake(videoFrame.height, videoFrame.width); + + if (!CGSizeEqualToSize(size_, current_size)) { + size_ = current_size; + [adapter_.videoRenderer setSize:size_]; + } + [adapter_.videoRenderer renderFrame:videoFrame]; + } + + private: + __weak RTCVideoRendererAdapter *adapter_; + CGSize size_; +}; +} + +@implementation RTCVideoRendererAdapter { + std::unique_ptr<webrtc::VideoRendererAdapter> _adapter; +} + +@synthesize videoRenderer = _videoRenderer; + +- (instancetype)initWithNativeRenderer:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)videoRenderer { + NSParameterAssert(videoRenderer); + if (self = [super init]) { + _videoRenderer = videoRenderer; + _adapter.reset(new webrtc::VideoRendererAdapter(self)); + } + return self; +} + +- (rtc::VideoSinkInterface<webrtc::VideoFrame> *)nativeVideoRenderer { + return _adapter.get(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/logging/RTCCallbackLogger.h b/third_party/libwebrtc/sdk/objc/api/logging/RTCCallbackLogger.h new file mode 100644 index 0000000000..c1aeb825cb --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/logging/RTCCallbackLogger.h @@ -0,0 +1,41 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCLogging.h" +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^RTCCallbackLoggerMessageHandler)(NSString *message); +typedef void (^RTCCallbackLoggerMessageAndSeverityHandler)(NSString *message, + RTCLoggingSeverity severity); + +// This class intercepts WebRTC logs and forwards them to a registered block. +// This class is not threadsafe. +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCCallbackLogger) : NSObject + +// The severity level to capture. The default is kRTCLoggingSeverityInfo. +@property(nonatomic, assign) RTCLoggingSeverity severity; + +// The callback handler will be called on the same thread that does the +// logging, so if the logging callback can be slow it may be a good idea +// to implement dispatching to some other queue. +- (void)start:(nullable RTCCallbackLoggerMessageHandler)handler; +- (void)startWithMessageAndSeverityHandler: + (nullable RTCCallbackLoggerMessageAndSeverityHandler)handler; + +- (void)stop; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/logging/RTCCallbackLogger.mm b/third_party/libwebrtc/sdk/objc/api/logging/RTCCallbackLogger.mm new file mode 100644 index 0000000000..ba6fe1b1cc --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/logging/RTCCallbackLogger.mm @@ -0,0 +1,151 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCallbackLogger.h" + +#import "helpers/NSString+StdString.h" + +#include <memory> + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/log_sinks.h" +#include "rtc_base/logging.h" + +namespace { + +class CallbackLogSink final : public rtc::LogSink { + public: + CallbackLogSink(RTCCallbackLoggerMessageHandler callbackHandler) + : callback_handler_(callbackHandler) {} + + void OnLogMessage(const std::string& message) override { + OnLogMessage(absl::string_view(message)); + } + + void OnLogMessage(absl::string_view message) override { + if (callback_handler_) { + callback_handler_([NSString stringForAbslStringView:message]); + } + } + + private: + RTCCallbackLoggerMessageHandler callback_handler_; +}; + +class CallbackWithSeverityLogSink final : public rtc::LogSink { + public: + CallbackWithSeverityLogSink(RTCCallbackLoggerMessageAndSeverityHandler callbackHandler) + : callback_handler_(callbackHandler) {} + + void OnLogMessage(const std::string& message) override { RTC_DCHECK_NOTREACHED(); } + + void OnLogMessage(const std::string& message, rtc::LoggingSeverity severity) override { + OnLogMessage(absl::string_view(message), severity); + } + + void OnLogMessage(absl::string_view message, rtc::LoggingSeverity severity) override { + if (callback_handler_) { + RTCLoggingSeverity loggingSeverity = NativeSeverityToObjcSeverity(severity); + callback_handler_([NSString stringForAbslStringView:message], loggingSeverity); + } + } + + private: + static RTCLoggingSeverity NativeSeverityToObjcSeverity(rtc::LoggingSeverity severity) { + switch (severity) { + case rtc::LS_VERBOSE: + return RTCLoggingSeverityVerbose; + case rtc::LS_INFO: + return RTCLoggingSeverityInfo; + case rtc::LS_WARNING: + return RTCLoggingSeverityWarning; + case rtc::LS_ERROR: + return RTCLoggingSeverityError; + case rtc::LS_NONE: + return RTCLoggingSeverityNone; + } + } + + RTCCallbackLoggerMessageAndSeverityHandler callback_handler_; +}; + +} + +@implementation RTC_OBJC_TYPE (RTCCallbackLogger) { + BOOL _hasStarted; + std::unique_ptr<rtc::LogSink> _logSink; +} + +@synthesize severity = _severity; + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _severity = RTCLoggingSeverityInfo; + } + return self; +} + +- (void)dealloc { + [self stop]; +} + +- (void)start:(nullable RTCCallbackLoggerMessageHandler)handler { + if (_hasStarted) { + return; + } + + _logSink.reset(new CallbackLogSink(handler)); + + rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); + _hasStarted = YES; +} + +- (void)startWithMessageAndSeverityHandler: + (nullable RTCCallbackLoggerMessageAndSeverityHandler)handler { + if (_hasStarted) { + return; + } + + _logSink.reset(new CallbackWithSeverityLogSink(handler)); + + rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); + _hasStarted = YES; +} + +- (void)stop { + if (!_hasStarted) { + return; + } + RTC_DCHECK(_logSink); + rtc::LogMessage::RemoveLogToStream(_logSink.get()); + _hasStarted = NO; + _logSink.reset(); +} + +#pragma mark - Private + +- (rtc::LoggingSeverity)rtcSeverity { + switch (_severity) { + case RTCLoggingSeverityVerbose: + return rtc::LS_VERBOSE; + case RTCLoggingSeverityInfo: + return rtc::LS_INFO; + case RTCLoggingSeverityWarning: + return rtc::LS_WARNING; + case RTCLoggingSeverityError: + return rtc::LS_ERROR; + case RTCLoggingSeverityNone: + return rtc::LS_NONE; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource+Private.h new file mode 100644 index 0000000000..2c333f9d73 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource+Private.h @@ -0,0 +1,34 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSource.h" + +#import "RTCMediaSource+Private.h" + +@interface RTC_OBJC_TYPE (RTCAudioSource) +() + + /** + * The AudioSourceInterface object passed to this RTCAudioSource during + * construction. + */ + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::AudioSourceInterface> nativeAudioSource; + +/** Initialize an RTCAudioSource from a native AudioSourceInterface. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeAudioSource:(rtc::scoped_refptr<webrtc::AudioSourceInterface>)nativeAudioSource + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaSource:(rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + type:(RTCMediaSourceType)type NS_UNAVAILABLE; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource.h new file mode 100644 index 0000000000..9272fdf2d8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource.h @@ -0,0 +1,32 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCMediaSource.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCAudioSource) : RTC_OBJC_TYPE(RTCMediaSource) + +- (instancetype)init NS_UNAVAILABLE; + +// Sets the volume for the RTCMediaSource. `volume` is a gain value in the range +// [0, 10]. +// Temporary fix to be able to modify volume of remote audio tracks. +// TODO(kthelgason): Property stays here temporarily until a proper volume-api +// is available on the surface exposed by webrtc. +@property(nonatomic, assign) double volume; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource.mm new file mode 100644 index 0000000000..1541045099 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioSource.mm @@ -0,0 +1,52 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSource+Private.h" + +#include "rtc_base/checks.h" + +@implementation RTC_OBJC_TYPE (RTCAudioSource) { +} + +@synthesize volume = _volume; +@synthesize nativeAudioSource = _nativeAudioSource; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeAudioSource: + (rtc::scoped_refptr<webrtc::AudioSourceInterface>)nativeAudioSource { + RTC_DCHECK(factory); + RTC_DCHECK(nativeAudioSource); + + if (self = [super initWithFactory:factory + nativeMediaSource:nativeAudioSource + type:RTCMediaSourceTypeAudio]) { + _nativeAudioSource = nativeAudioSource; + } + return self; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaSource:(rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + type:(RTCMediaSourceType)type { + RTC_DCHECK_NOTREACHED(); + return nil; +} + +- (NSString *)description { + NSString *stateString = [[self class] stringForState:self.state]; + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCAudioSource)( %p ): %@", self, stateString]; +} + +- (void)setVolume:(double)volume { + _volume = volume; + _nativeAudioSource->SetVolume(volume); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack+Private.h new file mode 100644 index 0000000000..6495500484 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack+Private.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioTrack.h" + +#include "api/media_stream_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); +@interface RTC_OBJC_TYPE (RTCAudioTrack) +() + + /** AudioTrackInterface created or passed in at construction. */ + @property(nonatomic, readonly) rtc::scoped_refptr<webrtc::AudioTrackInterface> nativeAudioTrack; + +/** Initialize an RTCAudioTrack with an id. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + source:(RTC_OBJC_TYPE(RTCAudioSource) *)source + trackId:(NSString *)trackId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack.h new file mode 100644 index 0000000000..95eb5d3d48 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack.h @@ -0,0 +1,28 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMacros.h" +#import "RTCMediaStreamTrack.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCAudioSource); + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCAudioTrack) : RTC_OBJC_TYPE(RTCMediaStreamTrack) + +- (instancetype)init NS_UNAVAILABLE; + +/** The audio source for this audio track. */ +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCAudioSource) * source; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack.mm new file mode 100644 index 0000000000..5c1736f436 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCAudioTrack.mm @@ -0,0 +1,67 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioTrack+Private.h" + +#import "RTCAudioSource+Private.h" +#import "RTCMediaStreamTrack+Private.h" +#import "RTCPeerConnectionFactory+Private.h" +#import "helpers/NSString+StdString.h" + +#include "rtc_base/checks.h" + +@implementation RTC_OBJC_TYPE (RTCAudioTrack) + +@synthesize source = _source; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + source:(RTC_OBJC_TYPE(RTCAudioSource) *)source + trackId:(NSString *)trackId { + RTC_DCHECK(factory); + RTC_DCHECK(source); + RTC_DCHECK(trackId.length); + + std::string nativeId = [NSString stdStringForString:trackId]; + rtc::scoped_refptr<webrtc::AudioTrackInterface> track = + factory.nativeFactory->CreateAudioTrack(nativeId, source.nativeAudioSource.get()); + if (self = [self initWithFactory:factory nativeTrack:track type:RTCMediaStreamTrackTypeAudio]) { + _source = source; + } + return self; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack + type:(RTCMediaStreamTrackType)type { + NSParameterAssert(factory); + NSParameterAssert(nativeTrack); + NSParameterAssert(type == RTCMediaStreamTrackTypeAudio); + return [super initWithFactory:factory nativeTrack:nativeTrack type:type]; +} + +- (RTC_OBJC_TYPE(RTCAudioSource) *)source { + if (!_source) { + rtc::scoped_refptr<webrtc::AudioSourceInterface> source(self.nativeAudioTrack->GetSource()); + if (source) { + _source = [[RTC_OBJC_TYPE(RTCAudioSource) alloc] initWithFactory:self.factory + nativeAudioSource:source]; + } + } + return _source; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::AudioTrackInterface>)nativeAudioTrack { + return rtc::scoped_refptr<webrtc::AudioTrackInterface>( + static_cast<webrtc::AudioTrackInterface *>(self.nativeTrack.get())); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCertificate.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCertificate.h new file mode 100644 index 0000000000..5ac8984d4a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCertificate.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCCertificate) : NSObject <NSCopying> + +/** Private key in PEM. */ +@property(nonatomic, readonly, copy) NSString *private_key; + +/** Public key in an x509 cert encoded in PEM. */ +@property(nonatomic, readonly, copy) NSString *certificate; + +/** + * Initialize an RTCCertificate with PEM strings for private_key and certificate. + */ +- (instancetype)initWithPrivateKey:(NSString *)private_key + certificate:(NSString *)certificate NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/** Generate a new certificate for 're' use. + * + * Optional dictionary of parameters. Defaults to KeyType ECDSA if none are + * provided. + * - name: "ECDSA" or "RSASSA-PKCS1-v1_5" + */ ++ (nullable RTC_OBJC_TYPE(RTCCertificate) *)generateCertificateWithParams:(NSDictionary *)params; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCertificate.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCertificate.mm new file mode 100644 index 0000000000..e5c33e407c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCertificate.mm @@ -0,0 +1,72 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCertificate.h" + +#import "base/RTCLogging.h" + +#include "rtc_base/logging.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/ssl_identity.h" + +@implementation RTC_OBJC_TYPE (RTCCertificate) + +@synthesize private_key = _private_key; +@synthesize certificate = _certificate; + +- (id)copyWithZone:(NSZone *)zone { + id copy = [[[self class] alloc] initWithPrivateKey:[self.private_key copyWithZone:zone] + certificate:[self.certificate copyWithZone:zone]]; + return copy; +} + +- (instancetype)initWithPrivateKey:(NSString *)private_key certificate:(NSString *)certificate { + if (self = [super init]) { + _private_key = [private_key copy]; + _certificate = [certificate copy]; + } + return self; +} + ++ (nullable RTC_OBJC_TYPE(RTCCertificate) *)generateCertificateWithParams:(NSDictionary *)params { + rtc::KeyType keyType = rtc::KT_ECDSA; + NSString *keyTypeString = [params valueForKey:@"name"]; + if (keyTypeString && [keyTypeString isEqualToString:@"RSASSA-PKCS1-v1_5"]) { + keyType = rtc::KT_RSA; + } + + NSNumber *expires = [params valueForKey:@"expires"]; + rtc::scoped_refptr<rtc::RTCCertificate> cc_certificate = nullptr; + if (expires != nil) { + uint64_t expirationTimestamp = [expires unsignedLongLongValue]; + cc_certificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType), + expirationTimestamp); + } else { + cc_certificate = + rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType), absl::nullopt); + } + if (!cc_certificate) { + RTCLogError(@"Failed to generate certificate."); + return nullptr; + } + // grab PEMs and create an NS RTCCerticicate + rtc::RTCCertificatePEM pem = cc_certificate->ToPEM(); + std::string pem_private_key = pem.private_key(); + std::string pem_certificate = pem.certificate(); + RTC_LOG(LS_INFO) << "CERT PEM "; + RTC_LOG(LS_INFO) << pem_certificate; + + RTC_OBJC_TYPE(RTCCertificate) *cert = + [[RTC_OBJC_TYPE(RTCCertificate) alloc] initWithPrivateKey:@(pem_private_key.c_str()) + certificate:@(pem_certificate.c_str())]; + return cert; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration+Native.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration+Native.h new file mode 100644 index 0000000000..07c0da6041 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration+Native.h @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCConfiguration.h" + +#include "api/peer_connection_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCConfiguration) +() + + /** Optional TurnCustomizer. + * With this class one can modify outgoing TURN messages. + * The object passed in must remain valid until PeerConnection::Close() is + * called. + */ + @property(nonatomic, nullable) webrtc::TurnCustomizer* turnCustomizer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration+Private.h new file mode 100644 index 0000000000..70a6532dbc --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration+Private.h @@ -0,0 +1,79 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCConfiguration.h" + +#include "api/peer_connection_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCConfiguration) +() + + + (webrtc::PeerConnectionInterface::IceTransportsType)nativeTransportsTypeForTransportPolicy + : (RTCIceTransportPolicy)policy; + ++ (RTCIceTransportPolicy)transportPolicyForTransportsType: + (webrtc::PeerConnectionInterface::IceTransportsType)nativeType; + ++ (NSString *)stringForTransportPolicy:(RTCIceTransportPolicy)policy; + ++ (webrtc::PeerConnectionInterface::BundlePolicy)nativeBundlePolicyForPolicy: + (RTCBundlePolicy)policy; + ++ (RTCBundlePolicy)bundlePolicyForNativePolicy: + (webrtc::PeerConnectionInterface::BundlePolicy)nativePolicy; + ++ (NSString *)stringForBundlePolicy:(RTCBundlePolicy)policy; + ++ (webrtc::PeerConnectionInterface::RtcpMuxPolicy)nativeRtcpMuxPolicyForPolicy: + (RTCRtcpMuxPolicy)policy; + ++ (RTCRtcpMuxPolicy)rtcpMuxPolicyForNativePolicy: + (webrtc::PeerConnectionInterface::RtcpMuxPolicy)nativePolicy; + ++ (NSString *)stringForRtcpMuxPolicy:(RTCRtcpMuxPolicy)policy; + ++ (webrtc::PeerConnectionInterface::TcpCandidatePolicy)nativeTcpCandidatePolicyForPolicy: + (RTCTcpCandidatePolicy)policy; + ++ (RTCTcpCandidatePolicy)tcpCandidatePolicyForNativePolicy: + (webrtc::PeerConnectionInterface::TcpCandidatePolicy)nativePolicy; + ++ (NSString *)stringForTcpCandidatePolicy:(RTCTcpCandidatePolicy)policy; + ++ (webrtc::PeerConnectionInterface::CandidateNetworkPolicy)nativeCandidateNetworkPolicyForPolicy: + (RTCCandidateNetworkPolicy)policy; + ++ (RTCCandidateNetworkPolicy)candidateNetworkPolicyForNativePolicy: + (webrtc::PeerConnectionInterface::CandidateNetworkPolicy)nativePolicy; + ++ (NSString *)stringForCandidateNetworkPolicy:(RTCCandidateNetworkPolicy)policy; + ++ (rtc::KeyType)nativeEncryptionKeyTypeForKeyType:(RTCEncryptionKeyType)keyType; + ++ (webrtc::SdpSemantics)nativeSdpSemanticsForSdpSemantics:(RTCSdpSemantics)sdpSemantics; + ++ (RTCSdpSemantics)sdpSemanticsForNativeSdpSemantics:(webrtc::SdpSemantics)sdpSemantics; + ++ (NSString *)stringForSdpSemantics:(RTCSdpSemantics)sdpSemantics; + +/** + * RTCConfiguration struct representation of this RTCConfiguration. + * This is needed to pass to the underlying C++ APIs. + */ +- (nullable webrtc::PeerConnectionInterface::RTCConfiguration *)createNativeConfiguration; + +- (instancetype)initWithNativeConfiguration: + (const webrtc::PeerConnectionInterface::RTCConfiguration &)config NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration.h new file mode 100644 index 0000000000..1b0d14baf1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration.h @@ -0,0 +1,268 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCCertificate.h" +#import "RTCCryptoOptions.h" +#import "RTCMacros.h" + +@class RTC_OBJC_TYPE(RTCIceServer); + +/** + * Represents the ice transport policy. This exposes the same states in C++, + * which include one more state than what exists in the W3C spec. + */ +typedef NS_ENUM(NSInteger, RTCIceTransportPolicy) { + RTCIceTransportPolicyNone, + RTCIceTransportPolicyRelay, + RTCIceTransportPolicyNoHost, + RTCIceTransportPolicyAll +}; + +/** Represents the bundle policy. */ +typedef NS_ENUM(NSInteger, RTCBundlePolicy) { + RTCBundlePolicyBalanced, + RTCBundlePolicyMaxCompat, + RTCBundlePolicyMaxBundle +}; + +/** Represents the rtcp mux policy. */ +typedef NS_ENUM(NSInteger, RTCRtcpMuxPolicy) { RTCRtcpMuxPolicyNegotiate, RTCRtcpMuxPolicyRequire }; + +/** Represents the tcp candidate policy. */ +typedef NS_ENUM(NSInteger, RTCTcpCandidatePolicy) { + RTCTcpCandidatePolicyEnabled, + RTCTcpCandidatePolicyDisabled +}; + +/** Represents the candidate network policy. */ +typedef NS_ENUM(NSInteger, RTCCandidateNetworkPolicy) { + RTCCandidateNetworkPolicyAll, + RTCCandidateNetworkPolicyLowCost +}; + +/** Represents the continual gathering policy. */ +typedef NS_ENUM(NSInteger, RTCContinualGatheringPolicy) { + RTCContinualGatheringPolicyGatherOnce, + RTCContinualGatheringPolicyGatherContinually +}; + +/** Represents the encryption key type. */ +typedef NS_ENUM(NSInteger, RTCEncryptionKeyType) { + RTCEncryptionKeyTypeRSA, + RTCEncryptionKeyTypeECDSA, +}; + +/** Represents the chosen SDP semantics for the RTCPeerConnection. */ +typedef NS_ENUM(NSInteger, RTCSdpSemantics) { + // TODO(https://crbug.com/webrtc/13528): Remove support for Plan B. + RTCSdpSemanticsPlanB, + RTCSdpSemanticsUnifiedPlan, +}; + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCConfiguration) : NSObject + +/** If true, allows DSCP codes to be set on outgoing packets, configured using + * networkPriority field of RTCRtpEncodingParameters. Defaults to false. + */ +@property(nonatomic, assign) BOOL enableDscp; + +/** An array of Ice Servers available to be used by ICE. */ +@property(nonatomic, copy) NSArray<RTC_OBJC_TYPE(RTCIceServer) *> *iceServers; + +/** An RTCCertificate for 're' use. */ +@property(nonatomic, nullable) RTC_OBJC_TYPE(RTCCertificate) * certificate; + +/** Which candidates the ICE agent is allowed to use. The W3C calls it + * `iceTransportPolicy`, while in C++ it is called `type`. */ +@property(nonatomic, assign) RTCIceTransportPolicy iceTransportPolicy; + +/** The media-bundling policy to use when gathering ICE candidates. */ +@property(nonatomic, assign) RTCBundlePolicy bundlePolicy; + +/** The rtcp-mux policy to use when gathering ICE candidates. */ +@property(nonatomic, assign) RTCRtcpMuxPolicy rtcpMuxPolicy; +@property(nonatomic, assign) RTCTcpCandidatePolicy tcpCandidatePolicy; +@property(nonatomic, assign) RTCCandidateNetworkPolicy candidateNetworkPolicy; +@property(nonatomic, assign) RTCContinualGatheringPolicy continualGatheringPolicy; + +/** If set to YES, don't gather IPv6 ICE candidates on Wi-Fi. + * Only intended to be used on specific devices. Certain phones disable IPv6 + * when the screen is turned off and it would be better to just disable the + * IPv6 ICE candidates on Wi-Fi in those cases. + * Default is NO. + */ +@property(nonatomic, assign) BOOL disableIPV6OnWiFi; + +/** By default, the PeerConnection will use a limited number of IPv6 network + * interfaces, in order to avoid too many ICE candidate pairs being created + * and delaying ICE completion. + * + * Can be set to INT_MAX to effectively disable the limit. + */ +@property(nonatomic, assign) int maxIPv6Networks; + +/** Exclude link-local network interfaces + * from considertaion for gathering ICE candidates. + * Defaults to NO. + */ +@property(nonatomic, assign) BOOL disableLinkLocalNetworks; + +@property(nonatomic, assign) int audioJitterBufferMaxPackets; +@property(nonatomic, assign) BOOL audioJitterBufferFastAccelerate; +@property(nonatomic, assign) int iceConnectionReceivingTimeout; +@property(nonatomic, assign) int iceBackupCandidatePairPingInterval; + +/** Key type used to generate SSL identity. Default is ECDSA. */ +@property(nonatomic, assign) RTCEncryptionKeyType keyType; + +/** ICE candidate pool size as defined in JSEP. Default is 0. */ +@property(nonatomic, assign) int iceCandidatePoolSize; + +/** Prune turn ports on the same network to the same turn server. + * Default is NO. + */ +@property(nonatomic, assign) BOOL shouldPruneTurnPorts; + +/** If set to YES, this means the ICE transport should presume TURN-to-TURN + * candidate pairs will succeed, even before a binding response is received. + */ +@property(nonatomic, assign) BOOL shouldPresumeWritableWhenFullyRelayed; + +/* This flag is only effective when `continualGatheringPolicy` is + * RTCContinualGatheringPolicyGatherContinually. + * + * If YES, after the ICE transport type is changed such that new types of + * ICE candidates are allowed by the new transport type, e.g. from + * RTCIceTransportPolicyRelay to RTCIceTransportPolicyAll, candidates that + * have been gathered by the ICE transport but not matching the previous + * transport type and as a result not observed by PeerConnectionDelegateAdapter, + * will be surfaced to the delegate. + */ +@property(nonatomic, assign) BOOL shouldSurfaceIceCandidatesOnIceTransportTypeChanged; + +/** If set to non-nil, controls the minimal interval between consecutive ICE + * check packets. + */ +@property(nonatomic, copy, nullable) NSNumber *iceCheckMinInterval; + +/** + * Configure the SDP semantics used by this PeerConnection. By default, this + * is RTCSdpSemanticsUnifiedPlan which is compliant to the WebRTC 1.0 + * specification. It is possible to overrwite this to the deprecated + * RTCSdpSemanticsPlanB SDP format, but note that RTCSdpSemanticsPlanB will be + * deleted at some future date, see https://crbug.com/webrtc/13528. + * + * RTCSdpSemanticsUnifiedPlan will cause RTCPeerConnection to create offers and + * answers with multiple m= sections where each m= section maps to one + * RTCRtpSender and one RTCRtpReceiver (an RTCRtpTransceiver), either both audio + * or both video. This will also cause RTCPeerConnection to ignore all but the + * first a=ssrc lines that form a Plan B stream. + * + * RTCSdpSemanticsPlanB will cause RTCPeerConnection to create offers and + * answers with at most one audio and one video m= section with multiple + * RTCRtpSenders and RTCRtpReceivers specified as multiple a=ssrc lines within + * the section. This will also cause RTCPeerConnection to ignore all but the + * first m= section of the same media type. + */ +@property(nonatomic, assign) RTCSdpSemantics sdpSemantics; + +/** Actively reset the SRTP parameters when the DTLS transports underneath are + * changed after offer/answer negotiation. This is only intended to be a + * workaround for crbug.com/835958 + */ +@property(nonatomic, assign) BOOL activeResetSrtpParams; + +/** If the remote side support mid-stream codec switches then allow encoder + * switching to be performed. + */ + +@property(nonatomic, assign) BOOL allowCodecSwitching; + +/** + * Defines advanced optional cryptographic settings related to SRTP and + * frame encryption for native WebRTC. Setting this will overwrite any + * options set through the PeerConnectionFactory (which is deprecated). + */ +@property(nonatomic, nullable) RTC_OBJC_TYPE(RTCCryptoOptions) * cryptoOptions; + +/** + * An optional string that will be attached to the TURN_ALLOCATE_REQUEST which + * which can be used to correlate client logs with backend logs. + */ +@property(nonatomic, nullable, copy) NSString *turnLoggingId; + +/** + * Time interval between audio RTCP reports. + */ +@property(nonatomic, assign) int rtcpAudioReportIntervalMs; + +/** + * Time interval between video RTCP reports. + */ +@property(nonatomic, assign) int rtcpVideoReportIntervalMs; + +/** + * Allow implicit rollback of local description when remote description + * conflicts with local description. + * See: https://w3c.github.io/webrtc-pc/#dom-peerconnection-setremotedescription + */ +@property(nonatomic, assign) BOOL enableImplicitRollback; + +/** + * Control if "a=extmap-allow-mixed" is included in the offer. + * See: https://www.chromestatus.com/feature/6269234631933952 + */ +@property(nonatomic, assign) BOOL offerExtmapAllowMixed; + +/** + * Defines the interval applied to ALL candidate pairs + * when ICE is strongly connected, and it overrides the + * default value of this interval in the ICE implementation; + */ +@property(nonatomic, copy, nullable) NSNumber *iceCheckIntervalStrongConnectivity; + +/** + * Defines the counterpart for ALL pairs when ICE is + * weakly connected, and it overrides the default value of + * this interval in the ICE implementation + */ +@property(nonatomic, copy, nullable) NSNumber *iceCheckIntervalWeakConnectivity; + +/** + * The min time period for which a candidate pair must wait for response to + * connectivity checks before it becomes unwritable. This parameter + * overrides the default value in the ICE implementation if set. + */ +@property(nonatomic, copy, nullable) NSNumber *iceUnwritableTimeout; + +/** + * The min number of connectivity checks that a candidate pair must sent + * without receiving response before it becomes unwritable. This parameter + * overrides the default value in the ICE implementation if set. + */ +@property(nonatomic, copy, nullable) NSNumber *iceUnwritableMinChecks; + +/** + * The min time period for which a candidate pair must wait for response to + * connectivity checks it becomes inactive. This parameter overrides the + * default value in the ICE implementation if set. + */ +@property(nonatomic, copy, nullable) NSNumber *iceInactiveTimeout; + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration.mm new file mode 100644 index 0000000000..8e42cb2a82 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCConfiguration.mm @@ -0,0 +1,544 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCConfiguration+Private.h" + +#include <memory> + +#import "RTCCertificate.h" +#import "RTCConfiguration+Native.h" +#import "RTCIceServer+Private.h" +#import "base/RTCLogging.h" + +#include "rtc_base/checks.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/ssl_identity.h" + +@implementation RTC_OBJC_TYPE (RTCConfiguration) + +@synthesize enableDscp = _enableDscp; +@synthesize iceServers = _iceServers; +@synthesize certificate = _certificate; +@synthesize iceTransportPolicy = _iceTransportPolicy; +@synthesize bundlePolicy = _bundlePolicy; +@synthesize rtcpMuxPolicy = _rtcpMuxPolicy; +@synthesize tcpCandidatePolicy = _tcpCandidatePolicy; +@synthesize candidateNetworkPolicy = _candidateNetworkPolicy; +@synthesize continualGatheringPolicy = _continualGatheringPolicy; +@synthesize disableIPV6OnWiFi = _disableIPV6OnWiFi; +@synthesize maxIPv6Networks = _maxIPv6Networks; +@synthesize disableLinkLocalNetworks = _disableLinkLocalNetworks; +@synthesize audioJitterBufferMaxPackets = _audioJitterBufferMaxPackets; +@synthesize audioJitterBufferFastAccelerate = _audioJitterBufferFastAccelerate; +@synthesize iceConnectionReceivingTimeout = _iceConnectionReceivingTimeout; +@synthesize iceBackupCandidatePairPingInterval = + _iceBackupCandidatePairPingInterval; +@synthesize keyType = _keyType; +@synthesize iceCandidatePoolSize = _iceCandidatePoolSize; +@synthesize shouldPruneTurnPorts = _shouldPruneTurnPorts; +@synthesize shouldPresumeWritableWhenFullyRelayed = + _shouldPresumeWritableWhenFullyRelayed; +@synthesize shouldSurfaceIceCandidatesOnIceTransportTypeChanged = + _shouldSurfaceIceCandidatesOnIceTransportTypeChanged; +@synthesize iceCheckMinInterval = _iceCheckMinInterval; +@synthesize sdpSemantics = _sdpSemantics; +@synthesize turnCustomizer = _turnCustomizer; +@synthesize activeResetSrtpParams = _activeResetSrtpParams; +@synthesize allowCodecSwitching = _allowCodecSwitching; +@synthesize cryptoOptions = _cryptoOptions; +@synthesize turnLoggingId = _turnLoggingId; +@synthesize rtcpAudioReportIntervalMs = _rtcpAudioReportIntervalMs; +@synthesize rtcpVideoReportIntervalMs = _rtcpVideoReportIntervalMs; +@synthesize enableImplicitRollback = _enableImplicitRollback; +@synthesize offerExtmapAllowMixed = _offerExtmapAllowMixed; +@synthesize iceCheckIntervalStrongConnectivity = _iceCheckIntervalStrongConnectivity; +@synthesize iceCheckIntervalWeakConnectivity = _iceCheckIntervalWeakConnectivity; +@synthesize iceUnwritableTimeout = _iceUnwritableTimeout; +@synthesize iceUnwritableMinChecks = _iceUnwritableMinChecks; +@synthesize iceInactiveTimeout = _iceInactiveTimeout; + +- (instancetype)init { + // Copy defaults. + webrtc::PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; + return [self initWithNativeConfiguration:config]; +} + +- (instancetype)initWithNativeConfiguration: + (const webrtc::PeerConnectionInterface::RTCConfiguration &)config { + if (self = [super init]) { + _enableDscp = config.dscp(); + NSMutableArray *iceServers = [NSMutableArray array]; + for (const webrtc::PeerConnectionInterface::IceServer& server : config.servers) { + RTC_OBJC_TYPE(RTCIceServer) *iceServer = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithNativeServer:server]; + [iceServers addObject:iceServer]; + } + _iceServers = iceServers; + if (!config.certificates.empty()) { + rtc::scoped_refptr<rtc::RTCCertificate> native_cert; + native_cert = config.certificates[0]; + rtc::RTCCertificatePEM native_pem = native_cert->ToPEM(); + _certificate = [[RTC_OBJC_TYPE(RTCCertificate) alloc] + initWithPrivateKey:@(native_pem.private_key().c_str()) + certificate:@(native_pem.certificate().c_str())]; + } + _iceTransportPolicy = + [[self class] transportPolicyForTransportsType:config.type]; + _bundlePolicy = + [[self class] bundlePolicyForNativePolicy:config.bundle_policy]; + _rtcpMuxPolicy = + [[self class] rtcpMuxPolicyForNativePolicy:config.rtcp_mux_policy]; + _tcpCandidatePolicy = [[self class] tcpCandidatePolicyForNativePolicy: + config.tcp_candidate_policy]; + _candidateNetworkPolicy = [[self class] + candidateNetworkPolicyForNativePolicy:config.candidate_network_policy]; + webrtc::PeerConnectionInterface::ContinualGatheringPolicy nativePolicy = + config.continual_gathering_policy; + _continualGatheringPolicy = [[self class] continualGatheringPolicyForNativePolicy:nativePolicy]; + _disableIPV6OnWiFi = config.disable_ipv6_on_wifi; + _maxIPv6Networks = config.max_ipv6_networks; + _disableLinkLocalNetworks = config.disable_link_local_networks; + _audioJitterBufferMaxPackets = config.audio_jitter_buffer_max_packets; + _audioJitterBufferFastAccelerate = config.audio_jitter_buffer_fast_accelerate; + _iceConnectionReceivingTimeout = config.ice_connection_receiving_timeout; + _iceBackupCandidatePairPingInterval = + config.ice_backup_candidate_pair_ping_interval; + _keyType = RTCEncryptionKeyTypeECDSA; + _iceCandidatePoolSize = config.ice_candidate_pool_size; + _shouldPruneTurnPorts = config.prune_turn_ports; + _shouldPresumeWritableWhenFullyRelayed = + config.presume_writable_when_fully_relayed; + _shouldSurfaceIceCandidatesOnIceTransportTypeChanged = + config.surface_ice_candidates_on_ice_transport_type_changed; + if (config.ice_check_min_interval) { + _iceCheckMinInterval = + [NSNumber numberWithInt:*config.ice_check_min_interval]; + } + _sdpSemantics = [[self class] sdpSemanticsForNativeSdpSemantics:config.sdp_semantics]; + _turnCustomizer = config.turn_customizer; + _activeResetSrtpParams = config.active_reset_srtp_params; + if (config.crypto_options) { + _cryptoOptions = [[RTC_OBJC_TYPE(RTCCryptoOptions) alloc] + initWithSrtpEnableGcmCryptoSuites:config.crypto_options->srtp + .enable_gcm_crypto_suites + srtpEnableAes128Sha1_32CryptoCipher:config.crypto_options->srtp + .enable_aes128_sha1_32_crypto_cipher + srtpEnableEncryptedRtpHeaderExtensions:config.crypto_options->srtp + .enable_encrypted_rtp_header_extensions + sframeRequireFrameEncryption:config.crypto_options->sframe + .require_frame_encryption]; + } + _turnLoggingId = [NSString stringWithUTF8String:config.turn_logging_id.c_str()]; + _rtcpAudioReportIntervalMs = config.audio_rtcp_report_interval_ms(); + _rtcpVideoReportIntervalMs = config.video_rtcp_report_interval_ms(); + _allowCodecSwitching = config.allow_codec_switching.value_or(false); + _enableImplicitRollback = config.enable_implicit_rollback; + _offerExtmapAllowMixed = config.offer_extmap_allow_mixed; + _iceCheckIntervalStrongConnectivity = + config.ice_check_interval_strong_connectivity.has_value() ? + [NSNumber numberWithInt:*config.ice_check_interval_strong_connectivity] : + nil; + _iceCheckIntervalWeakConnectivity = config.ice_check_interval_weak_connectivity.has_value() ? + [NSNumber numberWithInt:*config.ice_check_interval_weak_connectivity] : + nil; + _iceUnwritableTimeout = config.ice_unwritable_timeout.has_value() ? + [NSNumber numberWithInt:*config.ice_unwritable_timeout] : + nil; + _iceUnwritableMinChecks = config.ice_unwritable_min_checks.has_value() ? + [NSNumber numberWithInt:*config.ice_unwritable_min_checks] : + nil; + _iceInactiveTimeout = config.ice_inactive_timeout.has_value() ? + [NSNumber numberWithInt:*config.ice_inactive_timeout] : + nil; + } + return self; +} + +- (NSString *)description { + static NSString *formatString = @"RTC_OBJC_TYPE(RTCConfiguration): " + @"{\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n" + @"%d\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n}\n"; + + return [NSString + stringWithFormat:formatString, + _iceServers, + [[self class] stringForTransportPolicy:_iceTransportPolicy], + [[self class] stringForBundlePolicy:_bundlePolicy], + [[self class] stringForRtcpMuxPolicy:_rtcpMuxPolicy], + [[self class] stringForTcpCandidatePolicy:_tcpCandidatePolicy], + [[self class] stringForCandidateNetworkPolicy:_candidateNetworkPolicy], + [[self class] stringForContinualGatheringPolicy:_continualGatheringPolicy], + [[self class] stringForSdpSemantics:_sdpSemantics], + _audioJitterBufferMaxPackets, + _audioJitterBufferFastAccelerate, + _iceConnectionReceivingTimeout, + _iceBackupCandidatePairPingInterval, + _iceCandidatePoolSize, + _shouldPruneTurnPorts, + _shouldPresumeWritableWhenFullyRelayed, + _shouldSurfaceIceCandidatesOnIceTransportTypeChanged, + _iceCheckMinInterval, + _disableLinkLocalNetworks, + _disableIPV6OnWiFi, + _maxIPv6Networks, + _activeResetSrtpParams, + _enableDscp, + _enableImplicitRollback]; +} + +#pragma mark - Private + +- (webrtc::PeerConnectionInterface::RTCConfiguration *) + createNativeConfiguration { + std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> + nativeConfig(new webrtc::PeerConnectionInterface::RTCConfiguration( + webrtc::PeerConnectionInterface::RTCConfigurationType::kAggressive)); + + nativeConfig->set_dscp(_enableDscp); + for (RTC_OBJC_TYPE(RTCIceServer) * iceServer in _iceServers) { + nativeConfig->servers.push_back(iceServer.nativeServer); + } + nativeConfig->type = + [[self class] nativeTransportsTypeForTransportPolicy:_iceTransportPolicy]; + nativeConfig->bundle_policy = + [[self class] nativeBundlePolicyForPolicy:_bundlePolicy]; + nativeConfig->rtcp_mux_policy = + [[self class] nativeRtcpMuxPolicyForPolicy:_rtcpMuxPolicy]; + nativeConfig->tcp_candidate_policy = + [[self class] nativeTcpCandidatePolicyForPolicy:_tcpCandidatePolicy]; + nativeConfig->candidate_network_policy = [[self class] + nativeCandidateNetworkPolicyForPolicy:_candidateNetworkPolicy]; + nativeConfig->continual_gathering_policy = + [[self class] nativeContinualGatheringPolicyForPolicy:_continualGatheringPolicy]; + nativeConfig->disable_ipv6_on_wifi = _disableIPV6OnWiFi; + nativeConfig->max_ipv6_networks = _maxIPv6Networks; + nativeConfig->disable_link_local_networks = _disableLinkLocalNetworks; + nativeConfig->audio_jitter_buffer_max_packets = _audioJitterBufferMaxPackets; + nativeConfig->audio_jitter_buffer_fast_accelerate = + _audioJitterBufferFastAccelerate ? true : false; + nativeConfig->ice_connection_receiving_timeout = + _iceConnectionReceivingTimeout; + nativeConfig->ice_backup_candidate_pair_ping_interval = + _iceBackupCandidatePairPingInterval; + rtc::KeyType keyType = + [[self class] nativeEncryptionKeyTypeForKeyType:_keyType]; + if (_certificate != nullptr) { + // if offered a pemcert use it... + RTC_LOG(LS_INFO) << "Have configured cert - using it."; + std::string pem_private_key = [[_certificate private_key] UTF8String]; + std::string pem_certificate = [[_certificate certificate] UTF8String]; + rtc::RTCCertificatePEM pem = rtc::RTCCertificatePEM(pem_private_key, pem_certificate); + rtc::scoped_refptr<rtc::RTCCertificate> certificate = rtc::RTCCertificate::FromPEM(pem); + RTC_LOG(LS_INFO) << "Created cert from PEM strings."; + if (!certificate) { + RTC_LOG(LS_ERROR) << "Failed to generate certificate from PEM."; + return nullptr; + } + nativeConfig->certificates.push_back(certificate); + } else { + RTC_LOG(LS_INFO) << "Don't have configured cert."; + // Generate non-default certificate. + if (keyType != rtc::KT_DEFAULT) { + rtc::scoped_refptr<rtc::RTCCertificate> certificate = + rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType), + absl::optional<uint64_t>()); + if (!certificate) { + RTCLogError(@"Failed to generate certificate."); + return nullptr; + } + nativeConfig->certificates.push_back(certificate); + } + } + nativeConfig->ice_candidate_pool_size = _iceCandidatePoolSize; + nativeConfig->prune_turn_ports = _shouldPruneTurnPorts ? true : false; + nativeConfig->presume_writable_when_fully_relayed = + _shouldPresumeWritableWhenFullyRelayed ? true : false; + nativeConfig->surface_ice_candidates_on_ice_transport_type_changed = + _shouldSurfaceIceCandidatesOnIceTransportTypeChanged ? true : false; + if (_iceCheckMinInterval != nil) { + nativeConfig->ice_check_min_interval = absl::optional<int>(_iceCheckMinInterval.intValue); + } + nativeConfig->sdp_semantics = [[self class] nativeSdpSemanticsForSdpSemantics:_sdpSemantics]; + if (_turnCustomizer) { + nativeConfig->turn_customizer = _turnCustomizer; + } + nativeConfig->active_reset_srtp_params = _activeResetSrtpParams ? true : false; + if (_cryptoOptions) { + webrtc::CryptoOptions nativeCryptoOptions; + nativeCryptoOptions.srtp.enable_gcm_crypto_suites = + _cryptoOptions.srtpEnableGcmCryptoSuites ? true : false; + nativeCryptoOptions.srtp.enable_aes128_sha1_32_crypto_cipher = + _cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher ? true : false; + nativeCryptoOptions.srtp.enable_encrypted_rtp_header_extensions = + _cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions ? true : false; + nativeCryptoOptions.sframe.require_frame_encryption = + _cryptoOptions.sframeRequireFrameEncryption ? true : false; + nativeConfig->crypto_options = absl::optional<webrtc::CryptoOptions>(nativeCryptoOptions); + } + nativeConfig->turn_logging_id = [_turnLoggingId UTF8String]; + nativeConfig->set_audio_rtcp_report_interval_ms(_rtcpAudioReportIntervalMs); + nativeConfig->set_video_rtcp_report_interval_ms(_rtcpVideoReportIntervalMs); + nativeConfig->allow_codec_switching = _allowCodecSwitching; + nativeConfig->enable_implicit_rollback = _enableImplicitRollback; + nativeConfig->offer_extmap_allow_mixed = _offerExtmapAllowMixed; + if (_iceCheckIntervalStrongConnectivity != nil) { + nativeConfig->ice_check_interval_strong_connectivity = + absl::optional<int>(_iceCheckIntervalStrongConnectivity.intValue); + } + if (_iceCheckIntervalWeakConnectivity != nil) { + nativeConfig->ice_check_interval_weak_connectivity = + absl::optional<int>(_iceCheckIntervalWeakConnectivity.intValue); + } + if (_iceUnwritableTimeout != nil) { + nativeConfig->ice_unwritable_timeout = absl::optional<int>(_iceUnwritableTimeout.intValue); + } + if (_iceUnwritableMinChecks != nil) { + nativeConfig->ice_unwritable_min_checks = absl::optional<int>(_iceUnwritableMinChecks.intValue); + } + if (_iceInactiveTimeout != nil) { + nativeConfig->ice_inactive_timeout = absl::optional<int>(_iceInactiveTimeout.intValue); + } + return nativeConfig.release(); +} + ++ (webrtc::PeerConnectionInterface::IceTransportsType) + nativeTransportsTypeForTransportPolicy:(RTCIceTransportPolicy)policy { + switch (policy) { + case RTCIceTransportPolicyNone: + return webrtc::PeerConnectionInterface::kNone; + case RTCIceTransportPolicyRelay: + return webrtc::PeerConnectionInterface::kRelay; + case RTCIceTransportPolicyNoHost: + return webrtc::PeerConnectionInterface::kNoHost; + case RTCIceTransportPolicyAll: + return webrtc::PeerConnectionInterface::kAll; + } +} + ++ (RTCIceTransportPolicy)transportPolicyForTransportsType: + (webrtc::PeerConnectionInterface::IceTransportsType)nativeType { + switch (nativeType) { + case webrtc::PeerConnectionInterface::kNone: + return RTCIceTransportPolicyNone; + case webrtc::PeerConnectionInterface::kRelay: + return RTCIceTransportPolicyRelay; + case webrtc::PeerConnectionInterface::kNoHost: + return RTCIceTransportPolicyNoHost; + case webrtc::PeerConnectionInterface::kAll: + return RTCIceTransportPolicyAll; + } +} + ++ (NSString *)stringForTransportPolicy:(RTCIceTransportPolicy)policy { + switch (policy) { + case RTCIceTransportPolicyNone: + return @"NONE"; + case RTCIceTransportPolicyRelay: + return @"RELAY"; + case RTCIceTransportPolicyNoHost: + return @"NO_HOST"; + case RTCIceTransportPolicyAll: + return @"ALL"; + } +} + ++ (webrtc::PeerConnectionInterface::BundlePolicy)nativeBundlePolicyForPolicy: + (RTCBundlePolicy)policy { + switch (policy) { + case RTCBundlePolicyBalanced: + return webrtc::PeerConnectionInterface::kBundlePolicyBalanced; + case RTCBundlePolicyMaxCompat: + return webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat; + case RTCBundlePolicyMaxBundle: + return webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle; + } +} + ++ (RTCBundlePolicy)bundlePolicyForNativePolicy: + (webrtc::PeerConnectionInterface::BundlePolicy)nativePolicy { + switch (nativePolicy) { + case webrtc::PeerConnectionInterface::kBundlePolicyBalanced: + return RTCBundlePolicyBalanced; + case webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat: + return RTCBundlePolicyMaxCompat; + case webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle: + return RTCBundlePolicyMaxBundle; + } +} + ++ (NSString *)stringForBundlePolicy:(RTCBundlePolicy)policy { + switch (policy) { + case RTCBundlePolicyBalanced: + return @"BALANCED"; + case RTCBundlePolicyMaxCompat: + return @"MAX_COMPAT"; + case RTCBundlePolicyMaxBundle: + return @"MAX_BUNDLE"; + } +} + ++ (webrtc::PeerConnectionInterface::RtcpMuxPolicy)nativeRtcpMuxPolicyForPolicy: + (RTCRtcpMuxPolicy)policy { + switch (policy) { + case RTCRtcpMuxPolicyNegotiate: + return webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate; + case RTCRtcpMuxPolicyRequire: + return webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire; + } +} + ++ (RTCRtcpMuxPolicy)rtcpMuxPolicyForNativePolicy: + (webrtc::PeerConnectionInterface::RtcpMuxPolicy)nativePolicy { + switch (nativePolicy) { + case webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate: + return RTCRtcpMuxPolicyNegotiate; + case webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire: + return RTCRtcpMuxPolicyRequire; + } +} + ++ (NSString *)stringForRtcpMuxPolicy:(RTCRtcpMuxPolicy)policy { + switch (policy) { + case RTCRtcpMuxPolicyNegotiate: + return @"NEGOTIATE"; + case RTCRtcpMuxPolicyRequire: + return @"REQUIRE"; + } +} + ++ (webrtc::PeerConnectionInterface::TcpCandidatePolicy) + nativeTcpCandidatePolicyForPolicy:(RTCTcpCandidatePolicy)policy { + switch (policy) { + case RTCTcpCandidatePolicyEnabled: + return webrtc::PeerConnectionInterface::kTcpCandidatePolicyEnabled; + case RTCTcpCandidatePolicyDisabled: + return webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled; + } +} + ++ (webrtc::PeerConnectionInterface::CandidateNetworkPolicy) + nativeCandidateNetworkPolicyForPolicy:(RTCCandidateNetworkPolicy)policy { + switch (policy) { + case RTCCandidateNetworkPolicyAll: + return webrtc::PeerConnectionInterface::kCandidateNetworkPolicyAll; + case RTCCandidateNetworkPolicyLowCost: + return webrtc::PeerConnectionInterface::kCandidateNetworkPolicyLowCost; + } +} + ++ (RTCTcpCandidatePolicy)tcpCandidatePolicyForNativePolicy: + (webrtc::PeerConnectionInterface::TcpCandidatePolicy)nativePolicy { + switch (nativePolicy) { + case webrtc::PeerConnectionInterface::kTcpCandidatePolicyEnabled: + return RTCTcpCandidatePolicyEnabled; + case webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled: + return RTCTcpCandidatePolicyDisabled; + } +} + ++ (NSString *)stringForTcpCandidatePolicy:(RTCTcpCandidatePolicy)policy { + switch (policy) { + case RTCTcpCandidatePolicyEnabled: + return @"TCP_ENABLED"; + case RTCTcpCandidatePolicyDisabled: + return @"TCP_DISABLED"; + } +} + ++ (RTCCandidateNetworkPolicy)candidateNetworkPolicyForNativePolicy: + (webrtc::PeerConnectionInterface::CandidateNetworkPolicy)nativePolicy { + switch (nativePolicy) { + case webrtc::PeerConnectionInterface::kCandidateNetworkPolicyAll: + return RTCCandidateNetworkPolicyAll; + case webrtc::PeerConnectionInterface::kCandidateNetworkPolicyLowCost: + return RTCCandidateNetworkPolicyLowCost; + } +} + ++ (NSString *)stringForCandidateNetworkPolicy: + (RTCCandidateNetworkPolicy)policy { + switch (policy) { + case RTCCandidateNetworkPolicyAll: + return @"CANDIDATE_ALL_NETWORKS"; + case RTCCandidateNetworkPolicyLowCost: + return @"CANDIDATE_LOW_COST_NETWORKS"; + } +} + ++ (webrtc::PeerConnectionInterface::ContinualGatheringPolicy) + nativeContinualGatheringPolicyForPolicy: + (RTCContinualGatheringPolicy)policy { + switch (policy) { + case RTCContinualGatheringPolicyGatherOnce: + return webrtc::PeerConnectionInterface::GATHER_ONCE; + case RTCContinualGatheringPolicyGatherContinually: + return webrtc::PeerConnectionInterface::GATHER_CONTINUALLY; + } +} + ++ (RTCContinualGatheringPolicy)continualGatheringPolicyForNativePolicy: + (webrtc::PeerConnectionInterface::ContinualGatheringPolicy)nativePolicy { + switch (nativePolicy) { + case webrtc::PeerConnectionInterface::GATHER_ONCE: + return RTCContinualGatheringPolicyGatherOnce; + case webrtc::PeerConnectionInterface::GATHER_CONTINUALLY: + return RTCContinualGatheringPolicyGatherContinually; + } +} + ++ (NSString *)stringForContinualGatheringPolicy: + (RTCContinualGatheringPolicy)policy { + switch (policy) { + case RTCContinualGatheringPolicyGatherOnce: + return @"GATHER_ONCE"; + case RTCContinualGatheringPolicyGatherContinually: + return @"GATHER_CONTINUALLY"; + } +} + ++ (rtc::KeyType)nativeEncryptionKeyTypeForKeyType: + (RTCEncryptionKeyType)keyType { + switch (keyType) { + case RTCEncryptionKeyTypeRSA: + return rtc::KT_RSA; + case RTCEncryptionKeyTypeECDSA: + return rtc::KT_ECDSA; + } +} + ++ (webrtc::SdpSemantics)nativeSdpSemanticsForSdpSemantics:(RTCSdpSemantics)sdpSemantics { + switch (sdpSemantics) { + case RTCSdpSemanticsPlanB: + return webrtc::SdpSemantics::kPlanB_DEPRECATED; + case RTCSdpSemanticsUnifiedPlan: + return webrtc::SdpSemantics::kUnifiedPlan; + } +} + ++ (RTCSdpSemantics)sdpSemanticsForNativeSdpSemantics:(webrtc::SdpSemantics)sdpSemantics { + switch (sdpSemantics) { + case webrtc::SdpSemantics::kPlanB_DEPRECATED: + return RTCSdpSemanticsPlanB; + case webrtc::SdpSemantics::kUnifiedPlan: + return RTCSdpSemanticsUnifiedPlan; + } +} + ++ (NSString *)stringForSdpSemantics:(RTCSdpSemantics)sdpSemantics { + switch (sdpSemantics) { + case RTCSdpSemanticsPlanB: + return @"PLAN_B"; + case RTCSdpSemanticsUnifiedPlan: + return @"UNIFIED_PLAN"; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCryptoOptions.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCryptoOptions.h new file mode 100644 index 0000000000..7894c8d50c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCryptoOptions.h @@ -0,0 +1,63 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Objective-C bindings for webrtc::CryptoOptions. This API had to be flattened + * as Objective-C doesn't support nested structures. + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCCryptoOptions) : NSObject + +/** + * Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used + * if both sides enable it + */ +@property(nonatomic, assign) BOOL srtpEnableGcmCryptoSuites; +/** + * If set to true, the (potentially insecure) crypto cipher + * kSrtpAes128CmSha1_32 will be included in the list of supported ciphers + * during negotiation. It will only be used if both peers support it and no + * other ciphers get preferred. + */ +@property(nonatomic, assign) BOOL srtpEnableAes128Sha1_32CryptoCipher; +/** + * If set to true, encrypted RTP header extensions as defined in RFC 6904 + * will be negotiated. They will only be used if both peers support them. + */ +@property(nonatomic, assign) BOOL srtpEnableEncryptedRtpHeaderExtensions; + +/** + * If set all RtpSenders must have an FrameEncryptor attached to them before + * they are allowed to send packets. All RtpReceivers must have a + * FrameDecryptor attached to them before they are able to receive packets. + */ +@property(nonatomic, assign) BOOL sframeRequireFrameEncryption; + +/** + * Initializes CryptoOptions with all possible options set explicitly. This + * is done when converting from a native RTCConfiguration.crypto_options. + */ +- (instancetype)initWithSrtpEnableGcmCryptoSuites:(BOOL)srtpEnableGcmCryptoSuites + srtpEnableAes128Sha1_32CryptoCipher:(BOOL)srtpEnableAes128Sha1_32CryptoCipher + srtpEnableEncryptedRtpHeaderExtensions:(BOOL)srtpEnableEncryptedRtpHeaderExtensions + sframeRequireFrameEncryption:(BOOL)sframeRequireFrameEncryption + NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCryptoOptions.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCryptoOptions.mm new file mode 100644 index 0000000000..fbaa1de58d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCCryptoOptions.mm @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCryptoOptions.h" + +@implementation RTC_OBJC_TYPE (RTCCryptoOptions) + +@synthesize srtpEnableGcmCryptoSuites = _srtpEnableGcmCryptoSuites; +@synthesize srtpEnableAes128Sha1_32CryptoCipher = _srtpEnableAes128Sha1_32CryptoCipher; +@synthesize srtpEnableEncryptedRtpHeaderExtensions = _srtpEnableEncryptedRtpHeaderExtensions; +@synthesize sframeRequireFrameEncryption = _sframeRequireFrameEncryption; + +- (instancetype)initWithSrtpEnableGcmCryptoSuites:(BOOL)srtpEnableGcmCryptoSuites + srtpEnableAes128Sha1_32CryptoCipher:(BOOL)srtpEnableAes128Sha1_32CryptoCipher + srtpEnableEncryptedRtpHeaderExtensions:(BOOL)srtpEnableEncryptedRtpHeaderExtensions + sframeRequireFrameEncryption:(BOOL)sframeRequireFrameEncryption { + if (self = [super init]) { + _srtpEnableGcmCryptoSuites = srtpEnableGcmCryptoSuites; + _srtpEnableAes128Sha1_32CryptoCipher = srtpEnableAes128Sha1_32CryptoCipher; + _srtpEnableEncryptedRtpHeaderExtensions = srtpEnableEncryptedRtpHeaderExtensions; + _sframeRequireFrameEncryption = sframeRequireFrameEncryption; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel+Private.h new file mode 100644 index 0000000000..2cdbdabec6 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel+Private.h @@ -0,0 +1,52 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDataChannel.h" + +#include "api/data_channel_interface.h" +#include "api/scoped_refptr.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); + +@interface RTC_OBJC_TYPE (RTCDataBuffer) +() + + /** + * The native DataBuffer representation of this RTCDatabuffer object. This is + * needed to pass to the underlying C++ APIs. + */ + @property(nonatomic, readonly) const webrtc::DataBuffer *nativeDataBuffer; + +/** Initialize an RTCDataBuffer from a native DataBuffer. */ +- (instancetype)initWithNativeBuffer:(const webrtc::DataBuffer &)nativeBuffer; + +@end + +@interface RTC_OBJC_TYPE (RTCDataChannel) +() + + /** Initialize an RTCDataChannel from a native DataChannelInterface. */ + - (instancetype)initWithFactory + : (RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory nativeDataChannel + : (rtc::scoped_refptr<webrtc::DataChannelInterface>)nativeDataChannel NS_DESIGNATED_INITIALIZER; + ++ (webrtc::DataChannelInterface::DataState)nativeDataChannelStateForState: + (RTCDataChannelState)state; + ++ (RTCDataChannelState)dataChannelStateForNativeState: + (webrtc::DataChannelInterface::DataState)nativeState; + ++ (NSString *)stringForState:(RTCDataChannelState)state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel.h new file mode 100644 index 0000000000..89eb58bc3f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel.h @@ -0,0 +1,132 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AvailabilityMacros.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCDataBuffer) : NSObject + +/** NSData representation of the underlying buffer. */ +@property(nonatomic, readonly) NSData *data; + +/** Indicates whether `data` contains UTF-8 or binary data. */ +@property(nonatomic, readonly) BOOL isBinary; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initialize an RTCDataBuffer from NSData. `isBinary` indicates whether `data` + * contains UTF-8 or binary data. + */ +- (instancetype)initWithData:(NSData *)data isBinary:(BOOL)isBinary; + +@end + +@class RTC_OBJC_TYPE(RTCDataChannel); +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCDataChannelDelegate)<NSObject> + + /** The data channel state changed. */ + - (void)dataChannelDidChangeState : (RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel; + +/** The data channel successfully received a data buffer. */ +- (void)dataChannel:(RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel + didReceiveMessageWithBuffer:(RTC_OBJC_TYPE(RTCDataBuffer) *)buffer; + +@optional +/** The data channel's `bufferedAmount` changed. */ +- (void)dataChannel:(RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel + didChangeBufferedAmount:(uint64_t)amount; + +@end + +/** Represents the state of the data channel. */ +typedef NS_ENUM(NSInteger, RTCDataChannelState) { + RTCDataChannelStateConnecting, + RTCDataChannelStateOpen, + RTCDataChannelStateClosing, + RTCDataChannelStateClosed, +}; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCDataChannel) : NSObject + +/** + * A label that can be used to distinguish this data channel from other data + * channel objects. + */ +@property(nonatomic, readonly) NSString *label; + +/** Whether the data channel can send messages in unreliable mode. */ +@property(nonatomic, readonly) BOOL isReliable DEPRECATED_ATTRIBUTE; + +/** Returns whether this data channel is ordered or not. */ +@property(nonatomic, readonly) BOOL isOrdered; + +/** Deprecated. Use maxPacketLifeTime. */ +@property(nonatomic, readonly) NSUInteger maxRetransmitTime DEPRECATED_ATTRIBUTE; + +/** + * The length of the time window (in milliseconds) during which transmissions + * and retransmissions may occur in unreliable mode. + */ +@property(nonatomic, readonly) uint16_t maxPacketLifeTime; + +/** + * The maximum number of retransmissions that are attempted in unreliable mode. + */ +@property(nonatomic, readonly) uint16_t maxRetransmits; + +/** + * The name of the sub-protocol used with this data channel, if any. Otherwise + * this returns an empty string. + */ +@property(nonatomic, readonly) NSString *protocol; + +/** + * Returns whether this data channel was negotiated by the application or not. + */ +@property(nonatomic, readonly) BOOL isNegotiated; + +/** Deprecated. Use channelId. */ +@property(nonatomic, readonly) NSInteger streamId DEPRECATED_ATTRIBUTE; + +/** The identifier for this data channel. */ +@property(nonatomic, readonly) int channelId; + +/** The state of the data channel. */ +@property(nonatomic, readonly) RTCDataChannelState readyState; + +/** + * The number of bytes of application data that have been queued using + * `sendData:` but that have not yet been transmitted to the network. + */ +@property(nonatomic, readonly) uint64_t bufferedAmount; + +/** The delegate for this data channel. */ +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCDataChannelDelegate)> delegate; + +- (instancetype)init NS_UNAVAILABLE; + +/** Closes the data channel. */ +- (void)close; + +/** Attempt to send `data` on this data channel's underlying data transport. */ +- (BOOL)sendData:(RTC_OBJC_TYPE(RTCDataBuffer) *)data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel.mm new file mode 100644 index 0000000000..4a79cefdb4 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannel.mm @@ -0,0 +1,220 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDataChannel+Private.h" + +#import "helpers/NSString+StdString.h" + +#include <memory> + +namespace webrtc { + +class DataChannelDelegateAdapter : public DataChannelObserver { + public: + DataChannelDelegateAdapter(RTC_OBJC_TYPE(RTCDataChannel) * channel) { channel_ = channel; } + + void OnStateChange() override { + [channel_.delegate dataChannelDidChangeState:channel_]; + } + + void OnMessage(const DataBuffer& buffer) override { + RTC_OBJC_TYPE(RTCDataBuffer) *data_buffer = + [[RTC_OBJC_TYPE(RTCDataBuffer) alloc] initWithNativeBuffer:buffer]; + [channel_.delegate dataChannel:channel_ + didReceiveMessageWithBuffer:data_buffer]; + } + + void OnBufferedAmountChange(uint64_t previousAmount) override { + id<RTC_OBJC_TYPE(RTCDataChannelDelegate)> delegate = channel_.delegate; + SEL sel = @selector(dataChannel:didChangeBufferedAmount:); + if ([delegate respondsToSelector:sel]) { + [delegate dataChannel:channel_ didChangeBufferedAmount:previousAmount]; + } + } + + private: + __weak RTC_OBJC_TYPE(RTCDataChannel) * channel_; +}; +} + +@implementation RTC_OBJC_TYPE (RTCDataBuffer) { + std::unique_ptr<webrtc::DataBuffer> _dataBuffer; +} + +- (instancetype)initWithData:(NSData *)data isBinary:(BOOL)isBinary { + NSParameterAssert(data); + if (self = [super init]) { + rtc::CopyOnWriteBuffer buffer( + reinterpret_cast<const uint8_t*>(data.bytes), data.length); + _dataBuffer.reset(new webrtc::DataBuffer(buffer, isBinary)); + } + return self; +} + +- (NSData *)data { + return [NSData dataWithBytes:_dataBuffer->data.data() + length:_dataBuffer->data.size()]; +} + +- (BOOL)isBinary { + return _dataBuffer->binary; +} + +#pragma mark - Private + +- (instancetype)initWithNativeBuffer:(const webrtc::DataBuffer&)nativeBuffer { + if (self = [super init]) { + _dataBuffer.reset(new webrtc::DataBuffer(nativeBuffer)); + } + return self; +} + +- (const webrtc::DataBuffer *)nativeDataBuffer { + return _dataBuffer.get(); +} + +@end + +@implementation RTC_OBJC_TYPE (RTCDataChannel) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + rtc::scoped_refptr<webrtc::DataChannelInterface> _nativeDataChannel; + std::unique_ptr<webrtc::DataChannelDelegateAdapter> _observer; + BOOL _isObserverRegistered; +} + +@synthesize delegate = _delegate; + +- (void)dealloc { + // Handles unregistering the observer properly. We need to do this because + // there may still be other references to the underlying data channel. + _nativeDataChannel->UnregisterObserver(); +} + +- (NSString *)label { + return [NSString stringForStdString:_nativeDataChannel->label()]; +} + +- (BOOL)isReliable { + return _nativeDataChannel->reliable(); +} + +- (BOOL)isOrdered { + return _nativeDataChannel->ordered(); +} + +- (NSUInteger)maxRetransmitTime { + return self.maxPacketLifeTime; +} + +- (uint16_t)maxPacketLifeTime { + return _nativeDataChannel->maxRetransmitTime(); +} + +- (uint16_t)maxRetransmits { + return _nativeDataChannel->maxRetransmits(); +} + +- (NSString *)protocol { + return [NSString stringForStdString:_nativeDataChannel->protocol()]; +} + +- (BOOL)isNegotiated { + return _nativeDataChannel->negotiated(); +} + +- (NSInteger)streamId { + return self.channelId; +} + +- (int)channelId { + return _nativeDataChannel->id(); +} + +- (RTCDataChannelState)readyState { + return [[self class] dataChannelStateForNativeState: + _nativeDataChannel->state()]; +} + +- (uint64_t)bufferedAmount { + return _nativeDataChannel->buffered_amount(); +} + +- (void)close { + _nativeDataChannel->Close(); +} + +- (BOOL)sendData:(RTC_OBJC_TYPE(RTCDataBuffer) *)data { + return _nativeDataChannel->Send(*data.nativeDataBuffer); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCDataChannel):\n%ld\n%@\n%@", + (long)self.channelId, + self.label, + [[self class] stringForState:self.readyState]]; +} + +#pragma mark - Private + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeDataChannel: + (rtc::scoped_refptr<webrtc::DataChannelInterface>)nativeDataChannel { + NSParameterAssert(nativeDataChannel); + if (self = [super init]) { + _factory = factory; + _nativeDataChannel = nativeDataChannel; + _observer.reset(new webrtc::DataChannelDelegateAdapter(self)); + _nativeDataChannel->RegisterObserver(_observer.get()); + } + return self; +} + ++ (webrtc::DataChannelInterface::DataState) + nativeDataChannelStateForState:(RTCDataChannelState)state { + switch (state) { + case RTCDataChannelStateConnecting: + return webrtc::DataChannelInterface::DataState::kConnecting; + case RTCDataChannelStateOpen: + return webrtc::DataChannelInterface::DataState::kOpen; + case RTCDataChannelStateClosing: + return webrtc::DataChannelInterface::DataState::kClosing; + case RTCDataChannelStateClosed: + return webrtc::DataChannelInterface::DataState::kClosed; + } +} + ++ (RTCDataChannelState)dataChannelStateForNativeState: + (webrtc::DataChannelInterface::DataState)nativeState { + switch (nativeState) { + case webrtc::DataChannelInterface::DataState::kConnecting: + return RTCDataChannelStateConnecting; + case webrtc::DataChannelInterface::DataState::kOpen: + return RTCDataChannelStateOpen; + case webrtc::DataChannelInterface::DataState::kClosing: + return RTCDataChannelStateClosing; + case webrtc::DataChannelInterface::DataState::kClosed: + return RTCDataChannelStateClosed; + } +} + ++ (NSString *)stringForState:(RTCDataChannelState)state { + switch (state) { + case RTCDataChannelStateConnecting: + return @"Connecting"; + case RTCDataChannelStateOpen: + return @"Open"; + case RTCDataChannelStateClosing: + return @"Closing"; + case RTCDataChannelStateClosed: + return @"Closed"; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration+Private.h new file mode 100644 index 0000000000..5aef10fcef --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration+Private.h @@ -0,0 +1,24 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDataChannelConfiguration.h" + +#include "api/data_channel_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCDataChannelConfiguration) +() + + @property(nonatomic, readonly) webrtc::DataChannelInit nativeDataChannelInit; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration.h new file mode 100644 index 0000000000..9459ae0a13 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration.h @@ -0,0 +1,52 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AvailabilityMacros.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCDataChannelConfiguration) : NSObject + +/** Set to YES if ordered delivery is required. */ +@property(nonatomic, assign) BOOL isOrdered; + +/** Deprecated. Use maxPacketLifeTime. */ +@property(nonatomic, assign) NSInteger maxRetransmitTimeMs DEPRECATED_ATTRIBUTE; + +/** + * Max period in milliseconds in which retransmissions will be sent. After this + * time, no more retransmissions will be sent. -1 if unset. + */ +@property(nonatomic, assign) int maxPacketLifeTime; + +/** The max number of retransmissions. -1 if unset. */ +@property(nonatomic, assign) int maxRetransmits; + +/** Set to YES if the channel has been externally negotiated and we do not send + * an in-band signalling in the form of an "open" message. + */ +@property(nonatomic, assign) BOOL isNegotiated; + +/** Deprecated. Use channelId. */ +@property(nonatomic, assign) int streamId DEPRECATED_ATTRIBUTE; + +/** The id of the data channel. */ +@property(nonatomic, assign) int channelId; + +/** Set by the application and opaque to the WebRTC implementation. */ +@property(nonatomic) NSString* protocol; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration.mm new file mode 100644 index 0000000000..bf775b1afd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDataChannelConfiguration.mm @@ -0,0 +1,87 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDataChannelConfiguration+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCDataChannelConfiguration) + +@synthesize nativeDataChannelInit = _nativeDataChannelInit; + +- (BOOL)isOrdered { + return _nativeDataChannelInit.ordered; +} + +- (void)setIsOrdered:(BOOL)isOrdered { + _nativeDataChannelInit.ordered = isOrdered; +} + +- (NSInteger)maxRetransmitTimeMs { + return self.maxPacketLifeTime; +} + +- (void)setMaxRetransmitTimeMs:(NSInteger)maxRetransmitTimeMs { + self.maxPacketLifeTime = maxRetransmitTimeMs; +} + +- (int)maxPacketLifeTime { + return *_nativeDataChannelInit.maxRetransmitTime; +} + +- (void)setMaxPacketLifeTime:(int)maxPacketLifeTime { + _nativeDataChannelInit.maxRetransmitTime = maxPacketLifeTime; +} + +- (int)maxRetransmits { + if (_nativeDataChannelInit.maxRetransmits) { + return *_nativeDataChannelInit.maxRetransmits; + } else { + return -1; + } +} + +- (void)setMaxRetransmits:(int)maxRetransmits { + _nativeDataChannelInit.maxRetransmits = maxRetransmits; +} + +- (NSString *)protocol { + return [NSString stringForStdString:_nativeDataChannelInit.protocol]; +} + +- (void)setProtocol:(NSString *)protocol { + _nativeDataChannelInit.protocol = [NSString stdStringForString:protocol]; +} + +- (BOOL)isNegotiated { + return _nativeDataChannelInit.negotiated; +} + +- (void)setIsNegotiated:(BOOL)isNegotiated { + _nativeDataChannelInit.negotiated = isNegotiated; +} + +- (int)streamId { + return self.channelId; +} + +- (void)setStreamId:(int)streamId { + self.channelId = streamId; +} + +- (int)channelId { + return _nativeDataChannelInit.id; +} + +- (void)setChannelId:(int)channelId { + _nativeDataChannelInit.id = channelId; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender+Private.h new file mode 100644 index 0000000000..49a62164cd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDtmfSender.h" + +#include "api/dtmf_sender_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCDtmfSender) : NSObject <RTC_OBJC_TYPE(RTCDtmfSender)> + +@property(nonatomic, readonly) rtc::scoped_refptr<webrtc::DtmfSenderInterface> nativeDtmfSender; + +- (instancetype)init NS_UNAVAILABLE; + +/** Initialize an RTCDtmfSender with a native DtmfSenderInterface. */ +- (instancetype)initWithNativeDtmfSender: + (rtc::scoped_refptr<webrtc::DtmfSenderInterface>)nativeDtmfSender NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender.h new file mode 100644 index 0000000000..0f1b6ba4da --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender.h @@ -0,0 +1,71 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCDtmfSender)<NSObject> + + /** + * Returns true if this RTCDtmfSender is capable of sending DTMF. Otherwise + * returns false. To be able to send DTMF, the associated RTCRtpSender must be + * able to send packets, and a "telephone-event" codec must be negotiated. + */ + @property(nonatomic, readonly) BOOL canInsertDtmf; + +/** + * Queues a task that sends the DTMF tones. The tones parameter is treated + * as a series of characters. The characters 0 through 9, A through D, #, and * + * generate the associated DTMF tones. The characters a to d are equivalent + * to A to D. The character ',' indicates a delay of 2 seconds before + * processing the next character in the tones parameter. + * + * Unrecognized characters are ignored. + * + * @param duration The parameter indicates the duration to use for each + * character passed in the tones parameter. The duration cannot be more + * than 6000 or less than 70 ms. + * + * @param interToneGap The parameter indicates the gap between tones. + * This parameter must be at least 50 ms but should be as short as + * possible. + * + * If InsertDtmf is called on the same object while an existing task for this + * object to generate DTMF is still running, the previous task is canceled. + * Returns true on success and false on failure. + */ +- (BOOL)insertDtmf:(nonnull NSString *)tones + duration:(NSTimeInterval)duration + interToneGap:(NSTimeInterval)interToneGap; + +/** The tones remaining to be played out */ +- (nonnull NSString *)remainingTones; + +/** + * The current tone duration value. This value will be the value last set via the + * insertDtmf method, or the default value of 100 ms if insertDtmf was never called. + */ +- (NSTimeInterval)duration; + +/** + * The current value of the between-tone gap. This value will be the value last set + * via the insertDtmf() method, or the default value of 50 ms if insertDtmf() was never + * called. + */ +- (NSTimeInterval)interToneGap; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender.mm new file mode 100644 index 0000000000..ee3b79cd37 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCDtmfSender.mm @@ -0,0 +1,74 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDtmfSender+Private.h" + +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +#include "rtc_base/time_utils.h" + +@implementation RTC_OBJC_TYPE (RTCDtmfSender) { + rtc::scoped_refptr<webrtc::DtmfSenderInterface> _nativeDtmfSender; +} + +- (BOOL)canInsertDtmf { + return _nativeDtmfSender->CanInsertDtmf(); +} + +- (BOOL)insertDtmf:(nonnull NSString *)tones + duration:(NSTimeInterval)duration + interToneGap:(NSTimeInterval)interToneGap { + RTC_DCHECK(tones != nil); + + int durationMs = static_cast<int>(duration * rtc::kNumMillisecsPerSec); + int interToneGapMs = static_cast<int>(interToneGap * rtc::kNumMillisecsPerSec); + return _nativeDtmfSender->InsertDtmf( + [NSString stdStringForString:tones], durationMs, interToneGapMs); +} + +- (nonnull NSString *)remainingTones { + return [NSString stringForStdString:_nativeDtmfSender->tones()]; +} + +- (NSTimeInterval)duration { + return static_cast<NSTimeInterval>(_nativeDtmfSender->duration()) / rtc::kNumMillisecsPerSec; +} + +- (NSTimeInterval)interToneGap { + return static_cast<NSTimeInterval>(_nativeDtmfSender->inter_tone_gap()) / + rtc::kNumMillisecsPerSec; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCDtmfSender) {\n remainingTones: %@\n " + @"duration: %f sec\n interToneGap: %f sec\n}", + [self remainingTones], + [self duration], + [self interToneGap]]; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::DtmfSenderInterface>)nativeDtmfSender { + return _nativeDtmfSender; +} + +- (instancetype)initWithNativeDtmfSender: + (rtc::scoped_refptr<webrtc::DtmfSenderInterface>)nativeDtmfSender { + NSParameterAssert(nativeDtmfSender); + if (self = [super init]) { + _nativeDtmfSender = nativeDtmfSender; + RTCLogInfo( + @"RTC_OBJC_TYPE(RTCDtmfSender)(%p): created DTMF sender: %@", self, self.description); + } + return self; +} +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCEncodedImage+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCEncodedImage+Private.h new file mode 100644 index 0000000000..a078b0aded --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCEncodedImage+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "base/RTCEncodedImage.h" + +#include "api/video/encoded_image.h" + +NS_ASSUME_NONNULL_BEGIN + +/* Interfaces for converting to/from internal C++ formats. */ +@interface RTC_OBJC_TYPE (RTCEncodedImage) +(Private) + + - (instancetype)initWithNativeEncodedImage : (const webrtc::EncodedImage &)encodedImage; +- (webrtc::EncodedImage)nativeEncodedImage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm new file mode 100644 index 0000000000..7f8ae739e0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm @@ -0,0 +1,130 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCEncodedImage+Private.h" + +#import <objc/runtime.h> + +#include "rtc_base/numerics/safe_conversions.h" + +namespace { +// An implementation of EncodedImageBufferInterface that doesn't perform any copies. +class ObjCEncodedImageBuffer : public webrtc::EncodedImageBufferInterface { + public: + static rtc::scoped_refptr<ObjCEncodedImageBuffer> Create(NSData *data) { + return rtc::make_ref_counted<ObjCEncodedImageBuffer>(data); + } + const uint8_t *data() const override { return static_cast<const uint8_t *>(data_.bytes); } + // TODO(bugs.webrtc.org/9378): delete this non-const data method. + uint8_t *data() override { + return const_cast<uint8_t *>(static_cast<const uint8_t *>(data_.bytes)); + } + size_t size() const override { return data_.length; } + + protected: + explicit ObjCEncodedImageBuffer(NSData *data) : data_(data) {} + ~ObjCEncodedImageBuffer() {} + + NSData *data_; +}; +} + +// A simple wrapper around webrtc::EncodedImageBufferInterface to make it usable with associated +// objects. +@interface RTCWrappedEncodedImageBuffer : NSObject +@property(nonatomic) rtc::scoped_refptr<webrtc::EncodedImageBufferInterface> buffer; +- (instancetype)initWithEncodedImageBuffer: + (rtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)buffer; +@end +@implementation RTCWrappedEncodedImageBuffer +@synthesize buffer = _buffer; +- (instancetype)initWithEncodedImageBuffer: + (rtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)buffer { + self = [super init]; + if (self) { + _buffer = buffer; + } + return self; +} +@end + +@implementation RTC_OBJC_TYPE (RTCEncodedImage) +(Private) + + - (rtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)encodedData { + RTCWrappedEncodedImageBuffer *wrappedBuffer = + objc_getAssociatedObject(self, @selector(encodedData)); + return wrappedBuffer.buffer; +} + +- (void)setEncodedData:(rtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)buffer { + return objc_setAssociatedObject( + self, + @selector(encodedData), + [[RTCWrappedEncodedImageBuffer alloc] initWithEncodedImageBuffer:buffer], + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (instancetype)initWithNativeEncodedImage:(const webrtc::EncodedImage &)encodedImage { + if (self = [super init]) { + // A reference to the encodedData must be stored so that it's kept alive as long + // self.buffer references its underlying data. + self.encodedData = encodedImage.GetEncodedData(); + // Wrap the buffer in NSData without copying, do not take ownership. + self.buffer = [NSData dataWithBytesNoCopy:self.encodedData->data() + length:encodedImage.size() + freeWhenDone:NO]; + self.encodedWidth = rtc::dchecked_cast<int32_t>(encodedImage._encodedWidth); + self.encodedHeight = rtc::dchecked_cast<int32_t>(encodedImage._encodedHeight); + self.timeStamp = encodedImage.Timestamp(); + self.captureTimeMs = encodedImage.capture_time_ms_; + self.ntpTimeMs = encodedImage.ntp_time_ms_; + self.flags = encodedImage.timing_.flags; + self.encodeStartMs = encodedImage.timing_.encode_start_ms; + self.encodeFinishMs = encodedImage.timing_.encode_finish_ms; + self.frameType = static_cast<RTCFrameType>(encodedImage._frameType); + self.rotation = static_cast<RTCVideoRotation>(encodedImage.rotation_); + self.qp = @(encodedImage.qp_); + self.contentType = (encodedImage.content_type_ == webrtc::VideoContentType::SCREENSHARE) ? + RTCVideoContentTypeScreenshare : + RTCVideoContentTypeUnspecified; + } + + return self; +} + +- (webrtc::EncodedImage)nativeEncodedImage { + // Return the pointer without copying. + webrtc::EncodedImage encodedImage; + if (self.encodedData) { + encodedImage.SetEncodedData(self.encodedData); + } else if (self.buffer) { + encodedImage.SetEncodedData(ObjCEncodedImageBuffer::Create(self.buffer)); + } + encodedImage.set_size(self.buffer.length); + encodedImage._encodedWidth = rtc::dchecked_cast<uint32_t>(self.encodedWidth); + encodedImage._encodedHeight = rtc::dchecked_cast<uint32_t>(self.encodedHeight); + encodedImage.SetTimestamp(self.timeStamp); + encodedImage.capture_time_ms_ = self.captureTimeMs; + encodedImage.ntp_time_ms_ = self.ntpTimeMs; + encodedImage.timing_.flags = self.flags; + encodedImage.timing_.encode_start_ms = self.encodeStartMs; + encodedImage.timing_.encode_finish_ms = self.encodeFinishMs; + encodedImage._frameType = webrtc::VideoFrameType(self.frameType); + encodedImage.rotation_ = webrtc::VideoRotation(self.rotation); + encodedImage.qp_ = self.qp ? self.qp.intValue : -1; + encodedImage.content_type_ = (self.contentType == RTCVideoContentTypeScreenshare) ? + webrtc::VideoContentType::SCREENSHARE : + webrtc::VideoContentType::UNSPECIFIED; + + return encodedImage; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFieldTrials.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFieldTrials.h new file mode 100644 index 0000000000..3e8fcc8075 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFieldTrials.h @@ -0,0 +1,30 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +/** The only valid value for the following if set is kRTCFieldTrialEnabledValue. */ +RTC_EXTERN NSString *const kRTCFieldTrialAudioForceABWENoTWCCKey; +RTC_EXTERN NSString * const kRTCFieldTrialFlexFec03AdvertisedKey; +RTC_EXTERN NSString * const kRTCFieldTrialFlexFec03Key; +RTC_EXTERN NSString * const kRTCFieldTrialH264HighProfileKey; +RTC_EXTERN NSString * const kRTCFieldTrialMinimizeResamplingOnMobileKey; +RTC_EXTERN NSString *const kRTCFieldTrialUseNWPathMonitor; + +/** The valid value for field trials above. */ +RTC_EXTERN NSString * const kRTCFieldTrialEnabledValue; + +/** Initialize field trials using a dictionary mapping field trial keys to their + * values. See above for valid keys and values. Must be called before any other + * call into WebRTC. See: webrtc/system_wrappers/include/field_trial.h + */ +RTC_EXTERN void RTCInitFieldTrialDictionary(NSDictionary<NSString *, NSString *> *fieldTrials); diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFieldTrials.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFieldTrials.mm new file mode 100644 index 0000000000..193da9e4f7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFieldTrials.mm @@ -0,0 +1,56 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCFieldTrials.h" + +#include <memory> + +#import "base/RTCLogging.h" + +#include "system_wrappers/include/field_trial.h" + +NSString *const kRTCFieldTrialAudioForceABWENoTWCCKey = @"WebRTC-Audio-ABWENoTWCC"; +NSString * const kRTCFieldTrialFlexFec03AdvertisedKey = @"WebRTC-FlexFEC-03-Advertised"; +NSString * const kRTCFieldTrialFlexFec03Key = @"WebRTC-FlexFEC-03"; +NSString * const kRTCFieldTrialH264HighProfileKey = @"WebRTC-H264HighProfile"; +NSString * const kRTCFieldTrialMinimizeResamplingOnMobileKey = + @"WebRTC-Audio-MinimizeResamplingOnMobile"; +NSString *const kRTCFieldTrialUseNWPathMonitor = @"WebRTC-Network-UseNWPathMonitor"; +NSString * const kRTCFieldTrialEnabledValue = @"Enabled"; + +// InitFieldTrialsFromString stores the char*, so the char array must outlive +// the application. +static char *gFieldTrialInitString = nullptr; + +void RTCInitFieldTrialDictionary(NSDictionary<NSString *, NSString *> *fieldTrials) { + if (!fieldTrials) { + RTCLogWarning(@"No fieldTrials provided."); + return; + } + // Assemble the keys and values into the field trial string. + // We don't perform any extra format checking. That should be done by the underlying WebRTC calls. + NSMutableString *fieldTrialInitString = [NSMutableString string]; + for (NSString *key in fieldTrials) { + NSString *fieldTrialEntry = [NSString stringWithFormat:@"%@/%@/", key, fieldTrials[key]]; + [fieldTrialInitString appendString:fieldTrialEntry]; + } + size_t len = fieldTrialInitString.length + 1; + if (gFieldTrialInitString != nullptr) { + delete[] gFieldTrialInitString; + } + gFieldTrialInitString = new char[len]; + if (![fieldTrialInitString getCString:gFieldTrialInitString + maxLength:len + encoding:NSUTF8StringEncoding]) { + RTCLogError(@"Failed to convert field trial string."); + return; + } + webrtc::field_trial::InitFieldTrialsFromString(gFieldTrialInitString); +} diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFileLogger.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFileLogger.h new file mode 100644 index 0000000000..cb397c9633 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFileLogger.h @@ -0,0 +1,74 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +typedef NS_ENUM(NSUInteger, RTCFileLoggerSeverity) { + RTCFileLoggerSeverityVerbose, + RTCFileLoggerSeverityInfo, + RTCFileLoggerSeverityWarning, + RTCFileLoggerSeverityError +}; + +typedef NS_ENUM(NSUInteger, RTCFileLoggerRotationType) { + RTCFileLoggerTypeCall, + RTCFileLoggerTypeApp, +}; + +NS_ASSUME_NONNULL_BEGIN + +// This class intercepts WebRTC logs and saves them to a file. The file size +// will not exceed the given maximum bytesize. When the maximum bytesize is +// reached, logs are rotated according to the rotationType specified. +// For kRTCFileLoggerTypeCall, logs from the beginning and the end +// are preserved while the middle section is overwritten instead. +// For kRTCFileLoggerTypeApp, the oldest log is overwritten. +// This class is not threadsafe. +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCFileLogger) : NSObject + +// The severity level to capture. The default is kRTCFileLoggerSeverityInfo. +@property(nonatomic, assign) RTCFileLoggerSeverity severity; + +// The rotation type for this file logger. The default is +// kRTCFileLoggerTypeCall. +@property(nonatomic, readonly) RTCFileLoggerRotationType rotationType; + +// Disables buffering disk writes. Should be set before `start`. Buffering +// is enabled by default for performance. +@property(nonatomic, assign) BOOL shouldDisableBuffering; + +// Default constructor provides default settings for dir path, file size and +// rotation type. +- (instancetype)init; + +// Create file logger with default rotation type. +- (instancetype)initWithDirPath:(NSString *)dirPath maxFileSize:(NSUInteger)maxFileSize; + +- (instancetype)initWithDirPath:(NSString *)dirPath + maxFileSize:(NSUInteger)maxFileSize + rotationType:(RTCFileLoggerRotationType)rotationType NS_DESIGNATED_INITIALIZER; + +// Starts writing WebRTC logs to disk if not already started. Overwrites any +// existing file(s). +- (void)start; + +// Stops writing WebRTC logs to disk. This method is also called on dealloc. +- (void)stop; + +// Returns the current contents of the logs, or nil if start has been called +// without a stop. +- (nullable NSData *)logData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFileLogger.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFileLogger.mm new file mode 100644 index 0000000000..9562245611 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCFileLogger.mm @@ -0,0 +1,170 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCFileLogger.h" + +#include <memory> + +#include "rtc_base/checks.h" +#include "rtc_base/file_rotating_stream.h" +#include "rtc_base/log_sinks.h" +#include "rtc_base/logging.h" + +NSString *const kDefaultLogDirName = @"webrtc_logs"; +NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB. +const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log"; + +@implementation RTC_OBJC_TYPE (RTCFileLogger) { + BOOL _hasStarted; + NSString *_dirPath; + NSUInteger _maxFileSize; + std::unique_ptr<rtc::FileRotatingLogSink> _logSink; +} + +@synthesize severity = _severity; +@synthesize rotationType = _rotationType; +@synthesize shouldDisableBuffering = _shouldDisableBuffering; + +- (instancetype)init { + NSArray *paths = NSSearchPathForDirectoriesInDomains( + NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirPath = [paths firstObject]; + NSString *defaultDirPath = + [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName]; + return [self initWithDirPath:defaultDirPath + maxFileSize:kDefaultMaxFileSize]; +} + +- (instancetype)initWithDirPath:(NSString *)dirPath + maxFileSize:(NSUInteger)maxFileSize { + return [self initWithDirPath:dirPath + maxFileSize:maxFileSize + rotationType:RTCFileLoggerTypeCall]; +} + +- (instancetype)initWithDirPath:(NSString *)dirPath + maxFileSize:(NSUInteger)maxFileSize + rotationType:(RTCFileLoggerRotationType)rotationType { + NSParameterAssert(dirPath.length); + NSParameterAssert(maxFileSize); + if (self = [super init]) { + BOOL isDir = NO; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:dirPath isDirectory:&isDir]) { + if (!isDir) { + // Bail if something already exists there. + return nil; + } + } else { + if (![fileManager createDirectoryAtPath:dirPath + withIntermediateDirectories:NO + attributes:nil + error:nil]) { + // Bail if we failed to create a directory. + return nil; + } + } + _dirPath = dirPath; + _maxFileSize = maxFileSize; + _severity = RTCFileLoggerSeverityInfo; + } + return self; +} + +- (void)dealloc { + [self stop]; +} + +- (void)start { + if (_hasStarted) { + return; + } + switch (_rotationType) { + case RTCFileLoggerTypeApp: + _logSink.reset( + new rtc::FileRotatingLogSink(_dirPath.UTF8String, + kRTCFileLoggerRotatingLogPrefix, + _maxFileSize, + _maxFileSize / 10)); + break; + case RTCFileLoggerTypeCall: + _logSink.reset( + new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String, + _maxFileSize)); + break; + } + if (!_logSink->Init()) { + RTC_LOG(LS_ERROR) << "Failed to open log files at path: " << _dirPath.UTF8String; + _logSink.reset(); + return; + } + if (_shouldDisableBuffering) { + _logSink->DisableBuffering(); + } + rtc::LogMessage::LogThreads(true); + rtc::LogMessage::LogTimestamps(true); + rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); + _hasStarted = YES; +} + +- (void)stop { + if (!_hasStarted) { + return; + } + RTC_DCHECK(_logSink); + rtc::LogMessage::RemoveLogToStream(_logSink.get()); + _hasStarted = NO; + _logSink.reset(); +} + +- (nullable NSData *)logData { + if (_hasStarted) { + return nil; + } + NSMutableData* logData = [NSMutableData data]; + std::unique_ptr<rtc::FileRotatingStreamReader> stream; + switch(_rotationType) { + case RTCFileLoggerTypeApp: + stream = std::make_unique<rtc::FileRotatingStreamReader>(_dirPath.UTF8String, + kRTCFileLoggerRotatingLogPrefix); + break; + case RTCFileLoggerTypeCall: + stream = std::make_unique<rtc::CallSessionFileRotatingStreamReader>(_dirPath.UTF8String); + break; + } + size_t bufferSize = stream->GetSize(); + if (bufferSize == 0) { + return logData; + } + // Allocate memory using malloc so we can pass it direcly to NSData without + // copying. + std::unique_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize))); + size_t read = stream->ReadAll(buffer.get(), bufferSize); + logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release() + length:read]; + return logData; +} + +#pragma mark - Private + +- (rtc::LoggingSeverity)rtcSeverity { + switch (_severity) { + case RTCFileLoggerSeverityVerbose: + return rtc::LS_VERBOSE; + case RTCFileLoggerSeverityInfo: + return rtc::LS_INFO; + case RTCFileLoggerSeverityWarning: + return rtc::LS_WARNING; + case RTCFileLoggerSeverityError: + return rtc::LS_ERROR; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate+Private.h new file mode 100644 index 0000000000..409e16b608 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate+Private.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCIceCandidate.h" + +#include <memory> + +#include "api/jsep.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCIceCandidate) +() + + /** + * The native IceCandidateInterface representation of this RTCIceCandidate + * object. This is needed to pass to the underlying C++ APIs. + */ + @property(nonatomic, readonly) std::unique_ptr<webrtc::IceCandidateInterface> nativeCandidate; + +/** + * Initialize an RTCIceCandidate from a native IceCandidateInterface. No + * ownership is taken of the native candidate. + */ +- (instancetype)initWithNativeCandidate:(const webrtc::IceCandidateInterface *)candidate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate.h new file mode 100644 index 0000000000..f84843af6c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCIceCandidate) : NSObject + +/** + * If present, the identifier of the "media stream identification" for the media + * component this candidate is associated with. + */ +@property(nonatomic, readonly, nullable) NSString *sdpMid; + +/** + * The index (starting at zero) of the media description this candidate is + * associated with in the SDP. + */ +@property(nonatomic, readonly) int sdpMLineIndex; + +/** The SDP string for this candidate. */ +@property(nonatomic, readonly) NSString *sdp; + +/** The URL of the ICE server which this candidate is gathered from. */ +@property(nonatomic, readonly, nullable) NSString *serverUrl; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initialize an RTCIceCandidate from SDP. + */ +- (instancetype)initWithSdp:(NSString *)sdp + sdpMLineIndex:(int)sdpMLineIndex + sdpMid:(nullable NSString *)sdpMid NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate.mm new file mode 100644 index 0000000000..48385ef5b4 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidate.mm @@ -0,0 +1,76 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCIceCandidate+Private.h" + +#include <memory> + +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCIceCandidate) + +@synthesize sdpMid = _sdpMid; +@synthesize sdpMLineIndex = _sdpMLineIndex; +@synthesize sdp = _sdp; +@synthesize serverUrl = _serverUrl; + +- (instancetype)initWithSdp:(NSString *)sdp + sdpMLineIndex:(int)sdpMLineIndex + sdpMid:(NSString *)sdpMid { + NSParameterAssert(sdp.length); + if (self = [super init]) { + _sdpMid = [sdpMid copy]; + _sdpMLineIndex = sdpMLineIndex; + _sdp = [sdp copy]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCIceCandidate):\n%@\n%d\n%@\n%@", + _sdpMid, + _sdpMLineIndex, + _sdp, + _serverUrl]; +} + +#pragma mark - Private + +- (instancetype)initWithNativeCandidate: + (const webrtc::IceCandidateInterface *)candidate { + NSParameterAssert(candidate); + std::string sdp; + candidate->ToString(&sdp); + + RTC_OBJC_TYPE(RTCIceCandidate) *rtcCandidate = + [self initWithSdp:[NSString stringForStdString:sdp] + sdpMLineIndex:candidate->sdp_mline_index() + sdpMid:[NSString stringForStdString:candidate->sdp_mid()]]; + rtcCandidate->_serverUrl = [NSString stringForStdString:candidate->server_url()]; + return rtcCandidate; +} + +- (std::unique_ptr<webrtc::IceCandidateInterface>)nativeCandidate { + webrtc::SdpParseError error; + + webrtc::IceCandidateInterface *candidate = webrtc::CreateIceCandidate( + _sdpMid.stdString, _sdpMLineIndex, _sdp.stdString, &error); + + if (!candidate) { + RTCLog(@"Failed to create ICE candidate: %s\nline: %s", + error.description.c_str(), + error.line.c_str()); + } + + return std::unique_ptr<webrtc::IceCandidateInterface>(candidate); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent+Private.h new file mode 100644 index 0000000000..8502da08a8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCIceCandidateErrorEvent.h" + +#include <string> + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCIceCandidateErrorEvent) +() + + - (instancetype)initWithAddress : (const std::string&)address port : (const int)port url + : (const std::string&)url errorCode : (const int)errorCode errorText + : (const std::string&)errorText; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h new file mode 100644 index 0000000000..e0906fdbdd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCIceCandidateErrorEvent) : NSObject + +/** The local IP address used to communicate with the STUN or TURN server. */ +@property(nonatomic, readonly) NSString *address; + +/** The port used to communicate with the STUN or TURN server. */ +@property(nonatomic, readonly) int port; + +/** The STUN or TURN URL that identifies the STUN or TURN server for which the failure occurred. */ +@property(nonatomic, readonly) NSString *url; + +/** The numeric STUN error code returned by the STUN or TURN server. If no host candidate can reach + * the server, errorCode will be set to the value 701 which is outside the STUN error code range. + * This error is only fired once per server URL while in the RTCIceGatheringState of "gathering". */ +@property(nonatomic, readonly) int errorCode; + +/** The STUN reason text returned by the STUN or TURN server. If the server could not be reached, + * errorText will be set to an implementation-specific value providing details about the error. */ +@property(nonatomic, readonly) NSString *errorText; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.mm new file mode 100644 index 0000000000..573e30642b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.mm @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCIceCandidateErrorEvent+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCIceCandidateErrorEvent) + +@synthesize address = _address; +@synthesize port = _port; +@synthesize url = _url; +@synthesize errorCode = _errorCode; +@synthesize errorText = _errorText; + +- (instancetype)init { + return [super init]; +} + +- (instancetype)initWithAddress:(const std::string&)address + port:(const int)port + url:(const std::string&)url + errorCode:(const int)errorCode + errorText:(const std::string&)errorText { + if (self = [self init]) { + _address = [NSString stringForStdString:address]; + _port = port; + _url = [NSString stringForStdString:url]; + _errorCode = errorCode; + _errorText = [NSString stringForStdString:errorText]; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer+Private.h new file mode 100644 index 0000000000..3eee819965 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer+Private.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCIceServer.h" + +#include "api/peer_connection_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCIceServer) +() + + /** + * IceServer struct representation of this RTCIceServer object's data. + * This is needed to pass to the underlying C++ APIs. + */ + @property(nonatomic, readonly) webrtc::PeerConnectionInterface::IceServer nativeServer; + +/** Initialize an RTCIceServer from a native IceServer. */ +- (instancetype)initWithNativeServer:(webrtc::PeerConnectionInterface::IceServer)nativeServer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer.h new file mode 100644 index 0000000000..7ddcbc1a1f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer.h @@ -0,0 +1,114 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +typedef NS_ENUM(NSUInteger, RTCTlsCertPolicy) { + RTCTlsCertPolicySecure, + RTCTlsCertPolicyInsecureNoCheck +}; + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCIceServer) : NSObject + +/** URI(s) for this server represented as NSStrings. */ +@property(nonatomic, readonly) NSArray<NSString *> *urlStrings; + +/** Username to use if this RTCIceServer object is a TURN server. */ +@property(nonatomic, readonly, nullable) NSString *username; + +/** Credential to use if this RTCIceServer object is a TURN server. */ +@property(nonatomic, readonly, nullable) NSString *credential; + +/** + * TLS certificate policy to use if this RTCIceServer object is a TURN server. + */ +@property(nonatomic, readonly) RTCTlsCertPolicy tlsCertPolicy; + +/** + If the URIs in `urls` only contain IP addresses, this field can be used + to indicate the hostname, which may be necessary for TLS (using the SNI + extension). If `urls` itself contains the hostname, this isn't necessary. + */ +@property(nonatomic, readonly, nullable) NSString *hostname; + +/** List of protocols to be used in the TLS ALPN extension. */ +@property(nonatomic, readonly) NSArray<NSString *> *tlsAlpnProtocols; + +/** + List elliptic curves to be used in the TLS elliptic curves extension. + Only curve names supported by OpenSSL should be used (eg. "P-256","X25519"). + */ +@property(nonatomic, readonly) NSArray<NSString *> *tlsEllipticCurves; + +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** Convenience initializer for a server with no authentication (e.g. STUN). */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings; + +/** + * Initialize an RTCIceServer with its associated URLs, optional username, + * optional credential, and credentialType. + */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(nullable NSString *)username + credential:(nullable NSString *)credential; + +/** + * Initialize an RTCIceServer with its associated URLs, optional username, + * optional credential, and TLS cert policy. + */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(nullable NSString *)username + credential:(nullable NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy; + +/** + * Initialize an RTCIceServer with its associated URLs, optional username, + * optional credential, TLS cert policy and hostname. + */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(nullable NSString *)username + credential:(nullable NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy + hostname:(nullable NSString *)hostname; + +/** + * Initialize an RTCIceServer with its associated URLs, optional username, + * optional credential, TLS cert policy, hostname and ALPN protocols. + */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(nullable NSString *)username + credential:(nullable NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy + hostname:(nullable NSString *)hostname + tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols; + +/** + * Initialize an RTCIceServer with its associated URLs, optional username, + * optional credential, TLS cert policy, hostname, ALPN protocols and + * elliptic curves. + */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(nullable NSString *)username + credential:(nullable NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy + hostname:(nullable NSString *)hostname + tlsAlpnProtocols:(nullable NSArray<NSString *> *)tlsAlpnProtocols + tlsEllipticCurves:(nullable NSArray<NSString *> *)tlsEllipticCurves + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer.mm new file mode 100644 index 0000000000..19a0a7e9e8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCIceServer.mm @@ -0,0 +1,196 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCIceServer+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCIceServer) + +@synthesize urlStrings = _urlStrings; +@synthesize username = _username; +@synthesize credential = _credential; +@synthesize tlsCertPolicy = _tlsCertPolicy; +@synthesize hostname = _hostname; +@synthesize tlsAlpnProtocols = _tlsAlpnProtocols; +@synthesize tlsEllipticCurves = _tlsEllipticCurves; + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings { + return [self initWithURLStrings:urlStrings + username:nil + credential:nil]; +} + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(NSString *)username + credential:(NSString *)credential { + return [self initWithURLStrings:urlStrings + username:username + credential:credential + tlsCertPolicy:RTCTlsCertPolicySecure]; +} + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(NSString *)username + credential:(NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy { + return [self initWithURLStrings:urlStrings + username:username + credential:credential + tlsCertPolicy:tlsCertPolicy + hostname:nil]; +} + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(NSString *)username + credential:(NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy + hostname:(NSString *)hostname { + return [self initWithURLStrings:urlStrings + username:username + credential:credential + tlsCertPolicy:tlsCertPolicy + hostname:hostname + tlsAlpnProtocols:[NSArray array]]; +} + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(NSString *)username + credential:(NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy + hostname:(NSString *)hostname + tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols { + return [self initWithURLStrings:urlStrings + username:username + credential:credential + tlsCertPolicy:tlsCertPolicy + hostname:hostname + tlsAlpnProtocols:tlsAlpnProtocols + tlsEllipticCurves:[NSArray array]]; +} + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(NSString *)username + credential:(NSString *)credential + tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy + hostname:(NSString *)hostname + tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols + tlsEllipticCurves:(NSArray<NSString *> *)tlsEllipticCurves { + NSParameterAssert(urlStrings.count); + if (self = [super init]) { + _urlStrings = [[NSArray alloc] initWithArray:urlStrings copyItems:YES]; + _username = [username copy]; + _credential = [credential copy]; + _tlsCertPolicy = tlsCertPolicy; + _hostname = [hostname copy]; + _tlsAlpnProtocols = [[NSArray alloc] initWithArray:tlsAlpnProtocols copyItems:YES]; + _tlsEllipticCurves = [[NSArray alloc] initWithArray:tlsEllipticCurves copyItems:YES]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCIceServer):\n%@\n%@\n%@\n%@\n%@\n%@\n%@", + _urlStrings, + _username, + _credential, + [self stringForTlsCertPolicy:_tlsCertPolicy], + _hostname, + _tlsAlpnProtocols, + _tlsEllipticCurves]; +} + +#pragma mark - Private + +- (NSString *)stringForTlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy { + switch (tlsCertPolicy) { + case RTCTlsCertPolicySecure: + return @"RTCTlsCertPolicySecure"; + case RTCTlsCertPolicyInsecureNoCheck: + return @"RTCTlsCertPolicyInsecureNoCheck"; + } +} + +- (webrtc::PeerConnectionInterface::IceServer)nativeServer { + __block webrtc::PeerConnectionInterface::IceServer iceServer; + + iceServer.username = [NSString stdStringForString:_username]; + iceServer.password = [NSString stdStringForString:_credential]; + iceServer.hostname = [NSString stdStringForString:_hostname]; + + [_tlsAlpnProtocols enumerateObjectsUsingBlock:^(NSString *proto, NSUInteger idx, BOOL *stop) { + iceServer.tls_alpn_protocols.push_back(proto.stdString); + }]; + + [_tlsEllipticCurves enumerateObjectsUsingBlock:^(NSString *curve, NSUInteger idx, BOOL *stop) { + iceServer.tls_elliptic_curves.push_back(curve.stdString); + }]; + + [_urlStrings enumerateObjectsUsingBlock:^(NSString *url, + NSUInteger idx, + BOOL *stop) { + iceServer.urls.push_back(url.stdString); + }]; + + switch (_tlsCertPolicy) { + case RTCTlsCertPolicySecure: + iceServer.tls_cert_policy = + webrtc::PeerConnectionInterface::kTlsCertPolicySecure; + break; + case RTCTlsCertPolicyInsecureNoCheck: + iceServer.tls_cert_policy = + webrtc::PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck; + break; + } + return iceServer; +} + +- (instancetype)initWithNativeServer: + (webrtc::PeerConnectionInterface::IceServer)nativeServer { + NSMutableArray *urls = + [NSMutableArray arrayWithCapacity:nativeServer.urls.size()]; + for (auto const &url : nativeServer.urls) { + [urls addObject:[NSString stringForStdString:url]]; + } + NSString *username = [NSString stringForStdString:nativeServer.username]; + NSString *credential = [NSString stringForStdString:nativeServer.password]; + NSString *hostname = [NSString stringForStdString:nativeServer.hostname]; + NSMutableArray *tlsAlpnProtocols = + [NSMutableArray arrayWithCapacity:nativeServer.tls_alpn_protocols.size()]; + for (auto const &proto : nativeServer.tls_alpn_protocols) { + [tlsAlpnProtocols addObject:[NSString stringForStdString:proto]]; + } + NSMutableArray *tlsEllipticCurves = + [NSMutableArray arrayWithCapacity:nativeServer.tls_elliptic_curves.size()]; + for (auto const &curve : nativeServer.tls_elliptic_curves) { + [tlsEllipticCurves addObject:[NSString stringForStdString:curve]]; + } + RTCTlsCertPolicy tlsCertPolicy; + + switch (nativeServer.tls_cert_policy) { + case webrtc::PeerConnectionInterface::kTlsCertPolicySecure: + tlsCertPolicy = RTCTlsCertPolicySecure; + break; + case webrtc::PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck: + tlsCertPolicy = RTCTlsCertPolicyInsecureNoCheck; + break; + } + + self = [self initWithURLStrings:urls + username:username + credential:credential + tlsCertPolicy:tlsCertPolicy + hostname:hostname + tlsAlpnProtocols:tlsAlpnProtocols + tlsEllipticCurves:tlsEllipticCurves]; + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport+Private.h new file mode 100644 index 0000000000..7374b2b72f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport+Private.h @@ -0,0 +1,25 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCLegacyStatsReport.h" + +#include "api/legacy_stats_types.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCLegacyStatsReport) +() + + /** Initialize an RTCLegacyStatsReport object from a native StatsReport. */ + - (instancetype)initWithNativeReport : (const webrtc::StatsReport &)nativeReport; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport.h new file mode 100644 index 0000000000..b3bd12c5d7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** This does not currently conform to the spec. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCLegacyStatsReport) : NSObject + +/** Time since 1970-01-01T00:00:00Z in milliseconds. */ +@property(nonatomic, readonly) CFTimeInterval timestamp; + +/** The type of stats held by this object. */ +@property(nonatomic, readonly) NSString *type; + +/** The identifier for this object. */ +@property(nonatomic, readonly) NSString *reportId; + +/** A dictionary holding the actual stats. */ +@property(nonatomic, readonly) NSDictionary<NSString *, NSString *> *values; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport.mm new file mode 100644 index 0000000000..bd7a1ad9c9 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCLegacyStatsReport.mm @@ -0,0 +1,60 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCLegacyStatsReport+Private.h" + +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +#include "rtc_base/checks.h" + +@implementation RTC_OBJC_TYPE (RTCLegacyStatsReport) + +@synthesize timestamp = _timestamp; +@synthesize type = _type; +@synthesize reportId = _reportId; +@synthesize values = _values; + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCLegacyStatsReport):\n%@\n%@\n%f\n%@", + _reportId, + _type, + _timestamp, + _values]; +} + +#pragma mark - Private + +- (instancetype)initWithNativeReport:(const webrtc::StatsReport &)nativeReport { + if (self = [super init]) { + _timestamp = nativeReport.timestamp(); + _type = [NSString stringForStdString:nativeReport.TypeToString()]; + _reportId = [NSString stringForStdString: + nativeReport.id()->ToString()]; + + NSUInteger capacity = nativeReport.values().size(); + NSMutableDictionary *values = + [NSMutableDictionary dictionaryWithCapacity:capacity]; + for (auto const &valuePair : nativeReport.values()) { + NSString *key = [NSString stringForStdString: + valuePair.second->display_name()]; + NSString *value = [NSString stringForStdString: + valuePair.second->ToString()]; + + // Not expecting duplicate keys. + RTC_DCHECK(![values objectForKey:key]); + [values setObject:value forKey:key]; + } + _values = values; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints+Private.h new file mode 100644 index 0000000000..97eee8307d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints+Private.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaConstraints.h" + +#include <memory> + +#include "sdk/media_constraints.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCMediaConstraints) +() + + /** + * A MediaConstraints representation of this RTCMediaConstraints object. This is + * needed to pass to the underlying C++ APIs. + */ + - (std::unique_ptr<webrtc::MediaConstraints>)nativeConstraints; + +/** Return a native Constraints object representing these constraints */ ++ (webrtc::MediaConstraints::Constraints)nativeConstraintsForConstraints: + (NSDictionary<NSString*, NSString*>*)constraints; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints.h new file mode 100644 index 0000000000..c5baf20c1d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints.h @@ -0,0 +1,46 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Constraint keys for media sources. */ +/** The value for this key should be a base64 encoded string containing + * the data from the serialized configuration proto. + */ +RTC_EXTERN NSString *const kRTCMediaConstraintsAudioNetworkAdaptorConfig; + +/** Constraint keys for generating offers and answers. */ +RTC_EXTERN NSString *const kRTCMediaConstraintsIceRestart; +RTC_EXTERN NSString *const kRTCMediaConstraintsOfferToReceiveAudio; +RTC_EXTERN NSString *const kRTCMediaConstraintsOfferToReceiveVideo; +RTC_EXTERN NSString *const kRTCMediaConstraintsVoiceActivityDetection; + +/** Constraint values for Boolean parameters. */ +RTC_EXTERN NSString *const kRTCMediaConstraintsValueTrue; +RTC_EXTERN NSString *const kRTCMediaConstraintsValueFalse; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMediaConstraints) : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** Initialize with mandatory and/or optional constraints. */ +- (instancetype) + initWithMandatoryConstraints:(nullable NSDictionary<NSString *, NSString *> *)mandatory + optionalConstraints:(nullable NSDictionary<NSString *, NSString *> *)optional + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints.mm new file mode 100644 index 0000000000..0f46e4b8fe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaConstraints.mm @@ -0,0 +1,90 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaConstraints+Private.h" + +#import "helpers/NSString+StdString.h" + +#include <memory> + +NSString *const kRTCMediaConstraintsAudioNetworkAdaptorConfig = + @(webrtc::MediaConstraints::kAudioNetworkAdaptorConfig); + +NSString *const kRTCMediaConstraintsIceRestart = @(webrtc::MediaConstraints::kIceRestart); +NSString *const kRTCMediaConstraintsOfferToReceiveAudio = + @(webrtc::MediaConstraints::kOfferToReceiveAudio); +NSString *const kRTCMediaConstraintsOfferToReceiveVideo = + @(webrtc::MediaConstraints::kOfferToReceiveVideo); +NSString *const kRTCMediaConstraintsVoiceActivityDetection = + @(webrtc::MediaConstraints::kVoiceActivityDetection); + +NSString *const kRTCMediaConstraintsValueTrue = @(webrtc::MediaConstraints::kValueTrue); +NSString *const kRTCMediaConstraintsValueFalse = @(webrtc::MediaConstraints::kValueFalse); + +@implementation RTC_OBJC_TYPE (RTCMediaConstraints) { + NSDictionary<NSString *, NSString *> *_mandatory; + NSDictionary<NSString *, NSString *> *_optional; +} + +- (instancetype)initWithMandatoryConstraints: + (NSDictionary<NSString *, NSString *> *)mandatory + optionalConstraints: + (NSDictionary<NSString *, NSString *> *)optional { + if (self = [super init]) { + _mandatory = [[NSDictionary alloc] initWithDictionary:mandatory + copyItems:YES]; + _optional = [[NSDictionary alloc] initWithDictionary:optional + copyItems:YES]; + } + return self; +} + +- (NSString *)description { + return [NSString + stringWithFormat:@"RTC_OBJC_TYPE(RTCMediaConstraints):\n%@\n%@", _mandatory, _optional]; +} + +#pragma mark - Private + +- (std::unique_ptr<webrtc::MediaConstraints>)nativeConstraints { + webrtc::MediaConstraints::Constraints mandatory = + [[self class] nativeConstraintsForConstraints:_mandatory]; + webrtc::MediaConstraints::Constraints optional = + [[self class] nativeConstraintsForConstraints:_optional]; + + webrtc::MediaConstraints *nativeConstraints = + new webrtc::MediaConstraints(mandatory, optional); + return std::unique_ptr<webrtc::MediaConstraints>(nativeConstraints); +} + ++ (webrtc::MediaConstraints::Constraints)nativeConstraintsForConstraints: + (NSDictionary<NSString *, NSString *> *)constraints { + webrtc::MediaConstraints::Constraints nativeConstraints; + for (NSString *key in constraints) { + NSAssert([key isKindOfClass:[NSString class]], + @"%@ is not an NSString.", key); + NSString *value = [constraints objectForKey:key]; + NSAssert([value isKindOfClass:[NSString class]], + @"%@ is not an NSString.", value); + if ([kRTCMediaConstraintsAudioNetworkAdaptorConfig isEqualToString:key]) { + // This value is base64 encoded. + NSData *charData = [[NSData alloc] initWithBase64EncodedString:value options:0]; + std::string configValue = + std::string(reinterpret_cast<const char *>(charData.bytes), charData.length); + nativeConstraints.push_back(webrtc::MediaConstraints::Constraint(key.stdString, configValue)); + } else { + nativeConstraints.push_back( + webrtc::MediaConstraints::Constraint(key.stdString, value.stdString)); + } + } + return nativeConstraints; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource+Private.h new file mode 100644 index 0000000000..edda892e50 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource+Private.h @@ -0,0 +1,42 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaSource.h" + +#include "api/media_stream_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); + +typedef NS_ENUM(NSInteger, RTCMediaSourceType) { + RTCMediaSourceTypeAudio, + RTCMediaSourceTypeVideo, +}; + +@interface RTC_OBJC_TYPE (RTCMediaSource) +() + + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::MediaSourceInterface> nativeMediaSource; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaSource:(rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + type:(RTCMediaSourceType)type NS_DESIGNATED_INITIALIZER; + ++ (webrtc::MediaSourceInterface::SourceState)nativeSourceStateForState:(RTCSourceState)state; + ++ (RTCSourceState)sourceStateForNativeState:(webrtc::MediaSourceInterface::SourceState)nativeState; + ++ (NSString *)stringForState:(RTCSourceState)state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource.h new file mode 100644 index 0000000000..ba19c2a352 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource.h @@ -0,0 +1,34 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +typedef NS_ENUM(NSInteger, RTCSourceState) { + RTCSourceStateInitializing, + RTCSourceStateLive, + RTCSourceStateEnded, + RTCSourceStateMuted, +}; + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMediaSource) : NSObject + +/** The current state of the RTCMediaSource. */ +@property(nonatomic, readonly) RTCSourceState state; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource.mm new file mode 100644 index 0000000000..61472a782a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaSource.mm @@ -0,0 +1,82 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaSource+Private.h" + +#include "rtc_base/checks.h" + +@implementation RTC_OBJC_TYPE (RTCMediaSource) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + RTCMediaSourceType _type; +} + +@synthesize nativeMediaSource = _nativeMediaSource; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaSource:(rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + type:(RTCMediaSourceType)type { + RTC_DCHECK(factory); + RTC_DCHECK(nativeMediaSource); + if (self = [super init]) { + _factory = factory; + _nativeMediaSource = nativeMediaSource; + _type = type; + } + return self; +} + +- (RTCSourceState)state { + return [[self class] sourceStateForNativeState:_nativeMediaSource->state()]; +} + +#pragma mark - Private + ++ (webrtc::MediaSourceInterface::SourceState)nativeSourceStateForState: + (RTCSourceState)state { + switch (state) { + case RTCSourceStateInitializing: + return webrtc::MediaSourceInterface::kInitializing; + case RTCSourceStateLive: + return webrtc::MediaSourceInterface::kLive; + case RTCSourceStateEnded: + return webrtc::MediaSourceInterface::kEnded; + case RTCSourceStateMuted: + return webrtc::MediaSourceInterface::kMuted; + } +} + ++ (RTCSourceState)sourceStateForNativeState: + (webrtc::MediaSourceInterface::SourceState)nativeState { + switch (nativeState) { + case webrtc::MediaSourceInterface::kInitializing: + return RTCSourceStateInitializing; + case webrtc::MediaSourceInterface::kLive: + return RTCSourceStateLive; + case webrtc::MediaSourceInterface::kEnded: + return RTCSourceStateEnded; + case webrtc::MediaSourceInterface::kMuted: + return RTCSourceStateMuted; + } +} + ++ (NSString *)stringForState:(RTCSourceState)state { + switch (state) { + case RTCSourceStateInitializing: + return @"Initializing"; + case RTCSourceStateLive: + return @"Live"; + case RTCSourceStateEnded: + return @"Ended"; + case RTCSourceStateMuted: + return @"Muted"; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream+Private.h new file mode 100644 index 0000000000..6c8a602766 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream+Private.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaStream.h" + +#include "api/media_stream_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCMediaStream) +() + + /** + * MediaStreamInterface representation of this RTCMediaStream object. This is + * needed to pass to the underlying C++ APIs. + */ + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::MediaStreamInterface> nativeMediaStream; + +/** Initialize an RTCMediaStream with an id. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + streamId:(NSString *)streamId; + +/** Initialize an RTCMediaStream from a native MediaStreamInterface. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaStream:(rtc::scoped_refptr<webrtc::MediaStreamInterface>)nativeMediaStream; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream.h new file mode 100644 index 0000000000..2d56f15c7d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCAudioTrack); +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); +@class RTC_OBJC_TYPE(RTCVideoTrack); + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMediaStream) : NSObject + +/** The audio tracks in this stream. */ +@property(nonatomic, strong, readonly) NSArray<RTC_OBJC_TYPE(RTCAudioTrack) *> *audioTracks; + +/** The video tracks in this stream. */ +@property(nonatomic, strong, readonly) NSArray<RTC_OBJC_TYPE(RTCVideoTrack) *> *videoTracks; + +/** An identifier for this media stream. */ +@property(nonatomic, readonly) NSString *streamId; + +- (instancetype)init NS_UNAVAILABLE; + +/** Adds the given audio track to this media stream. */ +- (void)addAudioTrack:(RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrack; + +/** Adds the given video track to this media stream. */ +- (void)addVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)videoTrack; + +/** Removes the given audio track to this media stream. */ +- (void)removeAudioTrack:(RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrack; + +/** Removes the given video track to this media stream. */ +- (void)removeVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)videoTrack; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream.mm new file mode 100644 index 0000000000..0018dd6945 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStream.mm @@ -0,0 +1,155 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaStream+Private.h" + +#import "RTCAudioTrack+Private.h" +#import "RTCMediaStreamTrack+Private.h" +#import "RTCPeerConnectionFactory+Private.h" +#import "RTCVideoTrack+Private.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCMediaStream) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + rtc::Thread *_signalingThread; + NSMutableArray *_audioTracks /* accessed on _signalingThread */; + NSMutableArray *_videoTracks /* accessed on _signalingThread */; + rtc::scoped_refptr<webrtc::MediaStreamInterface> _nativeMediaStream; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + streamId:(NSString *)streamId { + NSParameterAssert(factory); + NSParameterAssert(streamId.length); + std::string nativeId = [NSString stdStringForString:streamId]; + rtc::scoped_refptr<webrtc::MediaStreamInterface> stream = + factory.nativeFactory->CreateLocalMediaStream(nativeId); + return [self initWithFactory:factory nativeMediaStream:stream]; +} + +- (NSArray<RTC_OBJC_TYPE(RTCAudioTrack) *> *)audioTracks { + if (!_signalingThread->IsCurrent()) { + return _signalingThread->BlockingCall([self]() { return self.audioTracks; }); + } + return [_audioTracks copy]; +} + +- (NSArray<RTC_OBJC_TYPE(RTCVideoTrack) *> *)videoTracks { + if (!_signalingThread->IsCurrent()) { + return _signalingThread->BlockingCall([self]() { return self.videoTracks; }); + } + return [_videoTracks copy]; +} + +- (NSString *)streamId { + return [NSString stringForStdString:_nativeMediaStream->id()]; +} + +- (void)addAudioTrack:(RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrack { + if (!_signalingThread->IsCurrent()) { + return _signalingThread->BlockingCall( + [audioTrack, self]() { return [self addAudioTrack:audioTrack]; }); + } + if (_nativeMediaStream->AddTrack(audioTrack.nativeAudioTrack)) { + [_audioTracks addObject:audioTrack]; + } +} + +- (void)addVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)videoTrack { + if (!_signalingThread->IsCurrent()) { + return _signalingThread->BlockingCall( + [videoTrack, self]() { return [self addVideoTrack:videoTrack]; }); + } + if (_nativeMediaStream->AddTrack(videoTrack.nativeVideoTrack)) { + [_videoTracks addObject:videoTrack]; + } +} + +- (void)removeAudioTrack:(RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrack { + if (!_signalingThread->IsCurrent()) { + return _signalingThread->BlockingCall( + [audioTrack, self]() { return [self removeAudioTrack:audioTrack]; }); + } + NSUInteger index = [_audioTracks indexOfObjectIdenticalTo:audioTrack]; + if (index == NSNotFound) { + RTC_LOG(LS_INFO) << "|removeAudioTrack| called on unexpected RTC_OBJC_TYPE(RTCAudioTrack)"; + return; + } + if (_nativeMediaStream->RemoveTrack(audioTrack.nativeAudioTrack)) { + [_audioTracks removeObjectAtIndex:index]; + } +} + +- (void)removeVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)videoTrack { + if (!_signalingThread->IsCurrent()) { + return _signalingThread->BlockingCall( + [videoTrack, self]() { return [self removeVideoTrack:videoTrack]; }); + } + NSUInteger index = [_videoTracks indexOfObjectIdenticalTo:videoTrack]; + if (index == NSNotFound) { + RTC_LOG(LS_INFO) << "|removeVideoTrack| called on unexpected RTC_OBJC_TYPE(RTCVideoTrack)"; + return; + } + + if (_nativeMediaStream->RemoveTrack(videoTrack.nativeVideoTrack)) { + [_videoTracks removeObjectAtIndex:index]; + } +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCMediaStream):\n%@\nA=%lu\nV=%lu", + self.streamId, + (unsigned long)self.audioTracks.count, + (unsigned long)self.videoTracks.count]; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::MediaStreamInterface>)nativeMediaStream { + return _nativeMediaStream; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaStream: + (rtc::scoped_refptr<webrtc::MediaStreamInterface>)nativeMediaStream { + NSParameterAssert(nativeMediaStream); + if (self = [super init]) { + _factory = factory; + _signalingThread = factory.signalingThread; + + webrtc::AudioTrackVector audioTracks = nativeMediaStream->GetAudioTracks(); + webrtc::VideoTrackVector videoTracks = nativeMediaStream->GetVideoTracks(); + + _audioTracks = [NSMutableArray arrayWithCapacity:audioTracks.size()]; + _videoTracks = [NSMutableArray arrayWithCapacity:videoTracks.size()]; + _nativeMediaStream = nativeMediaStream; + + for (auto &track : audioTracks) { + RTCMediaStreamTrackType type = RTCMediaStreamTrackTypeAudio; + RTC_OBJC_TYPE(RTCAudioTrack) *audioTrack = + [[RTC_OBJC_TYPE(RTCAudioTrack) alloc] initWithFactory:_factory + nativeTrack:track + type:type]; + [_audioTracks addObject:audioTrack]; + } + + for (auto &track : videoTracks) { + RTCMediaStreamTrackType type = RTCMediaStreamTrackTypeVideo; + RTC_OBJC_TYPE(RTCVideoTrack) *videoTrack = + [[RTC_OBJC_TYPE(RTCVideoTrack) alloc] initWithFactory:_factory + nativeTrack:track + type:type]; + [_videoTracks addObject:videoTrack]; + } + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack+Private.h new file mode 100644 index 0000000000..ee51e27b2d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack+Private.h @@ -0,0 +1,62 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaStreamTrack.h" + +#include "api/media_stream_interface.h" + +typedef NS_ENUM(NSInteger, RTCMediaStreamTrackType) { + RTCMediaStreamTrackTypeAudio, + RTCMediaStreamTrackTypeVideo, +}; + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); + +@interface RTC_OBJC_TYPE (RTCMediaStreamTrack) +() + + @property(nonatomic, readonly) RTC_OBJC_TYPE(RTCPeerConnectionFactory) * + factory; + +/** + * The native MediaStreamTrackInterface passed in or created during + * construction. + */ +@property(nonatomic, readonly) rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> nativeTrack; + +/** + * Initialize an RTCMediaStreamTrack from a native MediaStreamTrackInterface. + */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack + type:(RTCMediaStreamTrackType)type NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack; + +- (BOOL)isEqualToTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track; + ++ (webrtc::MediaStreamTrackInterface::TrackState)nativeTrackStateForState: + (RTCMediaStreamTrackState)state; + ++ (RTCMediaStreamTrackState)trackStateForNativeState: + (webrtc::MediaStreamTrackInterface::TrackState)nativeState; + ++ (NSString *)stringForState:(RTCMediaStreamTrackState)state; + ++ (RTC_OBJC_TYPE(RTCMediaStreamTrack) *) + mediaTrackForNativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack + factory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack.h new file mode 100644 index 0000000000..2200122ccd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack.h @@ -0,0 +1,50 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +/** + * Represents the state of the track. This exposes the same states in C++. + */ +typedef NS_ENUM(NSInteger, RTCMediaStreamTrackState) { + RTCMediaStreamTrackStateLive, + RTCMediaStreamTrackStateEnded +}; + +NS_ASSUME_NONNULL_BEGIN + +RTC_EXTERN NSString *const kRTCMediaStreamTrackKindAudio; +RTC_EXTERN NSString *const kRTCMediaStreamTrackKindVideo; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMediaStreamTrack) : NSObject + +/** + * The kind of track. For example, "audio" if this track represents an audio + * track and "video" if this track represents a video track. + */ +@property(nonatomic, readonly) NSString *kind; + +/** An identifier string. */ +@property(nonatomic, readonly) NSString *trackId; + +/** The enabled state of the track. */ +@property(nonatomic, assign) BOOL isEnabled; + +/** The state of the track. */ +@property(nonatomic, readonly) RTCMediaStreamTrackState readyState; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack.mm new file mode 100644 index 0000000000..f1e128ca60 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMediaStreamTrack.mm @@ -0,0 +1,161 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioTrack+Private.h" +#import "RTCMediaStreamTrack+Private.h" +#import "RTCVideoTrack+Private.h" + +#import "helpers/NSString+StdString.h" + +NSString * const kRTCMediaStreamTrackKindAudio = + @(webrtc::MediaStreamTrackInterface::kAudioKind); +NSString * const kRTCMediaStreamTrackKindVideo = + @(webrtc::MediaStreamTrackInterface::kVideoKind); + +@implementation RTC_OBJC_TYPE (RTCMediaStreamTrack) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> _nativeTrack; + RTCMediaStreamTrackType _type; +} + +- (NSString *)kind { + return [NSString stringForStdString:_nativeTrack->kind()]; +} + +- (NSString *)trackId { + return [NSString stringForStdString:_nativeTrack->id()]; +} + +- (BOOL)isEnabled { + return _nativeTrack->enabled(); +} + +- (void)setIsEnabled:(BOOL)isEnabled { + _nativeTrack->set_enabled(isEnabled); +} + +- (RTCMediaStreamTrackState)readyState { + return [[self class] trackStateForNativeState:_nativeTrack->state()]; +} + +- (NSString *)description { + NSString *readyState = [[self class] stringForState:self.readyState]; + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCMediaStreamTrack):\n%@\n%@\n%@\n%@", + self.kind, + self.trackId, + self.isEnabled ? @"enabled" : @"disabled", + readyState]; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isMemberOfClass:[self class]]) { + return NO; + } + return [self isEqualToTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)object]; +} + +- (NSUInteger)hash { + return (NSUInteger)_nativeTrack.get(); +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack { + return _nativeTrack; +} + +@synthesize factory = _factory; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack + type:(RTCMediaStreamTrackType)type { + NSParameterAssert(nativeTrack); + NSParameterAssert(factory); + if (self = [super init]) { + _factory = factory; + _nativeTrack = nativeTrack; + _type = type; + } + return self; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack { + NSParameterAssert(nativeTrack); + if (nativeTrack->kind() == + std::string(webrtc::MediaStreamTrackInterface::kAudioKind)) { + return [self initWithFactory:factory nativeTrack:nativeTrack type:RTCMediaStreamTrackTypeAudio]; + } + if (nativeTrack->kind() == + std::string(webrtc::MediaStreamTrackInterface::kVideoKind)) { + return [self initWithFactory:factory nativeTrack:nativeTrack type:RTCMediaStreamTrackTypeVideo]; + } + return nil; +} + +- (BOOL)isEqualToTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track { + if (!track) { + return NO; + } + return _nativeTrack == track.nativeTrack; +} + ++ (webrtc::MediaStreamTrackInterface::TrackState)nativeTrackStateForState: + (RTCMediaStreamTrackState)state { + switch (state) { + case RTCMediaStreamTrackStateLive: + return webrtc::MediaStreamTrackInterface::kLive; + case RTCMediaStreamTrackStateEnded: + return webrtc::MediaStreamTrackInterface::kEnded; + } +} + ++ (RTCMediaStreamTrackState)trackStateForNativeState: + (webrtc::MediaStreamTrackInterface::TrackState)nativeState { + switch (nativeState) { + case webrtc::MediaStreamTrackInterface::kLive: + return RTCMediaStreamTrackStateLive; + case webrtc::MediaStreamTrackInterface::kEnded: + return RTCMediaStreamTrackStateEnded; + } +} + ++ (NSString *)stringForState:(RTCMediaStreamTrackState)state { + switch (state) { + case RTCMediaStreamTrackStateLive: + return @"Live"; + case RTCMediaStreamTrackStateEnded: + return @"Ended"; + } +} + ++ (RTC_OBJC_TYPE(RTCMediaStreamTrack) *) + mediaTrackForNativeTrack:(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack + factory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory { + NSParameterAssert(nativeTrack); + NSParameterAssert(factory); + if (nativeTrack->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { + return [[RTC_OBJC_TYPE(RTCAudioTrack) alloc] initWithFactory:factory + nativeTrack:nativeTrack + type:RTCMediaStreamTrackTypeAudio]; + } else if (nativeTrack->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { + return [[RTC_OBJC_TYPE(RTCVideoTrack) alloc] initWithFactory:factory + nativeTrack:nativeTrack + type:RTCMediaStreamTrackTypeVideo]; + } else { + return [[RTC_OBJC_TYPE(RTCMediaStreamTrack) alloc] initWithFactory:factory + nativeTrack:nativeTrack]; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetrics.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetrics.h new file mode 100644 index 0000000000..fddbb27c90 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetrics.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCMetricsSampleInfo.h" + +/** + * Enables gathering of metrics (which can be fetched with + * RTCGetAndResetMetrics). Must be called before any other call into WebRTC. + */ +RTC_EXTERN void RTCEnableMetrics(void); + +/** Gets and clears native histograms. */ +RTC_EXTERN NSArray<RTC_OBJC_TYPE(RTCMetricsSampleInfo) *>* RTCGetAndResetMetrics(void); diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetrics.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetrics.mm new file mode 100644 index 0000000000..87eb8c0210 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetrics.mm @@ -0,0 +1,34 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMetrics.h" + +#import "RTCMetricsSampleInfo+Private.h" + +#include "rtc_base/string_utils.h" + +void RTCEnableMetrics(void) { + webrtc::metrics::Enable(); +} + +NSArray<RTC_OBJC_TYPE(RTCMetricsSampleInfo) *> *RTCGetAndResetMetrics(void) { + std::map<std::string, std::unique_ptr<webrtc::metrics::SampleInfo>, rtc::AbslStringViewCmp> + histograms; + webrtc::metrics::GetAndReset(&histograms); + + NSMutableArray *metrics = + [NSMutableArray arrayWithCapacity:histograms.size()]; + for (auto const &histogram : histograms) { + RTC_OBJC_TYPE(RTCMetricsSampleInfo) *metric = + [[RTC_OBJC_TYPE(RTCMetricsSampleInfo) alloc] initWithNativeSampleInfo:*histogram.second]; + [metrics addObject:metric]; + } + return metrics; +} diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo+Private.h new file mode 100644 index 0000000000..e4aa41f6c7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo+Private.h @@ -0,0 +1,25 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMetricsSampleInfo.h" + +#include "system_wrappers/include/metrics.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCMetricsSampleInfo) +() + + /** Initialize an RTCMetricsSampleInfo object from native SampleInfo. */ + - (instancetype)initWithNativeSampleInfo : (const webrtc::metrics::SampleInfo &)info; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo.h new file mode 100644 index 0000000000..47a877b6fb --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo.h @@ -0,0 +1,48 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMetricsSampleInfo) : NSObject + +/** + * Example of RTCMetricsSampleInfo: + * name: "WebRTC.Video.InputFramesPerSecond" + * min: 1 + * max: 100 + * bucketCount: 50 + * samples: [29]:2 [30]:1 + */ + +/** The name of the histogram. */ +@property(nonatomic, readonly) NSString *name; + +/** The minimum bucket value. */ +@property(nonatomic, readonly) int min; + +/** The maximum bucket value. */ +@property(nonatomic, readonly) int max; + +/** The number of buckets. */ +@property(nonatomic, readonly) int bucketCount; + +/** A dictionary holding the samples <value, # of events>. */ +@property(nonatomic, readonly) NSDictionary<NSNumber *, NSNumber *> *samples; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo.mm new file mode 100644 index 0000000000..e4be94e90a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCMetricsSampleInfo.mm @@ -0,0 +1,43 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMetricsSampleInfo+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCMetricsSampleInfo) + +@synthesize name = _name; +@synthesize min = _min; +@synthesize max = _max; +@synthesize bucketCount = _bucketCount; +@synthesize samples = _samples; + +#pragma mark - Private + +- (instancetype)initWithNativeSampleInfo: + (const webrtc::metrics::SampleInfo &)info { + if (self = [super init]) { + _name = [NSString stringForStdString:info.name]; + _min = info.min; + _max = info.max; + _bucketCount = info.bucket_count; + + NSMutableDictionary *samples = + [NSMutableDictionary dictionaryWithCapacity:info.samples.size()]; + for (auto const &sample : info.samples) { + [samples setObject:@(sample.second) forKey:@(sample.first)]; + } + _samples = samples; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+DataChannel.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+DataChannel.mm new file mode 100644 index 0000000000..cb75f061d8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+DataChannel.mm @@ -0,0 +1,34 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnection+Private.h" + +#import "RTCDataChannel+Private.h" +#import "RTCDataChannelConfiguration+Private.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCPeerConnection) +(DataChannel) + + - (nullable RTC_OBJC_TYPE(RTCDataChannel) *)dataChannelForLabel + : (NSString *)label configuration + : (RTC_OBJC_TYPE(RTCDataChannelConfiguration) *)configuration { + std::string labelString = [NSString stdStringForString:label]; + const webrtc::DataChannelInit nativeInit = + configuration.nativeDataChannelInit; + auto result = self.nativePeerConnection->CreateDataChannelOrError(labelString, &nativeInit); + if (!result.ok()) { + return nil; + } + return [[RTC_OBJC_TYPE(RTCDataChannel) alloc] initWithFactory:self.factory + nativeDataChannel:result.MoveValue()]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+Private.h new file mode 100644 index 0000000000..00f2ef7834 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+Private.h @@ -0,0 +1,143 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnection.h" + +#include "api/peer_connection_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace webrtc { + +/** + * These objects are created by RTCPeerConnectionFactory to wrap an + * id<RTCPeerConnectionDelegate> and call methods on that interface. + */ +class PeerConnectionDelegateAdapter : public PeerConnectionObserver { + public: + PeerConnectionDelegateAdapter(RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection); + ~PeerConnectionDelegateAdapter() override; + + void OnSignalingChange(PeerConnectionInterface::SignalingState new_state) override; + + void OnAddStream(rtc::scoped_refptr<MediaStreamInterface> stream) override; + + void OnRemoveStream(rtc::scoped_refptr<MediaStreamInterface> stream) override; + + void OnTrack(rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override; + + void OnDataChannel(rtc::scoped_refptr<DataChannelInterface> data_channel) override; + + void OnRenegotiationNeeded() override; + + void OnIceConnectionChange(PeerConnectionInterface::IceConnectionState new_state) override; + + void OnStandardizedIceConnectionChange( + PeerConnectionInterface::IceConnectionState new_state) override; + + void OnConnectionChange(PeerConnectionInterface::PeerConnectionState new_state) override; + + void OnIceGatheringChange(PeerConnectionInterface::IceGatheringState new_state) override; + + void OnIceCandidate(const IceCandidateInterface *candidate) override; + + void OnIceCandidateError(const std::string &address, + int port, + const std::string &url, + int error_code, + const std::string &error_text) override; + + void OnIceCandidatesRemoved(const std::vector<cricket::Candidate> &candidates) override; + + void OnIceSelectedCandidatePairChanged(const cricket::CandidatePairChangeEvent &event) override; + + void OnAddTrack(rtc::scoped_refptr<RtpReceiverInterface> receiver, + const std::vector<rtc::scoped_refptr<MediaStreamInterface>> &streams) override; + + void OnRemoveTrack(rtc::scoped_refptr<RtpReceiverInterface> receiver) override; + + private: + __weak RTC_OBJC_TYPE(RTCPeerConnection) * peer_connection_; +}; + +} // namespace webrtc +@protocol RTC_OBJC_TYPE +(RTCSSLCertificateVerifier); + +@interface RTC_OBJC_TYPE (RTCPeerConnection) +() + + /** The factory used to create this RTCPeerConnection */ + @property(nonatomic, readonly) RTC_OBJC_TYPE(RTCPeerConnectionFactory) * + factory; + +/** The native PeerConnectionInterface created during construction. */ +@property(nonatomic, readonly) rtc::scoped_refptr<webrtc::PeerConnectionInterface> + nativePeerConnection; + +/** Initialize an RTCPeerConnection with a configuration, constraints, and + * delegate. + */ +- (nullable instancetype) + initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + configuration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + certificateVerifier:(nullable id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)>)certificateVerifier + delegate:(nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate; + +/** Initialize an RTCPeerConnection with a configuration, constraints, + * delegate and PeerConnectionDependencies. + */ +- (nullable instancetype) + initWithDependencies:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + configuration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + dependencies:(std::unique_ptr<webrtc::PeerConnectionDependencies>)dependencies + delegate:(nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate + NS_DESIGNATED_INITIALIZER; + ++ (webrtc::PeerConnectionInterface::SignalingState)nativeSignalingStateForState: + (RTCSignalingState)state; + ++ (RTCSignalingState)signalingStateForNativeState: + (webrtc::PeerConnectionInterface::SignalingState)nativeState; + ++ (NSString *)stringForSignalingState:(RTCSignalingState)state; + ++ (webrtc::PeerConnectionInterface::IceConnectionState)nativeIceConnectionStateForState: + (RTCIceConnectionState)state; + ++ (webrtc::PeerConnectionInterface::PeerConnectionState)nativeConnectionStateForState: + (RTCPeerConnectionState)state; + ++ (RTCIceConnectionState)iceConnectionStateForNativeState: + (webrtc::PeerConnectionInterface::IceConnectionState)nativeState; + ++ (RTCPeerConnectionState)connectionStateForNativeState: + (webrtc::PeerConnectionInterface::PeerConnectionState)nativeState; + ++ (NSString *)stringForIceConnectionState:(RTCIceConnectionState)state; + ++ (NSString *)stringForConnectionState:(RTCPeerConnectionState)state; + ++ (webrtc::PeerConnectionInterface::IceGatheringState)nativeIceGatheringStateForState: + (RTCIceGatheringState)state; + ++ (RTCIceGatheringState)iceGatheringStateForNativeState: + (webrtc::PeerConnectionInterface::IceGatheringState)nativeState; + ++ (NSString *)stringForIceGatheringState:(RTCIceGatheringState)state; + ++ (webrtc::PeerConnectionInterface::StatsOutputLevel)nativeStatsOutputLevelForLevel: + (RTCStatsOutputLevel)level; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm new file mode 100644 index 0000000000..f8d38143f3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm @@ -0,0 +1,102 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnection+Private.h" + +#import "RTCLegacyStatsReport+Private.h" +#import "RTCMediaStreamTrack+Private.h" +#import "RTCRtpReceiver+Private.h" +#import "RTCRtpSender+Private.h" +#import "RTCStatisticsReport+Private.h" +#import "helpers/NSString+StdString.h" + +#include "rtc_base/checks.h" + +namespace webrtc { + +class StatsCollectorCallbackAdapter : public RTCStatsCollectorCallback { + public: + StatsCollectorCallbackAdapter(RTCStatisticsCompletionHandler completion_handler) + : completion_handler_(completion_handler) {} + + void OnStatsDelivered(const rtc::scoped_refptr<const RTCStatsReport> &report) override { + RTC_DCHECK(completion_handler_); + RTC_OBJC_TYPE(RTCStatisticsReport) *statisticsReport = + [[RTC_OBJC_TYPE(RTCStatisticsReport) alloc] initWithReport:*report]; + completion_handler_(statisticsReport); + completion_handler_ = nil; + } + + private: + RTCStatisticsCompletionHandler completion_handler_; +}; + +class StatsObserverAdapter : public StatsObserver { + public: + StatsObserverAdapter( + void (^completionHandler)(NSArray<RTC_OBJC_TYPE(RTCLegacyStatsReport) *> *stats)) { + completion_handler_ = completionHandler; + } + + ~StatsObserverAdapter() override { completion_handler_ = nil; } + + void OnComplete(const StatsReports& reports) override { + RTC_DCHECK(completion_handler_); + NSMutableArray *stats = [NSMutableArray arrayWithCapacity:reports.size()]; + for (const auto* report : reports) { + RTC_OBJC_TYPE(RTCLegacyStatsReport) *statsReport = + [[RTC_OBJC_TYPE(RTCLegacyStatsReport) alloc] initWithNativeReport:*report]; + [stats addObject:statsReport]; + } + completion_handler_(stats); + completion_handler_ = nil; + } + + private: + void (^completion_handler_)(NSArray<RTC_OBJC_TYPE(RTCLegacyStatsReport) *> *stats); +}; +} // namespace webrtc + +@implementation RTC_OBJC_TYPE (RTCPeerConnection) +(Stats) + + - (void)statisticsForSender : (RTC_OBJC_TYPE(RTCRtpSender) *)sender completionHandler + : (RTCStatisticsCompletionHandler)completionHandler { + rtc::scoped_refptr<webrtc::StatsCollectorCallbackAdapter> collector = + rtc::make_ref_counted<webrtc::StatsCollectorCallbackAdapter>(completionHandler); + self.nativePeerConnection->GetStats(sender.nativeRtpSender, collector); +} + +- (void)statisticsForReceiver:(RTC_OBJC_TYPE(RTCRtpReceiver) *)receiver + completionHandler:(RTCStatisticsCompletionHandler)completionHandler { + rtc::scoped_refptr<webrtc::StatsCollectorCallbackAdapter> collector = + rtc::make_ref_counted<webrtc::StatsCollectorCallbackAdapter>(completionHandler); + self.nativePeerConnection->GetStats(receiver.nativeRtpReceiver, collector); +} + +- (void)statisticsWithCompletionHandler:(RTCStatisticsCompletionHandler)completionHandler { + rtc::scoped_refptr<webrtc::StatsCollectorCallbackAdapter> collector = + rtc::make_ref_counted<webrtc::StatsCollectorCallbackAdapter>(completionHandler); + self.nativePeerConnection->GetStats(collector.get()); +} + +- (void)statsForTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)mediaStreamTrack + statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel + completionHandler: + (void (^)(NSArray<RTC_OBJC_TYPE(RTCLegacyStatsReport) *> *stats))completionHandler { + rtc::scoped_refptr<webrtc::StatsObserverAdapter> observer = + rtc::make_ref_counted<webrtc::StatsObserverAdapter>(completionHandler); + webrtc::PeerConnectionInterface::StatsOutputLevel nativeOutputLevel = + [[self class] nativeStatsOutputLevelForLevel:statsOutputLevel]; + self.nativePeerConnection->GetStats( + observer.get(), mediaStreamTrack.nativeTrack.get(), nativeOutputLevel); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection.h new file mode 100644 index 0000000000..55af6868fd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection.h @@ -0,0 +1,398 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +@class RTC_OBJC_TYPE(RTCConfiguration); +@class RTC_OBJC_TYPE(RTCDataChannel); +@class RTC_OBJC_TYPE(RTCDataChannelConfiguration); +@class RTC_OBJC_TYPE(RTCIceCandidate); +@class RTC_OBJC_TYPE(RTCIceCandidateErrorEvent); +@class RTC_OBJC_TYPE(RTCMediaConstraints); +@class RTC_OBJC_TYPE(RTCMediaStream); +@class RTC_OBJC_TYPE(RTCMediaStreamTrack); +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); +@class RTC_OBJC_TYPE(RTCRtpReceiver); +@class RTC_OBJC_TYPE(RTCRtpSender); +@class RTC_OBJC_TYPE(RTCRtpTransceiver); +@class RTC_OBJC_TYPE(RTCRtpTransceiverInit); +@class RTC_OBJC_TYPE(RTCSessionDescription); +@class RTC_OBJC_TYPE(RTCStatisticsReport); +@class RTC_OBJC_TYPE(RTCLegacyStatsReport); + +typedef NS_ENUM(NSInteger, RTCRtpMediaType); + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kRTCPeerConnectionErrorDomain; +extern int const kRTCSessionDescriptionErrorCode; + +/** Represents the signaling state of the peer connection. */ +typedef NS_ENUM(NSInteger, RTCSignalingState) { + RTCSignalingStateStable, + RTCSignalingStateHaveLocalOffer, + RTCSignalingStateHaveLocalPrAnswer, + RTCSignalingStateHaveRemoteOffer, + RTCSignalingStateHaveRemotePrAnswer, + // Not an actual state, represents the total number of states. + RTCSignalingStateClosed, +}; + +/** Represents the ice connection state of the peer connection. */ +typedef NS_ENUM(NSInteger, RTCIceConnectionState) { + RTCIceConnectionStateNew, + RTCIceConnectionStateChecking, + RTCIceConnectionStateConnected, + RTCIceConnectionStateCompleted, + RTCIceConnectionStateFailed, + RTCIceConnectionStateDisconnected, + RTCIceConnectionStateClosed, + RTCIceConnectionStateCount, +}; + +/** Represents the combined ice+dtls connection state of the peer connection. */ +typedef NS_ENUM(NSInteger, RTCPeerConnectionState) { + RTCPeerConnectionStateNew, + RTCPeerConnectionStateConnecting, + RTCPeerConnectionStateConnected, + RTCPeerConnectionStateDisconnected, + RTCPeerConnectionStateFailed, + RTCPeerConnectionStateClosed, +}; + +/** Represents the ice gathering state of the peer connection. */ +typedef NS_ENUM(NSInteger, RTCIceGatheringState) { + RTCIceGatheringStateNew, + RTCIceGatheringStateGathering, + RTCIceGatheringStateComplete, +}; + +/** Represents the stats output level. */ +typedef NS_ENUM(NSInteger, RTCStatsOutputLevel) { + RTCStatsOutputLevelStandard, + RTCStatsOutputLevelDebug, +}; + +typedef void (^RTCCreateSessionDescriptionCompletionHandler)(RTC_OBJC_TYPE(RTCSessionDescription) * + _Nullable sdp, + NSError *_Nullable error); + +typedef void (^RTCSetSessionDescriptionCompletionHandler)(NSError *_Nullable error); + +@class RTC_OBJC_TYPE(RTCPeerConnection); + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCPeerConnectionDelegate)<NSObject> + + /** Called when the SignalingState changed. */ + - (void)peerConnection + : (RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection didChangeSignalingState + : (RTCSignalingState)stateChanged; + +/** Called when media is received on a new stream from remote peer. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didAddStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream; + +/** Called when a remote peer closes a stream. + * This is not called when RTCSdpSemanticsUnifiedPlan is specified. + */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didRemoveStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream; + +/** Called when negotiation is needed, for example ICE has restarted. */ +- (void)peerConnectionShouldNegotiate:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection; + +/** Called any time the IceConnectionState changes. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didChangeIceConnectionState:(RTCIceConnectionState)newState; + +/** Called any time the IceGatheringState changes. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didChangeIceGatheringState:(RTCIceGatheringState)newState; + +/** New ice candidate has been found. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didGenerateIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate; + +/** Called when a group of local Ice candidates have been removed. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didRemoveIceCandidates:(NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)candidates; + +/** New data channel has been opened. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didOpenDataChannel:(RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel; + +/** Called when signaling indicates a transceiver will be receiving media from + * the remote endpoint. + * This is only called with RTCSdpSemanticsUnifiedPlan specified. + */ +@optional +/** Called any time the IceConnectionState changes following standardized + * transition. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didChangeStandardizedIceConnectionState:(RTCIceConnectionState)newState; + +/** Called any time the PeerConnectionState changes. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didChangeConnectionState:(RTCPeerConnectionState)newState; + +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didStartReceivingOnTransceiver:(RTC_OBJC_TYPE(RTCRtpTransceiver) *)transceiver; + +/** Called when a receiver and its track are created. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didAddReceiver:(RTC_OBJC_TYPE(RTCRtpReceiver) *)rtpReceiver + streams:(NSArray<RTC_OBJC_TYPE(RTCMediaStream) *> *)mediaStreams; + +/** Called when the receiver and its track are removed. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didRemoveReceiver:(RTC_OBJC_TYPE(RTCRtpReceiver) *)rtpReceiver; + +/** Called when the selected ICE candidate pair is changed. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didChangeLocalCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)local + remoteCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)remote + lastReceivedMs:(int)lastDataReceivedMs + changeReason:(NSString *)reason; + +/** Called when gathering of an ICE candidate failed. */ +- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection + didFailToGatherIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidateErrorEvent) *)event; + +@end + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCPeerConnection) : NSObject + +/** The object that will be notifed about events such as state changes and + * streams being added or removed. + */ +@property(nonatomic, weak, nullable) id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)> delegate; +/** This property is not available with RTCSdpSemanticsUnifiedPlan. Please use + * `senders` instead. + */ +@property(nonatomic, readonly) NSArray<RTC_OBJC_TYPE(RTCMediaStream) *> *localStreams; +@property(nonatomic, readonly, nullable) RTC_OBJC_TYPE(RTCSessionDescription) * localDescription; +@property(nonatomic, readonly, nullable) RTC_OBJC_TYPE(RTCSessionDescription) * remoteDescription; +@property(nonatomic, readonly) RTCSignalingState signalingState; +@property(nonatomic, readonly) RTCIceConnectionState iceConnectionState; +@property(nonatomic, readonly) RTCPeerConnectionState connectionState; +@property(nonatomic, readonly) RTCIceGatheringState iceGatheringState; +@property(nonatomic, readonly, copy) RTC_OBJC_TYPE(RTCConfiguration) * configuration; + +/** Gets all RTCRtpSenders associated with this peer connection. + * Note: reading this property returns different instances of RTCRtpSender. + * Use isEqual: instead of == to compare RTCRtpSender instances. + */ +@property(nonatomic, readonly) NSArray<RTC_OBJC_TYPE(RTCRtpSender) *> *senders; + +/** Gets all RTCRtpReceivers associated with this peer connection. + * Note: reading this property returns different instances of RTCRtpReceiver. + * Use isEqual: instead of == to compare RTCRtpReceiver instances. + */ +@property(nonatomic, readonly) NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *receivers; + +/** Gets all RTCRtpTransceivers associated with this peer connection. + * Note: reading this property returns different instances of + * RTCRtpTransceiver. Use isEqual: instead of == to compare + * RTCRtpTransceiver instances. This is only available with + * RTCSdpSemanticsUnifiedPlan specified. + */ +@property(nonatomic, readonly) NSArray<RTC_OBJC_TYPE(RTCRtpTransceiver) *> *transceivers; + +- (instancetype)init NS_UNAVAILABLE; + +/** Sets the PeerConnection's global configuration to `configuration`. + * Any changes to STUN/TURN servers or ICE candidate policy will affect the + * next gathering phase, and cause the next call to createOffer to generate + * new ICE credentials. Note that the BUNDLE and RTCP-multiplexing policies + * cannot be changed with this method. + */ +- (BOOL)setConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration; + +/** Terminate all media and close the transport. */ +- (void)close; + +/** Provide a remote candidate to the ICE Agent. */ +- (void)addIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate + DEPRECATED_MSG_ATTRIBUTE("Please use addIceCandidate:completionHandler: instead"); + +/** Provide a remote candidate to the ICE Agent. */ +- (void)addIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate + completionHandler:(void (^)(NSError *_Nullable error))completionHandler; + +/** Remove a group of remote candidates from the ICE Agent. */ +- (void)removeIceCandidates:(NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)candidates; + +/** Add a new media stream to be sent on this peer connection. + * This method is not supported with RTCSdpSemanticsUnifiedPlan. Please use + * addTrack instead. + */ +- (void)addStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream; + +/** Remove the given media stream from this peer connection. + * This method is not supported with RTCSdpSemanticsUnifiedPlan. Please use + * removeTrack instead. + */ +- (void)removeStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream; + +/** Add a new media stream track to be sent on this peer connection, and return + * the newly created RTCRtpSender. The RTCRtpSender will be + * associated with the streams specified in the `streamIds` list. + * + * Errors: If an error occurs, returns nil. An error can occur if: + * - A sender already exists for the track. + * - The peer connection is closed. + */ +- (nullable RTC_OBJC_TYPE(RTCRtpSender) *)addTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track + streamIds:(NSArray<NSString *> *)streamIds; + +/** With PlanB semantics, removes an RTCRtpSender from this peer connection. + * + * With UnifiedPlan semantics, sets sender's track to null and removes the + * send component from the associated RTCRtpTransceiver's direction. + * + * Returns YES on success. + */ +- (BOOL)removeTrack:(RTC_OBJC_TYPE(RTCRtpSender) *)sender; + +/** addTransceiver creates a new RTCRtpTransceiver and adds it to the set of + * transceivers. Adding a transceiver will cause future calls to CreateOffer + * to add a media description for the corresponding transceiver. + * + * The initial value of `mid` in the returned transceiver is nil. Setting a + * new session description may change it to a non-nil value. + * + * https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver + * + * Optionally, an RtpTransceiverInit structure can be specified to configure + * the transceiver from construction. If not specified, the transceiver will + * default to having a direction of kSendRecv and not be part of any streams. + * + * These methods are only available when Unified Plan is enabled (see + * RTCConfiguration). + */ + +/** Adds a transceiver with a sender set to transmit the given track. The kind + * of the transceiver (and sender/receiver) will be derived from the kind of + * the track. + */ +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverWithTrack: + (RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track; +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *) + addTransceiverWithTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track + init:(RTC_OBJC_TYPE(RTCRtpTransceiverInit) *)init; + +/** Adds a transceiver with the given kind. Can either be RTCRtpMediaTypeAudio + * or RTCRtpMediaTypeVideo. + */ +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverOfType:(RTCRtpMediaType)mediaType; +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *) + addTransceiverOfType:(RTCRtpMediaType)mediaType + init:(RTC_OBJC_TYPE(RTCRtpTransceiverInit) *)init; + +/** Tells the PeerConnection that ICE should be restarted. This triggers a need + * for negotiation and subsequent offerForConstraints:completionHandler call will act as if + * RTCOfferAnswerOptions::ice_restart is true. + */ +- (void)restartIce; + +/** Generate an SDP offer. */ +- (void)offerForConstraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + completionHandler:(RTCCreateSessionDescriptionCompletionHandler)completionHandler; + +/** Generate an SDP answer. */ +- (void)answerForConstraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + completionHandler:(RTCCreateSessionDescriptionCompletionHandler)completionHandler; + +/** Apply the supplied RTCSessionDescription as the local description. */ +- (void)setLocalDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp + completionHandler:(RTCSetSessionDescriptionCompletionHandler)completionHandler; + +/** Creates an offer or answer (depending on current signaling state) and sets + * it as the local session description. */ +- (void)setLocalDescriptionWithCompletionHandler: + (RTCSetSessionDescriptionCompletionHandler)completionHandler; + +/** Apply the supplied RTCSessionDescription as the remote description. */ +- (void)setRemoteDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp + completionHandler:(RTCSetSessionDescriptionCompletionHandler)completionHandler; + +/** Limits the bandwidth allocated for all RTP streams sent by this + * PeerConnection. Nil parameters will be unchanged. Setting + * `currentBitrateBps` will force the available bitrate estimate to the given + * value. Returns YES if the parameters were successfully updated. + */ +- (BOOL)setBweMinBitrateBps:(nullable NSNumber *)minBitrateBps + currentBitrateBps:(nullable NSNumber *)currentBitrateBps + maxBitrateBps:(nullable NSNumber *)maxBitrateBps; + +/** Start or stop recording an Rtc EventLog. */ +- (BOOL)startRtcEventLogWithFilePath:(NSString *)filePath maxSizeInBytes:(int64_t)maxSizeInBytes; +- (void)stopRtcEventLog; + +@end + +@interface RTC_OBJC_TYPE (RTCPeerConnection) +(Media) + + /** Create an RTCRtpSender with the specified kind and media stream ID. + * See RTCMediaStreamTrack.h for available kinds. + * This method is not supported with RTCSdpSemanticsUnifiedPlan. Please use + * addTransceiver instead. + */ + - (RTC_OBJC_TYPE(RTCRtpSender) *)senderWithKind : (NSString *)kind streamId + : (NSString *)streamId; + +@end + +@interface RTC_OBJC_TYPE (RTCPeerConnection) +(DataChannel) + + /** Create a new data channel with the given label and configuration. */ + - (nullable RTC_OBJC_TYPE(RTCDataChannel) *)dataChannelForLabel + : (NSString *)label configuration : (RTC_OBJC_TYPE(RTCDataChannelConfiguration) *)configuration; + +@end + +typedef void (^RTCStatisticsCompletionHandler)(RTC_OBJC_TYPE(RTCStatisticsReport) *); + +@interface RTC_OBJC_TYPE (RTCPeerConnection) +(Stats) + + /** Gather stats for the given RTCMediaStreamTrack. If `mediaStreamTrack` is nil + * statistics are gathered for all tracks. + */ + - (void)statsForTrack + : (nullable RTC_OBJC_TYPE(RTCMediaStreamTrack) *)mediaStreamTrack statsOutputLevel + : (RTCStatsOutputLevel)statsOutputLevel completionHandler + : (nullable void (^)(NSArray<RTC_OBJC_TYPE(RTCLegacyStatsReport) *> *stats))completionHandler; + +/** Gather statistic through the v2 statistics API. */ +- (void)statisticsWithCompletionHandler:(RTCStatisticsCompletionHandler)completionHandler; + +/** Spec-compliant getStats() performing the stats selection algorithm with the + * sender. + */ +- (void)statisticsForSender:(RTC_OBJC_TYPE(RTCRtpSender) *)sender + completionHandler:(RTCStatisticsCompletionHandler)completionHandler; + +/** Spec-compliant getStats() performing the stats selection algorithm with the + * receiver. + */ +- (void)statisticsForReceiver:(RTC_OBJC_TYPE(RTCRtpReceiver) *)receiver + completionHandler:(RTCStatisticsCompletionHandler)completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection.mm new file mode 100644 index 0000000000..df99030111 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnection.mm @@ -0,0 +1,935 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnection+Private.h" + +#import "RTCConfiguration+Private.h" +#import "RTCDataChannel+Private.h" +#import "RTCIceCandidate+Private.h" +#import "RTCIceCandidateErrorEvent+Private.h" +#import "RTCLegacyStatsReport+Private.h" +#import "RTCMediaConstraints+Private.h" +#import "RTCMediaStream+Private.h" +#import "RTCMediaStreamTrack+Private.h" +#import "RTCPeerConnectionFactory+Private.h" +#import "RTCRtpReceiver+Private.h" +#import "RTCRtpSender+Private.h" +#import "RTCRtpTransceiver+Private.h" +#import "RTCSessionDescription+Private.h" +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +#include <memory> + +#include "api/jsep_ice_candidate.h" +#include "api/rtc_event_log_output_file.h" +#include "api/set_local_description_observer_interface.h" +#include "api/set_remote_description_observer_interface.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "sdk/objc/native/api/ssl_certificate_verifier.h" + +NSString *const kRTCPeerConnectionErrorDomain = @"org.webrtc.RTC_OBJC_TYPE(RTCPeerConnection)"; +int const kRTCPeerConnnectionSessionDescriptionError = -1; + +namespace { + +class SetSessionDescriptionObserver : public webrtc::SetLocalDescriptionObserverInterface, + public webrtc::SetRemoteDescriptionObserverInterface { + public: + SetSessionDescriptionObserver(RTCSetSessionDescriptionCompletionHandler completionHandler) { + completion_handler_ = completionHandler; + } + + virtual void OnSetLocalDescriptionComplete(webrtc::RTCError error) override { + OnCompelete(error); + } + + virtual void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { + OnCompelete(error); + } + + private: + void OnCompelete(webrtc::RTCError error) { + RTC_DCHECK(completion_handler_ != nil); + if (error.ok()) { + completion_handler_(nil); + } else { + // TODO(hta): Add handling of error.type() + NSString *str = [NSString stringForStdString:error.message()]; + NSError *err = [NSError errorWithDomain:kRTCPeerConnectionErrorDomain + code:kRTCPeerConnnectionSessionDescriptionError + userInfo:@{NSLocalizedDescriptionKey : str}]; + completion_handler_(err); + } + completion_handler_ = nil; + } + RTCSetSessionDescriptionCompletionHandler completion_handler_; +}; + +} // anonymous namespace + +namespace webrtc { + +class CreateSessionDescriptionObserverAdapter + : public CreateSessionDescriptionObserver { + public: + CreateSessionDescriptionObserverAdapter(void (^completionHandler)( + RTC_OBJC_TYPE(RTCSessionDescription) * sessionDescription, NSError *error)) { + completion_handler_ = completionHandler; + } + + ~CreateSessionDescriptionObserverAdapter() override { completion_handler_ = nil; } + + void OnSuccess(SessionDescriptionInterface *desc) override { + RTC_DCHECK(completion_handler_); + std::unique_ptr<webrtc::SessionDescriptionInterface> description = + std::unique_ptr<webrtc::SessionDescriptionInterface>(desc); + RTC_OBJC_TYPE(RTCSessionDescription) *session = + [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description.get()]; + completion_handler_(session, nil); + completion_handler_ = nil; + } + + void OnFailure(RTCError error) override { + RTC_DCHECK(completion_handler_); + // TODO(hta): Add handling of error.type() + NSString *str = [NSString stringForStdString:error.message()]; + NSError* err = + [NSError errorWithDomain:kRTCPeerConnectionErrorDomain + code:kRTCPeerConnnectionSessionDescriptionError + userInfo:@{ NSLocalizedDescriptionKey : str }]; + completion_handler_(nil, err); + completion_handler_ = nil; + } + + private: + void (^completion_handler_)(RTC_OBJC_TYPE(RTCSessionDescription) * sessionDescription, + NSError *error); +}; + +PeerConnectionDelegateAdapter::PeerConnectionDelegateAdapter(RTC_OBJC_TYPE(RTCPeerConnection) * + peerConnection) { + peer_connection_ = peerConnection; +} + +PeerConnectionDelegateAdapter::~PeerConnectionDelegateAdapter() { + peer_connection_ = nil; +} + +void PeerConnectionDelegateAdapter::OnSignalingChange( + PeerConnectionInterface::SignalingState new_state) { + RTCSignalingState state = + [[RTC_OBJC_TYPE(RTCPeerConnection) class] signalingStateForNativeState:new_state]; + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + [peer_connection.delegate peerConnection:peer_connection + didChangeSignalingState:state]; +} + +void PeerConnectionDelegateAdapter::OnAddStream( + rtc::scoped_refptr<MediaStreamInterface> stream) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + RTC_OBJC_TYPE(RTCMediaStream) *mediaStream = + [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:peer_connection.factory + nativeMediaStream:stream]; + [peer_connection.delegate peerConnection:peer_connection + didAddStream:mediaStream]; +} + +void PeerConnectionDelegateAdapter::OnRemoveStream( + rtc::scoped_refptr<MediaStreamInterface> stream) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + RTC_OBJC_TYPE(RTCMediaStream) *mediaStream = + [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:peer_connection.factory + nativeMediaStream:stream]; + + [peer_connection.delegate peerConnection:peer_connection + didRemoveStream:mediaStream]; +} + +void PeerConnectionDelegateAdapter::OnTrack( + rtc::scoped_refptr<RtpTransceiverInterface> nativeTransceiver) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver = + [[RTC_OBJC_TYPE(RTCRtpTransceiver) alloc] initWithFactory:peer_connection.factory + nativeRtpTransceiver:nativeTransceiver]; + if ([peer_connection.delegate + respondsToSelector:@selector(peerConnection:didStartReceivingOnTransceiver:)]) { + [peer_connection.delegate peerConnection:peer_connection + didStartReceivingOnTransceiver:transceiver]; + } +} + +void PeerConnectionDelegateAdapter::OnDataChannel( + rtc::scoped_refptr<DataChannelInterface> data_channel) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + RTC_OBJC_TYPE(RTCDataChannel) *dataChannel = + [[RTC_OBJC_TYPE(RTCDataChannel) alloc] initWithFactory:peer_connection.factory + nativeDataChannel:data_channel]; + [peer_connection.delegate peerConnection:peer_connection + didOpenDataChannel:dataChannel]; +} + +void PeerConnectionDelegateAdapter::OnRenegotiationNeeded() { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + [peer_connection.delegate peerConnectionShouldNegotiate:peer_connection]; +} + +void PeerConnectionDelegateAdapter::OnIceConnectionChange( + PeerConnectionInterface::IceConnectionState new_state) { + RTCIceConnectionState state = + [RTC_OBJC_TYPE(RTCPeerConnection) iceConnectionStateForNativeState:new_state]; + [peer_connection_.delegate peerConnection:peer_connection_ didChangeIceConnectionState:state]; +} + +void PeerConnectionDelegateAdapter::OnStandardizedIceConnectionChange( + PeerConnectionInterface::IceConnectionState new_state) { + if ([peer_connection_.delegate + respondsToSelector:@selector(peerConnection:didChangeStandardizedIceConnectionState:)]) { + RTCIceConnectionState state = + [RTC_OBJC_TYPE(RTCPeerConnection) iceConnectionStateForNativeState:new_state]; + [peer_connection_.delegate peerConnection:peer_connection_ + didChangeStandardizedIceConnectionState:state]; + } +} + +void PeerConnectionDelegateAdapter::OnConnectionChange( + PeerConnectionInterface::PeerConnectionState new_state) { + if ([peer_connection_.delegate + respondsToSelector:@selector(peerConnection:didChangeConnectionState:)]) { + RTCPeerConnectionState state = + [RTC_OBJC_TYPE(RTCPeerConnection) connectionStateForNativeState:new_state]; + [peer_connection_.delegate peerConnection:peer_connection_ didChangeConnectionState:state]; + } +} + +void PeerConnectionDelegateAdapter::OnIceGatheringChange( + PeerConnectionInterface::IceGatheringState new_state) { + RTCIceGatheringState state = + [[RTC_OBJC_TYPE(RTCPeerConnection) class] iceGatheringStateForNativeState:new_state]; + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + [peer_connection.delegate peerConnection:peer_connection + didChangeIceGatheringState:state]; +} + +void PeerConnectionDelegateAdapter::OnIceCandidate( + const IceCandidateInterface *candidate) { + RTC_OBJC_TYPE(RTCIceCandidate) *iceCandidate = + [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithNativeCandidate:candidate]; + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + [peer_connection.delegate peerConnection:peer_connection + didGenerateIceCandidate:iceCandidate]; +} + +void PeerConnectionDelegateAdapter::OnIceCandidateError(const std::string &address, + int port, + const std::string &url, + int error_code, + const std::string &error_text) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + RTC_OBJC_TYPE(RTCIceCandidateErrorEvent) *event = + [[RTC_OBJC_TYPE(RTCIceCandidateErrorEvent) alloc] initWithAddress:address + port:port + url:url + errorCode:error_code + errorText:error_text]; + if ([peer_connection.delegate respondsToSelector:@selector(peerConnection: + didFailToGatherIceCandidate:)]) { + [peer_connection.delegate peerConnection:peer_connection didFailToGatherIceCandidate:event]; + } +} + +void PeerConnectionDelegateAdapter::OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) { + NSMutableArray* ice_candidates = + [NSMutableArray arrayWithCapacity:candidates.size()]; + for (const auto& candidate : candidates) { + std::unique_ptr<JsepIceCandidate> candidate_wrapper( + new JsepIceCandidate(candidate.transport_name(), -1, candidate)); + RTC_OBJC_TYPE(RTCIceCandidate) *ice_candidate = + [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithNativeCandidate:candidate_wrapper.get()]; + [ice_candidates addObject:ice_candidate]; + } + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + [peer_connection.delegate peerConnection:peer_connection + didRemoveIceCandidates:ice_candidates]; +} + +void PeerConnectionDelegateAdapter::OnIceSelectedCandidatePairChanged( + const cricket::CandidatePairChangeEvent &event) { + const auto &selected_pair = event.selected_candidate_pair; + auto local_candidate_wrapper = std::make_unique<JsepIceCandidate>( + selected_pair.local_candidate().transport_name(), -1, selected_pair.local_candidate()); + RTC_OBJC_TYPE(RTCIceCandidate) *local_candidate = [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] + initWithNativeCandidate:local_candidate_wrapper.release()]; + auto remote_candidate_wrapper = std::make_unique<JsepIceCandidate>( + selected_pair.remote_candidate().transport_name(), -1, selected_pair.remote_candidate()); + RTC_OBJC_TYPE(RTCIceCandidate) *remote_candidate = [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] + initWithNativeCandidate:remote_candidate_wrapper.release()]; + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + NSString *nsstr_reason = [NSString stringForStdString:event.reason]; + if ([peer_connection.delegate + respondsToSelector:@selector + (peerConnection:didChangeLocalCandidate:remoteCandidate:lastReceivedMs:changeReason:)]) { + [peer_connection.delegate peerConnection:peer_connection + didChangeLocalCandidate:local_candidate + remoteCandidate:remote_candidate + lastReceivedMs:event.last_data_received_ms + changeReason:nsstr_reason]; + } +} + +void PeerConnectionDelegateAdapter::OnAddTrack( + rtc::scoped_refptr<RtpReceiverInterface> receiver, + const std::vector<rtc::scoped_refptr<MediaStreamInterface>> &streams) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + if ([peer_connection.delegate respondsToSelector:@selector(peerConnection: + didAddReceiver:streams:)]) { + NSMutableArray *mediaStreams = [NSMutableArray arrayWithCapacity:streams.size()]; + for (const auto &nativeStream : streams) { + RTC_OBJC_TYPE(RTCMediaStream) *mediaStream = + [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:peer_connection.factory + nativeMediaStream:nativeStream]; + [mediaStreams addObject:mediaStream]; + } + RTC_OBJC_TYPE(RTCRtpReceiver) *rtpReceiver = + [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:peer_connection.factory + nativeRtpReceiver:receiver]; + + [peer_connection.delegate peerConnection:peer_connection + didAddReceiver:rtpReceiver + streams:mediaStreams]; + } +} + +void PeerConnectionDelegateAdapter::OnRemoveTrack( + rtc::scoped_refptr<RtpReceiverInterface> receiver) { + RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; + if ([peer_connection.delegate respondsToSelector:@selector(peerConnection:didRemoveReceiver:)]) { + RTC_OBJC_TYPE(RTCRtpReceiver) *rtpReceiver = + [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:peer_connection.factory + nativeRtpReceiver:receiver]; + [peer_connection.delegate peerConnection:peer_connection didRemoveReceiver:rtpReceiver]; + } +} + +} // namespace webrtc + +@implementation RTC_OBJC_TYPE (RTCPeerConnection) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + NSMutableArray<RTC_OBJC_TYPE(RTCMediaStream) *> *_localStreams; + std::unique_ptr<webrtc::PeerConnectionDelegateAdapter> _observer; + rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection; + std::unique_ptr<webrtc::MediaConstraints> _nativeConstraints; + BOOL _hasStartedRtcEventLog; +} + +@synthesize delegate = _delegate; +@synthesize factory = _factory; + +- (nullable instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + configuration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + certificateVerifier: + (nullable id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)>)certificateVerifier + delegate:(id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { + NSParameterAssert(factory); + std::unique_ptr<webrtc::PeerConnectionDependencies> dependencies = + std::make_unique<webrtc::PeerConnectionDependencies>(nullptr); + if (certificateVerifier != nil) { + dependencies->tls_cert_verifier = webrtc::ObjCToNativeCertificateVerifier(certificateVerifier); + } + return [self initWithDependencies:factory + configuration:configuration + constraints:constraints + dependencies:std::move(dependencies) + delegate:delegate]; +} + +- (nullable instancetype) + initWithDependencies:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + configuration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + dependencies:(std::unique_ptr<webrtc::PeerConnectionDependencies>)dependencies + delegate:(id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { + NSParameterAssert(factory); + NSParameterAssert(dependencies.get()); + std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> config( + [configuration createNativeConfiguration]); + if (!config) { + return nil; + } + if (self = [super init]) { + _observer.reset(new webrtc::PeerConnectionDelegateAdapter(self)); + _nativeConstraints = constraints.nativeConstraints; + CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), config.get()); + + webrtc::PeerConnectionDependencies deps = std::move(*dependencies.release()); + deps.observer = _observer.get(); + auto result = factory.nativeFactory->CreatePeerConnectionOrError(*config, std::move(deps)); + + if (!result.ok()) { + return nil; + } + _peerConnection = result.MoveValue(); + _factory = factory; + _localStreams = [[NSMutableArray alloc] init]; + _delegate = delegate; + } + return self; +} + +- (NSArray<RTC_OBJC_TYPE(RTCMediaStream) *> *)localStreams { + return [_localStreams copy]; +} + +- (RTC_OBJC_TYPE(RTCSessionDescription) *)localDescription { + // It's only safe to operate on SessionDescriptionInterface on the signaling thread. + return _peerConnection->signaling_thread()->BlockingCall([self] { + const webrtc::SessionDescriptionInterface *description = _peerConnection->local_description(); + return description ? + [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] : + nil; + }); +} + +- (RTC_OBJC_TYPE(RTCSessionDescription) *)remoteDescription { + // It's only safe to operate on SessionDescriptionInterface on the signaling thread. + return _peerConnection->signaling_thread()->BlockingCall([self] { + const webrtc::SessionDescriptionInterface *description = _peerConnection->remote_description(); + return description ? + [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] : + nil; + }); +} + +- (RTCSignalingState)signalingState { + return [[self class] + signalingStateForNativeState:_peerConnection->signaling_state()]; +} + +- (RTCIceConnectionState)iceConnectionState { + return [[self class] iceConnectionStateForNativeState: + _peerConnection->ice_connection_state()]; +} + +- (RTCPeerConnectionState)connectionState { + return [[self class] connectionStateForNativeState:_peerConnection->peer_connection_state()]; +} + +- (RTCIceGatheringState)iceGatheringState { + return [[self class] iceGatheringStateForNativeState: + _peerConnection->ice_gathering_state()]; +} + +- (BOOL)setConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration { + std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> config( + [configuration createNativeConfiguration]); + if (!config) { + return NO; + } + CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), + config.get()); + return _peerConnection->SetConfiguration(*config).ok(); +} + +- (RTC_OBJC_TYPE(RTCConfiguration) *)configuration { + webrtc::PeerConnectionInterface::RTCConfiguration config = + _peerConnection->GetConfiguration(); + return [[RTC_OBJC_TYPE(RTCConfiguration) alloc] initWithNativeConfiguration:config]; +} + +- (void)close { + _peerConnection->Close(); +} + +- (void)addIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate { + std::unique_ptr<const webrtc::IceCandidateInterface> iceCandidate( + candidate.nativeCandidate); + _peerConnection->AddIceCandidate(iceCandidate.get()); +} +- (void)addIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate + completionHandler:(void (^)(NSError *_Nullable error))completionHandler { + RTC_DCHECK(completionHandler != nil); + _peerConnection->AddIceCandidate( + candidate.nativeCandidate, [completionHandler](const auto &error) { + if (error.ok()) { + completionHandler(nil); + } else { + NSString *str = [NSString stringForStdString:error.message()]; + NSError *err = [NSError errorWithDomain:kRTCPeerConnectionErrorDomain + code:static_cast<NSInteger>(error.type()) + userInfo:@{NSLocalizedDescriptionKey : str}]; + completionHandler(err); + } + }); +} +- (void)removeIceCandidates:(NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)iceCandidates { + std::vector<cricket::Candidate> candidates; + for (RTC_OBJC_TYPE(RTCIceCandidate) * iceCandidate in iceCandidates) { + std::unique_ptr<const webrtc::IceCandidateInterface> candidate( + iceCandidate.nativeCandidate); + if (candidate) { + candidates.push_back(candidate->candidate()); + // Need to fill the transport name from the sdp_mid. + candidates.back().set_transport_name(candidate->sdp_mid()); + } + } + if (!candidates.empty()) { + _peerConnection->RemoveIceCandidates(candidates); + } +} + +- (void)addStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream { + if (!_peerConnection->AddStream(stream.nativeMediaStream.get())) { + RTCLogError(@"Failed to add stream: %@", stream); + return; + } + [_localStreams addObject:stream]; +} + +- (void)removeStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream { + _peerConnection->RemoveStream(stream.nativeMediaStream.get()); + [_localStreams removeObject:stream]; +} + +- (nullable RTC_OBJC_TYPE(RTCRtpSender) *)addTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track + streamIds:(NSArray<NSString *> *)streamIds { + std::vector<std::string> nativeStreamIds; + for (NSString *streamId in streamIds) { + nativeStreamIds.push_back([streamId UTF8String]); + } + webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>> nativeSenderOrError = + _peerConnection->AddTrack(track.nativeTrack, nativeStreamIds); + if (!nativeSenderOrError.ok()) { + RTCLogError(@"Failed to add track %@: %s", track, nativeSenderOrError.error().message()); + return nil; + } + return [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:self.factory + nativeRtpSender:nativeSenderOrError.MoveValue()]; +} + +- (BOOL)removeTrack:(RTC_OBJC_TYPE(RTCRtpSender) *)sender { + bool result = _peerConnection->RemoveTrackOrError(sender.nativeRtpSender).ok(); + if (!result) { + RTCLogError(@"Failed to remote track %@", sender); + } + return result; +} + +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverWithTrack: + (RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track { + return [self addTransceiverWithTrack:track + init:[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init]]; +} + +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *) + addTransceiverWithTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track + init:(RTC_OBJC_TYPE(RTCRtpTransceiverInit) *)init { + webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceiverOrError = + _peerConnection->AddTransceiver(track.nativeTrack, init.nativeInit); + if (!nativeTransceiverOrError.ok()) { + RTCLogError( + @"Failed to add transceiver %@: %s", track, nativeTransceiverOrError.error().message()); + return nil; + } + return [[RTC_OBJC_TYPE(RTCRtpTransceiver) alloc] + initWithFactory:self.factory + nativeRtpTransceiver:nativeTransceiverOrError.MoveValue()]; +} + +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverOfType:(RTCRtpMediaType)mediaType { + return [self addTransceiverOfType:mediaType + init:[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init]]; +} + +- (nullable RTC_OBJC_TYPE(RTCRtpTransceiver) *) + addTransceiverOfType:(RTCRtpMediaType)mediaType + init:(RTC_OBJC_TYPE(RTCRtpTransceiverInit) *)init { + webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceiverOrError = + _peerConnection->AddTransceiver( + [RTC_OBJC_TYPE(RTCRtpReceiver) nativeMediaTypeForMediaType:mediaType], init.nativeInit); + if (!nativeTransceiverOrError.ok()) { + RTCLogError(@"Failed to add transceiver %@: %s", + [RTC_OBJC_TYPE(RTCRtpReceiver) stringForMediaType:mediaType], + nativeTransceiverOrError.error().message()); + return nil; + } + return [[RTC_OBJC_TYPE(RTCRtpTransceiver) alloc] + initWithFactory:self.factory + nativeRtpTransceiver:nativeTransceiverOrError.MoveValue()]; +} + +- (void)restartIce { + _peerConnection->RestartIce(); +} + +- (void)offerForConstraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + completionHandler:(RTCCreateSessionDescriptionCompletionHandler)completionHandler { + RTC_DCHECK(completionHandler != nil); + rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserverAdapter> observer = + rtc::make_ref_counted<webrtc::CreateSessionDescriptionObserverAdapter>(completionHandler); + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; + CopyConstraintsIntoOfferAnswerOptions(constraints.nativeConstraints.get(), &options); + + _peerConnection->CreateOffer(observer.get(), options); +} + +- (void)answerForConstraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + completionHandler:(RTCCreateSessionDescriptionCompletionHandler)completionHandler { + RTC_DCHECK(completionHandler != nil); + rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserverAdapter> observer = + rtc::make_ref_counted<webrtc::CreateSessionDescriptionObserverAdapter>(completionHandler); + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; + CopyConstraintsIntoOfferAnswerOptions(constraints.nativeConstraints.get(), &options); + + _peerConnection->CreateAnswer(observer.get(), options); +} + +- (void)setLocalDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp + completionHandler:(RTCSetSessionDescriptionCompletionHandler)completionHandler { + RTC_DCHECK(completionHandler != nil); + rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface> observer = + rtc::make_ref_counted<::SetSessionDescriptionObserver>(completionHandler); + _peerConnection->SetLocalDescription(sdp.nativeDescription, observer); +} + +- (void)setLocalDescriptionWithCompletionHandler: + (RTCSetSessionDescriptionCompletionHandler)completionHandler { + RTC_DCHECK(completionHandler != nil); + rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface> observer = + rtc::make_ref_counted<::SetSessionDescriptionObserver>(completionHandler); + _peerConnection->SetLocalDescription(observer); +} + +- (void)setRemoteDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp + completionHandler:(RTCSetSessionDescriptionCompletionHandler)completionHandler { + RTC_DCHECK(completionHandler != nil); + rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface> observer = + rtc::make_ref_counted<::SetSessionDescriptionObserver>(completionHandler); + _peerConnection->SetRemoteDescription(sdp.nativeDescription, observer); +} + +- (BOOL)setBweMinBitrateBps:(nullable NSNumber *)minBitrateBps + currentBitrateBps:(nullable NSNumber *)currentBitrateBps + maxBitrateBps:(nullable NSNumber *)maxBitrateBps { + webrtc::BitrateSettings params; + if (minBitrateBps != nil) { + params.min_bitrate_bps = absl::optional<int>(minBitrateBps.intValue); + } + if (currentBitrateBps != nil) { + params.start_bitrate_bps = absl::optional<int>(currentBitrateBps.intValue); + } + if (maxBitrateBps != nil) { + params.max_bitrate_bps = absl::optional<int>(maxBitrateBps.intValue); + } + return _peerConnection->SetBitrate(params).ok(); +} + +- (BOOL)startRtcEventLogWithFilePath:(NSString *)filePath + maxSizeInBytes:(int64_t)maxSizeInBytes { + RTC_DCHECK(filePath.length); + RTC_DCHECK_GT(maxSizeInBytes, 0); + RTC_DCHECK(!_hasStartedRtcEventLog); + if (_hasStartedRtcEventLog) { + RTCLogError(@"Event logging already started."); + return NO; + } + FILE *f = fopen(filePath.UTF8String, "wb"); + if (!f) { + RTCLogError(@"Error opening file: %@. Error: %d", filePath, errno); + return NO; + } + // TODO(eladalon): It would be better to not allow negative values into PC. + const size_t max_size = (maxSizeInBytes < 0) ? webrtc::RtcEventLog::kUnlimitedOutput : + rtc::saturated_cast<size_t>(maxSizeInBytes); + + _hasStartedRtcEventLog = _peerConnection->StartRtcEventLog( + std::make_unique<webrtc::RtcEventLogOutputFile>(f, max_size)); + return _hasStartedRtcEventLog; +} + +- (void)stopRtcEventLog { + _peerConnection->StopRtcEventLog(); + _hasStartedRtcEventLog = NO; +} + +- (RTC_OBJC_TYPE(RTCRtpSender) *)senderWithKind:(NSString *)kind streamId:(NSString *)streamId { + std::string nativeKind = [NSString stdStringForString:kind]; + std::string nativeStreamId = [NSString stdStringForString:streamId]; + rtc::scoped_refptr<webrtc::RtpSenderInterface> nativeSender( + _peerConnection->CreateSender(nativeKind, nativeStreamId)); + return nativeSender ? [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:self.factory + nativeRtpSender:nativeSender] : + nil; +} + +- (NSArray<RTC_OBJC_TYPE(RTCRtpSender) *> *)senders { + std::vector<rtc::scoped_refptr<webrtc::RtpSenderInterface>> nativeSenders( + _peerConnection->GetSenders()); + NSMutableArray *senders = [[NSMutableArray alloc] init]; + for (const auto &nativeSender : nativeSenders) { + RTC_OBJC_TYPE(RTCRtpSender) *sender = + [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:self.factory + nativeRtpSender:nativeSender]; + [senders addObject:sender]; + } + return senders; +} + +- (NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *)receivers { + std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> nativeReceivers( + _peerConnection->GetReceivers()); + NSMutableArray *receivers = [[NSMutableArray alloc] init]; + for (const auto &nativeReceiver : nativeReceivers) { + RTC_OBJC_TYPE(RTCRtpReceiver) *receiver = + [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:self.factory + nativeRtpReceiver:nativeReceiver]; + [receivers addObject:receiver]; + } + return receivers; +} + +- (NSArray<RTC_OBJC_TYPE(RTCRtpTransceiver) *> *)transceivers { + std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceivers( + _peerConnection->GetTransceivers()); + NSMutableArray *transceivers = [[NSMutableArray alloc] init]; + for (const auto &nativeTransceiver : nativeTransceivers) { + RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver = + [[RTC_OBJC_TYPE(RTCRtpTransceiver) alloc] initWithFactory:self.factory + nativeRtpTransceiver:nativeTransceiver]; + [transceivers addObject:transceiver]; + } + return transceivers; +} + +#pragma mark - Private + ++ (webrtc::PeerConnectionInterface::SignalingState)nativeSignalingStateForState: + (RTCSignalingState)state { + switch (state) { + case RTCSignalingStateStable: + return webrtc::PeerConnectionInterface::kStable; + case RTCSignalingStateHaveLocalOffer: + return webrtc::PeerConnectionInterface::kHaveLocalOffer; + case RTCSignalingStateHaveLocalPrAnswer: + return webrtc::PeerConnectionInterface::kHaveLocalPrAnswer; + case RTCSignalingStateHaveRemoteOffer: + return webrtc::PeerConnectionInterface::kHaveRemoteOffer; + case RTCSignalingStateHaveRemotePrAnswer: + return webrtc::PeerConnectionInterface::kHaveRemotePrAnswer; + case RTCSignalingStateClosed: + return webrtc::PeerConnectionInterface::kClosed; + } +} + ++ (RTCSignalingState)signalingStateForNativeState: + (webrtc::PeerConnectionInterface::SignalingState)nativeState { + switch (nativeState) { + case webrtc::PeerConnectionInterface::kStable: + return RTCSignalingStateStable; + case webrtc::PeerConnectionInterface::kHaveLocalOffer: + return RTCSignalingStateHaveLocalOffer; + case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer: + return RTCSignalingStateHaveLocalPrAnswer; + case webrtc::PeerConnectionInterface::kHaveRemoteOffer: + return RTCSignalingStateHaveRemoteOffer; + case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer: + return RTCSignalingStateHaveRemotePrAnswer; + case webrtc::PeerConnectionInterface::kClosed: + return RTCSignalingStateClosed; + } +} + ++ (NSString *)stringForSignalingState:(RTCSignalingState)state { + switch (state) { + case RTCSignalingStateStable: + return @"STABLE"; + case RTCSignalingStateHaveLocalOffer: + return @"HAVE_LOCAL_OFFER"; + case RTCSignalingStateHaveLocalPrAnswer: + return @"HAVE_LOCAL_PRANSWER"; + case RTCSignalingStateHaveRemoteOffer: + return @"HAVE_REMOTE_OFFER"; + case RTCSignalingStateHaveRemotePrAnswer: + return @"HAVE_REMOTE_PRANSWER"; + case RTCSignalingStateClosed: + return @"CLOSED"; + } +} + ++ (webrtc::PeerConnectionInterface::PeerConnectionState)nativeConnectionStateForState: + (RTCPeerConnectionState)state { + switch (state) { + case RTCPeerConnectionStateNew: + return webrtc::PeerConnectionInterface::PeerConnectionState::kNew; + case RTCPeerConnectionStateConnecting: + return webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting; + case RTCPeerConnectionStateConnected: + return webrtc::PeerConnectionInterface::PeerConnectionState::kConnected; + case RTCPeerConnectionStateFailed: + return webrtc::PeerConnectionInterface::PeerConnectionState::kFailed; + case RTCPeerConnectionStateDisconnected: + return webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected; + case RTCPeerConnectionStateClosed: + return webrtc::PeerConnectionInterface::PeerConnectionState::kClosed; + } +} + ++ (RTCPeerConnectionState)connectionStateForNativeState: + (webrtc::PeerConnectionInterface::PeerConnectionState)nativeState { + switch (nativeState) { + case webrtc::PeerConnectionInterface::PeerConnectionState::kNew: + return RTCPeerConnectionStateNew; + case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting: + return RTCPeerConnectionStateConnecting; + case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected: + return RTCPeerConnectionStateConnected; + case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: + return RTCPeerConnectionStateFailed; + case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: + return RTCPeerConnectionStateDisconnected; + case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed: + return RTCPeerConnectionStateClosed; + } +} + ++ (NSString *)stringForConnectionState:(RTCPeerConnectionState)state { + switch (state) { + case RTCPeerConnectionStateNew: + return @"NEW"; + case RTCPeerConnectionStateConnecting: + return @"CONNECTING"; + case RTCPeerConnectionStateConnected: + return @"CONNECTED"; + case RTCPeerConnectionStateFailed: + return @"FAILED"; + case RTCPeerConnectionStateDisconnected: + return @"DISCONNECTED"; + case RTCPeerConnectionStateClosed: + return @"CLOSED"; + } +} + ++ (webrtc::PeerConnectionInterface::IceConnectionState) + nativeIceConnectionStateForState:(RTCIceConnectionState)state { + switch (state) { + case RTCIceConnectionStateNew: + return webrtc::PeerConnectionInterface::kIceConnectionNew; + case RTCIceConnectionStateChecking: + return webrtc::PeerConnectionInterface::kIceConnectionChecking; + case RTCIceConnectionStateConnected: + return webrtc::PeerConnectionInterface::kIceConnectionConnected; + case RTCIceConnectionStateCompleted: + return webrtc::PeerConnectionInterface::kIceConnectionCompleted; + case RTCIceConnectionStateFailed: + return webrtc::PeerConnectionInterface::kIceConnectionFailed; + case RTCIceConnectionStateDisconnected: + return webrtc::PeerConnectionInterface::kIceConnectionDisconnected; + case RTCIceConnectionStateClosed: + return webrtc::PeerConnectionInterface::kIceConnectionClosed; + case RTCIceConnectionStateCount: + return webrtc::PeerConnectionInterface::kIceConnectionMax; + } +} + ++ (RTCIceConnectionState)iceConnectionStateForNativeState: + (webrtc::PeerConnectionInterface::IceConnectionState)nativeState { + switch (nativeState) { + case webrtc::PeerConnectionInterface::kIceConnectionNew: + return RTCIceConnectionStateNew; + case webrtc::PeerConnectionInterface::kIceConnectionChecking: + return RTCIceConnectionStateChecking; + case webrtc::PeerConnectionInterface::kIceConnectionConnected: + return RTCIceConnectionStateConnected; + case webrtc::PeerConnectionInterface::kIceConnectionCompleted: + return RTCIceConnectionStateCompleted; + case webrtc::PeerConnectionInterface::kIceConnectionFailed: + return RTCIceConnectionStateFailed; + case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: + return RTCIceConnectionStateDisconnected; + case webrtc::PeerConnectionInterface::kIceConnectionClosed: + return RTCIceConnectionStateClosed; + case webrtc::PeerConnectionInterface::kIceConnectionMax: + return RTCIceConnectionStateCount; + } +} + ++ (NSString *)stringForIceConnectionState:(RTCIceConnectionState)state { + switch (state) { + case RTCIceConnectionStateNew: + return @"NEW"; + case RTCIceConnectionStateChecking: + return @"CHECKING"; + case RTCIceConnectionStateConnected: + return @"CONNECTED"; + case RTCIceConnectionStateCompleted: + return @"COMPLETED"; + case RTCIceConnectionStateFailed: + return @"FAILED"; + case RTCIceConnectionStateDisconnected: + return @"DISCONNECTED"; + case RTCIceConnectionStateClosed: + return @"CLOSED"; + case RTCIceConnectionStateCount: + return @"COUNT"; + } +} + ++ (webrtc::PeerConnectionInterface::IceGatheringState) + nativeIceGatheringStateForState:(RTCIceGatheringState)state { + switch (state) { + case RTCIceGatheringStateNew: + return webrtc::PeerConnectionInterface::kIceGatheringNew; + case RTCIceGatheringStateGathering: + return webrtc::PeerConnectionInterface::kIceGatheringGathering; + case RTCIceGatheringStateComplete: + return webrtc::PeerConnectionInterface::kIceGatheringComplete; + } +} + ++ (RTCIceGatheringState)iceGatheringStateForNativeState: + (webrtc::PeerConnectionInterface::IceGatheringState)nativeState { + switch (nativeState) { + case webrtc::PeerConnectionInterface::kIceGatheringNew: + return RTCIceGatheringStateNew; + case webrtc::PeerConnectionInterface::kIceGatheringGathering: + return RTCIceGatheringStateGathering; + case webrtc::PeerConnectionInterface::kIceGatheringComplete: + return RTCIceGatheringStateComplete; + } +} + ++ (NSString *)stringForIceGatheringState:(RTCIceGatheringState)state { + switch (state) { + case RTCIceGatheringStateNew: + return @"NEW"; + case RTCIceGatheringStateGathering: + return @"GATHERING"; + case RTCIceGatheringStateComplete: + return @"COMPLETE"; + } +} + ++ (webrtc::PeerConnectionInterface::StatsOutputLevel) + nativeStatsOutputLevelForLevel:(RTCStatsOutputLevel)level { + switch (level) { + case RTCStatsOutputLevelStandard: + return webrtc::PeerConnectionInterface::kStatsOutputLevelStandard; + case RTCStatsOutputLevelDebug: + return webrtc::PeerConnectionInterface::kStatsOutputLevelDebug; + } +} + +- (rtc::scoped_refptr<webrtc::PeerConnectionInterface>)nativePeerConnection { + return _peerConnection; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory+Native.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory+Native.h new file mode 100644 index 0000000000..f361b9f0ea --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory+Native.h @@ -0,0 +1,85 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactory.h" + +#include "api/scoped_refptr.h" + +namespace webrtc { + +class AudioDeviceModule; +class AudioEncoderFactory; +class AudioDecoderFactory; +class NetworkControllerFactoryInterface; +class VideoEncoderFactory; +class VideoDecoderFactory; +class AudioProcessing; +struct PeerConnectionDependencies; + +} // namespace webrtc + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class extension exposes methods that work directly with injectable C++ components. + */ +@interface RTC_OBJC_TYPE (RTCPeerConnectionFactory) +() + + - (instancetype)initNative NS_DESIGNATED_INITIALIZER; + +/* Initializer used when WebRTC is compiled with no media support */ +- (instancetype)initWithNoMedia; + +/* Initialize object with injectable native audio/video encoder/decoder factories */ +- (instancetype)initWithNativeAudioEncoderFactory: + (rtc::scoped_refptr<webrtc::AudioEncoderFactory>)audioEncoderFactory + nativeAudioDecoderFactory: + (rtc::scoped_refptr<webrtc::AudioDecoderFactory>)audioDecoderFactory + nativeVideoEncoderFactory: + (std::unique_ptr<webrtc::VideoEncoderFactory>)videoEncoderFactory + nativeVideoDecoderFactory: + (std::unique_ptr<webrtc::VideoDecoderFactory>)videoDecoderFactory + audioDeviceModule: + (nullable webrtc::AudioDeviceModule *)audioDeviceModule + audioProcessingModule: + (rtc::scoped_refptr<webrtc::AudioProcessing>)audioProcessingModule; + +- (instancetype) + initWithNativeAudioEncoderFactory: + (rtc::scoped_refptr<webrtc::AudioEncoderFactory>)audioEncoderFactory + nativeAudioDecoderFactory: + (rtc::scoped_refptr<webrtc::AudioDecoderFactory>)audioDecoderFactory + nativeVideoEncoderFactory: + (std::unique_ptr<webrtc::VideoEncoderFactory>)videoEncoderFactory + nativeVideoDecoderFactory: + (std::unique_ptr<webrtc::VideoDecoderFactory>)videoDecoderFactory + audioDeviceModule:(nullable webrtc::AudioDeviceModule *)audioDeviceModule + audioProcessingModule: + (rtc::scoped_refptr<webrtc::AudioProcessing>)audioProcessingModule + networkControllerFactory:(std::unique_ptr<webrtc::NetworkControllerFactoryInterface>) + networkControllerFactory; + +- (instancetype) + initWithEncoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)>)encoderFactory + decoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)>)decoderFactory; + +/** Initialize an RTCPeerConnection with a configuration, constraints, and + * dependencies. + */ +- (nullable RTC_OBJC_TYPE(RTCPeerConnection) *) + peerConnectionWithDependencies:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + dependencies:(std::unique_ptr<webrtc::PeerConnectionDependencies>)dependencies + delegate:(nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory+Private.h new file mode 100644 index 0000000000..9613646270 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory+Private.h @@ -0,0 +1,35 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactory.h" + +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "rtc_base/thread.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCPeerConnectionFactory) +() + + /** + * PeerConnectionFactoryInterface created and held by this + * RTCPeerConnectionFactory object. This is needed to pass to the underlying + * C++ APIs. + */ + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> nativeFactory; + +@property(nonatomic, readonly) rtc::Thread* signalingThread; +@property(nonatomic, readonly) rtc::Thread* workerThread; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h new file mode 100644 index 0000000000..5575af98c9 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h @@ -0,0 +1,113 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCAudioSource); +@class RTC_OBJC_TYPE(RTCAudioTrack); +@class RTC_OBJC_TYPE(RTCConfiguration); +@class RTC_OBJC_TYPE(RTCMediaConstraints); +@class RTC_OBJC_TYPE(RTCMediaStream); +@class RTC_OBJC_TYPE(RTCPeerConnection); +@class RTC_OBJC_TYPE(RTCVideoSource); +@class RTC_OBJC_TYPE(RTCVideoTrack); +@class RTC_OBJC_TYPE(RTCPeerConnectionFactoryOptions); +@protocol RTC_OBJC_TYPE +(RTCPeerConnectionDelegate); +@protocol RTC_OBJC_TYPE +(RTCVideoDecoderFactory); +@protocol RTC_OBJC_TYPE +(RTCVideoEncoderFactory); +@protocol RTC_OBJC_TYPE +(RTCSSLCertificateVerifier); +@protocol RTC_OBJC_TYPE +(RTCAudioDevice); + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCPeerConnectionFactory) : NSObject + +/* Initialize object with default H264 video encoder/decoder factories and default ADM */ +- (instancetype)init; + +/* Initialize object with injectable video encoder/decoder factories and default ADM */ +- (instancetype) + initWithEncoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)>)encoderFactory + decoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)>)decoderFactory; + +/* Initialize object with injectable video encoder/decoder factories and injectable ADM */ +- (instancetype) + initWithEncoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)>)encoderFactory + decoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)>)decoderFactory + audioDevice:(nullable id<RTC_OBJC_TYPE(RTCAudioDevice)>)audioDevice; + +/** Initialize an RTCAudioSource with constraints. */ +- (RTC_OBJC_TYPE(RTCAudioSource) *)audioSourceWithConstraints: + (nullable RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints; + +/** Initialize an RTCAudioTrack with an id. Convenience ctor to use an audio source + * with no constraints. + */ +- (RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrackWithTrackId:(NSString *)trackId; + +/** Initialize an RTCAudioTrack with a source and an id. */ +- (RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrackWithSource:(RTC_OBJC_TYPE(RTCAudioSource) *)source + trackId:(NSString *)trackId; + +/** Initialize a generic RTCVideoSource. The RTCVideoSource should be + * passed to a RTCVideoCapturer implementation, e.g. + * RTCCameraVideoCapturer, in order to produce frames. + */ +- (RTC_OBJC_TYPE(RTCVideoSource) *)videoSource; + +/** Initialize a generic RTCVideoSource with he posibility of marking + * it as usable for screen sharing. The RTCVideoSource should be + * passed to a RTCVideoCapturer implementation, e.g. + * RTCCameraVideoCapturer, in order to produce frames. + */ +- (RTC_OBJC_TYPE(RTCVideoSource) *)videoSourceForScreenCast:(BOOL)forScreenCast; + +/** Initialize an RTCVideoTrack with a source and an id. */ +- (RTC_OBJC_TYPE(RTCVideoTrack) *)videoTrackWithSource:(RTC_OBJC_TYPE(RTCVideoSource) *)source + trackId:(NSString *)trackId; + +/** Initialize an RTCMediaStream with an id. */ +- (RTC_OBJC_TYPE(RTCMediaStream) *)mediaStreamWithStreamId:(NSString *)streamId; + +/** Initialize an RTCPeerConnection with a configuration, constraints, and + * delegate. + */ +- (nullable RTC_OBJC_TYPE(RTCPeerConnection) *) + peerConnectionWithConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + delegate:(nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate; + +- (nullable RTC_OBJC_TYPE(RTCPeerConnection) *) + peerConnectionWithConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + certificateVerifier: + (id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)>)certificateVerifier + delegate:(nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate; + +/** Set the options to be used for subsequently created RTCPeerConnections */ +- (void)setOptions:(nonnull RTC_OBJC_TYPE(RTCPeerConnectionFactoryOptions) *)options; + +/** Start an AecDump recording. This API call will likely change in the future. */ +- (BOOL)startAecDumpWithFilePath:(NSString *)filePath maxSizeInBytes:(int64_t)maxSizeInBytes; + +/* Stop an active AecDump recording */ +- (void)stopAecDump; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm new file mode 100644 index 0000000000..c4d89e911d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm @@ -0,0 +1,342 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <memory> + +#import "RTCPeerConnectionFactory+Native.h" +#import "RTCPeerConnectionFactory+Private.h" +#import "RTCPeerConnectionFactoryOptions+Private.h" + +#import "RTCAudioSource+Private.h" +#import "RTCAudioTrack+Private.h" +#import "RTCMediaConstraints+Private.h" +#import "RTCMediaStream+Private.h" +#import "RTCPeerConnection+Private.h" +#import "RTCVideoSource+Private.h" +#import "RTCVideoTrack+Private.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoDecoderFactory.h" +#import "base/RTCVideoEncoderFactory.h" +#import "helpers/NSString+StdString.h" +#include "rtc_base/checks.h" +#include "sdk/objc/native/api/network_monitor_factory.h" +#include "sdk/objc/native/api/ssl_certificate_verifier.h" +#include "system_wrappers/include/field_trial.h" + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "api/transport/field_trial_based_config.h" +#import "components/video_codec/RTCVideoDecoderFactoryH264.h" +#import "components/video_codec/RTCVideoEncoderFactoryH264.h" +#include "media/engine/webrtc_media_engine.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" + +#include "sdk/objc/native/api/objc_audio_device_module.h" +#include "sdk/objc/native/api/video_decoder_factory.h" +#include "sdk/objc/native/api/video_encoder_factory.h" +#include "sdk/objc/native/src/objc_video_decoder_factory.h" +#include "sdk/objc/native/src/objc_video_encoder_factory.h" + +#if defined(WEBRTC_IOS) +#import "sdk/objc/native/api/audio_device_module.h" +#endif + +@implementation RTC_OBJC_TYPE (RTCPeerConnectionFactory) { + std::unique_ptr<rtc::Thread> _networkThread; + std::unique_ptr<rtc::Thread> _workerThread; + std::unique_ptr<rtc::Thread> _signalingThread; + BOOL _hasStartedAecDump; +} + +@synthesize nativeFactory = _nativeFactory; + +- (rtc::scoped_refptr<webrtc::AudioDeviceModule>)audioDeviceModule { +#if defined(WEBRTC_IOS) + return webrtc::CreateAudioDeviceModule(); +#else + return nullptr; +#endif +} + +- (instancetype)init { + return [self + initWithNativeAudioEncoderFactory:webrtc::CreateBuiltinAudioEncoderFactory() + nativeAudioDecoderFactory:webrtc::CreateBuiltinAudioDecoderFactory() + nativeVideoEncoderFactory:webrtc::ObjCToNativeVideoEncoderFactory([[RTC_OBJC_TYPE( + RTCVideoEncoderFactoryH264) alloc] init]) + nativeVideoDecoderFactory:webrtc::ObjCToNativeVideoDecoderFactory([[RTC_OBJC_TYPE( + RTCVideoDecoderFactoryH264) alloc] init]) + audioDeviceModule:[self audioDeviceModule].get() + audioProcessingModule:nullptr]; +} + +- (instancetype) + initWithEncoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)>)encoderFactory + decoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)>)decoderFactory { + return [self initWithEncoderFactory:encoderFactory decoderFactory:decoderFactory audioDevice:nil]; +} + +- (instancetype) + initWithEncoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)>)encoderFactory + decoderFactory:(nullable id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)>)decoderFactory + audioDevice:(nullable id<RTC_OBJC_TYPE(RTCAudioDevice)>)audioDevice { +#ifdef HAVE_NO_MEDIA + return [self initWithNoMedia]; +#else + std::unique_ptr<webrtc::VideoEncoderFactory> native_encoder_factory; + std::unique_ptr<webrtc::VideoDecoderFactory> native_decoder_factory; + if (encoderFactory) { + native_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory(encoderFactory); + } + if (decoderFactory) { + native_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory(decoderFactory); + } + rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device_module; + if (audioDevice) { + audio_device_module = webrtc::CreateAudioDeviceModule(audioDevice); + } else { + audio_device_module = [self audioDeviceModule]; + } + return [self initWithNativeAudioEncoderFactory:webrtc::CreateBuiltinAudioEncoderFactory() + nativeAudioDecoderFactory:webrtc::CreateBuiltinAudioDecoderFactory() + nativeVideoEncoderFactory:std::move(native_encoder_factory) + nativeVideoDecoderFactory:std::move(native_decoder_factory) + audioDeviceModule:audio_device_module.get() + audioProcessingModule:nullptr]; +#endif +} + +- (instancetype)initNative { + if (self = [super init]) { + _networkThread = rtc::Thread::CreateWithSocketServer(); + _networkThread->SetName("network_thread", _networkThread.get()); + BOOL result = _networkThread->Start(); + RTC_DCHECK(result) << "Failed to start network thread."; + + _workerThread = rtc::Thread::Create(); + _workerThread->SetName("worker_thread", _workerThread.get()); + result = _workerThread->Start(); + RTC_DCHECK(result) << "Failed to start worker thread."; + + _signalingThread = rtc::Thread::Create(); + _signalingThread->SetName("signaling_thread", _signalingThread.get()); + result = _signalingThread->Start(); + RTC_DCHECK(result) << "Failed to start signaling thread."; + } + return self; +} + +- (instancetype)initWithNoMedia { + if (self = [self initNative]) { + webrtc::PeerConnectionFactoryDependencies dependencies; + dependencies.network_thread = _networkThread.get(); + dependencies.worker_thread = _workerThread.get(); + dependencies.signaling_thread = _signalingThread.get(); + if (webrtc::field_trial::IsEnabled("WebRTC-Network-UseNWPathMonitor")) { + dependencies.network_monitor_factory = webrtc::CreateNetworkMonitorFactory(); + } + _nativeFactory = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies)); + NSAssert(_nativeFactory, @"Failed to initialize PeerConnectionFactory!"); + } + return self; +} + +- (instancetype)initWithNativeAudioEncoderFactory: + (rtc::scoped_refptr<webrtc::AudioEncoderFactory>)audioEncoderFactory + nativeAudioDecoderFactory: + (rtc::scoped_refptr<webrtc::AudioDecoderFactory>)audioDecoderFactory + nativeVideoEncoderFactory: + (std::unique_ptr<webrtc::VideoEncoderFactory>)videoEncoderFactory + nativeVideoDecoderFactory: + (std::unique_ptr<webrtc::VideoDecoderFactory>)videoDecoderFactory + audioDeviceModule:(webrtc::AudioDeviceModule *)audioDeviceModule + audioProcessingModule: + (rtc::scoped_refptr<webrtc::AudioProcessing>)audioProcessingModule { + return [self initWithNativeAudioEncoderFactory:audioEncoderFactory + nativeAudioDecoderFactory:audioDecoderFactory + nativeVideoEncoderFactory:std::move(videoEncoderFactory) + nativeVideoDecoderFactory:std::move(videoDecoderFactory) + audioDeviceModule:audioDeviceModule + audioProcessingModule:audioProcessingModule + networkControllerFactory:nullptr]; +} +- (instancetype)initWithNativeAudioEncoderFactory: + (rtc::scoped_refptr<webrtc::AudioEncoderFactory>)audioEncoderFactory + nativeAudioDecoderFactory: + (rtc::scoped_refptr<webrtc::AudioDecoderFactory>)audioDecoderFactory + nativeVideoEncoderFactory: + (std::unique_ptr<webrtc::VideoEncoderFactory>)videoEncoderFactory + nativeVideoDecoderFactory: + (std::unique_ptr<webrtc::VideoDecoderFactory>)videoDecoderFactory + audioDeviceModule:(webrtc::AudioDeviceModule *)audioDeviceModule + audioProcessingModule: + (rtc::scoped_refptr<webrtc::AudioProcessing>)audioProcessingModule + networkControllerFactory: + (std::unique_ptr<webrtc::NetworkControllerFactoryInterface>) + networkControllerFactory { + if (self = [self initNative]) { + webrtc::PeerConnectionFactoryDependencies dependencies; + dependencies.network_thread = _networkThread.get(); + dependencies.worker_thread = _workerThread.get(); + dependencies.signaling_thread = _signalingThread.get(); + if (webrtc::field_trial::IsEnabled("WebRTC-Network-UseNWPathMonitor")) { + dependencies.network_monitor_factory = webrtc::CreateNetworkMonitorFactory(); + } + dependencies.trials = std::make_unique<webrtc::FieldTrialBasedConfig>(); + dependencies.task_queue_factory = + webrtc::CreateDefaultTaskQueueFactory(dependencies.trials.get()); + cricket::MediaEngineDependencies media_deps; + media_deps.adm = std::move(audioDeviceModule); + media_deps.task_queue_factory = dependencies.task_queue_factory.get(); + media_deps.audio_encoder_factory = std::move(audioEncoderFactory); + media_deps.audio_decoder_factory = std::move(audioDecoderFactory); + media_deps.video_encoder_factory = std::move(videoEncoderFactory); + media_deps.video_decoder_factory = std::move(videoDecoderFactory); + if (audioProcessingModule) { + media_deps.audio_processing = std::move(audioProcessingModule); + } else { + media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); + } + media_deps.trials = dependencies.trials.get(); + dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_deps)); + dependencies.call_factory = webrtc::CreateCallFactory(); + dependencies.event_log_factory = + std::make_unique<webrtc::RtcEventLogFactory>(dependencies.task_queue_factory.get()); + dependencies.network_controller_factory = std::move(networkControllerFactory); + _nativeFactory = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies)); + NSAssert(_nativeFactory, @"Failed to initialize PeerConnectionFactory!"); + } + return self; +} + +- (RTC_OBJC_TYPE(RTCAudioSource) *)audioSourceWithConstraints: + (nullable RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints { + std::unique_ptr<webrtc::MediaConstraints> nativeConstraints; + if (constraints) { + nativeConstraints = constraints.nativeConstraints; + } + cricket::AudioOptions options; + CopyConstraintsIntoAudioOptions(nativeConstraints.get(), &options); + + rtc::scoped_refptr<webrtc::AudioSourceInterface> source = + _nativeFactory->CreateAudioSource(options); + return [[RTC_OBJC_TYPE(RTCAudioSource) alloc] initWithFactory:self nativeAudioSource:source]; +} + +- (RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrackWithTrackId:(NSString *)trackId { + RTC_OBJC_TYPE(RTCAudioSource) *audioSource = [self audioSourceWithConstraints:nil]; + return [self audioTrackWithSource:audioSource trackId:trackId]; +} + +- (RTC_OBJC_TYPE(RTCAudioTrack) *)audioTrackWithSource:(RTC_OBJC_TYPE(RTCAudioSource) *)source + trackId:(NSString *)trackId { + return [[RTC_OBJC_TYPE(RTCAudioTrack) alloc] initWithFactory:self source:source trackId:trackId]; +} + +- (RTC_OBJC_TYPE(RTCVideoSource) *)videoSource { + return [[RTC_OBJC_TYPE(RTCVideoSource) alloc] initWithFactory:self + signalingThread:_signalingThread.get() + workerThread:_workerThread.get()]; +} + +- (RTC_OBJC_TYPE(RTCVideoSource) *)videoSourceForScreenCast:(BOOL)forScreenCast { + return [[RTC_OBJC_TYPE(RTCVideoSource) alloc] initWithFactory:self + signalingThread:_signalingThread.get() + workerThread:_workerThread.get() + isScreenCast:forScreenCast]; +} + +- (RTC_OBJC_TYPE(RTCVideoTrack) *)videoTrackWithSource:(RTC_OBJC_TYPE(RTCVideoSource) *)source + trackId:(NSString *)trackId { + return [[RTC_OBJC_TYPE(RTCVideoTrack) alloc] initWithFactory:self source:source trackId:trackId]; +} + +- (RTC_OBJC_TYPE(RTCMediaStream) *)mediaStreamWithStreamId:(NSString *)streamId { + return [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:self streamId:streamId]; +} + +- (nullable RTC_OBJC_TYPE(RTCPeerConnection) *) + peerConnectionWithConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + delegate: + (nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { + return [[RTC_OBJC_TYPE(RTCPeerConnection) alloc] initWithFactory:self + configuration:configuration + constraints:constraints + certificateVerifier:nil + delegate:delegate]; +} + +- (nullable RTC_OBJC_TYPE(RTCPeerConnection) *) + peerConnectionWithConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + certificateVerifier: + (id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)>)certificateVerifier + delegate: + (nullable id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { + return [[RTC_OBJC_TYPE(RTCPeerConnection) alloc] initWithFactory:self + configuration:configuration + constraints:constraints + certificateVerifier:certificateVerifier + delegate:delegate]; +} + +- (nullable RTC_OBJC_TYPE(RTCPeerConnection) *) + peerConnectionWithDependencies:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration + constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints + dependencies:(std::unique_ptr<webrtc::PeerConnectionDependencies>)dependencies + delegate:(id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { + return [[RTC_OBJC_TYPE(RTCPeerConnection) alloc] initWithDependencies:self + configuration:configuration + constraints:constraints + dependencies:std::move(dependencies) + delegate:delegate]; +} + +- (void)setOptions:(nonnull RTC_OBJC_TYPE(RTCPeerConnectionFactoryOptions) *)options { + RTC_DCHECK(options != nil); + _nativeFactory->SetOptions(options.nativeOptions); +} + +- (BOOL)startAecDumpWithFilePath:(NSString *)filePath + maxSizeInBytes:(int64_t)maxSizeInBytes { + RTC_DCHECK(filePath.length); + RTC_DCHECK_GT(maxSizeInBytes, 0); + + if (_hasStartedAecDump) { + RTCLogError(@"Aec dump already started."); + return NO; + } + FILE *f = fopen(filePath.UTF8String, "wb"); + if (!f) { + RTCLogError(@"Error opening file: %@. Error: %s", filePath, strerror(errno)); + return NO; + } + _hasStartedAecDump = _nativeFactory->StartAecDump(f, maxSizeInBytes); + return _hasStartedAecDump; +} + +- (void)stopAecDump { + _nativeFactory->StopAecDump(); + _hasStartedAecDump = NO; +} + +- (rtc::Thread *)signalingThread { + return _signalingThread.get(); +} + +- (rtc::Thread *)workerThread { + return _workerThread.get(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.h new file mode 100644 index 0000000000..070a0e74a5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.h @@ -0,0 +1,21 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactoryBuilder.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCPeerConnectionFactoryBuilder (DefaultComponents) + ++ (RTCPeerConnectionFactoryBuilder *)defaultBuilder; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.mm new file mode 100644 index 0000000000..522e520e12 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.mm @@ -0,0 +1,49 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactory+Native.h" +#import "RTCPeerConnectionFactoryBuilder+DefaultComponents.h" + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#import "components/video_codec/RTCVideoDecoderFactoryH264.h" +#import "components/video_codec/RTCVideoEncoderFactoryH264.h" +#include "sdk/objc/native/api/video_decoder_factory.h" +#include "sdk/objc/native/api/video_encoder_factory.h" + +#if defined(WEBRTC_IOS) +#import "sdk/objc/native/api/audio_device_module.h" +#endif + +@implementation RTCPeerConnectionFactoryBuilder (DefaultComponents) + ++ (RTCPeerConnectionFactoryBuilder *)defaultBuilder { + RTCPeerConnectionFactoryBuilder *builder = [[RTCPeerConnectionFactoryBuilder alloc] init]; + auto audioEncoderFactory = webrtc::CreateBuiltinAudioEncoderFactory(); + [builder setAudioEncoderFactory:audioEncoderFactory]; + + auto audioDecoderFactory = webrtc::CreateBuiltinAudioDecoderFactory(); + [builder setAudioDecoderFactory:audioDecoderFactory]; + + auto videoEncoderFactory = webrtc::ObjCToNativeVideoEncoderFactory( + [[RTC_OBJC_TYPE(RTCVideoEncoderFactoryH264) alloc] init]); + [builder setVideoEncoderFactory:std::move(videoEncoderFactory)]; + + auto videoDecoderFactory = webrtc::ObjCToNativeVideoDecoderFactory( + [[RTC_OBJC_TYPE(RTCVideoDecoderFactoryH264) alloc] init]); + [builder setVideoDecoderFactory:std::move(videoDecoderFactory)]; + +#if defined(WEBRTC_IOS) + [builder setAudioDeviceModule:webrtc::CreateAudioDeviceModule()]; +#endif + return builder; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder.h new file mode 100644 index 0000000000..f0b0de156a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder.h @@ -0,0 +1,48 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactory.h" + +#include "api/scoped_refptr.h" + +namespace webrtc { + +class AudioDeviceModule; +class AudioEncoderFactory; +class AudioDecoderFactory; +class VideoEncoderFactory; +class VideoDecoderFactory; +class AudioProcessing; + +} // namespace webrtc + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCPeerConnectionFactoryBuilder : NSObject + ++ (RTCPeerConnectionFactoryBuilder *)builder; + +- (RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)createPeerConnectionFactory; + +- (void)setVideoEncoderFactory:(std::unique_ptr<webrtc::VideoEncoderFactory>)videoEncoderFactory; + +- (void)setVideoDecoderFactory:(std::unique_ptr<webrtc::VideoDecoderFactory>)videoDecoderFactory; + +- (void)setAudioEncoderFactory:(rtc::scoped_refptr<webrtc::AudioEncoderFactory>)audioEncoderFactory; + +- (void)setAudioDecoderFactory:(rtc::scoped_refptr<webrtc::AudioDecoderFactory>)audioDecoderFactory; + +- (void)setAudioDeviceModule:(rtc::scoped_refptr<webrtc::AudioDeviceModule>)audioDeviceModule; + +- (void)setAudioProcessingModule:(rtc::scoped_refptr<webrtc::AudioProcessing>)audioProcessingModule; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder.mm new file mode 100644 index 0000000000..627909a0e3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryBuilder.mm @@ -0,0 +1,72 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactoryBuilder.h" +#import "RTCPeerConnectionFactory+Native.h" + +#include "api/audio_codecs/audio_decoder_factory.h" +#include "api/audio_codecs/audio_encoder_factory.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" + +@implementation RTCPeerConnectionFactoryBuilder { + std::unique_ptr<webrtc::VideoEncoderFactory> _videoEncoderFactory; + std::unique_ptr<webrtc::VideoDecoderFactory> _videoDecoderFactory; + rtc::scoped_refptr<webrtc::AudioEncoderFactory> _audioEncoderFactory; + rtc::scoped_refptr<webrtc::AudioDecoderFactory> _audioDecoderFactory; + rtc::scoped_refptr<webrtc::AudioDeviceModule> _audioDeviceModule; + rtc::scoped_refptr<webrtc::AudioProcessing> _audioProcessingModule; +} + ++ (RTCPeerConnectionFactoryBuilder *)builder { + return [[RTCPeerConnectionFactoryBuilder alloc] init]; +} + +- (RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)createPeerConnectionFactory { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) *factory = + [RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc]; + return [factory initWithNativeAudioEncoderFactory:_audioEncoderFactory + nativeAudioDecoderFactory:_audioDecoderFactory + nativeVideoEncoderFactory:std::move(_videoEncoderFactory) + nativeVideoDecoderFactory:std::move(_videoDecoderFactory) + audioDeviceModule:_audioDeviceModule.get() + audioProcessingModule:_audioProcessingModule]; +} + +- (void)setVideoEncoderFactory:(std::unique_ptr<webrtc::VideoEncoderFactory>)videoEncoderFactory { + _videoEncoderFactory = std::move(videoEncoderFactory); +} + +- (void)setVideoDecoderFactory:(std::unique_ptr<webrtc::VideoDecoderFactory>)videoDecoderFactory { + _videoDecoderFactory = std::move(videoDecoderFactory); +} + +- (void)setAudioEncoderFactory: + (rtc::scoped_refptr<webrtc::AudioEncoderFactory>)audioEncoderFactory { + _audioEncoderFactory = audioEncoderFactory; +} + +- (void)setAudioDecoderFactory: + (rtc::scoped_refptr<webrtc::AudioDecoderFactory>)audioDecoderFactory { + _audioDecoderFactory = audioDecoderFactory; +} + +- (void)setAudioDeviceModule:(rtc::scoped_refptr<webrtc::AudioDeviceModule>)audioDeviceModule { + _audioDeviceModule = audioDeviceModule; +} + +- (void)setAudioProcessingModule: + (rtc::scoped_refptr<webrtc::AudioProcessing>)audioProcessingModule { + _audioProcessingModule = audioProcessingModule; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions+Private.h new file mode 100644 index 0000000000..8832b23695 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactoryOptions.h" + +#include "api/peer_connection_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCPeerConnectionFactoryOptions) +() + + /** Returns the equivalent native PeerConnectionFactoryInterface::Options + * structure. */ + @property(nonatomic, readonly) webrtc::PeerConnectionFactoryInterface::Options nativeOptions; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h new file mode 100644 index 0000000000..bfc54a5d7b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.h @@ -0,0 +1,38 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCPeerConnectionFactoryOptions) : NSObject + +@property(nonatomic, assign) BOOL disableEncryption; + +@property(nonatomic, assign) BOOL disableNetworkMonitor; + +@property(nonatomic, assign) BOOL ignoreLoopbackNetworkAdapter; + +@property(nonatomic, assign) BOOL ignoreVPNNetworkAdapter; + +@property(nonatomic, assign) BOOL ignoreCellularNetworkAdapter; + +@property(nonatomic, assign) BOOL ignoreWiFiNetworkAdapter; + +@property(nonatomic, assign) BOOL ignoreEthernetNetworkAdapter; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm new file mode 100644 index 0000000000..5467bd5fc9 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCPeerConnectionFactoryOptions.mm @@ -0,0 +1,56 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCPeerConnectionFactoryOptions+Private.h" + +#include "rtc_base/network_constants.h" + +namespace { + +void setNetworkBit(webrtc::PeerConnectionFactoryInterface::Options* options, + rtc::AdapterType type, + bool ignore) { + if (ignore) { + options->network_ignore_mask |= type; + } else { + options->network_ignore_mask &= ~type; + } +} +} // namespace + +@implementation RTC_OBJC_TYPE (RTCPeerConnectionFactoryOptions) + +@synthesize disableEncryption = _disableEncryption; +@synthesize disableNetworkMonitor = _disableNetworkMonitor; +@synthesize ignoreLoopbackNetworkAdapter = _ignoreLoopbackNetworkAdapter; +@synthesize ignoreVPNNetworkAdapter = _ignoreVPNNetworkAdapter; +@synthesize ignoreCellularNetworkAdapter = _ignoreCellularNetworkAdapter; +@synthesize ignoreWiFiNetworkAdapter = _ignoreWiFiNetworkAdapter; +@synthesize ignoreEthernetNetworkAdapter = _ignoreEthernetNetworkAdapter; + +- (instancetype)init { + return [super init]; +} + +- (webrtc::PeerConnectionFactoryInterface::Options)nativeOptions { + webrtc::PeerConnectionFactoryInterface::Options options; + options.disable_encryption = self.disableEncryption; + options.disable_network_monitor = self.disableNetworkMonitor; + + setNetworkBit(&options, rtc::ADAPTER_TYPE_LOOPBACK, self.ignoreLoopbackNetworkAdapter); + setNetworkBit(&options, rtc::ADAPTER_TYPE_VPN, self.ignoreVPNNetworkAdapter); + setNetworkBit(&options, rtc::ADAPTER_TYPE_CELLULAR, self.ignoreCellularNetworkAdapter); + setNetworkBit(&options, rtc::ADAPTER_TYPE_WIFI, self.ignoreWiFiNetworkAdapter); + setNetworkBit(&options, rtc::ADAPTER_TYPE_ETHERNET, self.ignoreEthernetNetworkAdapter); + + return options; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters+Private.h new file mode 100644 index 0000000000..c4d196cf79 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtcpParameters.h" + +#include "api/rtp_parameters.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCRtcpParameters) +() + + /** Returns the equivalent native RtcpParameters structure. */ + @property(nonatomic, readonly) webrtc::RtcpParameters nativeParameters; + +/** Initialize the object with a native RtcpParameters structure. */ +- (instancetype)initWithNativeParameters:(const webrtc::RtcpParameters &)nativeParameters + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters.h new file mode 100644 index 0000000000..2f7aad3aef --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtcpParameters) : NSObject + +/** The Canonical Name used by RTCP. */ +@property(nonatomic, readonly, copy) NSString *cname; + +/** Whether reduced size RTCP is configured or compound RTCP. */ +@property(nonatomic, assign) BOOL isReducedSize; + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters.mm new file mode 100644 index 0000000000..e92ee4b3e7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtcpParameters.mm @@ -0,0 +1,40 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtcpParameters+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCRtcpParameters) + +@synthesize cname = _cname; +@synthesize isReducedSize = _isReducedSize; + +- (instancetype)init { + webrtc::RtcpParameters nativeParameters; + return [self initWithNativeParameters:nativeParameters]; +} + +- (instancetype)initWithNativeParameters:(const webrtc::RtcpParameters &)nativeParameters { + if (self = [super init]) { + _cname = [NSString stringForStdString:nativeParameters.cname]; + _isReducedSize = nativeParameters.reduced_size; + } + return self; +} + +- (webrtc::RtcpParameters)nativeParameters { + webrtc::RtcpParameters parameters; + parameters.cname = [NSString stdStringForString:_cname]; + parameters.reduced_size = _isReducedSize; + return parameters; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters+Private.h new file mode 100644 index 0000000000..ff23cfd642 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpCodecParameters.h" + +#include "api/rtp_parameters.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCRtpCodecParameters) +() + + /** Returns the equivalent native RtpCodecParameters structure. */ + @property(nonatomic, readonly) webrtc::RtpCodecParameters nativeParameters; + +/** Initialize the object with a native RtpCodecParameters structure. */ +- (instancetype)initWithNativeParameters:(const webrtc::RtpCodecParameters &)nativeParameters + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters.h new file mode 100644 index 0000000000..6135223720 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters.h @@ -0,0 +1,73 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_EXTERN const NSString *const kRTCRtxCodecName; +RTC_EXTERN const NSString *const kRTCRedCodecName; +RTC_EXTERN const NSString *const kRTCUlpfecCodecName; +RTC_EXTERN const NSString *const kRTCFlexfecCodecName; +RTC_EXTERN const NSString *const kRTCOpusCodecName; +RTC_EXTERN const NSString *const kRTCIsacCodecName; +RTC_EXTERN const NSString *const kRTCL16CodecName; +RTC_EXTERN const NSString *const kRTCG722CodecName; +RTC_EXTERN const NSString *const kRTCIlbcCodecName; +RTC_EXTERN const NSString *const kRTCPcmuCodecName; +RTC_EXTERN const NSString *const kRTCPcmaCodecName; +RTC_EXTERN const NSString *const kRTCDtmfCodecName; +RTC_EXTERN const NSString *const kRTCComfortNoiseCodecName; +RTC_EXTERN const NSString *const kRTCVp8CodecName; +RTC_EXTERN const NSString *const kRTCVp9CodecName; +RTC_EXTERN const NSString *const kRTCH264CodecName; + +/** Defined in https://www.w3.org/TR/webrtc/#idl-def-rtcrtpcodecparameters */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpCodecParameters) : NSObject + +/** The RTP payload type. */ +@property(nonatomic, assign) int payloadType; + +/** + * The codec MIME subtype. Valid types are listed in: + * http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-2 + * + * Several supported types are represented by the constants above. + */ +@property(nonatomic, readonly, nonnull) NSString *name; + +/** + * The media type of this codec. Equivalent to MIME top-level type. + * + * Valid values are kRTCMediaStreamTrackKindAudio and + * kRTCMediaStreamTrackKindVideo. + */ +@property(nonatomic, readonly, nonnull) NSString *kind; + +/** The codec clock rate expressed in Hertz. */ +@property(nonatomic, readonly, nullable) NSNumber *clockRate; + +/** + * The number of channels (mono=1, stereo=2). + * Set to null for video codecs. + **/ +@property(nonatomic, readonly, nullable) NSNumber *numChannels; + +/** The "format specific parameters" field from the "a=fmtp" line in the SDP */ +@property(nonatomic, readonly, nonnull) NSDictionary *parameters; + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters.mm new file mode 100644 index 0000000000..753667b635 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpCodecParameters.mm @@ -0,0 +1,113 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpCodecParameters+Private.h" + +#import "RTCMediaStreamTrack.h" +#import "helpers/NSString+StdString.h" + +#include "media/base/media_constants.h" +#include "rtc_base/checks.h" + +const NSString * const kRTCRtxCodecName = @(cricket::kRtxCodecName); +const NSString * const kRTCRedCodecName = @(cricket::kRedCodecName); +const NSString * const kRTCUlpfecCodecName = @(cricket::kUlpfecCodecName); +const NSString * const kRTCFlexfecCodecName = @(cricket::kFlexfecCodecName); +const NSString * const kRTCOpusCodecName = @(cricket::kOpusCodecName); +const NSString * const kRTCIsacCodecName = @(cricket::kIsacCodecName); +const NSString * const kRTCL16CodecName = @(cricket::kL16CodecName); +const NSString * const kRTCG722CodecName = @(cricket::kG722CodecName); +const NSString * const kRTCIlbcCodecName = @(cricket::kIlbcCodecName); +const NSString * const kRTCPcmuCodecName = @(cricket::kPcmuCodecName); +const NSString * const kRTCPcmaCodecName = @(cricket::kPcmaCodecName); +const NSString * const kRTCDtmfCodecName = @(cricket::kDtmfCodecName); +const NSString * const kRTCComfortNoiseCodecName = + @(cricket::kComfortNoiseCodecName); +const NSString * const kRTCVp8CodecName = @(cricket::kVp8CodecName); +const NSString * const kRTCVp9CodecName = @(cricket::kVp9CodecName); +const NSString * const kRTCH264CodecName = @(cricket::kH264CodecName); + +@implementation RTC_OBJC_TYPE (RTCRtpCodecParameters) + +@synthesize payloadType = _payloadType; +@synthesize name = _name; +@synthesize kind = _kind; +@synthesize clockRate = _clockRate; +@synthesize numChannels = _numChannels; +@synthesize parameters = _parameters; + +- (instancetype)init { + webrtc::RtpCodecParameters nativeParameters; + return [self initWithNativeParameters:nativeParameters]; +} + +- (instancetype)initWithNativeParameters: + (const webrtc::RtpCodecParameters &)nativeParameters { + if (self = [super init]) { + _payloadType = nativeParameters.payload_type; + _name = [NSString stringForStdString:nativeParameters.name]; + switch (nativeParameters.kind) { + case cricket::MEDIA_TYPE_AUDIO: + _kind = kRTCMediaStreamTrackKindAudio; + break; + case cricket::MEDIA_TYPE_VIDEO: + _kind = kRTCMediaStreamTrackKindVideo; + break; + case cricket::MEDIA_TYPE_DATA: + RTC_DCHECK_NOTREACHED(); + break; + case cricket::MEDIA_TYPE_UNSUPPORTED: + RTC_DCHECK_NOTREACHED(); + break; + } + if (nativeParameters.clock_rate) { + _clockRate = [NSNumber numberWithInt:*nativeParameters.clock_rate]; + } + if (nativeParameters.num_channels) { + _numChannels = [NSNumber numberWithInt:*nativeParameters.num_channels]; + } + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + for (const auto ¶meter : nativeParameters.parameters) { + [parameters setObject:[NSString stringForStdString:parameter.second] + forKey:[NSString stringForStdString:parameter.first]]; + } + _parameters = parameters; + } + return self; +} + +- (webrtc::RtpCodecParameters)nativeParameters { + webrtc::RtpCodecParameters parameters; + parameters.payload_type = _payloadType; + parameters.name = [NSString stdStringForString:_name]; + // NSString pointer comparison is safe here since "kind" is readonly and only + // populated above. + if (_kind == kRTCMediaStreamTrackKindAudio) { + parameters.kind = cricket::MEDIA_TYPE_AUDIO; + } else if (_kind == kRTCMediaStreamTrackKindVideo) { + parameters.kind = cricket::MEDIA_TYPE_VIDEO; + } else { + RTC_DCHECK_NOTREACHED(); + } + if (_clockRate != nil) { + parameters.clock_rate = absl::optional<int>(_clockRate.intValue); + } + if (_numChannels != nil) { + parameters.num_channels = absl::optional<int>(_numChannels.intValue); + } + for (NSString *paramKey in _parameters.allKeys) { + std::string key = [NSString stdStringForString:paramKey]; + std::string value = [NSString stdStringForString:_parameters[paramKey]]; + parameters.parameters[key] = value; + } + return parameters; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters+Private.h new file mode 100644 index 0000000000..d12ca624e3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpEncodingParameters.h" + +#include "api/rtp_parameters.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCRtpEncodingParameters) +() + + /** Returns the equivalent native RtpEncodingParameters structure. */ + @property(nonatomic, readonly) webrtc::RtpEncodingParameters nativeParameters; + +/** Initialize the object with a native RtpEncodingParameters structure. */ +- (instancetype)initWithNativeParameters:(const webrtc::RtpEncodingParameters &)nativeParameters + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters.h new file mode 100644 index 0000000000..07f6b7a39c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters.h @@ -0,0 +1,76 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Corresponds to webrtc::Priority. */ +typedef NS_ENUM(NSInteger, RTCPriority) { + RTCPriorityVeryLow, + RTCPriorityLow, + RTCPriorityMedium, + RTCPriorityHigh +}; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpEncodingParameters) : NSObject + +/** The idenfifier for the encoding layer. This is used in simulcast. */ +@property(nonatomic, copy, nullable) NSString *rid; + +/** Controls whether the encoding is currently transmitted. */ +@property(nonatomic, assign) BOOL isActive; + +/** The maximum bitrate to use for the encoding, or nil if there is no + * limit. + */ +@property(nonatomic, copy, nullable) NSNumber *maxBitrateBps; + +/** The minimum bitrate to use for the encoding, or nil if there is no + * limit. + */ +@property(nonatomic, copy, nullable) NSNumber *minBitrateBps; + +/** The maximum framerate to use for the encoding, or nil if there is no + * limit. + */ +@property(nonatomic, copy, nullable) NSNumber *maxFramerate; + +/** The requested number of temporal layers to use for the encoding, or nil + * if the default should be used. + */ +@property(nonatomic, copy, nullable) NSNumber *numTemporalLayers; + +/** Scale the width and height down by this factor for video. If nil, + * implementation default scaling factor will be used. + */ +@property(nonatomic, copy, nullable) NSNumber *scaleResolutionDownBy; + +/** The SSRC being used by this encoding. */ +@property(nonatomic, readonly, nullable) NSNumber *ssrc; + +/** The relative bitrate priority. */ +@property(nonatomic, assign) double bitratePriority; + +/** The relative DiffServ Code Point priority. */ +@property(nonatomic, assign) RTCPriority networkPriority; + +/** Allow dynamic frame length changes for audio: + https://w3c.github.io/webrtc-extensions/#dom-rtcrtpencodingparameters-adaptiveptime */ +@property(nonatomic, assign) BOOL adaptiveAudioPacketTime; + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters.mm new file mode 100644 index 0000000000..d6087dafb0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpEncodingParameters.mm @@ -0,0 +1,128 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpEncodingParameters+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCRtpEncodingParameters) + +@synthesize rid = _rid; +@synthesize isActive = _isActive; +@synthesize maxBitrateBps = _maxBitrateBps; +@synthesize minBitrateBps = _minBitrateBps; +@synthesize maxFramerate = _maxFramerate; +@synthesize numTemporalLayers = _numTemporalLayers; +@synthesize scaleResolutionDownBy = _scaleResolutionDownBy; +@synthesize ssrc = _ssrc; +@synthesize bitratePriority = _bitratePriority; +@synthesize networkPriority = _networkPriority; +@synthesize adaptiveAudioPacketTime = _adaptiveAudioPacketTime; + +- (instancetype)init { + webrtc::RtpEncodingParameters nativeParameters; + return [self initWithNativeParameters:nativeParameters]; +} + +- (instancetype)initWithNativeParameters: + (const webrtc::RtpEncodingParameters &)nativeParameters { + if (self = [super init]) { + if (!nativeParameters.rid.empty()) { + _rid = [NSString stringForStdString:nativeParameters.rid]; + } + _isActive = nativeParameters.active; + if (nativeParameters.max_bitrate_bps) { + _maxBitrateBps = + [NSNumber numberWithInt:*nativeParameters.max_bitrate_bps]; + } + if (nativeParameters.min_bitrate_bps) { + _minBitrateBps = + [NSNumber numberWithInt:*nativeParameters.min_bitrate_bps]; + } + if (nativeParameters.max_framerate) { + _maxFramerate = [NSNumber numberWithInt:*nativeParameters.max_framerate]; + } + if (nativeParameters.num_temporal_layers) { + _numTemporalLayers = [NSNumber numberWithInt:*nativeParameters.num_temporal_layers]; + } + if (nativeParameters.scale_resolution_down_by) { + _scaleResolutionDownBy = + [NSNumber numberWithDouble:*nativeParameters.scale_resolution_down_by]; + } + if (nativeParameters.ssrc) { + _ssrc = [NSNumber numberWithUnsignedLong:*nativeParameters.ssrc]; + } + _bitratePriority = nativeParameters.bitrate_priority; + _networkPriority = [RTC_OBJC_TYPE(RTCRtpEncodingParameters) + priorityFromNativePriority:nativeParameters.network_priority]; + _adaptiveAudioPacketTime = nativeParameters.adaptive_ptime; + } + return self; +} + +- (webrtc::RtpEncodingParameters)nativeParameters { + webrtc::RtpEncodingParameters parameters; + if (_rid != nil) { + parameters.rid = [NSString stdStringForString:_rid]; + } + parameters.active = _isActive; + if (_maxBitrateBps != nil) { + parameters.max_bitrate_bps = absl::optional<int>(_maxBitrateBps.intValue); + } + if (_minBitrateBps != nil) { + parameters.min_bitrate_bps = absl::optional<int>(_minBitrateBps.intValue); + } + if (_maxFramerate != nil) { + parameters.max_framerate = absl::optional<int>(_maxFramerate.intValue); + } + if (_numTemporalLayers != nil) { + parameters.num_temporal_layers = absl::optional<int>(_numTemporalLayers.intValue); + } + if (_scaleResolutionDownBy != nil) { + parameters.scale_resolution_down_by = + absl::optional<double>(_scaleResolutionDownBy.doubleValue); + } + if (_ssrc != nil) { + parameters.ssrc = absl::optional<uint32_t>(_ssrc.unsignedLongValue); + } + parameters.bitrate_priority = _bitratePriority; + parameters.network_priority = + [RTC_OBJC_TYPE(RTCRtpEncodingParameters) nativePriorityFromPriority:_networkPriority]; + parameters.adaptive_ptime = _adaptiveAudioPacketTime; + return parameters; +} + ++ (webrtc::Priority)nativePriorityFromPriority:(RTCPriority)networkPriority { + switch (networkPriority) { + case RTCPriorityVeryLow: + return webrtc::Priority::kVeryLow; + case RTCPriorityLow: + return webrtc::Priority::kLow; + case RTCPriorityMedium: + return webrtc::Priority::kMedium; + case RTCPriorityHigh: + return webrtc::Priority::kHigh; + } +} + ++ (RTCPriority)priorityFromNativePriority:(webrtc::Priority)nativePriority { + switch (nativePriority) { + case webrtc::Priority::kVeryLow: + return RTCPriorityVeryLow; + case webrtc::Priority::kLow: + return RTCPriorityLow; + case webrtc::Priority::kMedium: + return RTCPriorityMedium; + case webrtc::Priority::kHigh: + return RTCPriorityHigh; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension+Private.h new file mode 100644 index 0000000000..0e0fbba5ac --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpHeaderExtension.h" + +#include "api/rtp_parameters.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCRtpHeaderExtension) +() + + /** Returns the equivalent native RtpExtension structure. */ + @property(nonatomic, readonly) webrtc::RtpExtension nativeParameters; + +/** Initialize the object with a native RtpExtension structure. */ +- (instancetype)initWithNativeParameters:(const webrtc::RtpExtension &)nativeParameters + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension.h new file mode 100644 index 0000000000..4000bf5372 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension.h @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpHeaderExtension) : NSObject + +/** The URI of the RTP header extension, as defined in RFC5285. */ +@property(nonatomic, readonly, copy) NSString *uri; + +/** The value put in the RTP packet to identify the header extension. */ +@property(nonatomic, readonly) int id; + +/** Whether the header extension is encrypted or not. */ +@property(nonatomic, readonly, getter=isEncrypted) BOOL encrypted; + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension.mm new file mode 100644 index 0000000000..68093e92ea --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpHeaderExtension.mm @@ -0,0 +1,43 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpHeaderExtension+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCRtpHeaderExtension) + +@synthesize uri = _uri; +@synthesize id = _id; +@synthesize encrypted = _encrypted; + +- (instancetype)init { + webrtc::RtpExtension nativeExtension; + return [self initWithNativeParameters:nativeExtension]; +} + +- (instancetype)initWithNativeParameters:(const webrtc::RtpExtension &)nativeParameters { + if (self = [super init]) { + _uri = [NSString stringForStdString:nativeParameters.uri]; + _id = nativeParameters.id; + _encrypted = nativeParameters.encrypt; + } + return self; +} + +- (webrtc::RtpExtension)nativeParameters { + webrtc::RtpExtension extension; + extension.uri = [NSString stdStringForString:_uri]; + extension.id = _id; + extension.encrypt = _encrypted; + return extension; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters+Private.h new file mode 100644 index 0000000000..139617f727 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpParameters.h" + +#include "api/rtp_parameters.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCRtpParameters) +() + + /** Returns the equivalent native RtpParameters structure. */ + @property(nonatomic, readonly) webrtc::RtpParameters nativeParameters; + +/** Initialize the object with a native RtpParameters structure. */ +- (instancetype)initWithNativeParameters:(const webrtc::RtpParameters &)nativeParameters + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters.h new file mode 100644 index 0000000000..3d71c55ab9 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters.h @@ -0,0 +1,58 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCRtcpParameters.h" +#import "RTCRtpCodecParameters.h" +#import "RTCRtpEncodingParameters.h" +#import "RTCRtpHeaderExtension.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Corresponds to webrtc::DegradationPreference. */ +typedef NS_ENUM(NSInteger, RTCDegradationPreference) { + RTCDegradationPreferenceDisabled, + RTCDegradationPreferenceMaintainFramerate, + RTCDegradationPreferenceMaintainResolution, + RTCDegradationPreferenceBalanced +}; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpParameters) : NSObject + +/** A unique identifier for the last set of parameters applied. */ +@property(nonatomic, copy) NSString *transactionId; + +/** Parameters used for RTCP. */ +@property(nonatomic, readonly, copy) RTC_OBJC_TYPE(RTCRtcpParameters) * rtcp; + +/** An array containing parameters for RTP header extensions. */ +@property(nonatomic, readonly, copy) + NSArray<RTC_OBJC_TYPE(RTCRtpHeaderExtension) *> *headerExtensions; + +/** The currently active encodings in the order of preference. */ +@property(nonatomic, copy) NSArray<RTC_OBJC_TYPE(RTCRtpEncodingParameters) *> *encodings; + +/** The negotiated set of send codecs in order of preference. */ +@property(nonatomic, copy) NSArray<RTC_OBJC_TYPE(RTCRtpCodecParameters) *> *codecs; + +/** + * Degradation preference in case of CPU adaptation or constrained bandwidth. + * If nil, implementation default degradation preference will be used. + */ +@property(nonatomic, copy, nullable) NSNumber *degradationPreference; + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters.mm new file mode 100644 index 0000000000..2baf0ecd80 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpParameters.mm @@ -0,0 +1,121 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpParameters+Private.h" + +#import "RTCRtcpParameters+Private.h" +#import "RTCRtpCodecParameters+Private.h" +#import "RTCRtpEncodingParameters+Private.h" +#import "RTCRtpHeaderExtension+Private.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCRtpParameters) + +@synthesize transactionId = _transactionId; +@synthesize rtcp = _rtcp; +@synthesize headerExtensions = _headerExtensions; +@synthesize encodings = _encodings; +@synthesize codecs = _codecs; +@synthesize degradationPreference = _degradationPreference; + +- (instancetype)init { + webrtc::RtpParameters nativeParameters; + return [self initWithNativeParameters:nativeParameters]; +} + +- (instancetype)initWithNativeParameters: + (const webrtc::RtpParameters &)nativeParameters { + if (self = [super init]) { + _transactionId = [NSString stringForStdString:nativeParameters.transaction_id]; + _rtcp = + [[RTC_OBJC_TYPE(RTCRtcpParameters) alloc] initWithNativeParameters:nativeParameters.rtcp]; + + NSMutableArray *headerExtensions = [[NSMutableArray alloc] init]; + for (const auto &headerExtension : nativeParameters.header_extensions) { + [headerExtensions addObject:[[RTC_OBJC_TYPE(RTCRtpHeaderExtension) alloc] + initWithNativeParameters:headerExtension]]; + } + _headerExtensions = headerExtensions; + + NSMutableArray *encodings = [[NSMutableArray alloc] init]; + for (const auto &encoding : nativeParameters.encodings) { + [encodings addObject:[[RTC_OBJC_TYPE(RTCRtpEncodingParameters) alloc] + initWithNativeParameters:encoding]]; + } + _encodings = encodings; + + NSMutableArray *codecs = [[NSMutableArray alloc] init]; + for (const auto &codec : nativeParameters.codecs) { + [codecs + addObject:[[RTC_OBJC_TYPE(RTCRtpCodecParameters) alloc] initWithNativeParameters:codec]]; + } + _codecs = codecs; + + _degradationPreference = [RTC_OBJC_TYPE(RTCRtpParameters) + degradationPreferenceFromNativeDegradationPreference:nativeParameters + .degradation_preference]; + } + return self; +} + +- (webrtc::RtpParameters)nativeParameters { + webrtc::RtpParameters parameters; + parameters.transaction_id = [NSString stdStringForString:_transactionId]; + parameters.rtcp = [_rtcp nativeParameters]; + for (RTC_OBJC_TYPE(RTCRtpHeaderExtension) * headerExtension in _headerExtensions) { + parameters.header_extensions.push_back(headerExtension.nativeParameters); + } + for (RTC_OBJC_TYPE(RTCRtpEncodingParameters) * encoding in _encodings) { + parameters.encodings.push_back(encoding.nativeParameters); + } + for (RTC_OBJC_TYPE(RTCRtpCodecParameters) * codec in _codecs) { + parameters.codecs.push_back(codec.nativeParameters); + } + if (_degradationPreference) { + parameters.degradation_preference = [RTC_OBJC_TYPE(RTCRtpParameters) + nativeDegradationPreferenceFromDegradationPreference:(RTCDegradationPreference) + _degradationPreference.intValue]; + } + return parameters; +} + ++ (webrtc::DegradationPreference)nativeDegradationPreferenceFromDegradationPreference: + (RTCDegradationPreference)degradationPreference { + switch (degradationPreference) { + case RTCDegradationPreferenceDisabled: + return webrtc::DegradationPreference::DISABLED; + case RTCDegradationPreferenceMaintainFramerate: + return webrtc::DegradationPreference::MAINTAIN_FRAMERATE; + case RTCDegradationPreferenceMaintainResolution: + return webrtc::DegradationPreference::MAINTAIN_RESOLUTION; + case RTCDegradationPreferenceBalanced: + return webrtc::DegradationPreference::BALANCED; + } +} + ++ (NSNumber *)degradationPreferenceFromNativeDegradationPreference: + (absl::optional<webrtc::DegradationPreference>)nativeDegradationPreference { + if (!nativeDegradationPreference.has_value()) { + return nil; + } + + switch (*nativeDegradationPreference) { + case webrtc::DegradationPreference::DISABLED: + return @(RTCDegradationPreferenceDisabled); + case webrtc::DegradationPreference::MAINTAIN_FRAMERATE: + return @(RTCDegradationPreferenceMaintainFramerate); + case webrtc::DegradationPreference::MAINTAIN_RESOLUTION: + return @(RTCDegradationPreferenceMaintainResolution); + case webrtc::DegradationPreference::BALANCED: + return @(RTCDegradationPreferenceBalanced); + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver+Native.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver+Native.h new file mode 100644 index 0000000000..c15ce70079 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver+Native.h @@ -0,0 +1,32 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpReceiver.h" + +#include "api/crypto/frame_decryptor_interface.h" +#include "api/scoped_refptr.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class extension exposes methods that work directly with injectable C++ components. + */ +@interface RTC_OBJC_TYPE (RTCRtpReceiver) +() + + /** Sets a user defined frame decryptor that will decrypt the entire frame. + * This will decrypt the entire frame using the user provided decryption + * mechanism regardless of whether SRTP is enabled or not. + */ + - (void)setFrameDecryptor : (rtc::scoped_refptr<webrtc::FrameDecryptorInterface>)frameDecryptor; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver+Private.h new file mode 100644 index 0000000000..6aed0b4bc5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver+Private.h @@ -0,0 +1,52 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpReceiver.h" + +#include "api/rtp_receiver_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); + +namespace webrtc { + +class RtpReceiverDelegateAdapter : public RtpReceiverObserverInterface { + public: + RtpReceiverDelegateAdapter(RTC_OBJC_TYPE(RTCRtpReceiver) * receiver); + + void OnFirstPacketReceived(cricket::MediaType media_type) override; + + private: + __weak RTC_OBJC_TYPE(RTCRtpReceiver) * receiver_; +}; + +} // namespace webrtc + +@interface RTC_OBJC_TYPE (RTCRtpReceiver) +() + + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::RtpReceiverInterface> nativeRtpReceiver; + +/** Initialize an RTCRtpReceiver with a native RtpReceiverInterface. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeRtpReceiver:(rtc::scoped_refptr<webrtc::RtpReceiverInterface>)nativeRtpReceiver + NS_DESIGNATED_INITIALIZER; + ++ (RTCRtpMediaType)mediaTypeForNativeMediaType:(cricket::MediaType)nativeMediaType; + ++ (cricket::MediaType)nativeMediaTypeForMediaType:(RTCRtpMediaType)mediaType; + ++ (NSString*)stringForMediaType:(RTCRtpMediaType)mediaType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver.h new file mode 100644 index 0000000000..1e407fd71b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver.h @@ -0,0 +1,86 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCMediaStreamTrack.h" +#import "RTCRtpParameters.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Represents the media type of the RtpReceiver. */ +typedef NS_ENUM(NSInteger, RTCRtpMediaType) { + RTCRtpMediaTypeAudio, + RTCRtpMediaTypeVideo, + RTCRtpMediaTypeData, + RTCRtpMediaTypeUnsupported, +}; + +@class RTC_OBJC_TYPE(RTCRtpReceiver); + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCRtpReceiverDelegate)<NSObject> + + /** Called when the first RTP packet is received. + * + * Note: Currently if there are multiple RtpReceivers of the same media type, + * they will all call OnFirstPacketReceived at once. + * + * For example, if we create three audio receivers, A/B/C, they will listen to + * the same signal from the underneath network layer. Whenever the first audio packet + * is received, the underneath signal will be fired. All the receivers A/B/C will be + * notified and the callback of the receiver's delegate will be called. + * + * The process is the same for video receivers. + */ + - (void)rtpReceiver + : (RTC_OBJC_TYPE(RTCRtpReceiver) *)rtpReceiver didReceiveFirstPacketForMediaType + : (RTCRtpMediaType)mediaType; + +@end + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCRtpReceiver)<NSObject> + + /** A unique identifier for this receiver. */ + @property(nonatomic, readonly) NSString *receiverId; + +/** The currently active RTCRtpParameters, as defined in + * https://www.w3.org/TR/webrtc/#idl-def-RTCRtpParameters. + * + * The WebRTC specification only defines RTCRtpParameters in terms of senders, + * but this API also applies them to receivers, similar to ORTC: + * http://ortc.org/wp-content/uploads/2016/03/ortc.html#rtcrtpparameters*. + */ +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCRtpParameters) * parameters; + +/** The RTCMediaStreamTrack associated with the receiver. + * Note: reading this property returns a new instance of + * RTCMediaStreamTrack. Use isEqual: instead of == to compare + * RTCMediaStreamTrack instances. + */ +@property(nonatomic, readonly, nullable) RTC_OBJC_TYPE(RTCMediaStreamTrack) * track; + +/** The delegate for this RtpReceiver. */ +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCRtpReceiverDelegate)> delegate; + +@end + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpReceiver) : NSObject <RTC_OBJC_TYPE(RTCRtpReceiver)> + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver.mm new file mode 100644 index 0000000000..60af86ac1b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpReceiver.mm @@ -0,0 +1,159 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpReceiver+Private.h" + +#import "RTCMediaStreamTrack+Private.h" +#import "RTCRtpParameters+Private.h" +#import "RTCRtpReceiver+Native.h" +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +#include "api/media_stream_interface.h" + +namespace webrtc { + +RtpReceiverDelegateAdapter::RtpReceiverDelegateAdapter(RTC_OBJC_TYPE(RTCRtpReceiver) * receiver) { + RTC_CHECK(receiver); + receiver_ = receiver; +} + +void RtpReceiverDelegateAdapter::OnFirstPacketReceived( + cricket::MediaType media_type) { + RTCRtpMediaType packet_media_type = + [RTC_OBJC_TYPE(RTCRtpReceiver) mediaTypeForNativeMediaType:media_type]; + RTC_OBJC_TYPE(RTCRtpReceiver) *receiver = receiver_; + [receiver.delegate rtpReceiver:receiver didReceiveFirstPacketForMediaType:packet_media_type]; +} + +} // namespace webrtc + +@implementation RTC_OBJC_TYPE (RTCRtpReceiver) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + rtc::scoped_refptr<webrtc::RtpReceiverInterface> _nativeRtpReceiver; + std::unique_ptr<webrtc::RtpReceiverDelegateAdapter> _observer; +} + +@synthesize delegate = _delegate; + +- (NSString *)receiverId { + return [NSString stringForStdString:_nativeRtpReceiver->id()]; +} + +- (RTC_OBJC_TYPE(RTCRtpParameters) *)parameters { + return [[RTC_OBJC_TYPE(RTCRtpParameters) alloc] + initWithNativeParameters:_nativeRtpReceiver->GetParameters()]; +} + +- (nullable RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track { + rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> nativeTrack( + _nativeRtpReceiver->track()); + if (nativeTrack) { + return [RTC_OBJC_TYPE(RTCMediaStreamTrack) mediaTrackForNativeTrack:nativeTrack + factory:_factory]; + } + return nil; +} + +- (NSString *)description { + return [NSString + stringWithFormat:@"RTC_OBJC_TYPE(RTCRtpReceiver) {\n receiverId: %@\n}", self.receiverId]; +} + +- (void)dealloc { + if (_nativeRtpReceiver) { + _nativeRtpReceiver->SetObserver(nullptr); + } +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (object == nil) { + return NO; + } + if (![object isMemberOfClass:[self class]]) { + return NO; + } + RTC_OBJC_TYPE(RTCRtpReceiver) *receiver = (RTC_OBJC_TYPE(RTCRtpReceiver) *)object; + return _nativeRtpReceiver == receiver.nativeRtpReceiver; +} + +- (NSUInteger)hash { + return (NSUInteger)_nativeRtpReceiver.get(); +} + +#pragma mark - Native + +- (void)setFrameDecryptor:(rtc::scoped_refptr<webrtc::FrameDecryptorInterface>)frameDecryptor { + _nativeRtpReceiver->SetFrameDecryptor(frameDecryptor); +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::RtpReceiverInterface>)nativeRtpReceiver { + return _nativeRtpReceiver; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeRtpReceiver: + (rtc::scoped_refptr<webrtc::RtpReceiverInterface>)nativeRtpReceiver { + if (self = [super init]) { + _factory = factory; + _nativeRtpReceiver = nativeRtpReceiver; + RTCLogInfo(@"RTC_OBJC_TYPE(RTCRtpReceiver)(%p): created receiver: %@", self, self.description); + _observer.reset(new webrtc::RtpReceiverDelegateAdapter(self)); + _nativeRtpReceiver->SetObserver(_observer.get()); + } + return self; +} + ++ (RTCRtpMediaType)mediaTypeForNativeMediaType: + (cricket::MediaType)nativeMediaType { + switch (nativeMediaType) { + case cricket::MEDIA_TYPE_AUDIO: + return RTCRtpMediaTypeAudio; + case cricket::MEDIA_TYPE_VIDEO: + return RTCRtpMediaTypeVideo; + case cricket::MEDIA_TYPE_DATA: + return RTCRtpMediaTypeData; + case cricket::MEDIA_TYPE_UNSUPPORTED: + return RTCRtpMediaTypeUnsupported; + } +} + ++ (cricket::MediaType)nativeMediaTypeForMediaType:(RTCRtpMediaType)mediaType { + switch (mediaType) { + case RTCRtpMediaTypeAudio: + return cricket::MEDIA_TYPE_AUDIO; + case RTCRtpMediaTypeVideo: + return cricket::MEDIA_TYPE_VIDEO; + case RTCRtpMediaTypeData: + return cricket::MEDIA_TYPE_DATA; + case RTCRtpMediaTypeUnsupported: + return cricket::MEDIA_TYPE_UNSUPPORTED; + } +} + ++ (NSString *)stringForMediaType:(RTCRtpMediaType)mediaType { + switch (mediaType) { + case RTCRtpMediaTypeAudio: + return @"AUDIO"; + case RTCRtpMediaTypeVideo: + return @"VIDEO"; + case RTCRtpMediaTypeData: + return @"DATA"; + case RTCRtpMediaTypeUnsupported: + return @"UNSUPPORTED"; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender+Native.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender+Native.h new file mode 100644 index 0000000000..249d5c5e09 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender+Native.h @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpSender.h" + +#include "api/crypto/frame_encryptor_interface.h" +#include "api/scoped_refptr.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class extension exposes methods that work directly with injectable C++ components. + */ +@interface RTC_OBJC_TYPE (RTCRtpSender) +() + + /** Sets a defined frame encryptor that will encrypt the entire frame + * before it is sent across the network. This will encrypt the entire frame + * using the user provided encryption mechanism regardless of whether SRTP is + * enabled or not. + */ + - (void)setFrameEncryptor : (rtc::scoped_refptr<webrtc::FrameEncryptorInterface>)frameEncryptor; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender+Private.h new file mode 100644 index 0000000000..6fdb42bb22 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender+Private.h @@ -0,0 +1,31 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpSender.h" + +#include "api/rtp_sender_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); + +@interface RTC_OBJC_TYPE (RTCRtpSender) +() + + @property(nonatomic, readonly) rtc::scoped_refptr<webrtc::RtpSenderInterface> nativeRtpSender; + +/** Initialize an RTCRtpSender with a native RtpSenderInterface. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeRtpSender:(rtc::scoped_refptr<webrtc::RtpSenderInterface>)nativeRtpSender + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender.h new file mode 100644 index 0000000000..fcdf199869 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender.h @@ -0,0 +1,54 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCDtmfSender.h" +#import "RTCMacros.h" +#import "RTCMediaStreamTrack.h" +#import "RTCRtpParameters.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCRtpSender)<NSObject> + +/** A unique identifier for this sender. */ +@property(nonatomic, readonly) NSString *senderId; + +/** The currently active RTCRtpParameters, as defined in + * https://www.w3.org/TR/webrtc/#idl-def-RTCRtpParameters. + */ +@property(nonatomic, copy) RTC_OBJC_TYPE(RTCRtpParameters) * parameters; + +/** The RTCMediaStreamTrack associated with the sender. + * Note: reading this property returns a new instance of + * RTCMediaStreamTrack. Use isEqual: instead of == to compare + * RTCMediaStreamTrack instances. + */ +@property(nonatomic, copy, nullable) RTC_OBJC_TYPE(RTCMediaStreamTrack) * track; + +/** IDs of streams associated with the RTP sender */ +@property(nonatomic, copy) NSArray<NSString *> *streamIds; + +/** The RTCDtmfSender accociated with the RTP sender. */ +@property(nonatomic, readonly, nullable) id<RTC_OBJC_TYPE(RTCDtmfSender)> dtmfSender; + +@end + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpSender) : NSObject <RTC_OBJC_TYPE(RTCRtpSender)> + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender.mm new file mode 100644 index 0000000000..4fadb30f49 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpSender.mm @@ -0,0 +1,132 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpSender+Private.h" + +#import "RTCDtmfSender+Private.h" +#import "RTCMediaStreamTrack+Private.h" +#import "RTCRtpParameters+Private.h" +#import "RTCRtpSender+Native.h" +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +#include "api/media_stream_interface.h" + +@implementation RTC_OBJC_TYPE (RTCRtpSender) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + rtc::scoped_refptr<webrtc::RtpSenderInterface> _nativeRtpSender; +} + +@synthesize dtmfSender = _dtmfSender; + +- (NSString *)senderId { + return [NSString stringForStdString:_nativeRtpSender->id()]; +} + +- (RTC_OBJC_TYPE(RTCRtpParameters) *)parameters { + return [[RTC_OBJC_TYPE(RTCRtpParameters) alloc] + initWithNativeParameters:_nativeRtpSender->GetParameters()]; +} + +- (void)setParameters:(RTC_OBJC_TYPE(RTCRtpParameters) *)parameters { + if (!_nativeRtpSender->SetParameters(parameters.nativeParameters).ok()) { + RTCLogError(@"RTC_OBJC_TYPE(RTCRtpSender)(%p): Failed to set parameters: %@", self, parameters); + } +} + +- (RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track { + rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> nativeTrack( + _nativeRtpSender->track()); + if (nativeTrack) { + return [RTC_OBJC_TYPE(RTCMediaStreamTrack) mediaTrackForNativeTrack:nativeTrack + factory:_factory]; + } + return nil; +} + +- (void)setTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track { + if (!_nativeRtpSender->SetTrack(track.nativeTrack.get())) { + RTCLogError(@"RTC_OBJC_TYPE(RTCRtpSender)(%p): Failed to set track %@", self, track); + } +} + +- (NSArray<NSString *> *)streamIds { + std::vector<std::string> nativeStreamIds = _nativeRtpSender->stream_ids(); + NSMutableArray *streamIds = [NSMutableArray arrayWithCapacity:nativeStreamIds.size()]; + for (const auto &s : nativeStreamIds) { + [streamIds addObject:[NSString stringForStdString:s]]; + } + return streamIds; +} + +- (void)setStreamIds:(NSArray<NSString *> *)streamIds { + std::vector<std::string> nativeStreamIds; + for (NSString *streamId in streamIds) { + nativeStreamIds.push_back([streamId UTF8String]); + } + _nativeRtpSender->SetStreams(nativeStreamIds); +} + +- (NSString *)description { + return [NSString + stringWithFormat:@"RTC_OBJC_TYPE(RTCRtpSender) {\n senderId: %@\n}", self.senderId]; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (object == nil) { + return NO; + } + if (![object isMemberOfClass:[self class]]) { + return NO; + } + RTC_OBJC_TYPE(RTCRtpSender) *sender = (RTC_OBJC_TYPE(RTCRtpSender) *)object; + return _nativeRtpSender == sender.nativeRtpSender; +} + +- (NSUInteger)hash { + return (NSUInteger)_nativeRtpSender.get(); +} + +#pragma mark - Native + +- (void)setFrameEncryptor:(rtc::scoped_refptr<webrtc::FrameEncryptorInterface>)frameEncryptor { + _nativeRtpSender->SetFrameEncryptor(frameEncryptor); +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::RtpSenderInterface>)nativeRtpSender { + return _nativeRtpSender; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeRtpSender:(rtc::scoped_refptr<webrtc::RtpSenderInterface>)nativeRtpSender { + NSParameterAssert(factory); + NSParameterAssert(nativeRtpSender); + if (self = [super init]) { + _factory = factory; + _nativeRtpSender = nativeRtpSender; + if (_nativeRtpSender->media_type() == cricket::MEDIA_TYPE_AUDIO) { + rtc::scoped_refptr<webrtc::DtmfSenderInterface> nativeDtmfSender( + _nativeRtpSender->GetDtmfSender()); + if (nativeDtmfSender) { + _dtmfSender = + [[RTC_OBJC_TYPE(RTCDtmfSender) alloc] initWithNativeDtmfSender:nativeDtmfSender]; + } + } + RTCLogInfo(@"RTC_OBJC_TYPE(RTCRtpSender)(%p): created sender: %@", self, self.description); + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver+Private.h new file mode 100644 index 0000000000..65d45fb88e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver+Private.h @@ -0,0 +1,46 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpTransceiver.h" + +#include "api/rtp_transceiver_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); + +@interface RTC_OBJC_TYPE (RTCRtpTransceiverInit) +() + + @property(nonatomic, readonly) webrtc::RtpTransceiverInit nativeInit; + +@end + +@interface RTC_OBJC_TYPE (RTCRtpTransceiver) +() + + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::RtpTransceiverInterface> nativeRtpTransceiver; + +/** Initialize an RTCRtpTransceiver with a native RtpTransceiverInterface. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeRtpTransceiver: + (rtc::scoped_refptr<webrtc::RtpTransceiverInterface>)nativeRtpTransceiver + NS_DESIGNATED_INITIALIZER; + ++ (webrtc::RtpTransceiverDirection)nativeRtpTransceiverDirectionFromDirection: + (RTCRtpTransceiverDirection)direction; + ++ (RTCRtpTransceiverDirection)rtpTransceiverDirectionFromNativeDirection: + (webrtc::RtpTransceiverDirection)nativeDirection; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver.h new file mode 100644 index 0000000000..fd59013639 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver.h @@ -0,0 +1,137 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCRtpReceiver.h" +#import "RTCRtpSender.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kRTCRtpTransceiverErrorDomain; + +/** https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverdirection */ +typedef NS_ENUM(NSInteger, RTCRtpTransceiverDirection) { + RTCRtpTransceiverDirectionSendRecv, + RTCRtpTransceiverDirectionSendOnly, + RTCRtpTransceiverDirectionRecvOnly, + RTCRtpTransceiverDirectionInactive, + RTCRtpTransceiverDirectionStopped +}; + +/** Structure for initializing an RTCRtpTransceiver in a call to + * RTCPeerConnection.addTransceiver. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverinit + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpTransceiverInit) : NSObject + +/** Direction of the RTCRtpTransceiver. See RTCRtpTransceiver.direction. */ +@property(nonatomic) RTCRtpTransceiverDirection direction; + +/** The added RTCRtpTransceiver will be added to these streams. */ +@property(nonatomic) NSArray<NSString *> *streamIds; + +/** TODO(bugs.webrtc.org/7600): Not implemented. */ +@property(nonatomic) NSArray<RTC_OBJC_TYPE(RTCRtpEncodingParameters) *> *sendEncodings; + +@end + +@class RTC_OBJC_TYPE(RTCRtpTransceiver); + +/** The RTCRtpTransceiver maps to the RTCRtpTransceiver defined by the + * WebRTC specification. A transceiver represents a combination of an RTCRtpSender + * and an RTCRtpReceiver that share a common mid. As defined in JSEP, an + * RTCRtpTransceiver is said to be associated with a media description if its + * mid property is non-nil; otherwise, it is said to be disassociated. + * JSEP: https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24 + * + * Note that RTCRtpTransceivers are only supported when using + * RTCPeerConnection with Unified Plan SDP. + * + * WebRTC specification for RTCRtpTransceiver, the JavaScript analog: + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCRtpTransceiver)<NSObject> + + /** Media type of the transceiver. The sender and receiver will also have this + * type. + */ + @property(nonatomic, readonly) RTCRtpMediaType mediaType; + +/** The mid attribute is the mid negotiated and present in the local and + * remote descriptions. Before negotiation is complete, the mid value may be + * nil. After rollbacks, the value may change from a non-nil value to nil. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-mid + */ +@property(nonatomic, readonly) NSString *mid; + +/** The sender attribute exposes the RTCRtpSender corresponding to the RTP + * media that may be sent with the transceiver's mid. The sender is always + * present, regardless of the direction of media. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-sender + */ +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCRtpSender) * sender; + +/** The receiver attribute exposes the RTCRtpReceiver corresponding to the RTP + * media that may be received with the transceiver's mid. The receiver is + * always present, regardless of the direction of media. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-receiver + */ +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCRtpReceiver) * receiver; + +/** The isStopped attribute indicates that the sender of this transceiver will + * no longer send, and that the receiver will no longer receive. It is true if + * either stop has been called or if setting the local or remote description + * has caused the RTCRtpTransceiver to be stopped. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stopped + */ +@property(nonatomic, readonly) BOOL isStopped; + +/** The direction attribute indicates the preferred direction of this + * transceiver, which will be used in calls to createOffer and createAnswer. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction + */ +@property(nonatomic, readonly) RTCRtpTransceiverDirection direction; + +/** The currentDirection attribute indicates the current direction negotiated + * for this transceiver. If this transceiver has never been represented in an + * offer/answer exchange, or if the transceiver is stopped, the value is not + * present and this method returns NO. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-currentdirection + */ +- (BOOL)currentDirection:(RTCRtpTransceiverDirection *)currentDirectionOut; + +/** The stop method irreversibly stops the RTCRtpTransceiver. The sender of + * this transceiver will no longer send, the receiver will no longer receive. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop + */ +- (void)stopInternal; + +/** An update of directionality does not take effect immediately. Instead, + * future calls to createOffer and createAnswer mark the corresponding media + * descriptions as sendrecv, sendonly, recvonly, or inactive. + * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction + */ +- (void)setDirection:(RTCRtpTransceiverDirection)direction error:(NSError **)error; + +@end + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCRtpTransceiver) : NSObject <RTC_OBJC_TYPE(RTCRtpTransceiver)> + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm new file mode 100644 index 0000000000..ae1cf79864 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm @@ -0,0 +1,190 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCRtpTransceiver+Private.h" + +#import "RTCRtpEncodingParameters+Private.h" +#import "RTCRtpParameters+Private.h" +#import "RTCRtpReceiver+Private.h" +#import "RTCRtpSender+Private.h" +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +NSString *const kRTCRtpTransceiverErrorDomain = @"org.webrtc.RTCRtpTranceiver"; + +@implementation RTC_OBJC_TYPE (RTCRtpTransceiverInit) + +@synthesize direction = _direction; +@synthesize streamIds = _streamIds; +@synthesize sendEncodings = _sendEncodings; + +- (instancetype)init { + if (self = [super init]) { + _direction = RTCRtpTransceiverDirectionSendRecv; + } + return self; +} + +- (webrtc::RtpTransceiverInit)nativeInit { + webrtc::RtpTransceiverInit init; + init.direction = + [RTC_OBJC_TYPE(RTCRtpTransceiver) nativeRtpTransceiverDirectionFromDirection:_direction]; + for (NSString *streamId in _streamIds) { + init.stream_ids.push_back([streamId UTF8String]); + } + for (RTC_OBJC_TYPE(RTCRtpEncodingParameters) * sendEncoding in _sendEncodings) { + init.send_encodings.push_back(sendEncoding.nativeParameters); + } + return init; +} + +@end + +@implementation RTC_OBJC_TYPE (RTCRtpTransceiver) { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; + rtc::scoped_refptr<webrtc::RtpTransceiverInterface> _nativeRtpTransceiver; +} + +- (RTCRtpMediaType)mediaType { + return [RTC_OBJC_TYPE(RTCRtpReceiver) + mediaTypeForNativeMediaType:_nativeRtpTransceiver->media_type()]; +} + +- (NSString *)mid { + if (_nativeRtpTransceiver->mid()) { + return [NSString stringForStdString:*_nativeRtpTransceiver->mid()]; + } else { + return nil; + } +} + +@synthesize sender = _sender; +@synthesize receiver = _receiver; + +- (BOOL)isStopped { + return _nativeRtpTransceiver->stopped(); +} + +- (RTCRtpTransceiverDirection)direction { + return [RTC_OBJC_TYPE(RTCRtpTransceiver) + rtpTransceiverDirectionFromNativeDirection:_nativeRtpTransceiver->direction()]; +} + +- (void)setDirection:(RTCRtpTransceiverDirection)direction error:(NSError **)error { + webrtc::RTCError nativeError = _nativeRtpTransceiver->SetDirectionWithError( + [RTC_OBJC_TYPE(RTCRtpTransceiver) nativeRtpTransceiverDirectionFromDirection:direction]); + + if (!nativeError.ok() && error) { + *error = [NSError errorWithDomain:kRTCRtpTransceiverErrorDomain + code:static_cast<int>(nativeError.type()) + userInfo:@{ + @"message" : [NSString stringWithCString:nativeError.message() + encoding:NSUTF8StringEncoding] + }]; + } +} + +- (BOOL)currentDirection:(RTCRtpTransceiverDirection *)currentDirectionOut { + if (_nativeRtpTransceiver->current_direction()) { + *currentDirectionOut = [RTC_OBJC_TYPE(RTCRtpTransceiver) + rtpTransceiverDirectionFromNativeDirection:*_nativeRtpTransceiver->current_direction()]; + return YES; + } else { + return NO; + } +} + +- (void)stopInternal { + _nativeRtpTransceiver->StopInternal(); +} + +- (NSString *)description { + return [NSString + stringWithFormat:@"RTC_OBJC_TYPE(RTCRtpTransceiver) {\n sender: %@\n receiver: %@\n}", + _sender, + _receiver]; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (object == nil) { + return NO; + } + if (![object isMemberOfClass:[self class]]) { + return NO; + } + RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver = (RTC_OBJC_TYPE(RTCRtpTransceiver) *)object; + return _nativeRtpTransceiver == transceiver.nativeRtpTransceiver; +} + +- (NSUInteger)hash { + return (NSUInteger)_nativeRtpTransceiver.get(); +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::RtpTransceiverInterface>)nativeRtpTransceiver { + return _nativeRtpTransceiver; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeRtpTransceiver: + (rtc::scoped_refptr<webrtc::RtpTransceiverInterface>)nativeRtpTransceiver { + NSParameterAssert(factory); + NSParameterAssert(nativeRtpTransceiver); + if (self = [super init]) { + _factory = factory; + _nativeRtpTransceiver = nativeRtpTransceiver; + _sender = [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:_factory + nativeRtpSender:nativeRtpTransceiver->sender()]; + _receiver = + [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:_factory + nativeRtpReceiver:nativeRtpTransceiver->receiver()]; + RTCLogInfo( + @"RTC_OBJC_TYPE(RTCRtpTransceiver)(%p): created transceiver: %@", self, self.description); + } + return self; +} + ++ (webrtc::RtpTransceiverDirection)nativeRtpTransceiverDirectionFromDirection: + (RTCRtpTransceiverDirection)direction { + switch (direction) { + case RTCRtpTransceiverDirectionSendRecv: + return webrtc::RtpTransceiverDirection::kSendRecv; + case RTCRtpTransceiverDirectionSendOnly: + return webrtc::RtpTransceiverDirection::kSendOnly; + case RTCRtpTransceiverDirectionRecvOnly: + return webrtc::RtpTransceiverDirection::kRecvOnly; + case RTCRtpTransceiverDirectionInactive: + return webrtc::RtpTransceiverDirection::kInactive; + case RTCRtpTransceiverDirectionStopped: + return webrtc::RtpTransceiverDirection::kStopped; + } +} + ++ (RTCRtpTransceiverDirection)rtpTransceiverDirectionFromNativeDirection: + (webrtc::RtpTransceiverDirection)nativeDirection { + switch (nativeDirection) { + case webrtc::RtpTransceiverDirection::kSendRecv: + return RTCRtpTransceiverDirectionSendRecv; + case webrtc::RtpTransceiverDirection::kSendOnly: + return RTCRtpTransceiverDirectionSendOnly; + case webrtc::RtpTransceiverDirection::kRecvOnly: + return RTCRtpTransceiverDirectionRecvOnly; + case webrtc::RtpTransceiverDirection::kInactive: + return RTCRtpTransceiverDirectionInactive; + case webrtc::RtpTransceiverDirection::kStopped: + return RTCRtpTransceiverDirectionStopped; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSSLAdapter.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSSLAdapter.h new file mode 100644 index 0000000000..f68bc5e9e3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSSLAdapter.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +/** + * Initialize and clean up the SSL library. Failure is fatal. These call the + * corresponding functions in webrtc/rtc_base/ssladapter.h. + */ +RTC_EXTERN BOOL RTCInitializeSSL(void); +RTC_EXTERN BOOL RTCCleanupSSL(void); diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSSLAdapter.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSSLAdapter.mm new file mode 100644 index 0000000000..430249577b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSSLAdapter.mm @@ -0,0 +1,26 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCSSLAdapter.h" + +#include "rtc_base/checks.h" +#include "rtc_base/ssl_adapter.h" + +BOOL RTCInitializeSSL(void) { + BOOL initialized = rtc::InitializeSSL(); + RTC_DCHECK(initialized); + return initialized; +} + +BOOL RTCCleanupSSL(void) { + BOOL cleanedUp = rtc::CleanupSSL(); + RTC_DCHECK(cleanedUp); + return cleanedUp; +} diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription+Private.h new file mode 100644 index 0000000000..aa087e557f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription+Private.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCSessionDescription.h" + +#include "api/jsep.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCSessionDescription) +() + + /** + * The native SessionDescriptionInterface representation of this + * RTCSessionDescription object. This is needed to pass to the underlying C++ + * APIs. + */ + @property(nonatomic, + readonly) std::unique_ptr<webrtc::SessionDescriptionInterface> nativeDescription; + +/** + * Initialize an RTCSessionDescription from a native + * SessionDescriptionInterface. No ownership is taken of the native session + * description. + */ +- (instancetype)initWithNativeDescription: + (const webrtc::SessionDescriptionInterface *)nativeDescription; + ++ (std::string)stdStringForType:(RTCSdpType)type; + ++ (RTCSdpType)typeForStdString:(const std::string &)string; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription.h new file mode 100644 index 0000000000..8a9479d5cf --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription.h @@ -0,0 +1,48 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +/** + * Represents the session description type. This exposes the same types that are + * in C++, which doesn't include the rollback type that is in the W3C spec. + */ +typedef NS_ENUM(NSInteger, RTCSdpType) { + RTCSdpTypeOffer, + RTCSdpTypePrAnswer, + RTCSdpTypeAnswer, + RTCSdpTypeRollback, +}; + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCSessionDescription) : NSObject + +/** The type of session description. */ +@property(nonatomic, readonly) RTCSdpType type; + +/** The SDP string representation of this session description. */ +@property(nonatomic, readonly) NSString *sdp; + +- (instancetype)init NS_UNAVAILABLE; + +/** Initialize a session description with a type and SDP string. */ +- (instancetype)initWithType:(RTCSdpType)type sdp:(NSString *)sdp NS_DESIGNATED_INITIALIZER; + ++ (NSString *)stringForType:(RTCSdpType)type; + ++ (RTCSdpType)typeForString:(NSString *)string; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription.mm new file mode 100644 index 0000000000..539c90b14c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCSessionDescription.mm @@ -0,0 +1,103 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCSessionDescription+Private.h" + +#import "base/RTCLogging.h" +#import "helpers/NSString+StdString.h" + +#include "rtc_base/checks.h" + +@implementation RTC_OBJC_TYPE (RTCSessionDescription) + +@synthesize type = _type; +@synthesize sdp = _sdp; + ++ (NSString *)stringForType:(RTCSdpType)type { + std::string string = [[self class] stdStringForType:type]; + return [NSString stringForStdString:string]; +} + ++ (RTCSdpType)typeForString:(NSString *)string { + std::string typeString = string.stdString; + return [[self class] typeForStdString:typeString]; +} + +- (instancetype)initWithType:(RTCSdpType)type sdp:(NSString *)sdp { + if (self = [super init]) { + _type = type; + _sdp = [sdp copy]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCSessionDescription):\n%@\n%@", + [[self class] stringForType:_type], + _sdp]; +} + +#pragma mark - Private + +- (std::unique_ptr<webrtc::SessionDescriptionInterface>)nativeDescription { + webrtc::SdpParseError error; + + std::unique_ptr<webrtc::SessionDescriptionInterface> description(webrtc::CreateSessionDescription( + [[self class] stdStringForType:_type], _sdp.stdString, &error)); + + if (!description) { + RTCLogError(@"Failed to create session description: %s\nline: %s", + error.description.c_str(), + error.line.c_str()); + } + + return description; +} + +- (instancetype)initWithNativeDescription: + (const webrtc::SessionDescriptionInterface *)nativeDescription { + NSParameterAssert(nativeDescription); + std::string sdp; + nativeDescription->ToString(&sdp); + RTCSdpType type = [[self class] typeForStdString:nativeDescription->type()]; + + return [self initWithType:type + sdp:[NSString stringForStdString:sdp]]; +} + ++ (std::string)stdStringForType:(RTCSdpType)type { + switch (type) { + case RTCSdpTypeOffer: + return webrtc::SessionDescriptionInterface::kOffer; + case RTCSdpTypePrAnswer: + return webrtc::SessionDescriptionInterface::kPrAnswer; + case RTCSdpTypeAnswer: + return webrtc::SessionDescriptionInterface::kAnswer; + case RTCSdpTypeRollback: + return webrtc::SessionDescriptionInterface::kRollback; + } +} + ++ (RTCSdpType)typeForStdString:(const std::string &)string { + if (string == webrtc::SessionDescriptionInterface::kOffer) { + return RTCSdpTypeOffer; + } else if (string == webrtc::SessionDescriptionInterface::kPrAnswer) { + return RTCSdpTypePrAnswer; + } else if (string == webrtc::SessionDescriptionInterface::kAnswer) { + return RTCSdpTypeAnswer; + } else if (string == webrtc::SessionDescriptionInterface::kRollback) { + return RTCSdpTypeRollback; + } else { + RTC_DCHECK_NOTREACHED(); + return RTCSdpTypeOffer; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport+Private.h new file mode 100644 index 0000000000..47c5241d51 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport+Private.h @@ -0,0 +1,19 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCStatisticsReport.h" + +#include "api/stats/rtc_stats_report.h" + +@interface RTC_OBJC_TYPE (RTCStatisticsReport) (Private) + +- (instancetype)initWithReport : (const webrtc::RTCStatsReport &)report; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport.h new file mode 100644 index 0000000000..06dbf48d88 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport.h @@ -0,0 +1,55 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +@class RTC_OBJC_TYPE(RTCStatistics); + +NS_ASSUME_NONNULL_BEGIN + +/** A statistics report. Encapsulates a number of RTCStatistics objects. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCStatisticsReport) : NSObject + +/** The timestamp of the report in microseconds since 1970-01-01T00:00:00Z. */ +@property(nonatomic, readonly) CFTimeInterval timestamp_us; + +/** RTCStatistics objects by id. */ +@property(nonatomic, readonly) NSDictionary<NSString *, RTC_OBJC_TYPE(RTCStatistics) *> *statistics; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +/** A part of a report (a subreport) covering a certain area. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCStatistics) : NSObject + +/** The id of this subreport, e.g. "RTCMediaStreamTrack_receiver_2". */ +@property(nonatomic, readonly) NSString *id; + +/** The timestamp of the subreport in microseconds since 1970-01-01T00:00:00Z. */ +@property(nonatomic, readonly) CFTimeInterval timestamp_us; + +/** The type of the subreport, e.g. "track", "codec". */ +@property(nonatomic, readonly) NSString *type; + +/** The keys and values of the subreport, e.g. "totalFramesDuration = 5.551". + The values are either NSNumbers or NSStrings or NSArrays encapsulating NSNumbers + or NSStrings, or NSDictionary of NSString keys to NSNumber values. */ +@property(nonatomic, readonly) NSDictionary<NSString *, NSObject *> *values; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport.mm new file mode 100644 index 0000000000..bfe2424553 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCStatisticsReport.mm @@ -0,0 +1,193 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCStatisticsReport+Private.h" + +#include "helpers/NSString+StdString.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +/** Converts a single value to a suitable NSNumber, NSString or NSArray containing NSNumbers + or NSStrings, or NSDictionary of NSString keys to NSNumber values.*/ +NSObject *ValueFromStatsMember(const RTCStatsMemberInterface *member) { + if (member->is_defined()) { + switch (member->type()) { + case RTCStatsMemberInterface::kBool: + return [NSNumber numberWithBool:*member->cast_to<RTCStatsMember<bool>>()]; + case RTCStatsMemberInterface::kInt32: + return [NSNumber numberWithInt:*member->cast_to<RTCStatsMember<int32_t>>()]; + case RTCStatsMemberInterface::kUint32: + return [NSNumber numberWithUnsignedInt:*member->cast_to<RTCStatsMember<uint32_t>>()]; + case RTCStatsMemberInterface::kInt64: + return [NSNumber numberWithLong:*member->cast_to<RTCStatsMember<int64_t>>()]; + case RTCStatsMemberInterface::kUint64: + return [NSNumber numberWithUnsignedLong:*member->cast_to<RTCStatsMember<uint64_t>>()]; + case RTCStatsMemberInterface::kDouble: + return [NSNumber numberWithDouble:*member->cast_to<RTCStatsMember<double>>()]; + case RTCStatsMemberInterface::kString: + return [NSString stringForStdString:*member->cast_to<RTCStatsMember<std::string>>()]; + case RTCStatsMemberInterface::kSequenceBool: { + std::vector<bool> sequence = *member->cast_to<RTCStatsMember<std::vector<bool>>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (auto item : sequence) { + [array addObject:[NSNumber numberWithBool:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceInt32: { + std::vector<int32_t> sequence = *member->cast_to<RTCStatsMember<std::vector<int32_t>>>(); + NSMutableArray<NSNumber *> *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithInt:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceUint32: { + std::vector<uint32_t> sequence = *member->cast_to<RTCStatsMember<std::vector<uint32_t>>>(); + NSMutableArray<NSNumber *> *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithUnsignedInt:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceInt64: { + std::vector<int64_t> sequence = *member->cast_to<RTCStatsMember<std::vector<int64_t>>>(); + NSMutableArray<NSNumber *> *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithLong:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceUint64: { + std::vector<uint64_t> sequence = *member->cast_to<RTCStatsMember<std::vector<uint64_t>>>(); + NSMutableArray<NSNumber *> *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithUnsignedLong:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceDouble: { + std::vector<double> sequence = *member->cast_to<RTCStatsMember<std::vector<double>>>(); + NSMutableArray<NSNumber *> *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithDouble:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceString: { + std::vector<std::string> sequence = + *member->cast_to<RTCStatsMember<std::vector<std::string>>>(); + NSMutableArray<NSString *> *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSString stringForStdString:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kMapStringUint64: { + std::map<std::string, uint64_t> map = + *member->cast_to<RTCStatsMember<std::map<std::string, uint64_t>>>(); + NSMutableDictionary<NSString *, NSNumber *> *dictionary = + [NSMutableDictionary dictionaryWithCapacity:map.size()]; + for (const auto &item : map) { + dictionary[[NSString stringForStdString:item.first]] = @(item.second); + } + return [dictionary copy]; + } + case RTCStatsMemberInterface::kMapStringDouble: { + std::map<std::string, double> map = + *member->cast_to<RTCStatsMember<std::map<std::string, double>>>(); + NSMutableDictionary<NSString *, NSNumber *> *dictionary = + [NSMutableDictionary dictionaryWithCapacity:map.size()]; + for (const auto &item : map) { + dictionary[[NSString stringForStdString:item.first]] = @(item.second); + } + return [dictionary copy]; + } + default: + RTC_DCHECK_NOTREACHED(); + } + } + + return nil; +} +} // namespace webrtc + +@implementation RTC_OBJC_TYPE (RTCStatistics) + +@synthesize id = _id; +@synthesize timestamp_us = _timestamp_us; +@synthesize type = _type; +@synthesize values = _values; + +- (instancetype)initWithStatistics:(const webrtc::RTCStats &)statistics { + if (self = [super init]) { + _id = [NSString stringForStdString:statistics.id()]; + _timestamp_us = statistics.timestamp().us(); + _type = [NSString stringWithCString:statistics.type() encoding:NSUTF8StringEncoding]; + + NSMutableDictionary<NSString *, NSObject *> *values = [NSMutableDictionary dictionary]; + for (const webrtc::RTCStatsMemberInterface *member : statistics.Members()) { + NSObject *value = ValueFromStatsMember(member); + if (value) { + NSString *name = [NSString stringWithCString:member->name() encoding:NSUTF8StringEncoding]; + RTC_DCHECK(name.length > 0); + RTC_DCHECK(!values[name]); + values[name] = value; + } + } + _values = [values copy]; + } + + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"id = %@, type = %@, timestamp = %.0f, values = %@", + self.id, + self.type, + self.timestamp_us, + self.values]; +} + +@end + +@implementation RTC_OBJC_TYPE (RTCStatisticsReport) + +@synthesize timestamp_us = _timestamp_us; +@synthesize statistics = _statistics; + +- (NSString *)description { + return [NSString + stringWithFormat:@"timestamp = %.0f, statistics = %@", self.timestamp_us, self.statistics]; +} + +@end + +@implementation RTC_OBJC_TYPE (RTCStatisticsReport) (Private) + +- (instancetype)initWithReport : (const webrtc::RTCStatsReport &)report { + if (self = [super init]) { + _timestamp_us = report.timestamp().us(); + + NSMutableDictionary *statisticsById = + [NSMutableDictionary dictionaryWithCapacity:report.size()]; + for (const auto &stat : report) { + RTC_OBJC_TYPE(RTCStatistics) *statistics = + [[RTC_OBJC_TYPE(RTCStatistics) alloc] initWithStatistics:stat]; + statisticsById[statistics.id] = statistics; + } + _statistics = [statisticsById copy]; + } + + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCTracing.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCTracing.h new file mode 100644 index 0000000000..5c66e5a63a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCTracing.h @@ -0,0 +1,21 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +RTC_EXTERN void RTCSetupInternalTracer(void); +/** Starts capture to specified file. Must be a valid writable path. + * Returns YES if capture starts. + */ +RTC_EXTERN BOOL RTCStartInternalCapture(NSString* filePath); +RTC_EXTERN void RTCStopInternalCapture(void); +RTC_EXTERN void RTCShutdownInternalTracer(void); diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCTracing.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCTracing.mm new file mode 100644 index 0000000000..72f9f4da13 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCTracing.mm @@ -0,0 +1,29 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCTracing.h" + +#include "rtc_base/event_tracer.h" + +void RTCSetupInternalTracer(void) { + rtc::tracing::SetupInternalTracer(); +} + +BOOL RTCStartInternalCapture(NSString *filePath) { + return rtc::tracing::StartInternalCapture(filePath.UTF8String); +} + +void RTCStopInternalCapture(void) { + rtc::tracing::StopInternalCapture(); +} + +void RTCShutdownInternalTracer(void) { + rtc::tracing::ShutdownInternalTracer(); +} diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.h new file mode 100644 index 0000000000..5eff996c4f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "base/RTCVideoCodecInfo.h" + +#include "api/video_codecs/sdp_video_format.h" + +NS_ASSUME_NONNULL_BEGIN + +/* Interface for converting to/from internal C++ formats. */ +@interface RTC_OBJC_TYPE (RTCVideoCodecInfo) +(Private) + + - (instancetype)initWithNativeSdpVideoFormat : (webrtc::SdpVideoFormat)format; +- (webrtc::SdpVideoFormat)nativeSdpVideoFormat; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.mm new file mode 100644 index 0000000000..2eb8d366d2 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.mm @@ -0,0 +1,38 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoCodecInfo+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCVideoCodecInfo) +(Private) + + - (instancetype)initWithNativeSdpVideoFormat : (webrtc::SdpVideoFormat)format { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + for (auto it = format.parameters.begin(); it != format.parameters.end(); ++it) { + [params setObject:[NSString stringForStdString:it->second] + forKey:[NSString stringForStdString:it->first]]; + } + return [self initWithName:[NSString stringForStdString:format.name] parameters:params]; +} + +- (webrtc::SdpVideoFormat)nativeSdpVideoFormat { + std::map<std::string, std::string> parameters; + for (NSString *paramKey in self.parameters.allKeys) { + std::string key = [NSString stdStringForString:paramKey]; + std::string value = [NSString stdStringForString:self.parameters[paramKey]]; + parameters[key] = value; + } + + return webrtc::SdpVideoFormat([NSString stdStringForString:self.name], parameters); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.h new file mode 100644 index 0000000000..8323b18dc1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "base/RTCVideoEncoderSettings.h" + +#include "modules/video_coding/include/video_codec_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +/* Interfaces for converting to/from internal C++ formats. */ +@interface RTC_OBJC_TYPE (RTCVideoEncoderSettings) +(Private) + + - (instancetype)initWithNativeVideoCodec : (const webrtc::VideoCodec *__nullable)videoCodec; +- (webrtc::VideoCodec)nativeVideoCodec; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.mm new file mode 100644 index 0000000000..dec3a61090 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.mm @@ -0,0 +1,52 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoEncoderSettings+Private.h" + +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderSettings) +(Private) + + - (instancetype)initWithNativeVideoCodec : (const webrtc::VideoCodec *)videoCodec { + if (self = [super init]) { + if (videoCodec) { + const char *codecName = CodecTypeToPayloadString(videoCodec->codecType); + self.name = [NSString stringWithUTF8String:codecName]; + + self.width = videoCodec->width; + self.height = videoCodec->height; + self.startBitrate = videoCodec->startBitrate; + self.maxBitrate = videoCodec->maxBitrate; + self.minBitrate = videoCodec->minBitrate; + self.maxFramerate = videoCodec->maxFramerate; + self.qpMax = videoCodec->qpMax; + self.mode = (RTCVideoCodecMode)videoCodec->mode; + } + } + + return self; +} + +- (webrtc::VideoCodec)nativeVideoCodec { + webrtc::VideoCodec videoCodec; + videoCodec.width = self.width; + videoCodec.height = self.height; + videoCodec.startBitrate = self.startBitrate; + videoCodec.maxBitrate = self.maxBitrate; + videoCodec.minBitrate = self.minBitrate; + videoCodec.maxBitrate = self.maxBitrate; + videoCodec.qpMax = self.qpMax; + videoCodec.mode = (webrtc::VideoCodecMode)self.mode; + + return videoCodec; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource+Private.h new file mode 100644 index 0000000000..8e475dd21e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource+Private.h @@ -0,0 +1,51 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoSource.h" + +#import "RTCMediaSource+Private.h" + +#include "api/media_stream_interface.h" +#include "rtc_base/thread.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCVideoSource) +() + + /** + * The VideoTrackSourceInterface object passed to this RTCVideoSource during + * construction. + */ + @property(nonatomic, + readonly) rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> nativeVideoSource; + +/** Initialize an RTCVideoSource from a native VideoTrackSourceInterface. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeVideoSource: + (rtc::scoped_refptr<webrtc::VideoTrackSourceInterface>)nativeVideoSource + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaSource:(rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + type:(RTCMediaSourceType)type NS_UNAVAILABLE; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + signalingThread:(rtc::Thread *)signalingThread + workerThread:(rtc::Thread *)workerThread; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + signalingThread:(rtc::Thread *)signalingThread + workerThread:(rtc::Thread *)workerThread + isScreenCast:(BOOL)isScreenCast; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource.h new file mode 100644 index 0000000000..cdef8b89a1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCMediaSource.h" +#import "RTCVideoCapturer.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT + +@interface RTC_OBJC_TYPE (RTCVideoSource) : RTC_OBJC_TYPE(RTCMediaSource) <RTC_OBJC_TYPE(RTCVideoCapturerDelegate)> + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Calling this function will cause frames to be scaled down to the + * requested resolution. Also, frames will be cropped to match the + * requested aspect ratio, and frames will be dropped to match the + * requested fps. The requested aspect ratio is orientation agnostic and + * will be adjusted to maintain the input orientation, so it doesn't + * matter if e.g. 1280x720 or 720x1280 is requested. + */ +- (void)adaptOutputFormatToWidth:(int)width height:(int)height fps:(int)fps; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource.mm new file mode 100644 index 0000000000..486ca93771 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoSource.mm @@ -0,0 +1,92 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoSource+Private.h" + +#include "pc/video_track_source_proxy.h" +#include "rtc_base/checks.h" +#include "sdk/objc/native/src/objc_video_track_source.h" + +static webrtc::ObjCVideoTrackSource *getObjCVideoSource( + const rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> nativeSource) { + webrtc::VideoTrackSourceProxy *proxy_source = + static_cast<webrtc::VideoTrackSourceProxy *>(nativeSource.get()); + return static_cast<webrtc::ObjCVideoTrackSource *>(proxy_source->internal()); +} + +// TODO(magjed): Refactor this class and target ObjCVideoTrackSource only once +// RTCAVFoundationVideoSource is gone. See http://crbug/webrtc/7177 for more +// info. +@implementation RTC_OBJC_TYPE (RTCVideoSource) { + rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> _nativeVideoSource; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeVideoSource: + (rtc::scoped_refptr<webrtc::VideoTrackSourceInterface>)nativeVideoSource { + RTC_DCHECK(factory); + RTC_DCHECK(nativeVideoSource); + if (self = [super initWithFactory:factory + nativeMediaSource:nativeVideoSource + type:RTCMediaSourceTypeVideo]) { + _nativeVideoSource = nativeVideoSource; + } + return self; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeMediaSource:(rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + type:(RTCMediaSourceType)type { + RTC_DCHECK_NOTREACHED(); + return nil; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + signalingThread:(rtc::Thread *)signalingThread + workerThread:(rtc::Thread *)workerThread { + return [self initWithFactory:factory + signalingThread:signalingThread + workerThread:workerThread + isScreenCast:NO]; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + signalingThread:(rtc::Thread *)signalingThread + workerThread:(rtc::Thread *)workerThread + isScreenCast:(BOOL)isScreenCast { + rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> objCVideoTrackSource = + rtc::make_ref_counted<webrtc::ObjCVideoTrackSource>(isScreenCast); + + return [self initWithFactory:factory + nativeVideoSource:webrtc::VideoTrackSourceProxy::Create( + signalingThread, workerThread, objCVideoTrackSource)]; +} + +- (NSString *)description { + NSString *stateString = [[self class] stringForState:self.state]; + return [NSString stringWithFormat:@"RTC_OBJC_TYPE(RTCVideoSource)( %p ): %@", self, stateString]; +} + +- (void)capturer:(RTC_OBJC_TYPE(RTCVideoCapturer) *)capturer + didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + getObjCVideoSource(_nativeVideoSource)->OnCapturedFrame(frame); +} + +- (void)adaptOutputFormatToWidth:(int)width height:(int)height fps:(int)fps { + getObjCVideoSource(_nativeVideoSource)->OnOutputFormatRequest(width, height, fps); +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::VideoTrackSourceInterface>)nativeVideoSource { + return _nativeVideoSource; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack+Private.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack+Private.h new file mode 100644 index 0000000000..f1a8d7e4ed --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack+Private.h @@ -0,0 +1,30 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoTrack.h" + +#include "api/media_stream_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCVideoTrack) +() + + /** VideoTrackInterface created or passed in at construction. */ + @property(nonatomic, readonly) rtc::scoped_refptr<webrtc::VideoTrackInterface> nativeVideoTrack; + +/** Initialize an RTCVideoTrack with its source and an id. */ +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + source:(RTC_OBJC_TYPE(RTCVideoSource) *)source + trackId:(NSString *)trackId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack.h b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack.h new file mode 100644 index 0000000000..5382b7169f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack.h @@ -0,0 +1,38 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMediaStreamTrack.h" + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol RTC_OBJC_TYPE +(RTCVideoRenderer); +@class RTC_OBJC_TYPE(RTCPeerConnectionFactory); +@class RTC_OBJC_TYPE(RTCVideoSource); + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoTrack) : RTC_OBJC_TYPE(RTCMediaStreamTrack) + +/** The video source for this video track. */ +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCVideoSource) *source; + +- (instancetype)init NS_UNAVAILABLE; + +/** Register a renderer that will render all frames received on this track. */ +- (void)addRenderer:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)renderer; + +/** Deregister a renderer. */ +- (void)removeRenderer:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)renderer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack.mm b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack.mm new file mode 100644 index 0000000000..d4862e3748 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/peerconnection/RTCVideoTrack.mm @@ -0,0 +1,125 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoTrack+Private.h" + +#import "RTCMediaStreamTrack+Private.h" +#import "RTCPeerConnectionFactory+Private.h" +#import "RTCVideoSource+Private.h" +#import "api/RTCVideoRendererAdapter+Private.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCVideoTrack) { + rtc::Thread *_workerThread; + NSMutableArray *_adapters /* accessed on _workerThread */; +} + +@synthesize source = _source; + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + source:(RTC_OBJC_TYPE(RTCVideoSource) *)source + trackId:(NSString *)trackId { + NSParameterAssert(factory); + NSParameterAssert(source); + NSParameterAssert(trackId.length); + std::string nativeId = [NSString stdStringForString:trackId]; + rtc::scoped_refptr<webrtc::VideoTrackInterface> track = + factory.nativeFactory->CreateVideoTrack(nativeId, source.nativeVideoSource.get()); + if (self = [self initWithFactory:factory nativeTrack:track type:RTCMediaStreamTrackTypeVideo]) { + _source = source; + } + return self; +} + +- (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory + nativeTrack: + (rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeMediaTrack + type:(RTCMediaStreamTrackType)type { + NSParameterAssert(factory); + NSParameterAssert(nativeMediaTrack); + NSParameterAssert(type == RTCMediaStreamTrackTypeVideo); + if (self = [super initWithFactory:factory nativeTrack:nativeMediaTrack type:type]) { + _adapters = [NSMutableArray array]; + _workerThread = factory.workerThread; + } + return self; +} + +- (void)dealloc { + for (RTCVideoRendererAdapter *adapter in _adapters) { + self.nativeVideoTrack->RemoveSink(adapter.nativeVideoRenderer); + } +} + +- (RTC_OBJC_TYPE(RTCVideoSource) *)source { + if (!_source) { + rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> source( + self.nativeVideoTrack->GetSource()); + if (source) { + _source = [[RTC_OBJC_TYPE(RTCVideoSource) alloc] initWithFactory:self.factory + nativeVideoSource:source]; + } + } + return _source; +} + +- (void)addRenderer:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)renderer { + if (!_workerThread->IsCurrent()) { + _workerThread->BlockingCall([renderer, self] { [self addRenderer:renderer]; }); + return; + } + + // Make sure we don't have this renderer yet. + for (RTCVideoRendererAdapter *adapter in _adapters) { + if (adapter.videoRenderer == renderer) { + RTC_LOG(LS_INFO) << "|renderer| is already attached to this track"; + return; + } + } + // Create a wrapper that provides a native pointer for us. + RTCVideoRendererAdapter* adapter = + [[RTCVideoRendererAdapter alloc] initWithNativeRenderer:renderer]; + [_adapters addObject:adapter]; + self.nativeVideoTrack->AddOrUpdateSink(adapter.nativeVideoRenderer, + rtc::VideoSinkWants()); +} + +- (void)removeRenderer:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)renderer { + if (!_workerThread->IsCurrent()) { + _workerThread->BlockingCall([renderer, self] { [self removeRenderer:renderer]; }); + return; + } + __block NSUInteger indexToRemove = NSNotFound; + [_adapters enumerateObjectsUsingBlock:^(RTCVideoRendererAdapter *adapter, + NSUInteger idx, + BOOL *stop) { + if (adapter.videoRenderer == renderer) { + indexToRemove = idx; + *stop = YES; + } + }]; + if (indexToRemove == NSNotFound) { + RTC_LOG(LS_INFO) << "removeRenderer called with a renderer that has not been previously added"; + return; + } + RTCVideoRendererAdapter *adapterToRemove = + [_adapters objectAtIndex:indexToRemove]; + self.nativeVideoTrack->RemoveSink(adapterToRemove.nativeVideoRenderer); + [_adapters removeObjectAtIndex:indexToRemove]; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::VideoTrackInterface>)nativeVideoTrack { + return rtc::scoped_refptr<webrtc::VideoTrackInterface>( + static_cast<webrtc::VideoTrackInterface *>(self.nativeTrack.get())); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoCodecConstants.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoCodecConstants.h new file mode 100644 index 0000000000..8b17a75aef --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoCodecConstants.h @@ -0,0 +1,17 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +RTC_EXTERN NSString* const kRTCVideoCodecVp8Name; +RTC_EXTERN NSString* const kRTCVideoCodecVp9Name; +RTC_EXTERN NSString* const kRTCVideoCodecAv1Name; diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoCodecConstants.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoCodecConstants.mm new file mode 100644 index 0000000000..1ab236a2c2 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoCodecConstants.mm @@ -0,0 +1,18 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import "RTCVideoCodecConstants.h" + +#include "media/base/media_constants.h" + +NSString *const kRTCVideoCodecVp8Name = @(cricket::kVp8CodecName); +NSString *const kRTCVideoCodecVp9Name = @(cricket::kVp9CodecName); +NSString *const kRTCVideoCodecAv1Name = @(cricket::kAv1CodecName); diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderAV1.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderAV1.h new file mode 100644 index 0000000000..3f6a689564 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderAV1.h @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoDecoderAV1) : NSObject + +/* This returns a AV1 decoder that can be returned from a RTCVideoDecoderFactory injected into + * RTCPeerConnectionFactory. Even though it implements the RTCVideoDecoder protocol, it can not be + * used independently from the RTCPeerConnectionFactory. + */ ++ (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)av1Decoder; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderAV1.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderAV1.mm new file mode 100644 index 0000000000..81f5f93eec --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderAV1.mm @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoderAV1.h" +#import "RTCWrappedNativeVideoDecoder.h" + +#include "modules/video_coding/codecs/av1/dav1d_decoder.h" + +@implementation RTC_OBJC_TYPE (RTCVideoDecoderAV1) + ++ (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)av1Decoder { + return [[RTC_OBJC_TYPE(RTCWrappedNativeVideoDecoder) alloc] + initWithNativeDecoder:std::unique_ptr<webrtc::VideoDecoder>(webrtc::CreateDav1dDecoder())]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP8.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP8.h new file mode 100644 index 0000000000..a118b25ed7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP8.h @@ -0,0 +1,25 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoDecoderVP8) : NSObject + +/* This returns a VP8 decoder that can be returned from a RTCVideoDecoderFactory injected into + * RTCPeerConnectionFactory. Even though it implements the RTCVideoDecoder protocol, it can not be + * used independently from the RTCPeerConnectionFactory. + */ ++ (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)vp8Decoder; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP8.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP8.mm new file mode 100644 index 0000000000..c150cf6d3a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP8.mm @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoderVP8.h" +#import "RTCWrappedNativeVideoDecoder.h" + +#include "modules/video_coding/codecs/vp8/include/vp8.h" + +@implementation RTC_OBJC_TYPE (RTCVideoDecoderVP8) + ++ (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)vp8Decoder { + return [[RTC_OBJC_TYPE(RTCWrappedNativeVideoDecoder) alloc] + initWithNativeDecoder:std::unique_ptr<webrtc::VideoDecoder>(webrtc::VP8Decoder::Create())]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP9.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP9.h new file mode 100644 index 0000000000..de7e62012b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP9.h @@ -0,0 +1,27 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoDecoderVP9) : NSObject + +/* This returns a VP9 decoder that can be returned from a RTCVideoDecoderFactory injected into + * RTCPeerConnectionFactory. Even though it implements the RTCVideoDecoder protocol, it can not be + * used independently from the RTCPeerConnectionFactory. + */ ++ (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)vp9Decoder; + ++ (bool)isSupported; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP9.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP9.mm new file mode 100644 index 0000000000..05446d436d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoDecoderVP9.mm @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoderVP9.h" +#import "RTCWrappedNativeVideoDecoder.h" + +#include "modules/video_coding/codecs/vp9/include/vp9.h" + +@implementation RTC_OBJC_TYPE (RTCVideoDecoderVP9) + ++ (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)vp9Decoder { + std::unique_ptr<webrtc::VideoDecoder> nativeDecoder(webrtc::VP9Decoder::Create()); + if (nativeDecoder == nullptr) { + return nil; + } + return [[RTC_OBJC_TYPE(RTCWrappedNativeVideoDecoder) alloc] + initWithNativeDecoder:std::move(nativeDecoder)]; +} + ++ (bool)isSupported { +#if defined(RTC_ENABLE_VP9) + return true; +#else + return false; +#endif +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderAV1.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderAV1.h new file mode 100644 index 0000000000..8aa55e4bfa --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderAV1.h @@ -0,0 +1,27 @@ +/* + * Copyright 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderAV1) : NSObject + +/* This returns a AV1 encoder that can be returned from a RTCVideoEncoderFactory injected into + * RTCPeerConnectionFactory. Even though it implements the RTCVideoEncoder protocol, it can not be + * used independently from the RTCPeerConnectionFactory. + */ ++ (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)av1Encoder; + ++ (bool)isSupported; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderAV1.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderAV1.mm new file mode 100644 index 0000000000..d2fe65293b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderAV1.mm @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoderAV1.h" +#import "RTCWrappedNativeVideoEncoder.h" +#include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderAV1) + ++ (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)av1Encoder { + std::unique_ptr<webrtc::VideoEncoder> nativeEncoder(webrtc::CreateLibaomAv1Encoder()); + return [[RTC_OBJC_TYPE(RTCWrappedNativeVideoEncoder) alloc] + initWithNativeEncoder:std::move(nativeEncoder)]; +} + ++ (bool)isSupported { + return true; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP8.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP8.h new file mode 100644 index 0000000000..e136a5bda8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP8.h @@ -0,0 +1,25 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderVP8) : NSObject + +/* This returns a VP8 encoder that can be returned from a RTCVideoEncoderFactory injected into + * RTCPeerConnectionFactory. Even though it implements the RTCVideoEncoder protocol, it can not be + * used independently from the RTCPeerConnectionFactory. + */ ++ (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)vp8Encoder; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP8.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP8.mm new file mode 100644 index 0000000000..d72f705813 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP8.mm @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoderVP8.h" +#import "RTCWrappedNativeVideoEncoder.h" + +#include "modules/video_coding/codecs/vp8/include/vp8.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderVP8) + ++ (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)vp8Encoder { + return [[RTC_OBJC_TYPE(RTCWrappedNativeVideoEncoder) alloc] + initWithNativeEncoder:std::unique_ptr<webrtc::VideoEncoder>(webrtc::VP8Encoder::Create())]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP9.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP9.h new file mode 100644 index 0000000000..f7dac6117d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP9.h @@ -0,0 +1,27 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderVP9) : NSObject + +/* This returns a VP9 encoder that can be returned from a RTCVideoEncoderFactory injected into + * RTCPeerConnectionFactory. Even though it implements the RTCVideoEncoder protocol, it can not be + * used independently from the RTCPeerConnectionFactory. + */ ++ (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)vp9Encoder; + ++ (bool)isSupported; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP9.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP9.mm new file mode 100644 index 0000000000..18a9353f7e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCVideoEncoderVP9.mm @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoderVP9.h" +#import "RTCWrappedNativeVideoEncoder.h" + +#include "modules/video_coding/codecs/vp9/include/vp9.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderVP9) + ++ (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)vp9Encoder { + std::unique_ptr<webrtc::VideoEncoder> nativeEncoder(webrtc::VP9Encoder::Create()); + if (nativeEncoder == nullptr) { + return nil; + } + return [[RTC_OBJC_TYPE(RTCWrappedNativeVideoEncoder) alloc] + initWithNativeEncoder:std::move(nativeEncoder)]; +} + ++ (bool)isSupported { +#if defined(RTC_ENABLE_VP9) + return true; +#else + return false; +#endif +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.h new file mode 100644 index 0000000000..3a9b39e959 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "base/RTCMacros.h" +#import "base/RTCVideoDecoder.h" + +#include "api/video_codecs/video_decoder.h" +#include "media/base/codec.h" + +@interface RTC_OBJC_TYPE (RTCWrappedNativeVideoDecoder) : NSObject <RTC_OBJC_TYPE (RTCVideoDecoder)> + +- (instancetype)initWithNativeDecoder:(std::unique_ptr<webrtc::VideoDecoder>)decoder; + +/* This moves the ownership of the wrapped decoder to the caller. */ +- (std::unique_ptr<webrtc::VideoDecoder>)releaseWrappedDecoder; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.mm new file mode 100644 index 0000000000..261874d20b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.mm @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCWrappedNativeVideoDecoder.h" +#import "base/RTCMacros.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCWrappedNativeVideoDecoder) { + std::unique_ptr<webrtc::VideoDecoder> _wrappedDecoder; +} + +- (instancetype)initWithNativeDecoder:(std::unique_ptr<webrtc::VideoDecoder>)decoder { + if (self = [super init]) { + _wrappedDecoder = std::move(decoder); + } + + return self; +} + +- (std::unique_ptr<webrtc::VideoDecoder>)releaseWrappedDecoder { + return std::move(_wrappedDecoder); +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoDecoder) + +- (void)setCallback:(RTCVideoDecoderCallback)callback { + RTC_DCHECK_NOTREACHED(); +} + +- (NSInteger)startDecodeWithNumberOfCores:(int)numberOfCores { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (NSInteger)releaseDecoder { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (NSInteger)decode:(RTC_OBJC_TYPE(RTCEncodedImage) *)encodedImage + missingFrames:(BOOL)missingFrames + codecSpecificInfo:(nullable id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)info + renderTimeMs:(int64_t)renderTimeMs { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (NSString *)implementationName { + RTC_DCHECK_NOTREACHED(); + return nil; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.h b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.h new file mode 100644 index 0000000000..8df9ceec35 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "base/RTCMacros.h" +#import "base/RTCVideoEncoder.h" + +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" +#include "media/base/codec.h" + +@interface RTC_OBJC_TYPE (RTCWrappedNativeVideoEncoder) : NSObject <RTC_OBJC_TYPE (RTCVideoEncoder)> + +- (instancetype)initWithNativeEncoder:(std::unique_ptr<webrtc::VideoEncoder>)encoder; + +/* This moves the ownership of the wrapped encoder to the caller. */ +- (std::unique_ptr<webrtc::VideoEncoder>)releaseWrappedEncoder; + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.mm b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.mm new file mode 100644 index 0000000000..4160572814 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.mm @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCWrappedNativeVideoEncoder.h" +#import "base/RTCMacros.h" +#import "helpers/NSString+StdString.h" + +@implementation RTC_OBJC_TYPE (RTCWrappedNativeVideoEncoder) { + std::unique_ptr<webrtc::VideoEncoder> _wrappedEncoder; +} + +- (instancetype)initWithNativeEncoder:(std::unique_ptr<webrtc::VideoEncoder>)encoder { + if (self = [super init]) { + _wrappedEncoder = std::move(encoder); + } + + return self; +} + +- (std::unique_ptr<webrtc::VideoEncoder>)releaseWrappedEncoder { + return std::move(_wrappedEncoder); +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoEncoder) + +- (void)setCallback:(RTCVideoEncoderCallback)callback { + RTC_DCHECK_NOTREACHED(); +} + +- (NSInteger)startEncodeWithSettings:(RTC_OBJC_TYPE(RTCVideoEncoderSettings) *)settings + numberOfCores:(int)numberOfCores { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (NSInteger)releaseEncoder { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame + codecSpecificInfo:(nullable id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)info + frameTypes:(NSArray<NSNumber *> *)frameTypes { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (int)setBitrate:(uint32_t)bitrateKbit framerate:(uint32_t)framerate { + RTC_DCHECK_NOTREACHED(); + return 0; +} + +- (NSString *)implementationName { + RTC_DCHECK_NOTREACHED(); + return nil; +} + +- (nullable RTC_OBJC_TYPE(RTCVideoEncoderQpThresholds) *)scalingSettings { + RTC_DCHECK_NOTREACHED(); + return nil; +} + +- (NSInteger)resolutionAlignment { + RTC_DCHECK_NOTREACHED(); + return 1; +} + +- (BOOL)applyAlignmentToAllSimulcastLayers { + RTC_DCHECK_NOTREACHED(); + return NO; +} + +- (BOOL)supportsNativeHandle { + RTC_DCHECK_NOTREACHED(); + return NO; +} +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer+Private.h b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer+Private.h new file mode 100644 index 0000000000..20dc807991 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNativeI420Buffer.h" + +#include "api/video/i420_buffer.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTC_OBJC_TYPE (RTCI420Buffer) +() { + @protected + rtc::scoped_refptr<webrtc::I420BufferInterface> _i420Buffer; +} + +/** Initialize an RTCI420Buffer with its backing I420BufferInterface. */ +- (instancetype)initWithFrameBuffer:(rtc::scoped_refptr<webrtc::I420BufferInterface>)i420Buffer; +- (rtc::scoped_refptr<webrtc::I420BufferInterface>)nativeI420Buffer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer.h b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer.h new file mode 100644 index 0000000000..3afe2090a2 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer.h @@ -0,0 +1,23 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCI420Buffer.h" +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** RTCI420Buffer implements the RTCI420Buffer protocol */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCI420Buffer) : NSObject<RTC_OBJC_TYPE(RTCI420Buffer)> +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer.mm b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer.mm new file mode 100644 index 0000000000..7aafd98f43 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer.mm @@ -0,0 +1,154 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNativeI420Buffer+Private.h" + +#include "api/video/i420_buffer.h" + +#if !defined(NDEBUG) && defined(WEBRTC_IOS) +#import <UIKit/UIKit.h> +#include "third_party/libyuv/include/libyuv.h" +#endif + +@implementation RTC_OBJC_TYPE (RTCI420Buffer) + +- (instancetype)initWithWidth:(int)width height:(int)height { + if (self = [super init]) { + _i420Buffer = webrtc::I420Buffer::Create(width, height); + } + + return self; +} + +- (instancetype)initWithWidth:(int)width + height:(int)height + dataY:(const uint8_t *)dataY + dataU:(const uint8_t *)dataU + dataV:(const uint8_t *)dataV { + if (self = [super init]) { + _i420Buffer = webrtc::I420Buffer::Copy( + width, height, dataY, width, dataU, (width + 1) / 2, dataV, (width + 1) / 2); + } + return self; +} + +- (instancetype)initWithWidth:(int)width + height:(int)height + strideY:(int)strideY + strideU:(int)strideU + strideV:(int)strideV { + if (self = [super init]) { + _i420Buffer = webrtc::I420Buffer::Create(width, height, strideY, strideU, strideV); + } + + return self; +} + +- (instancetype)initWithFrameBuffer:(rtc::scoped_refptr<webrtc::I420BufferInterface>)i420Buffer { + if (self = [super init]) { + _i420Buffer = i420Buffer; + } + + return self; +} + +- (int)width { + return _i420Buffer->width(); +} + +- (int)height { + return _i420Buffer->height(); +} + +- (int)strideY { + return _i420Buffer->StrideY(); +} + +- (int)strideU { + return _i420Buffer->StrideU(); +} + +- (int)strideV { + return _i420Buffer->StrideV(); +} + +- (int)chromaWidth { + return _i420Buffer->ChromaWidth(); +} + +- (int)chromaHeight { + return _i420Buffer->ChromaHeight(); +} + +- (const uint8_t *)dataY { + return _i420Buffer->DataY(); +} + +- (const uint8_t *)dataU { + return _i420Buffer->DataU(); +} + +- (const uint8_t *)dataV { + return _i420Buffer->DataV(); +} + +- (id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)>)cropAndScaleWith:(int)offsetX + offsetY:(int)offsetY + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + scaleWidth:(int)scaleWidth + scaleHeight:(int)scaleHeight { + rtc::scoped_refptr<webrtc::VideoFrameBuffer> scaled_buffer = + _i420Buffer->CropAndScale(offsetX, offsetY, cropWidth, cropHeight, scaleWidth, scaleHeight); + RTC_DCHECK_EQ(scaled_buffer->type(), webrtc::VideoFrameBuffer::Type::kI420); + // Calling ToI420() doesn't do any conversions. + rtc::scoped_refptr<webrtc::I420BufferInterface> buffer = scaled_buffer->ToI420(); + RTC_OBJC_TYPE(RTCI420Buffer) *result = + [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:buffer]; + return result; +} + +- (id<RTC_OBJC_TYPE(RTCI420Buffer)>)toI420 { + return self; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::I420BufferInterface>)nativeI420Buffer { + return _i420Buffer; +} + +#pragma mark - Debugging + +#if !defined(NDEBUG) && defined(WEBRTC_IOS) +- (id)debugQuickLookObject { + UIGraphicsBeginImageContext(CGSizeMake(_i420Buffer->width(), _i420Buffer->height())); + CGContextRef c = UIGraphicsGetCurrentContext(); + uint8_t *ctxData = (uint8_t *)CGBitmapContextGetData(c); + + libyuv::I420ToARGB(_i420Buffer->DataY(), + _i420Buffer->StrideY(), + _i420Buffer->DataU(), + _i420Buffer->StrideU(), + _i420Buffer->DataV(), + _i420Buffer->StrideV(), + ctxData, + CGBitmapContextGetBytesPerRow(c), + CGBitmapContextGetWidth(c), + CGBitmapContextGetHeight(c)); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} +#endif + +@end diff --git a/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeMutableI420Buffer.h b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeMutableI420Buffer.h new file mode 100644 index 0000000000..053a10a304 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeMutableI420Buffer.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCMacros.h" +#import "RTCMutableI420Buffer.h" +#import "RTCNativeI420Buffer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Mutable version of RTCI420Buffer */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMutableI420Buffer) : RTC_OBJC_TYPE(RTCI420Buffer)<RTC_OBJC_TYPE(RTCMutableI420Buffer)> +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeMutableI420Buffer.mm b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeMutableI420Buffer.mm new file mode 100644 index 0000000000..1e669bcb9c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/api/video_frame_buffer/RTCNativeMutableI420Buffer.mm @@ -0,0 +1,31 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNativeMutableI420Buffer.h" + +#import "RTCNativeI420Buffer+Private.h" + +#include "api/video/i420_buffer.h" + +@implementation RTC_OBJC_TYPE (RTCMutableI420Buffer) + +- (uint8_t *)mutableDataY { + return static_cast<webrtc::I420Buffer *>(_i420Buffer.get())->MutableDataY(); +} + +- (uint8_t *)mutableDataU { + return static_cast<webrtc::I420Buffer *>(_i420Buffer.get())->MutableDataU(); +} + +- (uint8_t *)mutableDataV { + return static_cast<webrtc::I420Buffer *>(_i420Buffer.get())->MutableDataV(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCCodecSpecificInfo.h b/third_party/libwebrtc/sdk/objc/base/RTCCodecSpecificInfo.h new file mode 100644 index 0000000000..5e7800e524 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCCodecSpecificInfo.h @@ -0,0 +1,24 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Implement this protocol to pass codec specific info from the encoder. + * Corresponds to webrtc::CodecSpecificInfo. + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCCodecSpecificInfo)<NSObject> @end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCEncodedImage.h b/third_party/libwebrtc/sdk/objc/base/RTCEncodedImage.h new file mode 100644 index 0000000000..28529e5906 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCEncodedImage.h @@ -0,0 +1,52 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoFrame.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Represents an encoded frame's type. */ +typedef NS_ENUM(NSUInteger, RTCFrameType) { + RTCFrameTypeEmptyFrame = 0, + RTCFrameTypeAudioFrameSpeech = 1, + RTCFrameTypeAudioFrameCN = 2, + RTCFrameTypeVideoFrameKey = 3, + RTCFrameTypeVideoFrameDelta = 4, +}; + +typedef NS_ENUM(NSUInteger, RTCVideoContentType) { + RTCVideoContentTypeUnspecified, + RTCVideoContentTypeScreenshare, +}; + +/** Represents an encoded frame. Corresponds to webrtc::EncodedImage. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCEncodedImage) : NSObject + +@property(nonatomic, strong) NSData *buffer; +@property(nonatomic, assign) int32_t encodedWidth; +@property(nonatomic, assign) int32_t encodedHeight; +@property(nonatomic, assign) uint32_t timeStamp; +@property(nonatomic, assign) int64_t captureTimeMs; +@property(nonatomic, assign) int64_t ntpTimeMs; +@property(nonatomic, assign) uint8_t flags; +@property(nonatomic, assign) int64_t encodeStartMs; +@property(nonatomic, assign) int64_t encodeFinishMs; +@property(nonatomic, assign) RTCFrameType frameType; +@property(nonatomic, assign) RTCVideoRotation rotation; +@property(nonatomic, strong) NSNumber *qp; +@property(nonatomic, assign) RTCVideoContentType contentType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCEncodedImage.m b/third_party/libwebrtc/sdk/objc/base/RTCEncodedImage.m new file mode 100644 index 0000000000..ad8441aabd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCEncodedImage.m @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCEncodedImage.h" + +@implementation RTC_OBJC_TYPE (RTCEncodedImage) + +@synthesize buffer = _buffer; +@synthesize encodedWidth = _encodedWidth; +@synthesize encodedHeight = _encodedHeight; +@synthesize timeStamp = _timeStamp; +@synthesize captureTimeMs = _captureTimeMs; +@synthesize ntpTimeMs = _ntpTimeMs; +@synthesize flags = _flags; +@synthesize encodeStartMs = _encodeStartMs; +@synthesize encodeFinishMs = _encodeFinishMs; +@synthesize frameType = _frameType; +@synthesize rotation = _rotation; +@synthesize qp = _qp; +@synthesize contentType = _contentType; + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCI420Buffer.h b/third_party/libwebrtc/sdk/objc/base/RTCI420Buffer.h new file mode 100644 index 0000000000..b97f05a5ba --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCI420Buffer.h @@ -0,0 +1,22 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCYUVPlanarBuffer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Protocol for RTCYUVPlanarBuffers containing I420 data */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCI420Buffer)<RTC_OBJC_TYPE(RTCYUVPlanarBuffer)> @end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCLogging.h b/third_party/libwebrtc/sdk/objc/base/RTCLogging.h new file mode 100644 index 0000000000..754945c8f2 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCLogging.h @@ -0,0 +1,67 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +// Subset of rtc::LoggingSeverity. +typedef NS_ENUM(NSInteger, RTCLoggingSeverity) { + RTCLoggingSeverityVerbose, + RTCLoggingSeverityInfo, + RTCLoggingSeverityWarning, + RTCLoggingSeverityError, + RTCLoggingSeverityNone, +}; + +// Wrapper for C++ RTC_LOG(sev) macros. +// Logs the log string to the webrtc logstream for the given severity. +RTC_EXTERN void RTCLogEx(RTCLoggingSeverity severity, NSString* log_string); + +// Wrapper for rtc::LogMessage::LogToDebug. +// Sets the minimum severity to be logged to console. +RTC_EXTERN void RTCSetMinDebugLogLevel(RTCLoggingSeverity severity); + +// Returns the filename with the path prefix removed. +RTC_EXTERN NSString* RTCFileName(const char* filePath); + +// Some convenience macros. + +#define RTCLogString(format, ...) \ + [NSString stringWithFormat:@"(%@:%d %s): " format, RTCFileName(__FILE__), \ + __LINE__, __FUNCTION__, ##__VA_ARGS__] + +#define RTCLogFormat(severity, format, ...) \ + do { \ + NSString* log_string = RTCLogString(format, ##__VA_ARGS__); \ + RTCLogEx(severity, log_string); \ + } while (false) + +#define RTCLogVerbose(format, ...) \ + RTCLogFormat(RTCLoggingSeverityVerbose, format, ##__VA_ARGS__) + +#define RTCLogInfo(format, ...) \ + RTCLogFormat(RTCLoggingSeverityInfo, format, ##__VA_ARGS__) + +#define RTCLogWarning(format, ...) \ + RTCLogFormat(RTCLoggingSeverityWarning, format, ##__VA_ARGS__) + +#define RTCLogError(format, ...) \ + RTCLogFormat(RTCLoggingSeverityError, format, ##__VA_ARGS__) + +#if !defined(NDEBUG) +#define RTCLogDebug(format, ...) RTCLogInfo(format, ##__VA_ARGS__) +#else +#define RTCLogDebug(format, ...) \ + do { \ + } while (false) +#endif + +#define RTCLog(format, ...) RTCLogInfo(format, ##__VA_ARGS__) diff --git a/third_party/libwebrtc/sdk/objc/base/RTCLogging.mm b/third_party/libwebrtc/sdk/objc/base/RTCLogging.mm new file mode 100644 index 0000000000..e8dae02efb --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCLogging.mm @@ -0,0 +1,48 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCLogging.h" + +#include "rtc_base/logging.h" + +rtc::LoggingSeverity RTCGetNativeLoggingSeverity(RTCLoggingSeverity severity) { + switch (severity) { + case RTCLoggingSeverityVerbose: + return rtc::LS_VERBOSE; + case RTCLoggingSeverityInfo: + return rtc::LS_INFO; + case RTCLoggingSeverityWarning: + return rtc::LS_WARNING; + case RTCLoggingSeverityError: + return rtc::LS_ERROR; + case RTCLoggingSeverityNone: + return rtc::LS_NONE; + } +} + +void RTCLogEx(RTCLoggingSeverity severity, NSString* log_string) { + if (log_string.length) { + const char* utf8_string = log_string.UTF8String; + RTC_LOG_V(RTCGetNativeLoggingSeverity(severity)) << utf8_string; + } +} + +void RTCSetMinDebugLogLevel(RTCLoggingSeverity severity) { + rtc::LogMessage::LogToDebug(RTCGetNativeLoggingSeverity(severity)); +} + +NSString* RTCFileName(const char* file_path) { + NSString* ns_file_path = + [[NSString alloc] initWithBytesNoCopy:const_cast<char*>(file_path) + length:strlen(file_path) + encoding:NSUTF8StringEncoding + freeWhenDone:NO]; + return ns_file_path.lastPathComponent; +} diff --git a/third_party/libwebrtc/sdk/objc/base/RTCMacros.h b/third_party/libwebrtc/sdk/objc/base/RTCMacros.h new file mode 100644 index 0000000000..469e3c93bd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCMacros.h @@ -0,0 +1,62 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_BASE_RTCMACROS_H_ +#define SDK_OBJC_BASE_RTCMACROS_H_ + +#ifdef WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT + +#if defined(WEBRTC_LIBRARY_IMPL) +#define RTC_OBJC_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT + +#ifndef RTC_OBJC_EXPORT +#define RTC_OBJC_EXPORT +#endif + +// Internal macros used to correctly concatenate symbols. +#define RTC_SYMBOL_CONCAT_HELPER(a, b) a##b +#define RTC_SYMBOL_CONCAT(a, b) RTC_SYMBOL_CONCAT_HELPER(a, b) + +// RTC_OBJC_TYPE_PREFIX +// +// Macro used to prepend a prefix to the API types that are exported with +// RTC_OBJC_EXPORT. +// +// Clients can patch the definition of this macro locally and build +// WebRTC.framework with their own prefix in case symbol clashing is a +// problem. +// +// This macro must only be defined here and not on via compiler flag to +// ensure it has a unique value. +#define RTC_OBJC_TYPE_PREFIX + +// RCT_OBJC_TYPE +// +// Macro used internally to declare API types. Declaring an API type without +// using this macro will not include the declared type in the set of types +// that will be affected by the configurable RTC_OBJC_TYPE_PREFIX. +#define RTC_OBJC_TYPE(type_name) RTC_SYMBOL_CONCAT(RTC_OBJC_TYPE_PREFIX, type_name) + +#if defined(__cplusplus) +#define RTC_EXTERN extern "C" RTC_OBJC_EXPORT +#else +#define RTC_EXTERN extern RTC_OBJC_EXPORT +#endif + +#ifdef __OBJC__ +#define RTC_FWD_DECL_OBJC_CLASS(classname) @class classname +#else +#define RTC_FWD_DECL_OBJC_CLASS(classname) typedef struct objc_object classname +#endif + +#endif // SDK_OBJC_BASE_RTCMACROS_H_ diff --git a/third_party/libwebrtc/sdk/objc/base/RTCMutableI420Buffer.h b/third_party/libwebrtc/sdk/objc/base/RTCMutableI420Buffer.h new file mode 100644 index 0000000000..cde721980b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCMutableI420Buffer.h @@ -0,0 +1,23 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCI420Buffer.h" +#import "RTCMutableYUVPlanarBuffer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Extension of the I420 buffer with mutable data access */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCMutableI420Buffer)<RTC_OBJC_TYPE(RTCI420Buffer), RTC_OBJC_TYPE(RTCMutableYUVPlanarBuffer)> @end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCMutableYUVPlanarBuffer.h b/third_party/libwebrtc/sdk/objc/base/RTCMutableYUVPlanarBuffer.h new file mode 100644 index 0000000000..bd14e3bca3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCMutableYUVPlanarBuffer.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCYUVPlanarBuffer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Extension of the YUV planar data buffer with mutable data access */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCMutableYUVPlanarBuffer)<RTC_OBJC_TYPE(RTCYUVPlanarBuffer)> + + @property(nonatomic, readonly) uint8_t *mutableDataY; +@property(nonatomic, readonly) uint8_t *mutableDataU; +@property(nonatomic, readonly) uint8_t *mutableDataV; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCSSLCertificateVerifier.h b/third_party/libwebrtc/sdk/objc/base/RTCSSLCertificateVerifier.h new file mode 100644 index 0000000000..53da0cceff --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCSSLCertificateVerifier.h @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE +(RTCSSLCertificateVerifier)<NSObject> + + /** The certificate to verify */ + - (BOOL)verify : (NSData *)derCertificate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoCapturer.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoCapturer.h new file mode 100644 index 0000000000..a1ffdcf38e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoCapturer.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoFrame.h" + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCVideoCapturer); + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoCapturerDelegate)<NSObject> - + (void)capturer : (RTC_OBJC_TYPE(RTCVideoCapturer) *)capturer didCaptureVideoFrame + : (RTC_OBJC_TYPE(RTCVideoFrame) *)frame; +@end + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoCapturer) : NSObject + +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)> delegate; + +- (instancetype)initWithDelegate:(id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoCapturer.m b/third_party/libwebrtc/sdk/objc/base/RTCVideoCapturer.m new file mode 100644 index 0000000000..ca31a731f0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoCapturer.m @@ -0,0 +1,24 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoCapturer.h" + +@implementation RTC_OBJC_TYPE (RTCVideoCapturer) + +@synthesize delegate = _delegate; + +- (instancetype)initWithDelegate:(id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)delegate { + if (self = [super init]) { + _delegate = delegate; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoCodecInfo.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoCodecInfo.h new file mode 100644 index 0000000000..fa28958f25 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoCodecInfo.h @@ -0,0 +1,36 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Holds information to identify a codec. Corresponds to webrtc::SdpVideoFormat. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoCodecInfo) : NSObject <NSCoding> + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithName:(NSString *)name; + +- (instancetype)initWithName:(NSString *)name + parameters:(nullable NSDictionary<NSString *, NSString *> *)parameters + NS_DESIGNATED_INITIALIZER; + +- (BOOL)isEqualToCodecInfo:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info; + +@property(nonatomic, readonly) NSString *name; +@property(nonatomic, readonly) NSDictionary<NSString *, NSString *> *parameters; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoCodecInfo.m b/third_party/libwebrtc/sdk/objc/base/RTCVideoCodecInfo.m new file mode 100644 index 0000000000..ce26ae1de3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoCodecInfo.m @@ -0,0 +1,65 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoCodecInfo.h" + +@implementation RTC_OBJC_TYPE (RTCVideoCodecInfo) + +@synthesize name = _name; +@synthesize parameters = _parameters; + +- (instancetype)initWithName:(NSString *)name { + return [self initWithName:name parameters:nil]; +} + +- (instancetype)initWithName:(NSString *)name + parameters:(nullable NSDictionary<NSString *, NSString *> *)parameters { + if (self = [super init]) { + _name = name; + _parameters = (parameters ? parameters : @{}); + } + + return self; +} + +- (BOOL)isEqualToCodecInfo:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info { + if (!info || + ![self.name isEqualToString:info.name] || + ![self.parameters isEqualToDictionary:info.parameters]) { + return NO; + } + return YES; +} + +- (BOOL)isEqual:(id)object { + if (self == object) + return YES; + if (![object isKindOfClass:[self class]]) + return NO; + return [self isEqualToCodecInfo:object]; +} + +- (NSUInteger)hash { + return [self.name hash] ^ [self.parameters hash]; +} + +#pragma mark - NSCoding + +- (instancetype)initWithCoder:(NSCoder *)decoder { + return [self initWithName:[decoder decodeObjectForKey:@"name"] + parameters:[decoder decodeObjectForKey:@"parameters"]]; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + [encoder encodeObject:_name forKey:@"name"]; + [encoder encodeObject:_parameters forKey:@"parameters"]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoDecoder.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoDecoder.h new file mode 100644 index 0000000000..ccddd42d42 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoDecoder.h @@ -0,0 +1,40 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCCodecSpecificInfo.h" +#import "RTCEncodedImage.h" +#import "RTCMacros.h" +#import "RTCVideoEncoderSettings.h" +#import "RTCVideoFrame.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Callback block for decoder. */ +typedef void (^RTCVideoDecoderCallback)(RTC_OBJC_TYPE(RTCVideoFrame) * frame); + +/** Protocol for decoder implementations. */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoDecoder)<NSObject> + + - (void)setCallback : (RTCVideoDecoderCallback)callback; +- (NSInteger)startDecodeWithNumberOfCores:(int)numberOfCores; +- (NSInteger)releaseDecoder; +- (NSInteger)decode:(RTC_OBJC_TYPE(RTCEncodedImage) *)encodedImage + missingFrames:(BOOL)missingFrames + codecSpecificInfo:(nullable id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)info + renderTimeMs:(int64_t)renderTimeMs; +- (NSString *)implementationName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoDecoderFactory.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoDecoderFactory.h new file mode 100644 index 0000000000..8d90138521 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoDecoderFactory.h @@ -0,0 +1,32 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoCodecInfo.h" +#import "RTCVideoDecoder.h" + +NS_ASSUME_NONNULL_BEGIN + +/** RTCVideoDecoderFactory is an Objective-C version of webrtc::VideoDecoderFactory. + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoDecoderFactory)<NSObject> + + - (nullable id<RTC_OBJC_TYPE(RTCVideoDecoder)>)createDecoder + : (RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info; +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *) + supportedCodecs; // TODO(andersc): "supportedFormats" instead? + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoder.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoder.h new file mode 100644 index 0000000000..2445d432d6 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoder.h @@ -0,0 +1,59 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCCodecSpecificInfo.h" +#import "RTCEncodedImage.h" +#import "RTCMacros.h" +#import "RTCVideoEncoderQpThresholds.h" +#import "RTCVideoEncoderSettings.h" +#import "RTCVideoFrame.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Callback block for encoder. */ +typedef BOOL (^RTCVideoEncoderCallback)(RTC_OBJC_TYPE(RTCEncodedImage) * frame, + id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)> info); + +/** Protocol for encoder implementations. */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoEncoder)<NSObject> + +- (void)setCallback:(nullable RTCVideoEncoderCallback)callback; +- (NSInteger)startEncodeWithSettings:(RTC_OBJC_TYPE(RTCVideoEncoderSettings) *)settings + numberOfCores:(int)numberOfCores; +- (NSInteger)releaseEncoder; +- (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame + codecSpecificInfo:(nullable id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)info + frameTypes:(NSArray<NSNumber *> *)frameTypes; +- (int)setBitrate:(uint32_t)bitrateKbit framerate:(uint32_t)framerate; +- (NSString *)implementationName; + +/** Returns QP scaling settings for encoder. The quality scaler adjusts the resolution in order to + * keep the QP from the encoded images within the given range. Returning nil from this function + * disables quality scaling. */ +- (nullable RTC_OBJC_TYPE(RTCVideoEncoderQpThresholds) *)scalingSettings; + +/** Resolutions should be aligned to this value. */ +@property(nonatomic, readonly) NSInteger resolutionAlignment; + +/** If enabled, resolution alignment is applied to all simulcast layers simultaneously so that when + scaled, all resolutions comply with 'resolutionAlignment'. */ +@property(nonatomic, readonly) BOOL applyAlignmentToAllSimulcastLayers; + +/** If YES, the receiver is expected to resample/scale the source texture to the expected output + size. */ +@property(nonatomic, readonly) BOOL supportsNativeHandle; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderFactory.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderFactory.h new file mode 100644 index 0000000000..a73cd77990 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderFactory.h @@ -0,0 +1,52 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoCodecInfo.h" +#import "RTCVideoEncoder.h" + +NS_ASSUME_NONNULL_BEGIN + +/** RTCVideoEncoderFactory is an Objective-C version of + webrtc::VideoEncoderFactory::VideoEncoderSelector. + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoEncoderSelector)<NSObject> + + - (void)registerCurrentEncoderInfo : (RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info; +- (nullable RTC_OBJC_TYPE(RTCVideoCodecInfo) *)encoderForBitrate:(NSInteger)bitrate; +- (nullable RTC_OBJC_TYPE(RTCVideoCodecInfo) *)encoderForBrokenEncoder; + +@optional +- (nullable RTC_OBJC_TYPE(RTCVideoCodecInfo) *)encoderForResolutionChangeBySize:(CGSize)size; + +@end + +/** RTCVideoEncoderFactory is an Objective-C version of webrtc::VideoEncoderFactory. + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoEncoderFactory)<NSObject> + + - (nullable id<RTC_OBJC_TYPE(RTCVideoEncoder)>)createEncoder + : (RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info; +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *) + supportedCodecs; // TODO(andersc): "supportedFormats" instead? + +@optional +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)implementations; +- (nullable id<RTC_OBJC_TYPE(RTCVideoEncoderSelector)>)encoderSelector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderQpThresholds.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderQpThresholds.h new file mode 100644 index 0000000000..1a6e9e88ab --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderQpThresholds.h @@ -0,0 +1,28 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** QP thresholds for encoder. Corresponds to webrtc::VideoEncoder::QpThresholds. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderQpThresholds) : NSObject + +- (instancetype)initWithThresholdsLow:(NSInteger)low high:(NSInteger)high; + +@property(nonatomic, readonly) NSInteger low; +@property(nonatomic, readonly) NSInteger high; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderQpThresholds.m b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderQpThresholds.m new file mode 100644 index 0000000000..fb7012f44f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderQpThresholds.m @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoEncoderQpThresholds.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderQpThresholds) + +@synthesize low = _low; +@synthesize high = _high; + +- (instancetype)initWithThresholdsLow:(NSInteger)low high:(NSInteger)high { + if (self = [super init]) { + _low = low; + _high = high; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderSettings.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderSettings.h new file mode 100644 index 0000000000..ae792eab71 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderSettings.h @@ -0,0 +1,42 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, RTCVideoCodecMode) { + RTCVideoCodecModeRealtimeVideo, + RTCVideoCodecModeScreensharing, +}; + +/** Settings for encoder. Corresponds to webrtc::VideoCodec. */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderSettings) : NSObject + +@property(nonatomic, strong) NSString *name; + +@property(nonatomic, assign) unsigned short width; +@property(nonatomic, assign) unsigned short height; + +@property(nonatomic, assign) unsigned int startBitrate; // kilobits/sec. +@property(nonatomic, assign) unsigned int maxBitrate; +@property(nonatomic, assign) unsigned int minBitrate; + +@property(nonatomic, assign) uint32_t maxFramerate; + +@property(nonatomic, assign) unsigned int qpMax; +@property(nonatomic, assign) RTCVideoCodecMode mode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderSettings.m b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderSettings.m new file mode 100644 index 0000000000..f66cd2cf77 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoEncoderSettings.m @@ -0,0 +1,25 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoEncoderSettings.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderSettings) + +@synthesize name = _name; +@synthesize width = _width; +@synthesize height = _height; +@synthesize startBitrate = _startBitrate; +@synthesize maxBitrate = _maxBitrate; +@synthesize minBitrate = _minBitrate; +@synthesize maxFramerate = _maxFramerate; +@synthesize qpMax = _qpMax; +@synthesize mode = _mode; + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoFrame.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoFrame.h new file mode 100644 index 0000000000..f5638d27cf --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoFrame.h @@ -0,0 +1,86 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, RTCVideoRotation) { + RTCVideoRotation_0 = 0, + RTCVideoRotation_90 = 90, + RTCVideoRotation_180 = 180, + RTCVideoRotation_270 = 270, +}; + +@protocol RTC_OBJC_TYPE +(RTCVideoFrameBuffer); + +// RTCVideoFrame is an ObjectiveC version of webrtc::VideoFrame. +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoFrame) : NSObject + +/** Width without rotation applied. */ +@property(nonatomic, readonly) int width; + +/** Height without rotation applied. */ +@property(nonatomic, readonly) int height; +@property(nonatomic, readonly) RTCVideoRotation rotation; + +/** Timestamp in nanoseconds. */ +@property(nonatomic, readonly) int64_t timeStampNs; + +/** Timestamp 90 kHz. */ +@property(nonatomic, assign) int32_t timeStamp; + +@property(nonatomic, readonly) id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> buffer; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype) new NS_UNAVAILABLE; + +/** Initialize an RTCVideoFrame from a pixel buffer, rotation, and timestamp. + * Deprecated - initialize with a RTCCVPixelBuffer instead + */ +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer + rotation:(RTCVideoRotation)rotation + timeStampNs:(int64_t)timeStampNs + DEPRECATED_MSG_ATTRIBUTE("use initWithBuffer instead"); + +/** Initialize an RTCVideoFrame from a pixel buffer combined with cropping and + * scaling. Cropping will be applied first on the pixel buffer, followed by + * scaling to the final resolution of scaledWidth x scaledHeight. + */ +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer + scaledWidth:(int)scaledWidth + scaledHeight:(int)scaledHeight + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + cropX:(int)cropX + cropY:(int)cropY + rotation:(RTCVideoRotation)rotation + timeStampNs:(int64_t)timeStampNs + DEPRECATED_MSG_ATTRIBUTE("use initWithBuffer instead"); + +/** Initialize an RTCVideoFrame from a frame buffer, rotation, and timestamp. + */ +- (instancetype)initWithBuffer:(id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)>)frameBuffer + rotation:(RTCVideoRotation)rotation + timeStampNs:(int64_t)timeStampNs; + +/** Return a frame that is guaranteed to be I420, i.e. it is possible to access + * the YUV data on it. + */ +- (RTC_OBJC_TYPE(RTCVideoFrame) *)newI420VideoFrame; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoFrame.mm b/third_party/libwebrtc/sdk/objc/base/RTCVideoFrame.mm new file mode 100644 index 0000000000..e162238d73 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoFrame.mm @@ -0,0 +1,78 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoFrame.h" + +#import "RTCI420Buffer.h" +#import "RTCVideoFrameBuffer.h" + +@implementation RTC_OBJC_TYPE (RTCVideoFrame) { + RTCVideoRotation _rotation; + int64_t _timeStampNs; +} + +@synthesize buffer = _buffer; +@synthesize timeStamp; + +- (int)width { + return _buffer.width; +} + +- (int)height { + return _buffer.height; +} + +- (RTCVideoRotation)rotation { + return _rotation; +} + +- (int64_t)timeStampNs { + return _timeStampNs; +} + +- (RTC_OBJC_TYPE(RTCVideoFrame) *)newI420VideoFrame { + return [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:[_buffer toI420] + rotation:_rotation + timeStampNs:_timeStampNs]; +} + +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer + rotation:(RTCVideoRotation)rotation + timeStampNs:(int64_t)timeStampNs { + // Deprecated. + return nil; +} + +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer + scaledWidth:(int)scaledWidth + scaledHeight:(int)scaledHeight + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + cropX:(int)cropX + cropY:(int)cropY + rotation:(RTCVideoRotation)rotation + timeStampNs:(int64_t)timeStampNs { + // Deprecated. + return nil; +} + +- (instancetype)initWithBuffer:(id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)>)buffer + rotation:(RTCVideoRotation)rotation + timeStampNs:(int64_t)timeStampNs { + if (self = [super init]) { + _buffer = buffer; + _rotation = rotation; + _timeStampNs = timeStampNs; + } + + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoFrameBuffer.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoFrameBuffer.h new file mode 100644 index 0000000000..a36807951b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoFrameBuffer.h @@ -0,0 +1,40 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol RTC_OBJC_TYPE +(RTCI420Buffer); + +// RTCVideoFrameBuffer is an ObjectiveC version of webrtc::VideoFrameBuffer. +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoFrameBuffer)<NSObject> + + @property(nonatomic, readonly) int width; +@property(nonatomic, readonly) int height; + +- (id<RTC_OBJC_TYPE(RTCI420Buffer)>)toI420; + +@optional +- (id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)>)cropAndScaleWith:(int)offsetX + offsetY:(int)offsetY + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + scaleWidth:(int)scaleWidth + scaleHeight:(int)scaleHeight; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCVideoRenderer.h b/third_party/libwebrtc/sdk/objc/base/RTCVideoRenderer.h new file mode 100644 index 0000000000..0f763295ad --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCVideoRenderer.h @@ -0,0 +1,43 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#endif + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCVideoFrame); + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoRenderer)<NSObject> + + /** The size of the frame. */ + - (void)setSize : (CGSize)size; + +/** The frame to be displayed. */ +- (void)renderFrame:(nullable RTC_OBJC_TYPE(RTCVideoFrame) *)frame; + +@end + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoViewDelegate) + + - (void)videoView : (id<RTC_OBJC_TYPE(RTCVideoRenderer)>)videoView didChangeVideoSize + : (CGSize)size; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/base/RTCYUVPlanarBuffer.h b/third_party/libwebrtc/sdk/objc/base/RTCYUVPlanarBuffer.h new file mode 100644 index 0000000000..be01b915f5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/base/RTCYUVPlanarBuffer.h @@ -0,0 +1,46 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCMacros.h" +#import "RTCVideoFrameBuffer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Protocol for RTCVideoFrameBuffers containing YUV planar data. */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCYUVPlanarBuffer)<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> + + @property(nonatomic, readonly) int chromaWidth; +@property(nonatomic, readonly) int chromaHeight; +@property(nonatomic, readonly) const uint8_t *dataY; +@property(nonatomic, readonly) const uint8_t *dataU; +@property(nonatomic, readonly) const uint8_t *dataV; +@property(nonatomic, readonly) int strideY; +@property(nonatomic, readonly) int strideU; +@property(nonatomic, readonly) int strideV; + +- (instancetype)initWithWidth:(int)width + height:(int)height + dataY:(const uint8_t *)dataY + dataU:(const uint8_t *)dataU + dataV:(const uint8_t *)dataV; +- (instancetype)initWithWidth:(int)width height:(int)height; +- (instancetype)initWithWidth:(int)width + height:(int)height + strideY:(int)strideY + strideU:(int)strideU + strideV:(int)strideV; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioDevice.h b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioDevice.h new file mode 100644 index 0000000000..f445825ff0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioDevice.h @@ -0,0 +1,308 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AudioUnit/AudioUnit.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef OSStatus (^RTC_OBJC_TYPE(RTCAudioDeviceGetPlayoutDataBlock))( + AudioUnitRenderActionFlags *_Nonnull actionFlags, + const AudioTimeStamp *_Nonnull timestamp, + NSInteger inputBusNumber, + UInt32 frameCount, + AudioBufferList *_Nonnull outputData); + +typedef OSStatus (^RTC_OBJC_TYPE(RTCAudioDeviceRenderRecordedDataBlock))( + AudioUnitRenderActionFlags *_Nonnull actionFlags, + const AudioTimeStamp *_Nonnull timestamp, + NSInteger inputBusNumber, + UInt32 frameCount, + AudioBufferList *_Nonnull inputData, + void *_Nullable renderContext); + +typedef OSStatus (^RTC_OBJC_TYPE(RTCAudioDeviceDeliverRecordedDataBlock))( + AudioUnitRenderActionFlags *_Nonnull actionFlags, + const AudioTimeStamp *_Nonnull timestamp, + NSInteger inputBusNumber, + UInt32 frameCount, + const AudioBufferList *_Nullable inputData, + void *_Nullable renderContext, + NS_NOESCAPE RTC_OBJC_TYPE(RTCAudioDeviceRenderRecordedDataBlock) _Nullable renderBlock); + +/** + * Delegate object provided by native ADM during RTCAudioDevice initialization. + * Provides blocks to poll playback audio samples from native ADM and to feed + * recorded audio samples into native ADM. + */ +RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE +(RTCAudioDeviceDelegate)<NSObject> + /** + * Implementation of RTCAudioSource should call this block to feed recorded PCM (16-bit integer) + * into native ADM. Stereo data is expected to be interleaved starting with the left channel. + * Either `inputData` with pre-filled audio data must be provided during block + * call or `renderBlock` must be provided which must fill provided audio buffer with recorded + * samples. + * + * NOTE: Implementation of RTCAudioDevice is expected to call the block on the same thread until + * `notifyAudioInterrupted` is called. When `notifyAudioInterrupted` is called implementation + * can call the block on a different thread. + */ + @property(readonly, nonnull) + RTC_OBJC_TYPE(RTCAudioDeviceDeliverRecordedDataBlock) deliverRecordedData; + +/** + * Provides input sample rate preference as it preferred by native ADM. + */ +@property(readonly) double preferredInputSampleRate; + +/** + * Provides input IO buffer duration preference as it preferred by native ADM. + */ +@property(readonly) NSTimeInterval preferredInputIOBufferDuration; + +/** + * Provides output sample rate preference as it preferred by native ADM. + */ +@property(readonly) double preferredOutputSampleRate; + +/** + * Provides output IO buffer duration preference as it preferred by native ADM. + */ +@property(readonly) NSTimeInterval preferredOutputIOBufferDuration; + +/** + * Implementation of RTCAudioDevice should call this block to request PCM (16-bit integer) + * from native ADM to play. Stereo data is interleaved starting with the left channel. + * + * NOTE: Implementation of RTCAudioDevice is expected to invoke of this block on the + * same thread until `notifyAudioInterrupted` is called. When `notifyAudioInterrupted` is called + * implementation can call the block from a different thread. + */ +@property(readonly, nonnull) RTC_OBJC_TYPE(RTCAudioDeviceGetPlayoutDataBlock) getPlayoutData; + +/** + * Notifies native ADM that some of the audio input parameters of RTCAudioDevice like + * samle rate and/or IO buffer duration and/or IO latency had possibly changed. + * Native ADM will adjust its audio input buffer to match current parameters of audio device. + * + * NOTE: Must be called within block executed via `dispatchAsync` or `dispatchSync`. + */ +- (void)notifyAudioInputParametersChange; + +/** + * Notifies native ADM that some of the audio output parameters of RTCAudioDevice like + * samle rate and/or IO buffer duration and/or IO latency had possibly changed. + * Native ADM will adjust its audio output buffer to match current parameters of audio device. + * + * NOTE: Must be called within block executed via `dispatchAsync` or `dispatchSync`. + */ +- (void)notifyAudioOutputParametersChange; + +/** + * Notifies native ADM that audio input is interrupted and further audio playout + * and recording might happen on a different thread. + * + * NOTE: Must be called within block executed via `dispatchAsync` or `dispatchSync`. + */ +- (void)notifyAudioInputInterrupted; + +/** + * Notifies native ADM that audio output is interrupted and further audio playout + * and recording might happen on a different thread. + * + * NOTE: Must be called within block executed via `dispatchAsync` or `dispatchSync`. + */ +- (void)notifyAudioOutputInterrupted; + +/** + * Asynchronously execute block of code within the context of + * thread which owns native ADM. + * + * NOTE: Intended to be used to invoke `notifyAudioInputParametersChange`, + * `notifyAudioOutputParametersChange`, `notifyAudioInputInterrupted`, + * `notifyAudioOutputInterrupted` on native ADM thread. + * Also could be used by `RTCAudioDevice` implementation to tie + * mutations of underlying audio objects (AVAudioEngine, AudioUnit, etc) + * to the native ADM thread. Could be useful to handle events like audio route change, which + * could lead to audio parameters change. + */ +- (void)dispatchAsync:(dispatch_block_t)block; + +/** + * Synchronously execute block of code within the context of + * thread which owns native ADM. Allows reentrancy. + * + * NOTE: Intended to be used to invoke `notifyAudioInputParametersChange`, + * `notifyAudioOutputParametersChange`, `notifyAudioInputInterrupted`, + * `notifyAudioOutputInterrupted` on native ADM thread and make sure + * aforementioned is completed before `dispatchSync` returns. Could be useful + * when implementation of `RTCAudioDevice` tie mutation to underlying audio objects (AVAudioEngine, + * AudioUnit, etc) to own thread to satisfy requirement that native ADM audio parameters + * must be kept in sync with current audio parameters before audio is actually played or recorded. + */ +- (void)dispatchSync:(dispatch_block_t)block; + +@end + +/** + * Protocol to abstract platform specific ways to implement playback and recording. + * + * NOTE: All the members of protocol are called by native ADM from the same thread + * between calls to `initializeWithDelegate` and `terminate`. + * NOTE: Implementation is fully responsible for configuring application's AVAudioSession. + * An example implementation of RTCAudioDevice: https://github.com/mstyura/RTCAudioDevice + * TODO(yura.yaroshevich): Implement custom RTCAudioDevice for AppRTCMobile demo app. + */ +RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE +(RTCAudioDevice)<NSObject> + + /** + * Indicates current sample rate of audio recording. Changes to this property + * must be notified back to native ADM via `-[RTCAudioDeviceDelegate + * notifyAudioParametersChange]`. + */ + @property(readonly) double deviceInputSampleRate; + +/** + * Indicates current size of record buffer. Changes to this property + * must be notified back to native ADM via `-[RTCAudioDeviceDelegate notifyAudioParametersChange]`. + */ +@property(readonly) NSTimeInterval inputIOBufferDuration; + +/** + * Indicates current number of recorded audio channels. Changes to this property + * must be notified back to native ADM via `-[RTCAudioDeviceDelegate notifyAudioParametersChange]`. + */ +@property(readonly) NSInteger inputNumberOfChannels; + +/** + * Indicates current input latency + */ +@property(readonly) NSTimeInterval inputLatency; + +/** + * Indicates current sample rate of audio playback. Changes to this property + * must be notified back to native ADM via `-[RTCAudioDeviceDelegate notifyAudioParametersChange]`. + */ +@property(readonly) double deviceOutputSampleRate; + +/** + * Indicates current size of playback buffer. Changes to this property + * must be notified back to native ADM via `-[RTCAudioDeviceDelegate notifyAudioParametersChange]`. + */ +@property(readonly) NSTimeInterval outputIOBufferDuration; + +/** + * Indicates current number of playback audio channels. Changes to this property + * must be notified back to WebRTC via `[RTCAudioDeviceDelegate notifyAudioParametersChange]`. + */ +@property(readonly) NSInteger outputNumberOfChannels; + +/** + * Indicates current output latency + */ +@property(readonly) NSTimeInterval outputLatency; + +/** + * Indicates if invocation of `initializeWithDelegate` required before usage of RTCAudioDevice. + * YES indicates that `initializeWithDelegate` was called earlier without subsequent call to + * `terminate`. NO indicates that either `initializeWithDelegate` not called or `terminate` called. + */ +@property(readonly) BOOL isInitialized; + +/** + * Initializes RTCAudioDevice with RTCAudioDeviceDelegate. + * Implementation must return YES if RTCAudioDevice initialized successfully and NO otherwise. + */ +- (BOOL)initializeWithDelegate:(id<RTC_OBJC_TYPE(RTCAudioDeviceDelegate)>)delegate; + +/** + * De-initializes RTCAudioDevice. Implementation should forget about `delegate` provided in + * `initializeWithDelegate`. + */ +- (BOOL)terminateDevice; + +/** + * Property to indicate if `initializePlayout` call required before invocation of `startPlayout`. + * YES indicates that `initializePlayout` was successfully invoked earlier or not necessary, + * NO indicates that `initializePlayout` invocation required. + */ +@property(readonly) BOOL isPlayoutInitialized; + +/** + * Prepares RTCAudioDevice to play audio. + * Called by native ADM before invocation of `startPlayout`. + * Implementation is expected to return YES in case of successful playout initialization and NO + * otherwise. + */ +- (BOOL)initializePlayout; + +/** + * Property to indicate if RTCAudioDevice should be playing according to + * earlier calls of `startPlayout` and `stopPlayout`. + */ +@property(readonly) BOOL isPlaying; + +/** + * Method is called when native ADM wants to play audio. + * Implementation is expected to return YES if playback start request + * successfully handled and NO otherwise. + */ +- (BOOL)startPlayout; + +/** + * Method is called when native ADM no longer needs to play audio. + * Implementation is expected to return YES if playback stop request + * successfully handled and NO otherwise. + */ +- (BOOL)stopPlayout; + +/** + * Property to indicate if `initializeRecording` call required before usage of `startRecording`. + * YES indicates that `initializeRecording` was successfully invoked earlier or not necessary, + * NO indicates that `initializeRecording` invocation required. + */ +@property(readonly) BOOL isRecordingInitialized; + +/** + * Prepares RTCAudioDevice to record audio. + * Called by native ADM before invocation of `startRecording`. + * Implementation may use this method to prepare resources required to record audio. + * Implementation is expected to return YES in case of successful record initialization and NO + * otherwise. + */ +- (BOOL)initializeRecording; + +/** + * Property to indicate if RTCAudioDevice should record audio according to + * earlier calls to `startRecording` and `stopRecording`. + */ +@property(readonly) BOOL isRecording; + +/** + * Method is called when native ADM wants to record audio. + * Implementation is expected to return YES if recording start request + * successfully handled and NO otherwise. + */ +- (BOOL)startRecording; + +/** + * Method is called when native ADM no longer needs to record audio. + * Implementation is expected to return YES if recording stop request + * successfully handled and NO otherwise. + */ +- (BOOL)stopRecording; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession+Configuration.mm b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession+Configuration.mm new file mode 100644 index 0000000000..449f31e9dd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession+Configuration.mm @@ -0,0 +1,176 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSession+Private.h" +#import "RTCAudioSessionConfiguration.h" + +#import "base/RTCLogging.h" + +@implementation RTC_OBJC_TYPE (RTCAudioSession) +(Configuration) + + - (BOOL)setConfiguration : (RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration error + : (NSError **)outError { + return [self setConfiguration:configuration + active:NO + shouldSetActive:NO + error:outError]; +} + +- (BOOL)setConfiguration:(RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration + active:(BOOL)active + error:(NSError **)outError { + return [self setConfiguration:configuration + active:active + shouldSetActive:YES + error:outError]; +} + +#pragma mark - Private + +- (BOOL)setConfiguration:(RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration + active:(BOOL)active + shouldSetActive:(BOOL)shouldSetActive + error:(NSError **)outError { + NSParameterAssert(configuration); + if (outError) { + *outError = nil; + } + + // Provide an error even if there isn't one so we can log it. We will not + // return immediately on error in this function and instead try to set + // everything we can. + NSError *error = nil; + + if (self.category != configuration.category || + self.categoryOptions != configuration.categoryOptions) { + NSError *categoryError = nil; + if (![self setCategory:configuration.category + withOptions:configuration.categoryOptions + error:&categoryError]) { + RTCLogError(@"Failed to set category: %@", + categoryError.localizedDescription); + error = categoryError; + } else { + RTCLog(@"Set category to: %@", configuration.category); + } + } + + if (self.mode != configuration.mode) { + NSError *modeError = nil; + if (![self setMode:configuration.mode error:&modeError]) { + RTCLogError(@"Failed to set mode: %@", + modeError.localizedDescription); + error = modeError; + } else { + RTCLog(@"Set mode to: %@", configuration.mode); + } + } + + // Sometimes category options don't stick after setting mode. + if (self.categoryOptions != configuration.categoryOptions) { + NSError *categoryError = nil; + if (![self setCategory:configuration.category + withOptions:configuration.categoryOptions + error:&categoryError]) { + RTCLogError(@"Failed to set category options: %@", + categoryError.localizedDescription); + error = categoryError; + } else { + RTCLog(@"Set category options to: %ld", + (long)configuration.categoryOptions); + } + } + + if (self.preferredSampleRate != configuration.sampleRate) { + NSError *sampleRateError = nil; + if (![self setPreferredSampleRate:configuration.sampleRate + error:&sampleRateError]) { + RTCLogError(@"Failed to set preferred sample rate: %@", + sampleRateError.localizedDescription); + if (!self.ignoresPreferredAttributeConfigurationErrors) { + error = sampleRateError; + } + } else { + RTCLog(@"Set preferred sample rate to: %.2f", + configuration.sampleRate); + } + } + + if (self.preferredIOBufferDuration != configuration.ioBufferDuration) { + NSError *bufferDurationError = nil; + if (![self setPreferredIOBufferDuration:configuration.ioBufferDuration + error:&bufferDurationError]) { + RTCLogError(@"Failed to set preferred IO buffer duration: %@", + bufferDurationError.localizedDescription); + if (!self.ignoresPreferredAttributeConfigurationErrors) { + error = bufferDurationError; + } + } else { + RTCLog(@"Set preferred IO buffer duration to: %f", + configuration.ioBufferDuration); + } + } + + if (shouldSetActive) { + NSError *activeError = nil; + if (![self setActive:active error:&activeError]) { + RTCLogError(@"Failed to setActive to %d: %@", + active, activeError.localizedDescription); + error = activeError; + } + } + + if (self.isActive && + // TODO(tkchin): Figure out which category/mode numChannels is valid for. + [self.mode isEqualToString:AVAudioSessionModeVoiceChat]) { + // Try to set the preferred number of hardware audio channels. These calls + // must be done after setting the audio session’s category and mode and + // activating the session. + NSInteger inputNumberOfChannels = configuration.inputNumberOfChannels; + if (self.inputNumberOfChannels != inputNumberOfChannels) { + NSError *inputChannelsError = nil; + if (![self setPreferredInputNumberOfChannels:inputNumberOfChannels + error:&inputChannelsError]) { + RTCLogError(@"Failed to set preferred input number of channels: %@", + inputChannelsError.localizedDescription); + if (!self.ignoresPreferredAttributeConfigurationErrors) { + error = inputChannelsError; + } + } else { + RTCLog(@"Set input number of channels to: %ld", + (long)inputNumberOfChannels); + } + } + NSInteger outputNumberOfChannels = configuration.outputNumberOfChannels; + if (self.outputNumberOfChannels != outputNumberOfChannels) { + NSError *outputChannelsError = nil; + if (![self setPreferredOutputNumberOfChannels:outputNumberOfChannels + error:&outputChannelsError]) { + RTCLogError(@"Failed to set preferred output number of channels: %@", + outputChannelsError.localizedDescription); + if (!self.ignoresPreferredAttributeConfigurationErrors) { + error = outputChannelsError; + } + } else { + RTCLog(@"Set output number of channels to: %ld", + (long)outputNumberOfChannels); + } + } + } + + if (outError) { + *outError = error; + } + + return error == nil; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession+Private.h b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession+Private.h new file mode 100644 index 0000000000..2be1b9fb3d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession+Private.h @@ -0,0 +1,95 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSession.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCAudioSessionConfiguration); + +@interface RTC_OBJC_TYPE (RTCAudioSession) +() + + /** Number of times setActive:YES has succeeded without a balanced call to + * setActive:NO. + */ + @property(nonatomic, readonly) int activationCount; + +/** The number of times `beginWebRTCSession` was called without a balanced call + * to `endWebRTCSession`. + */ +@property(nonatomic, readonly) int webRTCSessionCount; + +/** Convenience BOOL that checks useManualAudio and isAudioEnebled. */ +@property(readonly) BOOL canPlayOrRecord; + +/** Tracks whether we have been sent an interruption event that hasn't been matched by either an + * interrupted end event or a foreground event. + */ +@property(nonatomic, assign) BOOL isInterrupted; + +/** Adds the delegate to the list of delegates, and places it at the front of + * the list. This delegate will be notified before other delegates of + * audio events. + */ +- (void)pushDelegate:(id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)>)delegate; + +/** Signals RTCAudioSession that a WebRTC session is about to begin and + * audio configuration is needed. Will configure the audio session for WebRTC + * if not already configured and if configuration is not delayed. + * Successful calls must be balanced by a call to endWebRTCSession. + */ +- (BOOL)beginWebRTCSession:(NSError **)outError; + +/** Signals RTCAudioSession that a WebRTC session is about to end and audio + * unconfiguration is needed. Will unconfigure the audio session for WebRTC + * if this is the last unmatched call and if configuration is not delayed. + */ +- (BOOL)endWebRTCSession:(NSError **)outError; + +/** Configure the audio session for WebRTC. This call will fail if the session + * is already configured. On other failures, we will attempt to restore the + * previously used audio session configuration. + * `lockForConfiguration` must be called first. + * Successful calls to configureWebRTCSession must be matched by calls to + * `unconfigureWebRTCSession`. + */ +- (BOOL)configureWebRTCSession:(NSError **)outError; + +/** Unconfigures the session for WebRTC. This will attempt to restore the + * audio session to the settings used before `configureWebRTCSession` was + * called. + * `lockForConfiguration` must be called first. + */ +- (BOOL)unconfigureWebRTCSession:(NSError **)outError; + +/** Returns a configuration error with the given description. */ +- (NSError *)configurationErrorWithDescription:(NSString *)description; + +/** Notifies the receiver that a playout glitch was detected. */ +- (void)notifyDidDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches; + +/** Notifies the receiver that there was an error when starting an audio unit. */ +- (void)notifyAudioUnitStartFailedWithError:(OSStatus)error; + +// Properties and methods for tests. +- (void)notifyDidBeginInterruption; +- (void)notifyDidEndInterruptionWithShouldResumeSession:(BOOL)shouldResumeSession; +- (void)notifyDidChangeRouteWithReason:(AVAudioSessionRouteChangeReason)reason + previousRoute:(AVAudioSessionRouteDescription *)previousRoute; +- (void)notifyMediaServicesWereLost; +- (void)notifyMediaServicesWereReset; +- (void)notifyDidChangeCanPlayOrRecord:(BOOL)canPlayOrRecord; +- (void)notifyDidStartPlayOrRecord; +- (void)notifyDidStopPlayOrRecord; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.h b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.h new file mode 100644 index 0000000000..3b83b27ba5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.h @@ -0,0 +1,265 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kRTCAudioSessionErrorDomain; +/** Method that requires lock was called without lock. */ +extern NSInteger const kRTCAudioSessionErrorLockRequired; +/** Unknown configuration error occurred. */ +extern NSInteger const kRTCAudioSessionErrorConfiguration; + +@class RTC_OBJC_TYPE(RTCAudioSession); +@class RTC_OBJC_TYPE(RTCAudioSessionConfiguration); + +// Surfaces AVAudioSession events. WebRTC will listen directly for notifications +// from AVAudioSession and handle them before calling these delegate methods, +// at which point applications can perform additional processing if required. +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCAudioSessionDelegate)<NSObject> + + @optional +/** Called on a system notification thread when AVAudioSession starts an + * interruption event. + */ +- (void)audioSessionDidBeginInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session; + +/** Called on a system notification thread when AVAudioSession ends an + * interruption event. + */ +- (void)audioSessionDidEndInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session + shouldResumeSession:(BOOL)shouldResumeSession; + +/** Called on a system notification thread when AVAudioSession changes the + * route. + */ +- (void)audioSessionDidChangeRoute:(RTC_OBJC_TYPE(RTCAudioSession) *)session + reason:(AVAudioSessionRouteChangeReason)reason + previousRoute:(AVAudioSessionRouteDescription *)previousRoute; + +/** Called on a system notification thread when AVAudioSession media server + * terminates. + */ +- (void)audioSessionMediaServerTerminated:(RTC_OBJC_TYPE(RTCAudioSession) *)session; + +/** Called on a system notification thread when AVAudioSession media server + * restarts. + */ +- (void)audioSessionMediaServerReset:(RTC_OBJC_TYPE(RTCAudioSession) *)session; + +// TODO(tkchin): Maybe handle SilenceSecondaryAudioHintNotification. + +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)session + didChangeCanPlayOrRecord:(BOOL)canPlayOrRecord; + +/** Called on a WebRTC thread when the audio device is notified to begin + * playback or recording. + */ +- (void)audioSessionDidStartPlayOrRecord:(RTC_OBJC_TYPE(RTCAudioSession) *)session; + +/** Called on a WebRTC thread when the audio device is notified to stop + * playback or recording. + */ +- (void)audioSessionDidStopPlayOrRecord:(RTC_OBJC_TYPE(RTCAudioSession) *)session; + +/** Called when the AVAudioSession output volume value changes. */ +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + didChangeOutputVolume:(float)outputVolume; + +/** Called when the audio device detects a playout glitch. The argument is the + * number of glitches detected so far in the current audio playout session. + */ +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches; + +/** Called when the audio session is about to change the active state. + */ +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession willSetActive:(BOOL)active; + +/** Called after the audio session sucessfully changed the active state. + */ +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession didSetActive:(BOOL)active; + +/** Called after the audio session failed to change the active state. + */ +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + failedToSetActive:(BOOL)active + error:(NSError *)error; + +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + audioUnitStartFailedWithError:(NSError *)error; + +@end + +/** This is a protocol used to inform RTCAudioSession when the audio session + * activation state has changed outside of RTCAudioSession. The current known use + * case of this is when CallKit activates the audio session for the application + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCAudioSessionActivationDelegate)<NSObject> + + /** Called when the audio session is activated outside of the app by iOS. */ + - (void)audioSessionDidActivate : (AVAudioSession *)session; + +/** Called when the audio session is deactivated outside of the app by iOS. */ +- (void)audioSessionDidDeactivate:(AVAudioSession *)session; + +@end + +/** Proxy class for AVAudioSession that adds a locking mechanism similar to + * AVCaptureDevice. This is used to that interleaving configurations between + * WebRTC and the application layer are avoided. + * + * RTCAudioSession also coordinates activation so that the audio session is + * activated only once. See `setActive:error:`. + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCAudioSession) : NSObject <RTC_OBJC_TYPE(RTCAudioSessionActivationDelegate)> + +/** Convenience property to access the AVAudioSession singleton. Callers should + * not call setters on AVAudioSession directly, but other method invocations + * are fine. + */ +@property(nonatomic, readonly) AVAudioSession *session; + +/** Our best guess at whether the session is active based on results of calls to + * AVAudioSession. + */ +@property(nonatomic, readonly) BOOL isActive; + +/** If YES, WebRTC will not initialize the audio unit automatically when an + * audio track is ready for playout or recording. Instead, applications should + * call setIsAudioEnabled. If NO, WebRTC will initialize the audio unit + * as soon as an audio track is ready for playout or recording. + */ +@property(nonatomic, assign) BOOL useManualAudio; + +/** This property is only effective if useManualAudio is YES. + * Represents permission for WebRTC to initialize the VoIP audio unit. + * When set to NO, if the VoIP audio unit used by WebRTC is active, it will be + * stopped and uninitialized. This will stop incoming and outgoing audio. + * When set to YES, WebRTC will initialize and start the audio unit when it is + * needed (e.g. due to establishing an audio connection). + * This property was introduced to work around an issue where if an AVPlayer is + * playing audio while the VoIP audio unit is initialized, its audio would be + * either cut off completely or played at a reduced volume. By preventing + * the audio unit from being initialized until after the audio has completed, + * we are able to prevent the abrupt cutoff. + */ +@property(nonatomic, assign) BOOL isAudioEnabled; + +// Proxy properties. +@property(readonly) NSString *category; +@property(readonly) AVAudioSessionCategoryOptions categoryOptions; +@property(readonly) NSString *mode; +@property(readonly) BOOL secondaryAudioShouldBeSilencedHint; +@property(readonly) AVAudioSessionRouteDescription *currentRoute; +@property(readonly) NSInteger maximumInputNumberOfChannels; +@property(readonly) NSInteger maximumOutputNumberOfChannels; +@property(readonly) float inputGain; +@property(readonly) BOOL inputGainSettable; +@property(readonly) BOOL inputAvailable; +@property(readonly, nullable) NSArray<AVAudioSessionDataSourceDescription *> *inputDataSources; +@property(readonly, nullable) AVAudioSessionDataSourceDescription *inputDataSource; +@property(readonly, nullable) NSArray<AVAudioSessionDataSourceDescription *> *outputDataSources; +@property(readonly, nullable) AVAudioSessionDataSourceDescription *outputDataSource; +@property(readonly) double sampleRate; +@property(readonly) double preferredSampleRate; +@property(readonly) NSInteger inputNumberOfChannels; +@property(readonly) NSInteger outputNumberOfChannels; +@property(readonly) float outputVolume; +@property(readonly) NSTimeInterval inputLatency; +@property(readonly) NSTimeInterval outputLatency; +@property(readonly) NSTimeInterval IOBufferDuration; +@property(readonly) NSTimeInterval preferredIOBufferDuration; + +/** + When YES, calls to -setConfiguration:error: and -setConfiguration:active:error: ignore errors in + configuring the audio session's "preferred" attributes (e.g. preferredInputNumberOfChannels). + Typically, configurations to preferred attributes are optimizations, and ignoring this type of + configuration error allows code flow to continue along the happy path when these optimization are + not available. The default value of this property is NO. + */ +@property(nonatomic) BOOL ignoresPreferredAttributeConfigurationErrors; + +/** Default constructor. */ ++ (instancetype)sharedInstance; +- (instancetype)init NS_UNAVAILABLE; + +/** Adds a delegate, which is held weakly. */ +- (void)addDelegate:(id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)>)delegate; +/** Removes an added delegate. */ +- (void)removeDelegate:(id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)>)delegate; + +/** Request exclusive access to the audio session for configuration. This call + * will block if the lock is held by another object. + */ +- (void)lockForConfiguration; +/** Relinquishes exclusive access to the audio session. */ +- (void)unlockForConfiguration; + +/** If `active`, activates the audio session if it isn't already active. + * Successful calls must be balanced with a setActive:NO when activation is no + * longer required. If not `active`, deactivates the audio session if one is + * active and this is the last balanced call. When deactivating, the + * AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation option is passed to + * AVAudioSession. + */ +- (BOOL)setActive:(BOOL)active error:(NSError **)outError; + +// The following methods are proxies for the associated methods on +// AVAudioSession. `lockForConfiguration` must be called before using them +// otherwise they will fail with kRTCAudioSessionErrorLockRequired. + +- (BOOL)setCategory:(NSString *)category + withOptions:(AVAudioSessionCategoryOptions)options + error:(NSError **)outError; +- (BOOL)setMode:(NSString *)mode error:(NSError **)outError; +- (BOOL)setInputGain:(float)gain error:(NSError **)outError; +- (BOOL)setPreferredSampleRate:(double)sampleRate error:(NSError **)outError; +- (BOOL)setPreferredIOBufferDuration:(NSTimeInterval)duration error:(NSError **)outError; +- (BOOL)setPreferredInputNumberOfChannels:(NSInteger)count error:(NSError **)outError; +- (BOOL)setPreferredOutputNumberOfChannels:(NSInteger)count error:(NSError **)outError; +- (BOOL)overrideOutputAudioPort:(AVAudioSessionPortOverride)portOverride error:(NSError **)outError; +- (BOOL)setPreferredInput:(AVAudioSessionPortDescription *)inPort error:(NSError **)outError; +- (BOOL)setInputDataSource:(AVAudioSessionDataSourceDescription *)dataSource + error:(NSError **)outError; +- (BOOL)setOutputDataSource:(AVAudioSessionDataSourceDescription *)dataSource + error:(NSError **)outError; +@end + +@interface RTC_OBJC_TYPE (RTCAudioSession) +(Configuration) + + /** Applies the configuration to the current session. Attempts to set all + * properties even if previous ones fail. Only the last error will be + * returned. + * `lockForConfiguration` must be called first. + */ + - (BOOL)setConfiguration : (RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration error + : (NSError **)outError; + +/** Convenience method that calls both setConfiguration and setActive. + * `lockForConfiguration` must be called first. + */ +- (BOOL)setConfiguration:(RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration + active:(BOOL)active + error:(NSError **)outError; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm new file mode 100644 index 0000000000..550a426d36 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSession.mm @@ -0,0 +1,1000 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSession+Private.h" + +#import <UIKit/UIKit.h> + +#include <atomic> +#include <vector> + +#include "absl/base/attributes.h" +#include "rtc_base/checks.h" +#include "rtc_base/synchronization/mutex.h" + +#import "RTCAudioSessionConfiguration.h" +#import "base/RTCLogging.h" + +#if !defined(ABSL_HAVE_THREAD_LOCAL) +#error ABSL_HAVE_THREAD_LOCAL should be defined for MacOS / iOS Targets. +#endif + +NSString *const kRTCAudioSessionErrorDomain = @"org.webrtc.RTC_OBJC_TYPE(RTCAudioSession)"; +NSInteger const kRTCAudioSessionErrorLockRequired = -1; +NSInteger const kRTCAudioSessionErrorConfiguration = -2; +NSString * const kRTCAudioSessionOutputVolumeSelector = @"outputVolume"; + +namespace { +// Since webrtc::Mutex is not a reentrant lock and cannot check if the mutex is locked, +// we need a separate variable to check that the mutex is locked in the RTCAudioSession. +ABSL_CONST_INIT thread_local bool mutex_locked = false; +} // namespace + +@interface RTC_OBJC_TYPE (RTCAudioSession) +() @property(nonatomic, + readonly) std::vector<__weak id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)> > delegates; +@end + +// This class needs to be thread-safe because it is accessed from many threads. +// TODO(tkchin): Consider more granular locking. We're not expecting a lot of +// lock contention so coarse locks should be fine for now. +@implementation RTC_OBJC_TYPE (RTCAudioSession) { + webrtc::Mutex _mutex; + AVAudioSession *_session; + std::atomic<int> _activationCount; + std::atomic<int> _webRTCSessionCount; + BOOL _isActive; + BOOL _useManualAudio; + BOOL _isAudioEnabled; + BOOL _canPlayOrRecord; + BOOL _isInterrupted; +} + +@synthesize session = _session; +@synthesize delegates = _delegates; +@synthesize ignoresPreferredAttributeConfigurationErrors = + _ignoresPreferredAttributeConfigurationErrors; + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static RTC_OBJC_TYPE(RTCAudioSession) *sharedInstance = nil; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init { + return [self initWithAudioSession:[AVAudioSession sharedInstance]]; +} + +/** This initializer provides a way for unit tests to inject a fake/mock audio session. */ +- (instancetype)initWithAudioSession:(id)audioSession { + if (self = [super init]) { + _session = audioSession; + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(handleInterruptionNotification:) + name:AVAudioSessionInterruptionNotification + object:nil]; + [center addObserver:self + selector:@selector(handleRouteChangeNotification:) + name:AVAudioSessionRouteChangeNotification + object:nil]; + [center addObserver:self + selector:@selector(handleMediaServicesWereLost:) + name:AVAudioSessionMediaServicesWereLostNotification + object:nil]; + [center addObserver:self + selector:@selector(handleMediaServicesWereReset:) + name:AVAudioSessionMediaServicesWereResetNotification + object:nil]; + // Posted on the main thread when the primary audio from other applications + // starts and stops. Foreground applications may use this notification as a + // hint to enable or disable audio that is secondary. + [center addObserver:self + selector:@selector(handleSilenceSecondaryAudioHintNotification:) + name:AVAudioSessionSilenceSecondaryAudioHintNotification + object:nil]; + // Also track foreground event in order to deal with interruption ended situation. + [center addObserver:self + selector:@selector(handleApplicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + [_session addObserver:self + forKeyPath:kRTCAudioSessionOutputVolumeSelector + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld + context:(__bridge void *)RTC_OBJC_TYPE(RTCAudioSession).class]; + + RTCLog(@"RTC_OBJC_TYPE(RTCAudioSession) (%p): init.", self); + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_session removeObserver:self + forKeyPath:kRTCAudioSessionOutputVolumeSelector + context:(__bridge void *)RTC_OBJC_TYPE(RTCAudioSession).class]; + RTCLog(@"RTC_OBJC_TYPE(RTCAudioSession) (%p): dealloc.", self); +} + +- (NSString *)description { + NSString *format = @"RTC_OBJC_TYPE(RTCAudioSession): {\n" + " category: %@\n" + " categoryOptions: %ld\n" + " mode: %@\n" + " isActive: %d\n" + " sampleRate: %.2f\n" + " IOBufferDuration: %f\n" + " outputNumberOfChannels: %ld\n" + " inputNumberOfChannels: %ld\n" + " outputLatency: %f\n" + " inputLatency: %f\n" + " outputVolume: %f\n" + "}"; + NSString *description = [NSString stringWithFormat:format, + self.category, (long)self.categoryOptions, self.mode, + self.isActive, self.sampleRate, self.IOBufferDuration, + self.outputNumberOfChannels, self.inputNumberOfChannels, + self.outputLatency, self.inputLatency, self.outputVolume]; + return description; +} + +- (void)setIsActive:(BOOL)isActive { + @synchronized(self) { + _isActive = isActive; + } +} + +- (BOOL)isActive { + @synchronized(self) { + return _isActive; + } +} + +- (void)setUseManualAudio:(BOOL)useManualAudio { + @synchronized(self) { + if (_useManualAudio == useManualAudio) { + return; + } + _useManualAudio = useManualAudio; + } + [self updateCanPlayOrRecord]; +} + +- (BOOL)useManualAudio { + @synchronized(self) { + return _useManualAudio; + } +} + +- (void)setIsAudioEnabled:(BOOL)isAudioEnabled { + @synchronized(self) { + if (_isAudioEnabled == isAudioEnabled) { + return; + } + _isAudioEnabled = isAudioEnabled; + } + [self updateCanPlayOrRecord]; +} + +- (BOOL)isAudioEnabled { + @synchronized(self) { + return _isAudioEnabled; + } +} + +- (void)setIgnoresPreferredAttributeConfigurationErrors: + (BOOL)ignoresPreferredAttributeConfigurationErrors { + @synchronized(self) { + if (_ignoresPreferredAttributeConfigurationErrors == + ignoresPreferredAttributeConfigurationErrors) { + return; + } + _ignoresPreferredAttributeConfigurationErrors = ignoresPreferredAttributeConfigurationErrors; + } +} + +- (BOOL)ignoresPreferredAttributeConfigurationErrors { + @synchronized(self) { + return _ignoresPreferredAttributeConfigurationErrors; + } +} + +// TODO(tkchin): Check for duplicates. +- (void)addDelegate:(id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)>)delegate { + RTCLog(@"Adding delegate: (%p)", delegate); + if (!delegate) { + return; + } + @synchronized(self) { + _delegates.push_back(delegate); + [self removeZeroedDelegates]; + } +} + +- (void)removeDelegate:(id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)>)delegate { + RTCLog(@"Removing delegate: (%p)", delegate); + if (!delegate) { + return; + } + @synchronized(self) { + _delegates.erase(std::remove(_delegates.begin(), + _delegates.end(), + delegate), + _delegates.end()); + [self removeZeroedDelegates]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wthread-safety-analysis" + +- (void)lockForConfiguration { + RTC_CHECK(!mutex_locked); + _mutex.Lock(); + mutex_locked = true; +} + +- (void)unlockForConfiguration { + mutex_locked = false; + _mutex.Unlock(); +} + +#pragma clang diagnostic pop + +#pragma mark - AVAudioSession proxy methods + +- (NSString *)category { + return self.session.category; +} + +- (AVAudioSessionCategoryOptions)categoryOptions { + return self.session.categoryOptions; +} + +- (NSString *)mode { + return self.session.mode; +} + +- (BOOL)secondaryAudioShouldBeSilencedHint { + return self.session.secondaryAudioShouldBeSilencedHint; +} + +- (AVAudioSessionRouteDescription *)currentRoute { + return self.session.currentRoute; +} + +- (NSInteger)maximumInputNumberOfChannels { + return self.session.maximumInputNumberOfChannels; +} + +- (NSInteger)maximumOutputNumberOfChannels { + return self.session.maximumOutputNumberOfChannels; +} + +- (float)inputGain { + return self.session.inputGain; +} + +- (BOOL)inputGainSettable { + return self.session.inputGainSettable; +} + +- (BOOL)inputAvailable { + return self.session.inputAvailable; +} + +- (NSArray<AVAudioSessionDataSourceDescription *> *)inputDataSources { + return self.session.inputDataSources; +} + +- (AVAudioSessionDataSourceDescription *)inputDataSource { + return self.session.inputDataSource; +} + +- (NSArray<AVAudioSessionDataSourceDescription *> *)outputDataSources { + return self.session.outputDataSources; +} + +- (AVAudioSessionDataSourceDescription *)outputDataSource { + return self.session.outputDataSource; +} + +- (double)sampleRate { + return self.session.sampleRate; +} + +- (double)preferredSampleRate { + return self.session.preferredSampleRate; +} + +- (NSInteger)inputNumberOfChannels { + return self.session.inputNumberOfChannels; +} + +- (NSInteger)outputNumberOfChannels { + return self.session.outputNumberOfChannels; +} + +- (float)outputVolume { + return self.session.outputVolume; +} + +- (NSTimeInterval)inputLatency { + return self.session.inputLatency; +} + +- (NSTimeInterval)outputLatency { + return self.session.outputLatency; +} + +- (NSTimeInterval)IOBufferDuration { + return self.session.IOBufferDuration; +} + +- (NSTimeInterval)preferredIOBufferDuration { + return self.session.preferredIOBufferDuration; +} + +- (BOOL)setActive:(BOOL)active + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + int activationCount = _activationCount.load(); + if (!active && activationCount == 0) { + RTCLogWarning(@"Attempting to deactivate without prior activation."); + } + [self notifyWillSetActive:active]; + BOOL success = YES; + BOOL isActive = self.isActive; + // Keep a local error so we can log it. + NSError *error = nil; + BOOL shouldSetActive = + (active && !isActive) || (!active && isActive && activationCount == 1); + // Attempt to activate if we're not active. + // Attempt to deactivate if we're active and it's the last unbalanced call. + if (shouldSetActive) { + AVAudioSession *session = self.session; + // AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation is used to ensure + // that other audio sessions that were interrupted by our session can return + // to their active state. It is recommended for VoIP apps to use this + // option. + AVAudioSessionSetActiveOptions options = + active ? 0 : AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation; + success = [session setActive:active + withOptions:options + error:&error]; + if (outError) { + *outError = error; + } + } + if (success) { + if (active) { + if (shouldSetActive) { + self.isActive = active; + if (self.isInterrupted) { + self.isInterrupted = NO; + [self notifyDidEndInterruptionWithShouldResumeSession:YES]; + } + } + [self incrementActivationCount]; + [self notifyDidSetActive:active]; + } + } else { + RTCLogError(@"Failed to setActive:%d. Error: %@", + active, error.localizedDescription); + [self notifyFailedToSetActive:active error:error]; + } + // Set isActive and decrement activation count on deactivation + // whether or not it succeeded. + if (!active) { + self.isActive = active; + [self notifyDidSetActive:active]; + [self decrementActivationCount]; + } + RTCLog(@"Number of current activations: %d", _activationCount.load()); + return success; +} + +- (BOOL)setCategory:(NSString *)category + withOptions:(AVAudioSessionCategoryOptions)options + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setCategory:category withOptions:options error:outError]; +} + +- (BOOL)setMode:(NSString *)mode error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setMode:mode error:outError]; +} + +- (BOOL)setInputGain:(float)gain error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setInputGain:gain error:outError]; +} + +- (BOOL)setPreferredSampleRate:(double)sampleRate error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setPreferredSampleRate:sampleRate error:outError]; +} + +- (BOOL)setPreferredIOBufferDuration:(NSTimeInterval)duration + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setPreferredIOBufferDuration:duration error:outError]; +} + +- (BOOL)setPreferredInputNumberOfChannels:(NSInteger)count + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setPreferredInputNumberOfChannels:count error:outError]; +} +- (BOOL)setPreferredOutputNumberOfChannels:(NSInteger)count + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setPreferredOutputNumberOfChannels:count error:outError]; +} + +- (BOOL)overrideOutputAudioPort:(AVAudioSessionPortOverride)portOverride + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session overrideOutputAudioPort:portOverride error:outError]; +} + +- (BOOL)setPreferredInput:(AVAudioSessionPortDescription *)inPort + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setPreferredInput:inPort error:outError]; +} + +- (BOOL)setInputDataSource:(AVAudioSessionDataSourceDescription *)dataSource + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setInputDataSource:dataSource error:outError]; +} + +- (BOOL)setOutputDataSource:(AVAudioSessionDataSourceDescription *)dataSource + error:(NSError **)outError { + if (![self checkLock:outError]) { + return NO; + } + return [self.session setOutputDataSource:dataSource error:outError]; +} + +#pragma mark - Notifications + +- (void)handleInterruptionNotification:(NSNotification *)notification { + NSNumber* typeNumber = + notification.userInfo[AVAudioSessionInterruptionTypeKey]; + AVAudioSessionInterruptionType type = + (AVAudioSessionInterruptionType)typeNumber.unsignedIntegerValue; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + RTCLog(@"Audio session interruption began."); + self.isActive = NO; + self.isInterrupted = YES; + [self notifyDidBeginInterruption]; + break; + case AVAudioSessionInterruptionTypeEnded: { + RTCLog(@"Audio session interruption ended."); + self.isInterrupted = NO; + [self updateAudioSessionAfterEvent]; + NSNumber *optionsNumber = + notification.userInfo[AVAudioSessionInterruptionOptionKey]; + AVAudioSessionInterruptionOptions options = + optionsNumber.unsignedIntegerValue; + BOOL shouldResume = + options & AVAudioSessionInterruptionOptionShouldResume; + [self notifyDidEndInterruptionWithShouldResumeSession:shouldResume]; + break; + } + } +} + +- (void)handleRouteChangeNotification:(NSNotification *)notification { + // Get reason for current route change. + NSNumber* reasonNumber = + notification.userInfo[AVAudioSessionRouteChangeReasonKey]; + AVAudioSessionRouteChangeReason reason = + (AVAudioSessionRouteChangeReason)reasonNumber.unsignedIntegerValue; + RTCLog(@"Audio route changed:"); + switch (reason) { + case AVAudioSessionRouteChangeReasonUnknown: + RTCLog(@"Audio route changed: ReasonUnknown"); + break; + case AVAudioSessionRouteChangeReasonNewDeviceAvailable: + RTCLog(@"Audio route changed: NewDeviceAvailable"); + break; + case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: + RTCLog(@"Audio route changed: OldDeviceUnavailable"); + break; + case AVAudioSessionRouteChangeReasonCategoryChange: + RTCLog(@"Audio route changed: CategoryChange to :%@", + self.session.category); + break; + case AVAudioSessionRouteChangeReasonOverride: + RTCLog(@"Audio route changed: Override"); + break; + case AVAudioSessionRouteChangeReasonWakeFromSleep: + RTCLog(@"Audio route changed: WakeFromSleep"); + break; + case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: + RTCLog(@"Audio route changed: NoSuitableRouteForCategory"); + break; + case AVAudioSessionRouteChangeReasonRouteConfigurationChange: + RTCLog(@"Audio route changed: RouteConfigurationChange"); + break; + } + AVAudioSessionRouteDescription* previousRoute = + notification.userInfo[AVAudioSessionRouteChangePreviousRouteKey]; + // Log previous route configuration. + RTCLog(@"Previous route: %@\nCurrent route:%@", + previousRoute, self.session.currentRoute); + [self notifyDidChangeRouteWithReason:reason previousRoute:previousRoute]; +} + +- (void)handleMediaServicesWereLost:(NSNotification *)notification { + RTCLog(@"Media services were lost."); + [self updateAudioSessionAfterEvent]; + [self notifyMediaServicesWereLost]; +} + +- (void)handleMediaServicesWereReset:(NSNotification *)notification { + RTCLog(@"Media services were reset."); + [self updateAudioSessionAfterEvent]; + [self notifyMediaServicesWereReset]; +} + +- (void)handleSilenceSecondaryAudioHintNotification:(NSNotification *)notification { + // TODO(henrika): just adding logs here for now until we know if we are ever + // see this notification and might be affected by it or if further actions + // are required. + NSNumber *typeNumber = + notification.userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey]; + AVAudioSessionSilenceSecondaryAudioHintType type = + (AVAudioSessionSilenceSecondaryAudioHintType)typeNumber.unsignedIntegerValue; + switch (type) { + case AVAudioSessionSilenceSecondaryAudioHintTypeBegin: + RTCLog(@"Another application's primary audio has started."); + break; + case AVAudioSessionSilenceSecondaryAudioHintTypeEnd: + RTCLog(@"Another application's primary audio has stopped."); + break; + } +} + +- (void)handleApplicationDidBecomeActive:(NSNotification *)notification { + BOOL isInterrupted = self.isInterrupted; + RTCLog(@"Application became active after an interruption. Treating as interruption " + "end. isInterrupted changed from %d to 0.", + isInterrupted); + if (isInterrupted) { + self.isInterrupted = NO; + [self updateAudioSessionAfterEvent]; + } + // Always treat application becoming active as an interruption end event. + [self notifyDidEndInterruptionWithShouldResumeSession:YES]; +} + +#pragma mark - Private + ++ (NSError *)lockError { + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : @"Must call lockForConfiguration before calling this method."}; + NSError *error = [[NSError alloc] initWithDomain:kRTCAudioSessionErrorDomain + code:kRTCAudioSessionErrorLockRequired + userInfo:userInfo]; + return error; +} + +- (std::vector<__weak id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)> >)delegates { + @synchronized(self) { + // Note: this returns a copy. + return _delegates; + } +} + +// TODO(tkchin): check for duplicates. +- (void)pushDelegate:(id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)>)delegate { + @synchronized(self) { + _delegates.insert(_delegates.begin(), delegate); + } +} + +- (void)removeZeroedDelegates { + @synchronized(self) { + _delegates.erase( + std::remove_if(_delegates.begin(), + _delegates.end(), + [](id delegate) -> bool { return delegate == nil; }), + _delegates.end()); + } +} + +- (int)activationCount { + return _activationCount.load(); +} + +- (int)incrementActivationCount { + RTCLog(@"Incrementing activation count."); + return _activationCount.fetch_add(1) + 1; +} + +- (NSInteger)decrementActivationCount { + RTCLog(@"Decrementing activation count."); + return _activationCount.fetch_sub(1) - 1; +} + +- (int)webRTCSessionCount { + return _webRTCSessionCount.load(); +} + +- (BOOL)canPlayOrRecord { + return !self.useManualAudio || self.isAudioEnabled; +} + +- (BOOL)isInterrupted { + @synchronized(self) { + return _isInterrupted; + } +} + +- (void)setIsInterrupted:(BOOL)isInterrupted { + @synchronized(self) { + if (_isInterrupted == isInterrupted) { + return; + } + _isInterrupted = isInterrupted; + } +} + +- (BOOL)checkLock:(NSError **)outError { + if (!mutex_locked) { + if (outError) { + *outError = [RTC_OBJC_TYPE(RTCAudioSession) lockError]; + } + return NO; + } + return YES; +} + +- (BOOL)beginWebRTCSession:(NSError **)outError { + if (outError) { + *outError = nil; + } + _webRTCSessionCount.fetch_add(1); + [self notifyDidStartPlayOrRecord]; + return YES; +} + +- (BOOL)endWebRTCSession:(NSError **)outError { + if (outError) { + *outError = nil; + } + _webRTCSessionCount.fetch_sub(1); + [self notifyDidStopPlayOrRecord]; + return YES; +} + +- (BOOL)configureWebRTCSession:(NSError **)outError { + if (outError) { + *outError = nil; + } + RTCLog(@"Configuring audio session for WebRTC."); + + // Configure the AVAudioSession and activate it. + // Provide an error even if there isn't one so we can log it. + NSError *error = nil; + RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *webRTCConfig = + [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) webRTCConfiguration]; + if (![self setConfiguration:webRTCConfig active:YES error:&error]) { + RTCLogError(@"Failed to set WebRTC audio configuration: %@", + error.localizedDescription); + // Do not call setActive:NO if setActive:YES failed. + if (outError) { + *outError = error; + } + return NO; + } + + // Ensure that the device currently supports audio input. + // TODO(tkchin): Figure out if this is really necessary. + if (!self.inputAvailable) { + RTCLogError(@"No audio input path is available!"); + [self unconfigureWebRTCSession:nil]; + if (outError) { + *outError = [self configurationErrorWithDescription:@"No input path."]; + } + return NO; + } + + // It can happen (e.g. in combination with BT devices) that the attempt to set + // the preferred sample rate for WebRTC (48kHz) fails. If so, make a new + // configuration attempt using the sample rate that worked using the active + // audio session. A typical case is that only 8 or 16kHz can be set, e.g. in + // combination with BT headsets. Using this "trick" seems to avoid a state + // where Core Audio asks for a different number of audio frames than what the + // session's I/O buffer duration corresponds to. + // TODO(henrika): this fix resolves bugs.webrtc.org/6004 but it has only been + // tested on a limited set of iOS devices and BT devices. + double sessionSampleRate = self.sampleRate; + double preferredSampleRate = webRTCConfig.sampleRate; + if (sessionSampleRate != preferredSampleRate) { + RTCLogWarning( + @"Current sample rate (%.2f) is not the preferred rate (%.2f)", + sessionSampleRate, preferredSampleRate); + if (![self setPreferredSampleRate:sessionSampleRate + error:&error]) { + RTCLogError(@"Failed to set preferred sample rate: %@", + error.localizedDescription); + if (outError) { + *outError = error; + } + } + } + + return YES; +} + +- (BOOL)unconfigureWebRTCSession:(NSError **)outError { + if (outError) { + *outError = nil; + } + RTCLog(@"Unconfiguring audio session for WebRTC."); + [self setActive:NO error:outError]; + + return YES; +} + +- (NSError *)configurationErrorWithDescription:(NSString *)description { + NSDictionary* userInfo = @{ + NSLocalizedDescriptionKey: description, + }; + return [[NSError alloc] initWithDomain:kRTCAudioSessionErrorDomain + code:kRTCAudioSessionErrorConfiguration + userInfo:userInfo]; +} + +- (void)updateAudioSessionAfterEvent { + BOOL shouldActivate = self.activationCount > 0; + AVAudioSessionSetActiveOptions options = shouldActivate ? + 0 : AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation; + NSError *error = nil; + if ([self.session setActive:shouldActivate + withOptions:options + error:&error]) { + self.isActive = shouldActivate; + } else { + RTCLogError(@"Failed to set session active to %d. Error:%@", + shouldActivate, error.localizedDescription); + } +} + +- (void)updateCanPlayOrRecord { + BOOL canPlayOrRecord = NO; + BOOL shouldNotify = NO; + @synchronized(self) { + canPlayOrRecord = !self.useManualAudio || self.isAudioEnabled; + if (_canPlayOrRecord == canPlayOrRecord) { + return; + } + _canPlayOrRecord = canPlayOrRecord; + shouldNotify = YES; + } + if (shouldNotify) { + [self notifyDidChangeCanPlayOrRecord:canPlayOrRecord]; + } +} + +- (void)audioSessionDidActivate:(AVAudioSession *)session { + if (_session != session) { + RTCLogError(@"audioSessionDidActivate called on different AVAudioSession"); + } + RTCLog(@"Audio session was externally activated."); + [self incrementActivationCount]; + self.isActive = YES; + // When a CallKit call begins, it's possible that we receive an interruption + // begin without a corresponding end. Since we know that we have an activated + // audio session at this point, just clear any saved interruption flag since + // the app may never be foregrounded during the duration of the call. + if (self.isInterrupted) { + RTCLog(@"Clearing interrupted state due to external activation."); + self.isInterrupted = NO; + } + // Treat external audio session activation as an end interruption event. + [self notifyDidEndInterruptionWithShouldResumeSession:YES]; +} + +- (void)audioSessionDidDeactivate:(AVAudioSession *)session { + if (_session != session) { + RTCLogError(@"audioSessionDidDeactivate called on different AVAudioSession"); + } + RTCLog(@"Audio session was externally deactivated."); + self.isActive = NO; + [self decrementActivationCount]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (context == (__bridge void *)RTC_OBJC_TYPE(RTCAudioSession).class) { + if (object == _session) { + NSNumber *newVolume = change[NSKeyValueChangeNewKey]; + RTCLog(@"OutputVolumeDidChange to %f", newVolume.floatValue); + [self notifyDidChangeOutputVolume:newVolume.floatValue]; + } + } else { + [super observeValueForKeyPath:keyPath + ofObject:object + change:change + context:context]; + } +} + +- (void)notifyAudioUnitStartFailedWithError:(OSStatus)error { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSession:audioUnitStartFailedWithError:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self + audioUnitStartFailedWithError:[NSError errorWithDomain:kRTCAudioSessionErrorDomain + code:error + userInfo:nil]]; + } + } +} + +- (void)notifyDidBeginInterruption { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionDidBeginInterruption:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionDidBeginInterruption:self]; + } + } +} + +- (void)notifyDidEndInterruptionWithShouldResumeSession: + (BOOL)shouldResumeSession { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionDidEndInterruption:shouldResumeSession:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionDidEndInterruption:self + shouldResumeSession:shouldResumeSession]; + } + } +} + +- (void)notifyDidChangeRouteWithReason:(AVAudioSessionRouteChangeReason)reason + previousRoute:(AVAudioSessionRouteDescription *)previousRoute { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionDidChangeRoute:reason:previousRoute:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionDidChangeRoute:self + reason:reason + previousRoute:previousRoute]; + } + } +} + +- (void)notifyMediaServicesWereLost { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionMediaServerTerminated:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionMediaServerTerminated:self]; + } + } +} + +- (void)notifyMediaServicesWereReset { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionMediaServerReset:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionMediaServerReset:self]; + } + } +} + +- (void)notifyDidChangeCanPlayOrRecord:(BOOL)canPlayOrRecord { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSession:didChangeCanPlayOrRecord:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self didChangeCanPlayOrRecord:canPlayOrRecord]; + } + } +} + +- (void)notifyDidStartPlayOrRecord { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionDidStartPlayOrRecord:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionDidStartPlayOrRecord:self]; + } + } +} + +- (void)notifyDidStopPlayOrRecord { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSessionDidStopPlayOrRecord:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSessionDidStopPlayOrRecord:self]; + } + } +} + +- (void)notifyDidChangeOutputVolume:(float)volume { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSession:didChangeOutputVolume:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self didChangeOutputVolume:volume]; + } + } +} + +- (void)notifyDidDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSession:didDetectPlayoutGlitch:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self didDetectPlayoutGlitch:totalNumberOfGlitches]; + } + } +} + +- (void)notifyWillSetActive:(BOOL)active { + for (id delegate : self.delegates) { + SEL sel = @selector(audioSession:willSetActive:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self willSetActive:active]; + } + } +} + +- (void)notifyDidSetActive:(BOOL)active { + for (id delegate : self.delegates) { + SEL sel = @selector(audioSession:didSetActive:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self didSetActive:active]; + } + } +} + +- (void)notifyFailedToSetActive:(BOOL)active error:(NSError *)error { + for (id delegate : self.delegates) { + SEL sel = @selector(audioSession:failedToSetActive:error:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self failedToSetActive:active error:error]; + } + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSessionConfiguration.h b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSessionConfiguration.h new file mode 100644 index 0000000000..4582b80557 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSessionConfiguration.h @@ -0,0 +1,48 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_EXTERN const int kRTCAudioSessionPreferredNumberOfChannels; +RTC_EXTERN const double kRTCAudioSessionHighPerformanceSampleRate; +RTC_EXTERN const double kRTCAudioSessionLowComplexitySampleRate; +RTC_EXTERN const double kRTCAudioSessionHighPerformanceIOBufferDuration; +RTC_EXTERN const double kRTCAudioSessionLowComplexityIOBufferDuration; + +// Struct to hold configuration values. +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCAudioSessionConfiguration) : NSObject + +@property(nonatomic, strong) NSString *category; +@property(nonatomic, assign) AVAudioSessionCategoryOptions categoryOptions; +@property(nonatomic, strong) NSString *mode; +@property(nonatomic, assign) double sampleRate; +@property(nonatomic, assign) NSTimeInterval ioBufferDuration; +@property(nonatomic, assign) NSInteger inputNumberOfChannels; +@property(nonatomic, assign) NSInteger outputNumberOfChannels; + +/** Initializes configuration to defaults. */ +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +/** Returns the current configuration of the audio session. */ ++ (instancetype)currentConfiguration; +/** Returns the configuration that WebRTC needs. */ ++ (instancetype)webRTCConfiguration; +/** Provide a way to override the default configuration. */ ++ (void)setWebRTCConfiguration:(RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSessionConfiguration.m b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSessionConfiguration.m new file mode 100644 index 0000000000..39e9ac13ec --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCAudioSessionConfiguration.m @@ -0,0 +1,133 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSessionConfiguration.h" +#import "RTCAudioSession.h" + +#import "helpers/RTCDispatcher.h" +#import "helpers/UIDevice+RTCDevice.h" + +// Try to use mono to save resources. Also avoids channel format conversion +// in the I/O audio unit. Initial tests have shown that it is possible to use +// mono natively for built-in microphones and for BT headsets but not for +// wired headsets. Wired headsets only support stereo as native channel format +// but it is a low cost operation to do a format conversion to mono in the +// audio unit. Hence, we will not hit a RTC_CHECK in +// VerifyAudioParametersForActiveAudioSession() for a mismatch between the +// preferred number of channels and the actual number of channels. +const int kRTCAudioSessionPreferredNumberOfChannels = 1; + +// Preferred hardware sample rate (unit is in Hertz). The client sample rate +// will be set to this value as well to avoid resampling the the audio unit's +// format converter. Note that, some devices, e.g. BT headsets, only supports +// 8000Hz as native sample rate. +const double kRTCAudioSessionHighPerformanceSampleRate = 48000.0; + +// A lower sample rate will be used for devices with only one core +// (e.g. iPhone 4). The goal is to reduce the CPU load of the application. +const double kRTCAudioSessionLowComplexitySampleRate = 16000.0; + +// Use a hardware I/O buffer size (unit is in seconds) that matches the 10ms +// size used by WebRTC. The exact actual size will differ between devices. +// Example: using 48kHz on iPhone 6 results in a native buffer size of +// ~10.6667ms or 512 audio frames per buffer. The FineAudioBuffer instance will +// take care of any buffering required to convert between native buffers and +// buffers used by WebRTC. It is beneficial for the performance if the native +// size is as an even multiple of 10ms as possible since it results in "clean" +// callback sequence without bursts of callbacks back to back. +const double kRTCAudioSessionHighPerformanceIOBufferDuration = 0.02; + +// Use a larger buffer size on devices with only one core (e.g. iPhone 4). +// It will result in a lower CPU consumption at the cost of a larger latency. +// The size of 60ms is based on instrumentation that shows a significant +// reduction in CPU load compared with 10ms on low-end devices. +// TODO(henrika): monitor this size and determine if it should be modified. +const double kRTCAudioSessionLowComplexityIOBufferDuration = 0.06; + +static RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *gWebRTCConfiguration = nil; + +@implementation RTC_OBJC_TYPE (RTCAudioSessionConfiguration) + +@synthesize category = _category; +@synthesize categoryOptions = _categoryOptions; +@synthesize mode = _mode; +@synthesize sampleRate = _sampleRate; +@synthesize ioBufferDuration = _ioBufferDuration; +@synthesize inputNumberOfChannels = _inputNumberOfChannels; +@synthesize outputNumberOfChannels = _outputNumberOfChannels; + +- (instancetype)init { + if (self = [super init]) { + // Use a category which supports simultaneous recording and playback. + // By default, using this category implies that our app’s audio is + // nonmixable, hence activating the session will interrupt any other + // audio sessions which are also nonmixable. + _category = AVAudioSessionCategoryPlayAndRecord; + _categoryOptions = AVAudioSessionCategoryOptionAllowBluetooth; + + // Specify mode for two-way voice communication (e.g. VoIP). + _mode = AVAudioSessionModeVoiceChat; + + // Set the session's sample rate or the hardware sample rate. + // It is essential that we use the same sample rate as stream format + // to ensure that the I/O unit does not have to do sample rate conversion. + // Set the preferred audio I/O buffer duration, in seconds. + NSUInteger processorCount = [NSProcessInfo processInfo].processorCount; + // Use best sample rate and buffer duration if the CPU has more than one + // core. + if (processorCount > 1 && [UIDevice deviceType] != RTCDeviceTypeIPhone4S) { + _sampleRate = kRTCAudioSessionHighPerformanceSampleRate; + _ioBufferDuration = kRTCAudioSessionHighPerformanceIOBufferDuration; + } else { + _sampleRate = kRTCAudioSessionLowComplexitySampleRate; + _ioBufferDuration = kRTCAudioSessionLowComplexityIOBufferDuration; + } + + // We try to use mono in both directions to save resources and format + // conversions in the audio unit. Some devices does only support stereo; + // e.g. wired headset on iPhone 6. + // TODO(henrika): add support for stereo if needed. + _inputNumberOfChannels = kRTCAudioSessionPreferredNumberOfChannels; + _outputNumberOfChannels = kRTCAudioSessionPreferredNumberOfChannels; + } + return self; +} + ++ (void)initialize { + gWebRTCConfiguration = [[self alloc] init]; +} + ++ (instancetype)currentConfiguration { + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *config = + [[RTC_OBJC_TYPE(RTCAudioSessionConfiguration) alloc] init]; + config.category = session.category; + config.categoryOptions = session.categoryOptions; + config.mode = session.mode; + config.sampleRate = session.sampleRate; + config.ioBufferDuration = session.IOBufferDuration; + config.inputNumberOfChannels = session.inputNumberOfChannels; + config.outputNumberOfChannels = session.outputNumberOfChannels; + return config; +} + ++ (instancetype)webRTCConfiguration { + @synchronized(self) { + return (RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)gWebRTCConfiguration; + } +} + ++ (void)setWebRTCConfiguration:(RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *)configuration { + @synchronized(self) { + gWebRTCConfiguration = configuration; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.h b/third_party/libwebrtc/sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.h new file mode 100644 index 0000000000..6a75f01479 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.h @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCAudioSession.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace webrtc { +class AudioSessionObserver; +} + +/** Adapter that forwards RTCAudioSessionDelegate calls to the appropriate + * methods on the AudioSessionObserver. + */ +@interface RTCNativeAudioSessionDelegateAdapter : NSObject <RTC_OBJC_TYPE (RTCAudioSessionDelegate)> + +- (instancetype)init NS_UNAVAILABLE; + +/** `observer` is a raw pointer and should be kept alive + * for this object's lifetime. + */ +- (instancetype)initWithObserver:(webrtc::AudioSessionObserver *)observer NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.mm b/third_party/libwebrtc/sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.mm new file mode 100644 index 0000000000..daddf314a4 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/audio/RTCNativeAudioSessionDelegateAdapter.mm @@ -0,0 +1,89 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNativeAudioSessionDelegateAdapter.h" + +#include "sdk/objc/native/src/audio/audio_session_observer.h" + +#import "base/RTCLogging.h" + +@implementation RTCNativeAudioSessionDelegateAdapter { + webrtc::AudioSessionObserver *_observer; +} + +- (instancetype)initWithObserver:(webrtc::AudioSessionObserver *)observer { + RTC_DCHECK(observer); + if (self = [super init]) { + _observer = observer; + } + return self; +} + +#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate) + +- (void)audioSessionDidBeginInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session { + _observer->OnInterruptionBegin(); +} + +- (void)audioSessionDidEndInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session + shouldResumeSession:(BOOL)shouldResumeSession { + _observer->OnInterruptionEnd(); +} + +- (void)audioSessionDidChangeRoute:(RTC_OBJC_TYPE(RTCAudioSession) *)session + reason:(AVAudioSessionRouteChangeReason)reason + previousRoute:(AVAudioSessionRouteDescription *)previousRoute { + switch (reason) { + case AVAudioSessionRouteChangeReasonUnknown: + case AVAudioSessionRouteChangeReasonNewDeviceAvailable: + case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: + case AVAudioSessionRouteChangeReasonCategoryChange: + // It turns out that we see a category change (at least in iOS 9.2) + // when making a switch from a BT device to e.g. Speaker using the + // iOS Control Center and that we therefore must check if the sample + // rate has changed. And if so is the case, restart the audio unit. + case AVAudioSessionRouteChangeReasonOverride: + case AVAudioSessionRouteChangeReasonWakeFromSleep: + case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: + _observer->OnValidRouteChange(); + break; + case AVAudioSessionRouteChangeReasonRouteConfigurationChange: + // The set of input and output ports has not changed, but their + // configuration has, e.g., a port’s selected data source has + // changed. Ignore this type of route change since we are focusing + // on detecting headset changes. + RTCLog(@"Ignoring RouteConfigurationChange"); + break; + } +} + +- (void)audioSessionMediaServerTerminated:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSessionMediaServerReset:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)session + didChangeCanPlayOrRecord:(BOOL)canPlayOrRecord { + _observer->OnCanPlayOrRecordChange(canPlayOrRecord); +} + +- (void)audioSessionDidStartPlayOrRecord:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSessionDidStopPlayOrRecord:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + didChangeOutputVolume:(float)outputVolume { + _observer->OnChangedOutputVolume(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/capturer/RTCCameraVideoCapturer.h b/third_party/libwebrtc/sdk/objc/components/capturer/RTCCameraVideoCapturer.h new file mode 100644 index 0000000000..370bfa70f0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/capturer/RTCCameraVideoCapturer.h @@ -0,0 +1,56 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoCapturer.h" + +NS_ASSUME_NONNULL_BEGIN + +RTC_OBJC_EXPORT +// Camera capture that implements RTCVideoCapturer. Delivers frames to a +// RTCVideoCapturerDelegate (usually RTCVideoSource). +NS_EXTENSION_UNAVAILABLE_IOS("Camera not available in app extensions.") +@interface RTC_OBJC_TYPE (RTCCameraVideoCapturer) : RTC_OBJC_TYPE(RTCVideoCapturer) + +// Capture session that is used for capturing. Valid from initialization to dealloc. +@property(readonly, nonatomic) AVCaptureSession *captureSession; + +// Returns list of available capture devices that support video capture. ++ (NSArray<AVCaptureDevice *> *)captureDevices; +// Returns list of formats that are supported by this class for this device. ++ (NSArray<AVCaptureDeviceFormat *> *)supportedFormatsForDevice:(AVCaptureDevice *)device; + +// Returns the most efficient supported output pixel format for this capturer. +- (FourCharCode)preferredOutputPixelFormat; + +// Starts the capture session asynchronously and notifies callback on completion. +// The device will capture video in the format given in the `format` parameter. If the pixel format +// in `format` is supported by the WebRTC pipeline, the same pixel format will be used for the +// output. Otherwise, the format returned by `preferredOutputPixelFormat` will be used. +- (void)startCaptureWithDevice:(AVCaptureDevice *)device + format:(AVCaptureDeviceFormat *)format + fps:(NSInteger)fps + completionHandler:(nullable void (^)(NSError *_Nullable))completionHandler; +// Stops the capture session asynchronously and notifies callback on completion. +- (void)stopCaptureWithCompletionHandler:(nullable void (^)(void))completionHandler; + +// Starts the capture session asynchronously. +- (void)startCaptureWithDevice:(AVCaptureDevice *)device + format:(AVCaptureDeviceFormat *)format + fps:(NSInteger)fps; +// Stops the capture session asynchronously. +- (void)stopCapture; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/capturer/RTCCameraVideoCapturer.m b/third_party/libwebrtc/sdk/objc/components/capturer/RTCCameraVideoCapturer.m new file mode 100644 index 0000000000..98d3cf9f45 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/capturer/RTCCameraVideoCapturer.m @@ -0,0 +1,535 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCCameraVideoCapturer.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +#if TARGET_OS_IPHONE +#import "helpers/UIDevice+RTCDevice.h" +#endif + +#import "helpers/AVCaptureSession+DevicePosition.h" +#import "helpers/RTCDispatcher+Private.h" +#include "rtc_base/system/gcd_helpers.h" + +const int64_t kNanosecondsPerSecond = 1000000000; + +@interface RTC_OBJC_TYPE (RTCCameraVideoCapturer) +()<AVCaptureVideoDataOutputSampleBufferDelegate> @property(nonatomic, + readonly) dispatch_queue_t frameQueue; +@property(nonatomic, strong) AVCaptureDevice *currentDevice; +@property(nonatomic, assign) BOOL hasRetriedOnFatalError; +@property(nonatomic, assign) BOOL isRunning; +// Will the session be running once all asynchronous operations have been completed? +@property(nonatomic, assign) BOOL willBeRunning; +@end + +@implementation RTC_OBJC_TYPE (RTCCameraVideoCapturer) { + AVCaptureVideoDataOutput *_videoDataOutput; + AVCaptureSession *_captureSession; + FourCharCode _preferredOutputPixelFormat; + FourCharCode _outputPixelFormat; + RTCVideoRotation _rotation; +#if TARGET_OS_IPHONE + UIDeviceOrientation _orientation; + BOOL _generatingOrientationNotifications; +#endif +} + +@synthesize frameQueue = _frameQueue; +@synthesize captureSession = _captureSession; +@synthesize currentDevice = _currentDevice; +@synthesize hasRetriedOnFatalError = _hasRetriedOnFatalError; +@synthesize isRunning = _isRunning; +@synthesize willBeRunning = _willBeRunning; + +- (instancetype)init { + return [self initWithDelegate:nil captureSession:[[AVCaptureSession alloc] init]]; +} + +- (instancetype)initWithDelegate:(__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)delegate { + return [self initWithDelegate:delegate captureSession:[[AVCaptureSession alloc] init]]; +} + +// This initializer is used for testing. +- (instancetype)initWithDelegate:(__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)delegate + captureSession:(AVCaptureSession *)captureSession { + if (self = [super initWithDelegate:delegate]) { + // Create the capture session and all relevant inputs and outputs. We need + // to do this in init because the application may want the capture session + // before we start the capturer for e.g. AVCapturePreviewLayer. All objects + // created here are retained until dealloc and never recreated. + if (![self setupCaptureSession:captureSession]) { + return nil; + } + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; +#if TARGET_OS_IPHONE + _orientation = UIDeviceOrientationPortrait; + _rotation = RTCVideoRotation_90; + [center addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + [center addObserver:self + selector:@selector(handleCaptureSessionInterruption:) + name:AVCaptureSessionWasInterruptedNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleCaptureSessionInterruptionEnded:) + name:AVCaptureSessionInterruptionEndedNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleApplicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:[UIApplication sharedApplication]]; +#endif + [center addObserver:self + selector:@selector(handleCaptureSessionRuntimeError:) + name:AVCaptureSessionRuntimeErrorNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleCaptureSessionDidStartRunning:) + name:AVCaptureSessionDidStartRunningNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleCaptureSessionDidStopRunning:) + name:AVCaptureSessionDidStopRunningNotification + object:_captureSession]; + } + return self; +} + +- (void)dealloc { + NSAssert(!_willBeRunning, + @"Session was still running in RTC_OBJC_TYPE(RTCCameraVideoCapturer) dealloc. Forgot to " + @"call stopCapture?"); + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + ++ (NSArray<AVCaptureDevice *> *)captureDevices { +#if defined(WEBRTC_IOS) && defined(__IPHONE_10_0) && \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0 + AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession + discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ] + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified]; + return session.devices; +#else + return [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; +#endif +} + ++ (NSArray<AVCaptureDeviceFormat *> *)supportedFormatsForDevice:(AVCaptureDevice *)device { + // Support opening the device in any format. We make sure it's converted to a format we + // can handle, if needed, in the method `-setupVideoDataOutput`. + return device.formats; +} + +- (FourCharCode)preferredOutputPixelFormat { + return _preferredOutputPixelFormat; +} + +- (void)startCaptureWithDevice:(AVCaptureDevice *)device + format:(AVCaptureDeviceFormat *)format + fps:(NSInteger)fps { + [self startCaptureWithDevice:device format:format fps:fps completionHandler:nil]; +} + +- (void)stopCapture { + [self stopCaptureWithCompletionHandler:nil]; +} + +- (void)startCaptureWithDevice:(AVCaptureDevice *)device + format:(AVCaptureDeviceFormat *)format + fps:(NSInteger)fps + completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler { + _willBeRunning = YES; + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + RTCLogInfo("startCaptureWithDevice %@ @ %ld fps", format, (long)fps); + +#if TARGET_OS_IPHONE + dispatch_async(dispatch_get_main_queue(), ^{ + if (!self->_generatingOrientationNotifications) { + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + self->_generatingOrientationNotifications = YES; + } + }); +#endif + + self.currentDevice = device; + + NSError *error = nil; + if (![self.currentDevice lockForConfiguration:&error]) { + RTCLogError(@"Failed to lock device %@. Error: %@", + self.currentDevice, + error.userInfo); + if (completionHandler) { + completionHandler(error); + } + self.willBeRunning = NO; + return; + } + [self reconfigureCaptureSessionInput]; + [self updateOrientation]; + [self updateDeviceCaptureFormat:format fps:fps]; + [self updateVideoDataOutputPixelFormat:format]; + [self.captureSession startRunning]; + [self.currentDevice unlockForConfiguration]; + self.isRunning = YES; + if (completionHandler) { + completionHandler(nil); + } + }]; +} + +- (void)stopCaptureWithCompletionHandler:(nullable void (^)(void))completionHandler { + _willBeRunning = NO; + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + RTCLogInfo("Stop"); + self.currentDevice = nil; + for (AVCaptureDeviceInput *oldInput in [self.captureSession.inputs copy]) { + [self.captureSession removeInput:oldInput]; + } + [self.captureSession stopRunning]; + +#if TARGET_OS_IPHONE + dispatch_async(dispatch_get_main_queue(), ^{ + if (self->_generatingOrientationNotifications) { + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + self->_generatingOrientationNotifications = NO; + } + }); +#endif + self.isRunning = NO; + if (completionHandler) { + completionHandler(); + } + }]; +} + +#pragma mark iOS notifications + +#if TARGET_OS_IPHONE +- (void)deviceOrientationDidChange:(NSNotification *)notification { + [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + [self updateOrientation]; + }]; +} +#endif + +#pragma mark AVCaptureVideoDataOutputSampleBufferDelegate + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection { + NSParameterAssert(captureOutput == _videoDataOutput); + + if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(sampleBuffer) || + !CMSampleBufferDataIsReady(sampleBuffer)) { + return; + } + + CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + if (pixelBuffer == nil) { + return; + } + +#if TARGET_OS_IPHONE + // Default to portrait orientation on iPhone. + BOOL usingFrontCamera = NO; + // Check the image's EXIF for the camera the image came from as the image could have been + // delayed as we set alwaysDiscardsLateVideoFrames to NO. + AVCaptureDevicePosition cameraPosition = + [AVCaptureSession devicePositionForSampleBuffer:sampleBuffer]; + if (cameraPosition != AVCaptureDevicePositionUnspecified) { + usingFrontCamera = AVCaptureDevicePositionFront == cameraPosition; + } else { + AVCaptureDeviceInput *deviceInput = + (AVCaptureDeviceInput *)((AVCaptureInputPort *)connection.inputPorts.firstObject).input; + usingFrontCamera = AVCaptureDevicePositionFront == deviceInput.device.position; + } + switch (_orientation) { + case UIDeviceOrientationPortrait: + _rotation = RTCVideoRotation_90; + break; + case UIDeviceOrientationPortraitUpsideDown: + _rotation = RTCVideoRotation_270; + break; + case UIDeviceOrientationLandscapeLeft: + _rotation = usingFrontCamera ? RTCVideoRotation_180 : RTCVideoRotation_0; + break; + case UIDeviceOrientationLandscapeRight: + _rotation = usingFrontCamera ? RTCVideoRotation_0 : RTCVideoRotation_180; + break; + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + case UIDeviceOrientationUnknown: + // Ignore. + break; + } +#else + // No rotation on Mac. + _rotation = RTCVideoRotation_0; +#endif + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBuffer]; + int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) * + kNanosecondsPerSecond; + RTC_OBJC_TYPE(RTCVideoFrame) *videoFrame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:rtcPixelBuffer + rotation:_rotation + timeStampNs:timeStampNs]; + [self.delegate capturer:self didCaptureVideoFrame:videoFrame]; +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection { +#if TARGET_OS_IPHONE + CFStringRef droppedReason = + CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_DroppedFrameReason, nil); +#else + // DroppedFrameReason unavailable on macOS. + CFStringRef droppedReason = nil; +#endif + RTCLogError(@"Dropped sample buffer. Reason: %@", (__bridge NSString *)droppedReason); +} + +#pragma mark - AVCaptureSession notifications + +- (void)handleCaptureSessionInterruption:(NSNotification *)notification { + NSString *reasonString = nil; +#if TARGET_OS_IPHONE + NSNumber *reason = notification.userInfo[AVCaptureSessionInterruptionReasonKey]; + if (reason) { + switch (reason.intValue) { + case AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground: + reasonString = @"VideoDeviceNotAvailableInBackground"; + break; + case AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient: + reasonString = @"AudioDeviceInUseByAnotherClient"; + break; + case AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient: + reasonString = @"VideoDeviceInUseByAnotherClient"; + break; + case AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps: + reasonString = @"VideoDeviceNotAvailableWithMultipleForegroundApps"; + break; + } + } +#endif + RTCLog(@"Capture session interrupted: %@", reasonString); +} + +- (void)handleCaptureSessionInterruptionEnded:(NSNotification *)notification { + RTCLog(@"Capture session interruption ended."); +} + +- (void)handleCaptureSessionRuntimeError:(NSNotification *)notification { + NSError *error = [notification.userInfo objectForKey:AVCaptureSessionErrorKey]; + RTCLogError(@"Capture session runtime error: %@", error); + + [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ +#if TARGET_OS_IPHONE + if (error.code == AVErrorMediaServicesWereReset) { + [self handleNonFatalError]; + } else { + [self handleFatalError]; + } +#else + [self handleFatalError]; +#endif + }]; +} + +- (void)handleCaptureSessionDidStartRunning:(NSNotification *)notification { + RTCLog(@"Capture session started."); + + [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + // If we successfully restarted after an unknown + // error, allow future retries on fatal errors. + self.hasRetriedOnFatalError = NO; + }]; +} + +- (void)handleCaptureSessionDidStopRunning:(NSNotification *)notification { + RTCLog(@"Capture session stopped."); +} + +- (void)handleFatalError { + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + if (!self.hasRetriedOnFatalError) { + RTCLogWarning(@"Attempting to recover from fatal capture error."); + [self handleNonFatalError]; + self.hasRetriedOnFatalError = YES; + } else { + RTCLogError(@"Previous fatal error recovery failed."); + } + }]; +} + +- (void)handleNonFatalError { + [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + RTCLog(@"Restarting capture session after error."); + if (self.isRunning) { + [self.captureSession startRunning]; + } + }]; +} + +#if TARGET_OS_IPHONE + +#pragma mark - UIApplication notifications + +- (void)handleApplicationDidBecomeActive:(NSNotification *)notification { + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + if (self.isRunning && !self.captureSession.isRunning) { + RTCLog(@"Restarting capture session on active."); + [self.captureSession startRunning]; + } + }]; +} + +#endif // TARGET_OS_IPHONE + +#pragma mark - Private + +- (dispatch_queue_t)frameQueue { + if (!_frameQueue) { + _frameQueue = RTCDispatchQueueCreateWithTarget( + "org.webrtc.cameravideocapturer.video", + DISPATCH_QUEUE_SERIAL, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); + } + return _frameQueue; +} + +- (BOOL)setupCaptureSession:(AVCaptureSession *)captureSession { + NSAssert(_captureSession == nil, @"Setup capture session called twice."); + _captureSession = captureSession; +#if defined(WEBRTC_IOS) + _captureSession.sessionPreset = AVCaptureSessionPresetInputPriority; + _captureSession.usesApplicationAudioSession = NO; +#endif + [self setupVideoDataOutput]; + // Add the output. + if (![_captureSession canAddOutput:_videoDataOutput]) { + RTCLogError(@"Video data output unsupported."); + return NO; + } + [_captureSession addOutput:_videoDataOutput]; + + return YES; +} + +- (void)setupVideoDataOutput { + NSAssert(_videoDataOutput == nil, @"Setup video data output called twice."); + AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + + // `videoDataOutput.availableVideoCVPixelFormatTypes` returns the pixel formats supported by the + // device with the most efficient output format first. Find the first format that we support. + NSSet<NSNumber *> *supportedPixelFormats = + [RTC_OBJC_TYPE(RTCCVPixelBuffer) supportedPixelFormats]; + NSMutableOrderedSet *availablePixelFormats = + [NSMutableOrderedSet orderedSetWithArray:videoDataOutput.availableVideoCVPixelFormatTypes]; + [availablePixelFormats intersectSet:supportedPixelFormats]; + NSNumber *pixelFormat = availablePixelFormats.firstObject; + NSAssert(pixelFormat, @"Output device has no supported formats."); + + _preferredOutputPixelFormat = [pixelFormat unsignedIntValue]; + _outputPixelFormat = _preferredOutputPixelFormat; + videoDataOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : pixelFormat}; + videoDataOutput.alwaysDiscardsLateVideoFrames = NO; + [videoDataOutput setSampleBufferDelegate:self queue:self.frameQueue]; + _videoDataOutput = videoDataOutput; +} + +- (void)updateVideoDataOutputPixelFormat:(AVCaptureDeviceFormat *)format { + FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(format.formatDescription); + if (![[RTC_OBJC_TYPE(RTCCVPixelBuffer) supportedPixelFormats] containsObject:@(mediaSubType)]) { + mediaSubType = _preferredOutputPixelFormat; + } + + if (mediaSubType != _outputPixelFormat) { + _outputPixelFormat = mediaSubType; + } + + // Update videoSettings with dimensions, as some virtual cameras, e.g. Snap Camera, may not work + // otherwise. + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + _videoDataOutput.videoSettings = @{ + (id)kCVPixelBufferWidthKey : @(dimensions.width), + (id)kCVPixelBufferHeightKey : @(dimensions.height), + (id)kCVPixelBufferPixelFormatTypeKey : @(_outputPixelFormat), + }; +} + +#pragma mark - Private, called inside capture queue + +- (void)updateDeviceCaptureFormat:(AVCaptureDeviceFormat *)format fps:(NSInteger)fps { + NSAssert([RTC_OBJC_TYPE(RTCDispatcher) isOnQueueForType:RTCDispatcherTypeCaptureSession], + @"updateDeviceCaptureFormat must be called on the capture queue."); + @try { + _currentDevice.activeFormat = format; + _currentDevice.activeVideoMinFrameDuration = CMTimeMake(1, fps); + } @catch (NSException *exception) { + RTCLogError(@"Failed to set active format!\n User info:%@", exception.userInfo); + return; + } +} + +- (void)reconfigureCaptureSessionInput { + NSAssert([RTC_OBJC_TYPE(RTCDispatcher) isOnQueueForType:RTCDispatcherTypeCaptureSession], + @"reconfigureCaptureSessionInput must be called on the capture queue."); + NSError *error = nil; + AVCaptureDeviceInput *input = + [AVCaptureDeviceInput deviceInputWithDevice:_currentDevice error:&error]; + if (!input) { + RTCLogError(@"Failed to create front camera input: %@", error.localizedDescription); + return; + } + [_captureSession beginConfiguration]; + for (AVCaptureDeviceInput *oldInput in [_captureSession.inputs copy]) { + [_captureSession removeInput:oldInput]; + } + if ([_captureSession canAddInput:input]) { + [_captureSession addInput:input]; + } else { + RTCLogError(@"Cannot add camera as an input to the session."); + } + [_captureSession commitConfiguration]; +} + +- (void)updateOrientation { + NSAssert([RTC_OBJC_TYPE(RTCDispatcher) isOnQueueForType:RTCDispatcherTypeCaptureSession], + @"updateOrientation must be called on the capture queue."); +#if TARGET_OS_IPHONE + _orientation = [UIDevice currentDevice].orientation; +#endif +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/capturer/RTCFileVideoCapturer.h b/third_party/libwebrtc/sdk/objc/components/capturer/RTCFileVideoCapturer.h new file mode 100644 index 0000000000..19262c64cf --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/capturer/RTCFileVideoCapturer.h @@ -0,0 +1,51 @@ +/* + * Copyright 2017 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCVideoCapturer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Error passing block. + */ +typedef void (^RTCFileVideoCapturerErrorBlock)(NSError *error); + +/** + * Captures buffers from bundled video file. + * + * See @c RTCVideoCapturer for more info on capturers. + */ +RTC_OBJC_EXPORT + +NS_CLASS_AVAILABLE_IOS(10) +@interface RTC_OBJC_TYPE (RTCFileVideoCapturer) : RTC_OBJC_TYPE(RTCVideoCapturer) + +/** + * Starts asynchronous capture of frames from video file. + * + * Capturing is not started if error occurs. Underlying error will be + * relayed in the errorBlock if one is provided. + * Successfully captured video frames will be passed to the delegate. + * + * @param nameOfFile The name of the bundled video file to be read. + * @errorBlock block to be executed upon error. + */ +- (void)startCapturingFromFileNamed:(NSString *)nameOfFile + onError:(__nullable RTCFileVideoCapturerErrorBlock)errorBlock; + +/** + * Immediately stops capture. + */ +- (void)stopCapture; +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/capturer/RTCFileVideoCapturer.m b/third_party/libwebrtc/sdk/objc/components/capturer/RTCFileVideoCapturer.m new file mode 100644 index 0000000000..bcf1506259 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/capturer/RTCFileVideoCapturer.m @@ -0,0 +1,215 @@ +/** + * Copyright 2017 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCFileVideoCapturer.h" + +#import "base/RTCLogging.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#include "rtc_base/system/gcd_helpers.h" + +NSString *const kRTCFileVideoCapturerErrorDomain = + @"org.webrtc.RTC_OBJC_TYPE(RTCFileVideoCapturer)"; + +typedef NS_ENUM(NSInteger, RTCFileVideoCapturerErrorCode) { + RTCFileVideoCapturerErrorCode_CapturerRunning = 2000, + RTCFileVideoCapturerErrorCode_FileNotFound +}; + +typedef NS_ENUM(NSInteger, RTCFileVideoCapturerStatus) { + RTCFileVideoCapturerStatusNotInitialized, + RTCFileVideoCapturerStatusStarted, + RTCFileVideoCapturerStatusStopped +}; + +@interface RTC_OBJC_TYPE (RTCFileVideoCapturer) +() @property(nonatomic, assign) CMTime lastPresentationTime; +@property(nonatomic, strong) NSURL *fileURL; +@end + +@implementation RTC_OBJC_TYPE (RTCFileVideoCapturer) { + AVAssetReader *_reader; + AVAssetReaderTrackOutput *_outTrack; + RTCFileVideoCapturerStatus _status; + dispatch_queue_t _frameQueue; +} + +@synthesize lastPresentationTime = _lastPresentationTime; +@synthesize fileURL = _fileURL; + +- (void)startCapturingFromFileNamed:(NSString *)nameOfFile + onError:(RTCFileVideoCapturerErrorBlock)errorBlock { + if (_status == RTCFileVideoCapturerStatusStarted) { + NSError *error = + [NSError errorWithDomain:kRTCFileVideoCapturerErrorDomain + code:RTCFileVideoCapturerErrorCode_CapturerRunning + userInfo:@{NSUnderlyingErrorKey : @"Capturer has been started."}]; + + errorBlock(error); + return; + } else { + _status = RTCFileVideoCapturerStatusStarted; + } + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSString *pathForFile = [self pathForFileName:nameOfFile]; + if (!pathForFile) { + NSString *errorString = + [NSString stringWithFormat:@"File %@ not found in bundle", nameOfFile]; + NSError *error = [NSError errorWithDomain:kRTCFileVideoCapturerErrorDomain + code:RTCFileVideoCapturerErrorCode_FileNotFound + userInfo:@{NSUnderlyingErrorKey : errorString}]; + errorBlock(error); + return; + } + + self.lastPresentationTime = CMTimeMake(0, 0); + + self.fileURL = [NSURL fileURLWithPath:pathForFile]; + [self setupReaderOnError:errorBlock]; + }); +} + +- (void)setupReaderOnError:(RTCFileVideoCapturerErrorBlock)errorBlock { + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:_fileURL options:nil]; + + NSArray *allTracks = [asset tracksWithMediaType:AVMediaTypeVideo]; + NSError *error = nil; + + _reader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; + if (error) { + errorBlock(error); + return; + } + + NSDictionary *options = @{ + (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) + }; + _outTrack = + [[AVAssetReaderTrackOutput alloc] initWithTrack:allTracks.firstObject outputSettings:options]; + [_reader addOutput:_outTrack]; + + [_reader startReading]; + RTCLog(@"File capturer started reading"); + [self readNextBuffer]; +} +- (void)stopCapture { + _status = RTCFileVideoCapturerStatusStopped; + RTCLog(@"File capturer stopped."); +} + +#pragma mark - Private + +- (nullable NSString *)pathForFileName:(NSString *)fileName { + NSArray *nameComponents = [fileName componentsSeparatedByString:@"."]; + if (nameComponents.count != 2) { + return nil; + } + + NSString *path = + [[NSBundle mainBundle] pathForResource:nameComponents[0] ofType:nameComponents[1]]; + return path; +} + +- (dispatch_queue_t)frameQueue { + if (!_frameQueue) { + _frameQueue = RTCDispatchQueueCreateWithTarget( + "org.webrtc.filecapturer.video", + DISPATCH_QUEUE_SERIAL, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); + } + return _frameQueue; +} + +- (void)readNextBuffer { + if (_status == RTCFileVideoCapturerStatusStopped) { + [_reader cancelReading]; + _reader = nil; + return; + } + + if (_reader.status == AVAssetReaderStatusCompleted) { + [_reader cancelReading]; + _reader = nil; + [self setupReaderOnError:nil]; + return; + } + + CMSampleBufferRef sampleBuffer = [_outTrack copyNextSampleBuffer]; + if (!sampleBuffer) { + [self readNextBuffer]; + return; + } + if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(sampleBuffer) || + !CMSampleBufferDataIsReady(sampleBuffer)) { + CFRelease(sampleBuffer); + [self readNextBuffer]; + return; + } + + [self publishSampleBuffer:sampleBuffer]; +} + +- (void)publishSampleBuffer:(CMSampleBufferRef)sampleBuffer { + CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); + Float64 presentationDifference = + CMTimeGetSeconds(CMTimeSubtract(presentationTime, _lastPresentationTime)); + _lastPresentationTime = presentationTime; + int64_t presentationDifferenceRound = lroundf(presentationDifference * NSEC_PER_SEC); + + __block dispatch_source_t timer = [self createStrictTimer]; + // Strict timer that will fire `presentationDifferenceRound` ns from now and never again. + dispatch_source_set_timer(timer, + dispatch_time(DISPATCH_TIME_NOW, presentationDifferenceRound), + DISPATCH_TIME_FOREVER, + 0); + dispatch_source_set_event_handler(timer, ^{ + dispatch_source_cancel(timer); + timer = nil; + + CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + if (!pixelBuffer) { + CFRelease(sampleBuffer); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self readNextBuffer]; + }); + return; + } + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBuffer]; + NSTimeInterval timeStampSeconds = CACurrentMediaTime(); + int64_t timeStampNs = lroundf(timeStampSeconds * NSEC_PER_SEC); + RTC_OBJC_TYPE(RTCVideoFrame) *videoFrame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:rtcPixelBuffer + rotation:0 + timeStampNs:timeStampNs]; + CFRelease(sampleBuffer); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self readNextBuffer]; + }); + + [self.delegate capturer:self didCaptureVideoFrame:videoFrame]; + }); + dispatch_activate(timer); +} + +- (dispatch_source_t)createStrictTimer { + dispatch_source_t timer = dispatch_source_create( + DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, [self frameQueue]); + return timer; +} + +- (void)dealloc { + [self stopCapture]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor+Private.h b/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor+Private.h new file mode 100644 index 0000000000..b5c786be18 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNetworkMonitor.h" + +#include "sdk/objc/native/src/network_monitor_observer.h" + +@interface RTCNetworkMonitor () + +/** `observer` is a raw pointer and should be kept alive + * for this object's lifetime. + */ +- (instancetype)initWithObserver:(webrtc::NetworkMonitorObserver *)observer + NS_DESIGNATED_INITIALIZER; + +/** Stops the receiver from posting updates to `observer`. */ +- (void)stop; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor.h b/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor.h new file mode 100644 index 0000000000..21d22f5463 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor.h @@ -0,0 +1,24 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** Listens for NWPathMonitor updates and forwards the results to a C++ + * observer. + */ +@interface RTCNetworkMonitor : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor.mm b/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor.mm new file mode 100644 index 0000000000..7e75b2b4c0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/network/RTCNetworkMonitor.mm @@ -0,0 +1,126 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNetworkMonitor+Private.h" + +#import <Network/Network.h> + +#import "base/RTCLogging.h" +#import "helpers/RTCDispatcher+Private.h" + +#include "rtc_base/string_utils.h" + +namespace { + +rtc::AdapterType AdapterTypeFromInterfaceType(nw_interface_type_t interfaceType) { + rtc::AdapterType adapterType = rtc::ADAPTER_TYPE_UNKNOWN; + switch (interfaceType) { + case nw_interface_type_other: + adapterType = rtc::ADAPTER_TYPE_UNKNOWN; + break; + case nw_interface_type_wifi: + adapterType = rtc::ADAPTER_TYPE_WIFI; + break; + case nw_interface_type_cellular: + adapterType = rtc::ADAPTER_TYPE_CELLULAR; + break; + case nw_interface_type_wired: + adapterType = rtc::ADAPTER_TYPE_ETHERNET; + break; + case nw_interface_type_loopback: + adapterType = rtc::ADAPTER_TYPE_LOOPBACK; + break; + default: + adapterType = rtc::ADAPTER_TYPE_UNKNOWN; + break; + } + return adapterType; +} + +} // namespace + +@implementation RTCNetworkMonitor { + webrtc::NetworkMonitorObserver *_observer; + nw_path_monitor_t _pathMonitor; + dispatch_queue_t _monitorQueue; +} + +- (instancetype)initWithObserver:(webrtc::NetworkMonitorObserver *)observer { + RTC_DCHECK(observer); + if (self = [super init]) { + _observer = observer; + if (@available(iOS 12, *)) { + _pathMonitor = nw_path_monitor_create(); + if (_pathMonitor == nil) { + RTCLog(@"nw_path_monitor_create failed."); + return nil; + } + RTCLog(@"NW path monitor created."); + __weak RTCNetworkMonitor *weakSelf = self; + nw_path_monitor_set_update_handler(_pathMonitor, ^(nw_path_t path) { + if (weakSelf == nil) { + return; + } + RTCNetworkMonitor *strongSelf = weakSelf; + RTCLog(@"NW path monitor: updated."); + nw_path_status_t status = nw_path_get_status(path); + if (status == nw_path_status_invalid) { + RTCLog(@"NW path monitor status: invalid."); + } else if (status == nw_path_status_unsatisfied) { + RTCLog(@"NW path monitor status: unsatisfied."); + } else if (status == nw_path_status_satisfied) { + RTCLog(@"NW path monitor status: satisfied."); + } else if (status == nw_path_status_satisfiable) { + RTCLog(@"NW path monitor status: satisfiable."); + } + std::map<std::string, rtc::AdapterType, rtc::AbslStringViewCmp> *map = + new std::map<std::string, rtc::AdapterType, rtc::AbslStringViewCmp>(); + nw_path_enumerate_interfaces( + path, (nw_path_enumerate_interfaces_block_t) ^ (nw_interface_t interface) { + const char *name = nw_interface_get_name(interface); + nw_interface_type_t interfaceType = nw_interface_get_type(interface); + RTCLog(@"NW path monitor available interface: %s", name); + rtc::AdapterType adapterType = AdapterTypeFromInterfaceType(interfaceType); + map->insert(std::pair<std::string, rtc::AdapterType>(name, adapterType)); + }); + @synchronized(strongSelf) { + webrtc::NetworkMonitorObserver *observer = strongSelf->_observer; + if (observer) { + observer->OnPathUpdate(std::move(*map)); + } + } + delete map; + }); + nw_path_monitor_set_queue( + _pathMonitor, + [RTC_OBJC_TYPE(RTCDispatcher) dispatchQueueForType:RTCDispatcherTypeNetworkMonitor]); + nw_path_monitor_start(_pathMonitor); + } + } + return self; +} + +- (void)cancel { + if (@available(iOS 12, *)) { + nw_path_monitor_cancel(_pathMonitor); + } +} +- (void)stop { + [self cancel]; + @synchronized(self) { + _observer = nil; + } +} + +- (void)dealloc { + [self cancel]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLI420Renderer.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLI420Renderer.h new file mode 100644 index 0000000000..e5987fe22a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLI420Renderer.h @@ -0,0 +1,17 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMTLRenderer.h" + +NS_AVAILABLE(10_11, 9_0) +@interface RTCMTLI420Renderer : RTCMTLRenderer +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLI420Renderer.mm b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLI420Renderer.mm new file mode 100644 index 0000000000..f4c76fa313 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLI420Renderer.mm @@ -0,0 +1,170 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMTLI420Renderer.h" + +#import <Metal/Metal.h> +#import <MetalKit/MetalKit.h> + +#import "base/RTCI420Buffer.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" + +#import "RTCMTLRenderer+Private.h" + +static NSString *const shaderSource = MTL_STRINGIFY( + using namespace metal; + + typedef struct { + packed_float2 position; + packed_float2 texcoord; + } Vertex; + + typedef struct { + float4 position[[position]]; + float2 texcoord; + } Varyings; + + vertex Varyings vertexPassthrough(constant Vertex *verticies[[buffer(0)]], + unsigned int vid[[vertex_id]]) { + Varyings out; + constant Vertex &v = verticies[vid]; + out.position = float4(float2(v.position), 0.0, 1.0); + out.texcoord = v.texcoord; + + return out; + } + + fragment half4 fragmentColorConversion( + Varyings in[[stage_in]], + texture2d<float, access::sample> textureY[[texture(0)]], + texture2d<float, access::sample> textureU[[texture(1)]], + texture2d<float, access::sample> textureV[[texture(2)]]) { + constexpr sampler s(address::clamp_to_edge, filter::linear); + float y; + float u; + float v; + float r; + float g; + float b; + // Conversion for YUV to rgb from http://www.fourcc.org/fccyvrgb.php + y = textureY.sample(s, in.texcoord).r; + u = textureU.sample(s, in.texcoord).r; + v = textureV.sample(s, in.texcoord).r; + u = u - 0.5; + v = v - 0.5; + r = y + 1.403 * v; + g = y - 0.344 * u - 0.714 * v; + b = y + 1.770 * u; + + float4 out = float4(r, g, b, 1.0); + + return half4(out); + }); + +@implementation RTCMTLI420Renderer { + // Textures. + id<MTLTexture> _yTexture; + id<MTLTexture> _uTexture; + id<MTLTexture> _vTexture; + + MTLTextureDescriptor *_descriptor; + MTLTextureDescriptor *_chromaDescriptor; + + int _width; + int _height; + int _chromaWidth; + int _chromaHeight; +} + +#pragma mark - Virtual + +- (NSString *)shaderSource { + return shaderSource; +} + +- (void)getWidth:(nonnull int *)width + height:(nonnull int *)height + cropWidth:(nonnull int *)cropWidth + cropHeight:(nonnull int *)cropHeight + cropX:(nonnull int *)cropX + cropY:(nonnull int *)cropY + ofFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + *width = frame.width; + *height = frame.height; + *cropWidth = frame.width; + *cropHeight = frame.height; + *cropX = 0; + *cropY = 0; +} + +- (BOOL)setupTexturesForFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + if (![super setupTexturesForFrame:frame]) { + return NO; + } + + id<MTLDevice> device = [self currentMetalDevice]; + if (!device) { + return NO; + } + + id<RTC_OBJC_TYPE(RTCI420Buffer)> buffer = [frame.buffer toI420]; + + // Luma (y) texture. + if (!_descriptor || _width != frame.width || _height != frame.height) { + _width = frame.width; + _height = frame.height; + _descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm + width:_width + height:_height + mipmapped:NO]; + _descriptor.usage = MTLTextureUsageShaderRead; + _yTexture = [device newTextureWithDescriptor:_descriptor]; + } + + // Chroma (u,v) textures + [_yTexture replaceRegion:MTLRegionMake2D(0, 0, _width, _height) + mipmapLevel:0 + withBytes:buffer.dataY + bytesPerRow:buffer.strideY]; + + if (!_chromaDescriptor || _chromaWidth != frame.width / 2 || _chromaHeight != frame.height / 2) { + _chromaWidth = frame.width / 2; + _chromaHeight = frame.height / 2; + _chromaDescriptor = + [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm + width:_chromaWidth + height:_chromaHeight + mipmapped:NO]; + _chromaDescriptor.usage = MTLTextureUsageShaderRead; + _uTexture = [device newTextureWithDescriptor:_chromaDescriptor]; + _vTexture = [device newTextureWithDescriptor:_chromaDescriptor]; + } + + [_uTexture replaceRegion:MTLRegionMake2D(0, 0, _chromaWidth, _chromaHeight) + mipmapLevel:0 + withBytes:buffer.dataU + bytesPerRow:buffer.strideU]; + [_vTexture replaceRegion:MTLRegionMake2D(0, 0, _chromaWidth, _chromaHeight) + mipmapLevel:0 + withBytes:buffer.dataV + bytesPerRow:buffer.strideV]; + + return (_uTexture != nil) && (_yTexture != nil) && (_vTexture != nil); +} + +- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder { + [renderEncoder setFragmentTexture:_yTexture atIndex:0]; + [renderEncoder setFragmentTexture:_uTexture atIndex:1]; + [renderEncoder setFragmentTexture:_vTexture atIndex:2]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNSVideoView.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNSVideoView.h new file mode 100644 index 0000000000..f70e2ad5ee --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNSVideoView.h @@ -0,0 +1,24 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AppKit/AppKit.h> + +#import "RTCVideoRenderer.h" + +NS_AVAILABLE_MAC(10.11) + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMTLNSVideoView) : NSView <RTC_OBJC_TYPE(RTCVideoRenderer)> + +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCVideoViewDelegate)> delegate; + ++ (BOOL)isMetalAvailable; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNSVideoView.m b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNSVideoView.m new file mode 100644 index 0000000000..625fb1caa7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNSVideoView.m @@ -0,0 +1,122 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMTLNSVideoView.h" + +#import <Metal/Metal.h> +#import <MetalKit/MetalKit.h> + +#import "base/RTCVideoFrame.h" + +#import "RTCMTLI420Renderer.h" + +@interface RTC_OBJC_TYPE (RTCMTLNSVideoView) +()<MTKViewDelegate> @property(nonatomic) id<RTCMTLRenderer> renderer; +@property(nonatomic, strong) MTKView *metalView; +@property(atomic, strong) RTC_OBJC_TYPE(RTCVideoFrame) * videoFrame; +@end + +@implementation RTC_OBJC_TYPE (RTCMTLNSVideoView) { + id<RTCMTLRenderer> _renderer; +} + +@synthesize delegate = _delegate; +@synthesize renderer = _renderer; +@synthesize metalView = _metalView; +@synthesize videoFrame = _videoFrame; + +- (instancetype)initWithFrame:(CGRect)frameRect { + self = [super initWithFrame:frameRect]; + if (self) { + [self configure]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aCoder { + self = [super initWithCoder:aCoder]; + if (self) { + [self configure]; + } + return self; +} + +#pragma mark - Private + ++ (BOOL)isMetalAvailable { + return [MTLCopyAllDevices() count] > 0; +} + +- (void)configure { + if ([[self class] isMetalAvailable]) { + _metalView = [[MTKView alloc] initWithFrame:self.bounds]; + [self addSubview:_metalView]; + _metalView.layerContentsPlacement = NSViewLayerContentsPlacementScaleProportionallyToFit; + _metalView.translatesAutoresizingMaskIntoConstraints = NO; + _metalView.framebufferOnly = YES; + _metalView.delegate = self; + + _renderer = [[RTCMTLI420Renderer alloc] init]; + if (![(RTCMTLI420Renderer *)_renderer addRenderingDestination:_metalView]) { + _renderer = nil; + }; + } +} + +- (void)updateConstraints { + NSDictionary *views = NSDictionaryOfVariableBindings(_metalView); + + NSArray *constraintsHorizontal = + [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_metalView]-0-|" + options:0 + metrics:nil + views:views]; + [self addConstraints:constraintsHorizontal]; + + NSArray *constraintsVertical = + [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_metalView]-0-|" + options:0 + metrics:nil + views:views]; + [self addConstraints:constraintsVertical]; + [super updateConstraints]; +} + +#pragma mark - MTKViewDelegate methods +- (void)drawInMTKView:(nonnull MTKView *)view { + if (self.videoFrame == nil) { + return; + } + if (view == self.metalView) { + [_renderer drawFrame:self.videoFrame]; + } +} + +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer) + +- (void)setSize:(CGSize)size { + _metalView.drawableSize = size; + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate videoView:self didChangeVideoSize:size]; + }); + [_metalView draw]; +} + +- (void)renderFrame:(nullable RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + if (frame == nil) { + return; + } + self.videoFrame = [frame newI420VideoFrame]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNV12Renderer.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNV12Renderer.h new file mode 100644 index 0000000000..866b7ea17e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNV12Renderer.h @@ -0,0 +1,18 @@ +/* + * Copyright 2017 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMTLRenderer.h" + +NS_AVAILABLE(10_11, 9_0) +@interface RTCMTLNV12Renderer : RTCMTLRenderer + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNV12Renderer.mm b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNV12Renderer.mm new file mode 100644 index 0000000000..7b037c6dbc --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLNV12Renderer.mm @@ -0,0 +1,164 @@ +/* + * Copyright 2017 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMTLNV12Renderer.h" + +#import <Metal/Metal.h> +#import <MetalKit/MetalKit.h> + +#import "RTCMTLRenderer+Private.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +#include "rtc_base/checks.h" + +static NSString *const shaderSource = MTL_STRINGIFY( + using namespace metal; + + typedef struct { + packed_float2 position; + packed_float2 texcoord; + } Vertex; + + typedef struct { + float4 position[[position]]; + float2 texcoord; + } Varyings; + + vertex Varyings vertexPassthrough(constant Vertex *verticies[[buffer(0)]], + unsigned int vid[[vertex_id]]) { + Varyings out; + constant Vertex &v = verticies[vid]; + out.position = float4(float2(v.position), 0.0, 1.0); + out.texcoord = v.texcoord; + return out; + } + + // Receiving YCrCb textures. + fragment half4 fragmentColorConversion( + Varyings in[[stage_in]], + texture2d<float, access::sample> textureY[[texture(0)]], + texture2d<float, access::sample> textureCbCr[[texture(1)]]) { + constexpr sampler s(address::clamp_to_edge, filter::linear); + float y; + float2 uv; + y = textureY.sample(s, in.texcoord).r; + uv = textureCbCr.sample(s, in.texcoord).rg - float2(0.5, 0.5); + + // Conversion for YUV to rgb from http://www.fourcc.org/fccyvrgb.php + float4 out = float4(y + 1.403 * uv.y, y - 0.344 * uv.x - 0.714 * uv.y, y + 1.770 * uv.x, 1.0); + + return half4(out); + }); + +@implementation RTCMTLNV12Renderer { + // Textures. + CVMetalTextureCacheRef _textureCache; + id<MTLTexture> _yTexture; + id<MTLTexture> _CrCbTexture; +} + +- (BOOL)addRenderingDestination:(__kindof MTKView *)view { + if ([super addRenderingDestination:view]) { + return [self initializeTextureCache]; + } + return NO; +} + +- (BOOL)initializeTextureCache { + CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, [self currentMetalDevice], + nil, &_textureCache); + if (status != kCVReturnSuccess) { + RTCLogError(@"Metal: Failed to initialize metal texture cache. Return status is %d", status); + return NO; + } + + return YES; +} + +- (NSString *)shaderSource { + return shaderSource; +} + +- (void)getWidth:(nonnull int *)width + height:(nonnull int *)height + cropWidth:(nonnull int *)cropWidth + cropHeight:(nonnull int *)cropHeight + cropX:(nonnull int *)cropX + cropY:(nonnull int *)cropY + ofFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + RTC_OBJC_TYPE(RTCCVPixelBuffer) *pixelBuffer = (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer; + *width = CVPixelBufferGetWidth(pixelBuffer.pixelBuffer); + *height = CVPixelBufferGetHeight(pixelBuffer.pixelBuffer); + *cropWidth = pixelBuffer.cropWidth; + *cropHeight = pixelBuffer.cropHeight; + *cropX = pixelBuffer.cropX; + *cropY = pixelBuffer.cropY; +} + +- (BOOL)setupTexturesForFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + RTC_DCHECK([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]); + if (![super setupTexturesForFrame:frame]) { + return NO; + } + CVPixelBufferRef pixelBuffer = ((RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer).pixelBuffer; + + id<MTLTexture> lumaTexture = nil; + id<MTLTexture> chromaTexture = nil; + CVMetalTextureRef outTexture = nullptr; + + // Luma (y) texture. + int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + + int indexPlane = 0; + CVReturn result = CVMetalTextureCacheCreateTextureFromImage( + kCFAllocatorDefault, _textureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, lumaWidth, + lumaHeight, indexPlane, &outTexture); + + if (result == kCVReturnSuccess) { + lumaTexture = CVMetalTextureGetTexture(outTexture); + } + + // Same as CFRelease except it can be passed NULL without crashing. + CVBufferRelease(outTexture); + outTexture = nullptr; + + // Chroma (CrCb) texture. + indexPlane = 1; + result = CVMetalTextureCacheCreateTextureFromImage( + kCFAllocatorDefault, _textureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, lumaWidth / 2, + lumaHeight / 2, indexPlane, &outTexture); + if (result == kCVReturnSuccess) { + chromaTexture = CVMetalTextureGetTexture(outTexture); + } + CVBufferRelease(outTexture); + + if (lumaTexture != nil && chromaTexture != nil) { + _yTexture = lumaTexture; + _CrCbTexture = chromaTexture; + return YES; + } + return NO; +} + +- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder { + [renderEncoder setFragmentTexture:_yTexture atIndex:0]; + [renderEncoder setFragmentTexture:_CrCbTexture atIndex:1]; +} + +- (void)dealloc { + if (_textureCache) { + CFRelease(_textureCache); + } +} +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRGBRenderer.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRGBRenderer.h new file mode 100644 index 0000000000..9db422cd22 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRGBRenderer.h @@ -0,0 +1,22 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMTLRenderer.h" + +/** @abstract RGB/BGR renderer. + * @discussion This renderer handles both kCVPixelFormatType_32BGRA and + * kCVPixelFormatType_32ARGB. + */ +NS_AVAILABLE(10_11, 9_0) +@interface RTCMTLRGBRenderer : RTCMTLRenderer + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRGBRenderer.mm b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRGBRenderer.mm new file mode 100644 index 0000000000..e5dc4ef80a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRGBRenderer.mm @@ -0,0 +1,164 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMTLRGBRenderer.h" + +#import <Metal/Metal.h> +#import <MetalKit/MetalKit.h> + +#import "RTCMTLRenderer+Private.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +#include "rtc_base/checks.h" + +static NSString *const shaderSource = MTL_STRINGIFY( + using namespace metal; + + typedef struct { + packed_float2 position; + packed_float2 texcoord; + } Vertex; + + typedef struct { + float4 position[[position]]; + float2 texcoord; + } VertexIO; + + vertex VertexIO vertexPassthrough(constant Vertex *verticies[[buffer(0)]], + uint vid[[vertex_id]]) { + VertexIO out; + constant Vertex &v = verticies[vid]; + out.position = float4(float2(v.position), 0.0, 1.0); + out.texcoord = v.texcoord; + return out; + } + + fragment half4 fragmentColorConversion(VertexIO in[[stage_in]], + texture2d<half, access::sample> texture[[texture(0)]], + constant bool &isARGB[[buffer(0)]]) { + constexpr sampler s(address::clamp_to_edge, filter::linear); + + half4 out = texture.sample(s, in.texcoord); + if (isARGB) { + out = half4(out.g, out.b, out.a, out.r); + } + + return out; + }); + +@implementation RTCMTLRGBRenderer { + // Textures. + CVMetalTextureCacheRef _textureCache; + id<MTLTexture> _texture; + + // Uniforms. + id<MTLBuffer> _uniformsBuffer; +} + +- (BOOL)addRenderingDestination:(__kindof MTKView *)view { + if ([super addRenderingDestination:view]) { + return [self initializeTextureCache]; + } + return NO; +} + +- (BOOL)initializeTextureCache { + CVReturn status = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, [self currentMetalDevice], + nil, &_textureCache); + if (status != kCVReturnSuccess) { + RTCLogError(@"Metal: Failed to initialize metal texture cache. Return status is %d", status); + return NO; + } + + return YES; +} + +- (NSString *)shaderSource { + return shaderSource; +} + +- (void)getWidth:(nonnull int *)width + height:(nonnull int *)height + cropWidth:(nonnull int *)cropWidth + cropHeight:(nonnull int *)cropHeight + cropX:(nonnull int *)cropX + cropY:(nonnull int *)cropY + ofFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + RTC_OBJC_TYPE(RTCCVPixelBuffer) *pixelBuffer = (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer; + *width = CVPixelBufferGetWidth(pixelBuffer.pixelBuffer); + *height = CVPixelBufferGetHeight(pixelBuffer.pixelBuffer); + *cropWidth = pixelBuffer.cropWidth; + *cropHeight = pixelBuffer.cropHeight; + *cropX = pixelBuffer.cropX; + *cropY = pixelBuffer.cropY; +} + +- (BOOL)setupTexturesForFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + RTC_DCHECK([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]); + if (![super setupTexturesForFrame:frame]) { + return NO; + } + CVPixelBufferRef pixelBuffer = ((RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer).pixelBuffer; + + id<MTLTexture> gpuTexture = nil; + CVMetalTextureRef textureOut = nullptr; + bool isARGB; + + int width = CVPixelBufferGetWidth(pixelBuffer); + int height = CVPixelBufferGetHeight(pixelBuffer); + OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); + + MTLPixelFormat mtlPixelFormat; + if (pixelFormat == kCVPixelFormatType_32BGRA) { + mtlPixelFormat = MTLPixelFormatBGRA8Unorm; + isARGB = false; + } else if (pixelFormat == kCVPixelFormatType_32ARGB) { + mtlPixelFormat = MTLPixelFormatRGBA8Unorm; + isARGB = true; + } else { + RTC_DCHECK_NOTREACHED(); + return NO; + } + + CVReturn result = CVMetalTextureCacheCreateTextureFromImage( + kCFAllocatorDefault, _textureCache, pixelBuffer, nil, mtlPixelFormat, + width, height, 0, &textureOut); + if (result == kCVReturnSuccess) { + gpuTexture = CVMetalTextureGetTexture(textureOut); + } + CVBufferRelease(textureOut); + + if (gpuTexture != nil) { + _texture = gpuTexture; + _uniformsBuffer = + [[self currentMetalDevice] newBufferWithBytes:&isARGB + length:sizeof(isARGB) + options:MTLResourceCPUCacheModeDefaultCache]; + return YES; + } + + return NO; +} + +- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder { + [renderEncoder setFragmentTexture:_texture atIndex:0]; + [renderEncoder setFragmentBuffer:_uniformsBuffer offset:0 atIndex:0]; +} + +- (void)dealloc { + if (_textureCache) { + CFRelease(_textureCache); + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer+Private.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer+Private.h new file mode 100644 index 0000000000..916d4d4430 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer+Private.h @@ -0,0 +1,33 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Metal/Metal.h> + +#import "RTCMTLRenderer.h" + +#define MTL_STRINGIFY(s) @ #s + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMTLRenderer (Private) +- (nullable id<MTLDevice>)currentMetalDevice; +- (NSString *)shaderSource; +- (BOOL)setupTexturesForFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame; +- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder; +- (void)getWidth:(nonnull int *)width + height:(nonnull int *)height + cropWidth:(nonnull int *)cropWidth + cropHeight:(nonnull int *)cropHeight + cropX:(nonnull int *)cropX + cropY:(nonnull int *)cropY + ofFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame; +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer.h new file mode 100644 index 0000000000..aa31545973 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#else +#import <AppKit/AppKit.h> +#endif + +#import "base/RTCVideoFrame.h" + +NS_ASSUME_NONNULL_BEGIN +/** + * Protocol defining ability to render RTCVideoFrame in Metal enabled views. + */ +@protocol RTCMTLRenderer <NSObject> + +/** + * Method to be implemented to perform actual rendering of the provided frame. + * + * @param frame The frame to be rendered. + */ +- (void)drawFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame; + +/** + * Sets the provided view as rendering destination if possible. + * + * If not possible method returns NO and callers of the method are responisble for performing + * cleanups. + */ + +#if TARGET_OS_IOS +- (BOOL)addRenderingDestination:(__kindof UIView *)view; +#else +- (BOOL)addRenderingDestination:(__kindof NSView *)view; +#endif + +@end + +/** + * Implementation of RTCMTLRenderer protocol. + */ +NS_AVAILABLE(10_11, 9_0) +@interface RTCMTLRenderer : NSObject <RTCMTLRenderer> + +/** @abstract A wrapped RTCVideoRotation, or nil. + @discussion When not nil, the rotation of the actual frame is ignored when rendering. + */ +@property(atomic, nullable) NSValue *rotationOverride; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer.mm b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer.mm new file mode 100644 index 0000000000..410590a7b1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLRenderer.mm @@ -0,0 +1,328 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMTLRenderer+Private.h" + +#import <Metal/Metal.h> +#import <MetalKit/MetalKit.h> + +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" + +#include "api/video/video_rotation.h" +#include "rtc_base/checks.h" + +// As defined in shaderSource. +static NSString *const vertexFunctionName = @"vertexPassthrough"; +static NSString *const fragmentFunctionName = @"fragmentColorConversion"; + +static NSString *const pipelineDescriptorLabel = @"RTCPipeline"; +static NSString *const commandBufferLabel = @"RTCCommandBuffer"; +static NSString *const renderEncoderLabel = @"RTCEncoder"; +static NSString *const renderEncoderDebugGroup = @"RTCDrawFrame"; + +// Computes the texture coordinates given rotation and cropping. +static inline void getCubeVertexData(int cropX, + int cropY, + int cropWidth, + int cropHeight, + size_t frameWidth, + size_t frameHeight, + RTCVideoRotation rotation, + float *buffer) { + // The computed values are the adjusted texture coordinates, in [0..1]. + // For the left and top, 0.0 means no cropping and e.g. 0.2 means we're skipping 20% of the + // left/top edge. + // For the right and bottom, 1.0 means no cropping and e.g. 0.8 means we're skipping 20% of the + // right/bottom edge (i.e. render up to 80% of the width/height). + float cropLeft = cropX / (float)frameWidth; + float cropRight = (cropX + cropWidth) / (float)frameWidth; + float cropTop = cropY / (float)frameHeight; + float cropBottom = (cropY + cropHeight) / (float)frameHeight; + + // These arrays map the view coordinates to texture coordinates, taking cropping and rotation + // into account. The first two columns are view coordinates, the last two are texture coordinates. + switch (rotation) { + case RTCVideoRotation_0: { + float values[16] = {-1.0, -1.0, cropLeft, cropBottom, + 1.0, -1.0, cropRight, cropBottom, + -1.0, 1.0, cropLeft, cropTop, + 1.0, 1.0, cropRight, cropTop}; + memcpy(buffer, &values, sizeof(values)); + } break; + case RTCVideoRotation_90: { + float values[16] = {-1.0, -1.0, cropRight, cropBottom, + 1.0, -1.0, cropRight, cropTop, + -1.0, 1.0, cropLeft, cropBottom, + 1.0, 1.0, cropLeft, cropTop}; + memcpy(buffer, &values, sizeof(values)); + } break; + case RTCVideoRotation_180: { + float values[16] = {-1.0, -1.0, cropRight, cropTop, + 1.0, -1.0, cropLeft, cropTop, + -1.0, 1.0, cropRight, cropBottom, + 1.0, 1.0, cropLeft, cropBottom}; + memcpy(buffer, &values, sizeof(values)); + } break; + case RTCVideoRotation_270: { + float values[16] = {-1.0, -1.0, cropLeft, cropTop, + 1.0, -1.0, cropLeft, cropBottom, + -1.0, 1.0, cropRight, cropTop, + 1.0, 1.0, cropRight, cropBottom}; + memcpy(buffer, &values, sizeof(values)); + } break; + } +} + +// The max number of command buffers in flight (submitted to GPU). +// For now setting it up to 1. +// In future we might use triple buffering method if it improves performance. +static const NSInteger kMaxInflightBuffers = 1; + +@implementation RTCMTLRenderer { + __kindof MTKView *_view; + + // Controller. + dispatch_semaphore_t _inflight_semaphore; + + // Renderer. + id<MTLDevice> _device; + id<MTLCommandQueue> _commandQueue; + id<MTLLibrary> _defaultLibrary; + id<MTLRenderPipelineState> _pipelineState; + + // Buffers. + id<MTLBuffer> _vertexBuffer; + + // Values affecting the vertex buffer. Stored for comparison to avoid unnecessary recreation. + int _oldFrameWidth; + int _oldFrameHeight; + int _oldCropWidth; + int _oldCropHeight; + int _oldCropX; + int _oldCropY; + RTCVideoRotation _oldRotation; +} + +@synthesize rotationOverride = _rotationOverride; + +- (instancetype)init { + if (self = [super init]) { + _inflight_semaphore = dispatch_semaphore_create(kMaxInflightBuffers); + } + + return self; +} + +- (BOOL)addRenderingDestination:(__kindof MTKView *)view { + return [self setupWithView:view]; +} + +#pragma mark - Private + +- (BOOL)setupWithView:(__kindof MTKView *)view { + BOOL success = NO; + if ([self setupMetal]) { + _view = view; + view.device = _device; + view.preferredFramesPerSecond = 30; + view.autoResizeDrawable = NO; + + [self loadAssets]; + + float vertexBufferArray[16] = {0}; + _vertexBuffer = [_device newBufferWithBytes:vertexBufferArray + length:sizeof(vertexBufferArray) + options:MTLResourceCPUCacheModeWriteCombined]; + success = YES; + } + return success; +} +#pragma mark - Inheritance + +- (id<MTLDevice>)currentMetalDevice { + return _device; +} + +- (NSString *)shaderSource { + RTC_DCHECK_NOTREACHED() << "Virtual method not implemented in subclass."; + return nil; +} + +- (void)uploadTexturesToRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder { + RTC_DCHECK_NOTREACHED() << "Virtual method not implemented in subclass."; +} + +- (void)getWidth:(int *)width + height:(int *)height + cropWidth:(int *)cropWidth + cropHeight:(int *)cropHeight + cropX:(int *)cropX + cropY:(int *)cropY + ofFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + RTC_DCHECK_NOTREACHED() << "Virtual method not implemented in subclass."; +} + +- (BOOL)setupTexturesForFrame:(nonnull RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + // Apply rotation override if set. + RTCVideoRotation rotation; + NSValue *rotationOverride = self.rotationOverride; + if (rotationOverride) { +#if defined(__IPHONE_11_0) && defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) + if (@available(iOS 11, *)) { + [rotationOverride getValue:&rotation size:sizeof(rotation)]; + } else +#endif + { + [rotationOverride getValue:&rotation]; + } + } else { + rotation = frame.rotation; + } + + int frameWidth, frameHeight, cropWidth, cropHeight, cropX, cropY; + [self getWidth:&frameWidth + height:&frameHeight + cropWidth:&cropWidth + cropHeight:&cropHeight + cropX:&cropX + cropY:&cropY + ofFrame:frame]; + + // Recompute the texture cropping and recreate vertexBuffer if necessary. + if (cropX != _oldCropX || cropY != _oldCropY || cropWidth != _oldCropWidth || + cropHeight != _oldCropHeight || rotation != _oldRotation || frameWidth != _oldFrameWidth || + frameHeight != _oldFrameHeight) { + getCubeVertexData(cropX, + cropY, + cropWidth, + cropHeight, + frameWidth, + frameHeight, + rotation, + (float *)_vertexBuffer.contents); + _oldCropX = cropX; + _oldCropY = cropY; + _oldCropWidth = cropWidth; + _oldCropHeight = cropHeight; + _oldRotation = rotation; + _oldFrameWidth = frameWidth; + _oldFrameHeight = frameHeight; + } + + return YES; +} + +#pragma mark - GPU methods + +- (BOOL)setupMetal { + // Set the view to use the default device. + _device = MTLCreateSystemDefaultDevice(); + if (!_device) { + return NO; + } + + // Create a new command queue. + _commandQueue = [_device newCommandQueue]; + + // Load metal library from source. + NSError *libraryError = nil; + NSString *shaderSource = [self shaderSource]; + + id<MTLLibrary> sourceLibrary = + [_device newLibraryWithSource:shaderSource options:NULL error:&libraryError]; + + if (libraryError) { + RTCLogError(@"Metal: Library with source failed\n%@", libraryError); + return NO; + } + + if (!sourceLibrary) { + RTCLogError(@"Metal: Failed to load library. %@", libraryError); + return NO; + } + _defaultLibrary = sourceLibrary; + + return YES; +} + +- (void)loadAssets { + id<MTLFunction> vertexFunction = [_defaultLibrary newFunctionWithName:vertexFunctionName]; + id<MTLFunction> fragmentFunction = [_defaultLibrary newFunctionWithName:fragmentFunctionName]; + + MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineDescriptor.label = pipelineDescriptorLabel; + pipelineDescriptor.vertexFunction = vertexFunction; + pipelineDescriptor.fragmentFunction = fragmentFunction; + pipelineDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat; + pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid; + NSError *error = nil; + _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; + + if (!_pipelineState) { + RTCLogError(@"Metal: Failed to create pipeline state. %@", error); + } +} + +- (void)render { + id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer]; + commandBuffer.label = commandBufferLabel; + + __block dispatch_semaphore_t block_semaphore = _inflight_semaphore; + [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) { + // GPU work completed. + dispatch_semaphore_signal(block_semaphore); + }]; + + MTLRenderPassDescriptor *renderPassDescriptor = _view.currentRenderPassDescriptor; + if (renderPassDescriptor) { // Valid drawable. + id<MTLRenderCommandEncoder> renderEncoder = + [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + renderEncoder.label = renderEncoderLabel; + + // Set context state. + [renderEncoder pushDebugGroup:renderEncoderDebugGroup]; + [renderEncoder setRenderPipelineState:_pipelineState]; + [renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:0]; + [self uploadTexturesToRenderEncoder:renderEncoder]; + + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip + vertexStart:0 + vertexCount:4 + instanceCount:1]; + [renderEncoder popDebugGroup]; + [renderEncoder endEncoding]; + + [commandBuffer presentDrawable:_view.currentDrawable]; + } + + // CPU work is completed, GPU work can be started. + [commandBuffer commit]; +} + +#pragma mark - RTCMTLRenderer + +- (void)drawFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + @autoreleasepool { + // Wait until the inflight (curently sent to GPU) command buffer + // has completed the GPU work. + dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER); + + if ([self setupTexturesForFrame:frame]) { + [self render]; + } else { + dispatch_semaphore_signal(_inflight_semaphore); + } + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLVideoView.h b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLVideoView.h new file mode 100644 index 0000000000..3320d12076 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLVideoView.h @@ -0,0 +1,44 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoFrame.h" +#import "RTCVideoRenderer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * RTCMTLVideoView is thin wrapper around MTKView. + * + * It has id<RTCVideoRenderer> property that renders video frames in the view's + * bounds using Metal. + */ +NS_CLASS_AVAILABLE_IOS(9) + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCMTLVideoView) : UIView<RTC_OBJC_TYPE(RTCVideoRenderer)> + +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCVideoViewDelegate)> delegate; + +@property(nonatomic) UIViewContentMode videoContentMode; + +/** @abstract Enables/disables rendering. + */ +@property(nonatomic, getter=isEnabled) BOOL enabled; + +/** @abstract Wrapped RTCVideoRotation, or nil. + */ +@property(nonatomic, nullable) NSValue* rotationOverride; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLVideoView.m b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLVideoView.m new file mode 100644 index 0000000000..c5d9e4385f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/metal/RTCMTLVideoView.m @@ -0,0 +1,265 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCMTLVideoView.h" + +#import <Metal/Metal.h> +#import <MetalKit/MetalKit.h> + +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +#import "RTCMTLI420Renderer.h" +#import "RTCMTLNV12Renderer.h" +#import "RTCMTLRGBRenderer.h" + +// To avoid unreconized symbol linker errors, we're taking advantage of the objc runtime. +// Linking errors occur when compiling for architectures that don't support Metal. +#define MTKViewClass NSClassFromString(@"MTKView") +#define RTCMTLNV12RendererClass NSClassFromString(@"RTCMTLNV12Renderer") +#define RTCMTLI420RendererClass NSClassFromString(@"RTCMTLI420Renderer") +#define RTCMTLRGBRendererClass NSClassFromString(@"RTCMTLRGBRenderer") + +@interface RTC_OBJC_TYPE (RTCMTLVideoView) +()<MTKViewDelegate> @property(nonatomic) RTCMTLI420Renderer *rendererI420; +@property(nonatomic) RTCMTLNV12Renderer *rendererNV12; +@property(nonatomic) RTCMTLRGBRenderer *rendererRGB; +@property(nonatomic) MTKView *metalView; +@property(atomic) RTC_OBJC_TYPE(RTCVideoFrame) * videoFrame; +@property(nonatomic) CGSize videoFrameSize; +@property(nonatomic) int64_t lastFrameTimeNs; +@end + +@implementation RTC_OBJC_TYPE (RTCMTLVideoView) + +@synthesize delegate = _delegate; +@synthesize rendererI420 = _rendererI420; +@synthesize rendererNV12 = _rendererNV12; +@synthesize rendererRGB = _rendererRGB; +@synthesize metalView = _metalView; +@synthesize videoFrame = _videoFrame; +@synthesize videoFrameSize = _videoFrameSize; +@synthesize lastFrameTimeNs = _lastFrameTimeNs; +@synthesize rotationOverride = _rotationOverride; + +- (instancetype)initWithFrame:(CGRect)frameRect { + self = [super initWithFrame:frameRect]; + if (self) { + [self configure]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aCoder { + self = [super initWithCoder:aCoder]; + if (self) { + [self configure]; + } + return self; +} + +- (BOOL)isEnabled { + return !self.metalView.paused; +} + +- (void)setEnabled:(BOOL)enabled { + self.metalView.paused = !enabled; +} + +- (UIViewContentMode)videoContentMode { + return self.metalView.contentMode; +} + +- (void)setVideoContentMode:(UIViewContentMode)mode { + self.metalView.contentMode = mode; +} + +#pragma mark - Private + ++ (BOOL)isMetalAvailable { + return MTLCreateSystemDefaultDevice() != nil; +} + ++ (MTKView *)createMetalView:(CGRect)frame { + return [[MTKViewClass alloc] initWithFrame:frame]; +} + ++ (RTCMTLNV12Renderer *)createNV12Renderer { + return [[RTCMTLNV12RendererClass alloc] init]; +} + ++ (RTCMTLI420Renderer *)createI420Renderer { + return [[RTCMTLI420RendererClass alloc] init]; +} + ++ (RTCMTLRGBRenderer *)createRGBRenderer { + return [[RTCMTLRGBRenderer alloc] init]; +} + +- (void)configure { + NSAssert([RTC_OBJC_TYPE(RTCMTLVideoView) isMetalAvailable], + @"Metal not availiable on this device"); + + self.metalView = [RTC_OBJC_TYPE(RTCMTLVideoView) createMetalView:self.bounds]; + self.metalView.delegate = self; + self.metalView.contentMode = UIViewContentModeScaleAspectFill; + [self addSubview:self.metalView]; + self.videoFrameSize = CGSizeZero; +} + +- (void)setMultipleTouchEnabled:(BOOL)multipleTouchEnabled { + [super setMultipleTouchEnabled:multipleTouchEnabled]; + self.metalView.multipleTouchEnabled = multipleTouchEnabled; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGRect bounds = self.bounds; + self.metalView.frame = bounds; + if (!CGSizeEqualToSize(self.videoFrameSize, CGSizeZero)) { + self.metalView.drawableSize = [self drawableSize]; + } else { + self.metalView.drawableSize = bounds.size; + } +} + +#pragma mark - MTKViewDelegate methods + +- (void)drawInMTKView:(nonnull MTKView *)view { + NSAssert(view == self.metalView, @"Receiving draw callbacks from foreign instance."); + RTC_OBJC_TYPE(RTCVideoFrame) *videoFrame = self.videoFrame; + // Skip rendering if we've already rendered this frame. + if (!videoFrame || videoFrame.width <= 0 || videoFrame.height <= 0 || + videoFrame.timeStampNs == self.lastFrameTimeNs) { + return; + } + + if (CGRectIsEmpty(view.bounds)) { + return; + } + + RTCMTLRenderer *renderer; + if ([videoFrame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)videoFrame.buffer; + const OSType pixelFormat = CVPixelBufferGetPixelFormatType(buffer.pixelBuffer); + if (pixelFormat == kCVPixelFormatType_32BGRA || pixelFormat == kCVPixelFormatType_32ARGB) { + if (!self.rendererRGB) { + self.rendererRGB = [RTC_OBJC_TYPE(RTCMTLVideoView) createRGBRenderer]; + if (![self.rendererRGB addRenderingDestination:self.metalView]) { + self.rendererRGB = nil; + RTCLogError(@"Failed to create RGB renderer"); + return; + } + } + renderer = self.rendererRGB; + } else { + if (!self.rendererNV12) { + self.rendererNV12 = [RTC_OBJC_TYPE(RTCMTLVideoView) createNV12Renderer]; + if (![self.rendererNV12 addRenderingDestination:self.metalView]) { + self.rendererNV12 = nil; + RTCLogError(@"Failed to create NV12 renderer"); + return; + } + } + renderer = self.rendererNV12; + } + } else { + if (!self.rendererI420) { + self.rendererI420 = [RTC_OBJC_TYPE(RTCMTLVideoView) createI420Renderer]; + if (![self.rendererI420 addRenderingDestination:self.metalView]) { + self.rendererI420 = nil; + RTCLogError(@"Failed to create I420 renderer"); + return; + } + } + renderer = self.rendererI420; + } + + renderer.rotationOverride = self.rotationOverride; + + [renderer drawFrame:videoFrame]; + self.lastFrameTimeNs = videoFrame.timeStampNs; +} + +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { +} + +#pragma mark - + +- (void)setRotationOverride:(NSValue *)rotationOverride { + _rotationOverride = rotationOverride; + + self.metalView.drawableSize = [self drawableSize]; + [self setNeedsLayout]; +} + +- (RTCVideoRotation)frameRotation { + if (self.rotationOverride) { + RTCVideoRotation rotation; + if (@available(iOS 11, *)) { + [self.rotationOverride getValue:&rotation size:sizeof(rotation)]; + } else { + [self.rotationOverride getValue:&rotation]; + } + return rotation; + } + + return self.videoFrame.rotation; +} + +- (CGSize)drawableSize { + // Flip width/height if the rotations are not the same. + CGSize videoFrameSize = self.videoFrameSize; + RTCVideoRotation frameRotation = [self frameRotation]; + + BOOL useLandscape = + (frameRotation == RTCVideoRotation_0) || (frameRotation == RTCVideoRotation_180); + BOOL sizeIsLandscape = (self.videoFrame.rotation == RTCVideoRotation_0) || + (self.videoFrame.rotation == RTCVideoRotation_180); + + if (useLandscape == sizeIsLandscape) { + return videoFrameSize; + } else { + return CGSizeMake(videoFrameSize.height, videoFrameSize.width); + } +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer) + +- (void)setSize:(CGSize)size { + __weak RTC_OBJC_TYPE(RTCMTLVideoView) *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + RTC_OBJC_TYPE(RTCMTLVideoView) *strongSelf = weakSelf; + + strongSelf.videoFrameSize = size; + CGSize drawableSize = [strongSelf drawableSize]; + + strongSelf.metalView.drawableSize = drawableSize; + [strongSelf setNeedsLayout]; + [strongSelf.delegate videoView:self didChangeVideoSize:size]; + }); +} + +- (void)renderFrame:(nullable RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + if (!self.isEnabled) { + return; + } + + if (frame == nil) { + RTCLogInfo(@"Incoming frame is nil. Exiting render callback."); + return; + } + self.videoFrame = frame; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDefaultShader.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDefaultShader.h new file mode 100644 index 0000000000..71a073ab21 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDefaultShader.h @@ -0,0 +1,23 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoViewShading.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Default RTCVideoViewShading that will be used in RTCNSGLVideoView + * and RTCEAGLVideoView if no external shader is specified. This shader will render + * the video in a rectangle without any color or geometric transformations. + */ +@interface RTCDefaultShader : NSObject <RTC_OBJC_TYPE (RTCVideoViewShading)> + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDefaultShader.mm b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDefaultShader.mm new file mode 100644 index 0000000000..51dca3223d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDefaultShader.mm @@ -0,0 +1,207 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDefaultShader.h" + +#if TARGET_OS_IPHONE +#import <OpenGLES/ES3/gl.h> +#else +#import <OpenGL/gl3.h> +#endif + +#import "RTCOpenGLDefines.h" +#import "RTCShader.h" +#import "base/RTCLogging.h" + +#include "absl/types/optional.h" + +static const int kYTextureUnit = 0; +static const int kUTextureUnit = 1; +static const int kVTextureUnit = 2; +static const int kUvTextureUnit = 1; + +// Fragment shader converts YUV values from input textures into a final RGB +// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php. +static const char kI420FragmentShaderSource[] = + SHADER_VERSION + "precision highp float;" + FRAGMENT_SHADER_IN " vec2 v_texcoord;\n" + "uniform lowp sampler2D s_textureY;\n" + "uniform lowp sampler2D s_textureU;\n" + "uniform lowp sampler2D s_textureV;\n" + FRAGMENT_SHADER_OUT + "void main() {\n" + " float y, u, v, r, g, b;\n" + " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n" + " u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n" + " v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n" + " u = u - 0.5;\n" + " v = v - 0.5;\n" + " r = y + 1.403 * v;\n" + " g = y - 0.344 * u - 0.714 * v;\n" + " b = y + 1.770 * u;\n" + " " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n" + " }\n"; + +static const char kNV12FragmentShaderSource[] = + SHADER_VERSION + "precision mediump float;" + FRAGMENT_SHADER_IN " vec2 v_texcoord;\n" + "uniform lowp sampler2D s_textureY;\n" + "uniform lowp sampler2D s_textureUV;\n" + FRAGMENT_SHADER_OUT + "void main() {\n" + " mediump float y;\n" + " mediump vec2 uv;\n" + " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n" + " uv = " FRAGMENT_SHADER_TEXTURE "(s_textureUV, v_texcoord).ra -\n" + " vec2(0.5, 0.5);\n" + " " FRAGMENT_SHADER_COLOR " = vec4(y + 1.403 * uv.y,\n" + " y - 0.344 * uv.x - 0.714 * uv.y,\n" + " y + 1.770 * uv.x,\n" + " 1.0);\n" + " }\n"; + +@implementation RTCDefaultShader { + GLuint _vertexBuffer; + GLuint _vertexArray; + // Store current rotation and only upload new vertex data when rotation changes. + absl::optional<RTCVideoRotation> _currentRotation; + + GLuint _i420Program; + GLuint _nv12Program; +} + +- (void)dealloc { + glDeleteProgram(_i420Program); + glDeleteProgram(_nv12Program); + glDeleteBuffers(1, &_vertexBuffer); + glDeleteVertexArrays(1, &_vertexArray); +} + +- (BOOL)createAndSetupI420Program { + NSAssert(!_i420Program, @"I420 program already created"); + _i420Program = RTCCreateProgramFromFragmentSource(kI420FragmentShaderSource); + if (!_i420Program) { + return NO; + } + GLint ySampler = glGetUniformLocation(_i420Program, "s_textureY"); + GLint uSampler = glGetUniformLocation(_i420Program, "s_textureU"); + GLint vSampler = glGetUniformLocation(_i420Program, "s_textureV"); + + if (ySampler < 0 || uSampler < 0 || vSampler < 0) { + RTCLog(@"Failed to get uniform variable locations in I420 shader"); + glDeleteProgram(_i420Program); + _i420Program = 0; + return NO; + } + + glUseProgram(_i420Program); + glUniform1i(ySampler, kYTextureUnit); + glUniform1i(uSampler, kUTextureUnit); + glUniform1i(vSampler, kVTextureUnit); + + return YES; +} + +- (BOOL)createAndSetupNV12Program { + NSAssert(!_nv12Program, @"NV12 program already created"); + _nv12Program = RTCCreateProgramFromFragmentSource(kNV12FragmentShaderSource); + if (!_nv12Program) { + return NO; + } + GLint ySampler = glGetUniformLocation(_nv12Program, "s_textureY"); + GLint uvSampler = glGetUniformLocation(_nv12Program, "s_textureUV"); + + if (ySampler < 0 || uvSampler < 0) { + RTCLog(@"Failed to get uniform variable locations in NV12 shader"); + glDeleteProgram(_nv12Program); + _nv12Program = 0; + return NO; + } + + glUseProgram(_nv12Program); + glUniform1i(ySampler, kYTextureUnit); + glUniform1i(uvSampler, kUvTextureUnit); + + return YES; +} + +- (BOOL)prepareVertexBufferWithRotation:(RTCVideoRotation)rotation { + if (!_vertexBuffer && !RTCCreateVertexBuffer(&_vertexBuffer, &_vertexArray)) { + RTCLog(@"Failed to setup vertex buffer"); + return NO; + } +#if !TARGET_OS_IPHONE + glBindVertexArray(_vertexArray); +#endif + glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); + if (!_currentRotation || rotation != *_currentRotation) { + _currentRotation = absl::optional<RTCVideoRotation>(rotation); + RTCSetVertexData(*_currentRotation); + } + return YES; +} + +- (void)applyShadingForFrameWithWidth:(int)width + height:(int)height + rotation:(RTCVideoRotation)rotation + yPlane:(GLuint)yPlane + uPlane:(GLuint)uPlane + vPlane:(GLuint)vPlane { + if (![self prepareVertexBufferWithRotation:rotation]) { + return; + } + + if (!_i420Program && ![self createAndSetupI420Program]) { + RTCLog(@"Failed to setup I420 program"); + return; + } + + glUseProgram(_i420Program); + + glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kYTextureUnit)); + glBindTexture(GL_TEXTURE_2D, yPlane); + + glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kUTextureUnit)); + glBindTexture(GL_TEXTURE_2D, uPlane); + + glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kVTextureUnit)); + glBindTexture(GL_TEXTURE_2D, vPlane); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +- (void)applyShadingForFrameWithWidth:(int)width + height:(int)height + rotation:(RTCVideoRotation)rotation + yPlane:(GLuint)yPlane + uvPlane:(GLuint)uvPlane { + if (![self prepareVertexBufferWithRotation:rotation]) { + return; + } + + if (!_nv12Program && ![self createAndSetupNV12Program]) { + RTCLog(@"Failed to setup NV12 shader"); + return; + } + + glUseProgram(_nv12Program); + + glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kYTextureUnit)); + glBindTexture(GL_TEXTURE_2D, yPlane); + + glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kUvTextureUnit)); + glBindTexture(GL_TEXTURE_2D, uvPlane); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDisplayLinkTimer.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDisplayLinkTimer.h new file mode 100644 index 0000000000..b78501e9e6 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDisplayLinkTimer.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +// RTCDisplayLinkTimer wraps a CADisplayLink and is set to fire every two screen +// refreshes, which should be 30fps. We wrap the display link in order to avoid +// a retain cycle since CADisplayLink takes a strong reference onto its target. +// The timer is paused by default. +@interface RTCDisplayLinkTimer : NSObject + +@property(nonatomic) BOOL isPaused; + +- (instancetype)initWithTimerHandler:(void (^)(void))timerHandler; +- (void)invalidate; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDisplayLinkTimer.m b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDisplayLinkTimer.m new file mode 100644 index 0000000000..906bb898d6 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCDisplayLinkTimer.m @@ -0,0 +1,59 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDisplayLinkTimer.h" + +#import <UIKit/UIKit.h> + +@implementation RTCDisplayLinkTimer { + CADisplayLink *_displayLink; + void (^_timerHandler)(void); +} + +- (instancetype)initWithTimerHandler:(void (^)(void))timerHandler { + NSParameterAssert(timerHandler); + if (self = [super init]) { + _timerHandler = timerHandler; + _displayLink = + [CADisplayLink displayLinkWithTarget:self + selector:@selector(displayLinkDidFire:)]; + _displayLink.paused = YES; +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0 + _displayLink.preferredFramesPerSecond = 30; +#else + [_displayLink setFrameInterval:2]; +#endif + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] + forMode:NSRunLoopCommonModes]; + } + return self; +} + +- (void)dealloc { + [self invalidate]; +} + +- (BOOL)isPaused { + return _displayLink.paused; +} + +- (void)setIsPaused:(BOOL)isPaused { + _displayLink.paused = isPaused; +} + +- (void)invalidate { + [_displayLink invalidate]; +} + +- (void)displayLinkDidFire:(CADisplayLink *)displayLink { + _timerHandler(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCEAGLVideoView.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCEAGLVideoView.h new file mode 100644 index 0000000000..24b26cd602 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCEAGLVideoView.h @@ -0,0 +1,45 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +#import "RTCMacros.h" +#import "RTCVideoRenderer.h" +#import "RTCVideoViewShading.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCEAGLVideoView); + +/** + * RTCEAGLVideoView is an RTCVideoRenderer which renders video frames + * in its bounds using OpenGLES 2.0 or OpenGLES 3.0. + */ +RTC_OBJC_EXPORT +NS_EXTENSION_UNAVAILABLE_IOS("Rendering not available in app extensions.") +@interface RTC_OBJC_TYPE (RTCEAGLVideoView) : UIView <RTC_OBJC_TYPE(RTCVideoRenderer)> + +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCVideoViewDelegate)> delegate; + +- (instancetype)initWithFrame:(CGRect)frame + shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithCoder:(NSCoder *)aDecoder + shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader + NS_DESIGNATED_INITIALIZER; + +/** @abstract Wrapped RTCVideoRotation, or nil. + */ +@property(nonatomic, nullable) NSValue *rotationOverride; +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCEAGLVideoView.m b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCEAGLVideoView.m new file mode 100644 index 0000000000..89e62d2ce7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCEAGLVideoView.m @@ -0,0 +1,295 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCEAGLVideoView.h" + +#import <GLKit/GLKit.h> + +#import "RTCDefaultShader.h" +#import "RTCDisplayLinkTimer.h" +#import "RTCI420TextureCache.h" +#import "RTCNV12TextureCache.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +// RTC_OBJC_TYPE(RTCEAGLVideoView) wraps a GLKView which is setup with +// enableSetNeedsDisplay = NO for the purpose of gaining control of +// exactly when to call -[GLKView display]. This need for extra +// control is required to avoid triggering method calls on GLKView +// that results in attempting to bind the underlying render buffer +// when the drawable size would be empty which would result in the +// error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is +// the method that will trigger the binding of the render +// buffer. Because the standard behaviour of -[UIView setNeedsDisplay] +// is disabled for the reasons above, the RTC_OBJC_TYPE(RTCEAGLVideoView) maintains +// its own `isDirty` flag. + +@interface RTC_OBJC_TYPE (RTCEAGLVideoView) +()<GLKViewDelegate> + // `videoFrame` is set when we receive a frame from a worker thread and is read + // from the display link callback so atomicity is required. + @property(atomic, strong) RTC_OBJC_TYPE(RTCVideoFrame) * videoFrame; +@property(nonatomic, readonly) GLKView *glkView; +@end + +@implementation RTC_OBJC_TYPE (RTCEAGLVideoView) { + RTCDisplayLinkTimer *_timer; + EAGLContext *_glContext; + // This flag should only be set and read on the main thread (e.g. by + // setNeedsDisplay) + BOOL _isDirty; + id<RTC_OBJC_TYPE(RTCVideoViewShading)> _shader; + RTCNV12TextureCache *_nv12TextureCache; + RTCI420TextureCache *_i420TextureCache; + // As timestamps should be unique between frames, will store last + // drawn frame timestamp instead of the whole frame to reduce memory usage. + int64_t _lastDrawnFrameTimeStampNs; +} + +@synthesize delegate = _delegate; +@synthesize videoFrame = _videoFrame; +@synthesize glkView = _glkView; +@synthesize rotationOverride = _rotationOverride; + +- (instancetype)initWithFrame:(CGRect)frame { + return [self initWithFrame:frame shader:[[RTCDefaultShader alloc] init]]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithCoder:aDecoder shader:[[RTCDefaultShader alloc] init]]; +} + +- (instancetype)initWithFrame:(CGRect)frame shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader { + if (self = [super initWithFrame:frame]) { + _shader = shader; + if (![self configure]) { + return nil; + } + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder + shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader { + if (self = [super initWithCoder:aDecoder]) { + _shader = shader; + if (![self configure]) { + return nil; + } + } + return self; +} + +- (BOOL)configure { + EAGLContext *glContext = + [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + if (!glContext) { + glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + } + if (!glContext) { + RTCLogError(@"Failed to create EAGLContext"); + return NO; + } + _glContext = glContext; + + // GLKView manages a framebuffer for us. + _glkView = [[GLKView alloc] initWithFrame:CGRectZero + context:_glContext]; + _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; + _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + _glkView.drawableMultisample = GLKViewDrawableMultisampleNone; + _glkView.delegate = self; + _glkView.layer.masksToBounds = YES; + _glkView.enableSetNeedsDisplay = NO; + [self addSubview:_glkView]; + + // Listen to application state in order to clean up OpenGL before app goes + // away. + NSNotificationCenter *notificationCenter = + [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(willResignActive) + name:UIApplicationWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(didBecomeActive) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + // Frames are received on a separate thread, so we poll for current frame + // using a refresh rate proportional to screen refresh frequency. This + // occurs on the main thread. + __weak RTC_OBJC_TYPE(RTCEAGLVideoView) *weakSelf = self; + _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ + RTC_OBJC_TYPE(RTCEAGLVideoView) *strongSelf = weakSelf; + [strongSelf displayLinkTimerDidFire]; + }]; + if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) { + [self setupGL]; + } + return YES; +} + +- (void)setMultipleTouchEnabled:(BOOL)multipleTouchEnabled { + [super setMultipleTouchEnabled:multipleTouchEnabled]; + _glkView.multipleTouchEnabled = multipleTouchEnabled; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + UIApplicationState appState = + [UIApplication sharedApplication].applicationState; + if (appState == UIApplicationStateActive) { + [self teardownGL]; + } + [_timer invalidate]; + [self ensureGLContext]; + _shader = nil; + if (_glContext && [EAGLContext currentContext] == _glContext) { + [EAGLContext setCurrentContext:nil]; + } +} + +#pragma mark - UIView + +- (void)setNeedsDisplay { + [super setNeedsDisplay]; + _isDirty = YES; +} + +- (void)setNeedsDisplayInRect:(CGRect)rect { + [super setNeedsDisplayInRect:rect]; + _isDirty = YES; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _glkView.frame = self.bounds; +} + +#pragma mark - GLKViewDelegate + +// This method is called when the GLKView's content is dirty and needs to be +// redrawn. This occurs on main thread. +- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { + // The renderer will draw the frame to the framebuffer corresponding to the + // one used by `view`. + RTC_OBJC_TYPE(RTCVideoFrame) *frame = self.videoFrame; + if (!frame || frame.timeStampNs == _lastDrawnFrameTimeStampNs) { + return; + } + RTCVideoRotation rotation = frame.rotation; + if(_rotationOverride != nil) { + [_rotationOverride getValue: &rotation]; + } + [self ensureGLContext]; + glClear(GL_COLOR_BUFFER_BIT); + if ([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { + if (!_nv12TextureCache) { + _nv12TextureCache = [[RTCNV12TextureCache alloc] initWithContext:_glContext]; + } + if (_nv12TextureCache) { + [_nv12TextureCache uploadFrameToTextures:frame]; + [_shader applyShadingForFrameWithWidth:frame.width + height:frame.height + rotation:rotation + yPlane:_nv12TextureCache.yTexture + uvPlane:_nv12TextureCache.uvTexture]; + [_nv12TextureCache releaseTextures]; + + _lastDrawnFrameTimeStampNs = self.videoFrame.timeStampNs; + } + } else { + if (!_i420TextureCache) { + _i420TextureCache = [[RTCI420TextureCache alloc] initWithContext:_glContext]; + } + [_i420TextureCache uploadFrameToTextures:frame]; + [_shader applyShadingForFrameWithWidth:frame.width + height:frame.height + rotation:rotation + yPlane:_i420TextureCache.yTexture + uPlane:_i420TextureCache.uTexture + vPlane:_i420TextureCache.vTexture]; + + _lastDrawnFrameTimeStampNs = self.videoFrame.timeStampNs; + } +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer) + +// These methods may be called on non-main thread. +- (void)setSize:(CGSize)size { + __weak RTC_OBJC_TYPE(RTCEAGLVideoView) *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + RTC_OBJC_TYPE(RTCEAGLVideoView) *strongSelf = weakSelf; + [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; + }); +} + +- (void)renderFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + self.videoFrame = frame; +} + +#pragma mark - Private + +- (void)displayLinkTimerDidFire { + // Don't render unless video frame have changed or the view content + // has explicitly been marked dirty. + if (!_isDirty && _lastDrawnFrameTimeStampNs == self.videoFrame.timeStampNs) { + return; + } + + // Always reset isDirty at this point, even if -[GLKView display] + // won't be called in the case the drawable size is empty. + _isDirty = NO; + + // Only call -[GLKView display] if the drawable size is + // non-empty. Calling display will make the GLKView setup its + // render buffer if necessary, but that will fail with error + // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT if size is empty. + if (self.bounds.size.width > 0 && self.bounds.size.height > 0) { + [_glkView display]; + } +} + +- (void)setupGL { + [self ensureGLContext]; + glDisable(GL_DITHER); + _timer.isPaused = NO; +} + +- (void)teardownGL { + self.videoFrame = nil; + _timer.isPaused = YES; + [_glkView deleteDrawable]; + [self ensureGLContext]; + _nv12TextureCache = nil; + _i420TextureCache = nil; +} + +- (void)didBecomeActive { + [self setupGL]; +} + +- (void)willResignActive { + [self teardownGL]; +} + +- (void)ensureGLContext { + NSAssert(_glContext, @"context shouldn't be nil"); + if ([EAGLContext currentContext] != _glContext) { + [EAGLContext setCurrentContext:_glContext]; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCI420TextureCache.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCI420TextureCache.h new file mode 100644 index 0000000000..9fdcc5a695 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCI420TextureCache.h @@ -0,0 +1,25 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCOpenGLDefines.h" +#import "base/RTCVideoFrame.h" + +@interface RTCI420TextureCache : NSObject + +@property(nonatomic, readonly) GLuint yTexture; +@property(nonatomic, readonly) GLuint uTexture; +@property(nonatomic, readonly) GLuint vTexture; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithContext:(GlContextType *)context NS_DESIGNATED_INITIALIZER; + +- (void)uploadFrameToTextures:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCI420TextureCache.mm b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCI420TextureCache.mm new file mode 100644 index 0000000000..5dccd4bf6a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCI420TextureCache.mm @@ -0,0 +1,157 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCI420TextureCache.h" + +#if TARGET_OS_IPHONE +#import <OpenGLES/ES3/gl.h> +#else +#import <OpenGL/gl3.h> +#endif + +#import "base/RTCI420Buffer.h" +#import "base/RTCVideoFrameBuffer.h" + +#include <vector> + +// Two sets of 3 textures are used here, one for each of the Y, U and V planes. Having two sets +// alleviates CPU blockage in the event that the GPU is asked to render to a texture that is already +// in use. +static const GLsizei kNumTextureSets = 2; +static const GLsizei kNumTexturesPerSet = 3; +static const GLsizei kNumTextures = kNumTexturesPerSet * kNumTextureSets; + +@implementation RTCI420TextureCache { + BOOL _hasUnpackRowLength; + GLint _currentTextureSet; + // Handles for OpenGL constructs. + GLuint _textures[kNumTextures]; + // Used to create a non-padded plane for GPU upload when we receive padded frames. + std::vector<uint8_t> _planeBuffer; +} + +- (GLuint)yTexture { + return _textures[_currentTextureSet * kNumTexturesPerSet]; +} + +- (GLuint)uTexture { + return _textures[_currentTextureSet * kNumTexturesPerSet + 1]; +} + +- (GLuint)vTexture { + return _textures[_currentTextureSet * kNumTexturesPerSet + 2]; +} + +- (instancetype)initWithContext:(GlContextType *)context { + if (self = [super init]) { +#if TARGET_OS_IPHONE + _hasUnpackRowLength = (context.API == kEAGLRenderingAPIOpenGLES3); +#else + _hasUnpackRowLength = YES; +#endif + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + [self setupTextures]; + } + return self; +} + +- (void)dealloc { + glDeleteTextures(kNumTextures, _textures); +} + +- (void)setupTextures { + glGenTextures(kNumTextures, _textures); + // Set parameters for each of the textures we created. + for (GLsizei i = 0; i < kNumTextures; i++) { + glBindTexture(GL_TEXTURE_2D, _textures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +- (void)uploadPlane:(const uint8_t *)plane + texture:(GLuint)texture + width:(size_t)width + height:(size_t)height + stride:(int32_t)stride { + glBindTexture(GL_TEXTURE_2D, texture); + + const uint8_t *uploadPlane = plane; + if ((size_t)stride != width) { + if (_hasUnpackRowLength) { + // GLES3 allows us to specify stride. + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + static_cast<GLsizei>(width), + static_cast<GLsizei>(height), + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + uploadPlane); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + return; + } else { + // Make an unpadded copy and upload that instead. Quick profiling showed + // that this is faster than uploading row by row using glTexSubImage2D. + uint8_t *unpaddedPlane = _planeBuffer.data(); + for (size_t y = 0; y < height; ++y) { + memcpy(unpaddedPlane + y * width, plane + y * stride, width); + } + uploadPlane = unpaddedPlane; + } + } + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + static_cast<GLsizei>(width), + static_cast<GLsizei>(height), + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + uploadPlane); +} + +- (void)uploadFrameToTextures:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets; + + id<RTC_OBJC_TYPE(RTCI420Buffer)> buffer = [frame.buffer toI420]; + + const int chromaWidth = buffer.chromaWidth; + const int chromaHeight = buffer.chromaHeight; + if (buffer.strideY != frame.width || buffer.strideU != chromaWidth || + buffer.strideV != chromaWidth) { + _planeBuffer.resize(buffer.width * buffer.height); + } + + [self uploadPlane:buffer.dataY + texture:self.yTexture + width:buffer.width + height:buffer.height + stride:buffer.strideY]; + + [self uploadPlane:buffer.dataU + texture:self.uTexture + width:chromaWidth + height:chromaHeight + stride:buffer.strideU]; + + [self uploadPlane:buffer.dataV + texture:self.vTexture + width:chromaWidth + height:chromaHeight + stride:buffer.strideV]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNSGLVideoView.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNSGLVideoView.h new file mode 100644 index 0000000000..c9ee986f88 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNSGLVideoView.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#if !TARGET_OS_IPHONE + +#import <AppKit/NSOpenGLView.h> + +#import "RTCVideoRenderer.h" +#import "RTCVideoViewShading.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTC_OBJC_TYPE(RTCNSGLVideoView); + +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCNSGLVideoViewDelegate)<RTC_OBJC_TYPE(RTCVideoViewDelegate)> @end + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCNSGLVideoView) : NSOpenGLView <RTC_OBJC_TYPE(RTCVideoRenderer)> + +@property(nonatomic, weak) id<RTC_OBJC_TYPE(RTCVideoViewDelegate)> delegate; + +- (instancetype)initWithFrame:(NSRect)frameRect + pixelFormat:(NSOpenGLPixelFormat *)format + shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNSGLVideoView.m b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNSGLVideoView.m new file mode 100644 index 0000000000..168c73126f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNSGLVideoView.m @@ -0,0 +1,199 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#if !TARGET_OS_IPHONE + +#import "RTCNSGLVideoView.h" + +#import <AppKit/NSOpenGL.h> +#import <CoreVideo/CVDisplayLink.h> +#import <OpenGL/gl3.h> + +#import "RTCDefaultShader.h" +#import "RTCI420TextureCache.h" +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" + +@interface RTC_OBJC_TYPE (RTCNSGLVideoView) +() + // `videoFrame` is set when we receive a frame from a worker thread and is read + // from the display link callback so atomicity is required. + @property(atomic, strong) RTC_OBJC_TYPE(RTCVideoFrame) * + videoFrame; +@property(atomic, strong) RTCI420TextureCache *i420TextureCache; + +- (void)drawFrame; +@end + +static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink, + const CVTimeStamp *now, + const CVTimeStamp *outputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext) { + RTC_OBJC_TYPE(RTCNSGLVideoView) *view = + (__bridge RTC_OBJC_TYPE(RTCNSGLVideoView) *)displayLinkContext; + [view drawFrame]; + return kCVReturnSuccess; +} + +@implementation RTC_OBJC_TYPE (RTCNSGLVideoView) { + CVDisplayLinkRef _displayLink; + RTC_OBJC_TYPE(RTCVideoFrame) * _lastDrawnFrame; + id<RTC_OBJC_TYPE(RTCVideoViewShading)> _shader; +} + +@synthesize delegate = _delegate; +@synthesize videoFrame = _videoFrame; +@synthesize i420TextureCache = _i420TextureCache; + +- (instancetype)initWithFrame:(NSRect)frame pixelFormat:(NSOpenGLPixelFormat *)format { + return [self initWithFrame:frame pixelFormat:format shader:[[RTCDefaultShader alloc] init]]; +} + +- (instancetype)initWithFrame:(NSRect)frame + pixelFormat:(NSOpenGLPixelFormat *)format + shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader { + if (self = [super initWithFrame:frame pixelFormat:format]) { + _shader = shader; + } + return self; +} + +- (void)dealloc { + [self teardownDisplayLink]; +} + +- (void)drawRect:(NSRect)rect { + [self drawFrame]; +} + +- (void)reshape { + [super reshape]; + NSRect frame = [self frame]; + [self ensureGLContext]; + CGLLockContext([[self openGLContext] CGLContextObj]); + glViewport(0, 0, frame.size.width, frame.size.height); + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} + +- (void)lockFocus { + NSOpenGLContext *context = [self openGLContext]; + [super lockFocus]; + if ([context view] != self) { + [context setView:self]; + } + [context makeCurrentContext]; +} + +- (void)prepareOpenGL { + [super prepareOpenGL]; + [self ensureGLContext]; + glDisable(GL_DITHER); + [self setupDisplayLink]; +} + +- (void)clearGLContext { + [self ensureGLContext]; + self.i420TextureCache = nil; + [super clearGLContext]; +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer) + +// These methods may be called on non-main thread. +- (void)setSize:(CGSize)size { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate videoView:self didChangeVideoSize:size]; + }); +} + +- (void)renderFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + self.videoFrame = frame; +} + +#pragma mark - Private + +- (void)drawFrame { + RTC_OBJC_TYPE(RTCVideoFrame) *frame = self.videoFrame; + if (!frame || frame == _lastDrawnFrame) { + return; + } + // This method may be called from CVDisplayLink callback which isn't on the + // main thread so we have to lock the GL context before drawing. + NSOpenGLContext *context = [self openGLContext]; + CGLLockContext([context CGLContextObj]); + + [self ensureGLContext]; + glClear(GL_COLOR_BUFFER_BIT); + + // Rendering native CVPixelBuffer is not supported on OS X. + // TODO(magjed): Add support for NV12 texture cache on OS X. + frame = [frame newI420VideoFrame]; + if (!self.i420TextureCache) { + self.i420TextureCache = [[RTCI420TextureCache alloc] initWithContext:context]; + } + RTCI420TextureCache *i420TextureCache = self.i420TextureCache; + if (i420TextureCache) { + [i420TextureCache uploadFrameToTextures:frame]; + [_shader applyShadingForFrameWithWidth:frame.width + height:frame.height + rotation:frame.rotation + yPlane:i420TextureCache.yTexture + uPlane:i420TextureCache.uTexture + vPlane:i420TextureCache.vTexture]; + [context flushBuffer]; + _lastDrawnFrame = frame; + } + CGLUnlockContext([context CGLContextObj]); +} + +- (void)setupDisplayLink { + if (_displayLink) { + return; + } + // Synchronize buffer swaps with vertical refresh rate. + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + + // Create display link. + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); + CVDisplayLinkSetOutputCallback(_displayLink, + &OnDisplayLinkFired, + (__bridge void *)self); + // Set the display link for the current renderer. + CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; + CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( + _displayLink, cglContext, cglPixelFormat); + CVDisplayLinkStart(_displayLink); +} + +- (void)teardownDisplayLink { + if (!_displayLink) { + return; + } + CVDisplayLinkRelease(_displayLink); + _displayLink = NULL; +} + +- (void)ensureGLContext { + NSOpenGLContext* context = [self openGLContext]; + NSAssert(context, @"context shouldn't be nil"); + if ([NSOpenGLContext currentContext] != context) { + [context makeCurrentContext]; + } +} + +@end + +#endif // !TARGET_OS_IPHONE diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNV12TextureCache.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNV12TextureCache.h new file mode 100644 index 0000000000..f202b836b5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNV12TextureCache.h @@ -0,0 +1,33 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <GLKit/GLKit.h> + +#import "base/RTCMacros.h" + +@class RTC_OBJC_TYPE(RTCVideoFrame); + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCNV12TextureCache : NSObject + +@property(nonatomic, readonly) GLuint yTexture; +@property(nonatomic, readonly) GLuint uvTexture; + +- (instancetype)init NS_UNAVAILABLE; +- (nullable instancetype)initWithContext:(EAGLContext *)context NS_DESIGNATED_INITIALIZER; + +- (BOOL)uploadFrameToTextures:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame; + +- (void)releaseTextures; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNV12TextureCache.m b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNV12TextureCache.m new file mode 100644 index 0000000000..a520ac45b4 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCNV12TextureCache.m @@ -0,0 +1,113 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCNV12TextureCache.h" + +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +@implementation RTCNV12TextureCache { + CVOpenGLESTextureCacheRef _textureCache; + CVOpenGLESTextureRef _yTextureRef; + CVOpenGLESTextureRef _uvTextureRef; +} + +- (GLuint)yTexture { + return CVOpenGLESTextureGetName(_yTextureRef); +} + +- (GLuint)uvTexture { + return CVOpenGLESTextureGetName(_uvTextureRef); +} + +- (instancetype)initWithContext:(EAGLContext *)context { + if (self = [super init]) { + CVReturn ret = CVOpenGLESTextureCacheCreate( + kCFAllocatorDefault, NULL, +#if COREVIDEO_USE_EAGLCONTEXT_CLASS_IN_API + context, +#else + (__bridge void *)context, +#endif + NULL, &_textureCache); + if (ret != kCVReturnSuccess) { + self = nil; + } + } + return self; +} + +- (BOOL)loadTexture:(CVOpenGLESTextureRef *)textureOut + pixelBuffer:(CVPixelBufferRef)pixelBuffer + planeIndex:(int)planeIndex + pixelFormat:(GLenum)pixelFormat { + const int width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex); + const int height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex); + + if (*textureOut) { + CFRelease(*textureOut); + *textureOut = nil; + } + CVReturn ret = CVOpenGLESTextureCacheCreateTextureFromImage( + kCFAllocatorDefault, _textureCache, pixelBuffer, NULL, GL_TEXTURE_2D, pixelFormat, width, + height, pixelFormat, GL_UNSIGNED_BYTE, planeIndex, textureOut); + if (ret != kCVReturnSuccess) { + if (*textureOut) { + CFRelease(*textureOut); + *textureOut = nil; + } + return NO; + } + NSAssert(CVOpenGLESTextureGetTarget(*textureOut) == GL_TEXTURE_2D, + @"Unexpected GLES texture target"); + glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(*textureOut)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return YES; +} + +- (BOOL)uploadFrameToTextures:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + NSAssert([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]], + @"frame must be CVPixelBuffer backed"); + RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer = (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer; + CVPixelBufferRef pixelBuffer = rtcPixelBuffer.pixelBuffer; + return [self loadTexture:&_yTextureRef + pixelBuffer:pixelBuffer + planeIndex:0 + pixelFormat:GL_LUMINANCE] && + [self loadTexture:&_uvTextureRef + pixelBuffer:pixelBuffer + planeIndex:1 + pixelFormat:GL_LUMINANCE_ALPHA]; +} + +- (void)releaseTextures { + if (_uvTextureRef) { + CFRelease(_uvTextureRef); + _uvTextureRef = nil; + } + if (_yTextureRef) { + CFRelease(_yTextureRef); + _yTextureRef = nil; + } +} + +- (void)dealloc { + [self releaseTextures]; + if (_textureCache) { + CFRelease(_textureCache); + _textureCache = nil; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCOpenGLDefines.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCOpenGLDefines.h new file mode 100644 index 0000000000..4088535861 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCOpenGLDefines.h @@ -0,0 +1,37 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#if TARGET_OS_IPHONE +#define RTC_PIXEL_FORMAT GL_LUMINANCE +#define SHADER_VERSION +#define VERTEX_SHADER_IN "attribute" +#define VERTEX_SHADER_OUT "varying" +#define FRAGMENT_SHADER_IN "varying" +#define FRAGMENT_SHADER_OUT +#define FRAGMENT_SHADER_COLOR "gl_FragColor" +#define FRAGMENT_SHADER_TEXTURE "texture2D" + +@class EAGLContext; +typedef EAGLContext GlContextType; +#else +#define RTC_PIXEL_FORMAT GL_RED +#define SHADER_VERSION "#version 150\n" +#define VERTEX_SHADER_IN "in" +#define VERTEX_SHADER_OUT "out" +#define FRAGMENT_SHADER_IN "in" +#define FRAGMENT_SHADER_OUT "out vec4 fragColor;\n" +#define FRAGMENT_SHADER_COLOR "fragColor" +#define FRAGMENT_SHADER_TEXTURE "texture" + +@class NSOpenGLContext; +typedef NSOpenGLContext GlContextType; +#endif diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCShader.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCShader.h new file mode 100644 index 0000000000..d1b91fb643 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCShader.h @@ -0,0 +1,21 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "base/RTCVideoFrame.h" + +RTC_EXTERN const char kRTCVertexShaderSource[]; + +RTC_EXTERN GLuint RTCCreateShader(GLenum type, const GLchar* source); +RTC_EXTERN GLuint RTCCreateProgram(GLuint vertexShader, GLuint fragmentShader); +RTC_EXTERN GLuint +RTCCreateProgramFromFragmentSource(const char fragmentShaderSource[]); +RTC_EXTERN BOOL RTCCreateVertexBuffer(GLuint* vertexBuffer, + GLuint* vertexArray); +RTC_EXTERN void RTCSetVertexData(RTCVideoRotation rotation); diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCShader.mm b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCShader.mm new file mode 100644 index 0000000000..8eccd7fbec --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCShader.mm @@ -0,0 +1,189 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCShader.h" + +#if TARGET_OS_IPHONE +#import <OpenGLES/ES3/gl.h> +#else +#import <OpenGL/gl3.h> +#endif + +#include <algorithm> +#include <array> +#include <memory> + +#import "RTCOpenGLDefines.h" + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +// Vertex shader doesn't do anything except pass coordinates through. +const char kRTCVertexShaderSource[] = + SHADER_VERSION + VERTEX_SHADER_IN " vec2 position;\n" + VERTEX_SHADER_IN " vec2 texcoord;\n" + VERTEX_SHADER_OUT " vec2 v_texcoord;\n" + "void main() {\n" + " gl_Position = vec4(position.x, position.y, 0.0, 1.0);\n" + " v_texcoord = texcoord;\n" + "}\n"; + +// Compiles a shader of the given `type` with GLSL source `source` and returns +// the shader handle or 0 on error. +GLuint RTCCreateShader(GLenum type, const GLchar *source) { + GLuint shader = glCreateShader(type); + if (!shader) { + return 0; + } + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + GLint compileStatus = GL_FALSE; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); + if (compileStatus == GL_FALSE) { + GLint logLength = 0; + // The null termination character is included in the returned log length. + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + std::unique_ptr<char[]> compileLog(new char[logLength]); + // The returned string is null terminated. + glGetShaderInfoLog(shader, logLength, NULL, compileLog.get()); + RTC_LOG(LS_ERROR) << "Shader compile error: " << compileLog.get(); + } + glDeleteShader(shader); + shader = 0; + } + return shader; +} + +// Links a shader program with the given vertex and fragment shaders and +// returns the program handle or 0 on error. +GLuint RTCCreateProgram(GLuint vertexShader, GLuint fragmentShader) { + if (vertexShader == 0 || fragmentShader == 0) { + return 0; + } + GLuint program = glCreateProgram(); + if (!program) { + return 0; + } + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus == GL_FALSE) { + glDeleteProgram(program); + program = 0; + } + return program; +} + +// Creates and links a shader program with the given fragment shader source and +// a plain vertex shader. Returns the program handle or 0 on error. +GLuint RTCCreateProgramFromFragmentSource(const char fragmentShaderSource[]) { + GLuint vertexShader = RTCCreateShader(GL_VERTEX_SHADER, kRTCVertexShaderSource); + RTC_CHECK(vertexShader) << "failed to create vertex shader"; + GLuint fragmentShader = + RTCCreateShader(GL_FRAGMENT_SHADER, fragmentShaderSource); + RTC_CHECK(fragmentShader) << "failed to create fragment shader"; + GLuint program = RTCCreateProgram(vertexShader, fragmentShader); + // Shaders are created only to generate program. + if (vertexShader) { + glDeleteShader(vertexShader); + } + if (fragmentShader) { + glDeleteShader(fragmentShader); + } + + // Set vertex shader variables 'position' and 'texcoord' in program. + GLint position = glGetAttribLocation(program, "position"); + GLint texcoord = glGetAttribLocation(program, "texcoord"); + if (position < 0 || texcoord < 0) { + glDeleteProgram(program); + return 0; + } + + // Read position attribute with size of 2 and stride of 4 beginning at the start of the array. The + // last argument indicates offset of data within the vertex buffer. + glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)0); + glEnableVertexAttribArray(position); + + // Read texcoord attribute with size of 2 and stride of 4 beginning at the first texcoord in the + // array. The last argument indicates offset of data within the vertex buffer. + glVertexAttribPointer( + texcoord, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)(2 * sizeof(GLfloat))); + glEnableVertexAttribArray(texcoord); + + return program; +} + +BOOL RTCCreateVertexBuffer(GLuint *vertexBuffer, GLuint *vertexArray) { +#if !TARGET_OS_IPHONE + glGenVertexArrays(1, vertexArray); + if (*vertexArray == 0) { + return NO; + } + glBindVertexArray(*vertexArray); +#endif + glGenBuffers(1, vertexBuffer); + if (*vertexBuffer == 0) { + glDeleteVertexArrays(1, vertexArray); + return NO; + } + glBindBuffer(GL_ARRAY_BUFFER, *vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW); + return YES; +} + +// Set vertex data to the currently bound vertex buffer. +void RTCSetVertexData(RTCVideoRotation rotation) { + // When modelview and projection matrices are identity (default) the world is + // contained in the square around origin with unit size 2. Drawing to these + // coordinates is equivalent to drawing to the entire screen. The texture is + // stretched over that square using texture coordinates (u, v) that range + // from (0, 0) to (1, 1) inclusive. Texture coordinates are flipped vertically + // here because the incoming frame has origin in upper left hand corner but + // OpenGL expects origin in bottom left corner. + std::array<std::array<GLfloat, 2>, 4> UVCoords = {{ + {{0, 1}}, // Lower left. + {{1, 1}}, // Lower right. + {{1, 0}}, // Upper right. + {{0, 0}}, // Upper left. + }}; + + // Rotate the UV coordinates. + int rotation_offset; + switch (rotation) { + case RTCVideoRotation_0: + rotation_offset = 0; + break; + case RTCVideoRotation_90: + rotation_offset = 1; + break; + case RTCVideoRotation_180: + rotation_offset = 2; + break; + case RTCVideoRotation_270: + rotation_offset = 3; + break; + } + std::rotate(UVCoords.begin(), UVCoords.begin() + rotation_offset, + UVCoords.end()); + + const GLfloat gVertices[] = { + // X, Y, U, V. + -1, -1, UVCoords[0][0], UVCoords[0][1], + 1, -1, UVCoords[1][0], UVCoords[1][1], + 1, 1, UVCoords[2][0], UVCoords[2][1], + -1, 1, UVCoords[3][0], UVCoords[3][1], + }; + + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(gVertices), gVertices); +} diff --git a/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCVideoViewShading.h b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCVideoViewShading.h new file mode 100644 index 0000000000..9df30a8fa0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/renderer/opengl/RTCVideoViewShading.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCVideoFrame.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * RTCVideoViewShading provides a way for apps to customize the OpenGL(ES shaders + * used in rendering for the RTCEAGLVideoView/RTCNSGLVideoView. + */ +RTC_OBJC_EXPORT +@protocol RTC_OBJC_TYPE +(RTCVideoViewShading)<NSObject> + + /** Callback for I420 frames. Each plane is given as a texture. */ + - (void)applyShadingForFrameWithWidth : (int)width height : (int)height rotation + : (RTCVideoRotation)rotation yPlane : (GLuint)yPlane uPlane : (GLuint)uPlane vPlane + : (GLuint)vPlane; + +/** Callback for NV12 frames. Each plane is given as a texture. */ +- (void)applyShadingForFrameWithWidth:(int)width + height:(int)height + rotation:(RTCVideoRotation)rotation + yPlane:(GLuint)yPlane + uvPlane:(GLuint)uvPlane; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264+Private.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264+Private.h new file mode 100644 index 0000000000..a0cd8515d1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264+Private.h @@ -0,0 +1,25 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCodecSpecificInfoH264.h" + +#include "modules/video_coding/include/video_codec_interface.h" + +NS_ASSUME_NONNULL_BEGIN + +/* Interfaces for converting to/from internal C++ formats. */ +@interface RTC_OBJC_TYPE (RTCCodecSpecificInfoH264) +() + + - (webrtc::CodecSpecificInfo)nativeCodecSpecificInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264.h new file mode 100644 index 0000000000..ae3003a115 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264.h @@ -0,0 +1,27 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCCodecSpecificInfo.h" +#import "RTCMacros.h" + +/** Class for H264 specific config. */ +typedef NS_ENUM(NSUInteger, RTCH264PacketizationMode) { + RTCH264PacketizationModeNonInterleaved = 0, // Mode 1 - STAP-A, FU-A is allowed + RTCH264PacketizationModeSingleNalUnit // Mode 0 - only single NALU allowed +}; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCCodecSpecificInfoH264) : NSObject <RTC_OBJC_TYPE(RTCCodecSpecificInfo)> + +@property(nonatomic, assign) RTCH264PacketizationMode packetizationMode; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264.mm b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264.mm new file mode 100644 index 0000000000..e38ed307b3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCCodecSpecificInfoH264.mm @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCodecSpecificInfoH264+Private.h" + +#import "RTCH264ProfileLevelId.h" + +// H264 specific settings. +@implementation RTC_OBJC_TYPE (RTCCodecSpecificInfoH264) + +@synthesize packetizationMode = _packetizationMode; + +- (webrtc::CodecSpecificInfo)nativeCodecSpecificInfo { + webrtc::CodecSpecificInfo codecSpecificInfo; + codecSpecificInfo.codecType = webrtc::kVideoCodecH264; + codecSpecificInfo.codecSpecific.H264.packetization_mode = + (webrtc::H264PacketizationMode)_packetizationMode; + + return codecSpecificInfo; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h new file mode 100644 index 0000000000..de5a9c4684 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoderFactory.h" + +NS_ASSUME_NONNULL_BEGIN + +/** This decoder factory include support for all codecs bundled with WebRTC. If using custom + * codecs, create custom implementations of RTCVideoEncoderFactory and + * RTCVideoDecoderFactory. + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCDefaultVideoDecoderFactory) : NSObject <RTC_OBJC_TYPE(RTCVideoDecoderFactory)> +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.m b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.m new file mode 100644 index 0000000000..6e3baa8750 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.m @@ -0,0 +1,85 @@ +/* + * Copyright 2017 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDefaultVideoDecoderFactory.h" + +#import "RTCH264ProfileLevelId.h" +#import "RTCVideoDecoderH264.h" +#import "api/video_codec/RTCVideoCodecConstants.h" +#import "api/video_codec/RTCVideoDecoderVP8.h" +#import "api/video_codec/RTCVideoDecoderVP9.h" +#import "base/RTCVideoCodecInfo.h" + +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) +#import "api/video_codec/RTCVideoDecoderAV1.h" // nogncheck +#endif + +@implementation RTC_OBJC_TYPE (RTCDefaultVideoDecoderFactory) + +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs { + NSDictionary<NSString *, NSString *> *constrainedHighParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedHigh, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedHighInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecH264Name + parameters:constrainedHighParams]; + + NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedBaseline, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedBaselineInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecH264Name + parameters:constrainedBaselineParams]; + + RTC_OBJC_TYPE(RTCVideoCodecInfo) *vp8Info = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecVp8Name]; + + NSMutableArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *result = [@[ + constrainedHighInfo, + constrainedBaselineInfo, + vp8Info, + ] mutableCopy]; + + if ([RTC_OBJC_TYPE(RTCVideoDecoderVP9) isSupported]) { + [result + addObject:[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecVp9Name]]; + } + +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) + [result addObject:[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecAv1Name]]; +#endif + + return result; +} + +- (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)createDecoder:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info { + if ([info.name isEqualToString:kRTCVideoCodecH264Name]) { + return [[RTC_OBJC_TYPE(RTCVideoDecoderH264) alloc] init]; + } else if ([info.name isEqualToString:kRTCVideoCodecVp8Name]) { + return [RTC_OBJC_TYPE(RTCVideoDecoderVP8) vp8Decoder]; + } else if ([info.name isEqualToString:kRTCVideoCodecVp9Name] && + [RTC_OBJC_TYPE(RTCVideoDecoderVP9) isSupported]) { + return [RTC_OBJC_TYPE(RTCVideoDecoderVP9) vp9Decoder]; + } + +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) + if ([info.name isEqualToString:kRTCVideoCodecAv1Name]) { + return [RTC_OBJC_TYPE(RTCVideoDecoderAV1) av1Decoder]; + } +#endif + + return nil; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.h new file mode 100644 index 0000000000..92ab40c95b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.h @@ -0,0 +1,31 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoderFactory.h" + +NS_ASSUME_NONNULL_BEGIN + +/** This encoder factory include support for all codecs bundled with WebRTC. If using custom + * codecs, create custom implementations of RTCVideoEncoderFactory and + * RTCVideoDecoderFactory. + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCDefaultVideoEncoderFactory) : NSObject <RTC_OBJC_TYPE(RTCVideoEncoderFactory)> + +@property(nonatomic, retain) RTC_OBJC_TYPE(RTCVideoCodecInfo) *preferredCodec; + ++ (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.m b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.m new file mode 100644 index 0000000000..8de55bde4a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.m @@ -0,0 +1,102 @@ +/* + * Copyright 2017 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDefaultVideoEncoderFactory.h" + +#import "RTCH264ProfileLevelId.h" +#import "RTCVideoEncoderH264.h" +#import "api/video_codec/RTCVideoCodecConstants.h" +#import "api/video_codec/RTCVideoEncoderVP8.h" +#import "api/video_codec/RTCVideoEncoderVP9.h" +#import "base/RTCVideoCodecInfo.h" + +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) +#import "api/video_codec/RTCVideoEncoderAV1.h" // nogncheck +#endif + +@implementation RTC_OBJC_TYPE (RTCDefaultVideoEncoderFactory) + +@synthesize preferredCodec; + ++ (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs { + NSDictionary<NSString *, NSString *> *constrainedHighParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedHigh, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedHighInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecH264Name + parameters:constrainedHighParams]; + + NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedBaseline, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedBaselineInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecH264Name + parameters:constrainedBaselineParams]; + + RTC_OBJC_TYPE(RTCVideoCodecInfo) *vp8Info = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecVp8Name]; + + NSMutableArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *result = [@[ + constrainedHighInfo, + constrainedBaselineInfo, + vp8Info, + ] mutableCopy]; + + if ([RTC_OBJC_TYPE(RTCVideoEncoderVP9) isSupported]) { + [result + addObject:[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecVp9Name]]; + } + +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) + [result addObject:[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:kRTCVideoCodecAv1Name]]; +#endif + + return result; +} + +- (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)createEncoder:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info { + if ([info.name isEqualToString:kRTCVideoCodecH264Name]) { + return [[RTC_OBJC_TYPE(RTCVideoEncoderH264) alloc] initWithCodecInfo:info]; + } else if ([info.name isEqualToString:kRTCVideoCodecVp8Name]) { + return [RTC_OBJC_TYPE(RTCVideoEncoderVP8) vp8Encoder]; + } else if ([info.name isEqualToString:kRTCVideoCodecVp9Name] && + [RTC_OBJC_TYPE(RTCVideoEncoderVP9) isSupported]) { + return [RTC_OBJC_TYPE(RTCVideoEncoderVP9) vp9Encoder]; + } + +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) + if ([info.name isEqualToString:kRTCVideoCodecAv1Name]) { + return [RTC_OBJC_TYPE(RTCVideoEncoderAV1) av1Encoder]; + } +#endif + + return nil; +} + +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs { + NSMutableArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *codecs = + [[[self class] supportedCodecs] mutableCopy]; + + NSMutableArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *orderedCodecs = [NSMutableArray array]; + NSUInteger index = [codecs indexOfObject:self.preferredCodec]; + if (index != NSNotFound) { + [orderedCodecs addObject:[codecs objectAtIndex:index]]; + [codecs removeObjectAtIndex:index]; + } + [orderedCodecs addObjectsFromArray:codecs]; + + return [orderedCodecs copy]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCH264ProfileLevelId.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCH264ProfileLevelId.h new file mode 100644 index 0000000000..dac7bb5610 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCH264ProfileLevelId.h @@ -0,0 +1,60 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +RTC_OBJC_EXPORT extern NSString *const kRTCVideoCodecH264Name; +RTC_OBJC_EXPORT extern NSString *const kRTCLevel31ConstrainedHigh; +RTC_OBJC_EXPORT extern NSString *const kRTCLevel31ConstrainedBaseline; +RTC_OBJC_EXPORT extern NSString *const kRTCMaxSupportedH264ProfileLevelConstrainedHigh; +RTC_OBJC_EXPORT extern NSString *const kRTCMaxSupportedH264ProfileLevelConstrainedBaseline; + +/** H264 Profiles and levels. */ +typedef NS_ENUM(NSUInteger, RTCH264Profile) { + RTCH264ProfileConstrainedBaseline, + RTCH264ProfileBaseline, + RTCH264ProfileMain, + RTCH264ProfileConstrainedHigh, + RTCH264ProfileHigh, +}; + +typedef NS_ENUM(NSUInteger, RTCH264Level) { + RTCH264Level1_b = 0, + RTCH264Level1 = 10, + RTCH264Level1_1 = 11, + RTCH264Level1_2 = 12, + RTCH264Level1_3 = 13, + RTCH264Level2 = 20, + RTCH264Level2_1 = 21, + RTCH264Level2_2 = 22, + RTCH264Level3 = 30, + RTCH264Level3_1 = 31, + RTCH264Level3_2 = 32, + RTCH264Level4 = 40, + RTCH264Level4_1 = 41, + RTCH264Level4_2 = 42, + RTCH264Level5 = 50, + RTCH264Level5_1 = 51, + RTCH264Level5_2 = 52 +}; + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCH264ProfileLevelId) : NSObject + +@property(nonatomic, readonly) RTCH264Profile profile; +@property(nonatomic, readonly) RTCH264Level level; +@property(nonatomic, readonly) NSString *hexString; + +- (instancetype)initWithHexString:(NSString *)hexString; +- (instancetype)initWithProfile:(RTCH264Profile)profile level:(RTCH264Level)level; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCH264ProfileLevelId.mm b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCH264ProfileLevelId.mm new file mode 100644 index 0000000000..f0ef3ec232 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCH264ProfileLevelId.mm @@ -0,0 +1,120 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import "RTCH264ProfileLevelId.h" + +#import "helpers/NSString+StdString.h" +#if defined(WEBRTC_IOS) +#import "UIDevice+H264Profile.h" +#endif + +#include "api/video_codecs/h264_profile_level_id.h" +#include "media/base/media_constants.h" + +namespace { + +NSString *MaxSupportedProfileLevelConstrainedHigh(); +NSString *MaxSupportedProfileLevelConstrainedBaseline(); + +} // namespace + +NSString *const kRTCVideoCodecH264Name = @(cricket::kH264CodecName); +NSString *const kRTCLevel31ConstrainedHigh = @"640c1f"; +NSString *const kRTCLevel31ConstrainedBaseline = @"42e01f"; +NSString *const kRTCMaxSupportedH264ProfileLevelConstrainedHigh = + MaxSupportedProfileLevelConstrainedHigh(); +NSString *const kRTCMaxSupportedH264ProfileLevelConstrainedBaseline = + MaxSupportedProfileLevelConstrainedBaseline(); + +namespace { + +#if defined(WEBRTC_IOS) + +NSString *MaxSupportedLevelForProfile(webrtc::H264Profile profile) { + const absl::optional<webrtc::H264ProfileLevelId> profileLevelId = + [UIDevice maxSupportedH264Profile]; + if (profileLevelId && profileLevelId->profile >= profile) { + const absl::optional<std::string> profileString = + H264ProfileLevelIdToString(webrtc::H264ProfileLevelId(profile, profileLevelId->level)); + if (profileString) { + return [NSString stringForStdString:*profileString]; + } + } + return nil; +} +#endif + +NSString *MaxSupportedProfileLevelConstrainedBaseline() { +#if defined(WEBRTC_IOS) + NSString *profile = MaxSupportedLevelForProfile(webrtc::H264Profile::kProfileConstrainedBaseline); + if (profile != nil) { + return profile; + } +#endif + return kRTCLevel31ConstrainedBaseline; +} + +NSString *MaxSupportedProfileLevelConstrainedHigh() { +#if defined(WEBRTC_IOS) + NSString *profile = MaxSupportedLevelForProfile(webrtc::H264Profile::kProfileConstrainedHigh); + if (profile != nil) { + return profile; + } +#endif + return kRTCLevel31ConstrainedHigh; +} + +} // namespace + +@interface RTC_OBJC_TYPE (RTCH264ProfileLevelId) +() + + @property(nonatomic, assign) RTCH264Profile profile; +@property(nonatomic, assign) RTCH264Level level; +@property(nonatomic, strong) NSString *hexString; + +@end + +@implementation RTC_OBJC_TYPE (RTCH264ProfileLevelId) + +@synthesize profile = _profile; +@synthesize level = _level; +@synthesize hexString = _hexString; + +- (instancetype)initWithHexString:(NSString *)hexString { + if (self = [super init]) { + self.hexString = hexString; + + absl::optional<webrtc::H264ProfileLevelId> profile_level_id = + webrtc::ParseH264ProfileLevelId([hexString cStringUsingEncoding:NSUTF8StringEncoding]); + if (profile_level_id.has_value()) { + self.profile = static_cast<RTCH264Profile>(profile_level_id->profile); + self.level = static_cast<RTCH264Level>(profile_level_id->level); + } + } + return self; +} + +- (instancetype)initWithProfile:(RTCH264Profile)profile level:(RTCH264Level)level { + if (self = [super init]) { + self.profile = profile; + self.level = level; + + absl::optional<std::string> hex_string = + webrtc::H264ProfileLevelIdToString(webrtc::H264ProfileLevelId( + static_cast<webrtc::H264Profile>(profile), static_cast<webrtc::H264Level>(level))); + self.hexString = + [NSString stringWithCString:hex_string.value_or("").c_str() encoding:NSUTF8StringEncoding]; + } + return self; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.h new file mode 100644 index 0000000000..88bacbbdfe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.h @@ -0,0 +1,18 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoderFactory.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoDecoderFactoryH264) : NSObject <RTC_OBJC_TYPE(RTCVideoDecoderFactory)> +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.m b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.m new file mode 100644 index 0000000000..bdae19d687 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.m @@ -0,0 +1,49 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoDecoderFactoryH264.h" + +#import "RTCH264ProfileLevelId.h" +#import "RTCVideoDecoderH264.h" + +@implementation RTC_OBJC_TYPE (RTCVideoDecoderFactoryH264) + +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs { + NSMutableArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *codecs = [NSMutableArray array]; + NSString *codecName = kRTCVideoCodecH264Name; + + NSDictionary<NSString *, NSString *> *constrainedHighParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedHigh, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedHighInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:codecName + parameters:constrainedHighParams]; + [codecs addObject:constrainedHighInfo]; + + NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedBaseline, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedBaselineInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:codecName + parameters:constrainedBaselineParams]; + [codecs addObject:constrainedBaselineInfo]; + + return [codecs copy]; +} + +- (id<RTC_OBJC_TYPE(RTCVideoDecoder)>)createDecoder:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info { + return [[RTC_OBJC_TYPE(RTCVideoDecoderH264) alloc] init]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderH264.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderH264.h new file mode 100644 index 0000000000..a12e4212a7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderH264.h @@ -0,0 +1,18 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoDecoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoDecoderH264) : NSObject <RTC_OBJC_TYPE(RTCVideoDecoder)> +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderH264.mm b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderH264.mm new file mode 100644 index 0000000000..09e642bc37 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoDecoderH264.mm @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import "RTCVideoDecoderH264.h" + +#import <VideoToolbox/VideoToolbox.h> + +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#import "helpers.h" +#import "helpers/scoped_cftyperef.h" + +#if defined(WEBRTC_IOS) +#import "helpers/UIDevice+RTCDevice.h" +#endif + +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "sdk/objc/components/video_codec/nalu_rewriter.h" + +// Struct that we pass to the decoder per frame to decode. We receive it again +// in the decoder callback. +struct RTCFrameDecodeParams { + RTCFrameDecodeParams(RTCVideoDecoderCallback cb, int64_t ts) : callback(cb), timestamp(ts) {} + RTCVideoDecoderCallback callback; + int64_t timestamp; +}; + +@interface RTC_OBJC_TYPE (RTCVideoDecoderH264) +() - (void)setError : (OSStatus)error; +@end + +// This is the callback function that VideoToolbox calls when decode is +// complete. +void decompressionOutputCallback(void *decoderRef, + void *params, + OSStatus status, + VTDecodeInfoFlags infoFlags, + CVImageBufferRef imageBuffer, + CMTime timestamp, + CMTime duration) { + std::unique_ptr<RTCFrameDecodeParams> decodeParams( + reinterpret_cast<RTCFrameDecodeParams *>(params)); + if (status != noErr) { + RTC_OBJC_TYPE(RTCVideoDecoderH264) *decoder = + (__bridge RTC_OBJC_TYPE(RTCVideoDecoderH264) *)decoderRef; + [decoder setError:status]; + RTC_LOG(LS_ERROR) << "Failed to decode frame. Status: " << status; + return; + } + // TODO(tkchin): Handle CVO properly. + RTC_OBJC_TYPE(RTCCVPixelBuffer) *frameBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:imageBuffer]; + RTC_OBJC_TYPE(RTCVideoFrame) *decodedFrame = [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] + initWithBuffer:frameBuffer + rotation:RTCVideoRotation_0 + timeStampNs:CMTimeGetSeconds(timestamp) * rtc::kNumNanosecsPerSec]; + decodedFrame.timeStamp = decodeParams->timestamp; + decodeParams->callback(decodedFrame); +} + +// Decoder. +@implementation RTC_OBJC_TYPE (RTCVideoDecoderH264) { + CMVideoFormatDescriptionRef _videoFormat; + CMMemoryPoolRef _memoryPool; + VTDecompressionSessionRef _decompressionSession; + RTCVideoDecoderCallback _callback; + OSStatus _error; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _memoryPool = CMMemoryPoolCreate(nil); + } + return self; +} + +- (void)dealloc { + CMMemoryPoolInvalidate(_memoryPool); + CFRelease(_memoryPool); + [self destroyDecompressionSession]; + [self setVideoFormat:nullptr]; +} + +- (NSInteger)startDecodeWithNumberOfCores:(int)numberOfCores { + return WEBRTC_VIDEO_CODEC_OK; +} + +- (NSInteger)decode:(RTC_OBJC_TYPE(RTCEncodedImage) *)inputImage + missingFrames:(BOOL)missingFrames + codecSpecificInfo:(nullable id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)info + renderTimeMs:(int64_t)renderTimeMs { + RTC_DCHECK(inputImage.buffer); + + if (_error != noErr) { + RTC_LOG(LS_WARNING) << "Last frame decode failed."; + _error = noErr; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + rtc::ScopedCFTypeRef<CMVideoFormatDescriptionRef> inputFormat = + rtc::ScopedCF(webrtc::CreateVideoFormatDescription((uint8_t *)inputImage.buffer.bytes, + inputImage.buffer.length)); + if (inputFormat) { + // Check if the video format has changed, and reinitialize decoder if + // needed. + if (!CMFormatDescriptionEqual(inputFormat.get(), _videoFormat)) { + [self setVideoFormat:inputFormat.get()]; + int resetDecompressionSessionError = [self resetDecompressionSession]; + if (resetDecompressionSessionError != WEBRTC_VIDEO_CODEC_OK) { + return resetDecompressionSessionError; + } + } + } + if (!_videoFormat) { + // We received a frame but we don't have format information so we can't + // decode it. + // This can happen after backgrounding. We need to wait for the next + // sps/pps before we can resume so we request a keyframe by returning an + // error. + RTC_LOG(LS_WARNING) << "Missing video format. Frame with sps/pps required."; + return WEBRTC_VIDEO_CODEC_ERROR; + } + CMSampleBufferRef sampleBuffer = nullptr; + if (!webrtc::H264AnnexBBufferToCMSampleBuffer((uint8_t *)inputImage.buffer.bytes, + inputImage.buffer.length, + _videoFormat, + &sampleBuffer, + _memoryPool)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + RTC_DCHECK(sampleBuffer); + VTDecodeFrameFlags decodeFlags = kVTDecodeFrame_EnableAsynchronousDecompression; + std::unique_ptr<RTCFrameDecodeParams> frameDecodeParams; + frameDecodeParams.reset(new RTCFrameDecodeParams(_callback, inputImage.timeStamp)); + OSStatus status = VTDecompressionSessionDecodeFrame( + _decompressionSession, sampleBuffer, decodeFlags, frameDecodeParams.release(), nullptr); +#if defined(WEBRTC_IOS) + // Re-initialize the decoder if we have an invalid session while the app is + // active or decoder malfunctions and retry the decode request. + if ((status == kVTInvalidSessionErr || status == kVTVideoDecoderMalfunctionErr) && + [self resetDecompressionSession] == WEBRTC_VIDEO_CODEC_OK) { + RTC_LOG(LS_INFO) << "Failed to decode frame with code: " << status + << " retrying decode after decompression session reset"; + frameDecodeParams.reset(new RTCFrameDecodeParams(_callback, inputImage.timeStamp)); + status = VTDecompressionSessionDecodeFrame( + _decompressionSession, sampleBuffer, decodeFlags, frameDecodeParams.release(), nullptr); + } +#endif + CFRelease(sampleBuffer); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to decode frame with code: " << status; + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +- (void)setCallback:(RTCVideoDecoderCallback)callback { + _callback = callback; +} + +- (void)setError:(OSStatus)error { + _error = error; +} + +- (NSInteger)releaseDecoder { + // Need to invalidate the session so that callbacks no longer occur and it + // is safe to null out the callback. + [self destroyDecompressionSession]; + [self setVideoFormat:nullptr]; + _callback = nullptr; + return WEBRTC_VIDEO_CODEC_OK; +} + +#pragma mark - Private + +- (int)resetDecompressionSession { + [self destroyDecompressionSession]; + + // Need to wait for the first SPS to initialize decoder. + if (!_videoFormat) { + return WEBRTC_VIDEO_CODEC_OK; + } + + // Set keys for OpenGL and IOSurface compatibilty, which makes the encoder + // create pixel buffers with GPU backed memory. The intent here is to pass + // the pixel buffers directly so we avoid a texture upload later during + // rendering. This currently is moot because we are converting back to an + // I420 frame after decode, but eventually we will be able to plumb + // CVPixelBuffers directly to the renderer. + // TODO(tkchin): Maybe only set OpenGL/IOSurface keys if we know that that + // we can pass CVPixelBuffers as native handles in decoder output. + NSDictionary *attributes = @{ +#if defined(WEBRTC_IOS) && (TARGET_OS_MACCATALYST || TARGET_OS_SIMULATOR) + (NSString *)kCVPixelBufferMetalCompatibilityKey : @(YES), +#elif defined(WEBRTC_IOS) + (NSString *)kCVPixelBufferOpenGLESCompatibilityKey : @(YES), +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_ARCH_ARM64) + (NSString *)kCVPixelBufferOpenGLCompatibilityKey : @(YES), +#endif +#if !(TARGET_OS_SIMULATOR) + (NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{}, +#endif + (NSString *) + kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), + }; + + VTDecompressionOutputCallbackRecord record = { + decompressionOutputCallback, (__bridge void *)self, + }; + OSStatus status = VTDecompressionSessionCreate(nullptr, + _videoFormat, + nullptr, + (__bridge CFDictionaryRef)attributes, + &record, + &_decompressionSession); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to create decompression session: " << status; + [self destroyDecompressionSession]; + return WEBRTC_VIDEO_CODEC_ERROR; + } + [self configureDecompressionSession]; + + return WEBRTC_VIDEO_CODEC_OK; +} + +- (void)configureDecompressionSession { + RTC_DCHECK(_decompressionSession); +#if defined(WEBRTC_IOS) + VTSessionSetProperty(_decompressionSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue); +#endif +} + +- (void)destroyDecompressionSession { + if (_decompressionSession) { +#if defined(WEBRTC_IOS) + if ([UIDevice isIOS11OrLater]) { + VTDecompressionSessionWaitForAsynchronousFrames(_decompressionSession); + } +#endif + VTDecompressionSessionInvalidate(_decompressionSession); + CFRelease(_decompressionSession); + _decompressionSession = nullptr; + } +} + +- (void)setVideoFormat:(CMVideoFormatDescriptionRef)videoFormat { + if (_videoFormat == videoFormat) { + return; + } + if (_videoFormat) { + CFRelease(_videoFormat); + } + _videoFormat = videoFormat; + if (_videoFormat) { + CFRetain(_videoFormat); + } +} + +- (NSString *)implementationName { + return @"VideoToolbox"; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.h new file mode 100644 index 0000000000..45fc4be2ea --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.h @@ -0,0 +1,18 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoEncoderFactory.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderFactoryH264) : NSObject <RTC_OBJC_TYPE(RTCVideoEncoderFactory)> +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.m b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.m new file mode 100644 index 0000000000..9843849307 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.m @@ -0,0 +1,49 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCVideoEncoderFactoryH264.h" + +#import "RTCH264ProfileLevelId.h" +#import "RTCVideoEncoderH264.h" + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderFactoryH264) + +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs { + NSMutableArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *codecs = [NSMutableArray array]; + NSString *codecName = kRTCVideoCodecH264Name; + + NSDictionary<NSString *, NSString *> *constrainedHighParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedHigh, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedHighInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:codecName + parameters:constrainedHighParams]; + [codecs addObject:constrainedHighInfo]; + + NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{ + @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedBaseline, + @"level-asymmetry-allowed" : @"1", + @"packetization-mode" : @"1", + }; + RTC_OBJC_TYPE(RTCVideoCodecInfo) *constrainedBaselineInfo = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:codecName + parameters:constrainedBaselineParams]; + [codecs addObject:constrainedBaselineInfo]; + + return [codecs copy]; +} + +- (id<RTC_OBJC_TYPE(RTCVideoEncoder)>)createEncoder:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info { + return [[RTC_OBJC_TYPE(RTCVideoEncoderH264) alloc] initWithCodecInfo:info]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.h b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.h new file mode 100644 index 0000000000..9f4f4c7c8d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.h @@ -0,0 +1,22 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" +#import "RTCVideoCodecInfo.h" +#import "RTCVideoEncoder.h" + +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCVideoEncoderH264) : NSObject <RTC_OBJC_TYPE(RTCVideoEncoder)> + +- (instancetype)initWithCodecInfo:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)codecInfo; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.mm b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.mm new file mode 100644 index 0000000000..2160d79ae5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/RTCVideoEncoderH264.mm @@ -0,0 +1,828 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#import "RTCVideoEncoderH264.h" + +#import <VideoToolbox/VideoToolbox.h> +#include <vector> + +#if defined(WEBRTC_IOS) +#import "helpers/UIDevice+RTCDevice.h" +#endif +#import "RTCCodecSpecificInfoH264.h" +#import "RTCH264ProfileLevelId.h" +#import "api/peerconnection/RTCVideoCodecInfo+Private.h" +#import "base/RTCCodecSpecificInfo.h" +#import "base/RTCI420Buffer.h" +#import "base/RTCVideoEncoder.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#import "helpers.h" + +#include "api/video_codecs/h264_profile_level_id.h" +#include "common_video/h264/h264_bitstream_parser.h" +#include "common_video/include/bitrate_adjuster.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/buffer.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "sdk/objc/components/video_codec/nalu_rewriter.h" +#include "third_party/libyuv/include/libyuv/convert_from.h" + +@interface RTC_OBJC_TYPE (RTCVideoEncoderH264) +() + + - (void)frameWasEncoded : (OSStatus)status flags : (VTEncodeInfoFlags)infoFlags sampleBuffer + : (CMSampleBufferRef)sampleBuffer codecSpecificInfo + : (id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)codecSpecificInfo width : (int32_t)width height + : (int32_t)height renderTimeMs : (int64_t)renderTimeMs timestamp : (uint32_t)timestamp rotation + : (RTCVideoRotation)rotation; + +@end + +namespace { // anonymous namespace + +// The ratio between kVTCompressionPropertyKey_DataRateLimits and +// kVTCompressionPropertyKey_AverageBitRate. The data rate limit is set higher +// than the average bit rate to avoid undershooting the target. +const float kLimitToAverageBitRateFactor = 1.5f; +// These thresholds deviate from the default h264 QP thresholds, as they +// have been found to work better on devices that support VideoToolbox +const int kLowH264QpThreshold = 28; +const int kHighH264QpThreshold = 39; + +const OSType kNV12PixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; + +// Struct that we pass to the encoder per frame to encode. We receive it again +// in the encoder callback. +struct RTCFrameEncodeParams { + RTCFrameEncodeParams(RTC_OBJC_TYPE(RTCVideoEncoderH264) * e, + RTC_OBJC_TYPE(RTCCodecSpecificInfoH264) * csi, + int32_t w, + int32_t h, + int64_t rtms, + uint32_t ts, + RTCVideoRotation r) + : encoder(e), width(w), height(h), render_time_ms(rtms), timestamp(ts), rotation(r) { + if (csi) { + codecSpecificInfo = csi; + } else { + codecSpecificInfo = [[RTC_OBJC_TYPE(RTCCodecSpecificInfoH264) alloc] init]; + } + } + + RTC_OBJC_TYPE(RTCVideoEncoderH264) * encoder; + RTC_OBJC_TYPE(RTCCodecSpecificInfoH264) * codecSpecificInfo; + int32_t width; + int32_t height; + int64_t render_time_ms; + uint32_t timestamp; + RTCVideoRotation rotation; +}; + +// We receive I420Frames as input, but we need to feed CVPixelBuffers into the +// encoder. This performs the copy and format conversion. +// TODO(tkchin): See if encoder will accept i420 frames and compare performance. +bool CopyVideoFrameToNV12PixelBuffer(id<RTC_OBJC_TYPE(RTCI420Buffer)> frameBuffer, + CVPixelBufferRef pixelBuffer) { + RTC_DCHECK(pixelBuffer); + RTC_DCHECK_EQ(CVPixelBufferGetPixelFormatType(pixelBuffer), kNV12PixelFormat); + RTC_DCHECK_EQ(CVPixelBufferGetHeightOfPlane(pixelBuffer, 0), frameBuffer.height); + RTC_DCHECK_EQ(CVPixelBufferGetWidthOfPlane(pixelBuffer, 0), frameBuffer.width); + + CVReturn cvRet = CVPixelBufferLockBaseAddress(pixelBuffer, 0); + if (cvRet != kCVReturnSuccess) { + RTC_LOG(LS_ERROR) << "Failed to lock base address: " << cvRet; + return false; + } + uint8_t *dstY = reinterpret_cast<uint8_t *>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)); + int dstStrideY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + uint8_t *dstUV = reinterpret_cast<uint8_t *>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)); + int dstStrideUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); + // Convert I420 to NV12. + int ret = libyuv::I420ToNV12(frameBuffer.dataY, + frameBuffer.strideY, + frameBuffer.dataU, + frameBuffer.strideU, + frameBuffer.dataV, + frameBuffer.strideV, + dstY, + dstStrideY, + dstUV, + dstStrideUV, + frameBuffer.width, + frameBuffer.height); + CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); + if (ret) { + RTC_LOG(LS_ERROR) << "Error converting I420 VideoFrame to NV12 :" << ret; + return false; + } + return true; +} + +CVPixelBufferRef CreatePixelBuffer(VTCompressionSessionRef compression_session) { + if (!compression_session) { + RTC_LOG(LS_ERROR) << "Failed to get compression session."; + return nullptr; + } + CVPixelBufferPoolRef pixel_buffer_pool = + VTCompressionSessionGetPixelBufferPool(compression_session); + + if (!pixel_buffer_pool) { + RTC_LOG(LS_ERROR) << "Failed to get pixel buffer pool."; + return nullptr; + } + CVPixelBufferRef pixel_buffer; + CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(nullptr, pixel_buffer_pool, &pixel_buffer); + if (ret != kCVReturnSuccess) { + RTC_LOG(LS_ERROR) << "Failed to create pixel buffer: " << ret; + // We probably want to drop frames here, since failure probably means + // that the pool is empty. + return nullptr; + } + return pixel_buffer; +} + +// This is the callback function that VideoToolbox calls when encode is +// complete. From inspection this happens on its own queue. +void compressionOutputCallback(void *encoder, + void *params, + OSStatus status, + VTEncodeInfoFlags infoFlags, + CMSampleBufferRef sampleBuffer) { + if (!params) { + // If there are pending callbacks when the encoder is destroyed, this can happen. + return; + } + std::unique_ptr<RTCFrameEncodeParams> encodeParams( + reinterpret_cast<RTCFrameEncodeParams *>(params)); + [encodeParams->encoder frameWasEncoded:status + flags:infoFlags + sampleBuffer:sampleBuffer + codecSpecificInfo:encodeParams->codecSpecificInfo + width:encodeParams->width + height:encodeParams->height + renderTimeMs:encodeParams->render_time_ms + timestamp:encodeParams->timestamp + rotation:encodeParams->rotation]; +} + +// Extract VideoToolbox profile out of the webrtc::SdpVideoFormat. If there is +// no specific VideoToolbox profile for the specified level, AutoLevel will be +// returned. The user must initialize the encoder with a resolution and +// framerate conforming to the selected H264 level regardless. +CFStringRef ExtractProfile(const webrtc::H264ProfileLevelId &profile_level_id) { + switch (profile_level_id.profile) { + case webrtc::H264Profile::kProfileConstrainedBaseline: + case webrtc::H264Profile::kProfileBaseline: + switch (profile_level_id.level) { + case webrtc::H264Level::kLevel3: + return kVTProfileLevel_H264_Baseline_3_0; + case webrtc::H264Level::kLevel3_1: + return kVTProfileLevel_H264_Baseline_3_1; + case webrtc::H264Level::kLevel3_2: + return kVTProfileLevel_H264_Baseline_3_2; + case webrtc::H264Level::kLevel4: + return kVTProfileLevel_H264_Baseline_4_0; + case webrtc::H264Level::kLevel4_1: + return kVTProfileLevel_H264_Baseline_4_1; + case webrtc::H264Level::kLevel4_2: + return kVTProfileLevel_H264_Baseline_4_2; + case webrtc::H264Level::kLevel5: + return kVTProfileLevel_H264_Baseline_5_0; + case webrtc::H264Level::kLevel5_1: + return kVTProfileLevel_H264_Baseline_5_1; + case webrtc::H264Level::kLevel5_2: + return kVTProfileLevel_H264_Baseline_5_2; + case webrtc::H264Level::kLevel1: + case webrtc::H264Level::kLevel1_b: + case webrtc::H264Level::kLevel1_1: + case webrtc::H264Level::kLevel1_2: + case webrtc::H264Level::kLevel1_3: + case webrtc::H264Level::kLevel2: + case webrtc::H264Level::kLevel2_1: + case webrtc::H264Level::kLevel2_2: + return kVTProfileLevel_H264_Baseline_AutoLevel; + } + + case webrtc::H264Profile::kProfileMain: + switch (profile_level_id.level) { + case webrtc::H264Level::kLevel3: + return kVTProfileLevel_H264_Main_3_0; + case webrtc::H264Level::kLevel3_1: + return kVTProfileLevel_H264_Main_3_1; + case webrtc::H264Level::kLevel3_2: + return kVTProfileLevel_H264_Main_3_2; + case webrtc::H264Level::kLevel4: + return kVTProfileLevel_H264_Main_4_0; + case webrtc::H264Level::kLevel4_1: + return kVTProfileLevel_H264_Main_4_1; + case webrtc::H264Level::kLevel4_2: + return kVTProfileLevel_H264_Main_4_2; + case webrtc::H264Level::kLevel5: + return kVTProfileLevel_H264_Main_5_0; + case webrtc::H264Level::kLevel5_1: + return kVTProfileLevel_H264_Main_5_1; + case webrtc::H264Level::kLevel5_2: + return kVTProfileLevel_H264_Main_5_2; + case webrtc::H264Level::kLevel1: + case webrtc::H264Level::kLevel1_b: + case webrtc::H264Level::kLevel1_1: + case webrtc::H264Level::kLevel1_2: + case webrtc::H264Level::kLevel1_3: + case webrtc::H264Level::kLevel2: + case webrtc::H264Level::kLevel2_1: + case webrtc::H264Level::kLevel2_2: + return kVTProfileLevel_H264_Main_AutoLevel; + } + + case webrtc::H264Profile::kProfileConstrainedHigh: + case webrtc::H264Profile::kProfileHigh: + case webrtc::H264Profile::kProfilePredictiveHigh444: + switch (profile_level_id.level) { + case webrtc::H264Level::kLevel3: + return kVTProfileLevel_H264_High_3_0; + case webrtc::H264Level::kLevel3_1: + return kVTProfileLevel_H264_High_3_1; + case webrtc::H264Level::kLevel3_2: + return kVTProfileLevel_H264_High_3_2; + case webrtc::H264Level::kLevel4: + return kVTProfileLevel_H264_High_4_0; + case webrtc::H264Level::kLevel4_1: + return kVTProfileLevel_H264_High_4_1; + case webrtc::H264Level::kLevel4_2: + return kVTProfileLevel_H264_High_4_2; + case webrtc::H264Level::kLevel5: + return kVTProfileLevel_H264_High_5_0; + case webrtc::H264Level::kLevel5_1: + return kVTProfileLevel_H264_High_5_1; + case webrtc::H264Level::kLevel5_2: + return kVTProfileLevel_H264_High_5_2; + case webrtc::H264Level::kLevel1: + case webrtc::H264Level::kLevel1_b: + case webrtc::H264Level::kLevel1_1: + case webrtc::H264Level::kLevel1_2: + case webrtc::H264Level::kLevel1_3: + case webrtc::H264Level::kLevel2: + case webrtc::H264Level::kLevel2_1: + case webrtc::H264Level::kLevel2_2: + return kVTProfileLevel_H264_High_AutoLevel; + } + } +} + +// The function returns the max allowed sample rate (pixels per second) that +// can be processed by given encoder with `profile_level_id`. +// See https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-201610-S!!PDF-E&type=items +// for details. +NSUInteger GetMaxSampleRate(const webrtc::H264ProfileLevelId &profile_level_id) { + switch (profile_level_id.level) { + case webrtc::H264Level::kLevel3: + return 10368000; + case webrtc::H264Level::kLevel3_1: + return 27648000; + case webrtc::H264Level::kLevel3_2: + return 55296000; + case webrtc::H264Level::kLevel4: + case webrtc::H264Level::kLevel4_1: + return 62914560; + case webrtc::H264Level::kLevel4_2: + return 133693440; + case webrtc::H264Level::kLevel5: + return 150994944; + case webrtc::H264Level::kLevel5_1: + return 251658240; + case webrtc::H264Level::kLevel5_2: + return 530841600; + case webrtc::H264Level::kLevel1: + case webrtc::H264Level::kLevel1_b: + case webrtc::H264Level::kLevel1_1: + case webrtc::H264Level::kLevel1_2: + case webrtc::H264Level::kLevel1_3: + case webrtc::H264Level::kLevel2: + case webrtc::H264Level::kLevel2_1: + case webrtc::H264Level::kLevel2_2: + // Zero means auto rate setting. + return 0; + } +} +} // namespace + +@implementation RTC_OBJC_TYPE (RTCVideoEncoderH264) { + RTC_OBJC_TYPE(RTCVideoCodecInfo) * _codecInfo; + std::unique_ptr<webrtc::BitrateAdjuster> _bitrateAdjuster; + uint32_t _targetBitrateBps; + uint32_t _encoderBitrateBps; + uint32_t _encoderFrameRate; + uint32_t _maxAllowedFrameRate; + RTCH264PacketizationMode _packetizationMode; + absl::optional<webrtc::H264ProfileLevelId> _profile_level_id; + RTCVideoEncoderCallback _callback; + int32_t _width; + int32_t _height; + VTCompressionSessionRef _compressionSession; + RTCVideoCodecMode _mode; + + webrtc::H264BitstreamParser _h264BitstreamParser; + std::vector<uint8_t> _frameScaleBuffer; +} + +// .5 is set as a mininum to prevent overcompensating for large temporary +// overshoots. We don't want to degrade video quality too badly. +// .95 is set to prevent oscillations. When a lower bitrate is set on the +// encoder than previously set, its output seems to have a brief period of +// drastically reduced bitrate, so we want to avoid that. In steady state +// conditions, 0.95 seems to give us better overall bitrate over long periods +// of time. +- (instancetype)initWithCodecInfo:(RTC_OBJC_TYPE(RTCVideoCodecInfo) *)codecInfo { + if (self = [super init]) { + _codecInfo = codecInfo; + _bitrateAdjuster.reset(new webrtc::BitrateAdjuster(.5, .95)); + _packetizationMode = RTCH264PacketizationModeNonInterleaved; + _profile_level_id = + webrtc::ParseSdpForH264ProfileLevelId([codecInfo nativeSdpVideoFormat].parameters); + RTC_DCHECK(_profile_level_id); + RTC_LOG(LS_INFO) << "Using profile " << CFStringToString(ExtractProfile(*_profile_level_id)); + RTC_CHECK([codecInfo.name isEqualToString:kRTCVideoCodecH264Name]); + } + return self; +} + +- (void)dealloc { + [self destroyCompressionSession]; +} + +- (NSInteger)startEncodeWithSettings:(RTC_OBJC_TYPE(RTCVideoEncoderSettings) *)settings + numberOfCores:(int)numberOfCores { + RTC_DCHECK(settings); + RTC_DCHECK([settings.name isEqualToString:kRTCVideoCodecH264Name]); + + _width = settings.width; + _height = settings.height; + _mode = settings.mode; + + uint32_t aligned_width = (((_width + 15) >> 4) << 4); + uint32_t aligned_height = (((_height + 15) >> 4) << 4); + _maxAllowedFrameRate = static_cast<uint32_t>(GetMaxSampleRate(*_profile_level_id) / + (aligned_width * aligned_height)); + + // We can only set average bitrate on the HW encoder. + _targetBitrateBps = settings.startBitrate * 1000; // startBitrate is in kbps. + _bitrateAdjuster->SetTargetBitrateBps(_targetBitrateBps); + _encoderFrameRate = MIN(settings.maxFramerate, _maxAllowedFrameRate); + if (settings.maxFramerate > _maxAllowedFrameRate && _maxAllowedFrameRate > 0) { + RTC_LOG(LS_WARNING) << "Initial encoder frame rate setting " << settings.maxFramerate + << " is larger than the " + << "maximal allowed frame rate " << _maxAllowedFrameRate << "."; + } + + // TODO(tkchin): Try setting payload size via + // kVTCompressionPropertyKey_MaxH264SliceBytes. + + return [self resetCompressionSessionWithPixelFormat:kNV12PixelFormat]; +} + +- (NSInteger)encode:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame + codecSpecificInfo:(nullable id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)codecSpecificInfo + frameTypes:(NSArray<NSNumber *> *)frameTypes { + if (!_callback || !_compressionSession) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + BOOL isKeyframeRequired = NO; + + // Get a pixel buffer from the pool and copy frame data over. + if ([self resetCompressionSessionIfNeededWithFrame:frame]) { + isKeyframeRequired = YES; + } + + CVPixelBufferRef pixelBuffer = nullptr; + if ([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { + // Native frame buffer + RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer = + (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer; + if (![rtcPixelBuffer requiresCropping]) { + // This pixel buffer might have a higher resolution than what the + // compression session is configured to. The compression session can + // handle that and will output encoded frames in the configured + // resolution regardless of the input pixel buffer resolution. + pixelBuffer = rtcPixelBuffer.pixelBuffer; + CVBufferRetain(pixelBuffer); + } else { + // Cropping required, we need to crop and scale to a new pixel buffer. + pixelBuffer = CreatePixelBuffer(_compressionSession); + if (!pixelBuffer) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + int dstWidth = CVPixelBufferGetWidth(pixelBuffer); + int dstHeight = CVPixelBufferGetHeight(pixelBuffer); + if ([rtcPixelBuffer requiresScalingToWidth:dstWidth height:dstHeight]) { + int size = + [rtcPixelBuffer bufferSizeForCroppingAndScalingToWidth:dstWidth height:dstHeight]; + _frameScaleBuffer.resize(size); + } else { + _frameScaleBuffer.clear(); + } + _frameScaleBuffer.shrink_to_fit(); + if (![rtcPixelBuffer cropAndScaleTo:pixelBuffer withTempBuffer:_frameScaleBuffer.data()]) { + CVBufferRelease(pixelBuffer); + return WEBRTC_VIDEO_CODEC_ERROR; + } + } + } + + if (!pixelBuffer) { + // We did not have a native frame buffer + RTC_DCHECK_EQ(frame.width, _width); + RTC_DCHECK_EQ(frame.height, _height); + + pixelBuffer = CreatePixelBuffer(_compressionSession); + if (!pixelBuffer) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + RTC_DCHECK(pixelBuffer); + if (!CopyVideoFrameToNV12PixelBuffer([frame.buffer toI420], pixelBuffer)) { + RTC_LOG(LS_ERROR) << "Failed to copy frame data."; + CVBufferRelease(pixelBuffer); + return WEBRTC_VIDEO_CODEC_ERROR; + } + } + + // Check if we need a keyframe. + if (!isKeyframeRequired && frameTypes) { + for (NSNumber *frameType in frameTypes) { + if ((RTCFrameType)frameType.intValue == RTCFrameTypeVideoFrameKey) { + isKeyframeRequired = YES; + break; + } + } + } + + CMTime presentationTimeStamp = CMTimeMake(frame.timeStampNs / rtc::kNumNanosecsPerMillisec, 1000); + CFDictionaryRef frameProperties = nullptr; + if (isKeyframeRequired) { + CFTypeRef keys[] = {kVTEncodeFrameOptionKey_ForceKeyFrame}; + CFTypeRef values[] = {kCFBooleanTrue}; + frameProperties = CreateCFTypeDictionary(keys, values, 1); + } + + std::unique_ptr<RTCFrameEncodeParams> encodeParams; + encodeParams.reset(new RTCFrameEncodeParams(self, + codecSpecificInfo, + _width, + _height, + frame.timeStampNs / rtc::kNumNanosecsPerMillisec, + frame.timeStamp, + frame.rotation)); + encodeParams->codecSpecificInfo.packetizationMode = _packetizationMode; + + // Update the bitrate if needed. + [self setBitrateBps:_bitrateAdjuster->GetAdjustedBitrateBps() frameRate:_encoderFrameRate]; + + OSStatus status = VTCompressionSessionEncodeFrame(_compressionSession, + pixelBuffer, + presentationTimeStamp, + kCMTimeInvalid, + frameProperties, + encodeParams.release(), + nullptr); + if (frameProperties) { + CFRelease(frameProperties); + } + if (pixelBuffer) { + CVBufferRelease(pixelBuffer); + } + + if (status == kVTInvalidSessionErr) { + // This error occurs when entering foreground after backgrounding the app. + RTC_LOG(LS_ERROR) << "Invalid compression session, resetting."; + [self resetCompressionSessionWithPixelFormat:[self pixelFormatOfFrame:frame]]; + + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } else if (status == kVTVideoEncoderMalfunctionErr) { + // Sometimes the encoder malfunctions and needs to be restarted. + RTC_LOG(LS_ERROR) + << "Encountered video encoder malfunction error. Resetting compression session."; + [self resetCompressionSessionWithPixelFormat:[self pixelFormatOfFrame:frame]]; + + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } else if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to encode frame with code: " << status; + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +- (void)setCallback:(RTCVideoEncoderCallback)callback { + _callback = callback; +} + +- (int)setBitrate:(uint32_t)bitrateKbit framerate:(uint32_t)framerate { + _targetBitrateBps = 1000 * bitrateKbit; + _bitrateAdjuster->SetTargetBitrateBps(_targetBitrateBps); + if (framerate > _maxAllowedFrameRate && _maxAllowedFrameRate > 0) { + RTC_LOG(LS_WARNING) << "Encoder frame rate setting " << framerate << " is larger than the " + << "maximal allowed frame rate " << _maxAllowedFrameRate << "."; + } + framerate = MIN(framerate, _maxAllowedFrameRate); + [self setBitrateBps:_bitrateAdjuster->GetAdjustedBitrateBps() frameRate:framerate]; + return WEBRTC_VIDEO_CODEC_OK; +} + +- (NSInteger)resolutionAlignment { + return 1; +} + +- (BOOL)applyAlignmentToAllSimulcastLayers { + return NO; +} + +- (BOOL)supportsNativeHandle { + return YES; +} + +#pragma mark - Private + +- (NSInteger)releaseEncoder { + // Need to destroy so that the session is invalidated and won't use the + // callback anymore. Do not remove callback until the session is invalidated + // since async encoder callbacks can occur until invalidation. + [self destroyCompressionSession]; + _callback = nullptr; + return WEBRTC_VIDEO_CODEC_OK; +} + +- (OSType)pixelFormatOfFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + // Use NV12 for non-native frames. + if ([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { + RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer = + (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer; + return CVPixelBufferGetPixelFormatType(rtcPixelBuffer.pixelBuffer); + } + + return kNV12PixelFormat; +} + +- (BOOL)resetCompressionSessionIfNeededWithFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + BOOL resetCompressionSession = NO; + + // If we're capturing native frames in another pixel format than the compression session is + // configured with, make sure the compression session is reset using the correct pixel format. + OSType framePixelFormat = [self pixelFormatOfFrame:frame]; + + if (_compressionSession) { + // The pool attribute `kCVPixelBufferPixelFormatTypeKey` can contain either an array of pixel + // formats or a single pixel format. + + CVPixelBufferPoolRef pixelBufferPool = + VTCompressionSessionGetPixelBufferPool(_compressionSession); + if (!pixelBufferPool) { + return NO; + } + + NSDictionary *poolAttributes = + (__bridge NSDictionary *)CVPixelBufferPoolGetPixelBufferAttributes(pixelBufferPool); + id pixelFormats = + [poolAttributes objectForKey:(__bridge NSString *)kCVPixelBufferPixelFormatTypeKey]; + NSArray<NSNumber *> *compressionSessionPixelFormats = nil; + if ([pixelFormats isKindOfClass:[NSArray class]]) { + compressionSessionPixelFormats = (NSArray *)pixelFormats; + } else if ([pixelFormats isKindOfClass:[NSNumber class]]) { + compressionSessionPixelFormats = @[ (NSNumber *)pixelFormats ]; + } + + if (![compressionSessionPixelFormats + containsObject:[NSNumber numberWithLong:framePixelFormat]]) { + resetCompressionSession = YES; + RTC_LOG(LS_INFO) << "Resetting compression session due to non-matching pixel format."; + } + } else { + resetCompressionSession = YES; + } + + if (resetCompressionSession) { + [self resetCompressionSessionWithPixelFormat:framePixelFormat]; + } + return resetCompressionSession; +} + +- (int)resetCompressionSessionWithPixelFormat:(OSType)framePixelFormat { + [self destroyCompressionSession]; + + // Set source image buffer attributes. These attributes will be present on + // buffers retrieved from the encoder's pixel buffer pool. + NSDictionary *sourceAttributes = @{ +#if defined(WEBRTC_IOS) && (TARGET_OS_MACCATALYST || TARGET_OS_SIMULATOR) + (NSString *)kCVPixelBufferMetalCompatibilityKey : @(YES), +#elif defined(WEBRTC_IOS) + (NSString *)kCVPixelBufferOpenGLESCompatibilityKey : @(YES), +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_ARCH_ARM64) + (NSString *)kCVPixelBufferOpenGLCompatibilityKey : @(YES), +#endif + (NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{}, + (NSString *)kCVPixelBufferPixelFormatTypeKey : @(framePixelFormat), + }; + + NSDictionary *encoder_specs; +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + // Currently hw accl is supported above 360p on mac, below 360p + // the compression session will be created with hw accl disabled. + encoder_specs = @{ + (NSString *)kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder : @(YES), + }; + +#endif + OSStatus status = VTCompressionSessionCreate( + nullptr, // use default allocator + _width, + _height, + kCMVideoCodecType_H264, + (__bridge CFDictionaryRef)encoder_specs, // use hardware accelerated encoder if available + (__bridge CFDictionaryRef)sourceAttributes, + nullptr, // use default compressed data allocator + compressionOutputCallback, + nullptr, + &_compressionSession); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to create compression session: " << status; + return WEBRTC_VIDEO_CODEC_ERROR; + } +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + CFBooleanRef hwaccl_enabled = nullptr; + status = VTSessionCopyProperty(_compressionSession, + kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder, + nullptr, + &hwaccl_enabled); + if (status == noErr && (CFBooleanGetValue(hwaccl_enabled))) { + RTC_LOG(LS_INFO) << "Compression session created with hw accl enabled"; + } else { + RTC_LOG(LS_INFO) << "Compression session created with hw accl disabled"; + } +#endif + [self configureCompressionSession]; + + return WEBRTC_VIDEO_CODEC_OK; +} + +- (void)configureCompressionSession { + RTC_DCHECK(_compressionSession); + SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_RealTime, true); + SetVTSessionProperty(_compressionSession, + kVTCompressionPropertyKey_ProfileLevel, + ExtractProfile(*_profile_level_id)); + SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AllowFrameReordering, false); + [self setEncoderBitrateBps:_targetBitrateBps frameRate:_encoderFrameRate]; + // TODO(tkchin): Look at entropy mode and colorspace matrices. + // TODO(tkchin): Investigate to see if there's any way to make this work. + // May need it to interop with Android. Currently this call just fails. + // On inspecting encoder output on iOS8, this value is set to 6. + // internal::SetVTSessionProperty(compression_session_, + // kVTCompressionPropertyKey_MaxFrameDelayCount, + // 1); + + // Set a relatively large value for keyframe emission (7200 frames or 4 minutes). + SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, 7200); + SetVTSessionProperty( + _compressionSession, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, 240); +} + +- (void)destroyCompressionSession { + if (_compressionSession) { + VTCompressionSessionInvalidate(_compressionSession); + CFRelease(_compressionSession); + _compressionSession = nullptr; + } +} + +- (NSString *)implementationName { + return @"VideoToolbox"; +} + +- (void)setBitrateBps:(uint32_t)bitrateBps frameRate:(uint32_t)frameRate { + if (_encoderBitrateBps != bitrateBps || _encoderFrameRate != frameRate) { + [self setEncoderBitrateBps:bitrateBps frameRate:frameRate]; + } +} + +- (void)setEncoderBitrateBps:(uint32_t)bitrateBps frameRate:(uint32_t)frameRate { + if (_compressionSession) { + SetVTSessionProperty(_compressionSession, kVTCompressionPropertyKey_AverageBitRate, bitrateBps); + + // With zero `_maxAllowedFrameRate`, we fall back to automatic frame rate detection. + if (_maxAllowedFrameRate > 0) { + SetVTSessionProperty( + _compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, frameRate); + } + + // TODO(tkchin): Add a helper method to set array value. + int64_t dataLimitBytesPerSecondValue = + static_cast<int64_t>(bitrateBps * kLimitToAverageBitRateFactor / 8); + CFNumberRef bytesPerSecond = + CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &dataLimitBytesPerSecondValue); + int64_t oneSecondValue = 1; + CFNumberRef oneSecond = + CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &oneSecondValue); + const void *nums[2] = {bytesPerSecond, oneSecond}; + CFArrayRef dataRateLimits = CFArrayCreate(nullptr, nums, 2, &kCFTypeArrayCallBacks); + OSStatus status = VTSessionSetProperty( + _compressionSession, kVTCompressionPropertyKey_DataRateLimits, dataRateLimits); + if (bytesPerSecond) { + CFRelease(bytesPerSecond); + } + if (oneSecond) { + CFRelease(oneSecond); + } + if (dataRateLimits) { + CFRelease(dataRateLimits); + } + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to set data rate limit with code: " << status; + } + + _encoderBitrateBps = bitrateBps; + _encoderFrameRate = frameRate; + } +} + +- (void)frameWasEncoded:(OSStatus)status + flags:(VTEncodeInfoFlags)infoFlags + sampleBuffer:(CMSampleBufferRef)sampleBuffer + codecSpecificInfo:(id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)>)codecSpecificInfo + width:(int32_t)width + height:(int32_t)height + renderTimeMs:(int64_t)renderTimeMs + timestamp:(uint32_t)timestamp + rotation:(RTCVideoRotation)rotation { + RTCVideoEncoderCallback callback = _callback; + if (!callback) { + return; + } + if (status != noErr) { + RTC_LOG(LS_ERROR) << "H264 encode failed with code: " << status; + return; + } + if (infoFlags & kVTEncodeInfo_FrameDropped) { + RTC_LOG(LS_INFO) << "H264 encode dropped frame."; + return; + } + + BOOL isKeyframe = NO; + CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, 0); + if (attachments != nullptr && CFArrayGetCount(attachments)) { + CFDictionaryRef attachment = + static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachments, 0)); + isKeyframe = !CFDictionaryContainsKey(attachment, kCMSampleAttachmentKey_NotSync); + } + + if (isKeyframe) { + RTC_LOG(LS_INFO) << "Generated keyframe"; + } + + __block std::unique_ptr<rtc::Buffer> buffer = std::make_unique<rtc::Buffer>(); + if (!webrtc::H264CMSampleBufferToAnnexBBuffer(sampleBuffer, isKeyframe, buffer.get())) { + return; + } + + RTC_OBJC_TYPE(RTCEncodedImage) *frame = [[RTC_OBJC_TYPE(RTCEncodedImage) alloc] init]; + // This assumes ownership of `buffer` and is responsible for freeing it when done. + frame.buffer = [[NSData alloc] initWithBytesNoCopy:buffer->data() + length:buffer->size() + deallocator:^(void *bytes, NSUInteger size) { + buffer.reset(); + }]; + frame.encodedWidth = width; + frame.encodedHeight = height; + frame.frameType = isKeyframe ? RTCFrameTypeVideoFrameKey : RTCFrameTypeVideoFrameDelta; + frame.captureTimeMs = renderTimeMs; + frame.timeStamp = timestamp; + frame.rotation = rotation; + frame.contentType = (_mode == RTCVideoCodecModeScreensharing) ? RTCVideoContentTypeScreenshare : + RTCVideoContentTypeUnspecified; + frame.flags = webrtc::VideoSendTiming::kInvalid; + + _h264BitstreamParser.ParseBitstream(*buffer); + frame.qp = @(_h264BitstreamParser.GetLastSliceQp().value_or(-1)); + + BOOL res = callback(frame, codecSpecificInfo); + if (!res) { + RTC_LOG(LS_ERROR) << "Encode callback failed"; + return; + } + _bitrateAdjuster->Update(frame.buffer.length); +} + +- (nullable RTC_OBJC_TYPE(RTCVideoEncoderQpThresholds) *)scalingSettings { + return [[RTC_OBJC_TYPE(RTCVideoEncoderQpThresholds) alloc] + initWithThresholdsLow:kLowH264QpThreshold + high:kHighH264QpThreshold]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/UIDevice+H264Profile.h b/third_party/libwebrtc/sdk/objc/components/video_codec/UIDevice+H264Profile.h new file mode 100644 index 0000000000..a51debb9fa --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/UIDevice+H264Profile.h @@ -0,0 +1,19 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <UIKit/UIKit.h> + +#include "api/video_codecs/h264_profile_level_id.h" + +@interface UIDevice (H264Profile) + ++ (absl::optional<webrtc::H264ProfileLevelId>)maxSupportedH264Profile; + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/UIDevice+H264Profile.mm b/third_party/libwebrtc/sdk/objc/components/video_codec/UIDevice+H264Profile.mm new file mode 100644 index 0000000000..0ef6a8d77c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/UIDevice+H264Profile.mm @@ -0,0 +1,205 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "UIDevice+H264Profile.h" +#import "helpers/UIDevice+RTCDevice.h" + +#include <algorithm> + +namespace { + +using namespace webrtc; + +struct SupportedH264Profile { + const RTCDeviceType deviceType; + const H264ProfileLevelId profile; +}; + +constexpr SupportedH264Profile kH264MaxSupportedProfiles[] = { + // iPhones with at least iOS 9 + {RTCDeviceTypeIPhone13ProMax, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP848 + {RTCDeviceTypeIPhone13Pro, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP852 + {RTCDeviceTypeIPhone13, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP851 + {RTCDeviceTypeIPhone13Mini, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP847 + {RTCDeviceTypeIPhoneSE2Gen, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP820 + {RTCDeviceTypeIPhone12ProMax, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP832 + {RTCDeviceTypeIPhone12Pro, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP831 + {RTCDeviceTypeIPhone12, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP830 + {RTCDeviceTypeIPhone12Mini, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP829 + {RTCDeviceTypeIPhone11ProMax, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP806 + {RTCDeviceTypeIPhone11Pro, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP805 + {RTCDeviceTypeIPhone11, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP804 + {RTCDeviceTypeIPhoneXS, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP779 + {RTCDeviceTypeIPhoneXSMax, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP780 + {RTCDeviceTypeIPhoneXR, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP781 + {RTCDeviceTypeIPhoneX, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP770 + {RTCDeviceTypeIPhone8, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP767 + {RTCDeviceTypeIPhone8Plus, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP768 + {RTCDeviceTypeIPhone7, + {H264Profile::kProfileHigh, H264Level::kLevel5_1}}, // https://support.apple.com/kb/SP743 + {RTCDeviceTypeIPhone7Plus, + {H264Profile::kProfileHigh, H264Level::kLevel5_1}}, // https://support.apple.com/kb/SP744 + {RTCDeviceTypeIPhoneSE, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP738 + {RTCDeviceTypeIPhone6S, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP726 + {RTCDeviceTypeIPhone6SPlus, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP727 + {RTCDeviceTypeIPhone6, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP705 + {RTCDeviceTypeIPhone6Plus, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP706 + {RTCDeviceTypeIPhone5SGSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP685 + {RTCDeviceTypeIPhone5SGSM_CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP685 + {RTCDeviceTypeIPhone5GSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP655 + {RTCDeviceTypeIPhone5GSM_CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP655 + {RTCDeviceTypeIPhone5CGSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP684 + {RTCDeviceTypeIPhone5CGSM_CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP684 + {RTCDeviceTypeIPhone4S, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP643 + + // iPods with at least iOS 9 + {RTCDeviceTypeIPodTouch7G, + {H264Profile::kProfileMain, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP796 + {RTCDeviceTypeIPodTouch6G, + {H264Profile::kProfileMain, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP720 + {RTCDeviceTypeIPodTouch5G, + {H264Profile::kProfileMain, H264Level::kLevel3_1}}, // https://support.apple.com/kb/SP657 + + // iPads with at least iOS 9 + {RTCDeviceTypeIPadAir4Gen, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP828 + {RTCDeviceTypeIPad8, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP822 + {RTCDeviceTypeIPadPro4Gen12Inch, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP815 + {RTCDeviceTypeIPadPro4Gen11Inch, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP814 + {RTCDeviceTypeIPadAir3Gen, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP787 + {RTCDeviceTypeIPadMini5Gen, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP788 + {RTCDeviceTypeIPadPro3Gen12Inch, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP785 + {RTCDeviceTypeIPadPro3Gen11Inch, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP784 + {RTCDeviceTypeIPad7Gen10Inch, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP807 + {RTCDeviceTypeIPad2Wifi, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP622 + {RTCDeviceTypeIPad2GSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP622 + {RTCDeviceTypeIPad2CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP622 + {RTCDeviceTypeIPad2Wifi2, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP622 + {RTCDeviceTypeIPadMiniWifi, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP661 + {RTCDeviceTypeIPadMiniGSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP661 + {RTCDeviceTypeIPadMiniGSM_CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP661 + {RTCDeviceTypeIPad3Wifi, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP647 + {RTCDeviceTypeIPad3GSM_CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP647 + {RTCDeviceTypeIPad3GSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP647 + {RTCDeviceTypeIPad4Wifi, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP662 + {RTCDeviceTypeIPad4GSM, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP662 + {RTCDeviceTypeIPad4GSM_CDMA, + {H264Profile::kProfileHigh, H264Level::kLevel4_1}}, // https://support.apple.com/kb/SP662 + {RTCDeviceTypeIPad5, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP751 + {RTCDeviceTypeIPad6, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP774 + {RTCDeviceTypeIPadAirWifi, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP692 + {RTCDeviceTypeIPadAirCellular, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP692 + {RTCDeviceTypeIPadAirWifiCellular, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP692 + {RTCDeviceTypeIPadAir2, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP708 + {RTCDeviceTypeIPadMini2GWifi, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP693 + {RTCDeviceTypeIPadMini2GCellular, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP693 + {RTCDeviceTypeIPadMini2GWifiCellular, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP693 + {RTCDeviceTypeIPadMini3, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP709 + {RTCDeviceTypeIPadMini4, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP725 + {RTCDeviceTypeIPadPro9Inch, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP739 + {RTCDeviceTypeIPadPro12Inch, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/sp723 + {RTCDeviceTypeIPadPro12Inch2, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP761 + {RTCDeviceTypeIPadPro10Inch, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP762 + {RTCDeviceTypeIPadMini6, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP850 + {RTCDeviceTypeIPad9, + {H264Profile::kProfileHigh, H264Level::kLevel4_2}}, // https://support.apple.com/kb/SP849 + {RTCDeviceTypeIPadPro5Gen12Inch, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP844 + {RTCDeviceTypeIPadPro5Gen11Inch, + {H264Profile::kProfileHigh, H264Level::kLevel5_2}}, // https://support.apple.com/kb/SP843 +}; + +absl::optional<H264ProfileLevelId> FindMaxSupportedProfileForDevice(RTCDeviceType deviceType) { + const auto* result = std::find_if(std::begin(kH264MaxSupportedProfiles), + std::end(kH264MaxSupportedProfiles), + [deviceType](const SupportedH264Profile& supportedProfile) { + return supportedProfile.deviceType == deviceType; + }); + if (result != std::end(kH264MaxSupportedProfiles)) { + return result->profile; + } + return absl::nullopt; +} + +} // namespace + +@implementation UIDevice (H264Profile) + ++ (absl::optional<webrtc::H264ProfileLevelId>)maxSupportedH264Profile { + return FindMaxSupportedProfileForDevice([self deviceType]); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/helpers.cc b/third_party/libwebrtc/sdk/objc/components/video_codec/helpers.cc new file mode 100644 index 0000000000..ac957f1b49 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/helpers.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "helpers.h" + +#include <string> + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +// Copies characters from a CFStringRef into a std::string. +std::string CFStringToString(const CFStringRef cf_string) { + RTC_DCHECK(cf_string); + std::string std_string; + // Get the size needed for UTF8 plus terminating character. + size_t buffer_size = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(cf_string), + kCFStringEncodingUTF8) + + 1; + std::unique_ptr<char[]> buffer(new char[buffer_size]); + if (CFStringGetCString(cf_string, buffer.get(), buffer_size, + kCFStringEncodingUTF8)) { + // Copy over the characters. + std_string.assign(buffer.get()); + } + return std_string; +} + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, + CFStringRef key, + int32_t value) { + CFNumberRef cfNum = + CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + OSStatus status = VTSessionSetProperty(session, key, cfNum); + CFRelease(cfNum); + if (status != noErr) { + std::string key_string = CFStringToString(key); + RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string + << " to " << value << ": " << status; + } +} + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, + CFStringRef key, + uint32_t value) { + int64_t value_64 = value; + CFNumberRef cfNum = + CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value_64); + OSStatus status = VTSessionSetProperty(session, key, cfNum); + CFRelease(cfNum); + if (status != noErr) { + std::string key_string = CFStringToString(key); + RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string + << " to " << value << ": " << status; + } +} + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, CFStringRef key, bool value) { + CFBooleanRef cf_bool = (value) ? kCFBooleanTrue : kCFBooleanFalse; + OSStatus status = VTSessionSetProperty(session, key, cf_bool); + if (status != noErr) { + std::string key_string = CFStringToString(key); + RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string + << " to " << value << ": " << status; + } +} + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, + CFStringRef key, + CFStringRef value) { + OSStatus status = VTSessionSetProperty(session, key, value); + if (status != noErr) { + std::string key_string = CFStringToString(key); + std::string val_string = CFStringToString(value); + RTC_LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string + << " to " << val_string << ": " << status; + } +} diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/helpers.h b/third_party/libwebrtc/sdk/objc/components/video_codec/helpers.h new file mode 100644 index 0000000000..7c9ef1cd87 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/helpers.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef SDK_OBJC_FRAMEWORK_CLASSES_VIDEOTOOLBOX_HELPERS_H_ +#define SDK_OBJC_FRAMEWORK_CLASSES_VIDEOTOOLBOX_HELPERS_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <VideoToolbox/VideoToolbox.h> +#include <string> + +// Convenience function for creating a dictionary. +inline CFDictionaryRef CreateCFTypeDictionary(CFTypeRef* keys, + CFTypeRef* values, + size_t size) { + return CFDictionaryCreate(kCFAllocatorDefault, keys, values, size, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); +} + +// Copies characters from a CFStringRef into a std::string. +std::string CFStringToString(CFStringRef cf_string); + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, CFStringRef key, int32_t value); + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, + CFStringRef key, + uint32_t value); + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, CFStringRef key, bool value); + +// Convenience function for setting a VT property. +void SetVTSessionProperty(VTSessionRef session, + CFStringRef key, + CFStringRef value); + +#endif // SDK_OBJC_FRAMEWORK_CLASSES_VIDEOTOOLBOX_HELPERS_H_ diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/nalu_rewriter.cc b/third_party/libwebrtc/sdk/objc/components/video_codec/nalu_rewriter.cc new file mode 100644 index 0000000000..b7330e1f9c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/nalu_rewriter.cc @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "sdk/objc/components/video_codec/nalu_rewriter.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <memory> +#include <vector> + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +using H264::kAud; +using H264::kSps; +using H264::NaluIndex; +using H264::NaluType; +using H264::ParseNaluType; + +const char kAnnexBHeaderBytes[4] = {0, 0, 0, 1}; +const size_t kAvccHeaderByteSize = sizeof(uint32_t); + +bool H264CMSampleBufferToAnnexBBuffer(CMSampleBufferRef avcc_sample_buffer, + bool is_keyframe, + rtc::Buffer* annexb_buffer) { + RTC_DCHECK(avcc_sample_buffer); + + // Get format description from the sample buffer. + CMVideoFormatDescriptionRef description = + CMSampleBufferGetFormatDescription(avcc_sample_buffer); + if (description == nullptr) { + RTC_LOG(LS_ERROR) << "Failed to get sample buffer's description."; + return false; + } + + // Get parameter set information. + int nalu_header_size = 0; + size_t param_set_count = 0; + OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex( + description, 0, nullptr, nullptr, ¶m_set_count, &nalu_header_size); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to get parameter set."; + return false; + } + RTC_CHECK_EQ(nalu_header_size, kAvccHeaderByteSize); + RTC_DCHECK_EQ(param_set_count, 2); + + // Truncate any previous data in the buffer without changing its capacity. + annexb_buffer->SetSize(0); + + // Place all parameter sets at the front of buffer. + if (is_keyframe) { + size_t param_set_size = 0; + const uint8_t* param_set = nullptr; + for (size_t i = 0; i < param_set_count; ++i) { + status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex( + description, i, ¶m_set, ¶m_set_size, nullptr, nullptr); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to get parameter set."; + return false; + } + // Update buffer. + annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes)); + annexb_buffer->AppendData(reinterpret_cast<const char*>(param_set), + param_set_size); + } + } + + // Get block buffer from the sample buffer. + CMBlockBufferRef block_buffer = + CMSampleBufferGetDataBuffer(avcc_sample_buffer); + if (block_buffer == nullptr) { + RTC_LOG(LS_ERROR) << "Failed to get sample buffer's block buffer."; + return false; + } + CMBlockBufferRef contiguous_buffer = nullptr; + // Make sure block buffer is contiguous. + if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) { + status = CMBlockBufferCreateContiguous( + nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: " + << status; + return false; + } + } else { + contiguous_buffer = block_buffer; + // Retain to make cleanup easier. + CFRetain(contiguous_buffer); + block_buffer = nullptr; + } + + // Now copy the actual data. + char* data_ptr = nullptr; + size_t block_buffer_size = CMBlockBufferGetDataLength(contiguous_buffer); + status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, nullptr, + &data_ptr); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to get block buffer data."; + CFRelease(contiguous_buffer); + return false; + } + size_t bytes_remaining = block_buffer_size; + while (bytes_remaining > 0) { + // The size type here must match `nalu_header_size`, we expect 4 bytes. + // Read the length of the next packet of data. Must convert from big endian + // to host endian. + RTC_DCHECK_GE(bytes_remaining, (size_t)nalu_header_size); + uint32_t* uint32_data_ptr = reinterpret_cast<uint32_t*>(data_ptr); + uint32_t packet_size = CFSwapInt32BigToHost(*uint32_data_ptr); + // Update buffer. + annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes)); + annexb_buffer->AppendData(data_ptr + nalu_header_size, packet_size); + + size_t bytes_written = packet_size + sizeof(kAnnexBHeaderBytes); + bytes_remaining -= bytes_written; + data_ptr += bytes_written; + } + RTC_DCHECK_EQ(bytes_remaining, (size_t)0); + + CFRelease(contiguous_buffer); + return true; +} + +bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer, + size_t annexb_buffer_size, + CMVideoFormatDescriptionRef video_format, + CMSampleBufferRef* out_sample_buffer, + CMMemoryPoolRef memory_pool) { + RTC_DCHECK(annexb_buffer); + RTC_DCHECK(out_sample_buffer); + RTC_DCHECK(video_format); + *out_sample_buffer = nullptr; + + AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size); + if (reader.SeekToNextNaluOfType(kSps)) { + // Buffer contains an SPS NALU - skip it and the following PPS + const uint8_t* data; + size_t data_len; + if (!reader.ReadNalu(&data, &data_len)) { + RTC_LOG(LS_ERROR) << "Failed to read SPS"; + return false; + } + if (!reader.ReadNalu(&data, &data_len)) { + RTC_LOG(LS_ERROR) << "Failed to read PPS"; + return false; + } + } else { + // No SPS NALU - start reading from the first NALU in the buffer + reader.SeekToStart(); + } + + // Allocate memory as a block buffer. + CMBlockBufferRef block_buffer = nullptr; + CFAllocatorRef block_allocator = CMMemoryPoolGetAllocator(memory_pool); + OSStatus status = CMBlockBufferCreateWithMemoryBlock( + kCFAllocatorDefault, nullptr, reader.BytesRemaining(), block_allocator, + nullptr, 0, reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag, + &block_buffer); + if (status != kCMBlockBufferNoErr) { + RTC_LOG(LS_ERROR) << "Failed to create block buffer."; + return false; + } + + // Make sure block buffer is contiguous. + CMBlockBufferRef contiguous_buffer = nullptr; + if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) { + status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, block_buffer, + block_allocator, nullptr, 0, 0, 0, + &contiguous_buffer); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: " + << status; + CFRelease(block_buffer); + return false; + } + } else { + contiguous_buffer = block_buffer; + block_buffer = nullptr; + } + + // Get a raw pointer into allocated memory. + size_t block_buffer_size = 0; + char* data_ptr = nullptr; + status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, + &block_buffer_size, &data_ptr); + if (status != kCMBlockBufferNoErr) { + RTC_LOG(LS_ERROR) << "Failed to get block buffer data pointer."; + CFRelease(contiguous_buffer); + return false; + } + RTC_DCHECK(block_buffer_size == reader.BytesRemaining()); + + // Write Avcc NALUs into block buffer memory. + AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr), + block_buffer_size); + while (reader.BytesRemaining() > 0) { + const uint8_t* nalu_data_ptr = nullptr; + size_t nalu_data_size = 0; + if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) { + writer.WriteNalu(nalu_data_ptr, nalu_data_size); + } + } + + // Create sample buffer. + status = CMSampleBufferCreate(kCFAllocatorDefault, contiguous_buffer, true, + nullptr, nullptr, video_format, 1, 0, nullptr, + 0, nullptr, out_sample_buffer); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to create sample buffer."; + CFRelease(contiguous_buffer); + return false; + } + CFRelease(contiguous_buffer); + return true; +} + +CMVideoFormatDescriptionRef CreateVideoFormatDescription( + const uint8_t* annexb_buffer, + size_t annexb_buffer_size) { + const uint8_t* param_set_ptrs[2] = {}; + size_t param_set_sizes[2] = {}; + AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size); + // Skip everyting before the SPS, then read the SPS and PPS + if (!reader.SeekToNextNaluOfType(kSps)) { + return nullptr; + } + if (!reader.ReadNalu(¶m_set_ptrs[0], ¶m_set_sizes[0])) { + RTC_LOG(LS_ERROR) << "Failed to read SPS"; + return nullptr; + } + if (!reader.ReadNalu(¶m_set_ptrs[1], ¶m_set_sizes[1])) { + RTC_LOG(LS_ERROR) << "Failed to read PPS"; + return nullptr; + } + + // Parse the SPS and PPS into a CMVideoFormatDescription. + CMVideoFormatDescriptionRef description = nullptr; + OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( + kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, &description); + if (status != noErr) { + RTC_LOG(LS_ERROR) << "Failed to create video format description."; + return nullptr; + } + return description; +} + +AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer, + size_t length) + : start_(annexb_buffer), length_(length) { + RTC_DCHECK(annexb_buffer); + offsets_ = H264::FindNaluIndices(annexb_buffer, length); + offset_ = offsets_.begin(); +} + +AnnexBBufferReader::~AnnexBBufferReader() = default; + +bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu, + size_t* out_length) { + RTC_DCHECK(out_nalu); + RTC_DCHECK(out_length); + *out_nalu = nullptr; + *out_length = 0; + + if (offset_ == offsets_.end()) { + return false; + } + *out_nalu = start_ + offset_->payload_start_offset; + *out_length = offset_->payload_size; + ++offset_; + return true; +} + +size_t AnnexBBufferReader::BytesRemaining() const { + if (offset_ == offsets_.end()) { + return 0; + } + return length_ - offset_->start_offset; +} + +void AnnexBBufferReader::SeekToStart() { + offset_ = offsets_.begin(); +} + +bool AnnexBBufferReader::SeekToNextNaluOfType(NaluType type) { + for (; offset_ != offsets_.end(); ++offset_) { + if (offset_->payload_size < 1) + continue; + if (ParseNaluType(*(start_ + offset_->payload_start_offset)) == type) + return true; + } + return false; +} +AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length) + : start_(avcc_buffer), offset_(0), length_(length) { + RTC_DCHECK(avcc_buffer); +} + +bool AvccBufferWriter::WriteNalu(const uint8_t* data, size_t data_size) { + // Check if we can write this length of data. + if (data_size + kAvccHeaderByteSize > BytesRemaining()) { + return false; + } + // Write length header, which needs to be big endian. + uint32_t big_endian_length = CFSwapInt32HostToBig(data_size); + memcpy(start_ + offset_, &big_endian_length, sizeof(big_endian_length)); + offset_ += sizeof(big_endian_length); + // Write data. + memcpy(start_ + offset_, data, data_size); + offset_ += data_size; + return true; +} + +size_t AvccBufferWriter::BytesRemaining() const { + return length_ - offset_; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/components/video_codec/nalu_rewriter.h b/third_party/libwebrtc/sdk/objc/components/video_codec/nalu_rewriter.h new file mode 100644 index 0000000000..c6474971e2 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_codec/nalu_rewriter.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef SDK_OBJC_FRAMEWORK_CLASSES_VIDEOTOOLBOX_NALU_REWRITER_H_ +#define SDK_OBJC_FRAMEWORK_CLASSES_VIDEOTOOLBOX_NALU_REWRITER_H_ + +#include "modules/video_coding/codecs/h264/include/h264.h" + +#include <CoreMedia/CoreMedia.h> +#include <vector> + +#include "common_video/h264/h264_common.h" +#include "rtc_base/buffer.h" + +using webrtc::H264::NaluIndex; + +namespace webrtc { + +// Converts a sample buffer emitted from the VideoToolbox encoder into a buffer +// suitable for RTP. The sample buffer is in avcc format whereas the rtp buffer +// needs to be in Annex B format. Data is written directly to `annexb_buffer`. +bool H264CMSampleBufferToAnnexBBuffer(CMSampleBufferRef avcc_sample_buffer, + bool is_keyframe, + rtc::Buffer* annexb_buffer); + +// Converts a buffer received from RTP into a sample buffer suitable for the +// VideoToolbox decoder. The RTP buffer is in annex b format whereas the sample +// buffer is in avcc format. +// If `is_keyframe` is true then `video_format` is ignored since the format will +// be read from the buffer. Otherwise `video_format` must be provided. +// Caller is responsible for releasing the created sample buffer. +bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer, + size_t annexb_buffer_size, + CMVideoFormatDescriptionRef video_format, + CMSampleBufferRef* out_sample_buffer, + CMMemoryPoolRef memory_pool); + +// Returns a video format description created from the sps/pps information in +// the Annex B buffer. If there is no such information, nullptr is returned. +// The caller is responsible for releasing the description. +CMVideoFormatDescriptionRef CreateVideoFormatDescription( + const uint8_t* annexb_buffer, + size_t annexb_buffer_size); + +// Helper class for reading NALUs from an RTP Annex B buffer. +class AnnexBBufferReader final { + public: + AnnexBBufferReader(const uint8_t* annexb_buffer, size_t length); + ~AnnexBBufferReader(); + AnnexBBufferReader(const AnnexBBufferReader& other) = delete; + void operator=(const AnnexBBufferReader& other) = delete; + + // Returns a pointer to the beginning of the next NALU slice without the + // header bytes and its length. Returns false if no more slices remain. + bool ReadNalu(const uint8_t** out_nalu, size_t* out_length); + + // Returns the number of unread NALU bytes, including the size of the header. + // If the buffer has no remaining NALUs this will return zero. + size_t BytesRemaining() const; + + // Reset the reader to start reading from the first NALU + void SeekToStart(); + + // Seek to the next position that holds a NALU of the desired type, + // or the end if no such NALU is found. + // Return true if a NALU of the desired type is found, false if we + // reached the end instead + bool SeekToNextNaluOfType(H264::NaluType type); + + private: + // Returns the the next offset that contains NALU data. + size_t FindNextNaluHeader(const uint8_t* start, + size_t length, + size_t offset) const; + + const uint8_t* const start_; + std::vector<NaluIndex> offsets_; + std::vector<NaluIndex>::iterator offset_; + const size_t length_; +}; + +// Helper class for writing NALUs using avcc format into a buffer. +class AvccBufferWriter final { + public: + AvccBufferWriter(uint8_t* const avcc_buffer, size_t length); + ~AvccBufferWriter() {} + AvccBufferWriter(const AvccBufferWriter& other) = delete; + void operator=(const AvccBufferWriter& other) = delete; + + // Writes the data slice into the buffer. Returns false if there isn't + // enough space left. + bool WriteNalu(const uint8_t* data, size_t data_size); + + // Returns the unused bytes in the buffer. + size_t BytesRemaining() const; + + private: + uint8_t* const start_; + size_t offset_; + const size_t length_; +}; + +} // namespace webrtc + +#endif // SDK_OBJC_FRAMEWORK_CLASSES_VIDEOTOOLBOX_NALU_REWRITER_H_ diff --git a/third_party/libwebrtc/sdk/objc/components/video_frame_buffer/RTCCVPixelBuffer.h b/third_party/libwebrtc/sdk/objc/components/video_frame_buffer/RTCCVPixelBuffer.h new file mode 100644 index 0000000000..664d9bb904 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_frame_buffer/RTCCVPixelBuffer.h @@ -0,0 +1,52 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#import "RTCMacros.h" +#import "RTCVideoFrameBuffer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** RTCVideoFrameBuffer containing a CVPixelBufferRef */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCCVPixelBuffer) : NSObject <RTC_OBJC_TYPE(RTCVideoFrameBuffer)> + +@property(nonatomic, readonly) CVPixelBufferRef pixelBuffer; +@property(nonatomic, readonly) int cropX; +@property(nonatomic, readonly) int cropY; +@property(nonatomic, readonly) int cropWidth; +@property(nonatomic, readonly) int cropHeight; + ++ (NSSet<NSNumber *> *)supportedPixelFormats; + +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer; +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer + adaptedWidth:(int)adaptedWidth + adaptedHeight:(int)adaptedHeight + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + cropX:(int)cropX + cropY:(int)cropY; + +- (BOOL)requiresCropping; +- (BOOL)requiresScalingToWidth:(int)width height:(int)height; +- (int)bufferSizeForCroppingAndScalingToWidth:(int)width height:(int)height; + +/** The minimum size of the `tmpBuffer` must be the number of bytes returned from the + * bufferSizeForCroppingAndScalingToWidth:height: method. + * If that size is 0, the `tmpBuffer` may be nil. + */ +- (BOOL)cropAndScaleTo:(CVPixelBufferRef)outputPixelBuffer + withTempBuffer:(nullable uint8_t *)tmpBuffer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/components/video_frame_buffer/RTCCVPixelBuffer.mm b/third_party/libwebrtc/sdk/objc/components/video_frame_buffer/RTCCVPixelBuffer.mm new file mode 100644 index 0000000000..1a9b672d1a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/components/video_frame_buffer/RTCCVPixelBuffer.mm @@ -0,0 +1,367 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCVPixelBuffer.h" + +#import "api/video_frame_buffer/RTCNativeMutableI420Buffer.h" + +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "third_party/libyuv/include/libyuv.h" + +#if !defined(NDEBUG) && defined(WEBRTC_IOS) +#import <UIKit/UIKit.h> +#import <VideoToolbox/VideoToolbox.h> +#endif + +@implementation RTC_OBJC_TYPE (RTCCVPixelBuffer) { + int _width; + int _height; + int _bufferWidth; + int _bufferHeight; + int _cropWidth; + int _cropHeight; +} + +@synthesize pixelBuffer = _pixelBuffer; +@synthesize cropX = _cropX; +@synthesize cropY = _cropY; +@synthesize cropWidth = _cropWidth; +@synthesize cropHeight = _cropHeight; + ++ (NSSet<NSNumber*>*)supportedPixelFormats { + return [NSSet setWithObjects:@(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), + @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange), + @(kCVPixelFormatType_32BGRA), + @(kCVPixelFormatType_32ARGB), + nil]; +} + +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer { + return [self initWithPixelBuffer:pixelBuffer + adaptedWidth:CVPixelBufferGetWidth(pixelBuffer) + adaptedHeight:CVPixelBufferGetHeight(pixelBuffer) + cropWidth:CVPixelBufferGetWidth(pixelBuffer) + cropHeight:CVPixelBufferGetHeight(pixelBuffer) + cropX:0 + cropY:0]; +} + +- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer + adaptedWidth:(int)adaptedWidth + adaptedHeight:(int)adaptedHeight + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + cropX:(int)cropX + cropY:(int)cropY { + if (self = [super init]) { + _width = adaptedWidth; + _height = adaptedHeight; + _pixelBuffer = pixelBuffer; + _bufferWidth = CVPixelBufferGetWidth(_pixelBuffer); + _bufferHeight = CVPixelBufferGetHeight(_pixelBuffer); + _cropWidth = cropWidth; + _cropHeight = cropHeight; + // Can only crop at even pixels. + _cropX = cropX & ~1; + _cropY = cropY & ~1; + CVBufferRetain(_pixelBuffer); + } + + return self; +} + +- (void)dealloc { + CVBufferRelease(_pixelBuffer); +} + +- (int)width { + return _width; +} + +- (int)height { + return _height; +} + +- (BOOL)requiresCropping { + return _cropWidth != _bufferWidth || _cropHeight != _bufferHeight; +} + +- (BOOL)requiresScalingToWidth:(int)width height:(int)height { + return _cropWidth != width || _cropHeight != height; +} + +- (int)bufferSizeForCroppingAndScalingToWidth:(int)width height:(int)height { + const OSType srcPixelFormat = CVPixelBufferGetPixelFormatType(_pixelBuffer); + switch (srcPixelFormat) { + case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: + case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: { + int srcChromaWidth = (_cropWidth + 1) / 2; + int srcChromaHeight = (_cropHeight + 1) / 2; + int dstChromaWidth = (width + 1) / 2; + int dstChromaHeight = (height + 1) / 2; + + return srcChromaWidth * srcChromaHeight * 2 + dstChromaWidth * dstChromaHeight * 2; + } + case kCVPixelFormatType_32BGRA: + case kCVPixelFormatType_32ARGB: { + return 0; // Scaling RGBA frames does not require a temporary buffer. + } + } + RTC_DCHECK_NOTREACHED() << "Unsupported pixel format."; + return 0; +} + +- (BOOL)cropAndScaleTo:(CVPixelBufferRef)outputPixelBuffer + withTempBuffer:(nullable uint8_t*)tmpBuffer { + const OSType srcPixelFormat = CVPixelBufferGetPixelFormatType(_pixelBuffer); + const OSType dstPixelFormat = CVPixelBufferGetPixelFormatType(outputPixelBuffer); + + switch (srcPixelFormat) { + case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: + case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: { + size_t dstWidth = CVPixelBufferGetWidth(outputPixelBuffer); + size_t dstHeight = CVPixelBufferGetHeight(outputPixelBuffer); + if (dstWidth > 0 && dstHeight > 0) { + RTC_DCHECK(dstPixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange || + dstPixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); + if ([self requiresScalingToWidth:dstWidth height:dstHeight]) { + RTC_DCHECK(tmpBuffer); + } + [self cropAndScaleNV12To:outputPixelBuffer withTempBuffer:tmpBuffer]; + } + break; + } + case kCVPixelFormatType_32BGRA: + case kCVPixelFormatType_32ARGB: { + RTC_DCHECK(srcPixelFormat == dstPixelFormat); + [self cropAndScaleARGBTo:outputPixelBuffer]; + break; + } + default: { + RTC_DCHECK_NOTREACHED() << "Unsupported pixel format."; + } + } + + return YES; +} +- (id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)>)cropAndScaleWith:(int)offsetX + offsetY:(int)offsetY + cropWidth:(int)cropWidth + cropHeight:(int)cropHeight + scaleWidth:(int)scaleWidth + scaleHeight:(int)scaleHeight { + return [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] + initWithPixelBuffer:_pixelBuffer + adaptedWidth:scaleWidth + adaptedHeight:scaleHeight + cropWidth:cropWidth * _cropWidth / _width + cropHeight:cropHeight * _cropHeight / _height + cropX:_cropX + offsetX * _cropWidth / _width + cropY:_cropY + offsetY * _cropHeight / _height]; +} + +- (id<RTC_OBJC_TYPE(RTCI420Buffer)>)toI420 { + const OSType pixelFormat = CVPixelBufferGetPixelFormatType(_pixelBuffer); + + CVPixelBufferLockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly); + + RTC_OBJC_TYPE(RTCMutableI420Buffer)* i420Buffer = + [[RTC_OBJC_TYPE(RTCMutableI420Buffer) alloc] initWithWidth:[self width] height:[self height]]; + + switch (pixelFormat) { + case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: + case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: { + const uint8_t* srcY = + static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 0)); + const int srcYStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 0); + const uint8_t* srcUV = + static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 1)); + const int srcUVStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 1); + + // Crop just by modifying pointers. + srcY += srcYStride * _cropY + _cropX; + srcUV += srcUVStride * (_cropY / 2) + _cropX; + + // TODO(magjed): Use a frame buffer pool. + webrtc::NV12ToI420Scaler nv12ToI420Scaler; + nv12ToI420Scaler.NV12ToI420Scale(srcY, + srcYStride, + srcUV, + srcUVStride, + _cropWidth, + _cropHeight, + i420Buffer.mutableDataY, + i420Buffer.strideY, + i420Buffer.mutableDataU, + i420Buffer.strideU, + i420Buffer.mutableDataV, + i420Buffer.strideV, + i420Buffer.width, + i420Buffer.height); + break; + } + case kCVPixelFormatType_32BGRA: + case kCVPixelFormatType_32ARGB: { + CVPixelBufferRef scaledPixelBuffer = NULL; + CVPixelBufferRef sourcePixelBuffer = NULL; + if ([self requiresCropping] || + [self requiresScalingToWidth:i420Buffer.width height:i420Buffer.height]) { + CVPixelBufferCreate( + NULL, i420Buffer.width, i420Buffer.height, pixelFormat, NULL, &scaledPixelBuffer); + [self cropAndScaleTo:scaledPixelBuffer withTempBuffer:NULL]; + + CVPixelBufferLockBaseAddress(scaledPixelBuffer, kCVPixelBufferLock_ReadOnly); + sourcePixelBuffer = scaledPixelBuffer; + } else { + sourcePixelBuffer = _pixelBuffer; + } + const uint8_t* src = static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(sourcePixelBuffer)); + const size_t bytesPerRow = CVPixelBufferGetBytesPerRow(sourcePixelBuffer); + + if (pixelFormat == kCVPixelFormatType_32BGRA) { + // Corresponds to libyuv::FOURCC_ARGB + libyuv::ARGBToI420(src, + bytesPerRow, + i420Buffer.mutableDataY, + i420Buffer.strideY, + i420Buffer.mutableDataU, + i420Buffer.strideU, + i420Buffer.mutableDataV, + i420Buffer.strideV, + i420Buffer.width, + i420Buffer.height); + } else if (pixelFormat == kCVPixelFormatType_32ARGB) { + // Corresponds to libyuv::FOURCC_BGRA + libyuv::BGRAToI420(src, + bytesPerRow, + i420Buffer.mutableDataY, + i420Buffer.strideY, + i420Buffer.mutableDataU, + i420Buffer.strideU, + i420Buffer.mutableDataV, + i420Buffer.strideV, + i420Buffer.width, + i420Buffer.height); + } + + if (scaledPixelBuffer) { + CVPixelBufferUnlockBaseAddress(scaledPixelBuffer, kCVPixelBufferLock_ReadOnly); + CVBufferRelease(scaledPixelBuffer); + } + break; + } + default: { + RTC_DCHECK_NOTREACHED() << "Unsupported pixel format."; + } + } + + CVPixelBufferUnlockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly); + + return i420Buffer; +} + +#pragma mark - Debugging + +#if !defined(NDEBUG) && defined(WEBRTC_IOS) +- (id)debugQuickLookObject { + CGImageRef cgImage; + VTCreateCGImageFromCVPixelBuffer(_pixelBuffer, NULL, &cgImage); + UIImage *image = [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp]; + CGImageRelease(cgImage); + return image; +} +#endif + +#pragma mark - Private + +- (void)cropAndScaleNV12To:(CVPixelBufferRef)outputPixelBuffer withTempBuffer:(uint8_t*)tmpBuffer { + // Prepare output pointers. + CVReturn cvRet = CVPixelBufferLockBaseAddress(outputPixelBuffer, 0); + if (cvRet != kCVReturnSuccess) { + RTC_LOG(LS_ERROR) << "Failed to lock base address: " << cvRet; + } + const int dstWidth = CVPixelBufferGetWidth(outputPixelBuffer); + const int dstHeight = CVPixelBufferGetHeight(outputPixelBuffer); + uint8_t* dstY = + reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(outputPixelBuffer, 0)); + const int dstYStride = CVPixelBufferGetBytesPerRowOfPlane(outputPixelBuffer, 0); + uint8_t* dstUV = + reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(outputPixelBuffer, 1)); + const int dstUVStride = CVPixelBufferGetBytesPerRowOfPlane(outputPixelBuffer, 1); + + // Prepare source pointers. + CVPixelBufferLockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly); + const uint8_t* srcY = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 0)); + const int srcYStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 0); + const uint8_t* srcUV = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 1)); + const int srcUVStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 1); + + // Crop just by modifying pointers. + srcY += srcYStride * _cropY + _cropX; + srcUV += srcUVStride * (_cropY / 2) + _cropX; + + webrtc::NV12Scale(tmpBuffer, + srcY, + srcYStride, + srcUV, + srcUVStride, + _cropWidth, + _cropHeight, + dstY, + dstYStride, + dstUV, + dstUVStride, + dstWidth, + dstHeight); + + CVPixelBufferUnlockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly); + CVPixelBufferUnlockBaseAddress(outputPixelBuffer, 0); +} + +- (void)cropAndScaleARGBTo:(CVPixelBufferRef)outputPixelBuffer { + // Prepare output pointers. + CVReturn cvRet = CVPixelBufferLockBaseAddress(outputPixelBuffer, 0); + if (cvRet != kCVReturnSuccess) { + RTC_LOG(LS_ERROR) << "Failed to lock base address: " << cvRet; + } + const int dstWidth = CVPixelBufferGetWidth(outputPixelBuffer); + const int dstHeight = CVPixelBufferGetHeight(outputPixelBuffer); + + uint8_t* dst = reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddress(outputPixelBuffer)); + const int dstStride = CVPixelBufferGetBytesPerRow(outputPixelBuffer); + + // Prepare source pointers. + CVPixelBufferLockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly); + const uint8_t* src = static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(_pixelBuffer)); + const int srcStride = CVPixelBufferGetBytesPerRow(_pixelBuffer); + + // Crop just by modifying pointers. Need to ensure that src pointer points to a byte corresponding + // to the start of a new pixel (byte with B for BGRA) so that libyuv scales correctly. + const int bytesPerPixel = 4; + src += srcStride * _cropY + (_cropX * bytesPerPixel); + + // kCVPixelFormatType_32BGRA corresponds to libyuv::FOURCC_ARGB + libyuv::ARGBScale(src, + srcStride, + _cropWidth, + _cropHeight, + dst, + dstStride, + dstWidth, + dstHeight, + libyuv::kFilterBox); + + CVPixelBufferUnlockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly); + CVPixelBufferUnlockBaseAddress(outputPixelBuffer, 0); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/AVCaptureSession+DevicePosition.h b/third_party/libwebrtc/sdk/objc/helpers/AVCaptureSession+DevicePosition.h new file mode 100644 index 0000000000..32ab6877f0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/AVCaptureSession+DevicePosition.h @@ -0,0 +1,23 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> +#import <CoreMedia/CoreMedia.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface AVCaptureSession (DevicePosition) + +// Check the image's EXIF for the camera the image came from. ++ (AVCaptureDevicePosition)devicePositionForSampleBuffer:(CMSampleBufferRef)sampleBuffer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/helpers/AVCaptureSession+DevicePosition.mm b/third_party/libwebrtc/sdk/objc/helpers/AVCaptureSession+DevicePosition.mm new file mode 100644 index 0000000000..0814ecc6c5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/AVCaptureSession+DevicePosition.mm @@ -0,0 +1,51 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "AVCaptureSession+DevicePosition.h" + +BOOL CFStringContainsString(CFStringRef theString, CFStringRef stringToFind) { + return CFStringFindWithOptions(theString, + stringToFind, + CFRangeMake(0, CFStringGetLength(theString)), + kCFCompareCaseInsensitive, + nil); +} + +@implementation AVCaptureSession (DevicePosition) + ++ (AVCaptureDevicePosition)devicePositionForSampleBuffer:(CMSampleBufferRef)sampleBuffer { + // Check the image's EXIF for the camera the image came from. + AVCaptureDevicePosition cameraPosition = AVCaptureDevicePositionUnspecified; + CFDictionaryRef attachments = CMCopyDictionaryOfAttachments( + kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate); + if (attachments) { + int size = CFDictionaryGetCount(attachments); + if (size > 0) { + CFDictionaryRef cfExifDictVal = nil; + if (CFDictionaryGetValueIfPresent( + attachments, (const void *)CFSTR("{Exif}"), (const void **)&cfExifDictVal)) { + CFStringRef cfLensModelStrVal; + if (CFDictionaryGetValueIfPresent(cfExifDictVal, + (const void *)CFSTR("LensModel"), + (const void **)&cfLensModelStrVal)) { + if (CFStringContainsString(cfLensModelStrVal, CFSTR("front"))) { + cameraPosition = AVCaptureDevicePositionFront; + } else if (CFStringContainsString(cfLensModelStrVal, CFSTR("back"))) { + cameraPosition = AVCaptureDevicePositionBack; + } + } + } + } + CFRelease(attachments); + } + return cameraPosition; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/NSString+StdString.h b/third_party/libwebrtc/sdk/objc/helpers/NSString+StdString.h new file mode 100644 index 0000000000..b0324e8a19 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/NSString+StdString.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#include <string> + +#include "absl/strings/string_view.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSString (StdString) + +@property(nonatomic, readonly) std::string stdString; + ++ (std::string)stdStringForString:(NSString *)nsString; ++ (NSString *)stringForStdString:(const std::string &)stdString; + +@end + +@interface NSString (AbslStringView) + ++ (NSString *)stringForAbslStringView:(const absl::string_view)abslStringView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/sdk/objc/helpers/NSString+StdString.mm b/third_party/libwebrtc/sdk/objc/helpers/NSString+StdString.mm new file mode 100644 index 0000000000..c98432c445 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/NSString+StdString.mm @@ -0,0 +1,45 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "NSString+StdString.h" + +#include "absl/strings/string_view.h" + +@implementation NSString (StdString) + +- (std::string)stdString { + return [NSString stdStringForString:self]; +} + ++ (std::string)stdStringForString:(NSString *)nsString { + NSData *charData = [nsString dataUsingEncoding:NSUTF8StringEncoding]; + return std::string(reinterpret_cast<const char *>(charData.bytes), + charData.length); +} + ++ (NSString *)stringForStdString:(const std::string&)stdString { + // std::string may contain null termination character so we construct + // using length. + return [[NSString alloc] initWithBytes:stdString.data() + length:stdString.length() + encoding:NSUTF8StringEncoding]; +} + +@end + +@implementation NSString (AbslStringView) + ++ (NSString *)stringForAbslStringView:(const absl::string_view)abslStringView { + return [[NSString alloc] initWithBytes:abslStringView.data() + length:abslStringView.length() + encoding:NSUTF8StringEncoding]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/RTCCameraPreviewView.h b/third_party/libwebrtc/sdk/objc/helpers/RTCCameraPreviewView.h new file mode 100644 index 0000000000..db9b15a45c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/RTCCameraPreviewView.h @@ -0,0 +1,30 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +#import "RTCMacros.h" + +@class AVCaptureSession; + +/** RTCCameraPreviewView is a view that renders local video from an + * AVCaptureSession. + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCCameraPreviewView) : UIView + +/** The capture session being rendered in the view. Capture session + * is assigned to AVCaptureVideoPreviewLayer async in the same + * queue that the AVCaptureSession is started/stopped. + */ +@property(nonatomic, strong) AVCaptureSession* captureSession; + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/RTCCameraPreviewView.m b/third_party/libwebrtc/sdk/objc/helpers/RTCCameraPreviewView.m new file mode 100644 index 0000000000..12e87d8d64 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/RTCCameraPreviewView.m @@ -0,0 +1,123 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCCameraPreviewView.h" + +#import <AVFoundation/AVFoundation.h> +#import <UIKit/UIKit.h> + +#import "RTCDispatcher+Private.h" + +@implementation RTC_OBJC_TYPE (RTCCameraPreviewView) + +@synthesize captureSession = _captureSession; + ++ (Class)layerClass { + return [AVCaptureVideoPreviewLayer class]; +} + +- (instancetype)initWithFrame:(CGRect)aRect { + self = [super initWithFrame:aRect]; + if (self) { + [self addOrientationObserver]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder*)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self addOrientationObserver]; + } + return self; +} + +- (void)dealloc { + [self removeOrientationObserver]; +} + +- (void)setCaptureSession:(AVCaptureSession *)captureSession { + if (_captureSession == captureSession) { + return; + } + _captureSession = captureSession; + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeMain + block:^{ + AVCaptureVideoPreviewLayer *previewLayer = [self previewLayer]; + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + previewLayer.session = captureSession; + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeMain + block:^{ + [self setCorrectVideoOrientation]; + }]; + }]; + }]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + // Update the video orientation based on the device orientation. + [self setCorrectVideoOrientation]; +} + +-(void)orientationChanged:(NSNotification *)notification { + [self setCorrectVideoOrientation]; +} + +- (void)setCorrectVideoOrientation { + // Get current device orientation. + UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation; + AVCaptureVideoPreviewLayer *previewLayer = [self previewLayer]; + + // First check if we are allowed to set the video orientation. + if (previewLayer.connection.isVideoOrientationSupported) { + // Set the video orientation based on device orientation. + if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown) { + previewLayer.connection.videoOrientation = + AVCaptureVideoOrientationPortraitUpsideDown; + } else if (deviceOrientation == UIInterfaceOrientationLandscapeRight) { + previewLayer.connection.videoOrientation = + AVCaptureVideoOrientationLandscapeRight; + } else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft) { + previewLayer.connection.videoOrientation = + AVCaptureVideoOrientationLandscapeLeft; + } else if (deviceOrientation == UIInterfaceOrientationPortrait) { + previewLayer.connection.videoOrientation = + AVCaptureVideoOrientationPortrait; + } + // If device orientation switches to FaceUp or FaceDown, don't change video orientation. + } +} + +#pragma mark - Private + +- (void)addOrientationObserver { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(orientationChanged:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; +} + +- (void)removeOrientationObserver { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIDeviceOrientationDidChangeNotification + object:nil]; +} + +- (AVCaptureVideoPreviewLayer *)previewLayer { + return (AVCaptureVideoPreviewLayer *)self.layer; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher+Private.h b/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher+Private.h new file mode 100644 index 0000000000..195c651790 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher+Private.h @@ -0,0 +1,18 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDispatcher.h" + +@interface RTC_OBJC_TYPE (RTCDispatcher) +() + + + (dispatch_queue_t)dispatchQueueForType : (RTCDispatcherQueueType)dispatchType; + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher.h b/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher.h new file mode 100644 index 0000000000..e148af6dea --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher.h @@ -0,0 +1,46 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> + +#import "RTCMacros.h" + +typedef NS_ENUM(NSInteger, RTCDispatcherQueueType) { + // Main dispatcher queue. + RTCDispatcherTypeMain, + // Used for starting/stopping AVCaptureSession, and assigning + // capture session to AVCaptureVideoPreviewLayer. + RTCDispatcherTypeCaptureSession, + // Used for operations on AVAudioSession. + RTCDispatcherTypeAudioSession, + // Used for operations on NWPathMonitor. + RTCDispatcherTypeNetworkMonitor, +}; + +/** Dispatcher that asynchronously dispatches blocks to a specific + * shared dispatch queue. + */ +RTC_OBJC_EXPORT +@interface RTC_OBJC_TYPE (RTCDispatcher) : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** Dispatch the block asynchronously on the queue for dispatchType. + * @param dispatchType The queue type to dispatch on. + * @param block The block to dispatch asynchronously. + */ ++ (void)dispatchAsyncOnType:(RTCDispatcherQueueType)dispatchType block:(dispatch_block_t)block; + +/** Returns YES if run on queue for the dispatchType otherwise NO. + * Useful for asserting that a method is run on a correct queue. + */ ++ (BOOL)isOnQueueForType:(RTCDispatcherQueueType)dispatchType; + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher.m b/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher.m new file mode 100644 index 0000000000..4df19bc297 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/RTCDispatcher.m @@ -0,0 +1,65 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "RTCDispatcher+Private.h" + +static dispatch_queue_t kAudioSessionQueue = nil; +static dispatch_queue_t kCaptureSessionQueue = nil; +static dispatch_queue_t kNetworkMonitorQueue = nil; + +@implementation RTC_OBJC_TYPE (RTCDispatcher) + ++ (void)initialize { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + kAudioSessionQueue = dispatch_queue_create( + "org.webrtc.RTCDispatcherAudioSession", + DISPATCH_QUEUE_SERIAL); + kCaptureSessionQueue = dispatch_queue_create( + "org.webrtc.RTCDispatcherCaptureSession", + DISPATCH_QUEUE_SERIAL); + kNetworkMonitorQueue = + dispatch_queue_create("org.webrtc.RTCDispatcherNetworkMonitor", DISPATCH_QUEUE_SERIAL); + }); +} + ++ (void)dispatchAsyncOnType:(RTCDispatcherQueueType)dispatchType + block:(dispatch_block_t)block { + dispatch_queue_t queue = [self dispatchQueueForType:dispatchType]; + dispatch_async(queue, block); +} + ++ (BOOL)isOnQueueForType:(RTCDispatcherQueueType)dispatchType { + dispatch_queue_t targetQueue = [self dispatchQueueForType:dispatchType]; + const char* targetLabel = dispatch_queue_get_label(targetQueue); + const char* currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); + + NSAssert(strlen(targetLabel) > 0, @"Label is required for the target queue."); + NSAssert(strlen(currentLabel) > 0, @"Label is required for the current queue."); + + return strcmp(targetLabel, currentLabel) == 0; +} + +#pragma mark - Private + ++ (dispatch_queue_t)dispatchQueueForType:(RTCDispatcherQueueType)dispatchType { + switch (dispatchType) { + case RTCDispatcherTypeMain: + return dispatch_get_main_queue(); + case RTCDispatcherTypeCaptureSession: + return kCaptureSessionQueue; + case RTCDispatcherTypeAudioSession: + return kAudioSessionQueue; + case RTCDispatcherTypeNetworkMonitor: + return kNetworkMonitorQueue; + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/UIDevice+RTCDevice.h b/third_party/libwebrtc/sdk/objc/helpers/UIDevice+RTCDevice.h new file mode 100644 index 0000000000..ab477e2ada --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/UIDevice+RTCDevice.h @@ -0,0 +1,111 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSInteger, RTCDeviceType) { + RTCDeviceTypeUnknown, + RTCDeviceTypeIPhone1G, + RTCDeviceTypeIPhone3G, + RTCDeviceTypeIPhone3GS, + RTCDeviceTypeIPhone4, + RTCDeviceTypeIPhone4Verizon, + RTCDeviceTypeIPhone4S, + RTCDeviceTypeIPhone5GSM, + RTCDeviceTypeIPhone5GSM_CDMA, + RTCDeviceTypeIPhone5CGSM, + RTCDeviceTypeIPhone5CGSM_CDMA, + RTCDeviceTypeIPhone5SGSM, + RTCDeviceTypeIPhone5SGSM_CDMA, + RTCDeviceTypeIPhone6Plus, + RTCDeviceTypeIPhone6, + RTCDeviceTypeIPhone6S, + RTCDeviceTypeIPhone6SPlus, + RTCDeviceTypeIPhone7, + RTCDeviceTypeIPhone7Plus, + RTCDeviceTypeIPhoneSE, + RTCDeviceTypeIPhone8, + RTCDeviceTypeIPhone8Plus, + RTCDeviceTypeIPhoneX, + RTCDeviceTypeIPhoneXS, + RTCDeviceTypeIPhoneXSMax, + RTCDeviceTypeIPhoneXR, + RTCDeviceTypeIPhone11, + RTCDeviceTypeIPhone11Pro, + RTCDeviceTypeIPhone11ProMax, + RTCDeviceTypeIPhone12Mini, + RTCDeviceTypeIPhone12, + RTCDeviceTypeIPhone12Pro, + RTCDeviceTypeIPhone12ProMax, + RTCDeviceTypeIPhoneSE2Gen, + RTCDeviceTypeIPhone13, + RTCDeviceTypeIPhone13Mini, + RTCDeviceTypeIPhone13Pro, + RTCDeviceTypeIPhone13ProMax, + + RTCDeviceTypeIPodTouch1G, + RTCDeviceTypeIPodTouch2G, + RTCDeviceTypeIPodTouch3G, + RTCDeviceTypeIPodTouch4G, + RTCDeviceTypeIPodTouch5G, + RTCDeviceTypeIPodTouch6G, + RTCDeviceTypeIPodTouch7G, + RTCDeviceTypeIPad, + RTCDeviceTypeIPad2Wifi, + RTCDeviceTypeIPad2GSM, + RTCDeviceTypeIPad2CDMA, + RTCDeviceTypeIPad2Wifi2, + RTCDeviceTypeIPadMiniWifi, + RTCDeviceTypeIPadMiniGSM, + RTCDeviceTypeIPadMiniGSM_CDMA, + RTCDeviceTypeIPad3Wifi, + RTCDeviceTypeIPad3GSM_CDMA, + RTCDeviceTypeIPad3GSM, + RTCDeviceTypeIPad4Wifi, + RTCDeviceTypeIPad4GSM, + RTCDeviceTypeIPad4GSM_CDMA, + RTCDeviceTypeIPad5, + RTCDeviceTypeIPad6, + RTCDeviceTypeIPadAirWifi, + RTCDeviceTypeIPadAirCellular, + RTCDeviceTypeIPadAirWifiCellular, + RTCDeviceTypeIPadAir2, + RTCDeviceTypeIPadMini2GWifi, + RTCDeviceTypeIPadMini2GCellular, + RTCDeviceTypeIPadMini2GWifiCellular, + RTCDeviceTypeIPadMini3, + RTCDeviceTypeIPadMini4, + RTCDeviceTypeIPadPro9Inch, + RTCDeviceTypeIPadPro12Inch, + RTCDeviceTypeIPadPro12Inch2, + RTCDeviceTypeIPadPro10Inch, + RTCDeviceTypeIPad7Gen10Inch, + RTCDeviceTypeIPadPro3Gen11Inch, + RTCDeviceTypeIPadPro3Gen12Inch, + RTCDeviceTypeIPadPro4Gen11Inch, + RTCDeviceTypeIPadPro4Gen12Inch, + RTCDeviceTypeIPadMini5Gen, + RTCDeviceTypeIPadAir3Gen, + RTCDeviceTypeIPad8, + RTCDeviceTypeIPad9, + RTCDeviceTypeIPadMini6, + RTCDeviceTypeIPadAir4Gen, + RTCDeviceTypeIPadPro5Gen11Inch, + RTCDeviceTypeIPadPro5Gen12Inch, + RTCDeviceTypeSimulatori386, + RTCDeviceTypeSimulatorx86_64, +}; + +@interface UIDevice (RTCDevice) + ++ (RTCDeviceType)deviceType; ++ (BOOL)isIOS11OrLater; + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/UIDevice+RTCDevice.mm b/third_party/libwebrtc/sdk/objc/helpers/UIDevice+RTCDevice.mm new file mode 100644 index 0000000000..77a5d79f79 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/UIDevice+RTCDevice.mm @@ -0,0 +1,171 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "UIDevice+RTCDevice.h" + +#import <sys/utsname.h> +#include <memory> + +@implementation UIDevice (RTCDevice) + ++ (RTCDeviceType)deviceType { + NSDictionary *machineNameToType = @{ + @"iPhone1,1" : @(RTCDeviceTypeIPhone1G), + @"iPhone1,2" : @(RTCDeviceTypeIPhone3G), + @"iPhone2,1" : @(RTCDeviceTypeIPhone3GS), + @"iPhone3,1" : @(RTCDeviceTypeIPhone4), + @"iPhone3,2" : @(RTCDeviceTypeIPhone4), + @"iPhone3,3" : @(RTCDeviceTypeIPhone4Verizon), + @"iPhone4,1" : @(RTCDeviceTypeIPhone4S), + @"iPhone5,1" : @(RTCDeviceTypeIPhone5GSM), + @"iPhone5,2" : @(RTCDeviceTypeIPhone5GSM_CDMA), + @"iPhone5,3" : @(RTCDeviceTypeIPhone5CGSM), + @"iPhone5,4" : @(RTCDeviceTypeIPhone5CGSM_CDMA), + @"iPhone6,1" : @(RTCDeviceTypeIPhone5SGSM), + @"iPhone6,2" : @(RTCDeviceTypeIPhone5SGSM_CDMA), + @"iPhone7,1" : @(RTCDeviceTypeIPhone6Plus), + @"iPhone7,2" : @(RTCDeviceTypeIPhone6), + @"iPhone8,1" : @(RTCDeviceTypeIPhone6S), + @"iPhone8,2" : @(RTCDeviceTypeIPhone6SPlus), + @"iPhone8,4" : @(RTCDeviceTypeIPhoneSE), + @"iPhone9,1" : @(RTCDeviceTypeIPhone7), + @"iPhone9,2" : @(RTCDeviceTypeIPhone7Plus), + @"iPhone9,3" : @(RTCDeviceTypeIPhone7), + @"iPhone9,4" : @(RTCDeviceTypeIPhone7Plus), + @"iPhone10,1" : @(RTCDeviceTypeIPhone8), + @"iPhone10,2" : @(RTCDeviceTypeIPhone8Plus), + @"iPhone10,3" : @(RTCDeviceTypeIPhoneX), + @"iPhone10,4" : @(RTCDeviceTypeIPhone8), + @"iPhone10,5" : @(RTCDeviceTypeIPhone8Plus), + @"iPhone10,6" : @(RTCDeviceTypeIPhoneX), + @"iPhone11,2" : @(RTCDeviceTypeIPhoneXS), + @"iPhone11,4" : @(RTCDeviceTypeIPhoneXSMax), + @"iPhone11,6" : @(RTCDeviceTypeIPhoneXSMax), + @"iPhone11,8" : @(RTCDeviceTypeIPhoneXR), + @"iPhone12,1" : @(RTCDeviceTypeIPhone11), + @"iPhone12,3" : @(RTCDeviceTypeIPhone11Pro), + @"iPhone12,5" : @(RTCDeviceTypeIPhone11ProMax), + @"iPhone12,8" : @(RTCDeviceTypeIPhoneSE2Gen), + @"iPhone13,1" : @(RTCDeviceTypeIPhone12Mini), + @"iPhone13,2" : @(RTCDeviceTypeIPhone12), + @"iPhone13,3" : @(RTCDeviceTypeIPhone12Pro), + @"iPhone13,4" : @(RTCDeviceTypeIPhone12ProMax), + @"iPhone14,5" : @(RTCDeviceTypeIPhone13), + @"iPhone14,4" : @(RTCDeviceTypeIPhone13Mini), + @"iPhone14,2" : @(RTCDeviceTypeIPhone13Pro), + @"iPhone14,3" : @(RTCDeviceTypeIPhone13ProMax), + @"iPod1,1" : @(RTCDeviceTypeIPodTouch1G), + @"iPod2,1" : @(RTCDeviceTypeIPodTouch2G), + @"iPod3,1" : @(RTCDeviceTypeIPodTouch3G), + @"iPod4,1" : @(RTCDeviceTypeIPodTouch4G), + @"iPod5,1" : @(RTCDeviceTypeIPodTouch5G), + @"iPod7,1" : @(RTCDeviceTypeIPodTouch6G), + @"iPod9,1" : @(RTCDeviceTypeIPodTouch7G), + @"iPad1,1" : @(RTCDeviceTypeIPad), + @"iPad2,1" : @(RTCDeviceTypeIPad2Wifi), + @"iPad2,2" : @(RTCDeviceTypeIPad2GSM), + @"iPad2,3" : @(RTCDeviceTypeIPad2CDMA), + @"iPad2,4" : @(RTCDeviceTypeIPad2Wifi2), + @"iPad2,5" : @(RTCDeviceTypeIPadMiniWifi), + @"iPad2,6" : @(RTCDeviceTypeIPadMiniGSM), + @"iPad2,7" : @(RTCDeviceTypeIPadMiniGSM_CDMA), + @"iPad3,1" : @(RTCDeviceTypeIPad3Wifi), + @"iPad3,2" : @(RTCDeviceTypeIPad3GSM_CDMA), + @"iPad3,3" : @(RTCDeviceTypeIPad3GSM), + @"iPad3,4" : @(RTCDeviceTypeIPad4Wifi), + @"iPad3,5" : @(RTCDeviceTypeIPad4GSM), + @"iPad3,6" : @(RTCDeviceTypeIPad4GSM_CDMA), + @"iPad4,1" : @(RTCDeviceTypeIPadAirWifi), + @"iPad4,2" : @(RTCDeviceTypeIPadAirCellular), + @"iPad4,3" : @(RTCDeviceTypeIPadAirWifiCellular), + @"iPad4,4" : @(RTCDeviceTypeIPadMini2GWifi), + @"iPad4,5" : @(RTCDeviceTypeIPadMini2GCellular), + @"iPad4,6" : @(RTCDeviceTypeIPadMini2GWifiCellular), + @"iPad4,7" : @(RTCDeviceTypeIPadMini3), + @"iPad4,8" : @(RTCDeviceTypeIPadMini3), + @"iPad4,9" : @(RTCDeviceTypeIPadMini3), + @"iPad5,1" : @(RTCDeviceTypeIPadMini4), + @"iPad5,2" : @(RTCDeviceTypeIPadMini4), + @"iPad5,3" : @(RTCDeviceTypeIPadAir2), + @"iPad5,4" : @(RTCDeviceTypeIPadAir2), + @"iPad6,3" : @(RTCDeviceTypeIPadPro9Inch), + @"iPad6,4" : @(RTCDeviceTypeIPadPro9Inch), + @"iPad6,7" : @(RTCDeviceTypeIPadPro12Inch), + @"iPad6,8" : @(RTCDeviceTypeIPadPro12Inch), + @"iPad6,11" : @(RTCDeviceTypeIPad5), + @"iPad6,12" : @(RTCDeviceTypeIPad5), + @"iPad7,1" : @(RTCDeviceTypeIPadPro12Inch2), + @"iPad7,2" : @(RTCDeviceTypeIPadPro12Inch2), + @"iPad7,3" : @(RTCDeviceTypeIPadPro10Inch), + @"iPad7,4" : @(RTCDeviceTypeIPadPro10Inch), + @"iPad7,5" : @(RTCDeviceTypeIPad6), + @"iPad7,6" : @(RTCDeviceTypeIPad6), + @"iPad7,11" : @(RTCDeviceTypeIPad7Gen10Inch), + @"iPad7,12" : @(RTCDeviceTypeIPad7Gen10Inch), + @"iPad8,1" : @(RTCDeviceTypeIPadPro3Gen11Inch), + @"iPad8,2" : @(RTCDeviceTypeIPadPro3Gen11Inch), + @"iPad8,3" : @(RTCDeviceTypeIPadPro3Gen11Inch), + @"iPad8,4" : @(RTCDeviceTypeIPadPro3Gen11Inch), + @"iPad8,5" : @(RTCDeviceTypeIPadPro3Gen12Inch), + @"iPad8,6" : @(RTCDeviceTypeIPadPro3Gen12Inch), + @"iPad8,7" : @(RTCDeviceTypeIPadPro3Gen12Inch), + @"iPad8,8" : @(RTCDeviceTypeIPadPro3Gen12Inch), + @"iPad8,9" : @(RTCDeviceTypeIPadPro4Gen11Inch), + @"iPad8,10" : @(RTCDeviceTypeIPadPro4Gen11Inch), + @"iPad8,11" : @(RTCDeviceTypeIPadPro4Gen12Inch), + @"iPad8,12" : @(RTCDeviceTypeIPadPro4Gen12Inch), + @"iPad11,1" : @(RTCDeviceTypeIPadMini5Gen), + @"iPad11,2" : @(RTCDeviceTypeIPadMini5Gen), + @"iPad11,3" : @(RTCDeviceTypeIPadAir3Gen), + @"iPad11,4" : @(RTCDeviceTypeIPadAir3Gen), + @"iPad11,6" : @(RTCDeviceTypeIPad8), + @"iPad11,7" : @(RTCDeviceTypeIPad8), + @"iPad12,1" : @(RTCDeviceTypeIPad9), + @"iPad12,2" : @(RTCDeviceTypeIPad9), + @"iPad13,1" : @(RTCDeviceTypeIPadAir4Gen), + @"iPad13,2" : @(RTCDeviceTypeIPadAir4Gen), + @"iPad13,4" : @(RTCDeviceTypeIPadPro5Gen11Inch), + @"iPad13,5" : @(RTCDeviceTypeIPadPro5Gen11Inch), + @"iPad13,6" : @(RTCDeviceTypeIPadPro5Gen11Inch), + @"iPad13,7" : @(RTCDeviceTypeIPadPro5Gen11Inch), + @"iPad13,8" : @(RTCDeviceTypeIPadPro5Gen12Inch), + @"iPad13,9" : @(RTCDeviceTypeIPadPro5Gen12Inch), + @"iPad13,10" : @(RTCDeviceTypeIPadPro5Gen12Inch), + @"iPad13,11" : @(RTCDeviceTypeIPadPro5Gen12Inch), + @"iPad14,1" : @(RTCDeviceTypeIPadMini6), + @"iPad14,2" : @(RTCDeviceTypeIPadMini6), + @"i386" : @(RTCDeviceTypeSimulatori386), + @"x86_64" : @(RTCDeviceTypeSimulatorx86_64), + }; + + RTCDeviceType deviceType = RTCDeviceTypeUnknown; + NSNumber *typeNumber = machineNameToType[[self machineName]]; + if (typeNumber) { + deviceType = static_cast<RTCDeviceType>(typeNumber.integerValue); + } + return deviceType; +} + ++ (NSString *)machineName { + struct utsname systemInfo; + uname(&systemInfo); + return [[NSString alloc] initWithCString:systemInfo.machine + encoding:NSUTF8StringEncoding]; +} + ++ (double)currentDeviceSystemVersion { + return [self currentDevice].systemVersion.doubleValue; +} + ++ (BOOL)isIOS11OrLater { + return [self currentDeviceSystemVersion] >= 11.0; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/helpers/noop.mm b/third_party/libwebrtc/sdk/objc/helpers/noop.mm new file mode 100644 index 0000000000..16a8e6d5c1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/noop.mm @@ -0,0 +1,13 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file is only needed to make ninja happy on some platforms. +// On some platforms it is not possible to link an rtc_static_library +// without any source file listed in the GN target. diff --git a/third_party/libwebrtc/sdk/objc/helpers/scoped_cftyperef.h b/third_party/libwebrtc/sdk/objc/helpers/scoped_cftyperef.h new file mode 100644 index 0000000000..092f02b3af --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/helpers/scoped_cftyperef.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef SDK_OBJC_HELPERS_SCOPED_CFTYPEREF_H_ +#define SDK_OBJC_HELPERS_SCOPED_CFTYPEREF_H_ + +#include <CoreFoundation/CoreFoundation.h> +namespace rtc { + +// RETAIN: ScopedTypeRef should retain the object when it takes +// ownership. +// ASSUME: Assume the object already has already been retained. +// ScopedTypeRef takes over ownership. +enum class RetainPolicy { RETAIN, ASSUME }; + +namespace internal { +template <typename T> +struct CFTypeRefTraits { + static T InvalidValue() { return nullptr; } + static void Release(T ref) { CFRelease(ref); } + static T Retain(T ref) { + CFRetain(ref); + return ref; + } +}; + +template <typename T, typename Traits> +class ScopedTypeRef { + public: + ScopedTypeRef() : ptr_(Traits::InvalidValue()) {} + explicit ScopedTypeRef(T ptr) : ptr_(ptr) {} + ScopedTypeRef(T ptr, RetainPolicy policy) : ScopedTypeRef(ptr) { + if (ptr_ && policy == RetainPolicy::RETAIN) + Traits::Retain(ptr_); + } + + ScopedTypeRef(const ScopedTypeRef<T, Traits>& rhs) : ptr_(rhs.ptr_) { + if (ptr_) + ptr_ = Traits::Retain(ptr_); + } + + ~ScopedTypeRef() { + if (ptr_) { + Traits::Release(ptr_); + } + } + + T get() const { return ptr_; } + T operator->() const { return ptr_; } + explicit operator bool() const { return ptr_; } + + bool operator!() const { return !ptr_; } + + ScopedTypeRef& operator=(const T& rhs) { + if (ptr_) + Traits::Release(ptr_); + ptr_ = rhs; + return *this; + } + + ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& rhs) { + reset(rhs.get(), RetainPolicy::RETAIN); + return *this; + } + + // This is intended to take ownership of objects that are + // created by pass-by-pointer initializers. + T* InitializeInto() { + RTC_DCHECK(!ptr_); + return &ptr_; + } + + void reset(T ptr, RetainPolicy policy = RetainPolicy::ASSUME) { + if (ptr && policy == RetainPolicy::RETAIN) + Traits::Retain(ptr); + if (ptr_) + Traits::Release(ptr_); + ptr_ = ptr; + } + + T release() { + T temp = ptr_; + ptr_ = Traits::InvalidValue(); + return temp; + } + + private: + T ptr_; +}; +} // namespace internal + +template <typename T> +using ScopedCFTypeRef = + internal::ScopedTypeRef<T, internal::CFTypeRefTraits<T>>; + +template <typename T> +static ScopedCFTypeRef<T> AdoptCF(T cftype) { + return ScopedCFTypeRef<T>(cftype, RetainPolicy::RETAIN); +} + +template <typename T> +static ScopedCFTypeRef<T> ScopedCF(T cftype) { + return ScopedCFTypeRef<T>(cftype); +} + +} // namespace rtc + +#endif // SDK_OBJC_HELPERS_SCOPED_CFTYPEREF_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/audio_device_module.h b/third_party/libwebrtc/sdk/objc/native/api/audio_device_module.h new file mode 100644 index 0000000000..3405469709 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/audio_device_module.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_AUDIO_DEVICE_MODULE_H_ +#define SDK_OBJC_NATIVE_API_AUDIO_DEVICE_MODULE_H_ + +#include <memory> + +#include "modules/audio_device/include/audio_device.h" + +namespace webrtc { + +// If `bypass_voice_processing` is true, WebRTC will attempt to disable hardware +// audio processing on iOS. +// Warning: Setting `bypass_voice_processing` will have unpredictable +// consequences for the audio path in the device. It is not advisable to use in +// most scenarios. +rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule( + bool bypass_voice_processing = false); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_AUDIO_DEVICE_MODULE_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/audio_device_module.mm b/third_party/libwebrtc/sdk/objc/native/api/audio_device_module.mm new file mode 100644 index 0000000000..4e7b681e69 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/audio_device_module.mm @@ -0,0 +1,29 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "audio_device_module.h" + +#include "api/make_ref_counted.h" +#include "rtc_base/logging.h" + +#include "sdk/objc/native/src/audio/audio_device_module_ios.h" + +namespace webrtc { + +rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule(bool bypass_voice_processing) { + RTC_DLOG(LS_INFO) << __FUNCTION__; +#if defined(WEBRTC_IOS) + return rtc::make_ref_counted<ios_adm::AudioDeviceModuleIOS>(bypass_voice_processing); +#else + RTC_LOG(LS_ERROR) << "current platform is not supported => this module will self destruct!"; + return nullptr; +#endif +} +} diff --git a/third_party/libwebrtc/sdk/objc/native/api/network_monitor_factory.h b/third_party/libwebrtc/sdk/objc/native/api/network_monitor_factory.h new file mode 100644 index 0000000000..903c66893d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/network_monitor_factory.h @@ -0,0 +1,24 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_NETWORK_MONITOR_FACTORY_H_ +#define SDK_OBJC_NATIVE_API_NETWORK_MONITOR_FACTORY_H_ + +#include <memory> + +#include "rtc_base/network_monitor_factory.h" + +namespace webrtc { + +std::unique_ptr<rtc::NetworkMonitorFactory> CreateNetworkMonitorFactory(); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_NETWORK_MONITOR_FACTORY_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/network_monitor_factory.mm b/third_party/libwebrtc/sdk/objc/native/api/network_monitor_factory.mm new file mode 100644 index 0000000000..acde634b1d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/network_monitor_factory.mm @@ -0,0 +1,30 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "network_monitor_factory.h" + +#if defined(WEBRTC_IOS) +#include "sdk/objc/native/src/objc_network_monitor.h" +#endif + +#include "rtc_base/logging.h" + +namespace webrtc { + +std::unique_ptr<rtc::NetworkMonitorFactory> CreateNetworkMonitorFactory() { + RTC_DLOG(LS_INFO) << __FUNCTION__; +#if defined(WEBRTC_IOS) + return std::make_unique<ObjCNetworkMonitorFactory>(); +#else + return nullptr; +#endif +} + +} diff --git a/third_party/libwebrtc/sdk/objc/native/api/objc_audio_device_module.h b/third_party/libwebrtc/sdk/objc/native/api/objc_audio_device_module.h new file mode 100644 index 0000000000..0fe2dda4a0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/objc_audio_device_module.h @@ -0,0 +1,24 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_OBJC_AUDIO_DEVICE_MODULE_H_ +#define SDK_OBJC_NATIVE_API_OBJC_AUDIO_DEVICE_MODULE_H_ + +#import "components/audio/RTCAudioDevice.h" +#include "modules/audio_device/include/audio_device.h" + +namespace webrtc { + +rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule( + id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_OBJC_AUDIO_DEVICE_MODULE_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/objc_audio_device_module.mm b/third_party/libwebrtc/sdk/objc/native/api/objc_audio_device_module.mm new file mode 100644 index 0000000000..76edd45605 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/objc_audio_device_module.mm @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "objc_audio_device_module.h" + +#include "api/make_ref_counted.h" +#include "rtc_base/logging.h" + +#include "sdk/objc/native/src/objc_audio_device.h" + +namespace webrtc { + +rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule( + id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + return rtc::make_ref_counted<objc_adm::ObjCAudioDeviceModule>(audio_device); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/ssl_certificate_verifier.h b/third_party/libwebrtc/sdk/objc/native/api/ssl_certificate_verifier.h new file mode 100644 index 0000000000..35ab1be9a8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/ssl_certificate_verifier.h @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_SSL_CERTIFICATE_VERIFIER_H_ +#define SDK_OBJC_NATIVE_API_SSL_CERTIFICATE_VERIFIER_H_ + +#include <memory> + +#import "RTCSSLCertificateVerifier.h" +#include "rtc_base/ssl_certificate.h" + +namespace webrtc { + +std::unique_ptr<rtc::SSLCertificateVerifier> ObjCToNativeCertificateVerifier( + id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)> objc_certificate_verifier); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_SSL_CERTIFICATE_VERIFIER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/ssl_certificate_verifier.mm b/third_party/libwebrtc/sdk/objc/native/api/ssl_certificate_verifier.mm new file mode 100644 index 0000000000..4437402b9c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/ssl_certificate_verifier.mm @@ -0,0 +1,48 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "ssl_certificate_verifier.h" + +#include "rtc_base/buffer.h" + +namespace { + +class SSLCertificateVerifierAdapter final : public rtc::SSLCertificateVerifier { + public: + SSLCertificateVerifierAdapter( + id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)> objc_certificate_verifier) + : objc_certificate_verifier_(objc_certificate_verifier) { + RTC_DCHECK(objc_certificate_verifier_ != nil); + } + + bool Verify(const rtc::SSLCertificate& certificate) override { + @autoreleasepool { + rtc::Buffer der_buffer; + certificate.ToDER(&der_buffer); + NSData* serialized_certificate = [[NSData alloc] initWithBytes:der_buffer.data() + length:der_buffer.size()]; + return [objc_certificate_verifier_ verify:serialized_certificate]; + } + } + + private: + id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)> objc_certificate_verifier_; +}; + +} + +namespace webrtc { + +std::unique_ptr<rtc::SSLCertificateVerifier> ObjCToNativeCertificateVerifier( + id<RTC_OBJC_TYPE(RTCSSLCertificateVerifier)> objc_certificate_verifier) { + return std::make_unique<SSLCertificateVerifierAdapter>(objc_certificate_verifier); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_capturer.h b/third_party/libwebrtc/sdk/objc/native/api/video_capturer.h new file mode 100644 index 0000000000..9847d8148b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_capturer.h @@ -0,0 +1,29 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_VIDEO_CAPTURER_H_ +#define SDK_OBJC_NATIVE_API_VIDEO_CAPTURER_H_ + +#import "base/RTCVideoCapturer.h" + +#include "api/media_stream_interface.h" +#include "api/scoped_refptr.h" +#include "rtc_base/thread.h" + +namespace webrtc { + +rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> ObjCToNativeVideoCapturer( + RTC_OBJC_TYPE(RTCVideoCapturer) * objc_video_capturer, + rtc::Thread* signaling_thread, + rtc::Thread* worker_thread); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_VIDEO_CAPTURER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_capturer.mm b/third_party/libwebrtc/sdk/objc/native/api/video_capturer.mm new file mode 100644 index 0000000000..a7260ab802 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_capturer.mm @@ -0,0 +1,35 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/api/video_capturer.h" + +#include "absl/memory/memory.h" +#include "api/video_track_source_proxy_factory.h" +#include "sdk/objc/native/src/objc_video_track_source.h" + +namespace webrtc { + +rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> ObjCToNativeVideoCapturer( + RTC_OBJC_TYPE(RTCVideoCapturer) * objc_video_capturer, + rtc::Thread *signaling_thread, + rtc::Thread *worker_thread) { + RTCObjCVideoSourceAdapter *adapter = [[RTCObjCVideoSourceAdapter alloc] init]; + rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> objc_video_track_source = + rtc::make_ref_counted<webrtc::ObjCVideoTrackSource>(adapter); + rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> video_source = + webrtc::CreateVideoTrackSourceProxy( + signaling_thread, worker_thread, objc_video_track_source.get()); + + objc_video_capturer.delegate = adapter; + + return video_source; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_decoder_factory.h b/third_party/libwebrtc/sdk/objc/native/api/video_decoder_factory.h new file mode 100644 index 0000000000..03d8af3cfe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_decoder_factory.h @@ -0,0 +1,27 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_VIDEO_DECODER_FACTORY_H_ +#define SDK_OBJC_NATIVE_API_VIDEO_DECODER_FACTORY_H_ + +#include <memory> + +#import "base/RTCVideoDecoderFactory.h" + +#include "api/video_codecs/video_decoder_factory.h" + +namespace webrtc { + +std::unique_ptr<VideoDecoderFactory> ObjCToNativeVideoDecoderFactory( + id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> objc_video_decoder_factory); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_VIDEO_DECODER_FACTORY_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_decoder_factory.mm b/third_party/libwebrtc/sdk/objc/native/api/video_decoder_factory.mm new file mode 100644 index 0000000000..d418f2fe6f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_decoder_factory.mm @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/api/video_decoder_factory.h" + +#include <memory> + +#include "sdk/objc/native/src/objc_video_decoder_factory.h" + +namespace webrtc { + +std::unique_ptr<VideoDecoderFactory> ObjCToNativeVideoDecoderFactory( + id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> objc_video_decoder_factory) { + return std::make_unique<ObjCVideoDecoderFactory>(objc_video_decoder_factory); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_encoder_factory.h b/third_party/libwebrtc/sdk/objc/native/api/video_encoder_factory.h new file mode 100644 index 0000000000..6e551b288d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_encoder_factory.h @@ -0,0 +1,27 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_VIDEO_ENCODER_FACTORY_H_ +#define SDK_OBJC_NATIVE_API_VIDEO_ENCODER_FACTORY_H_ + +#include <memory> + +#import "base/RTCVideoEncoderFactory.h" + +#include "api/video_codecs/video_encoder_factory.h" + +namespace webrtc { + +std::unique_ptr<VideoEncoderFactory> ObjCToNativeVideoEncoderFactory( + id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> objc_video_encoder_factory); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_VIDEO_ENCODER_FACTORY_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_encoder_factory.mm b/third_party/libwebrtc/sdk/objc/native/api/video_encoder_factory.mm new file mode 100644 index 0000000000..6fa5563f75 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_encoder_factory.mm @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/api/video_encoder_factory.h" + +#include <memory> + +#include "sdk/objc/native/src/objc_video_encoder_factory.h" + +namespace webrtc { + +std::unique_ptr<VideoEncoderFactory> ObjCToNativeVideoEncoderFactory( + id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> objc_video_encoder_factory) { + return std::make_unique<ObjCVideoEncoderFactory>(objc_video_encoder_factory); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_frame.h b/third_party/libwebrtc/sdk/objc/native/api/video_frame.h new file mode 100644 index 0000000000..b4416ffabe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_frame.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_VIDEO_FRAME_H_ +#define SDK_OBJC_NATIVE_API_VIDEO_FRAME_H_ + +#import "base/RTCVideoFrame.h" + +#include "api/video/video_frame.h" + +namespace webrtc { + +RTC_OBJC_TYPE(RTCVideoFrame) * NativeToObjCVideoFrame(const VideoFrame& frame); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_VIDEO_FRAME_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_frame.mm b/third_party/libwebrtc/sdk/objc/native/api/video_frame.mm new file mode 100644 index 0000000000..b82994fd5f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_frame.mm @@ -0,0 +1,21 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/api/video_frame.h" + +#include "sdk/objc/native/src/objc_video_frame.h" + +namespace webrtc { + +RTC_OBJC_TYPE(RTCVideoFrame) * NativeToObjCVideoFrame(const VideoFrame& frame) { + return ToObjCVideoFrame(frame); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_frame_buffer.h b/third_party/libwebrtc/sdk/objc/native/api/video_frame_buffer.h new file mode 100644 index 0000000000..204d65d850 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_frame_buffer.h @@ -0,0 +1,29 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_VIDEO_FRAME_BUFFER_H_ +#define SDK_OBJC_NATIVE_API_VIDEO_FRAME_BUFFER_H_ + +#import "base/RTCVideoFrameBuffer.h" + +#include "api/scoped_refptr.h" +#include "common_video/include/video_frame_buffer.h" + +namespace webrtc { + +rtc::scoped_refptr<VideoFrameBuffer> ObjCToNativeVideoFrameBuffer( + id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> objc_video_frame_buffer); + +id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> NativeToObjCVideoFrameBuffer( + const rtc::scoped_refptr<VideoFrameBuffer>& buffer); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_VIDEO_FRAME_BUFFER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_frame_buffer.mm b/third_party/libwebrtc/sdk/objc/native/api/video_frame_buffer.mm new file mode 100644 index 0000000000..4fe9037bce --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_frame_buffer.mm @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/api/video_frame_buffer.h" + +#include "api/make_ref_counted.h" +#include "sdk/objc/native/src/objc_frame_buffer.h" + +namespace webrtc { + +rtc::scoped_refptr<VideoFrameBuffer> ObjCToNativeVideoFrameBuffer( + id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> objc_video_frame_buffer) { + return rtc::make_ref_counted<ObjCFrameBuffer>(objc_video_frame_buffer); +} + +id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> NativeToObjCVideoFrameBuffer( + const rtc::scoped_refptr<VideoFrameBuffer> &buffer) { + return ToObjCVideoFrameBuffer(buffer); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_renderer.h b/third_party/libwebrtc/sdk/objc/native/api/video_renderer.h new file mode 100644 index 0000000000..04796b8049 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_renderer.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_API_VIDEO_RENDERER_H_ +#define SDK_OBJC_NATIVE_API_VIDEO_RENDERER_H_ + +#import "base/RTCVideoRenderer.h" + +#include <memory> + +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" + +namespace webrtc { + +std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> ObjCToNativeVideoRenderer( + id<RTC_OBJC_TYPE(RTCVideoRenderer)> objc_video_renderer); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_API_VIDEO_RENDERER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/api/video_renderer.mm b/third_party/libwebrtc/sdk/objc/native/api/video_renderer.mm new file mode 100644 index 0000000000..e92d47d1e3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/api/video_renderer.mm @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/api/video_renderer.h" + +#include <memory> + +#include "sdk/objc/native/src/objc_video_renderer.h" + +namespace webrtc { + +std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> ObjCToNativeVideoRenderer( + id<RTC_OBJC_TYPE(RTCVideoRenderer)> objc_video_renderer) { + return std::make_unique<ObjCVideoRenderer>(objc_video_renderer); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_ios.h b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_ios.h new file mode 100644 index 0000000000..a86acb56fe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_ios.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_DEVICE_IOS_H_ +#define SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_DEVICE_IOS_H_ + +#include <atomic> +#include <memory> + +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "audio_session_observer.h" +#include "modules/audio_device/audio_device_generic.h" +#include "rtc_base/buffer.h" +#include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" +#include "sdk/objc/base/RTCMacros.h" +#include "voice_processing_audio_unit.h" + +RTC_FWD_DECL_OBJC_CLASS(RTCNativeAudioSessionDelegateAdapter); + +namespace webrtc { + +class FineAudioBuffer; + +namespace ios_adm { + +// Implements full duplex 16-bit mono PCM audio support for iOS using a +// Voice-Processing (VP) I/O audio unit in Core Audio. The VP I/O audio unit +// supports audio echo cancellation. It also adds automatic gain control, +// adjustment of voice-processing quality and muting. +// +// An instance must be created and destroyed on one and the same thread. +// All supported public methods must also be called on the same thread. +// A thread checker will RTC_DCHECK if any supported method is called on an +// invalid thread. +// +// Recorded audio will be delivered on a real-time internal I/O thread in the +// audio unit. The audio unit will also ask for audio data to play out on this +// same thread. +class AudioDeviceIOS : public AudioDeviceGeneric, + public AudioSessionObserver, + public VoiceProcessingAudioUnitObserver { + public: + explicit AudioDeviceIOS(bool bypass_voice_processing); + ~AudioDeviceIOS() override; + + void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override; + + InitStatus Init() override; + int32_t Terminate() override; + bool Initialized() const override; + + int32_t InitPlayout() override; + bool PlayoutIsInitialized() const override; + + int32_t InitRecording() override; + bool RecordingIsInitialized() const override; + + int32_t StartPlayout() override; + int32_t StopPlayout() override; + bool Playing() const override; + + int32_t StartRecording() override; + int32_t StopRecording() override; + bool Recording() const override; + + // These methods returns hard-coded delay values and not dynamic delay + // estimates. The reason is that iOS supports a built-in AEC and the WebRTC + // AEC will always be disabled in the Libjingle layer to avoid running two + // AEC implementations at the same time. And, it saves resources to avoid + // updating these delay values continuously. + // TODO(henrika): it would be possible to mark these two methods as not + // implemented since they are only called for A/V-sync purposes today and + // A/V-sync is not supported on iOS. However, we avoid adding error messages + // the log by using these dummy implementations instead. + int32_t PlayoutDelay(uint16_t& delayMS) const override; + + // No implementation for playout underrun on iOS. We override it to avoid a + // periodic log that it isn't available from the base class. + int32_t GetPlayoutUnderrunCount() const override { return -1; } + + // Native audio parameters stored during construction. + // These methods are unique for the iOS implementation. + int GetPlayoutAudioParameters(AudioParameters* params) const override; + int GetRecordAudioParameters(AudioParameters* params) const override; + + // These methods are currently not fully implemented on iOS: + + // See audio_device_not_implemented.cc for trivial implementations. + int32_t ActiveAudioLayer( + AudioDeviceModule::AudioLayer& audioLayer) const override; + int32_t PlayoutIsAvailable(bool& available) override; + int32_t RecordingIsAvailable(bool& available) override; + int16_t PlayoutDevices() override; + int16_t RecordingDevices() override; + int32_t PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) override; + int32_t RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) override; + int32_t SetPlayoutDevice(uint16_t index) override; + int32_t SetPlayoutDevice( + AudioDeviceModule::WindowsDeviceType device) override; + int32_t SetRecordingDevice(uint16_t index) override; + int32_t SetRecordingDevice( + AudioDeviceModule::WindowsDeviceType device) override; + int32_t InitSpeaker() override; + bool SpeakerIsInitialized() const override; + int32_t InitMicrophone() override; + bool MicrophoneIsInitialized() const override; + int32_t SpeakerVolumeIsAvailable(bool& available) override; + int32_t SetSpeakerVolume(uint32_t volume) override; + int32_t SpeakerVolume(uint32_t& volume) const override; + int32_t MaxSpeakerVolume(uint32_t& maxVolume) const override; + int32_t MinSpeakerVolume(uint32_t& minVolume) const override; + int32_t MicrophoneVolumeIsAvailable(bool& available) override; + int32_t SetMicrophoneVolume(uint32_t volume) override; + int32_t MicrophoneVolume(uint32_t& volume) const override; + int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const override; + int32_t MinMicrophoneVolume(uint32_t& minVolume) const override; + int32_t MicrophoneMuteIsAvailable(bool& available) override; + int32_t SetMicrophoneMute(bool enable) override; + int32_t MicrophoneMute(bool& enabled) const override; + int32_t SpeakerMuteIsAvailable(bool& available) override; + int32_t SetSpeakerMute(bool enable) override; + int32_t SpeakerMute(bool& enabled) const override; + int32_t StereoPlayoutIsAvailable(bool& available) override; + int32_t SetStereoPlayout(bool enable) override; + int32_t StereoPlayout(bool& enabled) const override; + int32_t StereoRecordingIsAvailable(bool& available) override; + int32_t SetStereoRecording(bool enable) override; + int32_t StereoRecording(bool& enabled) const override; + + // AudioSessionObserver methods. May be called from any thread. + void OnInterruptionBegin() override; + void OnInterruptionEnd() override; + void OnValidRouteChange() override; + void OnCanPlayOrRecordChange(bool can_play_or_record) override; + void OnChangedOutputVolume() override; + + // VoiceProcessingAudioUnitObserver methods. + OSStatus OnDeliverRecordedData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) override; + OSStatus OnGetPlayoutData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) override; + + bool IsInterrupted(); + + private: + // Called by the relevant AudioSessionObserver methods on `thread_`. + void HandleInterruptionBegin(); + void HandleInterruptionEnd(); + void HandleValidRouteChange(); + void HandleCanPlayOrRecordChange(bool can_play_or_record); + void HandleSampleRateChange(); + void HandlePlayoutGlitchDetected(); + void HandleOutputVolumeChange(); + + // Uses current `playout_parameters_` and `record_parameters_` to inform the + // audio device buffer (ADB) about our internal audio parameters. + void UpdateAudioDeviceBuffer(); + + // Since the preferred audio parameters are only hints to the OS, the actual + // values may be different once the AVAudioSession has been activated. + // This method asks for the current hardware parameters and takes actions + // if they should differ from what we have asked for initially. It also + // defines `playout_parameters_` and `record_parameters_`. + void SetupAudioBuffersForActiveAudioSession(); + + // Creates the audio unit. + bool CreateAudioUnit(); + + // Updates the audio unit state based on current state. + void UpdateAudioUnit(bool can_play_or_record); + + // Configures the audio session for WebRTC. + bool ConfigureAudioSession(); + + // Like above, but requires caller to already hold session lock. + bool ConfigureAudioSessionLocked(); + + // Unconfigures the audio session. + void UnconfigureAudioSession(); + + // Activates our audio session, creates and initializes the voice-processing + // audio unit and verifies that we got the preferred native audio parameters. + bool InitPlayOrRecord(); + + // Closes and deletes the voice-processing I/O unit. + void ShutdownPlayOrRecord(); + + // Resets thread-checkers before a call is restarted. + void PrepareForNewStart(); + + // Determines whether voice processing should be enabled or disabled. + const bool bypass_voice_processing_; + + // Native I/O audio thread checker. + SequenceChecker io_thread_checker_; + + // Thread that this object is created on. + rtc::Thread* thread_; + + // Raw pointer handle provided to us in AttachAudioBuffer(). Owned by the + // AudioDeviceModuleImpl class and called by AudioDeviceModule::Create(). + // The AudioDeviceBuffer is a member of the AudioDeviceModuleImpl instance + // and therefore outlives this object. + AudioDeviceBuffer* audio_device_buffer_; + + // Contains audio parameters (sample rate, #channels, buffer size etc.) for + // the playout and recording sides. These structure is set in two steps: + // first, native sample rate and #channels are defined in Init(). Next, the + // audio session is activated and we verify that the preferred parameters + // were granted by the OS. At this stage it is also possible to add a third + // component to the parameters; the native I/O buffer duration. + // A RTC_CHECK will be hit if we for some reason fail to open an audio session + // using the specified parameters. + AudioParameters playout_parameters_; + AudioParameters record_parameters_; + + // The AudioUnit used to play and record audio. + std::unique_ptr<VoiceProcessingAudioUnit> audio_unit_; + + // FineAudioBuffer takes an AudioDeviceBuffer which delivers audio data + // in chunks of 10ms. It then allows for this data to be pulled in + // a finer or coarser granularity. I.e. interacting with this class instead + // of directly with the AudioDeviceBuffer one can ask for any number of + // audio data samples. Is also supports a similar scheme for the recording + // side. + // Example: native buffer size can be 128 audio frames at 16kHz sample rate. + // WebRTC will provide 480 audio frames per 10ms but iOS asks for 128 + // in each callback (one every 8ms). This class can then ask for 128 and the + // FineAudioBuffer will ask WebRTC for new data only when needed and also + // cache non-utilized audio between callbacks. On the recording side, iOS + // can provide audio data frames of size 128 and these are accumulated until + // enough data to supply one 10ms call exists. This 10ms chunk is then sent + // to WebRTC and the remaining part is stored. + std::unique_ptr<FineAudioBuffer> fine_audio_buffer_; + + // Temporary storage for recorded data. AudioUnitRender() renders into this + // array as soon as a frame of the desired buffer size has been recorded. + // On real iOS devices, the size will be fixed and set once. For iOS + // simulators, the size can vary from callback to callback and the size + // will be changed dynamically to account for this behavior. + rtc::BufferT<int16_t> record_audio_buffer_; + + // Set to 1 when recording is active and 0 otherwise. + std::atomic<int> recording_; + + // Set to 1 when playout is active and 0 otherwise. + std::atomic<int> playing_; + + // Set to true after successful call to Init(), false otherwise. + bool initialized_ RTC_GUARDED_BY(thread_); + + // Set to true after successful call to InitRecording() or InitPlayout(), + // false otherwise. + bool audio_is_initialized_; + + // Set to true if audio session is interrupted, false otherwise. + bool is_interrupted_; + + // Audio interruption observer instance. + RTCNativeAudioSessionDelegateAdapter* audio_session_observer_ + RTC_GUARDED_BY(thread_); + + // Set to true if we've activated the audio session. + bool has_configured_session_ RTC_GUARDED_BY(thread_); + + // Counts number of detected audio glitches on the playout side. + int64_t num_detected_playout_glitches_ RTC_GUARDED_BY(thread_); + int64_t last_playout_time_ RTC_GUARDED_BY(io_thread_checker_); + + // Counts number of playout callbacks per call. + // The value is updated on the native I/O thread and later read on the + // creating `thread_` but at this stage no audio is active. + // Hence, it is a "thread safe" design and no lock is needed. + int64_t num_playout_callbacks_; + + // Contains the time for when the last output volume change was detected. + int64_t last_output_volume_change_time_ RTC_GUARDED_BY(thread_); + + // Avoids running pending task after `this` is Terminated. + rtc::scoped_refptr<PendingTaskSafetyFlag> safety_ = + PendingTaskSafetyFlag::Create(); +}; +} // namespace ios_adm +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_DEVICE_IOS_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_ios.mm b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_ios.mm new file mode 100644 index 0000000000..dd2c11bdd2 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_ios.mm @@ -0,0 +1,1127 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> +#import <Foundation/Foundation.h> + +#include "audio_device_ios.h" + +#include <cmath> + +#include "api/array_view.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "helpers.h" +#include "modules/audio_device/fine_audio_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/time_utils.h" +#include "system_wrappers/include/field_trial.h" +#include "system_wrappers/include/metrics.h" + +#import "base/RTCLogging.h" +#import "components/audio/RTCAudioSession+Private.h" +#import "components/audio/RTCAudioSession.h" +#import "components/audio/RTCAudioSessionConfiguration.h" +#import "components/audio/RTCNativeAudioSessionDelegateAdapter.h" + +namespace webrtc { +namespace ios_adm { + +#define LOGI() RTC_LOG(LS_INFO) << "AudioDeviceIOS::" + +#define LOG_AND_RETURN_IF_ERROR(error, message) \ + do { \ + OSStatus err = error; \ + if (err) { \ + RTC_LOG(LS_ERROR) << message << ": " << err; \ + return false; \ + } \ + } while (0) + +#define LOG_IF_ERROR(error, message) \ + do { \ + OSStatus err = error; \ + if (err) { \ + RTC_LOG(LS_ERROR) << message << ": " << err; \ + } \ + } while (0) + +// Hardcoded delay estimates based on real measurements. +// TODO(henrika): these value is not used in combination with built-in AEC. +// Can most likely be removed. +const UInt16 kFixedPlayoutDelayEstimate = 30; +const UInt16 kFixedRecordDelayEstimate = 30; + +using ios::CheckAndLogError; + +#if !defined(NDEBUG) +// Returns true when the code runs on a device simulator. +static bool DeviceIsSimulator() { + return ios::GetDeviceName() == "x86_64"; +} + +// Helper method that logs essential device information strings. +static void LogDeviceInfo() { + RTC_LOG(LS_INFO) << "LogDeviceInfo"; + @autoreleasepool { + RTC_LOG(LS_INFO) << " system name: " << ios::GetSystemName(); + RTC_LOG(LS_INFO) << " system version: " << ios::GetSystemVersionAsString(); + RTC_LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); + RTC_LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); + RTC_LOG(LS_INFO) << " process name: " << ios::GetProcessName(); + RTC_LOG(LS_INFO) << " process ID: " << ios::GetProcessID(); + RTC_LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString(); + RTC_LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount(); + RTC_LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled(); +#if TARGET_IPHONE_SIMULATOR + RTC_LOG(LS_INFO) << " TARGET_IPHONE_SIMULATOR is defined"; +#endif + RTC_LOG(LS_INFO) << " DeviceIsSimulator: " << DeviceIsSimulator(); + } +} +#endif // !defined(NDEBUG) + +AudioDeviceIOS::AudioDeviceIOS(bool bypass_voice_processing) + : bypass_voice_processing_(bypass_voice_processing), + audio_device_buffer_(nullptr), + audio_unit_(nullptr), + recording_(0), + playing_(0), + initialized_(false), + audio_is_initialized_(false), + is_interrupted_(false), + has_configured_session_(false), + num_detected_playout_glitches_(0), + last_playout_time_(0), + num_playout_callbacks_(0), + last_output_volume_change_time_(0) { + LOGI() << "ctor" << ios::GetCurrentThreadDescription() + << ",bypass_voice_processing=" << bypass_voice_processing_; + io_thread_checker_.Detach(); + thread_ = rtc::Thread::Current(); + + audio_session_observer_ = [[RTCNativeAudioSessionDelegateAdapter alloc] initWithObserver:this]; +} + +AudioDeviceIOS::~AudioDeviceIOS() { + RTC_DCHECK_RUN_ON(thread_); + LOGI() << "~dtor" << ios::GetCurrentThreadDescription(); + safety_->SetNotAlive(); + Terminate(); + audio_session_observer_ = nil; +} + +void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { + LOGI() << "AttachAudioBuffer"; + RTC_DCHECK(audioBuffer); + RTC_DCHECK_RUN_ON(thread_); + audio_device_buffer_ = audioBuffer; +} + +AudioDeviceGeneric::InitStatus AudioDeviceIOS::Init() { + LOGI() << "Init"; + io_thread_checker_.Detach(); + + RTC_DCHECK_RUN_ON(thread_); + if (initialized_) { + return InitStatus::OK; + } +#if !defined(NDEBUG) + LogDeviceInfo(); +#endif + // Store the preferred sample rate and preferred number of channels already + // here. They have not been set and confirmed yet since configureForWebRTC + // is not called until audio is about to start. However, it makes sense to + // store the parameters now and then verify at a later stage. + RTC_OBJC_TYPE(RTCAudioSessionConfiguration)* config = + [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) webRTCConfiguration]; + playout_parameters_.reset(config.sampleRate, config.outputNumberOfChannels); + record_parameters_.reset(config.sampleRate, config.inputNumberOfChannels); + // Ensure that the audio device buffer (ADB) knows about the internal audio + // parameters. Note that, even if we are unable to get a mono audio session, + // we will always tell the I/O audio unit to do a channel format conversion + // to guarantee mono on the "input side" of the audio unit. + UpdateAudioDeviceBuffer(); + initialized_ = true; + return InitStatus::OK; +} + +int32_t AudioDeviceIOS::Terminate() { + LOGI() << "Terminate"; + RTC_DCHECK_RUN_ON(thread_); + if (!initialized_) { + return 0; + } + StopPlayout(); + StopRecording(); + initialized_ = false; + return 0; +} + +bool AudioDeviceIOS::Initialized() const { + RTC_DCHECK_RUN_ON(thread_); + return initialized_; +} + +int32_t AudioDeviceIOS::InitPlayout() { + LOGI() << "InitPlayout"; + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(initialized_); + RTC_DCHECK(!audio_is_initialized_); + RTC_DCHECK(!playing_.load()); + if (!audio_is_initialized_) { + if (!InitPlayOrRecord()) { + RTC_LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitPlayout!"; + return -1; + } + } + audio_is_initialized_ = true; + return 0; +} + +bool AudioDeviceIOS::PlayoutIsInitialized() const { + RTC_DCHECK_RUN_ON(thread_); + return audio_is_initialized_; +} + +bool AudioDeviceIOS::RecordingIsInitialized() const { + RTC_DCHECK_RUN_ON(thread_); + return audio_is_initialized_; +} + +int32_t AudioDeviceIOS::InitRecording() { + LOGI() << "InitRecording"; + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(initialized_); + RTC_DCHECK(!audio_is_initialized_); + RTC_DCHECK(!recording_.load()); + if (!audio_is_initialized_) { + if (!InitPlayOrRecord()) { + RTC_LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitRecording!"; + return -1; + } + } + audio_is_initialized_ = true; + return 0; +} + +int32_t AudioDeviceIOS::StartPlayout() { + LOGI() << "StartPlayout"; + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(audio_is_initialized_); + RTC_DCHECK(!playing_.load()); + RTC_DCHECK(audio_unit_); + if (fine_audio_buffer_) { + fine_audio_buffer_->ResetPlayout(); + } + if (!recording_.load() && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"StartPlayout failed to start audio unit, reason %d", result); + return -1; + } + RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; + } + playing_.store(1, std::memory_order_release); + num_playout_callbacks_ = 0; + num_detected_playout_glitches_ = 0; + return 0; +} + +int32_t AudioDeviceIOS::StopPlayout() { + LOGI() << "StopPlayout"; + RTC_DCHECK_RUN_ON(thread_); + if (!audio_is_initialized_ || !playing_.load()) { + return 0; + } + if (!recording_.load()) { + ShutdownPlayOrRecord(); + audio_is_initialized_ = false; + } + playing_.store(0, std::memory_order_release); + + // Derive average number of calls to OnGetPlayoutData() between detected + // audio glitches and add the result to a histogram. + int average_number_of_playout_callbacks_between_glitches = 100000; + RTC_DCHECK_GE(num_playout_callbacks_, num_detected_playout_glitches_); + if (num_detected_playout_glitches_ > 0) { + average_number_of_playout_callbacks_between_glitches = + num_playout_callbacks_ / num_detected_playout_glitches_; + } + RTC_HISTOGRAM_COUNTS_100000("WebRTC.Audio.AveragePlayoutCallbacksBetweenGlitches", + average_number_of_playout_callbacks_between_glitches); + RTCLog(@"Average number of playout callbacks between glitches: %d", + average_number_of_playout_callbacks_between_glitches); + return 0; +} + +bool AudioDeviceIOS::Playing() const { + return playing_.load(); +} + +int32_t AudioDeviceIOS::StartRecording() { + LOGI() << "StartRecording"; + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(audio_is_initialized_); + RTC_DCHECK(!recording_.load()); + RTC_DCHECK(audio_unit_); + if (fine_audio_buffer_) { + fine_audio_buffer_->ResetRecord(); + } + if (!playing_.load() && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"StartRecording failed to start audio unit, reason %d", result); + return -1; + } + RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; + } + recording_.store(1, std::memory_order_release); + return 0; +} + +int32_t AudioDeviceIOS::StopRecording() { + LOGI() << "StopRecording"; + RTC_DCHECK_RUN_ON(thread_); + if (!audio_is_initialized_ || !recording_.load()) { + return 0; + } + if (!playing_.load()) { + ShutdownPlayOrRecord(); + audio_is_initialized_ = false; + } + recording_.store(0, std::memory_order_release); + return 0; +} + +bool AudioDeviceIOS::Recording() const { + return recording_.load(); +} + +int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { + delayMS = kFixedPlayoutDelayEstimate; + return 0; +} + +int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const { + LOGI() << "GetPlayoutAudioParameters"; + RTC_DCHECK(playout_parameters_.is_valid()); + RTC_DCHECK_RUN_ON(thread_); + *params = playout_parameters_; + return 0; +} + +int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { + LOGI() << "GetRecordAudioParameters"; + RTC_DCHECK(record_parameters_.is_valid()); + RTC_DCHECK_RUN_ON(thread_); + *params = record_parameters_; + return 0; +} + +void AudioDeviceIOS::OnInterruptionBegin() { + RTC_DCHECK(thread_); + LOGI() << "OnInterruptionBegin"; + thread_->PostTask(SafeTask(safety_, [this] { HandleInterruptionBegin(); })); +} + +void AudioDeviceIOS::OnInterruptionEnd() { + RTC_DCHECK(thread_); + LOGI() << "OnInterruptionEnd"; + thread_->PostTask(SafeTask(safety_, [this] { HandleInterruptionEnd(); })); +} + +void AudioDeviceIOS::OnValidRouteChange() { + RTC_DCHECK(thread_); + thread_->PostTask(SafeTask(safety_, [this] { HandleValidRouteChange(); })); +} + +void AudioDeviceIOS::OnCanPlayOrRecordChange(bool can_play_or_record) { + RTC_DCHECK(thread_); + thread_->PostTask(SafeTask( + safety_, [this, can_play_or_record] { HandleCanPlayOrRecordChange(can_play_or_record); })); +} + +void AudioDeviceIOS::OnChangedOutputVolume() { + RTC_DCHECK(thread_); + thread_->PostTask(SafeTask(safety_, [this] { HandleOutputVolumeChange(); })); +} + +OSStatus AudioDeviceIOS::OnDeliverRecordedData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* /* io_data */) { + RTC_DCHECK_RUN_ON(&io_thread_checker_); + OSStatus result = noErr; + // Simply return if recording is not enabled. + if (!recording_.load(std::memory_order_acquire)) return result; + + // Set the size of our own audio buffer and clear it first to avoid copying + // in combination with potential reallocations. + // On real iOS devices, the size will only be set once (at first callback). + record_audio_buffer_.Clear(); + record_audio_buffer_.SetSize(num_frames); + + // Allocate AudioBuffers to be used as storage for the received audio. + // The AudioBufferList structure works as a placeholder for the + // AudioBuffer structure, which holds a pointer to the actual data buffer + // in `record_audio_buffer_`. Recorded audio will be rendered into this memory + // at each input callback when calling AudioUnitRender(). + AudioBufferList audio_buffer_list; + audio_buffer_list.mNumberBuffers = 1; + AudioBuffer* audio_buffer = &audio_buffer_list.mBuffers[0]; + audio_buffer->mNumberChannels = record_parameters_.channels(); + audio_buffer->mDataByteSize = + record_audio_buffer_.size() * VoiceProcessingAudioUnit::kBytesPerSample; + audio_buffer->mData = reinterpret_cast<int8_t*>(record_audio_buffer_.data()); + + // Obtain the recorded audio samples by initiating a rendering cycle. + // Since it happens on the input bus, the `io_data` parameter is a reference + // to the preallocated audio buffer list that the audio unit renders into. + // We can make the audio unit provide a buffer instead in io_data, but we + // currently just use our own. + // TODO(henrika): should error handling be improved? + result = audio_unit_->Render(flags, time_stamp, bus_number, num_frames, &audio_buffer_list); + if (result != noErr) { + RTCLogError(@"Failed to render audio."); + return result; + } + + // Get a pointer to the recorded audio and send it to the WebRTC ADB. + // Use the FineAudioBuffer instance to convert between native buffer size + // and the 10ms buffer size used by WebRTC. + fine_audio_buffer_->DeliverRecordedData(record_audio_buffer_, kFixedRecordDelayEstimate); + return noErr; +} + +OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + RTC_DCHECK_RUN_ON(&io_thread_checker_); + // Verify 16-bit, noninterleaved mono PCM signal format. + RTC_DCHECK_EQ(1, io_data->mNumberBuffers); + AudioBuffer* audio_buffer = &io_data->mBuffers[0]; + RTC_DCHECK_EQ(1, audio_buffer->mNumberChannels); + + // Produce silence and give audio unit a hint about it if playout is not + // activated. + if (!playing_.load(std::memory_order_acquire)) { + const size_t size_in_bytes = audio_buffer->mDataByteSize; + RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample, num_frames); + *flags |= kAudioUnitRenderAction_OutputIsSilence; + memset(static_cast<int8_t*>(audio_buffer->mData), 0, size_in_bytes); + return noErr; + } + + // Measure time since last call to OnGetPlayoutData() and see if it is larger + // than a well defined threshold which depends on the current IO buffer size. + // If so, we have an indication of a glitch in the output audio since the + // core audio layer will most likely run dry in this state. + ++num_playout_callbacks_; + const int64_t now_time = rtc::TimeMillis(); + if (time_stamp->mSampleTime != num_frames) { + const int64_t delta_time = now_time - last_playout_time_; + const int glitch_threshold = 1.6 * playout_parameters_.GetBufferSizeInMilliseconds(); + if (delta_time > glitch_threshold) { + RTCLogWarning(@"Possible playout audio glitch detected.\n" + " Time since last OnGetPlayoutData was %lld ms.\n", + delta_time); + // Exclude extreme delta values since they do most likely not correspond + // to a real glitch. Instead, the most probable cause is that a headset + // has been plugged in or out. There are more direct ways to detect + // audio device changes (see HandleValidRouteChange()) but experiments + // show that using it leads to more complex implementations. + // TODO(henrika): more tests might be needed to come up with an even + // better upper limit. + if (glitch_threshold < 120 && delta_time > 120) { + RTCLog(@"Glitch warning is ignored. Probably caused by device switch."); + } else { + thread_->PostTask(SafeTask(safety_, [this] { HandlePlayoutGlitchDetected(); })); + } + } + } + last_playout_time_ = now_time; + + // Read decoded 16-bit PCM samples from WebRTC (using a size that matches + // the native I/O audio unit) and copy the result to the audio buffer in the + // `io_data` destination. + fine_audio_buffer_->GetPlayoutData( + rtc::ArrayView<int16_t>(static_cast<int16_t*>(audio_buffer->mData), num_frames), + kFixedPlayoutDelayEstimate); + return noErr; +} + +void AudioDeviceIOS::HandleInterruptionBegin() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Interruption begin. IsInterrupted changed from %d to 1.", is_interrupted_); + if (audio_unit_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) { + RTCLog(@"Stopping the audio unit due to interruption begin."); + if (!audio_unit_->Stop()) { + RTCLogError(@"Failed to stop the audio unit for interruption begin."); + } + PrepareForNewStart(); + } + is_interrupted_ = true; +} + +void AudioDeviceIOS::HandleInterruptionEnd() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Interruption ended. IsInterrupted changed from %d to 0. " + "Updating audio unit state.", + is_interrupted_); + is_interrupted_ = false; + if (!audio_unit_) return; + if (webrtc::field_trial::IsEnabled("WebRTC-Audio-iOS-Holding")) { + // Work around an issue where audio does not restart properly after an interruption + // by restarting the audio unit when the interruption ends. + if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) { + audio_unit_->Stop(); + PrepareForNewStart(); + } + if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { + audio_unit_->Uninitialize(); + } + // Allocate new buffers given the potentially new stream format. + SetupAudioBuffersForActiveAudioSession(); + } + UpdateAudioUnit([RTC_OBJC_TYPE(RTCAudioSession) sharedInstance].canPlayOrRecord); +} + +void AudioDeviceIOS::HandleValidRouteChange() { + RTC_DCHECK_RUN_ON(thread_); + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + RTCLog(@"%@", session); + HandleSampleRateChange(); +} + +void AudioDeviceIOS::HandleCanPlayOrRecordChange(bool can_play_or_record) { + RTCLog(@"Handling CanPlayOrRecord change to: %d", can_play_or_record); + UpdateAudioUnit(can_play_or_record); +} + +void AudioDeviceIOS::HandleSampleRateChange() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Handling sample rate change."); + + // Don't do anything if we're interrupted. + if (is_interrupted_) { + RTCLog(@"Ignoring sample rate change due to interruption."); + return; + } + + // If we don't have an audio unit yet, or the audio unit is uninitialized, + // there is no work to do. + if (!audio_unit_ || audio_unit_->GetState() < VoiceProcessingAudioUnit::kInitialized) { + return; + } + + // The audio unit is already initialized or started. + // Check to see if the sample rate or buffer size has changed. + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + const double new_sample_rate = session.sampleRate; + const NSTimeInterval session_buffer_duration = session.IOBufferDuration; + const size_t new_frames_per_buffer = + static_cast<size_t>(new_sample_rate * session_buffer_duration + .5); + const double current_sample_rate = playout_parameters_.sample_rate(); + const size_t current_frames_per_buffer = playout_parameters_.frames_per_buffer(); + RTCLog(@"Handling playout sample rate change:\n" + " Session sample rate: %f frames_per_buffer: %lu\n" + " ADM sample rate: %f frames_per_buffer: %lu", + new_sample_rate, + (unsigned long)new_frames_per_buffer, + current_sample_rate, + (unsigned long)current_frames_per_buffer); + + // Sample rate and buffer size are the same, no work to do. + if (std::abs(current_sample_rate - new_sample_rate) <= DBL_EPSILON && + current_frames_per_buffer == new_frames_per_buffer) { + RTCLog(@"Ignoring sample rate change since audio parameters are intact."); + return; + } + + // Extra sanity check to ensure that the new sample rate is valid. + if (new_sample_rate <= 0.0) { + RTCLogError(@"Sample rate is invalid: %f", new_sample_rate); + return; + } + + // We need to adjust our format and buffer sizes. + // The stream format is about to be changed and it requires that we first + // stop and uninitialize the audio unit to deallocate its resources. + RTCLog(@"Stopping and uninitializing audio unit to adjust buffers."); + bool restart_audio_unit = false; + if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) { + audio_unit_->Stop(); + restart_audio_unit = true; + PrepareForNewStart(); + } + if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { + audio_unit_->Uninitialize(); + } + + // Allocate new buffers given the new stream format. + SetupAudioBuffersForActiveAudioSession(); + + // Initialize the audio unit again with the new sample rate. + if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) { + RTCLogError(@"Failed to initialize the audio unit with sample rate: %d", + playout_parameters_.sample_rate()); + return; + } + + // Restart the audio unit if it was already running. + if (restart_audio_unit) { + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"Failed to start audio unit with sample rate: %d, reason %d", + playout_parameters_.sample_rate(), + result); + return; + } + } + RTCLog(@"Successfully handled sample rate change."); +} + +void AudioDeviceIOS::HandlePlayoutGlitchDetected() { + RTC_DCHECK_RUN_ON(thread_); + // Don't update metrics if we're interrupted since a "glitch" is expected + // in this state. + if (is_interrupted_) { + RTCLog(@"Ignoring audio glitch due to interruption."); + return; + } + // Avoid doing glitch detection for two seconds after a volume change + // has been detected to reduce the risk of false alarm. + if (last_output_volume_change_time_ > 0 && + rtc::TimeSince(last_output_volume_change_time_) < 2000) { + RTCLog(@"Ignoring audio glitch due to recent output volume change."); + return; + } + num_detected_playout_glitches_++; + RTCLog(@"Number of detected playout glitches: %lld", num_detected_playout_glitches_); + + int64_t glitch_count = num_detected_playout_glitches_; + dispatch_async(dispatch_get_main_queue(), ^{ + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyDidDetectPlayoutGlitch:glitch_count]; + }); +} + +void AudioDeviceIOS::HandleOutputVolumeChange() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Output volume change detected."); + // Store time of this detection so it can be used to defer detection of + // glitches too close in time to this event. + last_output_volume_change_time_ = rtc::TimeMillis(); +} + +void AudioDeviceIOS::UpdateAudioDeviceBuffer() { + LOGI() << "UpdateAudioDevicebuffer"; + // AttachAudioBuffer() is called at construction by the main class but check + // just in case. + RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; + RTC_DCHECK_GT(playout_parameters_.sample_rate(), 0); + RTC_DCHECK_GT(record_parameters_.sample_rate(), 0); + RTC_DCHECK_EQ(playout_parameters_.channels(), 1); + RTC_DCHECK_EQ(record_parameters_.channels(), 1); + // Inform the audio device buffer (ADB) about the new audio format. + audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); + audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); + audio_device_buffer_->SetRecordingSampleRate(record_parameters_.sample_rate()); + audio_device_buffer_->SetRecordingChannels(record_parameters_.channels()); +} + +void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { + LOGI() << "SetupAudioBuffersForActiveAudioSession"; + // Verify the current values once the audio session has been activated. + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + double sample_rate = session.sampleRate; + NSTimeInterval io_buffer_duration = session.IOBufferDuration; + RTCLog(@"%@", session); + + // Log a warning message for the case when we are unable to set the preferred + // hardware sample rate but continue and use the non-ideal sample rate after + // reinitializing the audio parameters. Most BT headsets only support 8kHz or + // 16kHz. + RTC_OBJC_TYPE(RTCAudioSessionConfiguration)* webRTCConfig = + [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) webRTCConfiguration]; + if (sample_rate != webRTCConfig.sampleRate) { + RTC_LOG(LS_WARNING) << "Unable to set the preferred sample rate"; + } + + // Crash reports indicates that it can happen in rare cases that the reported + // sample rate is less than or equal to zero. If that happens and if a valid + // sample rate has already been set during initialization, the best guess we + // can do is to reuse the current sample rate. + if (sample_rate <= DBL_EPSILON && playout_parameters_.sample_rate() > 0) { + RTCLogError(@"Reported rate is invalid: %f. " + "Using %d as sample rate instead.", + sample_rate, playout_parameters_.sample_rate()); + sample_rate = playout_parameters_.sample_rate(); + } + + // At this stage, we also know the exact IO buffer duration and can add + // that info to the existing audio parameters where it is converted into + // number of audio frames. + // Example: IO buffer size = 0.008 seconds <=> 128 audio frames at 16kHz. + // Hence, 128 is the size we expect to see in upcoming render callbacks. + playout_parameters_.reset(sample_rate, playout_parameters_.channels(), io_buffer_duration); + RTC_DCHECK(playout_parameters_.is_complete()); + record_parameters_.reset(sample_rate, record_parameters_.channels(), io_buffer_duration); + RTC_DCHECK(record_parameters_.is_complete()); + RTC_LOG(LS_INFO) << " frames per I/O buffer: " << playout_parameters_.frames_per_buffer(); + RTC_LOG(LS_INFO) << " bytes per I/O buffer: " << playout_parameters_.GetBytesPerBuffer(); + RTC_DCHECK_EQ(playout_parameters_.GetBytesPerBuffer(), record_parameters_.GetBytesPerBuffer()); + + // Update the ADB parameters since the sample rate might have changed. + UpdateAudioDeviceBuffer(); + + // Create a modified audio buffer class which allows us to ask for, + // or deliver, any number of samples (and not only multiple of 10ms) to match + // the native audio unit buffer size. + RTC_DCHECK(audio_device_buffer_); + fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_)); +} + +bool AudioDeviceIOS::CreateAudioUnit() { + RTC_DCHECK(!audio_unit_); + + audio_unit_.reset(new VoiceProcessingAudioUnit(bypass_voice_processing_, this)); + if (!audio_unit_->Init()) { + audio_unit_.reset(); + return false; + } + + return true; +} + +void AudioDeviceIOS::UpdateAudioUnit(bool can_play_or_record) { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Updating audio unit state. CanPlayOrRecord=%d IsInterrupted=%d", + can_play_or_record, + is_interrupted_); + + if (is_interrupted_) { + RTCLog(@"Ignoring audio unit update due to interruption."); + return; + } + + // If we're not initialized we don't need to do anything. Audio unit will + // be initialized on initialization. + if (!audio_is_initialized_) return; + + // If we're initialized, we must have an audio unit. + RTC_DCHECK(audio_unit_); + + bool should_initialize_audio_unit = false; + bool should_uninitialize_audio_unit = false; + bool should_start_audio_unit = false; + bool should_stop_audio_unit = false; + + switch (audio_unit_->GetState()) { + case VoiceProcessingAudioUnit::kInitRequired: + RTCLog(@"VPAU state: InitRequired"); + RTC_DCHECK_NOTREACHED(); + break; + case VoiceProcessingAudioUnit::kUninitialized: + RTCLog(@"VPAU state: Uninitialized"); + should_initialize_audio_unit = can_play_or_record; + should_start_audio_unit = + should_initialize_audio_unit && (playing_.load() || recording_.load()); + break; + case VoiceProcessingAudioUnit::kInitialized: + RTCLog(@"VPAU state: Initialized"); + should_start_audio_unit = can_play_or_record && (playing_.load() || recording_.load()); + should_uninitialize_audio_unit = !can_play_or_record; + break; + case VoiceProcessingAudioUnit::kStarted: + RTCLog(@"VPAU state: Started"); + RTC_DCHECK(playing_.load() || recording_.load()); + should_stop_audio_unit = !can_play_or_record; + should_uninitialize_audio_unit = should_stop_audio_unit; + break; + } + + if (should_initialize_audio_unit) { + RTCLog(@"Initializing audio unit for UpdateAudioUnit"); + ConfigureAudioSession(); + SetupAudioBuffersForActiveAudioSession(); + if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) { + RTCLogError(@"Failed to initialize audio unit."); + return; + } + } + + if (should_start_audio_unit) { + RTCLog(@"Starting audio unit for UpdateAudioUnit"); + // Log session settings before trying to start audio streaming. + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + RTCLog(@"%@", session); + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"Failed to start audio unit, reason %d", result); + return; + } + } + + if (should_stop_audio_unit) { + RTCLog(@"Stopping audio unit for UpdateAudioUnit"); + if (!audio_unit_->Stop()) { + RTCLogError(@"Failed to stop audio unit."); + PrepareForNewStart(); + return; + } + PrepareForNewStart(); + } + + if (should_uninitialize_audio_unit) { + RTCLog(@"Uninitializing audio unit for UpdateAudioUnit"); + audio_unit_->Uninitialize(); + UnconfigureAudioSession(); + } +} + +bool AudioDeviceIOS::ConfigureAudioSession() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Configuring audio session."); + if (has_configured_session_) { + RTCLogWarning(@"Audio session already configured."); + return false; + } + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session lockForConfiguration]; + bool success = [session configureWebRTCSession:nil]; + [session unlockForConfiguration]; + if (success) { + has_configured_session_ = true; + RTCLog(@"Configured audio session."); + } else { + RTCLog(@"Failed to configure audio session."); + } + return success; +} + +bool AudioDeviceIOS::ConfigureAudioSessionLocked() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Configuring audio session."); + if (has_configured_session_) { + RTCLogWarning(@"Audio session already configured."); + return false; + } + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + bool success = [session configureWebRTCSession:nil]; + if (success) { + has_configured_session_ = true; + RTCLog(@"Configured audio session."); + } else { + RTCLog(@"Failed to configure audio session."); + } + return success; +} + +void AudioDeviceIOS::UnconfigureAudioSession() { + RTC_DCHECK_RUN_ON(thread_); + RTCLog(@"Unconfiguring audio session."); + if (!has_configured_session_) { + RTCLogWarning(@"Audio session already unconfigured."); + return; + } + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session lockForConfiguration]; + [session unconfigureWebRTCSession:nil]; + [session endWebRTCSession:nil]; + [session unlockForConfiguration]; + has_configured_session_ = false; + RTCLog(@"Unconfigured audio session."); +} + +bool AudioDeviceIOS::InitPlayOrRecord() { + LOGI() << "InitPlayOrRecord"; + RTC_DCHECK_RUN_ON(thread_); + + // There should be no audio unit at this point. + if (!CreateAudioUnit()) { + return false; + } + + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + // Subscribe to audio session events. + [session pushDelegate:audio_session_observer_]; + is_interrupted_ = session.isInterrupted ? true : false; + + // Lock the session to make configuration changes. + [session lockForConfiguration]; + NSError* error = nil; + if (![session beginWebRTCSession:&error]) { + [session unlockForConfiguration]; + RTCLogError(@"Failed to begin WebRTC session: %@", error.localizedDescription); + audio_unit_.reset(); + return false; + } + + // If we are ready to play or record, and if the audio session can be + // configured, then initialize the audio unit. + if (session.canPlayOrRecord) { + if (!ConfigureAudioSessionLocked()) { + // One possible reason for failure is if an attempt was made to use the + // audio session during or after a Media Services failure. + // See AVAudioSessionErrorCodeMediaServicesFailed for details. + [session unlockForConfiguration]; + audio_unit_.reset(); + return false; + } + SetupAudioBuffersForActiveAudioSession(); + audio_unit_->Initialize(playout_parameters_.sample_rate()); + } + + // Release the lock. + [session unlockForConfiguration]; + return true; +} + +void AudioDeviceIOS::ShutdownPlayOrRecord() { + LOGI() << "ShutdownPlayOrRecord"; + RTC_DCHECK_RUN_ON(thread_); + + // Stop the audio unit to prevent any additional audio callbacks. + audio_unit_->Stop(); + + // Close and delete the voice-processing I/O unit. + audio_unit_.reset(); + + // Detach thread checker for the AURemoteIO::IOThread to ensure that the + // next session uses a fresh thread id. + io_thread_checker_.Detach(); + + // Remove audio session notification observers. + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session removeDelegate:audio_session_observer_]; + + // All I/O should be stopped or paused prior to deactivating the audio + // session, hence we deactivate as last action. + UnconfigureAudioSession(); +} + +void AudioDeviceIOS::PrepareForNewStart() { + LOGI() << "PrepareForNewStart"; + // The audio unit has been stopped and preparations are needed for an upcoming + // restart. It will result in audio callbacks from a new native I/O thread + // which means that we must detach thread checkers here to be prepared for an + // upcoming new audio stream. + io_thread_checker_.Detach(); +} + +bool AudioDeviceIOS::IsInterrupted() { + return is_interrupted_; +} + +#pragma mark - Not Implemented + +int32_t AudioDeviceIOS::ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const { + audioLayer = AudioDeviceModule::kPlatformDefaultAudio; + return 0; +} + +int16_t AudioDeviceIOS::PlayoutDevices() { + // TODO(henrika): improve. + RTC_LOG_F(LS_WARNING) << "Not implemented"; + return (int16_t)1; +} + +int16_t AudioDeviceIOS::RecordingDevices() { + // TODO(henrika): improve. + RTC_LOG_F(LS_WARNING) << "Not implemented"; + return (int16_t)1; +} + +int32_t AudioDeviceIOS::InitSpeaker() { + return 0; +} + +bool AudioDeviceIOS::SpeakerIsInitialized() const { + return true; +} + +int32_t AudioDeviceIOS::SpeakerVolumeIsAvailable(bool& available) { + available = false; + return 0; +} + +int32_t AudioDeviceIOS::SetSpeakerVolume(uint32_t volume) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::SpeakerVolume(uint32_t& volume) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::MaxSpeakerVolume(uint32_t& maxVolume) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::MinSpeakerVolume(uint32_t& minVolume) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::SpeakerMuteIsAvailable(bool& available) { + available = false; + return 0; +} + +int32_t AudioDeviceIOS::SetSpeakerMute(bool enable) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::SpeakerMute(bool& enabled) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::SetPlayoutDevice(uint16_t index) { + RTC_LOG_F(LS_WARNING) << "Not implemented"; + return 0; +} + +int32_t AudioDeviceIOS::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::InitMicrophone() { + return 0; +} + +bool AudioDeviceIOS::MicrophoneIsInitialized() const { + return true; +} + +int32_t AudioDeviceIOS::MicrophoneMuteIsAvailable(bool& available) { + available = false; + return 0; +} + +int32_t AudioDeviceIOS::SetMicrophoneMute(bool enable) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::MicrophoneMute(bool& enabled) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::StereoRecordingIsAvailable(bool& available) { + available = false; + return 0; +} + +int32_t AudioDeviceIOS::SetStereoRecording(bool enable) { + RTC_LOG_F(LS_WARNING) << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::StereoRecording(bool& enabled) const { + enabled = false; + return 0; +} + +int32_t AudioDeviceIOS::StereoPlayoutIsAvailable(bool& available) { + available = false; + return 0; +} + +int32_t AudioDeviceIOS::SetStereoPlayout(bool enable) { + RTC_LOG_F(LS_WARNING) << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::StereoPlayout(bool& enabled) const { + enabled = false; + return 0; +} + +int32_t AudioDeviceIOS::MicrophoneVolumeIsAvailable(bool& available) { + available = false; + return 0; +} + +int32_t AudioDeviceIOS::SetMicrophoneVolume(uint32_t volume) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::MicrophoneVolume(uint32_t& volume) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::MaxMicrophoneVolume(uint32_t& maxVolume) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::MinMicrophoneVolume(uint32_t& minVolume) const { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::SetRecordingDevice(uint16_t index) { + RTC_LOG_F(LS_WARNING) << "Not implemented"; + return 0; +} + +int32_t AudioDeviceIOS::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType) { + RTC_DCHECK_NOTREACHED() << "Not implemented"; + return -1; +} + +int32_t AudioDeviceIOS::PlayoutIsAvailable(bool& available) { + available = true; + return 0; +} + +int32_t AudioDeviceIOS::RecordingIsAvailable(bool& available) { + available = true; + return 0; +} + +} // namespace ios_adm +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_module_ios.h b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_module_ios.h new file mode 100644 index 0000000000..9bcf114e32 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_module_ios.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_DEVICE_MODULE_IOS_H_ +#define SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_DEVICE_MODULE_IOS_H_ + +#include <memory> + +#include "audio_device_ios.h" + +#include "api/task_queue/task_queue_factory.h" +#include "modules/audio_device/audio_device_buffer.h" +#include "modules/audio_device/include/audio_device.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class AudioDeviceGeneric; + +namespace ios_adm { + +class AudioDeviceModuleIOS : public AudioDeviceModule { + public: + int32_t AttachAudioBuffer(); + + explicit AudioDeviceModuleIOS(bool bypass_voice_processing); + ~AudioDeviceModuleIOS() override; + + // Retrieve the currently utilized audio layer + int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override; + + // Full-duplex transportation of PCM audio + int32_t RegisterAudioCallback(AudioTransport* audioCallback) override; + + // Main initializaton and termination + int32_t Init() override; + int32_t Terminate() override; + bool Initialized() const override; + + // Device enumeration + int16_t PlayoutDevices() override; + int16_t RecordingDevices() override; + int32_t PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) override; + int32_t RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) override; + + // Device selection + int32_t SetPlayoutDevice(uint16_t index) override; + int32_t SetPlayoutDevice(WindowsDeviceType device) override; + int32_t SetRecordingDevice(uint16_t index) override; + int32_t SetRecordingDevice(WindowsDeviceType device) override; + + // Audio transport initialization + int32_t PlayoutIsAvailable(bool* available) override; + int32_t InitPlayout() override; + bool PlayoutIsInitialized() const override; + int32_t RecordingIsAvailable(bool* available) override; + int32_t InitRecording() override; + bool RecordingIsInitialized() const override; + + // Audio transport control + int32_t StartPlayout() override; + int32_t StopPlayout() override; + bool Playing() const override; + int32_t StartRecording() override; + int32_t StopRecording() override; + bool Recording() const override; + + // Audio mixer initialization + int32_t InitSpeaker() override; + bool SpeakerIsInitialized() const override; + int32_t InitMicrophone() override; + bool MicrophoneIsInitialized() const override; + + // Speaker volume controls + int32_t SpeakerVolumeIsAvailable(bool* available) override; + int32_t SetSpeakerVolume(uint32_t volume) override; + int32_t SpeakerVolume(uint32_t* volume) const override; + int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override; + int32_t MinSpeakerVolume(uint32_t* minVolume) const override; + + // Microphone volume controls + int32_t MicrophoneVolumeIsAvailable(bool* available) override; + int32_t SetMicrophoneVolume(uint32_t volume) override; + int32_t MicrophoneVolume(uint32_t* volume) const override; + int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override; + int32_t MinMicrophoneVolume(uint32_t* minVolume) const override; + + // Speaker mute control + int32_t SpeakerMuteIsAvailable(bool* available) override; + int32_t SetSpeakerMute(bool enable) override; + int32_t SpeakerMute(bool* enabled) const override; + + // Microphone mute control + int32_t MicrophoneMuteIsAvailable(bool* available) override; + int32_t SetMicrophoneMute(bool enable) override; + int32_t MicrophoneMute(bool* enabled) const override; + + // Stereo support + int32_t StereoPlayoutIsAvailable(bool* available) const override; + int32_t SetStereoPlayout(bool enable) override; + int32_t StereoPlayout(bool* enabled) const override; + int32_t StereoRecordingIsAvailable(bool* available) const override; + int32_t SetStereoRecording(bool enable) override; + int32_t StereoRecording(bool* enabled) const override; + + // Delay information and control + int32_t PlayoutDelay(uint16_t* delayMS) const override; + + bool BuiltInAECIsAvailable() const override; + int32_t EnableBuiltInAEC(bool enable) override; + bool BuiltInAGCIsAvailable() const override; + int32_t EnableBuiltInAGC(bool enable) override; + bool BuiltInNSIsAvailable() const override; + int32_t EnableBuiltInNS(bool enable) override; + + int32_t GetPlayoutUnderrunCount() const override; + +#if defined(WEBRTC_IOS) + int GetPlayoutAudioParameters(AudioParameters* params) const override; + int GetRecordAudioParameters(AudioParameters* params) const override; +#endif // WEBRTC_IOS + private: + const bool bypass_voice_processing_; + bool initialized_ = false; + const std::unique_ptr<TaskQueueFactory> task_queue_factory_; + std::unique_ptr<AudioDeviceIOS> audio_device_; + std::unique_ptr<AudioDeviceBuffer> audio_device_buffer_; +}; +} // namespace ios_adm +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_DEVICE_MODULE_IOS_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_module_ios.mm b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_module_ios.mm new file mode 100644 index 0000000000..5effef3abd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_device_module_ios.mm @@ -0,0 +1,669 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "audio_device_module_ios.h" + +#include "api/task_queue/default_task_queue_factory.h" +#include "modules/audio_device/audio_device_config.h" +#include "modules/audio_device/audio_device_generic.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_count.h" +#include "system_wrappers/include/metrics.h" + +#if defined(WEBRTC_IOS) +#include "audio_device_ios.h" +#endif + +#define CHECKinitialized_() \ + { \ + if (!initialized_) { \ + return -1; \ + }; \ + } + +#define CHECKinitialized__BOOL() \ + { \ + if (!initialized_) { \ + return false; \ + }; \ + } + +namespace webrtc { +namespace ios_adm { + +AudioDeviceModuleIOS::AudioDeviceModuleIOS(bool bypass_voice_processing) + : bypass_voice_processing_(bypass_voice_processing), + task_queue_factory_(CreateDefaultTaskQueueFactory()) { + RTC_LOG(LS_INFO) << "current platform is IOS"; + RTC_LOG(LS_INFO) << "iPhone Audio APIs will be utilized."; +} + + int32_t AudioDeviceModuleIOS::AttachAudioBuffer() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + audio_device_->AttachAudioBuffer(audio_device_buffer_.get()); + return 0; + } + + AudioDeviceModuleIOS::~AudioDeviceModuleIOS() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + } + + int32_t AudioDeviceModuleIOS::ActiveAudioLayer(AudioLayer* audioLayer) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + AudioLayer activeAudio; + if (audio_device_->ActiveAudioLayer(activeAudio) == -1) { + return -1; + } + *audioLayer = activeAudio; + return 0; + } + + int32_t AudioDeviceModuleIOS::Init() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + if (initialized_) + return 0; + + audio_device_buffer_.reset(new webrtc::AudioDeviceBuffer(task_queue_factory_.get())); + audio_device_.reset(new ios_adm::AudioDeviceIOS(bypass_voice_processing_)); + RTC_CHECK(audio_device_); + + this->AttachAudioBuffer(); + + AudioDeviceGeneric::InitStatus status = audio_device_->Init(); + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.InitializationResult", static_cast<int>(status), + static_cast<int>(AudioDeviceGeneric::InitStatus::NUM_STATUSES)); + if (status != AudioDeviceGeneric::InitStatus::OK) { + RTC_LOG(LS_ERROR) << "Audio device initialization failed."; + return -1; + } + initialized_ = true; + return 0; + } + + int32_t AudioDeviceModuleIOS::Terminate() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + if (!initialized_) + return 0; + if (audio_device_->Terminate() == -1) { + return -1; + } + initialized_ = false; + return 0; + } + + bool AudioDeviceModuleIOS::Initialized() const { + RTC_DLOG(LS_INFO) << __FUNCTION__ << ": " << initialized_; + return initialized_; + } + + int32_t AudioDeviceModuleIOS::InitSpeaker() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + return audio_device_->InitSpeaker(); + } + + int32_t AudioDeviceModuleIOS::InitMicrophone() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + return audio_device_->InitMicrophone(); + } + + int32_t AudioDeviceModuleIOS::SpeakerVolumeIsAvailable(bool* available) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->SpeakerVolumeIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::SetSpeakerVolume(uint32_t volume) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << volume << ")"; + CHECKinitialized_(); + return audio_device_->SetSpeakerVolume(volume); + } + + int32_t AudioDeviceModuleIOS::SpeakerVolume(uint32_t* volume) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + uint32_t level = 0; + if (audio_device_->SpeakerVolume(level) == -1) { + return -1; + } + *volume = level; + RTC_DLOG(LS_INFO) << "output: " << *volume; + return 0; + } + + bool AudioDeviceModuleIOS::SpeakerIsInitialized() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + bool isInitialized = audio_device_->SpeakerIsInitialized(); + RTC_DLOG(LS_INFO) << "output: " << isInitialized; + return isInitialized; + } + + bool AudioDeviceModuleIOS::MicrophoneIsInitialized() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + bool isInitialized = audio_device_->MicrophoneIsInitialized(); + RTC_DLOG(LS_INFO) << "output: " << isInitialized; + return isInitialized; + } + + int32_t AudioDeviceModuleIOS::MaxSpeakerVolume(uint32_t* maxVolume) const { + CHECKinitialized_(); + uint32_t maxVol = 0; + if (audio_device_->MaxSpeakerVolume(maxVol) == -1) { + return -1; + } + *maxVolume = maxVol; + return 0; + } + + int32_t AudioDeviceModuleIOS::MinSpeakerVolume(uint32_t* minVolume) const { + CHECKinitialized_(); + uint32_t minVol = 0; + if (audio_device_->MinSpeakerVolume(minVol) == -1) { + return -1; + } + *minVolume = minVol; + return 0; + } + + int32_t AudioDeviceModuleIOS::SpeakerMuteIsAvailable(bool* available) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->SpeakerMuteIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::SetSpeakerMute(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + return audio_device_->SetSpeakerMute(enable); + } + + int32_t AudioDeviceModuleIOS::SpeakerMute(bool* enabled) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool muted = false; + if (audio_device_->SpeakerMute(muted) == -1) { + return -1; + } + *enabled = muted; + RTC_DLOG(LS_INFO) << "output: " << muted; + return 0; + } + + int32_t AudioDeviceModuleIOS::MicrophoneMuteIsAvailable(bool* available) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->MicrophoneMuteIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::SetMicrophoneMute(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + return (audio_device_->SetMicrophoneMute(enable)); + } + + int32_t AudioDeviceModuleIOS::MicrophoneMute(bool* enabled) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool muted = false; + if (audio_device_->MicrophoneMute(muted) == -1) { + return -1; + } + *enabled = muted; + RTC_DLOG(LS_INFO) << "output: " << muted; + return 0; + } + + int32_t AudioDeviceModuleIOS::MicrophoneVolumeIsAvailable(bool* available) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->MicrophoneVolumeIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::SetMicrophoneVolume(uint32_t volume) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << volume << ")"; + CHECKinitialized_(); + return (audio_device_->SetMicrophoneVolume(volume)); + } + + int32_t AudioDeviceModuleIOS::MicrophoneVolume(uint32_t* volume) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + uint32_t level = 0; + if (audio_device_->MicrophoneVolume(level) == -1) { + return -1; + } + *volume = level; + RTC_DLOG(LS_INFO) << "output: " << *volume; + return 0; + } + + int32_t AudioDeviceModuleIOS::StereoRecordingIsAvailable( + bool* available) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->StereoRecordingIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::SetStereoRecording(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + if (enable) { + RTC_LOG(LS_WARNING) << "recording in stereo is not supported"; + } + return -1; + } + + int32_t AudioDeviceModuleIOS::StereoRecording(bool* enabled) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool stereo = false; + if (audio_device_->StereoRecording(stereo) == -1) { + return -1; + } + *enabled = stereo; + RTC_DLOG(LS_INFO) << "output: " << stereo; + return 0; + } + + int32_t AudioDeviceModuleIOS::StereoPlayoutIsAvailable(bool* available) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->StereoPlayoutIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::SetStereoPlayout(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + if (audio_device_->PlayoutIsInitialized()) { + RTC_LOG(LS_ERROR) << "unable to set stereo mode while playing side is initialized"; + return -1; + } + if (audio_device_->SetStereoPlayout(enable)) { + RTC_LOG(LS_WARNING) << "stereo playout is not supported"; + return -1; + } + int8_t nChannels(1); + if (enable) { + nChannels = 2; + } + audio_device_buffer_.get()->SetPlayoutChannels(nChannels); + return 0; + } + + int32_t AudioDeviceModuleIOS::StereoPlayout(bool* enabled) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool stereo = false; + if (audio_device_->StereoPlayout(stereo) == -1) { + return -1; + } + *enabled = stereo; + RTC_DLOG(LS_INFO) << "output: " << stereo; + return 0; + } + + int32_t AudioDeviceModuleIOS::PlayoutIsAvailable(bool* available) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->PlayoutIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::RecordingIsAvailable(bool* available) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + bool isAvailable = false; + if (audio_device_->RecordingIsAvailable(isAvailable) == -1) { + return -1; + } + *available = isAvailable; + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return 0; + } + + int32_t AudioDeviceModuleIOS::MaxMicrophoneVolume(uint32_t* maxVolume) const { + CHECKinitialized_(); + uint32_t maxVol(0); + if (audio_device_->MaxMicrophoneVolume(maxVol) == -1) { + return -1; + } + *maxVolume = maxVol; + return 0; + } + + int32_t AudioDeviceModuleIOS::MinMicrophoneVolume(uint32_t* minVolume) const { + CHECKinitialized_(); + uint32_t minVol(0); + if (audio_device_->MinMicrophoneVolume(minVol) == -1) { + return -1; + } + *minVolume = minVol; + return 0; + } + + int16_t AudioDeviceModuleIOS::PlayoutDevices() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + uint16_t nPlayoutDevices = audio_device_->PlayoutDevices(); + RTC_DLOG(LS_INFO) << "output: " << nPlayoutDevices; + return (int16_t)(nPlayoutDevices); + } + + int32_t AudioDeviceModuleIOS::SetPlayoutDevice(uint16_t index) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << index << ")"; + CHECKinitialized_(); + return audio_device_->SetPlayoutDevice(index); + } + + int32_t AudioDeviceModuleIOS::SetPlayoutDevice(WindowsDeviceType device) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + return audio_device_->SetPlayoutDevice(device); + } + + int32_t AudioDeviceModuleIOS::PlayoutDeviceName( + uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << index << ", ...)"; + CHECKinitialized_(); + if (name == NULL) { + return -1; + } + if (audio_device_->PlayoutDeviceName(index, name, guid) == -1) { + return -1; + } + if (name != NULL) { + RTC_DLOG(LS_INFO) << "output: name = " << name; + } + if (guid != NULL) { + RTC_DLOG(LS_INFO) << "output: guid = " << guid; + } + return 0; + } + + int32_t AudioDeviceModuleIOS::RecordingDeviceName( + uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << index << ", ...)"; + CHECKinitialized_(); + if (name == NULL) { + return -1; + } + if (audio_device_->RecordingDeviceName(index, name, guid) == -1) { + return -1; + } + if (name != NULL) { + RTC_DLOG(LS_INFO) << "output: name = " << name; + } + if (guid != NULL) { + RTC_DLOG(LS_INFO) << "output: guid = " << guid; + } + return 0; + } + + int16_t AudioDeviceModuleIOS::RecordingDevices() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + uint16_t nRecordingDevices = audio_device_->RecordingDevices(); + RTC_DLOG(LS_INFO) << "output: " << nRecordingDevices; + return (int16_t)nRecordingDevices; + } + + int32_t AudioDeviceModuleIOS::SetRecordingDevice(uint16_t index) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << index << ")"; + CHECKinitialized_(); + return audio_device_->SetRecordingDevice(index); + } + + int32_t AudioDeviceModuleIOS::SetRecordingDevice(WindowsDeviceType device) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + return audio_device_->SetRecordingDevice(device); + } + + int32_t AudioDeviceModuleIOS::InitPlayout() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + if (PlayoutIsInitialized()) { + return 0; + } + int32_t result = audio_device_->InitPlayout(); + RTC_DLOG(LS_INFO) << "output: " << result; + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.InitPlayoutSuccess", + static_cast<int>(result == 0)); + return result; + } + + int32_t AudioDeviceModuleIOS::InitRecording() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + if (RecordingIsInitialized()) { + return 0; + } + int32_t result = audio_device_->InitRecording(); + RTC_DLOG(LS_INFO) << "output: " << result; + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.InitRecordingSuccess", + static_cast<int>(result == 0)); + return result; + } + + bool AudioDeviceModuleIOS::PlayoutIsInitialized() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + return audio_device_->PlayoutIsInitialized(); + } + + bool AudioDeviceModuleIOS::RecordingIsInitialized() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + return audio_device_->RecordingIsInitialized(); + } + + int32_t AudioDeviceModuleIOS::StartPlayout() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + if (Playing()) { + return 0; + } + audio_device_buffer_.get()->StartPlayout(); + int32_t result = audio_device_->StartPlayout(); + RTC_DLOG(LS_INFO) << "output: " << result; + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StartPlayoutSuccess", + static_cast<int>(result == 0)); + return result; + } + + int32_t AudioDeviceModuleIOS::StopPlayout() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + int32_t result = audio_device_->StopPlayout(); + audio_device_buffer_.get()->StopPlayout(); + RTC_DLOG(LS_INFO) << "output: " << result; + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StopPlayoutSuccess", + static_cast<int>(result == 0)); + return result; + } + + bool AudioDeviceModuleIOS::Playing() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + return audio_device_->Playing(); + } + + int32_t AudioDeviceModuleIOS::StartRecording() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + if (Recording()) { + return 0; + } + audio_device_buffer_.get()->StartRecording(); + int32_t result = audio_device_->StartRecording(); + RTC_DLOG(LS_INFO) << "output: " << result; + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StartRecordingSuccess", + static_cast<int>(result == 0)); + return result; + } + + int32_t AudioDeviceModuleIOS::StopRecording() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); + int32_t result = audio_device_->StopRecording(); + audio_device_buffer_.get()->StopRecording(); + RTC_DLOG(LS_INFO) << "output: " << result; + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StopRecordingSuccess", + static_cast<int>(result == 0)); + return result; + } + + bool AudioDeviceModuleIOS::Recording() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + return audio_device_->Recording(); + } + + int32_t AudioDeviceModuleIOS::RegisterAudioCallback( + AudioTransport* audioCallback) { + RTC_DLOG(LS_INFO) << __FUNCTION__; + return audio_device_buffer_.get()->RegisterAudioCallback(audioCallback); + } + + int32_t AudioDeviceModuleIOS::PlayoutDelay(uint16_t* delayMS) const { + CHECKinitialized_(); + uint16_t delay = 0; + if (audio_device_->PlayoutDelay(delay) == -1) { + RTC_LOG(LS_ERROR) << "failed to retrieve the playout delay"; + return -1; + } + *delayMS = delay; + return 0; + } + + bool AudioDeviceModuleIOS::BuiltInAECIsAvailable() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + bool isAvailable = audio_device_->BuiltInAECIsAvailable(); + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return isAvailable; + } + + int32_t AudioDeviceModuleIOS::EnableBuiltInAEC(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + int32_t ok = audio_device_->EnableBuiltInAEC(enable); + RTC_DLOG(LS_INFO) << "output: " << ok; + return ok; + } + + bool AudioDeviceModuleIOS::BuiltInAGCIsAvailable() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + bool isAvailable = audio_device_->BuiltInAGCIsAvailable(); + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return isAvailable; + } + + int32_t AudioDeviceModuleIOS::EnableBuiltInAGC(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + int32_t ok = audio_device_->EnableBuiltInAGC(enable); + RTC_DLOG(LS_INFO) << "output: " << ok; + return ok; + } + + bool AudioDeviceModuleIOS::BuiltInNSIsAvailable() const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized__BOOL(); + bool isAvailable = audio_device_->BuiltInNSIsAvailable(); + RTC_DLOG(LS_INFO) << "output: " << isAvailable; + return isAvailable; + } + + int32_t AudioDeviceModuleIOS::EnableBuiltInNS(bool enable) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << "(" << enable << ")"; + CHECKinitialized_(); + int32_t ok = audio_device_->EnableBuiltInNS(enable); + RTC_DLOG(LS_INFO) << "output: " << ok; + return ok; + } + + int32_t AudioDeviceModuleIOS::GetPlayoutUnderrunCount() const { + // Don't log here, as this method can be called very often. + CHECKinitialized_(); + int32_t ok = audio_device_->GetPlayoutUnderrunCount(); + return ok; + } + +#if defined(WEBRTC_IOS) + int AudioDeviceModuleIOS::GetPlayoutAudioParameters( + AudioParameters* params) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + int r = audio_device_->GetPlayoutAudioParameters(params); + RTC_DLOG(LS_INFO) << "output: " << r; + return r; + } + + int AudioDeviceModuleIOS::GetRecordAudioParameters( + AudioParameters* params) const { + RTC_DLOG(LS_INFO) << __FUNCTION__; + int r = audio_device_->GetRecordAudioParameters(params); + RTC_DLOG(LS_INFO) << "output: " << r; + return r; + } +#endif // WEBRTC_IOS +} +} diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/audio_session_observer.h b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_session_observer.h new file mode 100644 index 0000000000..f7c44c8184 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/audio_session_observer.h @@ -0,0 +1,41 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_SESSION_OBSERVER_H_ +#define SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_SESSION_OBSERVER_H_ + +#include "rtc_base/thread.h" + +namespace webrtc { + +// Observer interface for listening to AVAudioSession events. +class AudioSessionObserver { + public: + // Called when audio session interruption begins. + virtual void OnInterruptionBegin() = 0; + + // Called when audio session interruption ends. + virtual void OnInterruptionEnd() = 0; + + // Called when audio route changes. + virtual void OnValidRouteChange() = 0; + + // Called when the ability to play or record changes. + virtual void OnCanPlayOrRecordChange(bool can_play_or_record) = 0; + + virtual void OnChangedOutputVolume() = 0; + + protected: + virtual ~AudioSessionObserver() {} +}; + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_SESSION_OBSERVER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/helpers.h b/third_party/libwebrtc/sdk/objc/native/src/audio/helpers.h new file mode 100644 index 0000000000..12464ac897 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/helpers.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_AUDIO_HELPERS_H_ +#define SDK_OBJC_NATIVE_SRC_AUDIO_HELPERS_H_ + +#import <Foundation/Foundation.h> +#include <objc/objc.h> + +#include <string> + +namespace webrtc { +namespace ios { + +bool CheckAndLogError(BOOL success, NSError* error); + +NSString* NSStringFromStdString(const std::string& stdString); +std::string StdStringFromNSString(NSString* nsString); + +// Return thread ID as a string. +std::string GetThreadId(); + +// Return thread ID as string suitable for debug logging. +std::string GetThreadInfo(); + +// Returns [NSThread currentThread] description as string. +// Example: <NSThread: 0x170066d80>{number = 1, name = main} +std::string GetCurrentThreadDescription(); + +#if defined(WEBRTC_IOS) +// Returns the current name of the operating system. +std::string GetSystemName(); + +// Returns the current version of the operating system as a string. +std::string GetSystemVersionAsString(); + +// Returns the version of the operating system in double representation. +// Uses a cached value of the system version. +double GetSystemVersion(); + +// Returns the device type. +// Examples: ”iPhone” and ”iPod touch”. +std::string GetDeviceType(); +#endif // defined(WEBRTC_IOS) + +// Returns a more detailed device name. +// Examples: "iPhone 5s (GSM)" and "iPhone 6 Plus". +std::string GetDeviceName(); + +// Returns the name of the process. Does not uniquely identify the process. +std::string GetProcessName(); + +// Returns the identifier of the process (often called process ID). +int GetProcessID(); + +// Returns a string containing the version of the operating system on which the +// process is executing. The string is string is human readable, localized, and +// is appropriate for displaying to the user. +std::string GetOSVersionString(); + +// Returns the number of processing cores available on the device. +int GetProcessorCount(); + +#if defined(WEBRTC_IOS) +// Indicates whether Low Power Mode is enabled on the iOS device. +bool GetLowPowerModeEnabled(); +#endif + +} // namespace ios +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_AUDIO_HELPERS_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/helpers.mm b/third_party/libwebrtc/sdk/objc/native/src/audio/helpers.mm new file mode 100644 index 0000000000..cd0469656a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/helpers.mm @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <sys/sysctl.h> +#if defined(WEBRTC_IOS) +#import <UIKit/UIKit.h> +#endif + +#include <memory> + +#include "helpers.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace ios { + +NSString* NSStringFromStdString(const std::string& stdString) { + // std::string may contain null termination character so we construct + // using length. + return [[NSString alloc] initWithBytes:stdString.data() + length:stdString.length() + encoding:NSUTF8StringEncoding]; +} + +std::string StdStringFromNSString(NSString* nsString) { + NSData* charData = [nsString dataUsingEncoding:NSUTF8StringEncoding]; + return std::string(reinterpret_cast<const char*>([charData bytes]), + [charData length]); +} + +bool CheckAndLogError(BOOL success, NSError* error) { + if (!success) { + NSString* msg = + [NSString stringWithFormat:@"Error: %ld, %@, %@", (long)error.code, + error.localizedDescription, + error.localizedFailureReason]; + RTC_LOG(LS_ERROR) << StdStringFromNSString(msg); + return false; + } + return true; +} + +// TODO(henrika): see if it is possible to move to GetThreadName in +// platform_thread.h and base it on pthread methods instead. +std::string GetCurrentThreadDescription() { + NSString* name = [NSString stringWithFormat:@"%@", [NSThread currentThread]]; + return StdStringFromNSString(name); +} + +#if defined(WEBRTC_IOS) +std::string GetSystemName() { + NSString* osName = [[UIDevice currentDevice] systemName]; + return StdStringFromNSString(osName); +} + +std::string GetSystemVersionAsString() { + NSString* osVersion = [[UIDevice currentDevice] systemVersion]; + return StdStringFromNSString(osVersion); +} + +std::string GetDeviceType() { + NSString* deviceModel = [[UIDevice currentDevice] model]; + return StdStringFromNSString(deviceModel); +} + +bool GetLowPowerModeEnabled() { + return [NSProcessInfo processInfo].lowPowerModeEnabled; +} +#endif + +std::string GetDeviceName() { + size_t size; + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + std::unique_ptr<char[]> machine; + machine.reset(new char[size]); + sysctlbyname("hw.machine", machine.get(), &size, NULL, 0); + return std::string(machine.get()); +} + +std::string GetProcessName() { + NSString* processName = [NSProcessInfo processInfo].processName; + return StdStringFromNSString(processName); +} + +int GetProcessID() { + return [NSProcessInfo processInfo].processIdentifier; +} + +std::string GetOSVersionString() { + NSString* osVersion = + [NSProcessInfo processInfo].operatingSystemVersionString; + return StdStringFromNSString(osVersion); +} + +int GetProcessorCount() { + return [NSProcessInfo processInfo].processorCount; +} + +} // namespace ios +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/voice_processing_audio_unit.h b/third_party/libwebrtc/sdk/objc/native/src/audio/voice_processing_audio_unit.h new file mode 100644 index 0000000000..ed9dd98568 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/voice_processing_audio_unit.h @@ -0,0 +1,141 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_AUDIO_VOICE_PROCESSING_AUDIO_UNIT_H_ +#define SDK_OBJC_NATIVE_SRC_AUDIO_VOICE_PROCESSING_AUDIO_UNIT_H_ + +#include <AudioUnit/AudioUnit.h> + +namespace webrtc { +namespace ios_adm { + +class VoiceProcessingAudioUnitObserver { + public: + // Callback function called on a real-time priority I/O thread from the audio + // unit. This method is used to signal that recorded audio is available. + virtual OSStatus OnDeliverRecordedData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) = 0; + + // Callback function called on a real-time priority I/O thread from the audio + // unit. This method is used to provide audio samples to the audio unit. + virtual OSStatus OnGetPlayoutData(AudioUnitRenderActionFlags* io_action_flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) = 0; + + protected: + ~VoiceProcessingAudioUnitObserver() {} +}; + +// Convenience class to abstract away the management of a Voice Processing +// I/O Audio Unit. The Voice Processing I/O unit has the same characteristics +// as the Remote I/O unit (supports full duplex low-latency audio input and +// output) and adds AEC for for two-way duplex communication. It also adds AGC, +// adjustment of voice-processing quality, and muting. Hence, ideal for +// VoIP applications. +class VoiceProcessingAudioUnit { + public: + VoiceProcessingAudioUnit(bool bypass_voice_processing, + VoiceProcessingAudioUnitObserver* observer); + ~VoiceProcessingAudioUnit(); + + // TODO(tkchin): enum for state and state checking. + enum State : int32_t { + // Init() should be called. + kInitRequired, + // Audio unit created but not initialized. + kUninitialized, + // Initialized but not started. Equivalent to stopped. + kInitialized, + // Initialized and started. + kStarted, + }; + + // Number of bytes per audio sample for 16-bit signed integer representation. + static const UInt32 kBytesPerSample; + + // Initializes this class by creating the underlying audio unit instance. + // Creates a Voice-Processing I/O unit and configures it for full-duplex + // audio. The selected stream format is selected to avoid internal resampling + // and to match the 10ms callback rate for WebRTC as well as possible. + // Does not intialize the audio unit. + bool Init(); + + VoiceProcessingAudioUnit::State GetState() const; + + // Initializes the underlying audio unit with the given sample rate. + bool Initialize(Float64 sample_rate); + + // Starts the underlying audio unit. + OSStatus Start(); + + // Stops the underlying audio unit. + bool Stop(); + + // Uninitializes the underlying audio unit. + bool Uninitialize(); + + // Calls render on the underlying audio unit. + OSStatus Render(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 output_bus_number, + UInt32 num_frames, + AudioBufferList* io_data); + + private: + // The C API used to set callbacks requires static functions. When these are + // called, they will invoke the relevant instance method by casting + // in_ref_con to VoiceProcessingAudioUnit*. + static OSStatus OnGetPlayoutData(void* in_ref_con, + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data); + static OSStatus OnDeliverRecordedData(void* in_ref_con, + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data); + + // Notifies observer that samples are needed for playback. + OSStatus NotifyGetPlayoutData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data); + // Notifies observer that recorded samples are available for render. + OSStatus NotifyDeliverRecordedData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data); + + // Returns the predetermined format with a specific sample rate. See + // implementation file for details on format. + AudioStreamBasicDescription GetFormat(Float64 sample_rate) const; + + // Deletes the underlying audio unit. + void DisposeAudioUnit(); + + const bool bypass_voice_processing_; + VoiceProcessingAudioUnitObserver* observer_; + AudioUnit vpio_unit_; + VoiceProcessingAudioUnit::State state_; +}; +} // namespace ios_adm +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_AUDIO_VOICE_PROCESSING_AUDIO_UNIT_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/audio/voice_processing_audio_unit.mm b/third_party/libwebrtc/sdk/objc/native/src/audio/voice_processing_audio_unit.mm new file mode 100644 index 0000000000..3905b6857a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/audio/voice_processing_audio_unit.mm @@ -0,0 +1,488 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "voice_processing_audio_unit.h" + +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +#import "base/RTCLogging.h" +#import "sdk/objc/components/audio/RTCAudioSessionConfiguration.h" + +#if !defined(NDEBUG) +static void LogStreamDescription(AudioStreamBasicDescription description) { + char formatIdString[5]; + UInt32 formatId = CFSwapInt32HostToBig(description.mFormatID); + bcopy(&formatId, formatIdString, 4); + formatIdString[4] = '\0'; + RTCLog(@"AudioStreamBasicDescription: {\n" + " mSampleRate: %.2f\n" + " formatIDString: %s\n" + " mFormatFlags: 0x%X\n" + " mBytesPerPacket: %u\n" + " mFramesPerPacket: %u\n" + " mBytesPerFrame: %u\n" + " mChannelsPerFrame: %u\n" + " mBitsPerChannel: %u\n" + " mReserved: %u\n}", + description.mSampleRate, formatIdString, + static_cast<unsigned int>(description.mFormatFlags), + static_cast<unsigned int>(description.mBytesPerPacket), + static_cast<unsigned int>(description.mFramesPerPacket), + static_cast<unsigned int>(description.mBytesPerFrame), + static_cast<unsigned int>(description.mChannelsPerFrame), + static_cast<unsigned int>(description.mBitsPerChannel), + static_cast<unsigned int>(description.mReserved)); +} +#endif + +namespace webrtc { +namespace ios_adm { + +// Calls to AudioUnitInitialize() can fail if called back-to-back on different +// ADM instances. A fall-back solution is to allow multiple sequential calls +// with as small delay between each. This factor sets the max number of allowed +// initialization attempts. +static const int kMaxNumberOfAudioUnitInitializeAttempts = 5; +// A VP I/O unit's bus 1 connects to input hardware (microphone). +static const AudioUnitElement kInputBus = 1; +// A VP I/O unit's bus 0 connects to output hardware (speaker). +static const AudioUnitElement kOutputBus = 0; + +// Returns the automatic gain control (AGC) state on the processed microphone +// signal. Should be on by default for Voice Processing audio units. +static OSStatus GetAGCState(AudioUnit audio_unit, UInt32* enabled) { + RTC_DCHECK(audio_unit); + UInt32 size = sizeof(*enabled); + OSStatus result = AudioUnitGetProperty(audio_unit, + kAUVoiceIOProperty_VoiceProcessingEnableAGC, + kAudioUnitScope_Global, + kInputBus, + enabled, + &size); + RTCLog(@"VPIO unit AGC: %u", static_cast<unsigned int>(*enabled)); + return result; +} + +VoiceProcessingAudioUnit::VoiceProcessingAudioUnit(bool bypass_voice_processing, + VoiceProcessingAudioUnitObserver* observer) + : bypass_voice_processing_(bypass_voice_processing), + observer_(observer), + vpio_unit_(nullptr), + state_(kInitRequired) { + RTC_DCHECK(observer); +} + +VoiceProcessingAudioUnit::~VoiceProcessingAudioUnit() { + DisposeAudioUnit(); +} + +const UInt32 VoiceProcessingAudioUnit::kBytesPerSample = 2; + +bool VoiceProcessingAudioUnit::Init() { + RTC_DCHECK_EQ(state_, kInitRequired); + + // Create an audio component description to identify the Voice Processing + // I/O audio unit. + AudioComponentDescription vpio_unit_description; + vpio_unit_description.componentType = kAudioUnitType_Output; + vpio_unit_description.componentSubType = kAudioUnitSubType_VoiceProcessingIO; + vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple; + vpio_unit_description.componentFlags = 0; + vpio_unit_description.componentFlagsMask = 0; + + // Obtain an audio unit instance given the description. + AudioComponent found_vpio_unit_ref = + AudioComponentFindNext(nullptr, &vpio_unit_description); + + // Create a Voice Processing IO audio unit. + OSStatus result = noErr; + result = AudioComponentInstanceNew(found_vpio_unit_ref, &vpio_unit_); + if (result != noErr) { + vpio_unit_ = nullptr; + RTCLogError(@"AudioComponentInstanceNew failed. Error=%ld.", (long)result); + return false; + } + + // Enable input on the input scope of the input element. + UInt32 enable_input = 1; + result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, kInputBus, &enable_input, + sizeof(enable_input)); + if (result != noErr) { + DisposeAudioUnit(); + RTCLogError(@"Failed to enable input on input scope of input element. " + "Error=%ld.", + (long)result); + return false; + } + + // Enable output on the output scope of the output element. + UInt32 enable_output = 1; + result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, kOutputBus, + &enable_output, sizeof(enable_output)); + if (result != noErr) { + DisposeAudioUnit(); + RTCLogError(@"Failed to enable output on output scope of output element. " + "Error=%ld.", + (long)result); + return false; + } + + // Specify the callback function that provides audio samples to the audio + // unit. + AURenderCallbackStruct render_callback; + render_callback.inputProc = OnGetPlayoutData; + render_callback.inputProcRefCon = this; + result = AudioUnitSetProperty( + vpio_unit_, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + kOutputBus, &render_callback, sizeof(render_callback)); + if (result != noErr) { + DisposeAudioUnit(); + RTCLogError(@"Failed to specify the render callback on the output bus. " + "Error=%ld.", + (long)result); + return false; + } + + // Disable AU buffer allocation for the recorder, we allocate our own. + // TODO(henrika): not sure that it actually saves resource to make this call. + UInt32 flag = 0; + result = AudioUnitSetProperty( + vpio_unit_, kAudioUnitProperty_ShouldAllocateBuffer, + kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag)); + if (result != noErr) { + DisposeAudioUnit(); + RTCLogError(@"Failed to disable buffer allocation on the input bus. " + "Error=%ld.", + (long)result); + return false; + } + + // Specify the callback to be called by the I/O thread to us when input audio + // is available. The recorded samples can then be obtained by calling the + // AudioUnitRender() method. + AURenderCallbackStruct input_callback; + input_callback.inputProc = OnDeliverRecordedData; + input_callback.inputProcRefCon = this; + result = AudioUnitSetProperty(vpio_unit_, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, kInputBus, + &input_callback, sizeof(input_callback)); + if (result != noErr) { + DisposeAudioUnit(); + RTCLogError(@"Failed to specify the input callback on the input bus. " + "Error=%ld.", + (long)result); + return false; + } + + state_ = kUninitialized; + return true; +} + +VoiceProcessingAudioUnit::State VoiceProcessingAudioUnit::GetState() const { + return state_; +} + +bool VoiceProcessingAudioUnit::Initialize(Float64 sample_rate) { + RTC_DCHECK_GE(state_, kUninitialized); + RTCLog(@"Initializing audio unit with sample rate: %f", sample_rate); + + OSStatus result = noErr; + AudioStreamBasicDescription format = GetFormat(sample_rate); + UInt32 size = sizeof(format); +#if !defined(NDEBUG) + LogStreamDescription(format); +#endif + + // Set the format on the output scope of the input element/bus. + result = + AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, kInputBus, &format, size); + if (result != noErr) { + RTCLogError(@"Failed to set format on output scope of input bus. " + "Error=%ld.", + (long)result); + return false; + } + + // Set the format on the input scope of the output element/bus. + result = + AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, kOutputBus, &format, size); + if (result != noErr) { + RTCLogError(@"Failed to set format on input scope of output bus. " + "Error=%ld.", + (long)result); + return false; + } + + // Initialize the Voice Processing I/O unit instance. + // Calls to AudioUnitInitialize() can fail if called back-to-back on + // different ADM instances. The error message in this case is -66635 which is + // undocumented. Tests have shown that calling AudioUnitInitialize a second + // time, after a short sleep, avoids this issue. + // See webrtc:5166 for details. + int failed_initalize_attempts = 0; + result = AudioUnitInitialize(vpio_unit_); + while (result != noErr) { + RTCLogError(@"Failed to initialize the Voice Processing I/O unit. " + "Error=%ld.", + (long)result); + ++failed_initalize_attempts; + if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) { + // Max number of initialization attempts exceeded, hence abort. + RTCLogError(@"Too many initialization attempts."); + return false; + } + RTCLog(@"Pause 100ms and try audio unit initialization again..."); + [NSThread sleepForTimeInterval:0.1f]; + result = AudioUnitInitialize(vpio_unit_); + } + if (result == noErr) { + RTCLog(@"Voice Processing I/O unit is now initialized."); + } + + if (bypass_voice_processing_) { + // Attempt to disable builtin voice processing. + UInt32 toggle = 1; + result = AudioUnitSetProperty(vpio_unit_, + kAUVoiceIOProperty_BypassVoiceProcessing, + kAudioUnitScope_Global, + kInputBus, + &toggle, + sizeof(toggle)); + if (result == noErr) { + RTCLog(@"Successfully bypassed voice processing."); + } else { + RTCLogError(@"Failed to bypass voice processing. Error=%ld.", (long)result); + } + state_ = kInitialized; + return true; + } + + // AGC should be enabled by default for Voice Processing I/O units but it is + // checked below and enabled explicitly if needed. This scheme is used + // to be absolutely sure that the AGC is enabled since we have seen cases + // where only zeros are recorded and a disabled AGC could be one of the + // reasons why it happens. + int agc_was_enabled_by_default = 0; + UInt32 agc_is_enabled = 0; + result = GetAGCState(vpio_unit_, &agc_is_enabled); + if (result != noErr) { + RTCLogError(@"Failed to get AGC state (1st attempt). " + "Error=%ld.", + (long)result); + // Example of error code: kAudioUnitErr_NoConnection (-10876). + // All error codes related to audio units are negative and are therefore + // converted into a postive value to match the UMA APIs. + RTC_HISTOGRAM_COUNTS_SPARSE_100000( + "WebRTC.Audio.GetAGCStateErrorCode1", (-1) * result); + } else if (agc_is_enabled) { + // Remember that the AGC was enabled by default. Will be used in UMA. + agc_was_enabled_by_default = 1; + } else { + // AGC was initially disabled => try to enable it explicitly. + UInt32 enable_agc = 1; + result = + AudioUnitSetProperty(vpio_unit_, + kAUVoiceIOProperty_VoiceProcessingEnableAGC, + kAudioUnitScope_Global, kInputBus, &enable_agc, + sizeof(enable_agc)); + if (result != noErr) { + RTCLogError(@"Failed to enable the built-in AGC. " + "Error=%ld.", + (long)result); + RTC_HISTOGRAM_COUNTS_SPARSE_100000( + "WebRTC.Audio.SetAGCStateErrorCode", (-1) * result); + } + result = GetAGCState(vpio_unit_, &agc_is_enabled); + if (result != noErr) { + RTCLogError(@"Failed to get AGC state (2nd attempt). " + "Error=%ld.", + (long)result); + RTC_HISTOGRAM_COUNTS_SPARSE_100000( + "WebRTC.Audio.GetAGCStateErrorCode2", (-1) * result); + } + } + + // Track if the built-in AGC was enabled by default (as it should) or not. + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.BuiltInAGCWasEnabledByDefault", + agc_was_enabled_by_default); + RTCLog(@"WebRTC.Audio.BuiltInAGCWasEnabledByDefault: %d", + agc_was_enabled_by_default); + // As a final step, add an UMA histogram for tracking the AGC state. + // At this stage, the AGC should be enabled, and if it is not, more work is + // needed to find out the root cause. + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.BuiltInAGCIsEnabled", agc_is_enabled); + RTCLog(@"WebRTC.Audio.BuiltInAGCIsEnabled: %u", + static_cast<unsigned int>(agc_is_enabled)); + + state_ = kInitialized; + return true; +} + +OSStatus VoiceProcessingAudioUnit::Start() { + RTC_DCHECK_GE(state_, kUninitialized); + RTCLog(@"Starting audio unit."); + + OSStatus result = AudioOutputUnitStart(vpio_unit_); + if (result != noErr) { + RTCLogError(@"Failed to start audio unit. Error=%ld", (long)result); + return result; + } else { + RTCLog(@"Started audio unit"); + } + state_ = kStarted; + return noErr; +} + +bool VoiceProcessingAudioUnit::Stop() { + RTC_DCHECK_GE(state_, kUninitialized); + RTCLog(@"Stopping audio unit."); + + OSStatus result = AudioOutputUnitStop(vpio_unit_); + if (result != noErr) { + RTCLogError(@"Failed to stop audio unit. Error=%ld", (long)result); + return false; + } else { + RTCLog(@"Stopped audio unit"); + } + + state_ = kInitialized; + return true; +} + +bool VoiceProcessingAudioUnit::Uninitialize() { + RTC_DCHECK_GE(state_, kUninitialized); + RTCLog(@"Unintializing audio unit."); + + OSStatus result = AudioUnitUninitialize(vpio_unit_); + if (result != noErr) { + RTCLogError(@"Failed to uninitialize audio unit. Error=%ld", (long)result); + return false; + } else { + RTCLog(@"Uninitialized audio unit."); + } + + state_ = kUninitialized; + return true; +} + +OSStatus VoiceProcessingAudioUnit::Render(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 output_bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + RTC_DCHECK(vpio_unit_) << "Init() not called."; + + OSStatus result = AudioUnitRender(vpio_unit_, flags, time_stamp, + output_bus_number, num_frames, io_data); + if (result != noErr) { + RTCLogError(@"Failed to render audio unit. Error=%ld", (long)result); + } + return result; +} + +OSStatus VoiceProcessingAudioUnit::OnGetPlayoutData( + void* in_ref_con, + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + VoiceProcessingAudioUnit* audio_unit = + static_cast<VoiceProcessingAudioUnit*>(in_ref_con); + return audio_unit->NotifyGetPlayoutData(flags, time_stamp, bus_number, + num_frames, io_data); +} + +OSStatus VoiceProcessingAudioUnit::OnDeliverRecordedData( + void* in_ref_con, + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + VoiceProcessingAudioUnit* audio_unit = + static_cast<VoiceProcessingAudioUnit*>(in_ref_con); + return audio_unit->NotifyDeliverRecordedData(flags, time_stamp, bus_number, + num_frames, io_data); +} + +OSStatus VoiceProcessingAudioUnit::NotifyGetPlayoutData( + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + return observer_->OnGetPlayoutData(flags, time_stamp, bus_number, num_frames, + io_data); +} + +OSStatus VoiceProcessingAudioUnit::NotifyDeliverRecordedData( + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + UInt32 bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + return observer_->OnDeliverRecordedData(flags, time_stamp, bus_number, + num_frames, io_data); +} + +AudioStreamBasicDescription VoiceProcessingAudioUnit::GetFormat( + Float64 sample_rate) const { + // Set the application formats for input and output: + // - use same format in both directions + // - avoid resampling in the I/O unit by using the hardware sample rate + // - linear PCM => noncompressed audio data format with one frame per packet + // - no need to specify interleaving since only mono is supported + AudioStreamBasicDescription format; + RTC_DCHECK_EQ(1, kRTCAudioSessionPreferredNumberOfChannels); + format.mSampleRate = sample_rate; + format.mFormatID = kAudioFormatLinearPCM; + format.mFormatFlags = + kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + format.mBytesPerPacket = kBytesPerSample; + format.mFramesPerPacket = 1; // uncompressed. + format.mBytesPerFrame = kBytesPerSample; + format.mChannelsPerFrame = kRTCAudioSessionPreferredNumberOfChannels; + format.mBitsPerChannel = 8 * kBytesPerSample; + return format; +} + +void VoiceProcessingAudioUnit::DisposeAudioUnit() { + if (vpio_unit_) { + switch (state_) { + case kStarted: + Stop(); + [[fallthrough]]; + case kInitialized: + Uninitialize(); + break; + case kUninitialized: + case kInitRequired: + break; + } + + RTCLog(@"Disposing audio unit."); + OSStatus result = AudioComponentInstanceDispose(vpio_unit_); + if (result != noErr) { + RTCLogError(@"AudioComponentInstanceDispose failed. Error=%ld.", + (long)result); + } + vpio_unit_ = nullptr; + } +} + +} // namespace ios_adm +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/network_monitor_observer.h b/third_party/libwebrtc/sdk/objc/native/src/network_monitor_observer.h new file mode 100644 index 0000000000..7c411a1db1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/network_monitor_observer.h @@ -0,0 +1,42 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_NETWORK_MONITOR_OBSERVER_H_ +#define SDK_OBJC_NATIVE_SRC_NETWORK_MONITOR_OBSERVER_H_ + +#include <map> +#include <string> + +#include "absl/strings/string_view.h" +#include "rtc_base/network_constants.h" +#include "rtc_base/string_utils.h" +#include "rtc_base/thread.h" + +namespace webrtc { + +// Observer interface for listening to NWPathMonitor updates. +class NetworkMonitorObserver { + public: + // Called when a path update occurs, on network monitor dispatch queue. + // + // `adapter_type_by_name` is a map from interface name (i.e. "pdp_ip0") to + // adapter type, for all available interfaces on the current path. If an + // interface name isn't present it can be assumed to be unavailable. + virtual void OnPathUpdate( + std::map<std::string, rtc::AdapterType, rtc::AbslStringViewCmp> + adapter_type_by_name) = 0; + + protected: + virtual ~NetworkMonitorObserver() {} +}; + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_AUDIO_AUDIO_SESSION_OBSERVER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device.h b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device.h new file mode 100644 index 0000000000..fcfe7a6e8b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_AUDIO_DEVICE_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_AUDIO_DEVICE_H_ + +#include <memory> + +#import "components/audio/RTCAudioDevice.h" + +#include "modules/audio_device/audio_device_buffer.h" +#include "modules/audio_device/include/audio_device.h" +#include "rtc_base/thread.h" + +@class ObjCAudioDeviceDelegate; + +namespace webrtc { + +class FineAudioBuffer; + +namespace objc_adm { + +class ObjCAudioDeviceModule : public AudioDeviceModule { + public: + explicit ObjCAudioDeviceModule(id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device); + ~ObjCAudioDeviceModule() override; + + // Retrieve the currently utilized audio layer + int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override; + + // Full-duplex transportation of PCM audio + int32_t RegisterAudioCallback(AudioTransport* audioCallback) override; + + // Main initialization and termination + int32_t Init() override; + int32_t Terminate() override; + bool Initialized() const override; + + // Device enumeration + int16_t PlayoutDevices() override; + int16_t RecordingDevices() override; + int32_t PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) override; + int32_t RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) override; + + // Device selection + int32_t SetPlayoutDevice(uint16_t index) override; + int32_t SetPlayoutDevice(WindowsDeviceType device) override; + int32_t SetRecordingDevice(uint16_t index) override; + int32_t SetRecordingDevice(WindowsDeviceType device) override; + + // Audio transport initialization + int32_t PlayoutIsAvailable(bool* available) override; + int32_t InitPlayout() override; + bool PlayoutIsInitialized() const override; + int32_t RecordingIsAvailable(bool* available) override; + int32_t InitRecording() override; + bool RecordingIsInitialized() const override; + + // Audio transport control + int32_t StartPlayout() override; + int32_t StopPlayout() override; + bool Playing() const override; + int32_t StartRecording() override; + int32_t StopRecording() override; + bool Recording() const override; + + // Audio mixer initialization + int32_t InitSpeaker() override; + bool SpeakerIsInitialized() const override; + int32_t InitMicrophone() override; + bool MicrophoneIsInitialized() const override; + + // Speaker volume controls + int32_t SpeakerVolumeIsAvailable(bool* available) override; + int32_t SetSpeakerVolume(uint32_t volume) override; + int32_t SpeakerVolume(uint32_t* volume) const override; + int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override; + int32_t MinSpeakerVolume(uint32_t* minVolume) const override; + + // Microphone volume controls + int32_t MicrophoneVolumeIsAvailable(bool* available) override; + int32_t SetMicrophoneVolume(uint32_t volume) override; + int32_t MicrophoneVolume(uint32_t* volume) const override; + int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override; + int32_t MinMicrophoneVolume(uint32_t* minVolume) const override; + + // Speaker mute control + int32_t SpeakerMuteIsAvailable(bool* available) override; + int32_t SetSpeakerMute(bool enable) override; + int32_t SpeakerMute(bool* enabled) const override; + + // Microphone mute control + int32_t MicrophoneMuteIsAvailable(bool* available) override; + int32_t SetMicrophoneMute(bool enable) override; + int32_t MicrophoneMute(bool* enabled) const override; + + // Stereo support + int32_t StereoPlayoutIsAvailable(bool* available) const override; + int32_t SetStereoPlayout(bool enable) override; + int32_t StereoPlayout(bool* enabled) const override; + int32_t StereoRecordingIsAvailable(bool* available) const override; + int32_t SetStereoRecording(bool enable) override; + int32_t StereoRecording(bool* enabled) const override; + + // Playout delay + int32_t PlayoutDelay(uint16_t* delayMS) const override; + + // Only supported on Android. + bool BuiltInAECIsAvailable() const override; + bool BuiltInAGCIsAvailable() const override; + bool BuiltInNSIsAvailable() const override; + + // Enables the built-in audio effects. Only supported on Android. + int32_t EnableBuiltInAEC(bool enable) override; + int32_t EnableBuiltInAGC(bool enable) override; + int32_t EnableBuiltInNS(bool enable) override; + + // Play underrun count. Only supported on Android. + int32_t GetPlayoutUnderrunCount() const override; + +#if defined(WEBRTC_IOS) + int GetPlayoutAudioParameters(AudioParameters* params) const override; + int GetRecordAudioParameters(AudioParameters* params) const override; +#endif // WEBRTC_IOS + + public: + OSStatus OnDeliverRecordedData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + NSInteger bus_number, + UInt32 num_frames, + const AudioBufferList* io_data, + void* render_context, + RTC_OBJC_TYPE(RTCAudioDeviceRenderRecordedDataBlock) render_block); + + OSStatus OnGetPlayoutData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + NSInteger bus_number, + UInt32 num_frames, + AudioBufferList* io_data); + + // Notifies `ObjCAudioDeviceModule` that at least one of the audio input + // parameters or audio input latency of `RTCAudioDevice` has changed. It necessary to + // update `record_parameters_` with current audio parameter of `RTCAudioDevice` + // via `UpdateAudioParameters` and if parameters are actually change then + // ADB parameters are updated with `UpdateInputAudioDeviceBuffer`. Audio input latency + // stored in `cached_recording_delay_ms_` is also updated with current latency + // of `RTCAudioDevice`. + void HandleAudioInputParametersChange(); + + // Same as `HandleAudioInputParametersChange` but should be called when audio output + // parameters of `RTCAudioDevice` has changed. + void HandleAudioOutputParametersChange(); + + // Notifies `ObjCAudioDeviceModule` about audio input interruption happen due to + // any reason so `ObjCAudioDeviceModule` is can prepare to restart of audio IO. + void HandleAudioInputInterrupted(); + + // Same as `ObjCAudioDeviceModule` but should be called when audio output + // is interrupted. + void HandleAudioOutputInterrupted(); + + private: + // Update our audio parameters if they are different from current device audio parameters + // Returns true when our parameters are update, false - otherwise. + // `ObjCAudioDeviceModule` has audio device buffer (ADB) which has audio parameters + // of playout & recording. The ADB is configured to work with specific sample rate & channel + // count. `ObjCAudioDeviceModule` stores audio parameters which were used to configure ADB in the + // fields `playout_parameters_` and `recording_parameters_`. + // `RTCAudioDevice` protocol has its own audio parameters exposed as individual properties. + // `RTCAudioDevice` audio parameters might change when playout/recording is already in progress, + // for example, when device is switched. `RTCAudioDevice` audio parameters must be kept in sync + // with ADB audio parameters. This method is invoked when `RTCAudioDevice` reports that it's audio + // parameters (`device_params`) are changed and it detects if there any difference with our + // current audio parameters (`params`). Our parameters are updated in case of actual change and + // method returns true. In case of actual change there is follow-up call to either + // `UpdateOutputAudioDeviceBuffer` or `UpdateInputAudioDeviceBuffer` to apply updated + // `playout_parameters_` or `recording_parameters_` to ADB. + + bool UpdateAudioParameters(AudioParameters& params, const AudioParameters& device_params); + + // Update our cached audio latency with device latency. Device latency is reported by + // `RTCAudioDevice` object. Whenever latency is changed, `RTCAudioDevice` is obliged to notify ADM + // about the change via `HandleAudioInputParametersChange` or `HandleAudioOutputParametersChange`. + // Current device IO latency is cached in the atomic field and used from audio IO thread + // to be reported to audio device buffer. It is highly recommended by Apple not to call any + // ObjC methods from audio IO thread, that is why implementation relies on caching latency + // into a field and being notified when latency is changed, which is the case when device + // is switched. + void UpdateAudioDelay(std::atomic<int>& delay_ms, const NSTimeInterval device_latency); + + // Uses current `playout_parameters_` to inform the audio device buffer (ADB) + // about our internal audio parameters. + void UpdateOutputAudioDeviceBuffer(); + + // Uses current `record_parameters_` to inform the audio device buffer (ADB) + // about our internal audio parameters. + void UpdateInputAudioDeviceBuffer(); + + private: + id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device_; + + const std::unique_ptr<TaskQueueFactory> task_queue_factory_; + + // AudioDeviceBuffer is a buffer to consume audio recorded by `RTCAudioDevice` + // and provide audio to be played via `RTCAudioDevice`. + // Audio PCMs could have different sample rate and channels count, but expected + // to be in 16-bit integer interleaved linear PCM format. + // The current parameters ADB configured to work with is stored in field + // `playout_parameters_` for playout and `record_parameters_` for recording. + // These parameters and ADB must kept in sync with `RTCAudioDevice` audio parameters. + std::unique_ptr<AudioDeviceBuffer> audio_device_buffer_; + + // Set to 1 when recording is active and 0 otherwise. + std::atomic<bool> recording_ = false; + + // Set to 1 when playout is active and 0 otherwise. + std::atomic<bool> playing_ = false; + + // Stores cached value of `RTCAudioDevice outputLatency` to be used from + // audio IO thread. Latency is updated on audio output parameters change. + std::atomic<int> cached_playout_delay_ms_ = 0; + + // Same as `cached_playout_delay_ms_` but for audio input + std::atomic<int> cached_recording_delay_ms_ = 0; + + // Thread that is initialized audio device module. + rtc::Thread* thread_; + + // Ensures that methods are called from the same thread as this object is + // initialized on. + SequenceChecker thread_checker_; + + // I/O audio thread checker. + SequenceChecker io_playout_thread_checker_; + SequenceChecker io_record_thread_checker_; + + bool is_initialized_ RTC_GUARDED_BY(thread_checker_) = false; + bool is_playout_initialized_ RTC_GUARDED_BY(thread_checker_) = false; + bool is_recording_initialized_ RTC_GUARDED_BY(thread_checker_) = false; + + // Contains audio parameters (sample rate, #channels, buffer size etc.) for + // the playout and recording sides. + AudioParameters playout_parameters_; + AudioParameters record_parameters_; + + // `FineAudioBuffer` takes an `AudioDeviceBuffer` which delivers audio data + // in chunks of 10ms. `RTCAudioDevice` might deliver recorded data in + // chunks which are not 10ms long. `FineAudioBuffer` implements adaptation + // from undetermined chunk size to 10ms chunks. + std::unique_ptr<FineAudioBuffer> record_fine_audio_buffer_; + + // Same as `record_fine_audio_buffer_` but for audio output. + std::unique_ptr<FineAudioBuffer> playout_fine_audio_buffer_; + + // Temporary storage for recorded data. + rtc::BufferT<int16_t> record_audio_buffer_; + + // Delegate object provided to RTCAudioDevice during initialization + ObjCAudioDeviceDelegate* audio_device_delegate_; +}; + +} // namespace objc_adm + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_AUDIO_DEVICE_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device.mm new file mode 100644 index 0000000000..d629fae20f --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device.mm @@ -0,0 +1,711 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "objc_audio_device.h" +#include "objc_audio_device_delegate.h" + +#import "components/audio/RTCAudioDevice.h" +#include "modules/audio_device/fine_audio_buffer.h" + +#include "api/task_queue/default_task_queue_factory.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "rtc_base/time_utils.h" + +namespace { + +webrtc::AudioParameters RecordParameters(id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device) { + const double sample_rate = static_cast<int>([audio_device deviceInputSampleRate]); + const size_t channels = static_cast<size_t>([audio_device inputNumberOfChannels]); + const size_t frames_per_buffer = + static_cast<size_t>(sample_rate * [audio_device inputIOBufferDuration] + .5); + return webrtc::AudioParameters(sample_rate, channels, frames_per_buffer); +} + +webrtc::AudioParameters PlayoutParameters(id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device) { + const double sample_rate = static_cast<int>([audio_device deviceOutputSampleRate]); + const size_t channels = static_cast<size_t>([audio_device outputNumberOfChannels]); + const size_t frames_per_buffer = + static_cast<size_t>(sample_rate * [audio_device outputIOBufferDuration] + .5); + return webrtc::AudioParameters(sample_rate, channels, frames_per_buffer); +} + +} // namespace + +namespace webrtc { +namespace objc_adm { + +ObjCAudioDeviceModule::ObjCAudioDeviceModule(id<RTC_OBJC_TYPE(RTCAudioDevice)> audio_device) + : audio_device_(audio_device), task_queue_factory_(CreateDefaultTaskQueueFactory()) { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK(audio_device_); + thread_checker_.Detach(); + io_playout_thread_checker_.Detach(); + io_record_thread_checker_.Detach(); +} + +ObjCAudioDeviceModule::~ObjCAudioDeviceModule() { + RTC_DLOG_F(LS_VERBOSE) << ""; +} + +int32_t ObjCAudioDeviceModule::RegisterAudioCallback(AudioTransport* audioCallback) { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK(audio_device_buffer_); + return audio_device_buffer_->RegisterAudioCallback(audioCallback); +} + +int32_t ObjCAudioDeviceModule::Init() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + + if (Initialized()) { + RTC_LOG_F(LS_INFO) << "Already initialized"; + return 0; + } + io_playout_thread_checker_.Detach(); + io_record_thread_checker_.Detach(); + + thread_ = rtc::Thread::Current(); + audio_device_buffer_.reset(new webrtc::AudioDeviceBuffer(task_queue_factory_.get())); + + if (![audio_device_ isInitialized]) { + if (audio_device_delegate_ == nil) { + audio_device_delegate_ = [[ObjCAudioDeviceDelegate alloc] + initWithAudioDeviceModule:rtc::scoped_refptr<ObjCAudioDeviceModule>(this) + audioDeviceThread:thread_]; + } + + if (![audio_device_ initializeWithDelegate:audio_device_delegate_]) { + RTC_LOG_F(LS_WARNING) << "Failed to initialize audio device"; + [audio_device_delegate_ resetAudioDeviceModule]; + audio_device_delegate_ = nil; + return -1; + } + } + + playout_parameters_.reset([audio_device_delegate_ preferredOutputSampleRate], 1); + UpdateOutputAudioDeviceBuffer(); + + record_parameters_.reset([audio_device_delegate_ preferredInputSampleRate], 1); + UpdateInputAudioDeviceBuffer(); + + is_initialized_ = true; + + RTC_LOG_F(LS_INFO) << "Did initialize"; + return 0; +} + +int32_t ObjCAudioDeviceModule::Terminate() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + + if (!Initialized()) { + RTC_LOG_F(LS_INFO) << "Not initialized"; + return 0; + } + + if ([audio_device_ isInitialized]) { + if (![audio_device_ terminateDevice]) { + RTC_LOG_F(LS_ERROR) << "Failed to terminate audio device"; + return -1; + } + } + + if (audio_device_delegate_ != nil) { + [audio_device_delegate_ resetAudioDeviceModule]; + audio_device_delegate_ = nil; + } + + is_initialized_ = false; + is_playout_initialized_ = false; + is_recording_initialized_ = false; + thread_ = nullptr; + + RTC_LOG_F(LS_INFO) << "Did terminate"; + return 0; +} + +bool ObjCAudioDeviceModule::Initialized() const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + return is_initialized_ && [audio_device_ isInitialized]; +} + +int32_t ObjCAudioDeviceModule::PlayoutIsAvailable(bool* available) { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + *available = Initialized(); + return 0; +} + +bool ObjCAudioDeviceModule::PlayoutIsInitialized() const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + return Initialized() && is_playout_initialized_ && [audio_device_ isPlayoutInitialized]; +} + +int32_t ObjCAudioDeviceModule::InitPlayout() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + if (!Initialized()) { + return -1; + } + if (PlayoutIsInitialized()) { + return 0; + } + RTC_DCHECK(!playing_.load()); + + if (![audio_device_ isPlayoutInitialized]) { + if (![audio_device_ initializePlayout]) { + RTC_LOG_F(LS_ERROR) << "Failed to initialize audio device playout"; + return -1; + } + } + + if (UpdateAudioParameters(playout_parameters_, PlayoutParameters(audio_device_))) { + UpdateOutputAudioDeviceBuffer(); + } + + is_playout_initialized_ = true; + RTC_LOG_F(LS_INFO) << "Did initialize playout"; + return 0; +} + +bool ObjCAudioDeviceModule::Playing() const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + return playing_.load() && [audio_device_ isPlaying]; +} + +int32_t ObjCAudioDeviceModule::StartPlayout() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + if (!PlayoutIsInitialized()) { + return -1; + } + if (Playing()) { + return 0; + } + + audio_device_buffer_->StartPlayout(); + if (playout_fine_audio_buffer_) { + playout_fine_audio_buffer_->ResetPlayout(); + } + if (![audio_device_ startPlayout]) { + RTC_LOG_F(LS_ERROR) << "Failed to start audio device playout"; + return -1; + } + playing_.store(true, std::memory_order_release); + RTC_LOG_F(LS_INFO) << "Did start playout"; + return 0; +} + +int32_t ObjCAudioDeviceModule::StopPlayout() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + + if (![audio_device_ stopPlayout]) { + RTC_LOG_F(LS_WARNING) << "Failed to stop playout"; + return -1; + } + + audio_device_buffer_->StopPlayout(); + playing_.store(false, std::memory_order_release); + RTC_LOG_F(LS_INFO) << "Did stop playout"; + return 0; +} + +int32_t ObjCAudioDeviceModule::PlayoutDelay(uint16_t* delayMS) const { + RTC_DCHECK_RUN_ON(&thread_checker_); + *delayMS = static_cast<uint16_t>(rtc::SafeClamp<int>( + cached_playout_delay_ms_.load(), 0, std::numeric_limits<uint16_t>::max())); + return 0; +} + +int32_t ObjCAudioDeviceModule::RecordingIsAvailable(bool* available) { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + *available = Initialized(); + return 0; +} + +bool ObjCAudioDeviceModule::RecordingIsInitialized() const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + return Initialized() && is_recording_initialized_ && [audio_device_ isRecordingInitialized]; +} + +int32_t ObjCAudioDeviceModule::InitRecording() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + if (!Initialized()) { + return -1; + } + if (RecordingIsInitialized()) { + return 0; + } + RTC_DCHECK(!recording_.load()); + + if (![audio_device_ isRecordingInitialized]) { + if (![audio_device_ initializeRecording]) { + RTC_LOG_F(LS_ERROR) << "Failed to initialize audio device recording"; + return -1; + } + } + + if (UpdateAudioParameters(record_parameters_, RecordParameters(audio_device_))) { + UpdateInputAudioDeviceBuffer(); + } + + is_recording_initialized_ = true; + RTC_LOG_F(LS_INFO) << "Did initialize recording"; + return 0; +} + +bool ObjCAudioDeviceModule::Recording() const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + return recording_.load() && [audio_device_ isRecording]; +} + +int32_t ObjCAudioDeviceModule::StartRecording() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + if (!RecordingIsInitialized()) { + return -1; + } + if (Recording()) { + return 0; + } + + audio_device_buffer_->StartRecording(); + if (record_fine_audio_buffer_) { + record_fine_audio_buffer_->ResetRecord(); + } + + if (![audio_device_ startRecording]) { + RTC_LOG_F(LS_ERROR) << "Failed to start audio device recording"; + return -1; + } + recording_.store(true, std::memory_order_release); + RTC_LOG_F(LS_INFO) << "Did start recording"; + return 0; +} + +int32_t ObjCAudioDeviceModule::StopRecording() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + + if (![audio_device_ stopRecording]) { + RTC_LOG_F(LS_WARNING) << "Failed to stop recording"; + return -1; + } + audio_device_buffer_->StopRecording(); + recording_.store(false, std::memory_order_release); + RTC_LOG_F(LS_INFO) << "Did stop recording"; + return 0; +} + +#if defined(WEBRTC_IOS) + +int ObjCAudioDeviceModule::GetPlayoutAudioParameters(AudioParameters* params) const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK(playout_parameters_.is_valid()); + RTC_DCHECK_RUN_ON(&thread_checker_); + *params = playout_parameters_; + return 0; +} + +int ObjCAudioDeviceModule::GetRecordAudioParameters(AudioParameters* params) const { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK(record_parameters_.is_valid()); + RTC_DCHECK_RUN_ON(&thread_checker_); + *params = record_parameters_; + return 0; +} + +#endif // WEBRTC_IOS + +void ObjCAudioDeviceModule::UpdateOutputAudioDeviceBuffer() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; + + RTC_DCHECK_GT(playout_parameters_.sample_rate(), 0); + RTC_DCHECK(playout_parameters_.channels() == 1 || playout_parameters_.channels() == 2); + + audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); + audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); + playout_fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_.get())); +} + +void ObjCAudioDeviceModule::UpdateInputAudioDeviceBuffer() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; + + RTC_DCHECK_GT(record_parameters_.sample_rate(), 0); + RTC_DCHECK(record_parameters_.channels() == 1 || record_parameters_.channels() == 2); + + audio_device_buffer_->SetRecordingSampleRate(record_parameters_.sample_rate()); + audio_device_buffer_->SetRecordingChannels(record_parameters_.channels()); + record_fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_.get())); +} + +void ObjCAudioDeviceModule::UpdateAudioDelay(std::atomic<int>& delay_ms, + const NSTimeInterval device_latency) { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + int latency_ms = static_cast<int>(rtc::kNumMillisecsPerSec * device_latency); + if (latency_ms <= 0) { + return; + } + const int old_latency_ms = delay_ms.exchange(latency_ms); + if (old_latency_ms != latency_ms) { + RTC_LOG_F(LS_INFO) << "Did change audio IO latency from: " << old_latency_ms + << " ms to: " << latency_ms << " ms"; + } +} + +bool ObjCAudioDeviceModule::UpdateAudioParameters(AudioParameters& params, + const AudioParameters& device_params) { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + if (!device_params.is_complete()) { + RTC_LOG_F(LS_INFO) << "Device params are incomplete: " << device_params.ToString(); + return false; + } + if (params.channels() == device_params.channels() && + params.frames_per_buffer() == device_params.frames_per_buffer() && + params.sample_rate() == device_params.sample_rate()) { + RTC_LOG_F(LS_INFO) << "Device params: " << device_params.ToString() + << " are not different from: " << params.ToString(); + return false; + } + + RTC_LOG_F(LS_INFO) << "Audio params will be changed from: " << params.ToString() + << " to: " << device_params.ToString(); + params.reset( + device_params.sample_rate(), device_params.channels(), device_params.frames_per_buffer()); + return true; +} + +OSStatus ObjCAudioDeviceModule::OnDeliverRecordedData( + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + NSInteger bus_number, + UInt32 num_frames, + const AudioBufferList* io_data, + void* render_context, + RTC_OBJC_TYPE(RTCAudioDeviceRenderRecordedDataBlock) render_block) { + RTC_DCHECK_RUN_ON(&io_record_thread_checker_); + OSStatus result = noErr; + // Simply return if recording is not enabled. + if (!recording_.load()) return result; + + if (io_data != nullptr) { + // AudioBuffer already fullfilled with audio data + RTC_DCHECK_EQ(1, io_data->mNumberBuffers); + const AudioBuffer* audio_buffer = &io_data->mBuffers[0]; + RTC_DCHECK(audio_buffer->mNumberChannels == 1 || audio_buffer->mNumberChannels == 2); + + record_fine_audio_buffer_->DeliverRecordedData( + rtc::ArrayView<const int16_t>(static_cast<int16_t*>(audio_buffer->mData), num_frames), + cached_recording_delay_ms_.load()); + return noErr; + } + RTC_DCHECK(render_block != nullptr) << "Either io_data or render_block must be provided"; + + // Set the size of our own audio buffer and clear it first to avoid copying + // in combination with potential reallocations. + // On real iOS devices, the size will only be set once (at first callback). + const int channels_count = record_parameters_.channels(); + record_audio_buffer_.Clear(); + record_audio_buffer_.SetSize(num_frames * channels_count); + + // Allocate AudioBuffers to be used as storage for the received audio. + // The AudioBufferList structure works as a placeholder for the + // AudioBuffer structure, which holds a pointer to the actual data buffer + // in `record_audio_buffer_`. Recorded audio will be rendered into this memory + // at each input callback when calling `render_block`. + AudioBufferList audio_buffer_list; + audio_buffer_list.mNumberBuffers = 1; + AudioBuffer* audio_buffer = &audio_buffer_list.mBuffers[0]; + audio_buffer->mNumberChannels = channels_count; + audio_buffer->mDataByteSize = + record_audio_buffer_.size() * sizeof(decltype(record_audio_buffer_)::value_type); + audio_buffer->mData = reinterpret_cast<int8_t*>(record_audio_buffer_.data()); + + // Obtain the recorded audio samples by initiating a rendering cycle into own buffer. + result = + render_block(flags, time_stamp, bus_number, num_frames, &audio_buffer_list, render_context); + if (result != noErr) { + RTC_LOG_F(LS_ERROR) << "Failed to render audio: " << result; + return result; + } + + // Get a pointer to the recorded audio and send it to the WebRTC ADB. + // Use the FineAudioBuffer instance to convert between native buffer size + // and the 10ms buffer size used by WebRTC. + record_fine_audio_buffer_->DeliverRecordedData(record_audio_buffer_, + cached_recording_delay_ms_.load()); + return noErr; +} + +OSStatus ObjCAudioDeviceModule::OnGetPlayoutData(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* time_stamp, + NSInteger bus_number, + UInt32 num_frames, + AudioBufferList* io_data) { + RTC_DCHECK_RUN_ON(&io_playout_thread_checker_); + // Verify 16-bit, noninterleaved mono or stereo PCM signal format. + RTC_DCHECK_EQ(1, io_data->mNumberBuffers); + AudioBuffer* audio_buffer = &io_data->mBuffers[0]; + RTC_DCHECK(audio_buffer->mNumberChannels == 1 || audio_buffer->mNumberChannels == 2); + RTC_DCHECK_EQ(audio_buffer->mDataByteSize, + sizeof(int16_t) * num_frames * audio_buffer->mNumberChannels); + + // Produce silence and give player a hint about it if playout is not + // activated. + if (!playing_.load()) { + *flags |= kAudioUnitRenderAction_OutputIsSilence; + memset(static_cast<int8_t*>(audio_buffer->mData), 0, audio_buffer->mDataByteSize); + return noErr; + } + + // Read decoded 16-bit PCM samples from WebRTC into the + // `io_data` destination buffer. + playout_fine_audio_buffer_->GetPlayoutData( + rtc::ArrayView<int16_t>(static_cast<int16_t*>(audio_buffer->mData), + num_frames * audio_buffer->mNumberChannels), + cached_playout_delay_ms_.load()); + + return noErr; +} + +void ObjCAudioDeviceModule::HandleAudioInputInterrupted() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + io_record_thread_checker_.Detach(); +} + +void ObjCAudioDeviceModule::HandleAudioOutputInterrupted() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + io_playout_thread_checker_.Detach(); +} + +void ObjCAudioDeviceModule::HandleAudioInputParametersChange() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + + if (UpdateAudioParameters(record_parameters_, RecordParameters(audio_device_))) { + UpdateInputAudioDeviceBuffer(); + } + + UpdateAudioDelay(cached_recording_delay_ms_, [audio_device_ inputLatency]); +} + +void ObjCAudioDeviceModule::HandleAudioOutputParametersChange() { + RTC_DLOG_F(LS_VERBOSE) << ""; + RTC_DCHECK_RUN_ON(&thread_checker_); + + if (UpdateAudioParameters(playout_parameters_, PlayoutParameters(audio_device_))) { + UpdateOutputAudioDeviceBuffer(); + } + + UpdateAudioDelay(cached_playout_delay_ms_, [audio_device_ outputLatency]); +} + +#pragma mark - Not implemented/Not relevant methods from AudioDeviceModule + +int32_t ObjCAudioDeviceModule::ActiveAudioLayer(AudioLayer* audioLayer) const { + return -1; +} + +int16_t ObjCAudioDeviceModule::PlayoutDevices() { + return 0; +} + +int16_t ObjCAudioDeviceModule::RecordingDevices() { + return 0; +} + +int32_t ObjCAudioDeviceModule::PlayoutDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + return -1; +} + +int32_t ObjCAudioDeviceModule::RecordingDeviceName(uint16_t index, + char name[kAdmMaxDeviceNameSize], + char guid[kAdmMaxGuidSize]) { + return -1; +} + +int32_t ObjCAudioDeviceModule::SetPlayoutDevice(uint16_t index) { + return 0; +} + +int32_t ObjCAudioDeviceModule::SetPlayoutDevice(WindowsDeviceType device) { + return -1; +} + +int32_t ObjCAudioDeviceModule::SetRecordingDevice(uint16_t index) { + return 0; +} + +int32_t ObjCAudioDeviceModule::SetRecordingDevice(WindowsDeviceType device) { + return -1; +} + +int32_t ObjCAudioDeviceModule::InitSpeaker() { + return 0; +} + +bool ObjCAudioDeviceModule::SpeakerIsInitialized() const { + return true; +} + +int32_t ObjCAudioDeviceModule::InitMicrophone() { + return 0; +} + +bool ObjCAudioDeviceModule::MicrophoneIsInitialized() const { + return true; +} + +int32_t ObjCAudioDeviceModule::SpeakerVolumeIsAvailable(bool* available) { + *available = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::SetSpeakerVolume(uint32_t volume) { + return -1; +} + +int32_t ObjCAudioDeviceModule::SpeakerVolume(uint32_t* volume) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::MaxSpeakerVolume(uint32_t* maxVolume) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::MinSpeakerVolume(uint32_t* minVolume) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::SpeakerMuteIsAvailable(bool* available) { + *available = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::SetSpeakerMute(bool enable) { + return -1; +} + +int32_t ObjCAudioDeviceModule::SpeakerMute(bool* enabled) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::MicrophoneMuteIsAvailable(bool* available) { + *available = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::SetMicrophoneMute(bool enable) { + return -1; +} + +int32_t ObjCAudioDeviceModule::MicrophoneMute(bool* enabled) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::MicrophoneVolumeIsAvailable(bool* available) { + *available = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::SetMicrophoneVolume(uint32_t volume) { + return -1; +} + +int32_t ObjCAudioDeviceModule::MicrophoneVolume(uint32_t* volume) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::MaxMicrophoneVolume(uint32_t* maxVolume) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::MinMicrophoneVolume(uint32_t* minVolume) const { + return -1; +} + +int32_t ObjCAudioDeviceModule::StereoPlayoutIsAvailable(bool* available) const { + *available = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::SetStereoPlayout(bool enable) { + return -1; +} + +int32_t ObjCAudioDeviceModule::StereoPlayout(bool* enabled) const { + *enabled = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::StereoRecordingIsAvailable(bool* available) const { + *available = false; + return 0; +} + +int32_t ObjCAudioDeviceModule::SetStereoRecording(bool enable) { + return -1; +} + +int32_t ObjCAudioDeviceModule::StereoRecording(bool* enabled) const { + *enabled = false; + return 0; +} + +bool ObjCAudioDeviceModule::BuiltInAECIsAvailable() const { + return false; +} + +int32_t ObjCAudioDeviceModule::EnableBuiltInAEC(bool enable) { + return 0; +} + +bool ObjCAudioDeviceModule::BuiltInAGCIsAvailable() const { + return false; +} + +int32_t ObjCAudioDeviceModule::EnableBuiltInAGC(bool enable) { + return 0; +} + +bool ObjCAudioDeviceModule::BuiltInNSIsAvailable() const { + return false; +} + +int32_t ObjCAudioDeviceModule::EnableBuiltInNS(bool enable) { + return 0; +} + +int32_t ObjCAudioDeviceModule::GetPlayoutUnderrunCount() const { + return -1; +} + +} // namespace objc_adm + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device_delegate.h b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device_delegate.h new file mode 100644 index 0000000000..3af079dad9 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device_delegate.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_AUDIO_DEVICE_DELEGATE_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_AUDIO_DEVICE_DELEGATE_H_ + +#include "api/scoped_refptr.h" +#include "rtc_base/thread.h" + +#import "components/audio/RTCAudioDevice.h" + +namespace webrtc { +namespace objc_adm { +class ObjCAudioDeviceModule; +} // namespace objc_adm +} // namespace webrtc + +@interface ObjCAudioDeviceDelegate : NSObject <RTC_OBJC_TYPE (RTCAudioDeviceDelegate)> + +- (instancetype)initWithAudioDeviceModule: + (rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule>)audioDeviceModule + audioDeviceThread:(rtc::Thread*)thread; + +- (void)resetAudioDeviceModule; + +@end + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_AUDIO_DEVICE_DELEGATE_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device_delegate.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device_delegate.mm new file mode 100644 index 0000000000..156d6326a4 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_audio_device_delegate.mm @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AudioUnit/AudioUnit.h> +#import <Foundation/Foundation.h> + +#import "objc_audio_device.h" +#import "objc_audio_device_delegate.h" + +#include "api/make_ref_counted.h" +#include "api/ref_counted_base.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +namespace { + +constexpr double kPreferredInputSampleRate = 48000.0; +constexpr double kPreferredOutputSampleRate = 48000.0; + +// WebRTC processes audio in chunks of 10ms. Preferring 20ms audio chunks +// is a compromize between performance and power consumption. +constexpr NSTimeInterval kPeferredInputIOBufferDuration = 0.02; +constexpr NSTimeInterval kPeferredOutputIOBufferDuration = 0.02; + +class AudioDeviceDelegateImpl final : public rtc::RefCountedNonVirtual<AudioDeviceDelegateImpl> { + public: + AudioDeviceDelegateImpl( + rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule> audio_device_module, + rtc::Thread* thread) + : audio_device_module_(audio_device_module), thread_(thread) { + RTC_DCHECK(audio_device_module_); + RTC_DCHECK(thread_); + } + + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module() const { + return audio_device_module_.get(); + } + + rtc::Thread* thread() const { return thread_; } + + void reset_audio_device_module() { audio_device_module_ = nullptr; } + + private: + rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule> audio_device_module_; + rtc::Thread* thread_; +}; + +} // namespace + +@implementation ObjCAudioDeviceDelegate { + rtc::scoped_refptr<AudioDeviceDelegateImpl> impl_; +} + +@synthesize getPlayoutData = getPlayoutData_; + +@synthesize deliverRecordedData = deliverRecordedData_; + +@synthesize preferredInputSampleRate = preferredInputSampleRate_; + +@synthesize preferredInputIOBufferDuration = preferredInputIOBufferDuration_; + +@synthesize preferredOutputSampleRate = preferredOutputSampleRate_; + +@synthesize preferredOutputIOBufferDuration = preferredOutputIOBufferDuration_; + +- (instancetype)initWithAudioDeviceModule: + (rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule>)audioDeviceModule + audioDeviceThread:(rtc::Thread*)thread { + RTC_DCHECK_RUN_ON(thread); + if (self = [super init]) { + impl_ = rtc::make_ref_counted<AudioDeviceDelegateImpl>(audioDeviceModule, thread); + preferredInputSampleRate_ = kPreferredInputSampleRate; + preferredInputIOBufferDuration_ = kPeferredInputIOBufferDuration; + preferredOutputSampleRate_ = kPreferredOutputSampleRate; + preferredOutputIOBufferDuration_ = kPeferredOutputIOBufferDuration; + + rtc::scoped_refptr<AudioDeviceDelegateImpl> playout_delegate = impl_; + getPlayoutData_ = ^OSStatus(AudioUnitRenderActionFlags* _Nonnull actionFlags, + const AudioTimeStamp* _Nonnull timestamp, + NSInteger inputBusNumber, + UInt32 frameCount, + AudioBufferList* _Nonnull outputData) { + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device = + playout_delegate->audio_device_module(); + if (audio_device) { + return audio_device->OnGetPlayoutData( + actionFlags, timestamp, inputBusNumber, frameCount, outputData); + } else { + *actionFlags |= kAudioUnitRenderAction_OutputIsSilence; + RTC_LOG(LS_VERBOSE) << "No alive audio device"; + return noErr; + } + }; + + rtc::scoped_refptr<AudioDeviceDelegateImpl> record_delegate = impl_; + deliverRecordedData_ = + ^OSStatus(AudioUnitRenderActionFlags* _Nonnull actionFlags, + const AudioTimeStamp* _Nonnull timestamp, + NSInteger inputBusNumber, + UInt32 frameCount, + const AudioBufferList* _Nullable inputData, + void* renderContext, + RTC_OBJC_TYPE(RTCAudioDeviceRenderRecordedDataBlock) _Nullable renderBlock) { + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device = + record_delegate->audio_device_module(); + if (audio_device) { + return audio_device->OnDeliverRecordedData(actionFlags, + timestamp, + inputBusNumber, + frameCount, + inputData, + renderContext, + renderBlock); + } else { + RTC_LOG(LS_VERBOSE) << "No alive audio device"; + return noErr; + } + }; + } + return self; +} + +- (void)notifyAudioInputParametersChange { + RTC_DCHECK_RUN_ON(impl_->thread()); + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); + if (audio_device_module) { + audio_device_module->HandleAudioInputParametersChange(); + } +} + +- (void)notifyAudioOutputParametersChange { + RTC_DCHECK_RUN_ON(impl_->thread()); + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); + if (audio_device_module) { + audio_device_module->HandleAudioOutputParametersChange(); + } +} + +- (void)notifyAudioInputInterrupted { + RTC_DCHECK_RUN_ON(impl_->thread()); + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); + if (audio_device_module) { + audio_device_module->HandleAudioInputInterrupted(); + } +} + +- (void)notifyAudioOutputInterrupted { + RTC_DCHECK_RUN_ON(impl_->thread()); + webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); + if (audio_device_module) { + audio_device_module->HandleAudioOutputInterrupted(); + } +} + +- (void)dispatchAsync:(dispatch_block_t)block { + rtc::Thread* thread = impl_->thread(); + RTC_DCHECK(thread); + thread->PostTask([block] { + @autoreleasepool { + block(); + } + }); +} + +- (void)dispatchSync:(dispatch_block_t)block { + rtc::Thread* thread = impl_->thread(); + RTC_DCHECK(thread); + if (thread->IsCurrent()) { + @autoreleasepool { + block(); + } + } else { + thread->BlockingCall([block] { + @autoreleasepool { + block(); + } + }); + } +} + +- (void)resetAudioDeviceModule { + RTC_DCHECK_RUN_ON(impl_->thread()); + impl_->reset_audio_device_module(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_frame_buffer.h b/third_party/libwebrtc/sdk/objc/native/src/objc_frame_buffer.h new file mode 100644 index 0000000000..944690c8bc --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_frame_buffer.h @@ -0,0 +1,56 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_FRAME_BUFFER_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_FRAME_BUFFER_H_ + +#import <CoreVideo/CoreVideo.h> + +#import "base/RTCMacros.h" + +#include "common_video/include/video_frame_buffer.h" + +@protocol RTC_OBJC_TYPE +(RTCVideoFrameBuffer); + +namespace webrtc { + +class ObjCFrameBuffer : public VideoFrameBuffer { + public: + explicit ObjCFrameBuffer(id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)>); + ~ObjCFrameBuffer() override; + + Type type() const override; + + int width() const override; + int height() const override; + + rtc::scoped_refptr<I420BufferInterface> ToI420() override; + rtc::scoped_refptr<VideoFrameBuffer> CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) override; + + id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> wrapped_frame_buffer() const; + + private: + id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> frame_buffer_; + int width_; + int height_; +}; + +id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> ToObjCVideoFrameBuffer( + const rtc::scoped_refptr<VideoFrameBuffer>& buffer); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_FRAME_BUFFER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_frame_buffer.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_frame_buffer.mm new file mode 100644 index 0000000000..00e4b4be85 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_frame_buffer.mm @@ -0,0 +1,107 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_frame_buffer.h" + +#include "api/make_ref_counted.h" +#import "base/RTCVideoFrameBuffer.h" +#import "sdk/objc/api/video_frame_buffer/RTCNativeI420Buffer+Private.h" + +namespace webrtc { + +namespace { + +/** ObjCFrameBuffer that conforms to I420BufferInterface by wrapping RTC_OBJC_TYPE(RTCI420Buffer) */ +class ObjCI420FrameBuffer : public I420BufferInterface { + public: + explicit ObjCI420FrameBuffer(id<RTC_OBJC_TYPE(RTCI420Buffer)> frame_buffer) + : frame_buffer_(frame_buffer), width_(frame_buffer.width), height_(frame_buffer.height) {} + ~ObjCI420FrameBuffer() override {} + + int width() const override { return width_; } + + int height() const override { return height_; } + + const uint8_t* DataY() const override { return frame_buffer_.dataY; } + + const uint8_t* DataU() const override { return frame_buffer_.dataU; } + + const uint8_t* DataV() const override { return frame_buffer_.dataV; } + + int StrideY() const override { return frame_buffer_.strideY; } + + int StrideU() const override { return frame_buffer_.strideU; } + + int StrideV() const override { return frame_buffer_.strideV; } + + private: + id<RTC_OBJC_TYPE(RTCI420Buffer)> frame_buffer_; + int width_; + int height_; +}; + +} // namespace + +ObjCFrameBuffer::ObjCFrameBuffer(id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> frame_buffer) + : frame_buffer_(frame_buffer), width_(frame_buffer.width), height_(frame_buffer.height) {} + +ObjCFrameBuffer::~ObjCFrameBuffer() {} + +VideoFrameBuffer::Type ObjCFrameBuffer::type() const { + return Type::kNative; +} + +int ObjCFrameBuffer::width() const { + return width_; +} + +int ObjCFrameBuffer::height() const { + return height_; +} + +rtc::scoped_refptr<I420BufferInterface> ObjCFrameBuffer::ToI420() { + return rtc::make_ref_counted<ObjCI420FrameBuffer>([frame_buffer_ toI420]); +} + +rtc::scoped_refptr<VideoFrameBuffer> ObjCFrameBuffer::CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) { + if ([frame_buffer_ respondsToSelector:@selector + (cropAndScaleWith:offsetY:cropWidth:cropHeight:scaleWidth:scaleHeight:)]) { + return rtc::make_ref_counted<ObjCFrameBuffer>([frame_buffer_ cropAndScaleWith:offset_x + offsetY:offset_y + cropWidth:crop_width + cropHeight:crop_height + scaleWidth:scaled_width + scaleHeight:scaled_height]); + } + + // Use the default implementation. + return VideoFrameBuffer::CropAndScale( + offset_x, offset_y, crop_width, crop_height, scaled_width, scaled_height); +} + +id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> ObjCFrameBuffer::wrapped_frame_buffer() const { + return frame_buffer_; +} + +id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> ToObjCVideoFrameBuffer( + const rtc::scoped_refptr<VideoFrameBuffer>& buffer) { + if (buffer->type() == VideoFrameBuffer::Type::kNative) { + return static_cast<ObjCFrameBuffer*>(buffer.get())->wrapped_frame_buffer(); + } else { + return [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:buffer->ToI420()]; + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_network_monitor.h b/third_party/libwebrtc/sdk/objc/native/src/objc_network_monitor.h new file mode 100644 index 0000000000..709e9dfbe5 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_network_monitor.h @@ -0,0 +1,67 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_NETWORK_MONITOR_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_NETWORK_MONITOR_H_ + +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/field_trials_view.h" +#include "api/sequence_checker.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/network_monitor.h" +#include "rtc_base/network_monitor_factory.h" +#include "rtc_base/string_utils.h" +#include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" +#include "sdk/objc/components/network/RTCNetworkMonitor+Private.h" +#include "sdk/objc/native/src/network_monitor_observer.h" + +namespace webrtc { + +class ObjCNetworkMonitorFactory : public rtc::NetworkMonitorFactory { + public: + ObjCNetworkMonitorFactory() = default; + ~ObjCNetworkMonitorFactory() override = default; + + rtc::NetworkMonitorInterface* CreateNetworkMonitor( + const FieldTrialsView& field_trials) override; +}; + +class ObjCNetworkMonitor : public rtc::NetworkMonitorInterface, + public NetworkMonitorObserver { + public: + ObjCNetworkMonitor(); + ~ObjCNetworkMonitor() override; + + void Start() override; + void Stop() override; + + InterfaceInfo GetInterfaceInfo(absl::string_view interface_name) override; + + // NetworkMonitorObserver override. + // Fans out updates to observers on the correct thread. + void OnPathUpdate( + std::map<std::string, rtc::AdapterType, rtc::AbslStringViewCmp> + adapter_type_by_name) override; + + private: + rtc::Thread* thread_ = nullptr; + bool started_ = false; + std::map<std::string, rtc::AdapterType, rtc::AbslStringViewCmp> + adapter_type_by_name_ RTC_GUARDED_BY(thread_); + rtc::scoped_refptr<PendingTaskSafetyFlag> safety_flag_; + RTCNetworkMonitor* network_monitor_ = nil; +}; + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_NETWORK_MONITOR_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_network_monitor.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_network_monitor.mm new file mode 100644 index 0000000000..535548c64c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_network_monitor.mm @@ -0,0 +1,95 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_network_monitor.h" +#include "absl/strings/string_view.h" + +#include <algorithm> + +#include "rtc_base/logging.h" +#include "rtc_base/string_utils.h" + +namespace webrtc { + +rtc::NetworkMonitorInterface* ObjCNetworkMonitorFactory::CreateNetworkMonitor( + const FieldTrialsView& field_trials) { + return new ObjCNetworkMonitor(); +} + +ObjCNetworkMonitor::ObjCNetworkMonitor() { + safety_flag_ = PendingTaskSafetyFlag::Create(); +} + +ObjCNetworkMonitor::~ObjCNetworkMonitor() { + [network_monitor_ stop]; + network_monitor_ = nil; +} + +void ObjCNetworkMonitor::Start() { + if (started_) { + return; + } + thread_ = rtc::Thread::Current(); + RTC_DCHECK_RUN_ON(thread_); + safety_flag_->SetAlive(); + network_monitor_ = [[RTCNetworkMonitor alloc] initWithObserver:this]; + if (network_monitor_ == nil) { + RTC_LOG(LS_WARNING) << "Failed to create RTCNetworkMonitor; not available on this OS?"; + } + started_ = true; +} + +void ObjCNetworkMonitor::Stop() { + RTC_DCHECK_RUN_ON(thread_); + if (!started_) { + return; + } + safety_flag_->SetNotAlive(); + [network_monitor_ stop]; + network_monitor_ = nil; + started_ = false; +} + +rtc::NetworkMonitorInterface::InterfaceInfo ObjCNetworkMonitor::GetInterfaceInfo( + absl::string_view interface_name) { + RTC_DCHECK_RUN_ON(thread_); + if (adapter_type_by_name_.empty()) { + // If we have no path update, assume everything's available, because it's + // preferable for WebRTC to try all interfaces rather than none at all. + return { + .adapter_type = rtc::ADAPTER_TYPE_UNKNOWN, + .available = true, + }; + } + auto iter = adapter_type_by_name_.find(interface_name); + if (iter == adapter_type_by_name_.end()) { + return { + .adapter_type = rtc::ADAPTER_TYPE_UNKNOWN, + .available = false, + }; + } + + return { + .adapter_type = iter->second, + .available = true, + }; +} + +void ObjCNetworkMonitor::OnPathUpdate( + std::map<std::string, rtc::AdapterType, rtc::AbslStringViewCmp> adapter_type_by_name) { + RTC_DCHECK(network_monitor_ != nil); + thread_->PostTask(SafeTask(safety_flag_, [this, adapter_type_by_name] { + RTC_DCHECK_RUN_ON(thread_); + adapter_type_by_name_ = adapter_type_by_name; + InvokeNetworksChangedCallback(); + })); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_decoder_factory.h b/third_party/libwebrtc/sdk/objc/native/src/objc_video_decoder_factory.h new file mode 100644 index 0000000000..30ad8c2a4b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_decoder_factory.h @@ -0,0 +1,41 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_DECODER_FACTORY_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_DECODER_FACTORY_H_ + +#import "base/RTCMacros.h" + +#include "api/video_codecs/video_decoder_factory.h" +#include "media/base/codec.h" + +@protocol RTC_OBJC_TYPE +(RTCVideoDecoderFactory); + +namespace webrtc { + +class ObjCVideoDecoderFactory : public VideoDecoderFactory { + public: + explicit ObjCVideoDecoderFactory(id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)>); + ~ObjCVideoDecoderFactory() override; + + id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> wrapped_decoder_factory() const; + + std::vector<SdpVideoFormat> GetSupportedFormats() const override; + std::unique_ptr<VideoDecoder> CreateVideoDecoder( + const SdpVideoFormat& format) override; + + private: + id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> decoder_factory_; +}; + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_DECODER_FACTORY_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_decoder_factory.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_video_decoder_factory.mm new file mode 100644 index 0000000000..da3b302275 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_decoder_factory.mm @@ -0,0 +1,123 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_video_decoder_factory.h" + +#import "base/RTCMacros.h" +#import "base/RTCVideoDecoder.h" +#import "base/RTCVideoDecoderFactory.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_codec/RTCCodecSpecificInfoH264.h" +#import "sdk/objc/api/peerconnection/RTCEncodedImage+Private.h" +#import "sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.h" +#import "sdk/objc/api/video_codec/RTCWrappedNativeVideoDecoder.h" +#import "sdk/objc/helpers/NSString+StdString.h" + +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_decoder.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "sdk/objc/native/src/objc_frame_buffer.h" + +namespace webrtc { + +namespace { +class ObjCVideoDecoder : public VideoDecoder { + public: + ObjCVideoDecoder(id<RTC_OBJC_TYPE(RTCVideoDecoder)> decoder) + : decoder_(decoder), implementation_name_([decoder implementationName].stdString) {} + + bool Configure(const Settings &settings) override { + return + [decoder_ startDecodeWithNumberOfCores:settings.number_of_cores()] == WEBRTC_VIDEO_CODEC_OK; + } + + int32_t Decode(const EncodedImage &input_image, + bool missing_frames, + int64_t render_time_ms = -1) override { + RTC_OBJC_TYPE(RTCEncodedImage) *encodedImage = + [[RTC_OBJC_TYPE(RTCEncodedImage) alloc] initWithNativeEncodedImage:input_image]; + + return [decoder_ decode:encodedImage + missingFrames:missing_frames + codecSpecificInfo:nil + renderTimeMs:render_time_ms]; + } + + int32_t RegisterDecodeCompleteCallback(DecodedImageCallback *callback) override { + [decoder_ setCallback:^(RTC_OBJC_TYPE(RTCVideoFrame) * frame) { + const auto buffer = rtc::make_ref_counted<ObjCFrameBuffer>(frame.buffer); + VideoFrame videoFrame = + VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_timestamp_rtp((uint32_t)(frame.timeStampNs / rtc::kNumNanosecsPerMicrosec)) + .set_timestamp_ms(0) + .set_rotation((VideoRotation)frame.rotation) + .build(); + videoFrame.set_timestamp(frame.timeStamp); + + callback->Decoded(videoFrame); + }]; + + return WEBRTC_VIDEO_CODEC_OK; + } + + int32_t Release() override { return [decoder_ releaseDecoder]; } + + const char *ImplementationName() const override { return implementation_name_.c_str(); } + + private: + id<RTC_OBJC_TYPE(RTCVideoDecoder)> decoder_; + const std::string implementation_name_; +}; +} // namespace + +ObjCVideoDecoderFactory::ObjCVideoDecoderFactory( + id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> decoder_factory) + : decoder_factory_(decoder_factory) {} + +ObjCVideoDecoderFactory::~ObjCVideoDecoderFactory() {} + +id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> ObjCVideoDecoderFactory::wrapped_decoder_factory() const { + return decoder_factory_; +} + +std::unique_ptr<VideoDecoder> ObjCVideoDecoderFactory::CreateVideoDecoder( + const SdpVideoFormat &format) { + NSString *codecName = [NSString stringWithUTF8String:format.name.c_str()]; + for (RTC_OBJC_TYPE(RTCVideoCodecInfo) * codecInfo in decoder_factory_.supportedCodecs) { + if ([codecName isEqualToString:codecInfo.name]) { + id<RTC_OBJC_TYPE(RTCVideoDecoder)> decoder = [decoder_factory_ createDecoder:codecInfo]; + + if ([decoder isKindOfClass:[RTC_OBJC_TYPE(RTCWrappedNativeVideoDecoder) class]]) { + return [(RTC_OBJC_TYPE(RTCWrappedNativeVideoDecoder) *)decoder releaseWrappedDecoder]; + } else { + return std::unique_ptr<ObjCVideoDecoder>(new ObjCVideoDecoder(decoder)); + } + } + } + + return nullptr; +} + +std::vector<SdpVideoFormat> ObjCVideoDecoderFactory::GetSupportedFormats() const { + std::vector<SdpVideoFormat> supported_formats; + for (RTC_OBJC_TYPE(RTCVideoCodecInfo) * supportedCodec in decoder_factory_.supportedCodecs) { + SdpVideoFormat format = [supportedCodec nativeSdpVideoFormat]; + supported_formats.push_back(format); + } + + return supported_formats; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_encoder_factory.h b/third_party/libwebrtc/sdk/objc/native/src/objc_video_encoder_factory.h new file mode 100644 index 0000000000..38db5e6ae7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_encoder_factory.h @@ -0,0 +1,44 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_ENCODER_FACTORY_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_ENCODER_FACTORY_H_ + +#import <Foundation/Foundation.h> + +#import "base/RTCMacros.h" + +#include "api/video_codecs/video_encoder_factory.h" + +@protocol RTC_OBJC_TYPE +(RTCVideoEncoderFactory); + +namespace webrtc { + +class ObjCVideoEncoderFactory : public VideoEncoderFactory { + public: + explicit ObjCVideoEncoderFactory(id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)>); + ~ObjCVideoEncoderFactory() override; + + id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> wrapped_encoder_factory() const; + + std::vector<SdpVideoFormat> GetSupportedFormats() const override; + std::vector<SdpVideoFormat> GetImplementations() const override; + std::unique_ptr<VideoEncoder> CreateVideoEncoder( + const SdpVideoFormat& format) override; + std::unique_ptr<EncoderSelectorInterface> GetEncoderSelector() const override; + + private: + id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> encoder_factory_; +}; + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_ENCODER_FACTORY_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_encoder_factory.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_video_encoder_factory.mm new file mode 100644 index 0000000000..d4ea79cc88 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_encoder_factory.mm @@ -0,0 +1,209 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_video_encoder_factory.h" + +#include <string> + +#import "base/RTCMacros.h" +#import "base/RTCVideoEncoder.h" +#import "base/RTCVideoEncoderFactory.h" +#import "components/video_codec/RTCCodecSpecificInfoH264+Private.h" +#import "sdk/objc/api/peerconnection/RTCEncodedImage+Private.h" +#import "sdk/objc/api/peerconnection/RTCVideoCodecInfo+Private.h" +#import "sdk/objc/api/peerconnection/RTCVideoEncoderSettings+Private.h" +#import "sdk/objc/api/video_codec/RTCVideoCodecConstants.h" +#import "sdk/objc/api/video_codec/RTCWrappedNativeVideoEncoder.h" +#import "sdk/objc/helpers/NSString+StdString.h" + +#include "api/video/video_frame.h" +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/logging.h" +#include "sdk/objc/native/src/objc_video_frame.h" + +namespace webrtc { + +namespace { + +class ObjCVideoEncoder : public VideoEncoder { + public: + ObjCVideoEncoder(id<RTC_OBJC_TYPE(RTCVideoEncoder)> encoder) + : encoder_(encoder), implementation_name_([encoder implementationName].stdString) {} + + int32_t InitEncode(const VideoCodec *codec_settings, const Settings &encoder_settings) override { + RTC_OBJC_TYPE(RTCVideoEncoderSettings) *settings = + [[RTC_OBJC_TYPE(RTCVideoEncoderSettings) alloc] initWithNativeVideoCodec:codec_settings]; + return [encoder_ startEncodeWithSettings:settings + numberOfCores:encoder_settings.number_of_cores]; + } + + int32_t RegisterEncodeCompleteCallback(EncodedImageCallback *callback) override { + if (callback) { + [encoder_ setCallback:^BOOL(RTC_OBJC_TYPE(RTCEncodedImage) * _Nonnull frame, + id<RTC_OBJC_TYPE(RTCCodecSpecificInfo)> _Nonnull info) { + EncodedImage encodedImage = [frame nativeEncodedImage]; + + // Handle types that can be converted into one of CodecSpecificInfo's hard coded cases. + CodecSpecificInfo codecSpecificInfo; + if ([info isKindOfClass:[RTC_OBJC_TYPE(RTCCodecSpecificInfoH264) class]]) { + codecSpecificInfo = + [(RTC_OBJC_TYPE(RTCCodecSpecificInfoH264) *)info nativeCodecSpecificInfo]; + } + + EncodedImageCallback::Result res = callback->OnEncodedImage(encodedImage, &codecSpecificInfo); + return res.error == EncodedImageCallback::Result::OK; + }]; + } else { + [encoder_ setCallback:nil]; + } + return WEBRTC_VIDEO_CODEC_OK; + } + + int32_t Release() override { return [encoder_ releaseEncoder]; } + + int32_t Encode(const VideoFrame &frame, + const std::vector<VideoFrameType> *frame_types) override { + NSMutableArray<NSNumber *> *rtcFrameTypes = [NSMutableArray array]; + for (size_t i = 0; i < frame_types->size(); ++i) { + [rtcFrameTypes addObject:@(RTCFrameType(frame_types->at(i)))]; + } + + return [encoder_ encode:ToObjCVideoFrame(frame) + codecSpecificInfo:nil + frameTypes:rtcFrameTypes]; + } + + void SetRates(const RateControlParameters ¶meters) override { + const uint32_t bitrate = parameters.bitrate.get_sum_kbps(); + const uint32_t framerate = static_cast<uint32_t>(parameters.framerate_fps + 0.5); + [encoder_ setBitrate:bitrate framerate:framerate]; + } + + VideoEncoder::EncoderInfo GetEncoderInfo() const override { + EncoderInfo info; + info.implementation_name = implementation_name_; + + RTC_OBJC_TYPE(RTCVideoEncoderQpThresholds) *qp_thresholds = [encoder_ scalingSettings]; + info.scaling_settings = qp_thresholds ? ScalingSettings(qp_thresholds.low, qp_thresholds.high) : + ScalingSettings::kOff; + + info.requested_resolution_alignment = encoder_.resolutionAlignment > 0 ?: 1; + info.apply_alignment_to_all_simulcast_layers = encoder_.applyAlignmentToAllSimulcastLayers; + info.supports_native_handle = encoder_.supportsNativeHandle; + info.is_hardware_accelerated = true; + return info; + } + + private: + id<RTC_OBJC_TYPE(RTCVideoEncoder)> encoder_; + const std::string implementation_name_; +}; + +class ObjcVideoEncoderSelector : public VideoEncoderFactory::EncoderSelectorInterface { + public: + ObjcVideoEncoderSelector(id<RTC_OBJC_TYPE(RTCVideoEncoderSelector)> selector) { + selector_ = selector; + } + void OnCurrentEncoder(const SdpVideoFormat &format) override { + RTC_OBJC_TYPE(RTCVideoCodecInfo) *info = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithNativeSdpVideoFormat:format]; + [selector_ registerCurrentEncoderInfo:info]; + } + absl::optional<SdpVideoFormat> OnEncoderBroken() override { + RTC_OBJC_TYPE(RTCVideoCodecInfo) *info = [selector_ encoderForBrokenEncoder]; + if (info) { + return [info nativeSdpVideoFormat]; + } + return absl::nullopt; + } + absl::optional<SdpVideoFormat> OnAvailableBitrate(const DataRate &rate) override { + RTC_OBJC_TYPE(RTCVideoCodecInfo) *info = [selector_ encoderForBitrate:rate.kbps<NSInteger>()]; + if (info) { + return [info nativeSdpVideoFormat]; + } + return absl::nullopt; + } + + absl::optional<SdpVideoFormat> OnResolutionChange(const RenderResolution &resolution) override { + if ([selector_ respondsToSelector:@selector(encoderForResolutionChangeBySize:)]) { + RTC_OBJC_TYPE(RTCVideoCodecInfo) *info = [selector_ + encoderForResolutionChangeBySize:CGSizeMake(resolution.Width(), resolution.Height())]; + if (info) { + return [info nativeSdpVideoFormat]; + } + } + return absl::nullopt; + } + + private: + id<RTC_OBJC_TYPE(RTCVideoEncoderSelector)> selector_; +}; + +} // namespace + +ObjCVideoEncoderFactory::ObjCVideoEncoderFactory( + id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> encoder_factory) + : encoder_factory_(encoder_factory) {} + +ObjCVideoEncoderFactory::~ObjCVideoEncoderFactory() {} + +id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> ObjCVideoEncoderFactory::wrapped_encoder_factory() const { + return encoder_factory_; +} + +std::vector<SdpVideoFormat> ObjCVideoEncoderFactory::GetSupportedFormats() const { + std::vector<SdpVideoFormat> supported_formats; + for (RTC_OBJC_TYPE(RTCVideoCodecInfo) * supportedCodec in [encoder_factory_ supportedCodecs]) { + SdpVideoFormat format = [supportedCodec nativeSdpVideoFormat]; + supported_formats.push_back(format); + } + + return supported_formats; +} + +std::vector<SdpVideoFormat> ObjCVideoEncoderFactory::GetImplementations() const { + if ([encoder_factory_ respondsToSelector:@selector(implementations)]) { + std::vector<SdpVideoFormat> supported_formats; + for (RTC_OBJC_TYPE(RTCVideoCodecInfo) * supportedCodec in [encoder_factory_ implementations]) { + SdpVideoFormat format = [supportedCodec nativeSdpVideoFormat]; + supported_formats.push_back(format); + } + return supported_formats; + } + return GetSupportedFormats(); +} + +std::unique_ptr<VideoEncoder> ObjCVideoEncoderFactory::CreateVideoEncoder( + const SdpVideoFormat &format) { + RTC_OBJC_TYPE(RTCVideoCodecInfo) *info = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithNativeSdpVideoFormat:format]; + id<RTC_OBJC_TYPE(RTCVideoEncoder)> encoder = [encoder_factory_ createEncoder:info]; + if ([encoder isKindOfClass:[RTC_OBJC_TYPE(RTCWrappedNativeVideoEncoder) class]]) { + return [(RTC_OBJC_TYPE(RTCWrappedNativeVideoEncoder) *)encoder releaseWrappedEncoder]; + } else { + return std::unique_ptr<ObjCVideoEncoder>(new ObjCVideoEncoder(encoder)); + } +} + +std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface> + ObjCVideoEncoderFactory::GetEncoderSelector() const { + if ([encoder_factory_ respondsToSelector:@selector(encoderSelector)]) { + id<RTC_OBJC_TYPE(RTCVideoEncoderSelector)> selector = [encoder_factory_ encoderSelector]; + if (selector) { + return absl::make_unique<ObjcVideoEncoderSelector>(selector); + } + } + return nullptr; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_frame.h b/third_party/libwebrtc/sdk/objc/native/src/objc_video_frame.h new file mode 100644 index 0000000000..c2931cb2f8 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_frame.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_FRAME_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_FRAME_H_ + +#import "base/RTCVideoFrame.h" + +#include "api/video/video_frame.h" + +namespace webrtc { + +RTC_OBJC_TYPE(RTCVideoFrame) * ToObjCVideoFrame(const VideoFrame& frame); + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_FRAME_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_frame.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_video_frame.mm new file mode 100644 index 0000000000..2e8ce6153e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_frame.mm @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_video_frame.h" + +#include "rtc_base/time_utils.h" +#include "sdk/objc/native/src/objc_frame_buffer.h" + +namespace webrtc { + +RTC_OBJC_TYPE(RTCVideoFrame) * ToObjCVideoFrame(const VideoFrame &frame) { + RTC_OBJC_TYPE(RTCVideoFrame) *videoFrame = [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] + initWithBuffer:ToObjCVideoFrameBuffer(frame.video_frame_buffer()) + rotation:RTCVideoRotation(frame.rotation()) + timeStampNs:frame.timestamp_us() * rtc::kNumNanosecsPerMicrosec]; + videoFrame.timeStamp = frame.timestamp(); + + return videoFrame; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_renderer.h b/third_party/libwebrtc/sdk/objc/native/src/objc_video_renderer.h new file mode 100644 index 0000000000..f9c35eae96 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_renderer.h @@ -0,0 +1,39 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_RENDERER_H_ +#define SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_RENDERER_H_ + +#import <CoreGraphics/CoreGraphics.h> +#import <Foundation/Foundation.h> + +#import "base/RTCMacros.h" + +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" + +@protocol RTC_OBJC_TYPE +(RTCVideoRenderer); + +namespace webrtc { + +class ObjCVideoRenderer : public rtc::VideoSinkInterface<VideoFrame> { + public: + ObjCVideoRenderer(id<RTC_OBJC_TYPE(RTCVideoRenderer)> renderer); + void OnFrame(const VideoFrame& nativeVideoFrame) override; + + private: + id<RTC_OBJC_TYPE(RTCVideoRenderer)> renderer_; + CGSize size_; +}; + +} // namespace webrtc + +#endif // SDK_OBJC_NATIVE_SRC_OBJC_VIDEO_RENDERER_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_renderer.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_video_renderer.mm new file mode 100644 index 0000000000..4a9b647ec3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_renderer.mm @@ -0,0 +1,38 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_video_renderer.h" + +#import "base/RTCMacros.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoRenderer.h" + +#include "sdk/objc/native/src/objc_video_frame.h" + +namespace webrtc { + +ObjCVideoRenderer::ObjCVideoRenderer(id<RTC_OBJC_TYPE(RTCVideoRenderer)> renderer) + : renderer_(renderer), size_(CGSizeZero) {} + +void ObjCVideoRenderer::OnFrame(const VideoFrame& nativeVideoFrame) { + RTC_OBJC_TYPE(RTCVideoFrame)* videoFrame = ToObjCVideoFrame(nativeVideoFrame); + + CGSize current_size = (videoFrame.rotation % 180 == 0) ? + CGSizeMake(videoFrame.width, videoFrame.height) : + CGSizeMake(videoFrame.height, videoFrame.width); + + if (!CGSizeEqualToSize(size_, current_size)) { + size_ = current_size; + [renderer_ setSize:size_]; + } + [renderer_ renderFrame:videoFrame]; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_track_source.h b/third_party/libwebrtc/sdk/objc/native/src/objc_video_track_source.h new file mode 100644 index 0000000000..19a3d6db43 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_track_source.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SDK_OBJC_CLASSES_VIDEO_OBJC_VIDEO_TRACK_SOURCE_H_ +#define SDK_OBJC_CLASSES_VIDEO_OBJC_VIDEO_TRACK_SOURCE_H_ + +#import "base/RTCVideoCapturer.h" + +#include "base/RTCMacros.h" +#include "media/base/adapted_video_track_source.h" +#include "rtc_base/timestamp_aligner.h" + +RTC_FWD_DECL_OBJC_CLASS(RTC_OBJC_TYPE(RTCVideoFrame)); + +@interface RTCObjCVideoSourceAdapter : NSObject <RTC_OBJC_TYPE (RTCVideoCapturerDelegate)> +@end + +namespace webrtc { + +class ObjCVideoTrackSource : public rtc::AdaptedVideoTrackSource { + public: + ObjCVideoTrackSource(); + explicit ObjCVideoTrackSource(bool is_screencast); + explicit ObjCVideoTrackSource(RTCObjCVideoSourceAdapter* adapter); + + bool is_screencast() const override; + + // Indicates that the encoder should denoise video before encoding it. + // If it is not set, the default configuration is used which is different + // depending on video codec. + absl::optional<bool> needs_denoising() const override; + + SourceState state() const override; + + bool remote() const override; + + void OnCapturedFrame(RTC_OBJC_TYPE(RTCVideoFrame) * frame); + + // Called by RTCVideoSource. + void OnOutputFormatRequest(int width, int height, int fps); + + private: + rtc::VideoBroadcaster broadcaster_; + rtc::TimestampAligner timestamp_aligner_; + + RTCObjCVideoSourceAdapter* adapter_; + bool is_screencast_; +}; + +} // namespace webrtc + +#endif // SDK_OBJC_CLASSES_VIDEO_OBJC_VIDEO_TRACK_SOURCE_H_ diff --git a/third_party/libwebrtc/sdk/objc/native/src/objc_video_track_source.mm b/third_party/libwebrtc/sdk/objc/native/src/objc_video_track_source.mm new file mode 100644 index 0000000000..7937e90505 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/native/src/objc_video_track_source.mm @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/native/src/objc_video_track_source.h" + +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +#include "api/video/i420_buffer.h" +#include "sdk/objc/native/src/objc_frame_buffer.h" + +@interface RTCObjCVideoSourceAdapter () +@property(nonatomic) webrtc::ObjCVideoTrackSource *objCVideoTrackSource; +@end + +@implementation RTCObjCVideoSourceAdapter + +@synthesize objCVideoTrackSource = _objCVideoTrackSource; + +- (void)capturer:(RTC_OBJC_TYPE(RTCVideoCapturer) *)capturer + didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + _objCVideoTrackSource->OnCapturedFrame(frame); +} + +@end + +namespace webrtc { + +ObjCVideoTrackSource::ObjCVideoTrackSource() : ObjCVideoTrackSource(false) {} + +ObjCVideoTrackSource::ObjCVideoTrackSource(bool is_screencast) + : AdaptedVideoTrackSource(/* required resolution alignment */ 2), + is_screencast_(is_screencast) {} + +ObjCVideoTrackSource::ObjCVideoTrackSource(RTCObjCVideoSourceAdapter *adapter) : adapter_(adapter) { + adapter_.objCVideoTrackSource = this; +} + +bool ObjCVideoTrackSource::is_screencast() const { + return is_screencast_; +} + +absl::optional<bool> ObjCVideoTrackSource::needs_denoising() const { + return false; +} + +MediaSourceInterface::SourceState ObjCVideoTrackSource::state() const { + return SourceState::kLive; +} + +bool ObjCVideoTrackSource::remote() const { + return false; +} + +void ObjCVideoTrackSource::OnOutputFormatRequest(int width, int height, int fps) { + cricket::VideoFormat format(width, height, cricket::VideoFormat::FpsToInterval(fps), 0); + video_adapter()->OnOutputFormatRequest(format); +} + +void ObjCVideoTrackSource::OnCapturedFrame(RTC_OBJC_TYPE(RTCVideoFrame) * frame) { + const int64_t timestamp_us = frame.timeStampNs / rtc::kNumNanosecsPerMicrosec; + const int64_t translated_timestamp_us = + timestamp_aligner_.TranslateTimestamp(timestamp_us, rtc::TimeMicros()); + + int adapted_width; + int adapted_height; + int crop_width; + int crop_height; + int crop_x; + int crop_y; + if (!AdaptFrame(frame.width, + frame.height, + timestamp_us, + &adapted_width, + &adapted_height, + &crop_width, + &crop_height, + &crop_x, + &crop_y)) { + return; + } + + rtc::scoped_refptr<VideoFrameBuffer> buffer; + if (adapted_width == frame.width && adapted_height == frame.height) { + // No adaption - optimized path. + buffer = rtc::make_ref_counted<ObjCFrameBuffer>(frame.buffer); + } else if ([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { + // Adapted CVPixelBuffer frame. + RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer = + (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer; + buffer = rtc::make_ref_counted<ObjCFrameBuffer>([[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] + initWithPixelBuffer:rtcPixelBuffer.pixelBuffer + adaptedWidth:adapted_width + adaptedHeight:adapted_height + cropWidth:crop_width + cropHeight:crop_height + cropX:crop_x + rtcPixelBuffer.cropX + cropY:crop_y + rtcPixelBuffer.cropY]); + } else { + // Adapted I420 frame. + // TODO(magjed): Optimize this I420 path. + rtc::scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(adapted_width, adapted_height); + buffer = rtc::make_ref_counted<ObjCFrameBuffer>(frame.buffer); + i420_buffer->CropAndScaleFrom(*buffer->ToI420(), crop_x, crop_y, crop_width, crop_height); + buffer = i420_buffer; + } + + // Applying rotation is only supported for legacy reasons and performance is + // not critical here. + VideoRotation rotation = static_cast<VideoRotation>(frame.rotation); + if (apply_rotation() && rotation != kVideoRotation_0) { + buffer = I420Buffer::Rotate(*buffer->ToI420(), rotation); + rotation = kVideoRotation_0; + } + + OnFrame(VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rotation(rotation) + .set_timestamp_us(translated_timestamp_us) + .build()); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/sdk/objc/unittests/ObjCVideoTrackSource_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/ObjCVideoTrackSource_xctest.mm new file mode 100644 index 0000000000..4c8bf348f4 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/ObjCVideoTrackSource_xctest.mm @@ -0,0 +1,469 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include "sdk/objc/native/src/objc_video_track_source.h" + +#import "api/video_frame_buffer/RTCNativeI420Buffer+Private.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#import "frame_buffer_helpers.h" + +#include "api/scoped_refptr.h" +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "media/base/fake_video_renderer.h" +#include "sdk/objc/native/api/video_frame.h" + +typedef void (^VideoSinkCallback)(RTC_OBJC_TYPE(RTCVideoFrame) *); + +namespace { + +class ObjCCallbackVideoSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> { + public: + ObjCCallbackVideoSink(VideoSinkCallback callback) : callback_(callback) {} + + void OnFrame(const webrtc::VideoFrame &frame) override { + callback_(NativeToObjCVideoFrame(frame)); + } + + private: + VideoSinkCallback callback_; +}; + +} // namespace + +@interface ObjCVideoTrackSourceTests : XCTestCase +@end + +@implementation ObjCVideoTrackSourceTests { + rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> _video_source; +} + +- (void)setUp { + _video_source = rtc::make_ref_counted<webrtc::ObjCVideoTrackSource>(); +} + +- (void)tearDown { + _video_source = NULL; +} + +- (void)testOnCapturedFrameAdaptsFrame { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer(); + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + XCTAssertEqual(video_renderer->num_rendered_frames(), 1); + XCTAssertEqual(video_renderer->width(), 360); + XCTAssertEqual(video_renderer->height(), 640); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameAdaptsFrameWithAlignment { + // Requesting to adapt 1280x720 to 912x514 gives 639x360 without alignment. The 639 causes issues + // with some hardware encoders (e.g. HEVC) so in this test we verify that the alignment is set and + // respected. + + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer(); + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants); + + _video_source->OnOutputFormatRequest(912, 514, 30); + _video_source->OnCapturedFrame(frame); + + XCTAssertEqual(video_renderer->num_rendered_frames(), 1); + XCTAssertEqual(video_renderer->width(), 360); + XCTAssertEqual(video_renderer->height(), 640); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameAdaptationResultsInCommonResolutions { + // Some of the most common resolutions used in the wild are 640x360, 480x270 and 320x180. + // Make sure that we properly scale down to exactly these resolutions. + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer(); + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + XCTAssertEqual(video_renderer->num_rendered_frames(), 1); + XCTAssertEqual(video_renderer->width(), 360); + XCTAssertEqual(video_renderer->height(), 640); + + _video_source->OnOutputFormatRequest(480, 270, 30); + _video_source->OnCapturedFrame(frame); + + XCTAssertEqual(video_renderer->num_rendered_frames(), 2); + XCTAssertEqual(video_renderer->width(), 270); + XCTAssertEqual(video_renderer->height(), 480); + + _video_source->OnOutputFormatRequest(320, 180, 30); + _video_source->OnCapturedFrame(frame); + + XCTAssertEqual(video_renderer->num_rendered_frames(), 3); + XCTAssertEqual(video_renderer->width(), 180); + XCTAssertEqual(video_renderer->height(), 320); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameWithoutAdaptation { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 360, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(frame.width, outputFrame.width); + XCTAssertEqual(frame.height, outputFrame.height); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; + XCTAssertEqual(buffer.cropX, outputBuffer.cropX); + XCTAssertEqual(buffer.cropY, outputBuffer.cropY); + XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameCVPixelBufferNeedsAdaptation { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 360); + XCTAssertEqual(outputFrame.height, 640); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; + XCTAssertEqual(outputBuffer.cropX, 0); + XCTAssertEqual(outputBuffer.cropY, 0); + XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameCVPixelBufferNeedsCropping { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 360); + XCTAssertEqual(outputFrame.height, 640); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; + XCTAssertEqual(outputBuffer.cropX, 10); + XCTAssertEqual(outputBuffer.cropY, 0); + XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFramePreAdaptedCVPixelBufferNeedsAdaptation { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + // Create a frame that's already adapted down. + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef + adaptedWidth:640 + adaptedHeight:360 + cropWidth:720 + cropHeight:1280 + cropX:0 + cropY:0]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 480); + XCTAssertEqual(outputFrame.height, 270); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; + XCTAssertEqual(outputBuffer.cropX, 0); + XCTAssertEqual(outputBuffer.cropY, 0); + XCTAssertEqual(outputBuffer.cropWidth, 640); + XCTAssertEqual(outputBuffer.cropHeight, 360); + XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(480, 270, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFramePreCroppedCVPixelBufferNeedsCropping { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef + adaptedWidth:370 + adaptedHeight:640 + cropWidth:370 + cropHeight:640 + cropX:10 + cropY:0]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 360); + XCTAssertEqual(outputFrame.height, 640); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; + XCTAssertEqual(outputBuffer.cropX, 14); + XCTAssertEqual(outputBuffer.cropY, 0); + XCTAssertEqual(outputBuffer.cropWidth, 360); + XCTAssertEqual(outputBuffer.cropHeight, 640); + XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameSmallerPreCroppedCVPixelBufferNeedsCropping { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef + adaptedWidth:300 + adaptedHeight:640 + cropWidth:300 + cropHeight:640 + cropX:40 + cropY:0]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 300); + XCTAssertEqual(outputFrame.height, 534); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; + XCTAssertEqual(outputBuffer.cropX, 40); + XCTAssertEqual(outputBuffer.cropY, 52); + XCTAssertEqual(outputBuffer.cropWidth, 300); + XCTAssertEqual(outputBuffer.cropHeight, 534); + XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; + CVBufferRelease(pixelBufferRef); +} + +- (void)testOnCapturedFrameI420BufferNeedsAdaptation { + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280); + RTC_OBJC_TYPE(RTCI420Buffer) *buffer = + [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 360); + XCTAssertEqual(outputFrame.height, 640); + + RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer; + + double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]); + XCTAssertEqual(psnr, webrtc::kPerfectPSNR); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +- (void)testOnCapturedFrameI420BufferNeedsCropping { + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(380, 640); + RTC_OBJC_TYPE(RTCI420Buffer) *buffer = + [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer]; + RTC_OBJC_TYPE(RTCVideoFrame) *frame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; + ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { + XCTAssertEqual(outputFrame.width, 360); + XCTAssertEqual(outputFrame.height, 640); + + RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer; + + double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]); + XCTAssertGreaterThanOrEqual(psnr, 40); + + [callbackExpectation fulfill]; + }); + + const rtc::VideoSinkWants video_sink_wants; + rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); + video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); + + _video_source->OnOutputFormatRequest(640, 360, 30); + _video_source->OnCapturedFrame(frame); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCAudioDeviceModule_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCAudioDeviceModule_xctest.mm new file mode 100644 index 0000000000..f8ce844652 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCAudioDeviceModule_xctest.mm @@ -0,0 +1,593 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <XCTest/XCTest.h> + +#if defined(WEBRTC_IOS) +#import "sdk/objc/native/api/audio_device_module.h" +#endif + +#include "api/scoped_refptr.h" + +typedef int32_t(^NeedMorePlayDataBlock)(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms); + +typedef int32_t(^RecordedDataIsAvailableBlock)(const void* audioSamples, + const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + const uint32_t totalDelayMS, + const int32_t clockDrift, + const uint32_t currentMicLevel, + const bool keyPressed, + uint32_t& newMicLevel); + + +// This class implements the AudioTransport API and forwards all methods to the appropriate blocks. +class MockAudioTransport : public webrtc::AudioTransport { +public: + MockAudioTransport() {} + ~MockAudioTransport() override {} + + void expectNeedMorePlayData(NeedMorePlayDataBlock block) { + needMorePlayDataBlock = block; + } + + void expectRecordedDataIsAvailable(RecordedDataIsAvailableBlock block) { + recordedDataIsAvailableBlock = block; + } + + int32_t NeedMorePlayData(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) override { + return needMorePlayDataBlock(nSamples, + nBytesPerSample, + nChannels, + samplesPerSec, + audioSamples, + nSamplesOut, + elapsed_time_ms, + ntp_time_ms); + } + + int32_t RecordedDataIsAvailable(const void* audioSamples, + const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + const uint32_t totalDelayMS, + const int32_t clockDrift, + const uint32_t currentMicLevel, + const bool keyPressed, + uint32_t& newMicLevel) override { + return recordedDataIsAvailableBlock(audioSamples, + nSamples, + nBytesPerSample, + nChannels, + samplesPerSec, + totalDelayMS, + clockDrift, + currentMicLevel, + keyPressed, + newMicLevel); + } + + void PullRenderData(int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) override {} + + private: + NeedMorePlayDataBlock needMorePlayDataBlock; + RecordedDataIsAvailableBlock recordedDataIsAvailableBlock; +}; + +// Number of callbacks (input or output) the tests waits for before we set +// an event indicating that the test was OK. +static const NSUInteger kNumCallbacks = 10; +// Max amount of time we wait for an event to be set while counting callbacks. +static const NSTimeInterval kTestTimeOutInSec = 20.0; +// Number of bits per PCM audio sample. +static const NSUInteger kBitsPerSample = 16; +// Number of bytes per PCM audio sample. +static const NSUInteger kBytesPerSample = kBitsPerSample / 8; +// Average number of audio callbacks per second assuming 10ms packet size. +static const NSUInteger kNumCallbacksPerSecond = 100; +// Play out a test file during this time (unit is in seconds). +static const NSUInteger kFilePlayTimeInSec = 15; +// Run the full-duplex test during this time (unit is in seconds). +// Note that first `kNumIgnoreFirstCallbacks` are ignored. +static const NSUInteger kFullDuplexTimeInSec = 10; +// Wait for the callback sequence to stabilize by ignoring this amount of the +// initial callbacks (avoids initial FIFO access). +// Only used in the RunPlayoutAndRecordingInFullDuplex test. +static const NSUInteger kNumIgnoreFirstCallbacks = 50; + +@interface RTCAudioDeviceModuleTests : XCTestCase { + rtc::scoped_refptr<webrtc::AudioDeviceModule> audioDeviceModule; + MockAudioTransport mock; +} + +@property(nonatomic, assign) webrtc::AudioParameters playoutParameters; +@property(nonatomic, assign) webrtc::AudioParameters recordParameters; + +@end + +@implementation RTCAudioDeviceModuleTests + +@synthesize playoutParameters; +@synthesize recordParameters; + +- (void)setUp { + [super setUp]; + audioDeviceModule = webrtc::CreateAudioDeviceModule(); + XCTAssertEqual(0, audioDeviceModule->Init()); + XCTAssertEqual(0, audioDeviceModule->GetPlayoutAudioParameters(&playoutParameters)); + XCTAssertEqual(0, audioDeviceModule->GetRecordAudioParameters(&recordParameters)); +} + +- (void)tearDown { + XCTAssertEqual(0, audioDeviceModule->Terminate()); + audioDeviceModule = nullptr; + [super tearDown]; +} + +- (void)startPlayout { + XCTAssertFalse(audioDeviceModule->Playing()); + XCTAssertEqual(0, audioDeviceModule->InitPlayout()); + XCTAssertTrue(audioDeviceModule->PlayoutIsInitialized()); + XCTAssertEqual(0, audioDeviceModule->StartPlayout()); + XCTAssertTrue(audioDeviceModule->Playing()); +} + +- (void)stopPlayout { + XCTAssertEqual(0, audioDeviceModule->StopPlayout()); + XCTAssertFalse(audioDeviceModule->Playing()); +} + +- (void)startRecording{ + XCTAssertFalse(audioDeviceModule->Recording()); + XCTAssertEqual(0, audioDeviceModule->InitRecording()); + XCTAssertTrue(audioDeviceModule->RecordingIsInitialized()); + XCTAssertEqual(0, audioDeviceModule->StartRecording()); + XCTAssertTrue(audioDeviceModule->Recording()); +} + +- (void)stopRecording{ + XCTAssertEqual(0, audioDeviceModule->StopRecording()); + XCTAssertFalse(audioDeviceModule->Recording()); +} + +- (NSURL*)fileURLForSampleRate:(int)sampleRate { + XCTAssertTrue(sampleRate == 48000 || sampleRate == 44100 || sampleRate == 16000); + NSString *filename = [NSString stringWithFormat:@"audio_short%d", sampleRate / 1000]; + NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:@"pcm"]; + XCTAssertNotNil(url); + + return url; +} + +#pragma mark - Tests + +- (void)testConstructDestruct { + // Using the test fixture to create and destruct the audio device module. +} + +- (void)testInitTerminate { + // Initialization is part of the test fixture. + XCTAssertTrue(audioDeviceModule->Initialized()); + XCTAssertEqual(0, audioDeviceModule->Terminate()); + XCTAssertFalse(audioDeviceModule->Initialized()); +} + +// Tests that playout can be initiated, started and stopped. No audio callback +// is registered in this test. +- (void)testStartStopPlayout { + [self startPlayout]; + [self stopPlayout]; + [self startPlayout]; + [self stopPlayout]; +} + +// Tests that recording can be initiated, started and stopped. No audio callback +// is registered in this test. +- (void)testStartStopRecording { + [self startRecording]; + [self stopRecording]; + [self startRecording]; + [self stopRecording]; +} +// Verify that calling StopPlayout() will leave us in an uninitialized state +// which will require a new call to InitPlayout(). This test does not call +// StartPlayout() while being uninitialized since doing so will hit a +// RTC_DCHECK. +- (void)testStopPlayoutRequiresInitToRestart { + XCTAssertEqual(0, audioDeviceModule->InitPlayout()); + XCTAssertEqual(0, audioDeviceModule->StartPlayout()); + XCTAssertEqual(0, audioDeviceModule->StopPlayout()); + XCTAssertFalse(audioDeviceModule->PlayoutIsInitialized()); +} + +// Verify that we can create two ADMs and start playing on the second ADM. +// Only the first active instance shall activate an audio session and the +// last active instance shall deactivate the audio session. The test does not +// explicitly verify correct audio session calls but instead focuses on +// ensuring that audio starts for both ADMs. +- (void)testStartPlayoutOnTwoInstances { + // Create and initialize a second/extra ADM instance. The default ADM is + // created by the test harness. + rtc::scoped_refptr<webrtc::AudioDeviceModule> secondAudioDeviceModule = + webrtc::CreateAudioDeviceModule(); + XCTAssertNotEqual(secondAudioDeviceModule.get(), nullptr); + XCTAssertEqual(0, secondAudioDeviceModule->Init()); + + // Start playout for the default ADM but don't wait here. Instead use the + // upcoming second stream for that. We set the same expectation on number + // of callbacks as for the second stream. + mock.expectNeedMorePlayData(^int32_t(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void *audioSamples, + size_t &nSamplesOut, + int64_t *elapsed_time_ms, + int64_t *ntp_time_ms) { + nSamplesOut = nSamples; + XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer()); + XCTAssertEqual(nBytesPerSample, kBytesPerSample); + XCTAssertEqual(nChannels, self.playoutParameters.channels()); + XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate()); + XCTAssertNotEqual((void*)NULL, audioSamples); + + return 0; + }); + + XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock)); + [self startPlayout]; + + // Initialize playout for the second ADM. If all is OK, the second ADM shall + // reuse the audio session activated when the first ADM started playing. + // This call will also ensure that we avoid a problem related to initializing + // two different audio unit instances back to back (see webrtc:5166 for + // details). + XCTAssertEqual(0, secondAudioDeviceModule->InitPlayout()); + XCTAssertTrue(secondAudioDeviceModule->PlayoutIsInitialized()); + + // Start playout for the second ADM and verify that it starts as intended. + // Passing this test ensures that initialization of the second audio unit + // has been done successfully and that there is no conflict with the already + // playing first ADM. + XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"]; + __block int num_callbacks = 0; + + MockAudioTransport mock2; + mock2.expectNeedMorePlayData(^int32_t(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void *audioSamples, + size_t &nSamplesOut, + int64_t *elapsed_time_ms, + int64_t *ntp_time_ms) { + nSamplesOut = nSamples; + XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer()); + XCTAssertEqual(nBytesPerSample, kBytesPerSample); + XCTAssertEqual(nChannels, self.playoutParameters.channels()); + XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate()); + XCTAssertNotEqual((void*)NULL, audioSamples); + if (++num_callbacks == kNumCallbacks) { + [playoutExpectation fulfill]; + } + + return 0; + }); + + XCTAssertEqual(0, secondAudioDeviceModule->RegisterAudioCallback(&mock2)); + XCTAssertEqual(0, secondAudioDeviceModule->StartPlayout()); + XCTAssertTrue(secondAudioDeviceModule->Playing()); + [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil]; + [self stopPlayout]; + XCTAssertEqual(0, secondAudioDeviceModule->StopPlayout()); + XCTAssertFalse(secondAudioDeviceModule->Playing()); + XCTAssertFalse(secondAudioDeviceModule->PlayoutIsInitialized()); + + XCTAssertEqual(0, secondAudioDeviceModule->Terminate()); +} + +// Start playout and verify that the native audio layer starts asking for real +// audio samples to play out using the NeedMorePlayData callback. +- (void)testStartPlayoutVerifyCallbacks { + + XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"]; + __block int num_callbacks = 0; + mock.expectNeedMorePlayData(^int32_t(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void *audioSamples, + size_t &nSamplesOut, + int64_t *elapsed_time_ms, + int64_t *ntp_time_ms) { + nSamplesOut = nSamples; + XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer()); + XCTAssertEqual(nBytesPerSample, kBytesPerSample); + XCTAssertEqual(nChannels, self.playoutParameters.channels()); + XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate()); + XCTAssertNotEqual((void*)NULL, audioSamples); + if (++num_callbacks == kNumCallbacks) { + [playoutExpectation fulfill]; + } + return 0; + }); + + XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock)); + + [self startPlayout]; + [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil]; + [self stopPlayout]; +} + +// Start recording and verify that the native audio layer starts feeding real +// audio samples via the RecordedDataIsAvailable callback. +- (void)testStartRecordingVerifyCallbacks { + XCTestExpectation *recordExpectation = + [self expectationWithDescription:@"RecordedDataIsAvailable"]; + __block int num_callbacks = 0; + + mock.expectRecordedDataIsAvailable(^(const void* audioSamples, + const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + const uint32_t totalDelayMS, + const int32_t clockDrift, + const uint32_t currentMicLevel, + const bool keyPressed, + uint32_t& newMicLevel) { + XCTAssertNotEqual((void*)NULL, audioSamples); + XCTAssertEqual(nSamples, self.recordParameters.frames_per_10ms_buffer()); + XCTAssertEqual(nBytesPerSample, kBytesPerSample); + XCTAssertEqual(nChannels, self.recordParameters.channels()); + XCTAssertEqual((int)samplesPerSec, self.recordParameters.sample_rate()); + XCTAssertEqual(0, clockDrift); + XCTAssertEqual(0u, currentMicLevel); + XCTAssertFalse(keyPressed); + if (++num_callbacks == kNumCallbacks) { + [recordExpectation fulfill]; + } + + return 0; + }); + + XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock)); + [self startRecording]; + [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil]; + [self stopRecording]; +} + +// Start playout and recording (full-duplex audio) and verify that audio is +// active in both directions. +- (void)testStartPlayoutAndRecordingVerifyCallbacks { + XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"]; + __block NSUInteger callbackCount = 0; + + XCTestExpectation *recordExpectation = + [self expectationWithDescription:@"RecordedDataIsAvailable"]; + recordExpectation.expectedFulfillmentCount = kNumCallbacks; + + mock.expectNeedMorePlayData(^int32_t(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void *audioSamples, + size_t &nSamplesOut, + int64_t *elapsed_time_ms, + int64_t *ntp_time_ms) { + nSamplesOut = nSamples; + XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer()); + XCTAssertEqual(nBytesPerSample, kBytesPerSample); + XCTAssertEqual(nChannels, self.playoutParameters.channels()); + XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate()); + XCTAssertNotEqual((void*)NULL, audioSamples); + if (callbackCount++ >= kNumCallbacks) { + [playoutExpectation fulfill]; + } + + return 0; + }); + + mock.expectRecordedDataIsAvailable(^(const void* audioSamples, + const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + const uint32_t totalDelayMS, + const int32_t clockDrift, + const uint32_t currentMicLevel, + const bool keyPressed, + uint32_t& newMicLevel) { + XCTAssertNotEqual((void*)NULL, audioSamples); + XCTAssertEqual(nSamples, self.recordParameters.frames_per_10ms_buffer()); + XCTAssertEqual(nBytesPerSample, kBytesPerSample); + XCTAssertEqual(nChannels, self.recordParameters.channels()); + XCTAssertEqual((int)samplesPerSec, self.recordParameters.sample_rate()); + XCTAssertEqual(0, clockDrift); + XCTAssertEqual(0u, currentMicLevel); + XCTAssertFalse(keyPressed); + [recordExpectation fulfill]; + + return 0; + }); + + XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock)); + [self startPlayout]; + [self startRecording]; + [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil]; + [self stopRecording]; + [self stopPlayout]; +} + +// Start playout and read audio from an external PCM file when the audio layer +// asks for data to play out. Real audio is played out in this test but it does +// not contain any explicit verification that the audio quality is perfect. +- (void)testRunPlayoutWithFileAsSource { + XCTAssertEqual(1u, playoutParameters.channels()); + + // Using XCTestExpectation to count callbacks is very slow. + XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"]; + const int expectedCallbackCount = kFilePlayTimeInSec * kNumCallbacksPerSecond; + __block int callbackCount = 0; + + NSURL *fileURL = [self fileURLForSampleRate:playoutParameters.sample_rate()]; + NSInputStream *inputStream = [[NSInputStream alloc] initWithURL:fileURL]; + + mock.expectNeedMorePlayData(^int32_t(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void *audioSamples, + size_t &nSamplesOut, + int64_t *elapsed_time_ms, + int64_t *ntp_time_ms) { + [inputStream read:(uint8_t *)audioSamples maxLength:nSamples*nBytesPerSample*nChannels]; + nSamplesOut = nSamples; + if (callbackCount++ == expectedCallbackCount) { + [playoutExpectation fulfill]; + } + + return 0; + }); + + XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock)); + [self startPlayout]; + NSTimeInterval waitTimeout = kFilePlayTimeInSec * 2.0; + [self waitForExpectationsWithTimeout:waitTimeout handler:nil]; + [self stopPlayout]; +} + +- (void)testDevices { + // Device enumeration is not supported. Verify fixed values only. + XCTAssertEqual(1, audioDeviceModule->PlayoutDevices()); + XCTAssertEqual(1, audioDeviceModule->RecordingDevices()); +} + +// Start playout and recording and store recorded data in an intermediate FIFO +// buffer from which the playout side then reads its samples in the same order +// as they were stored. Under ideal circumstances, a callback sequence would +// look like: ...+-+-+-+-+-+-+-..., where '+' means 'packet recorded' and '-' +// means 'packet played'. Under such conditions, the FIFO would only contain +// one packet on average. However, under more realistic conditions, the size +// of the FIFO will vary more due to an unbalance between the two sides. +// This test tries to verify that the device maintains a balanced callback- +// sequence by running in loopback for ten seconds while measuring the size +// (max and average) of the FIFO. The size of the FIFO is increased by the +// recording side and decreased by the playout side. +// TODO(henrika): tune the final test parameters after running tests on several +// different devices. +- (void)testRunPlayoutAndRecordingInFullDuplex { + XCTAssertEqual(recordParameters.channels(), playoutParameters.channels()); + XCTAssertEqual(recordParameters.sample_rate(), playoutParameters.sample_rate()); + + XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"]; + __block NSUInteger playoutCallbacks = 0; + NSUInteger expectedPlayoutCallbacks = kFullDuplexTimeInSec * kNumCallbacksPerSecond; + + // FIFO queue and measurements + NSMutableArray *fifoBuffer = [NSMutableArray arrayWithCapacity:20]; + __block NSUInteger fifoMaxSize = 0; + __block NSUInteger fifoTotalWrittenElements = 0; + __block NSUInteger fifoWriteCount = 0; + + mock.expectRecordedDataIsAvailable(^(const void* audioSamples, + const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + const uint32_t totalDelayMS, + const int32_t clockDrift, + const uint32_t currentMicLevel, + const bool keyPressed, + uint32_t& newMicLevel) { + if (fifoWriteCount++ < kNumIgnoreFirstCallbacks) { + return 0; + } + + NSData *data = [NSData dataWithBytes:audioSamples length:nSamples*nBytesPerSample*nChannels]; + @synchronized(fifoBuffer) { + [fifoBuffer addObject:data]; + fifoMaxSize = MAX(fifoMaxSize, fifoBuffer.count); + fifoTotalWrittenElements += fifoBuffer.count; + } + + return 0; + }); + + mock.expectNeedMorePlayData(^int32_t(const size_t nSamples, + const size_t nBytesPerSample, + const size_t nChannels, + const uint32_t samplesPerSec, + void *audioSamples, + size_t &nSamplesOut, + int64_t *elapsed_time_ms, + int64_t *ntp_time_ms) { + nSamplesOut = nSamples; + NSData *data; + @synchronized(fifoBuffer) { + data = fifoBuffer.firstObject; + if (data) { + [fifoBuffer removeObjectAtIndex:0]; + } + } + + if (data) { + memcpy(audioSamples, (char*) data.bytes, data.length); + } else { + memset(audioSamples, 0, nSamples*nBytesPerSample*nChannels); + } + + if (playoutCallbacks++ == expectedPlayoutCallbacks) { + [playoutExpectation fulfill]; + } + return 0; + }); + + XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock)); + [self startRecording]; + [self startPlayout]; + NSTimeInterval waitTimeout = kFullDuplexTimeInSec * 2.0; + [self waitForExpectationsWithTimeout:waitTimeout handler:nil]; + + size_t fifoAverageSize = + (fifoTotalWrittenElements == 0) + ? 0.0 + : 0.5 + (double)fifoTotalWrittenElements / (fifoWriteCount - kNumIgnoreFirstCallbacks); + + [self stopPlayout]; + [self stopRecording]; + XCTAssertLessThan(fifoAverageSize, 10u); + XCTAssertLessThan(fifoMaxSize, 20u); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCAudioDevice_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCAudioDevice_xctest.mm new file mode 100644 index 0000000000..e01fdbd6e3 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCAudioDevice_xctest.mm @@ -0,0 +1,115 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <XCTest/XCTest.h> + +#include "api/task_queue/default_task_queue_factory.h" + +#import "sdk/objc/components/audio/RTCAudioSession+Private.h" +#import "sdk/objc/native/api/audio_device_module.h" +#import "sdk/objc/native/src/audio/audio_device_ios.h" + +@interface RTCAudioDeviceTests : XCTestCase { + rtc::scoped_refptr<webrtc::AudioDeviceModule> _audioDeviceModule; + std::unique_ptr<webrtc::ios_adm::AudioDeviceIOS> _audio_device; +} + +@property(nonatomic) RTC_OBJC_TYPE(RTCAudioSession) * audioSession; + +@end + +@implementation RTCAudioDeviceTests + +@synthesize audioSession = _audioSession; + +- (void)setUp { + [super setUp]; + + _audioDeviceModule = webrtc::CreateAudioDeviceModule(); + _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(/*bypass_voice_processing=*/false)); + self.audioSession = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + + NSError *error = nil; + [self.audioSession lockForConfiguration]; + [self.audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:&error]; + XCTAssertNil(error); + + [self.audioSession setMode:AVAudioSessionModeVoiceChat error:&error]; + XCTAssertNil(error); + + [self.audioSession setActive:YES error:&error]; + XCTAssertNil(error); + + [self.audioSession unlockForConfiguration]; +} + +- (void)tearDown { + _audio_device->Terminate(); + _audio_device.reset(nullptr); + _audioDeviceModule = nullptr; + [self.audioSession notifyDidEndInterruptionWithShouldResumeSession:NO]; + + [super tearDown]; +} + +// Verifies that the AudioDeviceIOS is_interrupted_ flag is reset correctly +// after an iOS AVAudioSessionInterruptionTypeEnded notification event. +// AudioDeviceIOS listens to RTC_OBJC_TYPE(RTCAudioSession) interrupted notifications by: +// - In AudioDeviceIOS.InitPlayOrRecord registers its audio_session_observer_ +// callback with RTC_OBJC_TYPE(RTCAudioSession)'s delegate list. +// - When RTC_OBJC_TYPE(RTCAudioSession) receives an iOS audio interrupted notification, it +// passes the notification to callbacks in its delegate list which sets +// AudioDeviceIOS's is_interrupted_ flag to true. +// - When AudioDeviceIOS.ShutdownPlayOrRecord is called, its +// audio_session_observer_ callback is removed from RTCAudioSessions's +// delegate list. +// So if RTC_OBJC_TYPE(RTCAudioSession) receives an iOS end audio interruption notification, +// AudioDeviceIOS is not notified as its callback is not in RTC_OBJC_TYPE(RTCAudioSession)'s +// delegate list. This causes AudioDeviceIOS's is_interrupted_ flag to be in +// the wrong (true) state and the audio session will ignore audio changes. +// As RTC_OBJC_TYPE(RTCAudioSession) keeps its own interrupted state, the fix is to initialize +// AudioDeviceIOS's is_interrupted_ flag to RTC_OBJC_TYPE(RTCAudioSession)'s isInterrupted +// flag in AudioDeviceIOS.InitPlayOrRecord. +- (void)testInterruptedAudioSession { + XCTAssertTrue(self.audioSession.isActive); + XCTAssertTrue([self.audioSession.category isEqual:AVAudioSessionCategoryPlayAndRecord] || + [self.audioSession.category isEqual:AVAudioSessionCategoryPlayback]); + XCTAssertEqual(AVAudioSessionModeVoiceChat, self.audioSession.mode); + + std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory = + webrtc::CreateDefaultTaskQueueFactory(); + std::unique_ptr<webrtc::AudioDeviceBuffer> audio_buffer; + audio_buffer.reset(new webrtc::AudioDeviceBuffer(task_queue_factory.get())); + _audio_device->AttachAudioBuffer(audio_buffer.get()); + XCTAssertEqual(webrtc::AudioDeviceGeneric::InitStatus::OK, _audio_device->Init()); + XCTAssertEqual(0, _audio_device->InitPlayout()); + XCTAssertEqual(0, _audio_device->StartPlayout()); + + // Force interruption. + [self.audioSession notifyDidBeginInterruption]; + + // Wait for notification to propagate. + rtc::ThreadManager::ProcessAllMessageQueuesForTesting(); + XCTAssertTrue(_audio_device->IsInterrupted()); + + // Force it for testing. + _audio_device->StopPlayout(); + + [self.audioSession notifyDidEndInterruptionWithShouldResumeSession:YES]; + // Wait for notification to propagate. + rtc::ThreadManager::ProcessAllMessageQueuesForTesting(); + XCTAssertTrue(_audio_device->IsInterrupted()); + + _audio_device->Init(); + _audio_device->InitPlayout(); + XCTAssertFalse(_audio_device->IsInterrupted()); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCAudioSessionTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCAudioSessionTest.mm new file mode 100644 index 0000000000..2a00909abe --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCAudioSessionTest.mm @@ -0,0 +1,324 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <OCMock/OCMock.h> +#import <XCTest/XCTest.h> + +#include <vector> + +#include "rtc_base/event.h" +#include "rtc_base/gunit.h" + +#import "components/audio/RTCAudioSession+Private.h" + +#import "components/audio/RTCAudioSession.h" +#import "components/audio/RTCAudioSessionConfiguration.h" + +@interface RTC_OBJC_TYPE (RTCAudioSession) +(UnitTesting) + + @property(nonatomic, + readonly) std::vector<__weak id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)> > delegates; + +- (instancetype)initWithAudioSession:(id)audioSession; + +@end + +@interface MockAVAudioSession : NSObject + +@property (nonatomic, readwrite, assign) float outputVolume; + +@end + +@implementation MockAVAudioSession +@synthesize outputVolume = _outputVolume; +@end + +@interface RTCAudioSessionTestDelegate : NSObject <RTC_OBJC_TYPE (RTCAudioSessionDelegate)> + +@property (nonatomic, readonly) float outputVolume; + +@end + +@implementation RTCAudioSessionTestDelegate + +@synthesize outputVolume = _outputVolume; + +- (instancetype)init { + if (self = [super init]) { + _outputVolume = -1; + } + return self; +} + +- (void)audioSessionDidBeginInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSessionDidEndInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session + shouldResumeSession:(BOOL)shouldResumeSession { +} + +- (void)audioSessionDidChangeRoute:(RTC_OBJC_TYPE(RTCAudioSession) *)session + reason:(AVAudioSessionRouteChangeReason)reason + previousRoute:(AVAudioSessionRouteDescription *)previousRoute { +} + +- (void)audioSessionMediaServerTerminated:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSessionMediaServerReset:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSessionShouldConfigure:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSessionShouldUnconfigure:(RTC_OBJC_TYPE(RTCAudioSession) *)session { +} + +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + didChangeOutputVolume:(float)outputVolume { + _outputVolume = outputVolume; +} + +@end + +// A delegate that adds itself to the audio session on init and removes itself +// in its dealloc. +@interface RTCTestRemoveOnDeallocDelegate : RTCAudioSessionTestDelegate +@end + +@implementation RTCTestRemoveOnDeallocDelegate + +- (instancetype)init { + if (self = [super init]) { + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session addDelegate:self]; + } + return self; +} + +- (void)dealloc { + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session removeDelegate:self]; +} + +@end + +@interface RTCAudioSessionTest : XCTestCase + +@end + +@implementation RTCAudioSessionTest + +- (void)testAddAndRemoveDelegates { + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + NSMutableArray *delegates = [NSMutableArray array]; + const size_t count = 5; + for (size_t i = 0; i < count; ++i) { + RTCAudioSessionTestDelegate *delegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session addDelegate:delegate]; + [delegates addObject:delegate]; + EXPECT_EQ(i + 1, session.delegates.size()); + } + [delegates enumerateObjectsUsingBlock:^(RTCAudioSessionTestDelegate *obj, + NSUInteger idx, + BOOL *stop) { + [session removeDelegate:obj]; + }]; + EXPECT_EQ(0u, session.delegates.size()); +} + +- (void)testPushDelegate { + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + NSMutableArray *delegates = [NSMutableArray array]; + const size_t count = 2; + for (size_t i = 0; i < count; ++i) { + RTCAudioSessionTestDelegate *delegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session addDelegate:delegate]; + [delegates addObject:delegate]; + } + // Test that it gets added to the front of the list. + RTCAudioSessionTestDelegate *pushedDelegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session pushDelegate:pushedDelegate]; + EXPECT_TRUE(pushedDelegate == session.delegates[0]); + + // Test that it stays at the front of the list. + for (size_t i = 0; i < count; ++i) { + RTCAudioSessionTestDelegate *delegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session addDelegate:delegate]; + [delegates addObject:delegate]; + } + EXPECT_TRUE(pushedDelegate == session.delegates[0]); + + // Test that the next one goes to the front too. + pushedDelegate = [[RTCAudioSessionTestDelegate alloc] init]; + [session pushDelegate:pushedDelegate]; + EXPECT_TRUE(pushedDelegate == session.delegates[0]); +} + +// Tests that delegates added to the audio session properly zero out. This is +// checking an implementation detail (that vectors of __weak work as expected). +- (void)testZeroingWeakDelegate { + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + @autoreleasepool { + // Add a delegate to the session. There should be one delegate at this + // point. + RTCAudioSessionTestDelegate *delegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session addDelegate:delegate]; + EXPECT_EQ(1u, session.delegates.size()); + EXPECT_TRUE(session.delegates[0]); + } + // The previously created delegate should've de-alloced, leaving a nil ptr. + EXPECT_FALSE(session.delegates[0]); + RTCAudioSessionTestDelegate *delegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session addDelegate:delegate]; + // On adding a new delegate, nil ptrs should've been cleared. + EXPECT_EQ(1u, session.delegates.size()); + EXPECT_TRUE(session.delegates[0]); +} + +// Tests that we don't crash when removing delegates in dealloc. +// Added as a regression test. +- (void)testRemoveDelegateOnDealloc { + @autoreleasepool { + RTCTestRemoveOnDeallocDelegate *delegate = + [[RTCTestRemoveOnDeallocDelegate alloc] init]; + EXPECT_TRUE(delegate); + } + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + EXPECT_EQ(0u, session.delegates.size()); +} + +- (void)testAudioSessionActivation { + RTC_OBJC_TYPE(RTCAudioSession) *audioSession = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + EXPECT_EQ(0, audioSession.activationCount); + [audioSession audioSessionDidActivate:[AVAudioSession sharedInstance]]; + EXPECT_EQ(1, audioSession.activationCount); + [audioSession audioSessionDidDeactivate:[AVAudioSession sharedInstance]]; + EXPECT_EQ(0, audioSession.activationCount); +} + +// Hack - fixes OCMVerify link error +// Link error is: Undefined symbols for architecture i386: +// "OCMMakeLocation(objc_object*, char const*, int)", referenced from: +// -[RTCAudioSessionTest testConfigureWebRTCSession] in RTCAudioSessionTest.o +// ld: symbol(s) not found for architecture i386 +// REASON: https://github.com/erikdoe/ocmock/issues/238 +OCMLocation *OCMMakeLocation(id testCase, const char *fileCString, int line){ + return [OCMLocation locationWithTestCase:testCase + file:[NSString stringWithUTF8String:fileCString] + line:line]; +} + +- (void)testConfigureWebRTCSession { + NSError *error = nil; + + void (^setActiveBlock)(NSInvocation *invocation) = ^(NSInvocation *invocation) { + __autoreleasing NSError **retError; + [invocation getArgument:&retError atIndex:4]; + *retError = [NSError errorWithDomain:@"AVAudioSession" + code:AVAudioSessionErrorCodeCannotInterruptOthers + userInfo:nil]; + BOOL failure = NO; + [invocation setReturnValue:&failure]; + }; + + id mockAVAudioSession = OCMPartialMock([AVAudioSession sharedInstance]); + OCMStub([[mockAVAudioSession ignoringNonObjectArgs] setActive:YES + withOptions:0 + error:([OCMArg anyObjectRef])]) + .andDo(setActiveBlock); + + id mockAudioSession = OCMPartialMock([RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]); + OCMStub([mockAudioSession session]).andReturn(mockAVAudioSession); + + RTC_OBJC_TYPE(RTCAudioSession) *audioSession = mockAudioSession; + EXPECT_EQ(0, audioSession.activationCount); + [audioSession lockForConfiguration]; + // configureWebRTCSession is forced to fail in the above mock interface, + // so activationCount should remain 0 + OCMExpect([[mockAVAudioSession ignoringNonObjectArgs] setActive:YES + withOptions:0 + error:([OCMArg anyObjectRef])]) + .andDo(setActiveBlock); + OCMExpect([mockAudioSession session]).andReturn(mockAVAudioSession); + EXPECT_FALSE([audioSession configureWebRTCSession:&error]); + EXPECT_EQ(0, audioSession.activationCount); + + id session = audioSession.session; + EXPECT_EQ(session, mockAVAudioSession); + EXPECT_EQ(NO, [mockAVAudioSession setActive:YES withOptions:0 error:&error]); + [audioSession unlockForConfiguration]; + + OCMVerify([mockAudioSession session]); + OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:YES withOptions:0 error:&error]); + OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:NO withOptions:0 error:&error]); + + [mockAVAudioSession stopMocking]; + [mockAudioSession stopMocking]; +} + +- (void)testConfigureWebRTCSessionWithoutLocking { + NSError *error = nil; + + id mockAVAudioSession = OCMPartialMock([AVAudioSession sharedInstance]); + id mockAudioSession = OCMPartialMock([RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]); + OCMStub([mockAudioSession session]).andReturn(mockAVAudioSession); + + RTC_OBJC_TYPE(RTCAudioSession) *audioSession = mockAudioSession; + + std::unique_ptr<rtc::Thread> thread = rtc::Thread::Create(); + EXPECT_TRUE(thread); + EXPECT_TRUE(thread->Start()); + + rtc::Event waitLock; + rtc::Event waitCleanup; + constexpr webrtc::TimeDelta timeout = webrtc::TimeDelta::Seconds(5); + thread->PostTask([audioSession, &waitLock, &waitCleanup, timeout] { + [audioSession lockForConfiguration]; + waitLock.Set(); + waitCleanup.Wait(timeout); + [audioSession unlockForConfiguration]; + }); + + waitLock.Wait(timeout); + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:&error]; + EXPECT_TRUE(error != nil); + EXPECT_EQ(error.domain, kRTCAudioSessionErrorDomain); + EXPECT_EQ(error.code, kRTCAudioSessionErrorLockRequired); + waitCleanup.Set(); + thread->Stop(); + + [mockAVAudioSession stopMocking]; + [mockAudioSession stopMocking]; +} + +- (void)testAudioVolumeDidNotify { + MockAVAudioSession *mockAVAudioSession = [[MockAVAudioSession alloc] init]; + RTC_OBJC_TYPE(RTCAudioSession) *session = + [[RTC_OBJC_TYPE(RTCAudioSession) alloc] initWithAudioSession:mockAVAudioSession]; + RTCAudioSessionTestDelegate *delegate = + [[RTCAudioSessionTestDelegate alloc] init]; + [session addDelegate:delegate]; + + float expectedVolume = 0.75; + mockAVAudioSession.outputVolume = expectedVolume; + + EXPECT_EQ(expectedVolume, delegate.outputVolume); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCCVPixelBuffer_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCCVPixelBuffer_xctest.mm new file mode 100644 index 0000000000..cf759c5243 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCCVPixelBuffer_xctest.mm @@ -0,0 +1,461 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +#import "api/video_frame_buffer/RTCNativeI420Buffer+Private.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "frame_buffer_helpers.h" + +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "third_party/libyuv/include/libyuv.h" + +namespace { + +struct ToI420WithCropAndScaleSetting { + int inputWidth; + int inputHeight; + int offsetX; + int offsetY; + int cropWidth; + int cropHeight; + int scaleWidth; + int scaleHeight; +}; + +constexpr const ToI420WithCropAndScaleSetting kToI420WithCropAndScaleSettings[] = { + ToI420WithCropAndScaleSetting{ + .inputWidth = 640, + .inputHeight = 360, + .offsetX = 0, + .offsetY = 0, + .cropWidth = 640, + .cropHeight = 360, + .scaleWidth = 320, + .scaleHeight = 180, + }, + ToI420WithCropAndScaleSetting{ + .inputWidth = 640, + .inputHeight = 360, + .offsetX = 160, + .offsetY = 90, + .cropWidth = 160, + .cropHeight = 90, + .scaleWidth = 320, + .scaleHeight = 180, + }, +}; + +} // namespace + +@interface RTCCVPixelBufferTests : XCTestCase +@end + +@implementation RTCCVPixelBufferTests { +} + +- (void)testRequiresCroppingNoCrop { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + + XCTAssertFalse([buffer requiresCropping]); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testRequiresCroppingWithCrop { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + RTC_OBJC_TYPE(RTCCVPixelBuffer) *croppedBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef + adaptedWidth:720 + adaptedHeight:1280 + cropWidth:360 + cropHeight:640 + cropX:100 + cropY:100]; + + XCTAssertTrue([croppedBuffer requiresCropping]); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testRequiresScalingNoScale { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + XCTAssertFalse([buffer requiresScalingToWidth:720 height:1280]); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testRequiresScalingWithScale { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + XCTAssertTrue([buffer requiresScalingToWidth:360 height:640]); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testRequiresScalingWithScaleAndMatchingCrop { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef + adaptedWidth:720 + adaptedHeight:1280 + cropWidth:360 + cropHeight:640 + cropX:100 + cropY:100]; + XCTAssertFalse([buffer requiresScalingToWidth:360 height:640]); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testBufferSize_NV12 { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + XCTAssertEqual([buffer bufferSizeForCroppingAndScalingToWidth:360 height:640], 576000); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testBufferSize_RGB { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate(NULL, 720, 1280, kCVPixelFormatType_32BGRA, NULL, &pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + XCTAssertEqual([buffer bufferSizeForCroppingAndScalingToWidth:360 height:640], 0); + + CVBufferRelease(pixelBufferRef); +} + +- (void)testCropAndScale_NV12 { + [self cropAndScaleTestWithNV12]; +} + +- (void)testCropAndScaleNoOp_NV12 { + [self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputSize:CGSizeMake(720, 1280)]; +} + +- (void)testCropAndScale_NV12FullToVideo { + [self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]; +} + +- (void)testCropAndScaleZeroSizeFrame_NV12 { + [self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputSize:CGSizeMake(0, 0)]; +} + +- (void)testCropAndScaleToSmallFormat_NV12 { + [self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputSize:CGSizeMake(148, 320)]; +} + +- (void)testCropAndScaleToOddFormat_NV12 { + [self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputSize:CGSizeMake(361, 640)]; +} + +- (void)testCropAndScale_32BGRA { + [self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32BGRA]; +} + +- (void)testCropAndScale_32ARGB { + [self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32ARGB]; +} + +- (void)testCropAndScaleWithSmallCropInfo_32ARGB { + [self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32ARGB cropX:2 cropY:3]; +} + +- (void)testCropAndScaleWithLargeCropInfo_32ARGB { + [self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32ARGB cropX:200 cropY:300]; +} + +- (void)testToI420_NV12 { + [self toI420WithPixelFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]; +} + +- (void)testToI420_32BGRA { + [self toI420WithPixelFormat:kCVPixelFormatType_32BGRA]; +} + +- (void)testToI420_32ARGB { + [self toI420WithPixelFormat:kCVPixelFormatType_32ARGB]; +} + +- (void)testToI420WithCropAndScale_NV12 { + for (const auto &setting : kToI420WithCropAndScaleSettings) { + [self toI420WithCropAndScaleWithPixelFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + setting:setting]; + } +} + +- (void)testToI420WithCropAndScale_32BGRA { + for (const auto &setting : kToI420WithCropAndScaleSettings) { + [self toI420WithCropAndScaleWithPixelFormat:kCVPixelFormatType_32BGRA setting:setting]; + } +} + +- (void)testToI420WithCropAndScale_32ARGB { + for (const auto &setting : kToI420WithCropAndScaleSettings) { + [self toI420WithCropAndScaleWithPixelFormat:kCVPixelFormatType_32ARGB setting:setting]; + } +} + +- (void)testScaleBufferTest { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, 1920, 1080, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); + + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(1920, 1080); + CopyI420BufferToCVPixelBuffer(i420Buffer, pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + + XCTAssertEqual(buffer.width, 1920); + XCTAssertEqual(buffer.height, 1080); + XCTAssertEqual(buffer.cropX, 0); + XCTAssertEqual(buffer.cropY, 0); + XCTAssertEqual(buffer.cropWidth, 1920); + XCTAssertEqual(buffer.cropHeight, 1080); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer2 = + (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)[buffer cropAndScaleWith:320 + offsetY:180 + cropWidth:1280 + cropHeight:720 + scaleWidth:960 + scaleHeight:540]; + + XCTAssertEqual(buffer2.width, 960); + XCTAssertEqual(buffer2.height, 540); + XCTAssertEqual(buffer2.cropX, 320); + XCTAssertEqual(buffer2.cropY, 180); + XCTAssertEqual(buffer2.cropWidth, 1280); + XCTAssertEqual(buffer2.cropHeight, 720); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer3 = + (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)[buffer2 cropAndScaleWith:240 + offsetY:135 + cropWidth:480 + cropHeight:270 + scaleWidth:320 + scaleHeight:180]; + + XCTAssertEqual(buffer3.width, 320); + XCTAssertEqual(buffer3.height, 180); + XCTAssertEqual(buffer3.cropX, 640); + XCTAssertEqual(buffer3.cropY, 360); + XCTAssertEqual(buffer3.cropWidth, 640); + XCTAssertEqual(buffer3.cropHeight, 360); + + CVBufferRelease(pixelBufferRef); +} + +#pragma mark - Shared test code + +- (void)cropAndScaleTestWithNV12 { + [self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]; +} + +- (void)cropAndScaleTestWithNV12InputFormat:(OSType)inputFormat outputFormat:(OSType)outputFormat { + [self cropAndScaleTestWithNV12InputFormat:(OSType)inputFormat + outputFormat:(OSType)outputFormat + outputSize:CGSizeMake(360, 640)]; +} + +- (void)cropAndScaleTestWithNV12InputFormat:(OSType)inputFormat + outputFormat:(OSType)outputFormat + outputSize:(CGSize)outputSize { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate(NULL, 720, 1280, inputFormat, NULL, &pixelBufferRef); + + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280); + CopyI420BufferToCVPixelBuffer(i420Buffer, pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + XCTAssertEqual(buffer.width, 720); + XCTAssertEqual(buffer.height, 1280); + + CVPixelBufferRef outputPixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, outputSize.width, outputSize.height, outputFormat, NULL, &outputPixelBufferRef); + + std::vector<uint8_t> frameScaleBuffer; + if ([buffer requiresScalingToWidth:outputSize.width height:outputSize.height]) { + int size = + [buffer bufferSizeForCroppingAndScalingToWidth:outputSize.width height:outputSize.height]; + frameScaleBuffer.resize(size); + } else { + frameScaleBuffer.clear(); + } + frameScaleBuffer.shrink_to_fit(); + + [buffer cropAndScaleTo:outputPixelBufferRef withTempBuffer:frameScaleBuffer.data()]; + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *scaledBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:outputPixelBufferRef]; + XCTAssertEqual(scaledBuffer.width, outputSize.width); + XCTAssertEqual(scaledBuffer.height, outputSize.height); + + if (outputSize.width > 0 && outputSize.height > 0) { + RTC_OBJC_TYPE(RTCI420Buffer) *originalBufferI420 = [buffer toI420]; + RTC_OBJC_TYPE(RTCI420Buffer) *scaledBufferI420 = [scaledBuffer toI420]; + double psnr = + I420PSNR(*[originalBufferI420 nativeI420Buffer], *[scaledBufferI420 nativeI420Buffer]); + XCTAssertEqual(psnr, webrtc::kPerfectPSNR); + } + + CVBufferRelease(pixelBufferRef); +} + +- (void)cropAndScaleTestWithRGBPixelFormat:(OSType)pixelFormat { + [self cropAndScaleTestWithRGBPixelFormat:pixelFormat cropX:0 cropY:0]; +} + +- (void)cropAndScaleTestWithRGBPixelFormat:(OSType)pixelFormat cropX:(int)cropX cropY:(int)cropY { + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate(NULL, 720, 1280, pixelFormat, NULL, &pixelBufferRef); + + DrawGradientInRGBPixelBuffer(pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] + initWithPixelBuffer:pixelBufferRef + adaptedWidth:CVPixelBufferGetWidth(pixelBufferRef) + adaptedHeight:CVPixelBufferGetHeight(pixelBufferRef) + cropWidth:CVPixelBufferGetWidth(pixelBufferRef) - cropX + cropHeight:CVPixelBufferGetHeight(pixelBufferRef) - cropY + cropX:cropX + cropY:cropY]; + + XCTAssertEqual(buffer.width, 720); + XCTAssertEqual(buffer.height, 1280); + + CVPixelBufferRef outputPixelBufferRef = NULL; + CVPixelBufferCreate(NULL, 360, 640, pixelFormat, NULL, &outputPixelBufferRef); + [buffer cropAndScaleTo:outputPixelBufferRef withTempBuffer:NULL]; + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *scaledBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:outputPixelBufferRef]; + XCTAssertEqual(scaledBuffer.width, 360); + XCTAssertEqual(scaledBuffer.height, 640); + + RTC_OBJC_TYPE(RTCI420Buffer) *originalBufferI420 = [buffer toI420]; + RTC_OBJC_TYPE(RTCI420Buffer) *scaledBufferI420 = [scaledBuffer toI420]; + double psnr = + I420PSNR(*[originalBufferI420 nativeI420Buffer], *[scaledBufferI420 nativeI420Buffer]); + XCTAssertEqual(psnr, webrtc::kPerfectPSNR); + + CVBufferRelease(pixelBufferRef); +} + +- (void)toI420WithPixelFormat:(OSType)pixelFormat { + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(360, 640); + + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate(NULL, 360, 640, pixelFormat, NULL, &pixelBufferRef); + + CopyI420BufferToCVPixelBuffer(i420Buffer, pixelBufferRef); + + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + RTC_OBJC_TYPE(RTCI420Buffer) *fromCVPixelBuffer = [buffer toI420]; + + double psnr = I420PSNR(*i420Buffer, *[fromCVPixelBuffer nativeI420Buffer]); + double target = webrtc::kPerfectPSNR; + if (pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) { + // libyuv's I420ToRGB functions seem to lose some quality. + target = 19.0; + } + XCTAssertGreaterThanOrEqual(psnr, target); + + CVBufferRelease(pixelBufferRef); +} + +- (void)toI420WithCropAndScaleWithPixelFormat:(OSType)pixelFormat + setting:(const ToI420WithCropAndScaleSetting &)setting { + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = + CreateI420Gradient(setting.inputWidth, setting.inputHeight); + + CVPixelBufferRef pixelBufferRef = NULL; + CVPixelBufferCreate( + NULL, setting.inputWidth, setting.inputHeight, pixelFormat, NULL, &pixelBufferRef); + + CopyI420BufferToCVPixelBuffer(i420Buffer, pixelBufferRef); + + RTC_OBJC_TYPE(RTCI420Buffer) *objcI420Buffer = + [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer]; + RTC_OBJC_TYPE(RTCI420Buffer) *scaledObjcI420Buffer = + (RTC_OBJC_TYPE(RTCI420Buffer) *)[objcI420Buffer cropAndScaleWith:setting.offsetX + offsetY:setting.offsetY + cropWidth:setting.cropWidth + cropHeight:setting.cropHeight + scaleWidth:setting.scaleWidth + scaleHeight:setting.scaleHeight]; + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; + id<RTC_OBJC_TYPE(RTCVideoFrameBuffer)> scaledBuffer = + [buffer cropAndScaleWith:setting.offsetX + offsetY:setting.offsetY + cropWidth:setting.cropWidth + cropHeight:setting.cropHeight + scaleWidth:setting.scaleWidth + scaleHeight:setting.scaleHeight]; + XCTAssertTrue([scaledBuffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]); + + RTC_OBJC_TYPE(RTCI420Buffer) *fromCVPixelBuffer = [scaledBuffer toI420]; + + double psnr = + I420PSNR(*[scaledObjcI420Buffer nativeI420Buffer], *[fromCVPixelBuffer nativeI420Buffer]); + double target = webrtc::kPerfectPSNR; + if (pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) { + // libyuv's I420ToRGB functions seem to lose some quality. + target = 19.0; + } + XCTAssertGreaterThanOrEqual(psnr, target); + + CVBufferRelease(pixelBufferRef); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCCallbackLogger_xctest.m b/third_party/libwebrtc/sdk/objc/unittests/RTCCallbackLogger_xctest.m new file mode 100644 index 0000000000..1b6fb1c07b --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCCallbackLogger_xctest.m @@ -0,0 +1,244 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "api/logging/RTCCallbackLogger.h" + +#import <XCTest/XCTest.h> + +@interface RTCCallbackLoggerTests : XCTestCase + +@property(nonatomic, strong) RTC_OBJC_TYPE(RTCCallbackLogger) * logger; + +@end + +@implementation RTCCallbackLoggerTests + +@synthesize logger; + +- (void)setUp { + self.logger = [[RTC_OBJC_TYPE(RTCCallbackLogger) alloc] init]; +} + +- (void)tearDown { + self.logger = nil; +} + +- (void)testDefaultSeverityLevel { + XCTAssertEqual(self.logger.severity, RTCLoggingSeverityInfo); +} + +- (void)testCallbackGetsCalledForAppropriateLevel { + self.logger.severity = RTCLoggingSeverityWarning; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"callbackWarning"]; + + [self.logger start:^(NSString *message) { + XCTAssertTrue([message hasSuffix:@"Horrible error\n"]); + [callbackExpectation fulfill]; + }]; + + RTCLogError("Horrible error"); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +- (void)testCallbackWithSeverityGetsCalledForAppropriateLevel { + self.logger.severity = RTCLoggingSeverityWarning; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"callbackWarning"]; + + [self.logger + startWithMessageAndSeverityHandler:^(NSString *message, RTCLoggingSeverity severity) { + XCTAssertTrue([message hasSuffix:@"Horrible error\n"]); + XCTAssertEqual(severity, RTCLoggingSeverityError); + [callbackExpectation fulfill]; + }]; + + RTCLogError("Horrible error"); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +- (void)testCallbackDoesNotGetCalledForOtherLevels { + self.logger.severity = RTCLoggingSeverityError; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"callbackError"]; + + [self.logger start:^(NSString *message) { + XCTAssertTrue([message hasSuffix:@"Horrible error\n"]); + [callbackExpectation fulfill]; + }]; + + RTCLogInfo("Just some info"); + RTCLogWarning("Warning warning"); + RTCLogError("Horrible error"); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +- (void)testCallbackWithSeverityDoesNotGetCalledForOtherLevels { + self.logger.severity = RTCLoggingSeverityError; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"callbackError"]; + + [self.logger + startWithMessageAndSeverityHandler:^(NSString *message, RTCLoggingSeverity severity) { + XCTAssertTrue([message hasSuffix:@"Horrible error\n"]); + XCTAssertEqual(severity, RTCLoggingSeverityError); + [callbackExpectation fulfill]; + }]; + + RTCLogInfo("Just some info"); + RTCLogWarning("Warning warning"); + RTCLogError("Horrible error"); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +- (void)testCallbackDoesNotgetCalledForSeverityNone { + self.logger.severity = RTCLoggingSeverityNone; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"unexpectedCallback"]; + + [self.logger start:^(NSString *message) { + [callbackExpectation fulfill]; + XCTAssertTrue(false); + }]; + + RTCLogInfo("Just some info"); + RTCLogWarning("Warning warning"); + RTCLogError("Horrible error"); + + XCTWaiter *waiter = [[XCTWaiter alloc] init]; + XCTWaiterResult result = [waiter waitForExpectations:@[ callbackExpectation ] timeout:1.0]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); +} + +- (void)testCallbackWithSeverityDoesNotgetCalledForSeverityNone { + self.logger.severity = RTCLoggingSeverityNone; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"unexpectedCallback"]; + + [self.logger + startWithMessageAndSeverityHandler:^(NSString *message, RTCLoggingSeverity severity) { + [callbackExpectation fulfill]; + XCTAssertTrue(false); + }]; + + RTCLogInfo("Just some info"); + RTCLogWarning("Warning warning"); + RTCLogError("Horrible error"); + + XCTWaiter *waiter = [[XCTWaiter alloc] init]; + XCTWaiterResult result = [waiter waitForExpectations:@[ callbackExpectation ] timeout:1.0]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); +} + +- (void)testStartingWithNilCallbackDoesNotCrash { + [self.logger start:nil]; + + RTCLogError("Horrible error"); +} + +- (void)testStartingWithNilCallbackWithSeverityDoesNotCrash { + [self.logger startWithMessageAndSeverityHandler:nil]; + + RTCLogError("Horrible error"); +} + +- (void)testStopCallbackLogger { + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"stopped"]; + + [self.logger start:^(NSString *message) { + [callbackExpectation fulfill]; + }]; + + [self.logger stop]; + + RTCLogInfo("Just some info"); + + XCTWaiter *waiter = [[XCTWaiter alloc] init]; + XCTWaiterResult result = [waiter waitForExpectations:@[ callbackExpectation ] timeout:1.0]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); +} + +- (void)testStopCallbackWithSeverityLogger { + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"stopped"]; + + [self.logger + startWithMessageAndSeverityHandler:^(NSString *message, RTCLoggingSeverity loggingServerity) { + [callbackExpectation fulfill]; + }]; + + [self.logger stop]; + + RTCLogInfo("Just some info"); + + XCTWaiter *waiter = [[XCTWaiter alloc] init]; + XCTWaiterResult result = [waiter waitForExpectations:@[ callbackExpectation ] timeout:1.0]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); +} + +- (void)testDestroyingCallbackLogger { + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"destroyed"]; + + [self.logger start:^(NSString *message) { + [callbackExpectation fulfill]; + }]; + + self.logger = nil; + + RTCLogInfo("Just some info"); + + XCTWaiter *waiter = [[XCTWaiter alloc] init]; + XCTWaiterResult result = [waiter waitForExpectations:@[ callbackExpectation ] timeout:1.0]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); +} + +- (void)testDestroyingCallbackWithSeverityLogger { + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"destroyed"]; + + [self.logger + startWithMessageAndSeverityHandler:^(NSString *message, RTCLoggingSeverity loggingServerity) { + [callbackExpectation fulfill]; + }]; + + self.logger = nil; + + RTCLogInfo("Just some info"); + + XCTWaiter *waiter = [[XCTWaiter alloc] init]; + XCTWaiterResult result = [waiter waitForExpectations:@[ callbackExpectation ] timeout:1.0]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); +} + +- (void)testCallbackWithSeverityLoggerCannotStartTwice { + self.logger.severity = RTCLoggingSeverityWarning; + + XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"callbackWarning"]; + + [self.logger + startWithMessageAndSeverityHandler:^(NSString *message, RTCLoggingSeverity loggingServerity) { + XCTAssertTrue([message hasSuffix:@"Horrible error\n"]); + XCTAssertEqual(loggingServerity, RTCLoggingSeverityError); + [callbackExpectation fulfill]; + }]; + + [self.logger start:^(NSString *message) { + [callbackExpectation fulfill]; + XCTAssertTrue(false); + }]; + + RTCLogError("Horrible error"); + + [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCCameraVideoCapturerTests.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCCameraVideoCapturerTests.mm new file mode 100644 index 0000000000..7919878260 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCCameraVideoCapturerTests.mm @@ -0,0 +1,570 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <OCMock/OCMock.h> +#import <XCTest/XCTest.h> + +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#endif + +#import "base/RTCVideoFrame.h" +#import "components/capturer/RTCCameraVideoCapturer.h" +#import "helpers/AVCaptureSession+DevicePosition.h" +#import "helpers/RTCDispatcher.h" +#import "helpers/scoped_cftyperef.h" + +#define WAIT(timeoutMs) \ + do { \ + id expectation = [[XCTestExpectation alloc] initWithDescription:@"Dummy"]; \ + XCTWaiterResult res = [XCTWaiter waitForExpectations:@[ expectation ] \ + timeout:timeoutMs / 1000.0]; \ + XCTAssertEqual(XCTWaiterResultTimedOut, res); \ + } while (false); + +#if TARGET_OS_IPHONE +// Helper method. +CMSampleBufferRef createTestSampleBufferRef() { + + // This image is already in the testing bundle. + UIImage *image = [UIImage imageNamed:@"Default.png"]; + CGSize size = image.size; + CGImageRef imageRef = [image CGImage]; + + CVPixelBufferRef pixelBuffer = nullptr; + CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, nil, + &pixelBuffer); + + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + // We don't care about bitsPerComponent and bytesPerRow so arbitrary value of 8 for both. + CGContextRef context = CGBitmapContextCreate(nil, size.width, size.height, 8, 8 * size.width, + rgbColorSpace, kCGImageAlphaPremultipliedFirst); + + CGContextDrawImage( + context, CGRectMake(0, 0, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)), imageRef); + + CGColorSpaceRelease(rgbColorSpace); + CGContextRelease(context); + + // We don't really care about the timing. + CMSampleTimingInfo timing = {kCMTimeInvalid, kCMTimeInvalid, kCMTimeInvalid}; + CMVideoFormatDescriptionRef description = nullptr; + CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &description); + + CMSampleBufferRef sampleBuffer = nullptr; + CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, YES, NULL, NULL, description, + &timing, &sampleBuffer); + CFRelease(pixelBuffer); + + return sampleBuffer; + +} +#endif +@interface RTC_OBJC_TYPE (RTCCameraVideoCapturer) +(Tests)<AVCaptureVideoDataOutputSampleBufferDelegate> - + (instancetype)initWithDelegate + : (__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)delegate captureSession + : (AVCaptureSession *)captureSession; +@end + +@interface RTCCameraVideoCapturerTests : XCTestCase +@property(nonatomic, strong) id delegateMock; +@property(nonatomic, strong) id deviceMock; +@property(nonatomic, strong) id captureConnectionMock; +@property(nonatomic, strong) RTC_OBJC_TYPE(RTCCameraVideoCapturer) * capturer; +@end + +@implementation RTCCameraVideoCapturerTests +@synthesize delegateMock = _delegateMock; +@synthesize deviceMock = _deviceMock; +@synthesize captureConnectionMock = _captureConnectionMock; +@synthesize capturer = _capturer; + +- (void)setUp { + self.delegateMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoCapturerDelegate))); + self.captureConnectionMock = OCMClassMock([AVCaptureConnection class]); + self.capturer = + [[RTC_OBJC_TYPE(RTCCameraVideoCapturer) alloc] initWithDelegate:self.delegateMock]; + self.deviceMock = [RTCCameraVideoCapturerTests createDeviceMock]; +} + +- (void)tearDown { + [self.delegateMock stopMocking]; + [self.deviceMock stopMocking]; + self.delegateMock = nil; + self.deviceMock = nil; + self.capturer = nil; +} + +#pragma mark - utils + ++ (id)createDeviceMock { + return OCMClassMock([AVCaptureDevice class]); +} + +#pragma mark - test cases + +- (void)testSetupSession { + AVCaptureSession *session = self.capturer.captureSession; + XCTAssertTrue(session != nil); + +#if TARGET_OS_IPHONE + XCTAssertEqual(session.sessionPreset, AVCaptureSessionPresetInputPriority); + XCTAssertEqual(session.usesApplicationAudioSession, NO); +#endif + XCTAssertEqual(session.outputs.count, 1u); +} + +- (void)testSetupSessionOutput { + AVCaptureVideoDataOutput *videoOutput = self.capturer.captureSession.outputs[0]; + XCTAssertEqual(videoOutput.alwaysDiscardsLateVideoFrames, NO); + XCTAssertEqual(videoOutput.sampleBufferDelegate, self.capturer); +} + +- (void)testSupportedFormatsForDevice { + // given + id validFormat1 = OCMClassMock([AVCaptureDeviceFormat class]); + CMVideoFormatDescriptionRef format; + + // We don't care about width and heigth so arbitrary 123 and 456 values. + int width = 123; + int height = 456; + CMVideoFormatDescriptionCreate(nil, kCVPixelFormatType_420YpCbCr8PlanarFullRange, width, height, + nil, &format); + OCMStub([validFormat1 formatDescription]).andReturn(format); + + id validFormat2 = OCMClassMock([AVCaptureDeviceFormat class]); + CMVideoFormatDescriptionCreate(nil, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, width, + height, nil, &format); + OCMStub([validFormat2 formatDescription]).andReturn(format); + + id invalidFormat = OCMClassMock([AVCaptureDeviceFormat class]); + CMVideoFormatDescriptionCreate(nil, kCVPixelFormatType_422YpCbCr8_yuvs, width, height, nil, + &format); + OCMStub([invalidFormat formatDescription]).andReturn(format); + + NSArray *formats = @[ validFormat1, validFormat2, invalidFormat ]; + OCMStub([self.deviceMock formats]).andReturn(formats); + + // when + NSArray *supportedFormats = + [RTC_OBJC_TYPE(RTCCameraVideoCapturer) supportedFormatsForDevice:self.deviceMock]; + + // then + XCTAssertEqual(supportedFormats.count, 3u); + XCTAssertTrue([supportedFormats containsObject:validFormat1]); + XCTAssertTrue([supportedFormats containsObject:validFormat2]); + XCTAssertTrue([supportedFormats containsObject:invalidFormat]); + + // cleanup + [validFormat1 stopMocking]; + [validFormat2 stopMocking]; + [invalidFormat stopMocking]; + validFormat1 = nil; + validFormat2 = nil; + invalidFormat = nil; +} + +- (void)testDelegateCallbackNotCalledWhenInvalidBuffer { + // given + CMSampleBufferRef sampleBuffer = nullptr; + [[self.delegateMock reject] capturer:[OCMArg any] didCaptureVideoFrame:[OCMArg any]]; + + // when + [self.capturer captureOutput:self.capturer.captureSession.outputs[0] + didOutputSampleBuffer:sampleBuffer + fromConnection:self.captureConnectionMock]; + + // then + [self.delegateMock verify]; +} + +#if 0 +// See crbug.com/1404878 - XCTExpectFailure and XCTSkip are considered failures + +- (void)testDelegateCallbackWithValidBufferAndOrientationUpdate { +#if TARGET_OS_IPHONE + XCTExpectFailure(@"Setting orientation on UIDevice is not supported"); + [UIDevice.currentDevice setValue:@(UIDeviceOrientationPortraitUpsideDown) forKey:@"orientation"]; + CMSampleBufferRef sampleBuffer = createTestSampleBufferRef(); + + // then + [[self.delegateMock expect] capturer:self.capturer + didCaptureVideoFrame:[OCMArg checkWithBlock:^BOOL(RTC_OBJC_TYPE(RTCVideoFrame) * + expectedFrame) { + XCTAssertEqual(expectedFrame.rotation, RTCVideoRotation_270); + return YES; + }]]; + + // when + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center postNotificationName:UIDeviceOrientationDidChangeNotification object:nil]; + + // We need to wait for the dispatch to finish. + WAIT(1000); + + [self.capturer captureOutput:self.capturer.captureSession.outputs[0] + didOutputSampleBuffer:sampleBuffer + fromConnection:self.captureConnectionMock]; + + [self.delegateMock verify]; + CFRelease(sampleBuffer); +#endif +} + +// The XCTest framework considers functions that don't take arguments tests. This is a helper. +- (void)testRotationCamera:(AVCaptureDevicePosition)camera + withOrientation:(UIDeviceOrientation)deviceOrientation { +#if TARGET_OS_IPHONE + // Mock the AVCaptureConnection as we will get the camera position from the connection's + // input ports. + AVCaptureDeviceInput *inputPortMock = OCMClassMock([AVCaptureDeviceInput class]); + AVCaptureInputPort *captureInputPort = OCMClassMock([AVCaptureInputPort class]); + NSArray *inputPortsArrayMock = @[captureInputPort]; + AVCaptureDevice *captureDeviceMock = OCMClassMock([AVCaptureDevice class]); + OCMStub(((AVCaptureConnection *)self.captureConnectionMock).inputPorts). + andReturn(inputPortsArrayMock); + OCMStub(captureInputPort.input).andReturn(inputPortMock); + OCMStub(inputPortMock.device).andReturn(captureDeviceMock); + OCMStub(captureDeviceMock.position).andReturn(camera); + + XCTExpectFailure(@"Setting orientation on UIDevice is not supported"); + [UIDevice.currentDevice setValue:@(deviceOrientation) forKey:@"orientation"]; + + CMSampleBufferRef sampleBuffer = createTestSampleBufferRef(); + + [[self.delegateMock expect] capturer:self.capturer + didCaptureVideoFrame:[OCMArg checkWithBlock:^BOOL(RTC_OBJC_TYPE(RTCVideoFrame) * + expectedFrame) { + if (camera == AVCaptureDevicePositionFront) { + if (deviceOrientation == UIDeviceOrientationLandscapeLeft) { + XCTAssertEqual(expectedFrame.rotation, RTCVideoRotation_180); + } else if (deviceOrientation == UIDeviceOrientationLandscapeRight) { + XCTAssertEqual(expectedFrame.rotation, RTCVideoRotation_0); + } + } else if (camera == AVCaptureDevicePositionBack) { + if (deviceOrientation == UIDeviceOrientationLandscapeLeft) { + XCTAssertEqual(expectedFrame.rotation, RTCVideoRotation_0); + } else if (deviceOrientation == UIDeviceOrientationLandscapeRight) { + XCTAssertEqual(expectedFrame.rotation, RTCVideoRotation_180); + } + } + return YES; + }]]; + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center postNotificationName:UIDeviceOrientationDidChangeNotification object:nil]; + + // We need to wait for the dispatch to finish. + WAIT(1000); + + [self.capturer captureOutput:self.capturer.captureSession.outputs[0] + didOutputSampleBuffer:sampleBuffer + fromConnection:self.captureConnectionMock]; + + [self.delegateMock verify]; + + CFRelease(sampleBuffer); +#endif +} + +- (void)testRotationCameraBackLandscapeLeft { + [self testRotationCamera:AVCaptureDevicePositionBack + withOrientation:UIDeviceOrientationLandscapeLeft]; +} + +- (void)testRotationCameraFrontLandscapeLeft { + [self testRotationCamera:AVCaptureDevicePositionFront + withOrientation:UIDeviceOrientationLandscapeLeft]; +} + +- (void)testRotationCameraBackLandscapeRight { + [self testRotationCamera:AVCaptureDevicePositionBack + withOrientation:UIDeviceOrientationLandscapeRight]; +} + +- (void)testRotationCameraFrontLandscapeRight { + [self testRotationCamera:AVCaptureDevicePositionFront + withOrientation:UIDeviceOrientationLandscapeRight]; +} + +#endif + +- (void)setExif:(CMSampleBufferRef)sampleBuffer { + rtc::ScopedCFTypeRef<CFMutableDictionaryRef> exif(CFDictionaryCreateMutable( + kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFDictionarySetValue(exif.get(), CFSTR("LensModel"), CFSTR("iPhone SE back camera 4.15mm f/2.2")); + CMSetAttachment(sampleBuffer, CFSTR("{Exif}"), exif.get(), kCMAttachmentMode_ShouldPropagate); +} + +#if 0 +// See crbug.com/1404878 - XCTExpectFailure and XCTSkip are considered failures + +- (void)testRotationFrame { +#if TARGET_OS_IPHONE + // Mock the AVCaptureConnection as we will get the camera position from the connection's + // input ports. + AVCaptureDeviceInput *inputPortMock = OCMClassMock([AVCaptureDeviceInput class]); + AVCaptureInputPort *captureInputPort = OCMClassMock([AVCaptureInputPort class]); + NSArray *inputPortsArrayMock = @[captureInputPort]; + AVCaptureDevice *captureDeviceMock = OCMClassMock([AVCaptureDevice class]); + OCMStub(((AVCaptureConnection *)self.captureConnectionMock).inputPorts). + andReturn(inputPortsArrayMock); + OCMStub(captureInputPort.input).andReturn(inputPortMock); + OCMStub(inputPortMock.device).andReturn(captureDeviceMock); + OCMStub(captureDeviceMock.position).andReturn(AVCaptureDevicePositionFront); + + XCTExpectFailure(@"Setting orientation on UIDevice is not supported"); + [UIDevice.currentDevice setValue:@(UIDeviceOrientationLandscapeLeft) forKey:@"orientation"]; + + CMSampleBufferRef sampleBuffer = createTestSampleBufferRef(); + + [[self.delegateMock expect] capturer:self.capturer + didCaptureVideoFrame:[OCMArg checkWithBlock:^BOOL(RTC_OBJC_TYPE(RTCVideoFrame) * + expectedFrame) { + // Front camera and landscape left should return 180. But the frame's exif + // we add below says its from the back camera, so rotation should be 0. + XCTAssertEqual(expectedFrame.rotation, RTCVideoRotation_0); + return YES; + }]]; + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center postNotificationName:UIDeviceOrientationDidChangeNotification object:nil]; + + // We need to wait for the dispatch to finish. + WAIT(1000); + + [self setExif:sampleBuffer]; + + [self.capturer captureOutput:self.capturer.captureSession.outputs[0] + didOutputSampleBuffer:sampleBuffer + fromConnection:self.captureConnectionMock]; + + [self.delegateMock verify]; + CFRelease(sampleBuffer); +#endif +} + +#endif + +- (void)testImageExif { +#if TARGET_OS_IPHONE + CMSampleBufferRef sampleBuffer = createTestSampleBufferRef(); + [self setExif:sampleBuffer]; + + AVCaptureDevicePosition cameraPosition = [AVCaptureSession + devicePositionForSampleBuffer:sampleBuffer]; + XCTAssertEqual(cameraPosition, AVCaptureDevicePositionBack); +#endif +} + +@end + +@interface RTCCameraVideoCapturerTestsWithMockedCaptureSession : XCTestCase +@property(nonatomic, strong) id delegateMock; +@property(nonatomic, strong) id deviceMock; +@property(nonatomic, strong) id captureSessionMock; +@property(nonatomic, strong) RTC_OBJC_TYPE(RTCCameraVideoCapturer) * capturer; +@end + +@implementation RTCCameraVideoCapturerTestsWithMockedCaptureSession +@synthesize delegateMock = _delegateMock; +@synthesize deviceMock = _deviceMock; +@synthesize captureSessionMock = _captureSessionMock; +@synthesize capturer = _capturer; + +- (void)setUp { + self.captureSessionMock = OCMStrictClassMock([AVCaptureSession class]); + OCMStub([self.captureSessionMock setSessionPreset:[OCMArg any]]); + OCMStub([self.captureSessionMock setUsesApplicationAudioSession:NO]); + OCMStub([self.captureSessionMock canAddOutput:[OCMArg any]]).andReturn(YES); + OCMStub([self.captureSessionMock addOutput:[OCMArg any]]); + OCMStub([self.captureSessionMock beginConfiguration]); + OCMStub([self.captureSessionMock commitConfiguration]); + self.delegateMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoCapturerDelegate))); + self.capturer = + [[RTC_OBJC_TYPE(RTCCameraVideoCapturer) alloc] initWithDelegate:self.delegateMock + captureSession:self.captureSessionMock]; + self.deviceMock = [RTCCameraVideoCapturerTests createDeviceMock]; +} + +- (void)tearDown { + [self.delegateMock stopMocking]; + [self.deviceMock stopMocking]; + self.delegateMock = nil; + self.deviceMock = nil; + self.capturer = nil; + self.captureSessionMock = nil; +} + +#pragma mark - test cases + +- (void)testStartingAndStoppingCapture { + id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([captureDeviceInputMock deviceInputWithDevice:self.deviceMock error:[OCMArg setTo:nil]]) + .andReturn(expectedDeviceInputMock); + + OCMStub([self.deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([self.deviceMock unlockForConfiguration]); + OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES); + OCMStub([_captureSessionMock inputs]).andReturn(@[ expectedDeviceInputMock ]); + OCMStub([_captureSessionMock removeInput:expectedDeviceInputMock]); + + // Set expectation that the capture session should be started with correct device. + OCMExpect([_captureSessionMock addInput:expectedDeviceInputMock]); + OCMExpect([_captureSessionMock startRunning]); + OCMExpect([_captureSessionMock stopRunning]); + + id format = OCMClassMock([AVCaptureDeviceFormat class]); + [self.capturer startCaptureWithDevice:self.deviceMock format:format fps:30]; + [self.capturer stopCapture]; + + // Start capture code is dispatched async. + OCMVerifyAllWithDelay(_captureSessionMock, 15); +} + +- (void)testStartCaptureFailingToLockForConfiguration { + // The captureSessionMock is a strict mock, so this test will crash if the startCapture + // method does not return when failing to lock for configuration. + OCMExpect([self.deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(NO); + + id format = OCMClassMock([AVCaptureDeviceFormat class]); + [self.capturer startCaptureWithDevice:self.deviceMock format:format fps:30]; + + // Start capture code is dispatched async. + OCMVerifyAllWithDelay(self.deviceMock, 15); +} + +- (void)testStartingAndStoppingCaptureWithCallbacks { + id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([captureDeviceInputMock deviceInputWithDevice:self.deviceMock error:[OCMArg setTo:nil]]) + .andReturn(expectedDeviceInputMock); + + OCMStub([self.deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([self.deviceMock unlockForConfiguration]); + OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES); + OCMStub([_captureSessionMock inputs]).andReturn(@[ expectedDeviceInputMock ]); + OCMStub([_captureSessionMock removeInput:expectedDeviceInputMock]); + + // Set expectation that the capture session should be started with correct device. + OCMExpect([_captureSessionMock addInput:expectedDeviceInputMock]); + OCMExpect([_captureSessionMock startRunning]); + OCMExpect([_captureSessionMock stopRunning]); + + dispatch_semaphore_t completedStopSemaphore = dispatch_semaphore_create(0); + + __block BOOL completedStart = NO; + id format = OCMClassMock([AVCaptureDeviceFormat class]); + [self.capturer startCaptureWithDevice:self.deviceMock + format:format + fps:30 + completionHandler:^(NSError *error) { + XCTAssertEqual(error, nil); + completedStart = YES; + }]; + + __block BOOL completedStop = NO; + [self.capturer stopCaptureWithCompletionHandler:^{ + completedStop = YES; + dispatch_semaphore_signal(completedStopSemaphore); + }]; + + dispatch_semaphore_wait(completedStopSemaphore, + dispatch_time(DISPATCH_TIME_NOW, 15.0 * NSEC_PER_SEC)); + OCMVerifyAllWithDelay(_captureSessionMock, 15); + XCTAssertTrue(completedStart); + XCTAssertTrue(completedStop); +} + +- (void)testStartCaptureFailingToLockForConfigurationWithCallback { + id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([captureDeviceInputMock deviceInputWithDevice:self.deviceMock error:[OCMArg setTo:nil]]) + .andReturn(expectedDeviceInputMock); + + id errorMock = OCMClassMock([NSError class]); + + OCMStub([self.deviceMock lockForConfiguration:[OCMArg setTo:errorMock]]).andReturn(NO); + OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES); + OCMStub([self.deviceMock unlockForConfiguration]); + + OCMExpect([_captureSessionMock addInput:expectedDeviceInputMock]); + + dispatch_semaphore_t completedStartSemaphore = dispatch_semaphore_create(0); + __block NSError *callbackError = nil; + + id format = OCMClassMock([AVCaptureDeviceFormat class]); + [self.capturer startCaptureWithDevice:self.deviceMock + format:format + fps:30 + completionHandler:^(NSError *error) { + callbackError = error; + dispatch_semaphore_signal(completedStartSemaphore); + }]; + + long ret = dispatch_semaphore_wait(completedStartSemaphore, + dispatch_time(DISPATCH_TIME_NOW, 15.0 * NSEC_PER_SEC)); + XCTAssertEqual(ret, 0); + XCTAssertEqual(callbackError, errorMock); +} + +// TODO(crbug.com/webrtc/14829): Test is disabled on iOS < 16 and broken on iOS 16. +- (void)DISABLED_testStartCaptureSetsOutputDimensionsInvalidPixelFormat { + id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([captureDeviceInputMock deviceInputWithDevice:_deviceMock error:[OCMArg setTo:nil]]) + .andReturn(expectedDeviceInputMock); + + OCMStub([_deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([_deviceMock unlockForConfiguration]); + OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES); + OCMStub([_captureSessionMock addInput:expectedDeviceInputMock]); + OCMStub([_captureSessionMock inputs]).andReturn(@[ expectedDeviceInputMock ]); + OCMStub([_captureSessionMock removeInput:expectedDeviceInputMock]); + OCMStub([_captureSessionMock startRunning]); + OCMStub([_captureSessionMock stopRunning]); + + id deviceFormatMock = OCMClassMock([AVCaptureDeviceFormat class]); + CMVideoFormatDescriptionRef formatDescription; + + int width = 110; + int height = 220; + FourCharCode pixelFormat = 0x18000000; + CMVideoFormatDescriptionCreate(nil, pixelFormat, width, height, nil, &formatDescription); + OCMStub([deviceFormatMock formatDescription]).andReturn(formatDescription); + + [_capturer startCaptureWithDevice:_deviceMock format:deviceFormatMock fps:30]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"StopCompletion"]; + [_capturer stopCaptureWithCompletionHandler:^(void) { + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:15 handler:nil]; + + OCMVerify([_captureSessionMock + addOutput:[OCMArg checkWithBlock:^BOOL(AVCaptureVideoDataOutput *output) { + if (@available(iOS 16, *)) { + XCTAssertEqual(width, [output.videoSettings[(id)kCVPixelBufferWidthKey] intValue]); + XCTAssertEqual(height, [output.videoSettings[(id)kCVPixelBufferHeightKey] intValue]); + } else { + XCTAssertEqual(0, [output.videoSettings[(id)kCVPixelBufferWidthKey] intValue]); + XCTAssertEqual(0, [output.videoSettings[(id)kCVPixelBufferHeightKey] intValue]); + } + XCTAssertEqual( + (FourCharCode)kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, + [output.videoSettings[(id)kCVPixelBufferPixelFormatTypeKey] unsignedIntValue]); + return YES; + }]]); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCCertificateTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCCertificateTest.mm new file mode 100644 index 0000000000..bc1347336c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCCertificateTest.mm @@ -0,0 +1,73 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <vector> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCConfiguration+Private.h" +#import "api/peerconnection/RTCConfiguration.h" +#import "api/peerconnection/RTCIceServer.h" +#import "api/peerconnection/RTCMediaConstraints.h" +#import "api/peerconnection/RTCPeerConnection.h" +#import "api/peerconnection/RTCPeerConnectionFactory.h" +#import "helpers/NSString+StdString.h" + +@interface RTCCertificateTest : XCTestCase +@end + +@implementation RTCCertificateTest + +- (void)testCertificateIsUsedInConfig { + RTC_OBJC_TYPE(RTCConfiguration) *originalConfig = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + + NSArray *urlStrings = @[ @"stun:stun1.example.net" ]; + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:urlStrings]; + originalConfig.iceServers = @[ server ]; + + // Generate a new certificate. + RTC_OBJC_TYPE(RTCCertificate) *originalCertificate = [RTC_OBJC_TYPE(RTCCertificate) + generateCertificateWithParams:@{@"expires" : @100000, @"name" : @"RSASSA-PKCS1-v1_5"}]; + + // Store certificate in configuration. + originalConfig.certificate = originalCertificate; + + RTC_OBJC_TYPE(RTCMediaConstraints) *contraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCPeerConnectionFactory) *factory = + [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + + // Create PeerConnection with this certificate. + RTC_OBJC_TYPE(RTCPeerConnection) *peerConnection = + [factory peerConnectionWithConfiguration:originalConfig constraints:contraints delegate:nil]; + + // Retrieve certificate from the configuration. + RTC_OBJC_TYPE(RTCConfiguration) *retrievedConfig = peerConnection.configuration; + + // Extract PEM strings from original certificate. + std::string originalPrivateKeyField = [[originalCertificate private_key] UTF8String]; + std::string originalCertificateField = [[originalCertificate certificate] UTF8String]; + + // Extract PEM strings from certificate retrieved from configuration. + RTC_OBJC_TYPE(RTCCertificate) *retrievedCertificate = retrievedConfig.certificate; + std::string retrievedPrivateKeyField = [[retrievedCertificate private_key] UTF8String]; + std::string retrievedCertificateField = [[retrievedCertificate certificate] UTF8String]; + + // Check that the original certificate and retrieved certificate match. + EXPECT_EQ(originalPrivateKeyField, retrievedPrivateKeyField); + EXPECT_EQ(retrievedCertificateField, retrievedCertificateField); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCConfigurationTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCConfigurationTest.mm new file mode 100644 index 0000000000..18cc97191e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCConfigurationTest.mm @@ -0,0 +1,162 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <vector> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCConfiguration+Private.h" +#import "api/peerconnection/RTCConfiguration.h" +#import "api/peerconnection/RTCIceServer.h" +#import "helpers/NSString+StdString.h" + +@interface RTCConfigurationTest : XCTestCase +@end + +@implementation RTCConfigurationTest + +- (void)testConversionToNativeConfiguration { + NSArray *urlStrings = @[ @"stun:stun1.example.net" ]; + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:urlStrings]; + + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.iceServers = @[ server ]; + config.iceTransportPolicy = RTCIceTransportPolicyRelay; + config.bundlePolicy = RTCBundlePolicyMaxBundle; + config.rtcpMuxPolicy = RTCRtcpMuxPolicyNegotiate; + config.tcpCandidatePolicy = RTCTcpCandidatePolicyDisabled; + config.candidateNetworkPolicy = RTCCandidateNetworkPolicyLowCost; + const int maxPackets = 60; + const int timeout = 1; + const int interval = 2; + config.audioJitterBufferMaxPackets = maxPackets; + config.audioJitterBufferFastAccelerate = YES; + config.iceConnectionReceivingTimeout = timeout; + config.iceBackupCandidatePairPingInterval = interval; + config.continualGatheringPolicy = + RTCContinualGatheringPolicyGatherContinually; + config.shouldPruneTurnPorts = YES; + config.cryptoOptions = + [[RTC_OBJC_TYPE(RTCCryptoOptions) alloc] initWithSrtpEnableGcmCryptoSuites:YES + srtpEnableAes128Sha1_32CryptoCipher:YES + srtpEnableEncryptedRtpHeaderExtensions:YES + sframeRequireFrameEncryption:YES]; + config.rtcpAudioReportIntervalMs = 2500; + config.rtcpVideoReportIntervalMs = 3750; + + std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> + nativeConfig([config createNativeConfiguration]); + EXPECT_TRUE(nativeConfig.get()); + EXPECT_EQ(1u, nativeConfig->servers.size()); + webrtc::PeerConnectionInterface::IceServer nativeServer = + nativeConfig->servers.front(); + EXPECT_EQ(1u, nativeServer.urls.size()); + EXPECT_EQ("stun:stun1.example.net", nativeServer.urls.front()); + + EXPECT_EQ(webrtc::PeerConnectionInterface::kRelay, nativeConfig->type); + EXPECT_EQ(webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle, + nativeConfig->bundle_policy); + EXPECT_EQ(webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate, + nativeConfig->rtcp_mux_policy); + EXPECT_EQ(webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled, + nativeConfig->tcp_candidate_policy); + EXPECT_EQ(webrtc::PeerConnectionInterface::kCandidateNetworkPolicyLowCost, + nativeConfig->candidate_network_policy); + EXPECT_EQ(maxPackets, nativeConfig->audio_jitter_buffer_max_packets); + EXPECT_EQ(true, nativeConfig->audio_jitter_buffer_fast_accelerate); + EXPECT_EQ(timeout, nativeConfig->ice_connection_receiving_timeout); + EXPECT_EQ(interval, nativeConfig->ice_backup_candidate_pair_ping_interval); + EXPECT_EQ(webrtc::PeerConnectionInterface::GATHER_CONTINUALLY, + nativeConfig->continual_gathering_policy); + EXPECT_EQ(true, nativeConfig->prune_turn_ports); + EXPECT_EQ(true, nativeConfig->crypto_options->srtp.enable_gcm_crypto_suites); + EXPECT_EQ(true, nativeConfig->crypto_options->srtp.enable_aes128_sha1_32_crypto_cipher); + EXPECT_EQ(true, nativeConfig->crypto_options->srtp.enable_encrypted_rtp_header_extensions); + EXPECT_EQ(true, nativeConfig->crypto_options->sframe.require_frame_encryption); + EXPECT_EQ(2500, nativeConfig->audio_rtcp_report_interval_ms()); + EXPECT_EQ(3750, nativeConfig->video_rtcp_report_interval_ms()); +} + +- (void)testNativeConversionToConfiguration { + NSArray *urlStrings = @[ @"stun:stun1.example.net" ]; + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:urlStrings]; + + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.iceServers = @[ server ]; + config.iceTransportPolicy = RTCIceTransportPolicyRelay; + config.bundlePolicy = RTCBundlePolicyMaxBundle; + config.rtcpMuxPolicy = RTCRtcpMuxPolicyNegotiate; + config.tcpCandidatePolicy = RTCTcpCandidatePolicyDisabled; + config.candidateNetworkPolicy = RTCCandidateNetworkPolicyLowCost; + const int maxPackets = 60; + const int timeout = 1; + const int interval = 2; + config.audioJitterBufferMaxPackets = maxPackets; + config.audioJitterBufferFastAccelerate = YES; + config.iceConnectionReceivingTimeout = timeout; + config.iceBackupCandidatePairPingInterval = interval; + config.continualGatheringPolicy = + RTCContinualGatheringPolicyGatherContinually; + config.shouldPruneTurnPorts = YES; + config.cryptoOptions = + [[RTC_OBJC_TYPE(RTCCryptoOptions) alloc] initWithSrtpEnableGcmCryptoSuites:YES + srtpEnableAes128Sha1_32CryptoCipher:NO + srtpEnableEncryptedRtpHeaderExtensions:NO + sframeRequireFrameEncryption:NO]; + config.rtcpAudioReportIntervalMs = 1500; + config.rtcpVideoReportIntervalMs = 2150; + + webrtc::PeerConnectionInterface::RTCConfiguration *nativeConfig = + [config createNativeConfiguration]; + RTC_OBJC_TYPE(RTCConfiguration) *newConfig = + [[RTC_OBJC_TYPE(RTCConfiguration) alloc] initWithNativeConfiguration:*nativeConfig]; + EXPECT_EQ([config.iceServers count], newConfig.iceServers.count); + RTC_OBJC_TYPE(RTCIceServer) *newServer = newConfig.iceServers[0]; + RTC_OBJC_TYPE(RTCIceServer) *origServer = config.iceServers[0]; + EXPECT_EQ(origServer.urlStrings.count, server.urlStrings.count); + std::string origUrl = origServer.urlStrings.firstObject.UTF8String; + std::string url = newServer.urlStrings.firstObject.UTF8String; + EXPECT_EQ(origUrl, url); + + EXPECT_EQ(config.iceTransportPolicy, newConfig.iceTransportPolicy); + EXPECT_EQ(config.bundlePolicy, newConfig.bundlePolicy); + EXPECT_EQ(config.rtcpMuxPolicy, newConfig.rtcpMuxPolicy); + EXPECT_EQ(config.tcpCandidatePolicy, newConfig.tcpCandidatePolicy); + EXPECT_EQ(config.candidateNetworkPolicy, newConfig.candidateNetworkPolicy); + EXPECT_EQ(config.audioJitterBufferMaxPackets, newConfig.audioJitterBufferMaxPackets); + EXPECT_EQ(config.audioJitterBufferFastAccelerate, newConfig.audioJitterBufferFastAccelerate); + EXPECT_EQ(config.iceConnectionReceivingTimeout, newConfig.iceConnectionReceivingTimeout); + EXPECT_EQ(config.iceBackupCandidatePairPingInterval, + newConfig.iceBackupCandidatePairPingInterval); + EXPECT_EQ(config.continualGatheringPolicy, newConfig.continualGatheringPolicy); + EXPECT_EQ(config.shouldPruneTurnPorts, newConfig.shouldPruneTurnPorts); + EXPECT_EQ(config.cryptoOptions.srtpEnableGcmCryptoSuites, + newConfig.cryptoOptions.srtpEnableGcmCryptoSuites); + EXPECT_EQ(config.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher, + newConfig.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher); + EXPECT_EQ(config.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions, + newConfig.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions); + EXPECT_EQ(config.cryptoOptions.sframeRequireFrameEncryption, + newConfig.cryptoOptions.sframeRequireFrameEncryption); + EXPECT_EQ(config.rtcpAudioReportIntervalMs, newConfig.rtcpAudioReportIntervalMs); + EXPECT_EQ(config.rtcpVideoReportIntervalMs, newConfig.rtcpVideoReportIntervalMs); +} + +- (void)testDefaultValues { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + EXPECT_EQ(config.cryptoOptions, nil); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCDataChannelConfigurationTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCDataChannelConfigurationTest.mm new file mode 100644 index 0000000000..ccebd74198 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCDataChannelConfigurationTest.mm @@ -0,0 +1,51 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCDataChannelConfiguration+Private.h" +#import "api/peerconnection/RTCDataChannelConfiguration.h" +#import "helpers/NSString+StdString.h" + +@interface RTCDataChannelConfigurationTest : XCTestCase +@end + +@implementation RTCDataChannelConfigurationTest + +- (void)testConversionToNativeDataChannelInit { + BOOL isOrdered = NO; + int maxPacketLifeTime = 5; + int maxRetransmits = 4; + BOOL isNegotiated = YES; + int channelId = 4; + NSString *protocol = @"protocol"; + + RTC_OBJC_TYPE(RTCDataChannelConfiguration) *dataChannelConfig = + [[RTC_OBJC_TYPE(RTCDataChannelConfiguration) alloc] init]; + dataChannelConfig.isOrdered = isOrdered; + dataChannelConfig.maxPacketLifeTime = maxPacketLifeTime; + dataChannelConfig.maxRetransmits = maxRetransmits; + dataChannelConfig.isNegotiated = isNegotiated; + dataChannelConfig.channelId = channelId; + dataChannelConfig.protocol = protocol; + + webrtc::DataChannelInit nativeInit = dataChannelConfig.nativeDataChannelInit; + EXPECT_EQ(isOrdered, nativeInit.ordered); + EXPECT_EQ(maxPacketLifeTime, nativeInit.maxRetransmitTime); + EXPECT_EQ(maxRetransmits, nativeInit.maxRetransmits); + EXPECT_EQ(isNegotiated, nativeInit.negotiated); + EXPECT_EQ(channelId, nativeInit.id); + EXPECT_EQ(protocol.stdString, nativeInit.protocol); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCDoNotPutCPlusPlusInFrameworkHeaders_xctest.m b/third_party/libwebrtc/sdk/objc/unittests/RTCDoNotPutCPlusPlusInFrameworkHeaders_xctest.m new file mode 100644 index 0000000000..02bef9bfb7 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCDoNotPutCPlusPlusInFrameworkHeaders_xctest.m @@ -0,0 +1,30 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <XCTest/XCTest.h> + +#import <Foundation/Foundation.h> + +#import <WebRTC/WebRTC.h> + +@interface RTCDoNotPutCPlusPlusInFrameworkHeaders : XCTestCase +@end + +@implementation RTCDoNotPutCPlusPlusInFrameworkHeaders + +- (void)testNoCPlusPlusInFrameworkHeaders { + NSString *fullPath = [NSString stringWithFormat:@"%s", __FILE__]; + NSString *extension = fullPath.pathExtension; + + XCTAssertEqualObjects( + @"m", extension, @"Do not rename %@. It should end with .m.", fullPath.lastPathComponent); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCEncodedImage_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCEncodedImage_xctest.mm new file mode 100644 index 0000000000..84804fee87 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCEncodedImage_xctest.mm @@ -0,0 +1,55 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "api/peerconnection/RTCEncodedImage+Private.h" + +#import <XCTest/XCTest.h> + +@interface RTCEncodedImageTests : XCTestCase +@end + +@implementation RTCEncodedImageTests + +- (void)testInitializedWithNativeEncodedImage { + const auto encoded_data = webrtc::EncodedImageBuffer::Create(); + webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(encoded_data); + + RTC_OBJC_TYPE(RTCEncodedImage) *encodedImage = + [[RTC_OBJC_TYPE(RTCEncodedImage) alloc] initWithNativeEncodedImage:encoded_image]; + + XCTAssertEqual([encodedImage nativeEncodedImage].GetEncodedData(), encoded_data); +} + +- (void)testInitWithNSData { + NSData *bufferData = [NSData data]; + RTC_OBJC_TYPE(RTCEncodedImage) *encodedImage = [[RTC_OBJC_TYPE(RTCEncodedImage) alloc] init]; + encodedImage.buffer = bufferData; + + webrtc::EncodedImage result_encoded_image = [encodedImage nativeEncodedImage]; + XCTAssertTrue(result_encoded_image.GetEncodedData() != nullptr); + XCTAssertEqual(result_encoded_image.GetEncodedData()->data(), bufferData.bytes); +} + +- (void)testRetainsNativeEncodedImage { + RTC_OBJC_TYPE(RTCEncodedImage) * encodedImage; + { + const auto encoded_data = webrtc::EncodedImageBuffer::Create(); + webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(encoded_data); + encodedImage = + [[RTC_OBJC_TYPE(RTCEncodedImage) alloc] initWithNativeEncodedImage:encoded_image]; + } + webrtc::EncodedImage result_encoded_image = [encodedImage nativeEncodedImage]; + XCTAssertTrue(result_encoded_image.GetEncodedData() != nullptr); + XCTAssertTrue(result_encoded_image.GetEncodedData()->data() != nullptr); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCFileVideoCapturer_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCFileVideoCapturer_xctest.mm new file mode 100644 index 0000000000..2407c88c1a --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCFileVideoCapturer_xctest.mm @@ -0,0 +1,114 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "components/capturer/RTCFileVideoCapturer.h" + +#import <XCTest/XCTest.h> + +#include "rtc_base/gunit.h" + +NSString *const kTestFileName = @"foreman.mp4"; +static const int kTestTimeoutMs = 5 * 1000; // 5secs. + +@interface MockCapturerDelegate : NSObject <RTC_OBJC_TYPE (RTCVideoCapturerDelegate)> + +@property(nonatomic, assign) NSInteger capturedFramesCount; + +@end + +@implementation MockCapturerDelegate +@synthesize capturedFramesCount = _capturedFramesCount; + +- (void)capturer:(RTC_OBJC_TYPE(RTCVideoCapturer) *)capturer + didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame { + self.capturedFramesCount++; +} + +@end + +NS_CLASS_AVAILABLE_IOS(10) +@interface RTCFileVideoCapturerTests : XCTestCase + +@property(nonatomic, strong) RTC_OBJC_TYPE(RTCFileVideoCapturer) * capturer; +@property(nonatomic, strong) MockCapturerDelegate *mockDelegate; + +@end + +@implementation RTCFileVideoCapturerTests +@synthesize capturer = _capturer; +@synthesize mockDelegate = _mockDelegate; + +- (void)setUp { + self.mockDelegate = [[MockCapturerDelegate alloc] init]; + self.capturer = [[RTC_OBJC_TYPE(RTCFileVideoCapturer) alloc] initWithDelegate:self.mockDelegate]; +} + +- (void)tearDown { + self.capturer = nil; + self.mockDelegate = nil; +} + +- (void)testCaptureWhenFileNotInBundle { + __block BOOL errorOccured = NO; + + RTCFileVideoCapturerErrorBlock errorBlock = ^void(NSError *error) { + errorOccured = YES; + }; + + [self.capturer startCapturingFromFileNamed:@"not_in_bundle.mov" onError:errorBlock]; + ASSERT_TRUE_WAIT(errorOccured, kTestTimeoutMs); +} + +- (void)testSecondStartCaptureCallFails { + __block BOOL secondError = NO; + + RTCFileVideoCapturerErrorBlock firstErrorBlock = ^void(NSError *error) { + // This block should never be called. + NSLog(@"Error: %@", [error userInfo]); + ASSERT_TRUE(false); + }; + + RTCFileVideoCapturerErrorBlock secondErrorBlock = ^void(NSError *error) { + secondError = YES; + }; + + [self.capturer startCapturingFromFileNamed:kTestFileName onError:firstErrorBlock]; + [self.capturer startCapturingFromFileNamed:kTestFileName onError:secondErrorBlock]; + + ASSERT_TRUE_WAIT(secondError, kTestTimeoutMs); +} + +- (void)testStartStopCapturer { +#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) + if (@available(iOS 10, *)) { + [self.capturer startCapturingFromFileNamed:kTestFileName onError:nil]; + + __block BOOL done = NO; + __block NSInteger capturedFrames = -1; + NSInteger capturedFramesAfterStop = -1; + + // We're dispatching the `stopCapture` with delay to ensure the capturer has + // had the chance to capture several frames. + dispatch_time_t captureDelay = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); // 2secs. + dispatch_after(captureDelay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + capturedFrames = self.mockDelegate.capturedFramesCount; + [self.capturer stopCapture]; + done = YES; + }); + WAIT(done, kTestTimeoutMs); + + capturedFramesAfterStop = self.mockDelegate.capturedFramesCount; + ASSERT_TRUE(capturedFrames != -1); + ASSERT_EQ(capturedFrames, capturedFramesAfterStop); + } +#endif +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCH264ProfileLevelId_xctest.m b/third_party/libwebrtc/sdk/objc/unittests/RTCH264ProfileLevelId_xctest.m new file mode 100644 index 0000000000..ec9dc41796 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCH264ProfileLevelId_xctest.m @@ -0,0 +1,48 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "components/video_codec/RTCH264ProfileLevelId.h" + +#import <XCTest/XCTest.h> + +@interface RTCH264ProfileLevelIdTests : XCTestCase + +@end + +static NSString *level31ConstrainedHigh = @"640c1f"; +static NSString *level31ConstrainedBaseline = @"42e01f"; + +@implementation RTCH264ProfileLevelIdTests + +- (void)testInitWithString { + RTC_OBJC_TYPE(RTCH264ProfileLevelId) *profileLevelId = + [[RTC_OBJC_TYPE(RTCH264ProfileLevelId) alloc] initWithHexString:level31ConstrainedHigh]; + XCTAssertEqual(profileLevelId.profile, RTCH264ProfileConstrainedHigh); + XCTAssertEqual(profileLevelId.level, RTCH264Level3_1); + + profileLevelId = + [[RTC_OBJC_TYPE(RTCH264ProfileLevelId) alloc] initWithHexString:level31ConstrainedBaseline]; + XCTAssertEqual(profileLevelId.profile, RTCH264ProfileConstrainedBaseline); + XCTAssertEqual(profileLevelId.level, RTCH264Level3_1); +} + +- (void)testInitWithProfileAndLevel { + RTC_OBJC_TYPE(RTCH264ProfileLevelId) *profileLevelId = + [[RTC_OBJC_TYPE(RTCH264ProfileLevelId) alloc] initWithProfile:RTCH264ProfileConstrainedHigh + level:RTCH264Level3_1]; + XCTAssertEqualObjects(profileLevelId.hexString, level31ConstrainedHigh); + + profileLevelId = [[RTC_OBJC_TYPE(RTCH264ProfileLevelId) alloc] + initWithProfile:RTCH264ProfileConstrainedBaseline + level:RTCH264Level3_1]; + XCTAssertEqualObjects(profileLevelId.hexString, level31ConstrainedBaseline); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCIceCandidateTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCIceCandidateTest.mm new file mode 100644 index 0000000000..576411985d --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCIceCandidateTest.mm @@ -0,0 +1,60 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <memory> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCIceCandidate+Private.h" +#import "api/peerconnection/RTCIceCandidate.h" +#import "helpers/NSString+StdString.h" + +@interface RTCIceCandidateTest : XCTestCase +@end + +@implementation RTCIceCandidateTest + +- (void)testCandidate { + NSString *sdp = @"candidate:4025901590 1 udp 2122265343 " + "fdff:2642:12a6:fe38:c001:beda:fcf9:51aa " + "59052 typ host generation 0"; + + RTC_OBJC_TYPE(RTCIceCandidate) *candidate = + [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithSdp:sdp sdpMLineIndex:0 sdpMid:@"audio"]; + + std::unique_ptr<webrtc::IceCandidateInterface> nativeCandidate = + candidate.nativeCandidate; + EXPECT_EQ("audio", nativeCandidate->sdp_mid()); + EXPECT_EQ(0, nativeCandidate->sdp_mline_index()); + + std::string sdpString; + nativeCandidate->ToString(&sdpString); + EXPECT_EQ(sdp.stdString, sdpString); +} + +- (void)testInitFromNativeCandidate { + std::string sdp("candidate:4025901590 1 udp 2122265343 " + "fdff:2642:12a6:fe38:c001:beda:fcf9:51aa " + "59052 typ host generation 0"); + webrtc::IceCandidateInterface *nativeCandidate = + webrtc::CreateIceCandidate("audio", 0, sdp, nullptr); + + RTC_OBJC_TYPE(RTCIceCandidate) *iceCandidate = + [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithNativeCandidate:nativeCandidate]; + EXPECT_TRUE([@"audio" isEqualToString:iceCandidate.sdpMid]); + EXPECT_EQ(0, iceCandidate.sdpMLineIndex); + + EXPECT_EQ(sdp, iceCandidate.sdp.stdString); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCIceServerTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCIceServerTest.mm new file mode 100644 index 0000000000..772653c4dc --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCIceServerTest.mm @@ -0,0 +1,136 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <vector> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCIceServer+Private.h" +#import "api/peerconnection/RTCIceServer.h" +#import "helpers/NSString+StdString.h" + +@interface RTCIceServerTest : XCTestCase +@end + +@implementation RTCIceServerTest + +- (void)testOneURLServer { + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:@[ @"stun:stun1.example.net" ]]; + + webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer; + EXPECT_EQ(1u, iceStruct.urls.size()); + EXPECT_EQ("stun:stun1.example.net", iceStruct.urls.front()); + EXPECT_EQ("", iceStruct.username); + EXPECT_EQ("", iceStruct.password); +} + +- (void)testTwoURLServer { + RTC_OBJC_TYPE(RTCIceServer) *server = [[RTC_OBJC_TYPE(RTCIceServer) alloc] + initWithURLStrings:@[ @"turn1:turn1.example.net", @"turn2:turn2.example.net" ]]; + + webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer; + EXPECT_EQ(2u, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("turn2:turn2.example.net", iceStruct.urls.back()); + EXPECT_EQ("", iceStruct.username); + EXPECT_EQ("", iceStruct.password); +} + +- (void)testPasswordCredential { + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ] + username:@"username" + credential:@"credential"]; + webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer; + EXPECT_EQ(1u, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("username", iceStruct.username); + EXPECT_EQ("credential", iceStruct.password); +} + +- (void)testHostname { + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ] + username:@"username" + credential:@"credential" + tlsCertPolicy:RTCTlsCertPolicySecure + hostname:@"hostname"]; + webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer; + EXPECT_EQ(1u, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("username", iceStruct.username); + EXPECT_EQ("credential", iceStruct.password); + EXPECT_EQ("hostname", iceStruct.hostname); +} + +- (void)testTlsAlpnProtocols { + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ] + username:@"username" + credential:@"credential" + tlsCertPolicy:RTCTlsCertPolicySecure + hostname:@"hostname" + tlsAlpnProtocols:@[ @"proto1", @"proto2" ]]; + webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer; + EXPECT_EQ(1u, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("username", iceStruct.username); + EXPECT_EQ("credential", iceStruct.password); + EXPECT_EQ("hostname", iceStruct.hostname); + EXPECT_EQ(2u, iceStruct.tls_alpn_protocols.size()); +} + +- (void)testTlsEllipticCurves { + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ] + username:@"username" + credential:@"credential" + tlsCertPolicy:RTCTlsCertPolicySecure + hostname:@"hostname" + tlsAlpnProtocols:@[ @"proto1", @"proto2" ] + tlsEllipticCurves:@[ @"curve1", @"curve2" ]]; + webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer; + EXPECT_EQ(1u, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("username", iceStruct.username); + EXPECT_EQ("credential", iceStruct.password); + EXPECT_EQ("hostname", iceStruct.hostname); + EXPECT_EQ(2u, iceStruct.tls_alpn_protocols.size()); + EXPECT_EQ(2u, iceStruct.tls_elliptic_curves.size()); +} + +- (void)testInitFromNativeServer { + webrtc::PeerConnectionInterface::IceServer nativeServer; + nativeServer.username = "username"; + nativeServer.password = "password"; + nativeServer.urls.push_back("stun:stun.example.net"); + nativeServer.hostname = "hostname"; + nativeServer.tls_alpn_protocols.push_back("proto1"); + nativeServer.tls_alpn_protocols.push_back("proto2"); + nativeServer.tls_elliptic_curves.push_back("curve1"); + nativeServer.tls_elliptic_curves.push_back("curve2"); + + RTC_OBJC_TYPE(RTCIceServer) *iceServer = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithNativeServer:nativeServer]; + EXPECT_EQ(1u, iceServer.urlStrings.count); + EXPECT_EQ("stun:stun.example.net", + [NSString stdStringForString:iceServer.urlStrings.firstObject]); + EXPECT_EQ("username", [NSString stdStringForString:iceServer.username]); + EXPECT_EQ("password", [NSString stdStringForString:iceServer.credential]); + EXPECT_EQ("hostname", [NSString stdStringForString:iceServer.hostname]); + EXPECT_EQ(2u, iceServer.tlsAlpnProtocols.count); + EXPECT_EQ(2u, iceServer.tlsEllipticCurves.count); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCMTLVideoView_xctest.m b/third_party/libwebrtc/sdk/objc/unittests/RTCMTLVideoView_xctest.m new file mode 100644 index 0000000000..f152eeec91 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCMTLVideoView_xctest.m @@ -0,0 +1,298 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <XCTest/XCTest.h> + +#import <Foundation/Foundation.h> +#import <MetalKit/MetalKit.h> +#import <OCMock/OCMock.h> + +#import "components/renderer/metal/RTCMTLVideoView.h" + +#import "api/video_frame_buffer/RTCNativeI420Buffer.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/renderer/metal/RTCMTLNV12Renderer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +static size_t kBufferWidth = 200; +static size_t kBufferHeight = 200; + +// Extension of RTC_OBJC_TYPE(RTCMTLVideoView) for testing purposes. +@interface RTC_OBJC_TYPE (RTCMTLVideoView) +(Testing) + + @property(nonatomic, readonly) MTKView *metalView; + ++ (BOOL)isMetalAvailable; ++ (UIView *)createMetalView:(CGRect)frame; ++ (id<RTCMTLRenderer>)createNV12Renderer; ++ (id<RTCMTLRenderer>)createI420Renderer; +- (void)drawInMTKView:(id)view; +@end + +@interface RTCMTLVideoViewTests : XCTestCase +@property(nonatomic, strong) id classMock; +@property(nonatomic, strong) id rendererNV12Mock; +@property(nonatomic, strong) id rendererI420Mock; +@property(nonatomic, strong) id frameMock; +@end + +@implementation RTCMTLVideoViewTests + +@synthesize classMock = _classMock; +@synthesize rendererNV12Mock = _rendererNV12Mock; +@synthesize rendererI420Mock = _rendererI420Mock; +@synthesize frameMock = _frameMock; + +- (void)setUp { + self.classMock = OCMClassMock([RTC_OBJC_TYPE(RTCMTLVideoView) class]); + [self startMockingNilView]; +} + +- (void)tearDown { + [self.classMock stopMocking]; + [self.rendererI420Mock stopMocking]; + [self.rendererNV12Mock stopMocking]; + [self.frameMock stopMocking]; + self.classMock = nil; + self.rendererI420Mock = nil; + self.rendererNV12Mock = nil; + self.frameMock = nil; +} + +- (id)frameMockWithCVPixelBuffer:(BOOL)hasCVPixelBuffer { + id frameMock = OCMClassMock([RTC_OBJC_TYPE(RTCVideoFrame) class]); + if (hasCVPixelBuffer) { + CVPixelBufferRef pixelBufferRef; + CVPixelBufferCreate(kCFAllocatorDefault, + kBufferWidth, + kBufferHeight, + kCVPixelFormatType_420YpCbCr8Planar, + nil, + &pixelBufferRef); + OCMStub([frameMock buffer]) + .andReturn([[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]); + } else { + OCMStub([frameMock buffer]) + .andReturn([[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithWidth:kBufferWidth + height:kBufferHeight]); + } + OCMStub([((RTC_OBJC_TYPE(RTCVideoFrame) *)frameMock) width]).andReturn(kBufferWidth); + OCMStub([((RTC_OBJC_TYPE(RTCVideoFrame) *)frameMock) height]).andReturn(kBufferHeight); + OCMStub([frameMock timeStampNs]).andReturn(arc4random_uniform(INT_MAX)); + return frameMock; +} + +- (id)rendererMockWithSuccessfulSetup:(BOOL)success { + id rendererMock = OCMClassMock([RTCMTLRenderer class]); + OCMStub([rendererMock addRenderingDestination:[OCMArg any]]).andReturn(success); + return rendererMock; +} + +- (void)startMockingNilView { + // Use OCMock 2 syntax here until OCMock is upgraded to 3.4 + [[[self.classMock stub] andReturn:nil] createMetalView:CGRectZero]; +} + +#pragma mark - Test cases + +- (void)testInitAssertsIfMetalUnavailabe { + // given + OCMStub([self.classMock isMetalAvailable]).andReturn(NO); + + // when + BOOL asserts = NO; + @try { + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectZero]; + (void)realView; + } @catch (NSException *ex) { + asserts = YES; + } + + XCTAssertTrue(asserts); +} + +- (void)testRTCVideoRenderNilFrameCallback { + // given + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + self.frameMock = OCMClassMock([RTC_OBJC_TYPE(RTCVideoFrame) class]); + + [[self.frameMock reject] buffer]; + [[self.classMock reject] createNV12Renderer]; + [[self.classMock reject] createI420Renderer]; + + // when + [realView renderFrame:nil]; + [realView drawInMTKView:realView.metalView]; + + // then + [self.frameMock verify]; + [self.classMock verify]; +} + +- (void)testRTCVideoRenderFrameCallbackI420 { + // given + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererI420Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:NO]; + + OCMExpect([self.rendererI420Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createI420Renderer]).andReturn(self.rendererI420Mock); + [[self.classMock reject] createNV12Renderer]; + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + + // when + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + + // then + [self.rendererI420Mock verify]; + [self.classMock verify]; +} + +- (void)testRTCVideoRenderFrameCallbackNV12 { + // given + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererNV12Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + [[self.classMock reject] createI420Renderer]; + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + + // when + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + + // then + [self.rendererNV12Mock verify]; + [self.classMock verify]; +} + +- (void)testRTCVideoRenderWorksAfterReconstruction { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererNV12Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + [[self.classMock reject] createI420Renderer]; + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + [self.rendererNV12Mock verify]; + [self.classMock verify]; + + // Recreate view. + realView = [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + // View hould reinit renderer. + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + [self.rendererNV12Mock verify]; + [self.classMock verify]; +} + +- (void)testDontRedrawOldFrame { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererNV12Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + [[self.classMock reject] createI420Renderer]; + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + + [self.rendererNV12Mock verify]; + [self.classMock verify]; + + [[self.rendererNV12Mock reject] drawFrame:[OCMArg any]]; + + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + + [self.rendererNV12Mock verify]; +} + +- (void)testDoDrawNewFrame { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + self.rendererNV12Mock = [self rendererMockWithSuccessfulSetup:YES]; + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + OCMExpect([self.classMock createNV12Renderer]).andReturn(self.rendererNV12Mock); + [[self.classMock reject] createI420Renderer]; + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + + [self.rendererNV12Mock verify]; + [self.classMock verify]; + + // Get new frame. + self.frameMock = [self frameMockWithCVPixelBuffer:YES]; + OCMExpect([self.rendererNV12Mock drawFrame:self.frameMock]); + + [realView renderFrame:self.frameMock]; + [realView drawInMTKView:realView.metalView]; + + [self.rendererNV12Mock verify]; +} + +- (void)testReportsSizeChangesToDelegate { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + + id delegateMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoViewDelegate))); + CGSize size = CGSizeMake(640, 480); + OCMExpect([delegateMock videoView:[OCMArg any] didChangeVideoSize:size]); + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = + [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectMake(0, 0, 640, 480)]; + realView.delegate = delegateMock; + [realView setSize:size]; + + // Delegate method is invoked with a dispatch_async. + OCMVerifyAllWithDelay(delegateMock, 1); +} + +- (void)testSetContentMode { + OCMStub([self.classMock isMetalAvailable]).andReturn(YES); + id metalKitView = OCMClassMock([MTKView class]); + [[[[self.classMock stub] ignoringNonObjectArgs] andReturn:metalKitView] + createMetalView:CGRectZero]; + OCMExpect([metalKitView setContentMode:UIViewContentModeScaleAspectFill]); + + RTC_OBJC_TYPE(RTCMTLVideoView) *realView = [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] init]; + [realView setVideoContentMode:UIViewContentModeScaleAspectFill]; + + OCMVerify(metalKitView); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCMediaConstraintsTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCMediaConstraintsTest.mm new file mode 100644 index 0000000000..6ed7859ba1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCMediaConstraintsTest.mm @@ -0,0 +1,58 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <memory> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCMediaConstraints+Private.h" +#import "api/peerconnection/RTCMediaConstraints.h" +#import "helpers/NSString+StdString.h" + +@interface RTCMediaConstraintsTests : XCTestCase +@end + +@implementation RTCMediaConstraintsTests + +- (void)testMediaConstraints { + NSDictionary *mandatory = @{@"key1": @"value1", @"key2": @"value2"}; + NSDictionary *optional = @{@"key3": @"value3", @"key4": @"value4"}; + + RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:mandatory + optionalConstraints:optional]; + std::unique_ptr<webrtc::MediaConstraints> nativeConstraints = + [constraints nativeConstraints]; + + webrtc::MediaConstraints::Constraints nativeMandatory = nativeConstraints->GetMandatory(); + [self expectConstraints:mandatory inNativeConstraints:nativeMandatory]; + + webrtc::MediaConstraints::Constraints nativeOptional = nativeConstraints->GetOptional(); + [self expectConstraints:optional inNativeConstraints:nativeOptional]; +} + +- (void)expectConstraints:(NSDictionary *)constraints + inNativeConstraints:(webrtc::MediaConstraints::Constraints)nativeConstraints { + EXPECT_EQ(constraints.count, nativeConstraints.size()); + + for (NSString *key in constraints) { + NSString *value = [constraints objectForKey:key]; + + std::string nativeValue; + bool found = nativeConstraints.FindFirst(key.stdString, &nativeValue); + EXPECT_TRUE(found); + EXPECT_EQ(value.stdString, nativeValue); + } +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCNV12TextureCache_xctest.m b/third_party/libwebrtc/sdk/objc/unittests/RTCNV12TextureCache_xctest.m new file mode 100644 index 0000000000..7bdc538f67 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCNV12TextureCache_xctest.m @@ -0,0 +1,55 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <CoreVideo/CoreVideo.h> +#import <Foundation/Foundation.h> +#import <GLKit/GLKit.h> +#import <XCTest/XCTest.h> + +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/renderer/opengl/RTCNV12TextureCache.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" + +@interface RTCNV12TextureCacheTests : XCTestCase +@end + +@implementation RTCNV12TextureCacheTests { + EAGLContext *_glContext; + RTCNV12TextureCache *_nv12TextureCache; +} + +- (void)setUp { + [super setUp]; + _glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + if (!_glContext) { + _glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + } + _nv12TextureCache = [[RTCNV12TextureCache alloc] initWithContext:_glContext]; +} + +- (void)tearDown { + _nv12TextureCache = nil; + _glContext = nil; + [super tearDown]; +} + +- (void)testNV12TextureCacheDoesNotCrashOnEmptyFrame { + CVPixelBufferRef nullPixelBuffer = NULL; + RTC_OBJC_TYPE(RTCCVPixelBuffer) *badFrameBuffer = + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:nullPixelBuffer]; + RTC_OBJC_TYPE(RTCVideoFrame) *badFrame = + [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:badFrameBuffer + rotation:RTCVideoRotation_0 + timeStampNs:0]; + [_nv12TextureCache uploadFrameToTextures:badFrame]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionFactoryBuilderTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionFactoryBuilderTest.mm new file mode 100644 index 0000000000..5ba5a52a53 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionFactoryBuilderTest.mm @@ -0,0 +1,72 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> +#ifdef __cplusplus +extern "C" { +#endif +#import <OCMock/OCMock.h> +#ifdef __cplusplus +} +#endif +#import "api/peerconnection/RTCPeerConnectionFactory+Native.h" +#import "api/peerconnection/RTCPeerConnectionFactoryBuilder+DefaultComponents.h" +#import "api/peerconnection/RTCPeerConnectionFactoryBuilder.h" + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" + +#include "rtc_base/gunit.h" +#include "rtc_base/system/unused.h" + +@interface RTCPeerConnectionFactoryBuilderTests : XCTestCase +@end + +@implementation RTCPeerConnectionFactoryBuilderTests + +- (void)testBuilder { + id factoryMock = OCMStrictClassMock([RTC_OBJC_TYPE(RTCPeerConnectionFactory) class]); + OCMExpect([factoryMock alloc]).andReturn(factoryMock); + RTC_UNUSED([[[[factoryMock expect] andReturn:factoryMock] ignoringNonObjectArgs] + initWithNativeAudioEncoderFactory:nullptr + nativeAudioDecoderFactory:nullptr + nativeVideoEncoderFactory:nullptr + nativeVideoDecoderFactory:nullptr + audioDeviceModule:nullptr + audioProcessingModule:nullptr]); + RTCPeerConnectionFactoryBuilder* builder = [[RTCPeerConnectionFactoryBuilder alloc] init]; + RTC_OBJC_TYPE(RTCPeerConnectionFactory)* peerConnectionFactory = + [builder createPeerConnectionFactory]; + EXPECT_TRUE(peerConnectionFactory != nil); + OCMVerifyAll(factoryMock); +} + +- (void)testDefaultComponentsBuilder { + id factoryMock = OCMStrictClassMock([RTC_OBJC_TYPE(RTCPeerConnectionFactory) class]); + OCMExpect([factoryMock alloc]).andReturn(factoryMock); + RTC_UNUSED([[[[factoryMock expect] andReturn:factoryMock] ignoringNonObjectArgs] + initWithNativeAudioEncoderFactory:nullptr + nativeAudioDecoderFactory:nullptr + nativeVideoEncoderFactory:nullptr + nativeVideoDecoderFactory:nullptr + audioDeviceModule:nullptr + audioProcessingModule:nullptr]); + RTCPeerConnectionFactoryBuilder* builder = [RTCPeerConnectionFactoryBuilder defaultBuilder]; + RTC_OBJC_TYPE(RTCPeerConnectionFactory)* peerConnectionFactory = + [builder createPeerConnectionFactory]; + EXPECT_TRUE(peerConnectionFactory != nil); + OCMVerifyAll(factoryMock); +} +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionFactory_xctest.m b/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionFactory_xctest.m new file mode 100644 index 0000000000..28743a6fc1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionFactory_xctest.m @@ -0,0 +1,380 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "api/peerconnection/RTCAudioSource.h" +#import "api/peerconnection/RTCConfiguration.h" +#import "api/peerconnection/RTCDataChannel.h" +#import "api/peerconnection/RTCDataChannelConfiguration.h" +#import "api/peerconnection/RTCMediaConstraints.h" +#import "api/peerconnection/RTCMediaStreamTrack.h" +#import "api/peerconnection/RTCPeerConnection.h" +#import "api/peerconnection/RTCPeerConnectionFactory.h" +#import "api/peerconnection/RTCRtpReceiver.h" +#import "api/peerconnection/RTCRtpSender.h" +#import "api/peerconnection/RTCRtpTransceiver.h" +#import "api/peerconnection/RTCSessionDescription.h" +#import "api/peerconnection/RTCVideoSource.h" +#import "rtc_base/system/unused.h" + +#import <XCTest/XCTest.h> + +@interface RTCPeerConnectionFactoryTests : XCTestCase +@end + +@implementation RTCPeerConnectionFactoryTests + +- (void)testPeerConnectionLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + + RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + peerConnection = + [factory peerConnectionWithConfiguration:config constraints:constraints delegate:nil]; + [peerConnection close]; + factory = nil; + } + peerConnection = nil; + } + + XCTAssertTrue(true, @"Expect test does not crash"); +} + +- (void)testMediaStreamLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCMediaStream) * mediaStream; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + mediaStream = [factory mediaStreamWithStreamId:@"mediaStream"]; + factory = nil; + } + mediaStream = nil; + RTC_UNUSED(mediaStream); + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testDataChannelLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCDataChannelConfiguration) *dataChannelConfig = + [[RTC_OBJC_TYPE(RTCDataChannelConfiguration) alloc] init]; + + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection; + RTC_OBJC_TYPE(RTCDataChannel) * dataChannel; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + peerConnection = + [factory peerConnectionWithConfiguration:config constraints:constraints delegate:nil]; + dataChannel = + [peerConnection dataChannelForLabel:@"test_channel" configuration:dataChannelConfig]; + XCTAssertNotNil(dataChannel); + [peerConnection close]; + peerConnection = nil; + factory = nil; + } + dataChannel = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testRTCRtpTransceiverLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; + RTC_OBJC_TYPE(RTCMediaConstraints) *contraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCRtpTransceiverInit) *init = + [[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init]; + + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection; + RTC_OBJC_TYPE(RTCRtpTransceiver) * tranceiver; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + peerConnection = + [factory peerConnectionWithConfiguration:config constraints:contraints delegate:nil]; + tranceiver = [peerConnection addTransceiverOfType:RTCRtpMediaTypeAudio init:init]; + XCTAssertNotNil(tranceiver); + [peerConnection close]; + peerConnection = nil; + factory = nil; + } + tranceiver = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testRTCRtpSenderLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsPlanB; + RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection; + RTC_OBJC_TYPE(RTCRtpSender) * sender; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + peerConnection = + [factory peerConnectionWithConfiguration:config constraints:constraints delegate:nil]; + sender = [peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo streamId:@"stream"]; + XCTAssertNotNil(sender); + [peerConnection close]; + peerConnection = nil; + factory = nil; + } + sender = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testRTCRtpReceiverLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsPlanB; + RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCPeerConnection) * pc1; + RTC_OBJC_TYPE(RTCPeerConnection) * pc2; + + NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *receivers1; + NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *receivers2; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + pc1 = [factory peerConnectionWithConfiguration:config constraints:constraints delegate:nil]; + [pc1 senderWithKind:kRTCMediaStreamTrackKindAudio streamId:@"stream"]; + + pc2 = [factory peerConnectionWithConfiguration:config constraints:constraints delegate:nil]; + [pc2 senderWithKind:kRTCMediaStreamTrackKindAudio streamId:@"stream"]; + + NSTimeInterval negotiationTimeout = 15; + XCTAssertTrue([self negotiatePeerConnection:pc1 + withPeerConnection:pc2 + negotiationTimeout:negotiationTimeout]); + + XCTAssertEqual(pc1.signalingState, RTCSignalingStateStable); + XCTAssertEqual(pc2.signalingState, RTCSignalingStateStable); + + receivers1 = pc1.receivers; + receivers2 = pc2.receivers; + XCTAssertTrue(receivers1.count > 0); + XCTAssertTrue(receivers2.count > 0); + [pc1 close]; + [pc2 close]; + pc1 = nil; + pc2 = nil; + factory = nil; + } + receivers1 = nil; + receivers2 = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testAudioSourceLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCAudioSource) * audioSource; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + audioSource = [factory audioSourceWithConstraints:nil]; + XCTAssertNotNil(audioSource); + factory = nil; + } + audioSource = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testVideoSourceLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCVideoSource) * videoSource; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + videoSource = [factory videoSource]; + XCTAssertNotNil(videoSource); + factory = nil; + } + videoSource = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testAudioTrackLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCAudioTrack) * audioTrack; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + audioTrack = [factory audioTrackWithTrackId:@"audioTrack"]; + XCTAssertNotNil(audioTrack); + factory = nil; + } + audioTrack = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testVideoTrackLifetime { + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + RTC_OBJC_TYPE(RTCVideoTrack) * videoTrack; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + videoTrack = [factory videoTrackWithSource:[factory videoSource] trackId:@"videoTrack"]; + XCTAssertNotNil(videoTrack); + factory = nil; + } + videoTrack = nil; + } + + XCTAssertTrue(true, "Expect test does not crash"); +} + +- (void)testRollback { + @autoreleasepool { + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; + RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{ + kRTCMediaConstraintsOfferToReceiveAudio : kRTCMediaConstraintsValueTrue + } + optionalConstraints:nil]; + + __block RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory; + __block RTC_OBJC_TYPE(RTCPeerConnection) * pc1; + RTCSessionDescription *rollback = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeRollback + sdp:@""]; + + @autoreleasepool { + factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + pc1 = [factory peerConnectionWithConfiguration:config constraints:constraints delegate:nil]; + dispatch_semaphore_t negotiatedSem = dispatch_semaphore_create(0); + [pc1 offerForConstraints:constraints + completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * offer, NSError * error) { + XCTAssertNil(error); + XCTAssertNotNil(offer); + + __weak RTC_OBJC_TYPE(RTCPeerConnection) *weakPC1 = pc1; + [pc1 setLocalDescription:offer + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [weakPC1 setLocalDescription:rollback + completionHandler:^(NSError *error) { + XCTAssertNil(error); + }]; + }]; + NSTimeInterval negotiationTimeout = 15; + dispatch_semaphore_wait( + negotiatedSem, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(negotiationTimeout * NSEC_PER_SEC))); + + XCTAssertEqual(pc1.signalingState, RTCSignalingStateStable); + + [pc1 close]; + pc1 = nil; + factory = nil; + }]; + } + + XCTAssertTrue(true, "Expect test does not crash"); + } +} + +- (bool)negotiatePeerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)pc1 + withPeerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)pc2 + negotiationTimeout:(NSTimeInterval)timeout { + __weak RTC_OBJC_TYPE(RTCPeerConnection) *weakPC1 = pc1; + __weak RTC_OBJC_TYPE(RTCPeerConnection) *weakPC2 = pc2; + RTC_OBJC_TYPE(RTCMediaConstraints) *sdpConstraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{ + kRTCMediaConstraintsOfferToReceiveAudio : kRTCMediaConstraintsValueTrue + } + optionalConstraints:nil]; + + dispatch_semaphore_t negotiatedSem = dispatch_semaphore_create(0); + [weakPC1 offerForConstraints:sdpConstraints + completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * offer, NSError * error) { + XCTAssertNil(error); + XCTAssertNotNil(offer); + [weakPC1 + setLocalDescription:offer + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [weakPC2 + setRemoteDescription:offer + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [weakPC2 + answerForConstraints:sdpConstraints + completionHandler:^( + RTC_OBJC_TYPE(RTCSessionDescription) * answer, + NSError * error) { + XCTAssertNil(error); + XCTAssertNotNil(answer); + [weakPC2 + setLocalDescription:answer + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [weakPC1 + setRemoteDescription:answer + completionHandler:^(NSError *error) { + XCTAssertNil(error); + dispatch_semaphore_signal(negotiatedSem); + }]; + }]; + }]; + }]; + }]; + }]; + + return 0 == + dispatch_semaphore_wait(negotiatedSem, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionTest.mm new file mode 100644 index 0000000000..9ca8403559 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCPeerConnectionTest.mm @@ -0,0 +1,204 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <memory> +#include <vector> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCConfiguration+Private.h" +#import "api/peerconnection/RTCConfiguration.h" +#import "api/peerconnection/RTCCryptoOptions.h" +#import "api/peerconnection/RTCIceCandidate.h" +#import "api/peerconnection/RTCIceServer.h" +#import "api/peerconnection/RTCMediaConstraints.h" +#import "api/peerconnection/RTCPeerConnection.h" +#import "api/peerconnection/RTCPeerConnectionFactory+Native.h" +#import "api/peerconnection/RTCPeerConnectionFactory.h" +#import "api/peerconnection/RTCSessionDescription.h" +#import "helpers/NSString+StdString.h" + +@interface RTCPeerConnectionTests : XCTestCase +@end + +@implementation RTCPeerConnectionTests + +- (void)testConfigurationGetter { + NSArray *urlStrings = @[ @"stun:stun1.example.net" ]; + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:urlStrings]; + + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; + config.iceServers = @[ server ]; + config.iceTransportPolicy = RTCIceTransportPolicyRelay; + config.bundlePolicy = RTCBundlePolicyMaxBundle; + config.rtcpMuxPolicy = RTCRtcpMuxPolicyNegotiate; + config.tcpCandidatePolicy = RTCTcpCandidatePolicyDisabled; + config.candidateNetworkPolicy = RTCCandidateNetworkPolicyLowCost; + const int maxPackets = 60; + const int timeout = 1500; + const int interval = 2000; + config.audioJitterBufferMaxPackets = maxPackets; + config.audioJitterBufferFastAccelerate = YES; + config.iceConnectionReceivingTimeout = timeout; + config.iceBackupCandidatePairPingInterval = interval; + config.continualGatheringPolicy = + RTCContinualGatheringPolicyGatherContinually; + config.shouldPruneTurnPorts = YES; + config.activeResetSrtpParams = YES; + config.cryptoOptions = + [[RTC_OBJC_TYPE(RTCCryptoOptions) alloc] initWithSrtpEnableGcmCryptoSuites:YES + srtpEnableAes128Sha1_32CryptoCipher:YES + srtpEnableEncryptedRtpHeaderExtensions:NO + sframeRequireFrameEncryption:NO]; + + RTC_OBJC_TYPE(RTCMediaConstraints) *contraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCPeerConnectionFactory) *factory = + [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + + RTC_OBJC_TYPE(RTCConfiguration) * newConfig; + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnection) *peerConnection = + [factory peerConnectionWithConfiguration:config constraints:contraints delegate:nil]; + newConfig = peerConnection.configuration; + + EXPECT_TRUE([peerConnection setBweMinBitrateBps:[NSNumber numberWithInt:100000] + currentBitrateBps:[NSNumber numberWithInt:5000000] + maxBitrateBps:[NSNumber numberWithInt:500000000]]); + EXPECT_FALSE([peerConnection setBweMinBitrateBps:[NSNumber numberWithInt:2] + currentBitrateBps:[NSNumber numberWithInt:1] + maxBitrateBps:nil]); + } + + EXPECT_EQ([config.iceServers count], [newConfig.iceServers count]); + RTC_OBJC_TYPE(RTCIceServer) *newServer = newConfig.iceServers[0]; + RTC_OBJC_TYPE(RTCIceServer) *origServer = config.iceServers[0]; + std::string origUrl = origServer.urlStrings.firstObject.UTF8String; + std::string url = newServer.urlStrings.firstObject.UTF8String; + EXPECT_EQ(origUrl, url); + + EXPECT_EQ(config.iceTransportPolicy, newConfig.iceTransportPolicy); + EXPECT_EQ(config.bundlePolicy, newConfig.bundlePolicy); + EXPECT_EQ(config.rtcpMuxPolicy, newConfig.rtcpMuxPolicy); + EXPECT_EQ(config.tcpCandidatePolicy, newConfig.tcpCandidatePolicy); + EXPECT_EQ(config.candidateNetworkPolicy, newConfig.candidateNetworkPolicy); + EXPECT_EQ(config.audioJitterBufferMaxPackets, newConfig.audioJitterBufferMaxPackets); + EXPECT_EQ(config.audioJitterBufferFastAccelerate, newConfig.audioJitterBufferFastAccelerate); + EXPECT_EQ(config.iceConnectionReceivingTimeout, newConfig.iceConnectionReceivingTimeout); + EXPECT_EQ(config.iceBackupCandidatePairPingInterval, + newConfig.iceBackupCandidatePairPingInterval); + EXPECT_EQ(config.continualGatheringPolicy, newConfig.continualGatheringPolicy); + EXPECT_EQ(config.shouldPruneTurnPorts, newConfig.shouldPruneTurnPorts); + EXPECT_EQ(config.activeResetSrtpParams, newConfig.activeResetSrtpParams); + EXPECT_EQ(config.cryptoOptions.srtpEnableGcmCryptoSuites, + newConfig.cryptoOptions.srtpEnableGcmCryptoSuites); + EXPECT_EQ(config.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher, + newConfig.cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher); + EXPECT_EQ(config.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions, + newConfig.cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions); + EXPECT_EQ(config.cryptoOptions.sframeRequireFrameEncryption, + newConfig.cryptoOptions.sframeRequireFrameEncryption); +} + +- (void)testWithDependencies { + NSArray *urlStrings = @[ @"stun:stun1.example.net" ]; + RTC_OBJC_TYPE(RTCIceServer) *server = + [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithURLStrings:urlStrings]; + + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; + config.iceServers = @[ server ]; + RTC_OBJC_TYPE(RTCMediaConstraints) *contraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCPeerConnectionFactory) *factory = + [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + + std::unique_ptr<webrtc::PeerConnectionDependencies> pc_dependencies = + std::make_unique<webrtc::PeerConnectionDependencies>(nullptr); + @autoreleasepool { + RTC_OBJC_TYPE(RTCPeerConnection) *peerConnection = + [factory peerConnectionWithDependencies:config + constraints:contraints + dependencies:std::move(pc_dependencies) + delegate:nil]; + ASSERT_NE(peerConnection, nil); + } +} + +- (void)testWithInvalidSDP { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) *factory = + [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; + RTC_OBJC_TYPE(RTCMediaConstraints) *contraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCPeerConnection) *peerConnection = + [factory peerConnectionWithConfiguration:config constraints:contraints delegate:nil]; + + dispatch_semaphore_t negotiatedSem = dispatch_semaphore_create(0); + [peerConnection setRemoteDescription:[[RTC_OBJC_TYPE(RTCSessionDescription) alloc] + initWithType:RTCSdpTypeOffer + sdp:@"invalid"] + completionHandler:^(NSError *error) { + ASSERT_NE(error, nil); + if (error != nil) { + dispatch_semaphore_signal(negotiatedSem); + } + }]; + + NSTimeInterval timeout = 5; + ASSERT_EQ( + 0, + dispatch_semaphore_wait(negotiatedSem, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)))); + [peerConnection close]; +} + +- (void)testWithInvalidIceCandidate { + RTC_OBJC_TYPE(RTCPeerConnectionFactory) *factory = + [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init]; + + RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; + config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; + RTC_OBJC_TYPE(RTCMediaConstraints) *contraints = + [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{} + optionalConstraints:nil]; + RTC_OBJC_TYPE(RTCPeerConnection) *peerConnection = + [factory peerConnectionWithConfiguration:config constraints:contraints delegate:nil]; + + dispatch_semaphore_t negotiatedSem = dispatch_semaphore_create(0); + [peerConnection addIceCandidate:[[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithSdp:@"invalid" + sdpMLineIndex:-1 + sdpMid:nil] + completionHandler:^(NSError *error) { + ASSERT_NE(error, nil); + if (error != nil) { + dispatch_semaphore_signal(negotiatedSem); + } + }]; + + NSTimeInterval timeout = 5; + ASSERT_EQ( + 0, + dispatch_semaphore_wait(negotiatedSem, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)))); + [peerConnection close]; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCSessionDescriptionTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCSessionDescriptionTest.mm new file mode 100644 index 0000000000..70c82f78ce --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCSessionDescriptionTest.mm @@ -0,0 +1,122 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCSessionDescription+Private.h" +#import "api/peerconnection/RTCSessionDescription.h" +#import "helpers/NSString+StdString.h" + +@interface RTCSessionDescriptionTests : XCTestCase +@end + +@implementation RTCSessionDescriptionTests + +/** + * Test conversion of an Objective-C RTC_OBJC_TYPE(RTCSessionDescription) to a native + * SessionDescriptionInterface (based on the types and SDP strings being equal). + */ +- (void)testSessionDescriptionConversion { + RTC_OBJC_TYPE(RTCSessionDescription) *description = + [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithType:RTCSdpTypeAnswer sdp:[self sdp]]; + + std::unique_ptr<webrtc::SessionDescriptionInterface> nativeDescription = + description.nativeDescription; + + EXPECT_EQ(RTCSdpTypeAnswer, + [RTC_OBJC_TYPE(RTCSessionDescription) typeForStdString:nativeDescription->type()]); + + std::string sdp; + nativeDescription->ToString(&sdp); + EXPECT_EQ([self sdp].stdString, sdp); +} + +- (void)testInitFromNativeSessionDescription { + webrtc::SessionDescriptionInterface *nativeDescription; + + nativeDescription = webrtc::CreateSessionDescription( + webrtc::SessionDescriptionInterface::kAnswer, + [self sdp].stdString, + nullptr); + + RTC_OBJC_TYPE(RTCSessionDescription) *description = + [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:nativeDescription]; + EXPECT_EQ(webrtc::SessionDescriptionInterface::kAnswer, + [RTC_OBJC_TYPE(RTCSessionDescription) stdStringForType:description.type]); + EXPECT_TRUE([[self sdp] isEqualToString:description.sdp]); +} + +- (NSString *)sdp { + return @"v=0\r\n" + "o=- 5319989746393411314 2 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=group:BUNDLE audio video\r\n" + "a=msid-semantic: WMS ARDAMS\r\n" + "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 0 8 126\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:f3o+0HG7l9nwIWFY\r\n" + "a=ice-pwd:VDctmJNCptR2TB7+meDpw7w5\r\n" + "a=fingerprint:sha-256 A9:D5:8D:A8:69:22:39:60:92:AD:94:1A:22:2D:5E:" + "A5:4A:A9:18:C2:35:5D:46:5E:59:BD:1C:AF:38:9F:E6:E1\r\n" + "a=setup:active\r\n" + "a=mid:audio\r\n" + "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/" + "abs-send-time\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=fmtp:111 minptime=10;useinbandfec=1\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:126 telephone-event/8000\r\n" + "a=maxptime:60\r\n" + "a=ssrc:1504474588 cname:V+FdIC5AJpxLhdYQ\r\n" + "a=ssrc:1504474588 msid:ARDAMS ARDAMSa0\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 100 116 117 96\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:f3o+0HG7l9nwIWFY\r\n" + "a=ice-pwd:VDctmJNCptR2TB7+meDpw7w5\r\n" + "a=fingerprint:sha-256 A9:D5:8D:A8:69:22:39:60:92:AD:94:1A:22:2D:5E:" + "A5:4A:A9:18:C2:35:5D:46:5E:59:BD:1C:AF:38:9F:E6:E1\r\n" + "a=setup:active\r\n" + "a=mid:video\r\n" + "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n" + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/" + "abs-send-time\r\n" + "a=extmap:4 urn:3gpp:video-orientation\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtpmap:100 VP8/90000\r\n" + "a=rtcp-fb:100 ccm fir\r\n" + "a=rtcp-fb:100 goog-lntf\r\n" + "a=rtcp-fb:100 nack\r\n" + "a=rtcp-fb:100 nack pli\r\n" + "a=rtcp-fb:100 goog-remb\r\n" + "a=rtpmap:116 red/90000\r\n" + "a=rtpmap:117 ulpfec/90000\r\n" + "a=rtpmap:96 rtx/90000\r\n" + "a=fmtp:96 apt=100\r\n" + "a=ssrc-group:FID 498297514 1644357692\r\n" + "a=ssrc:498297514 cname:V+FdIC5AJpxLhdYQ\r\n" + "a=ssrc:498297514 msid:ARDAMS ARDAMSv0\r\n" + "a=ssrc:1644357692 cname:V+FdIC5AJpxLhdYQ\r\n" + "a=ssrc:1644357692 msid:ARDAMS ARDAMSv0\r\n"; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/RTCTracingTest.mm b/third_party/libwebrtc/sdk/objc/unittests/RTCTracingTest.mm new file mode 100644 index 0000000000..ff93047bdf --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/RTCTracingTest.mm @@ -0,0 +1,41 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#include <vector> + +#include "rtc_base/gunit.h" + +#import "api/peerconnection/RTCTracing.h" +#import "helpers/NSString+StdString.h" + +@interface RTCTracingTests : XCTestCase +@end + +@implementation RTCTracingTests + +- (NSString *)documentsFilePathForFileName:(NSString *)fileName { + NSParameterAssert(fileName.length); + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirPath = paths.firstObject; + NSString *filePath = + [documentsDirPath stringByAppendingPathComponent:fileName]; + return filePath; +} + +- (void)testTracingTestNoInitialization { + NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"]; + EXPECT_EQ(NO, RTCStartInternalCapture(filePath)); + RTCStopInternalCapture(); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/audio_short16.pcm b/third_party/libwebrtc/sdk/objc/unittests/audio_short16.pcm Binary files differnew file mode 100644 index 0000000000..15a0f1811c --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/audio_short16.pcm diff --git a/third_party/libwebrtc/sdk/objc/unittests/audio_short44.pcm b/third_party/libwebrtc/sdk/objc/unittests/audio_short44.pcm Binary files differnew file mode 100644 index 0000000000..011cdce959 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/audio_short44.pcm diff --git a/third_party/libwebrtc/sdk/objc/unittests/audio_short48.pcm b/third_party/libwebrtc/sdk/objc/unittests/audio_short48.pcm Binary files differnew file mode 100644 index 0000000000..06fd8261cd --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/audio_short48.pcm diff --git a/third_party/libwebrtc/sdk/objc/unittests/avformatmappertests.mm b/third_party/libwebrtc/sdk/objc/unittests/avformatmappertests.mm new file mode 100644 index 0000000000..35e95a8c22 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/avformatmappertests.mm @@ -0,0 +1,243 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <OCMock/OCMock.h> + +#include "rtc_base/gunit.h" + +// Width and height don't play any role so lets use predefined values throughout +// the tests. +static const int kFormatWidth = 789; +static const int kFormatHeight = 987; + +// Hardcoded framrate to be used throughout the tests. +static const int kFramerate = 30; + +// Same width and height is used so it's ok to expect same cricket::VideoFormat +static cricket::VideoFormat expectedFormat = + cricket::VideoFormat(kFormatWidth, + kFormatHeight, + cricket::VideoFormat::FpsToInterval(kFramerate), + cricket::FOURCC_NV12); + +// Mock class for AVCaptureDeviceFormat. +// Custom implementation needed because OCMock cannot handle the +// CMVideoDescriptionRef mocking. +@interface AVCaptureDeviceFormatMock : NSObject + +@property (nonatomic, assign) CMVideoFormatDescriptionRef format; +@property (nonatomic, strong) OCMockObject *rangeMock; + +- (instancetype)initWithMediaSubtype:(FourCharCode)subtype + minFps:(float)minFps + maxFps:(float)maxFps; ++ (instancetype)validFormat; ++ (instancetype)invalidFpsFormat; ++ (instancetype)invalidMediaSubtypeFormat; + +@end + +@implementation AVCaptureDeviceFormatMock + +@synthesize format = _format; +@synthesize rangeMock = _rangeMock; + +- (instancetype)initWithMediaSubtype:(FourCharCode)subtype + minFps:(float)minFps + maxFps:(float)maxFps { + if (self = [super init]) { + CMVideoFormatDescriptionCreate(nil, subtype, kFormatWidth, kFormatHeight, + nil, &_format); + // We can use OCMock for the range. + _rangeMock = [OCMockObject mockForClass:[AVFrameRateRange class]]; + [[[_rangeMock stub] andReturnValue:@(minFps)] minFrameRate]; + [[[_rangeMock stub] andReturnValue:@(maxFps)] maxFrameRate]; + } + + return self; +} + ++ (instancetype)validFormat { + AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] + initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + minFps:0.0 + maxFps:30.0]; + return instance; +} + ++ (instancetype)invalidFpsFormat { + AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] + initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + minFps:0.0 + maxFps:22.0]; + return instance; +} + ++ (instancetype)invalidMediaSubtypeFormat { + AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] + initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8Planar + minFps:0.0 + maxFps:60.0]; + return instance; +} + +- (void)dealloc { + if (_format != nil) { + CFRelease(_format); + _format = nil; + } +} + +// Redefinition of AVCaptureDevice methods we want to mock. +- (CMVideoFormatDescriptionRef)formatDescription { + return self.format; +} + +- (NSArray *)videoSupportedFrameRateRanges { + return @[ self.rangeMock ]; +} + +@end + +TEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFramerateFormats) { + // given + id mockDevice = OCMClassMock([AVCaptureDevice class]); + + // Valid media subtype, invalid framerate + AVCaptureDeviceFormatMock* mock = + [AVCaptureDeviceFormatMock invalidFpsFormat]; + OCMStub([mockDevice formats]).andReturn(@[ mock ]); + + // when + std::set<cricket::VideoFormat> result = + webrtc::GetSupportedVideoFormatsForDevice(mockDevice); + + // then + EXPECT_TRUE(result.empty()); +} + +TEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFormats) { + // given + id mockDevice = OCMClassMock([AVCaptureDevice class]); + + // Invalid media subtype, valid framerate + AVCaptureDeviceFormatMock* mock = + [AVCaptureDeviceFormatMock invalidMediaSubtypeFormat]; + OCMStub([mockDevice formats]).andReturn(@[ mock ]); + + // when + std::set<cricket::VideoFormat> result = + webrtc::GetSupportedVideoFormatsForDevice(mockDevice); + + // then + EXPECT_TRUE(result.empty()); +} + +TEST(AVFormatMapperTest, SuportedCricketFormats) { + // given + id mockDevice = OCMClassMock([AVCaptureDevice class]); + + // valid media subtype, valid framerate + AVCaptureDeviceFormatMock* mock = [AVCaptureDeviceFormatMock validFormat]; + OCMStub([mockDevice formats]).andReturn(@[ mock ]); + + // when + std::set<cricket::VideoFormat> result = + webrtc::GetSupportedVideoFormatsForDevice(mockDevice); + + // then + EXPECT_EQ(1u, result.size()); + // make sure the set has the expected format + EXPECT_EQ(expectedFormat, *result.begin()); +} + +TEST(AVFormatMapperTest, MediaSubtypePreference) { + // given + id mockDevice = OCMClassMock([AVCaptureDevice class]); + + // valid media subtype, valid framerate + AVCaptureDeviceFormatMock* mockOne = [[AVCaptureDeviceFormatMock alloc] + initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + minFps:0.0 + maxFps:30.0]; + // valid media subtype, valid framerate. + // This media subtype should be the preffered one. + AVCaptureDeviceFormatMock* mockTwo = [[AVCaptureDeviceFormatMock alloc] + initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + minFps:0.0 + maxFps:30.0]; + OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([mockDevice unlockForConfiguration]); + NSArray* array = @[ mockOne, mockTwo ]; + OCMStub([mockDevice formats]).andReturn(array); + + // to verify + OCMExpect([mockDevice setActiveFormat:(AVCaptureDeviceFormat*)mockTwo]); + OCMExpect( + [mockDevice setActiveVideoMinFrameDuration:CMTimeMake(1, kFramerate)]); + + // when + bool resultFormat = + webrtc::SetFormatForCaptureDevice(mockDevice, nil, expectedFormat); + + // then + EXPECT_TRUE(resultFormat); + [mockDevice verify]; +} + +TEST(AVFormatMapperTest, SetFormatWhenDeviceCannotLock) { + // given + id mockDevice = OCMClassMock([AVCaptureDevice class]); + [[[mockDevice stub] andReturnValue:@(NO)] + lockForConfiguration:[OCMArg setTo:nil]]; + [[[mockDevice stub] andReturn:@[]] formats]; + + // when + bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil, + cricket::VideoFormat()); + + // then + EXPECT_FALSE(resultFormat); +} + +TEST(AVFormatMapperTest, SetFormatWhenFormatIsIncompatible) { + // given + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice formats]).andReturn(@[]); + OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); + NSException* testException = + [NSException exceptionWithName:@"Test exception" + reason:@"Raised from unit tests" + userInfo:nil]; + OCMStub([mockDevice setActiveFormat:[OCMArg any]]).andThrow(testException); + OCMExpect([mockDevice unlockForConfiguration]); + + // when + bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil, + cricket::VideoFormat()); + + // then + EXPECT_FALSE(resultFormat); + + // TODO(denicija): Remove try-catch when Chromium rolls this change: + // https://github.com/erikdoe/ocmock/commit/de1419415581dc307045e54bfe9c98c86efea96b + // Without it, stubbed exceptions are being re-raised on [mock verify]. + // More information here: + //https://github.com/erikdoe/ocmock/issues/241 + @try { + [mockDevice verify]; + } @catch (NSException* exception) { + if ([exception.reason isEqual:testException.reason]) { + // Nothing dangerous here + EXPECT_TRUE([exception.reason isEqualToString:exception.reason]); + } + } +} diff --git a/third_party/libwebrtc/sdk/objc/unittests/foreman.mp4 b/third_party/libwebrtc/sdk/objc/unittests/foreman.mp4 Binary files differnew file mode 100644 index 0000000000..ccffbf4722 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/foreman.mp4 diff --git a/third_party/libwebrtc/sdk/objc/unittests/frame_buffer_helpers.h b/third_party/libwebrtc/sdk/objc/unittests/frame_buffer_helpers.h new file mode 100644 index 0000000000..76c0d15c7e --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/frame_buffer_helpers.h @@ -0,0 +1,22 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <AVFoundation/AVFoundation.h> + +#include "api/video/i420_buffer.h" + +void DrawGradientInRGBPixelBuffer(CVPixelBufferRef pixelBuffer); + +rtc::scoped_refptr<webrtc::I420Buffer> CreateI420Gradient(int width, + int height); + +void CopyI420BufferToCVPixelBuffer( + rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer, + CVPixelBufferRef pixelBuffer); diff --git a/third_party/libwebrtc/sdk/objc/unittests/frame_buffer_helpers.mm b/third_party/libwebrtc/sdk/objc/unittests/frame_buffer_helpers.mm new file mode 100644 index 0000000000..98b86c54c0 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/frame_buffer_helpers.mm @@ -0,0 +1,126 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "sdk/objc/unittests/frame_buffer_helpers.h" + +#include "third_party/libyuv/include/libyuv.h" + +void DrawGradientInRGBPixelBuffer(CVPixelBufferRef pixelBuffer) { + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + void* baseAddr = CVPixelBufferGetBaseAddress(pixelBuffer); + size_t width = CVPixelBufferGetWidth(pixelBuffer); + size_t height = CVPixelBufferGetHeight(pixelBuffer); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + int byteOrder = CVPixelBufferGetPixelFormatType(pixelBuffer) == kCVPixelFormatType_32ARGB ? + kCGBitmapByteOrder32Little : + 0; + CGContextRef cgContext = CGBitmapContextCreate(baseAddr, + width, + height, + 8, + CVPixelBufferGetBytesPerRow(pixelBuffer), + colorSpace, + byteOrder | kCGImageAlphaNoneSkipLast); + + // Create a gradient + CGFloat colors[] = { + 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, + }; + CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 4); + + CGContextDrawLinearGradient( + cgContext, gradient, CGPointMake(0, 0), CGPointMake(width, height), 0); + CGGradientRelease(gradient); + + CGImageRef cgImage = CGBitmapContextCreateImage(cgContext); + CGContextRelease(cgContext); + CGImageRelease(cgImage); + CGColorSpaceRelease(colorSpace); + + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); +} + +rtc::scoped_refptr<webrtc::I420Buffer> CreateI420Gradient(int width, int height) { + rtc::scoped_refptr<webrtc::I420Buffer> buffer(webrtc::I420Buffer::Create(width, height)); + // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + buffer->MutableDataY()[x + y * width] = 128 * (x * height + y * width) / (width * height); + } + } + int chroma_width = buffer->ChromaWidth(); + int chroma_height = buffer->ChromaHeight(); + for (int x = 0; x < chroma_width; x++) { + for (int y = 0; y < chroma_height; y++) { + buffer->MutableDataU()[x + y * chroma_width] = 255 * x / (chroma_width - 1); + buffer->MutableDataV()[x + y * chroma_width] = 255 * y / (chroma_height - 1); + } + } + return buffer; +} + +void CopyI420BufferToCVPixelBuffer(rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer, + CVPixelBufferRef pixelBuffer) { + CVPixelBufferLockBaseAddress(pixelBuffer, 0); + + const OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); + if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange || + pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { + // NV12 + uint8_t* dstY = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)); + const int dstYStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + uint8_t* dstUV = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)); + const int dstUVStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); + + libyuv::I420ToNV12(i420Buffer->DataY(), + i420Buffer->StrideY(), + i420Buffer->DataU(), + i420Buffer->StrideU(), + i420Buffer->DataV(), + i420Buffer->StrideV(), + dstY, + dstYStride, + dstUV, + dstUVStride, + i420Buffer->width(), + i420Buffer->height()); + } else { + uint8_t* dst = static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(pixelBuffer)); + const int bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer); + + if (pixelFormat == kCVPixelFormatType_32BGRA) { + // Corresponds to libyuv::FOURCC_ARGB + libyuv::I420ToARGB(i420Buffer->DataY(), + i420Buffer->StrideY(), + i420Buffer->DataU(), + i420Buffer->StrideU(), + i420Buffer->DataV(), + i420Buffer->StrideV(), + dst, + bytesPerRow, + i420Buffer->width(), + i420Buffer->height()); + } else if (pixelFormat == kCVPixelFormatType_32ARGB) { + // Corresponds to libyuv::FOURCC_BGRA + libyuv::I420ToBGRA(i420Buffer->DataY(), + i420Buffer->StrideY(), + i420Buffer->DataU(), + i420Buffer->StrideU(), + i420Buffer->DataV(), + i420Buffer->StrideV(), + dst, + bytesPerRow, + i420Buffer->width(), + i420Buffer->height()); + } + } + + CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); +} diff --git a/third_party/libwebrtc/sdk/objc/unittests/main.mm b/third_party/libwebrtc/sdk/objc/unittests/main.mm new file mode 100644 index 0000000000..9c513762c1 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/main.mm @@ -0,0 +1,24 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> +#include "rtc_base/thread.h" +#include "test/ios/coverage_util_ios.h" + +int main(int argc, char* argv[]) { + rtc::test::ConfigureCoverageReportPath(); + + rtc::AutoThread main_thread; + + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, nil); + } +} diff --git a/third_party/libwebrtc/sdk/objc/unittests/nalu_rewriter_xctest.mm b/third_party/libwebrtc/sdk/objc/unittests/nalu_rewriter_xctest.mm new file mode 100644 index 0000000000..82da549bb6 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/nalu_rewriter_xctest.mm @@ -0,0 +1,374 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "common_video/h264/h264_common.h" +#include "components/video_codec/nalu_rewriter.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/gunit.h" + +#import <XCTest/XCTest.h> + +#if TARGET_OS_IPHONE +#import <AVFoundation/AVFoundation.h> +#import <UIKit/UIKit.h> +#endif + +@interface NaluRewriterTests : XCTestCase + +@end + +static const uint8_t NALU_TEST_DATA_0[] = {0xAA, 0xBB, 0xCC}; +static const uint8_t NALU_TEST_DATA_1[] = {0xDE, 0xAD, 0xBE, 0xEF}; + +// clang-format off +static const uint8_t SPS_PPS_BUFFER[] = { + // SPS nalu. + 0x00, 0x00, 0x00, 0x01, 0x27, 0x42, 0x00, 0x1E, 0xAB, 0x40, 0xF0, 0x28, + 0xD3, 0x70, 0x20, 0x20, 0x20, 0x20, + // PPS nalu. + 0x00, 0x00, 0x00, 0x01, 0x28, 0xCE, 0x3C, 0x30}; +// clang-format on + +@implementation NaluRewriterTests + +- (void)testCreateVideoFormatDescription { + CMVideoFormatDescriptionRef description = + webrtc::CreateVideoFormatDescription(SPS_PPS_BUFFER, arraysize(SPS_PPS_BUFFER)); + XCTAssertTrue(description); + if (description) { + CFRelease(description); + description = nullptr; + } + + // clang-format off + const uint8_t sps_pps_not_at_start_buffer[] = { + // Add some non-SPS/PPS NALUs at the beginning + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x01, + 0xAB, 0x33, 0x21, + // SPS nalu. + 0x00, 0x00, 0x01, 0x27, 0x42, 0x00, 0x1E, 0xAB, 0x40, 0xF0, 0x28, 0xD3, + 0x70, 0x20, 0x20, 0x20, 0x20, + // PPS nalu. + 0x00, 0x00, 0x01, 0x28, 0xCE, 0x3C, 0x30}; + // clang-format on + description = webrtc::CreateVideoFormatDescription(sps_pps_not_at_start_buffer, + arraysize(sps_pps_not_at_start_buffer)); + + XCTAssertTrue(description); + + if (description) { + CFRelease(description); + description = nullptr; + } + + const uint8_t other_buffer[] = {0x00, 0x00, 0x00, 0x01, 0x28}; + XCTAssertFalse(webrtc::CreateVideoFormatDescription(other_buffer, arraysize(other_buffer))); +} + +- (void)testReadEmptyInput { + const uint8_t annex_b_test_data[] = {0x00}; + webrtc::AnnexBBufferReader reader(annex_b_test_data, 0); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadSingleNalu { + const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x00, 0x01, 0xAA}; + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(arraysize(annex_b_test_data), reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 4, nalu); + XCTAssertEqual(1u, nalu_length); + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadSingleNalu3ByteHeader { + const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x01, 0xAA}; + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(arraysize(annex_b_test_data), reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 3, nalu); + XCTAssertEqual(1u, nalu_length); + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadMissingNalu { + // clang-format off + const uint8_t annex_b_test_data[] = {0x01, + 0x00, 0x01, + 0x00, 0x00, 0x00, 0xFF}; + // clang-format on + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadMultipleNalus { + // clang-format off + const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x00, 0x01, 0xFF, + 0x01, + 0x00, 0x01, + 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x01, 0xAA, 0xBB}; + // clang-format on + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(arraysize(annex_b_test_data), reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 4, nalu); + XCTAssertEqual(8u, nalu_length); + XCTAssertEqual(5u, reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 15, nalu); + XCTAssertEqual(2u, nalu_length); + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testEmptyOutputBuffer { + const uint8_t expected_buffer[] = {0x00}; + const size_t buffer_size = 1; + std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); + memset(buffer.get(), 0, buffer_size); + webrtc::AvccBufferWriter writer(buffer.get(), 0); + XCTAssertEqual(0u, writer.BytesRemaining()); + XCTAssertFalse(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)testWriteSingleNalu { + const uint8_t expected_buffer[] = { + 0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC, + }; + const size_t buffer_size = arraysize(NALU_TEST_DATA_0) + 4; + std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); + webrtc::AvccBufferWriter writer(buffer.get(), buffer_size); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertTrue(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(0u, writer.BytesRemaining()); + XCTAssertFalse(writer.WriteNalu(NALU_TEST_DATA_1, arraysize(NALU_TEST_DATA_1))); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)testWriteMultipleNalus { + // clang-format off + const uint8_t expected_buffer[] = { + 0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC, + 0x00, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF + }; + // clang-format on + const size_t buffer_size = arraysize(NALU_TEST_DATA_0) + arraysize(NALU_TEST_DATA_1) + 8; + std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); + webrtc::AvccBufferWriter writer(buffer.get(), buffer_size); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertTrue(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(buffer_size - (arraysize(NALU_TEST_DATA_0) + 4), writer.BytesRemaining()); + XCTAssertTrue(writer.WriteNalu(NALU_TEST_DATA_1, arraysize(NALU_TEST_DATA_1))); + XCTAssertEqual(0u, writer.BytesRemaining()); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)testOverflow { + const uint8_t expected_buffer[] = {0x00, 0x00, 0x00}; + const size_t buffer_size = arraysize(NALU_TEST_DATA_0); + std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); + memset(buffer.get(), 0, buffer_size); + webrtc::AvccBufferWriter writer(buffer.get(), buffer_size); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertFalse(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)testH264AnnexBBufferToCMSampleBuffer { + // clang-format off + const uint8_t annex_b_test_data[] = { + 0x00, + 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x01, + 0xAA, 0xFF, // second chunk, 2 bytes + 0x00, 0x00, 0x01, + 0xBB}; // third chunk, 1 byte, will not fit into output array + + const uint8_t expected_cmsample_data[] = { + 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x02, + 0xAA, 0xFF}; // second chunk, 2 bytes + // clang-format on + + CMMemoryPoolRef memory_pool = CMMemoryPoolCreate(nil); + CMSampleBufferRef out_sample_buffer = nil; + CMVideoFormatDescriptionRef description = [self createDescription]; + + Boolean result = webrtc::H264AnnexBBufferToCMSampleBuffer(annex_b_test_data, + arraysize(annex_b_test_data), + description, + &out_sample_buffer, + memory_pool); + + XCTAssertTrue(result); + + XCTAssertEqual(description, CMSampleBufferGetFormatDescription(out_sample_buffer)); + + char* data_ptr = nullptr; + CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(out_sample_buffer); + size_t block_buffer_size = CMBlockBufferGetDataLength(block_buffer); + CMBlockBufferGetDataPointer(block_buffer, 0, nullptr, nullptr, &data_ptr); + XCTAssertEqual(block_buffer_size, arraysize(annex_b_test_data)); + + int data_comparison_result = + memcmp(expected_cmsample_data, data_ptr, arraysize(expected_cmsample_data)); + + XCTAssertEqual(0, data_comparison_result); + + if (description) { + CFRelease(description); + description = nullptr; + } + + CMMemoryPoolInvalidate(memory_pool); + CFRelease(memory_pool); +} + +- (void)testH264CMSampleBufferToAnnexBBuffer { + // clang-format off + const uint8_t cmsample_data[] = { + 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x02, + 0xAA, 0xFF}; // second chunk, 2 bytes + + const uint8_t expected_annex_b_data[] = { + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x01, + 0xAA, 0xFF}; // second chunk, 2 bytes + // clang-format on + + rtc::Buffer annexb_buffer(arraysize(cmsample_data)); + CMSampleBufferRef sample_buffer = + [self createCMSampleBufferRef:(void*)cmsample_data cmsampleSize:arraysize(cmsample_data)]; + + Boolean result = webrtc::H264CMSampleBufferToAnnexBBuffer(sample_buffer, + /* is_keyframe */ false, + &annexb_buffer); + + XCTAssertTrue(result); + + XCTAssertEqual(arraysize(expected_annex_b_data), annexb_buffer.size()); + + int data_comparison_result = + memcmp(expected_annex_b_data, annexb_buffer.data(), arraysize(expected_annex_b_data)); + + XCTAssertEqual(0, data_comparison_result); +} + +- (void)testH264CMSampleBufferToAnnexBBufferWithKeyframe { + // clang-format off + const uint8_t cmsample_data[] = { + 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x02, + 0xAA, 0xFF}; // second chunk, 2 bytes + + const uint8_t expected_annex_b_data[] = { + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x01, + 0xAA, 0xFF}; // second chunk, 2 bytes + // clang-format on + + rtc::Buffer annexb_buffer(arraysize(cmsample_data)); + CMSampleBufferRef sample_buffer = + [self createCMSampleBufferRef:(void*)cmsample_data cmsampleSize:arraysize(cmsample_data)]; + + Boolean result = webrtc::H264CMSampleBufferToAnnexBBuffer(sample_buffer, + /* is_keyframe */ true, + &annexb_buffer); + + XCTAssertTrue(result); + + XCTAssertEqual(arraysize(SPS_PPS_BUFFER) + arraysize(expected_annex_b_data), + annexb_buffer.size()); + + XCTAssertEqual(0, memcmp(SPS_PPS_BUFFER, annexb_buffer.data(), arraysize(SPS_PPS_BUFFER))); + + XCTAssertEqual(0, + memcmp(expected_annex_b_data, + annexb_buffer.data() + arraysize(SPS_PPS_BUFFER), + arraysize(expected_annex_b_data))); +} + +- (CMVideoFormatDescriptionRef)createDescription { + CMVideoFormatDescriptionRef description = + webrtc::CreateVideoFormatDescription(SPS_PPS_BUFFER, arraysize(SPS_PPS_BUFFER)); + XCTAssertTrue(description); + return description; +} + +- (CMSampleBufferRef)createCMSampleBufferRef:(void*)cmsampleData cmsampleSize:(size_t)cmsampleSize { + CMSampleBufferRef sample_buffer = nil; + OSStatus status; + + CMVideoFormatDescriptionRef description = [self createDescription]; + CMBlockBufferRef block_buffer = nullptr; + + status = CMBlockBufferCreateWithMemoryBlock(nullptr, + cmsampleData, + cmsampleSize, + nullptr, + nullptr, + 0, + cmsampleSize, + kCMBlockBufferAssureMemoryNowFlag, + &block_buffer); + XCTAssertEqual(kCMBlockBufferNoErr, status); + + status = CMSampleBufferCreate(nullptr, + block_buffer, + true, + nullptr, + nullptr, + description, + 1, + 0, + nullptr, + 0, + nullptr, + &sample_buffer); + XCTAssertEqual(noErr, status); + + return sample_buffer; +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/objc_video_decoder_factory_tests.mm b/third_party/libwebrtc/sdk/objc/unittests/objc_video_decoder_factory_tests.mm new file mode 100644 index 0000000000..f44d831d29 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/objc_video_decoder_factory_tests.mm @@ -0,0 +1,107 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <OCMock/OCMock.h> +#import <XCTest/XCTest.h> + +#include "sdk/objc/native/src/objc_video_decoder_factory.h" + +#import "base/RTCMacros.h" +#import "base/RTCVideoDecoder.h" +#import "base/RTCVideoDecoderFactory.h" +#include "media/base/codec.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/gunit.h" + +id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> CreateDecoderFactoryReturning(int return_code) { + id decoderMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoDecoder))); + OCMStub([decoderMock startDecodeWithNumberOfCores:1]).andReturn(return_code); + OCMStub([decoderMock decode:[OCMArg any] + missingFrames:NO + codecSpecificInfo:[OCMArg any] + renderTimeMs:0]) + .andReturn(return_code); + OCMStub([decoderMock releaseDecoder]).andReturn(return_code); + + id decoderFactoryMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoDecoderFactory))); + RTC_OBJC_TYPE(RTCVideoCodecInfo)* supported = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"H264" parameters:nil]; + OCMStub([decoderFactoryMock supportedCodecs]).andReturn(@[ supported ]); + OCMStub([decoderFactoryMock createDecoder:[OCMArg any]]).andReturn(decoderMock); + return decoderFactoryMock; +} + +id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> CreateOKDecoderFactory() { + return CreateDecoderFactoryReturning(WEBRTC_VIDEO_CODEC_OK); +} + +id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> CreateErrorDecoderFactory() { + return CreateDecoderFactoryReturning(WEBRTC_VIDEO_CODEC_ERROR); +} + +std::unique_ptr<webrtc::VideoDecoder> GetObjCDecoder( + id<RTC_OBJC_TYPE(RTCVideoDecoderFactory)> factory) { + webrtc::ObjCVideoDecoderFactory decoder_factory(factory); + return decoder_factory.CreateVideoDecoder(webrtc::SdpVideoFormat(cricket::kH264CodecName)); +} + +#pragma mark - + +@interface ObjCVideoDecoderFactoryTests : XCTestCase +@end + +@implementation ObjCVideoDecoderFactoryTests + +- (void)testConfigureReturnsTrueOnSuccess { + std::unique_ptr<webrtc::VideoDecoder> decoder = GetObjCDecoder(CreateOKDecoderFactory()); + + webrtc::VideoDecoder::Settings settings; + EXPECT_TRUE(decoder->Configure(settings)); +} + +- (void)testConfigureReturnsFalseOnFail { + std::unique_ptr<webrtc::VideoDecoder> decoder = GetObjCDecoder(CreateErrorDecoderFactory()); + + webrtc::VideoDecoder::Settings settings; + EXPECT_FALSE(decoder->Configure(settings)); +} + +- (void)testDecodeReturnsOKOnSuccess { + std::unique_ptr<webrtc::VideoDecoder> decoder = GetObjCDecoder(CreateOKDecoderFactory()); + + webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(webrtc::EncodedImageBuffer::Create()); + + EXPECT_EQ(decoder->Decode(encoded_image, false, 0), WEBRTC_VIDEO_CODEC_OK); +} + +- (void)testDecodeReturnsErrorOnFail { + std::unique_ptr<webrtc::VideoDecoder> decoder = GetObjCDecoder(CreateErrorDecoderFactory()); + + webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(webrtc::EncodedImageBuffer::Create()); + + EXPECT_EQ(decoder->Decode(encoded_image, false, 0), WEBRTC_VIDEO_CODEC_ERROR); +} + +- (void)testReleaseDecodeReturnsOKOnSuccess { + std::unique_ptr<webrtc::VideoDecoder> decoder = GetObjCDecoder(CreateOKDecoderFactory()); + + EXPECT_EQ(decoder->Release(), WEBRTC_VIDEO_CODEC_OK); +} + +- (void)testReleaseDecodeReturnsErrorOnFail { + std::unique_ptr<webrtc::VideoDecoder> decoder = GetObjCDecoder(CreateErrorDecoderFactory()); + + EXPECT_EQ(decoder->Release(), WEBRTC_VIDEO_CODEC_ERROR); +} +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/objc_video_encoder_factory_tests.mm b/third_party/libwebrtc/sdk/objc/unittests/objc_video_encoder_factory_tests.mm new file mode 100644 index 0000000000..9a4fee2e95 --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/objc_video_encoder_factory_tests.mm @@ -0,0 +1,148 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <Foundation/Foundation.h> +#import <OCMock/OCMock.h> +#import <XCTest/XCTest.h> + +#include "sdk/objc/native/src/objc_video_encoder_factory.h" + +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" +#import "base/RTCVideoEncoder.h" +#import "base/RTCVideoEncoderFactory.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/gunit.h" +#include "sdk/objc/native/src/objc_frame_buffer.h" + +id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> CreateEncoderFactoryReturning(int return_code) { + id encoderMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoEncoder))); + OCMStub([encoderMock startEncodeWithSettings:[OCMArg any] numberOfCores:1]) + .andReturn(return_code); + OCMStub([encoderMock encode:[OCMArg any] codecSpecificInfo:[OCMArg any] frameTypes:[OCMArg any]]) + .andReturn(return_code); + OCMStub([encoderMock releaseEncoder]).andReturn(return_code); + OCMStub([encoderMock setBitrate:0 framerate:0]).andReturn(return_code); + + id encoderFactoryMock = OCMProtocolMock(@protocol(RTC_OBJC_TYPE(RTCVideoEncoderFactory))); + RTC_OBJC_TYPE(RTCVideoCodecInfo)* supported = + [[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"H264" parameters:nil]; + OCMStub([encoderFactoryMock supportedCodecs]).andReturn(@[ supported ]); + OCMStub([encoderFactoryMock implementations]).andReturn(@[ supported ]); + OCMStub([encoderFactoryMock createEncoder:[OCMArg any]]).andReturn(encoderMock); + return encoderFactoryMock; +} + +id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> CreateOKEncoderFactory() { + return CreateEncoderFactoryReturning(WEBRTC_VIDEO_CODEC_OK); +} + +id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> CreateErrorEncoderFactory() { + return CreateEncoderFactoryReturning(WEBRTC_VIDEO_CODEC_ERROR); +} + +std::unique_ptr<webrtc::VideoEncoder> GetObjCEncoder( + id<RTC_OBJC_TYPE(RTCVideoEncoderFactory)> factory) { + webrtc::ObjCVideoEncoderFactory encoder_factory(factory); + webrtc::SdpVideoFormat format("H264"); + return encoder_factory.CreateVideoEncoder(format); +} + +#pragma mark - + +@interface ObjCVideoEncoderFactoryTests : XCTestCase +@end + +@implementation ObjCVideoEncoderFactoryTests + +- (void)testInitEncodeReturnsOKOnSuccess { + std::unique_ptr<webrtc::VideoEncoder> encoder = GetObjCEncoder(CreateOKEncoderFactory()); + + auto* settings = new webrtc::VideoCodec(); + const webrtc::VideoEncoder::Capabilities kCapabilities(false); + EXPECT_EQ(encoder->InitEncode(settings, webrtc::VideoEncoder::Settings(kCapabilities, 1, 0)), + WEBRTC_VIDEO_CODEC_OK); +} + +- (void)testInitEncodeReturnsErrorOnFail { + std::unique_ptr<webrtc::VideoEncoder> encoder = GetObjCEncoder(CreateErrorEncoderFactory()); + + auto* settings = new webrtc::VideoCodec(); + const webrtc::VideoEncoder::Capabilities kCapabilities(false); + EXPECT_EQ(encoder->InitEncode(settings, webrtc::VideoEncoder::Settings(kCapabilities, 1, 0)), + WEBRTC_VIDEO_CODEC_ERROR); +} + +- (void)testEncodeReturnsOKOnSuccess { + std::unique_ptr<webrtc::VideoEncoder> encoder = GetObjCEncoder(CreateOKEncoderFactory()); + + CVPixelBufferRef pixel_buffer; + CVPixelBufferCreate(kCFAllocatorDefault, 640, 480, kCVPixelFormatType_32ARGB, nil, &pixel_buffer); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer = + rtc::make_ref_counted<webrtc::ObjCFrameBuffer>( + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixel_buffer]); + webrtc::VideoFrame frame = webrtc::VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rotation(webrtc::kVideoRotation_0) + .set_timestamp_us(0) + .build(); + std::vector<webrtc::VideoFrameType> frame_types; + + EXPECT_EQ(encoder->Encode(frame, &frame_types), WEBRTC_VIDEO_CODEC_OK); +} + +- (void)testEncodeReturnsErrorOnFail { + std::unique_ptr<webrtc::VideoEncoder> encoder = GetObjCEncoder(CreateErrorEncoderFactory()); + + CVPixelBufferRef pixel_buffer; + CVPixelBufferCreate(kCFAllocatorDefault, 640, 480, kCVPixelFormatType_32ARGB, nil, &pixel_buffer); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer = + rtc::make_ref_counted<webrtc::ObjCFrameBuffer>( + [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixel_buffer]); + webrtc::VideoFrame frame = webrtc::VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rotation(webrtc::kVideoRotation_0) + .set_timestamp_us(0) + .build(); + std::vector<webrtc::VideoFrameType> frame_types; + + EXPECT_EQ(encoder->Encode(frame, &frame_types), WEBRTC_VIDEO_CODEC_ERROR); +} + +- (void)testReleaseEncodeReturnsOKOnSuccess { + std::unique_ptr<webrtc::VideoEncoder> encoder = GetObjCEncoder(CreateOKEncoderFactory()); + + EXPECT_EQ(encoder->Release(), WEBRTC_VIDEO_CODEC_OK); +} + +- (void)testReleaseEncodeReturnsErrorOnFail { + std::unique_ptr<webrtc::VideoEncoder> encoder = GetObjCEncoder(CreateErrorEncoderFactory()); + + EXPECT_EQ(encoder->Release(), WEBRTC_VIDEO_CODEC_ERROR); +} + +- (void)testGetSupportedFormats { + webrtc::ObjCVideoEncoderFactory encoder_factory(CreateOKEncoderFactory()); + std::vector<webrtc::SdpVideoFormat> supportedFormats = encoder_factory.GetSupportedFormats(); + EXPECT_EQ(supportedFormats.size(), 1u); + EXPECT_EQ(supportedFormats[0].name, "H264"); +} + +- (void)testGetImplementations { + webrtc::ObjCVideoEncoderFactory encoder_factory(CreateOKEncoderFactory()); + std::vector<webrtc::SdpVideoFormat> supportedFormats = encoder_factory.GetImplementations(); + EXPECT_EQ(supportedFormats.size(), 1u); + EXPECT_EQ(supportedFormats[0].name, "H264"); +} + +@end diff --git a/third_party/libwebrtc/sdk/objc/unittests/scoped_cftyperef_tests.mm b/third_party/libwebrtc/sdk/objc/unittests/scoped_cftyperef_tests.mm new file mode 100644 index 0000000000..a354410ede --- /dev/null +++ b/third_party/libwebrtc/sdk/objc/unittests/scoped_cftyperef_tests.mm @@ -0,0 +1,111 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import <XCTest/XCTest.h> + +#include "sdk/objc/helpers/scoped_cftyperef.h" + +namespace { +struct TestType { + TestType() : has_value(true) {} + TestType(bool b) : has_value(b) {} + explicit operator bool() { return has_value; } + bool has_value; + int retain_count = 0; +}; + +typedef TestType* TestTypeRef; + +struct TestTypeTraits { + static TestTypeRef InvalidValue() { return TestTypeRef(false); } + static void Release(TestTypeRef t) { t->retain_count--; } + static TestTypeRef Retain(TestTypeRef t) { + t->retain_count++; + return t; + } +}; +} // namespace + +using ScopedTestType = rtc::internal::ScopedTypeRef<TestTypeRef, TestTypeTraits>; + +// In these tests we sometime introduce variables just to +// observe side-effects. Ignore the compilers complaints. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-variable" + +@interface ScopedTypeRefTests : XCTestCase +@end + +@implementation ScopedTypeRefTests + +- (void)testShouldNotRetainByDefault { + TestType a; + ScopedTestType ref(&a); + XCTAssertEqual(0, a.retain_count); +} + +- (void)testShouldRetainWithPolicy { + TestType a; + ScopedTestType ref(&a, rtc::RetainPolicy::RETAIN); + XCTAssertEqual(1, a.retain_count); +} + +- (void)testShouldReleaseWhenLeavingScope { + TestType a; + XCTAssertEqual(0, a.retain_count); + { + ScopedTestType ref(&a, rtc::RetainPolicy::RETAIN); + XCTAssertEqual(1, a.retain_count); + } + XCTAssertEqual(0, a.retain_count); +} + +- (void)testShouldBeCopyable { + TestType a; + XCTAssertEqual(0, a.retain_count); + { + ScopedTestType ref1(&a, rtc::RetainPolicy::RETAIN); + XCTAssertEqual(1, a.retain_count); + ScopedTestType ref2 = ref1; + XCTAssertEqual(2, a.retain_count); + } + XCTAssertEqual(0, a.retain_count); +} + +- (void)testCanReleaseOwnership { + TestType a; + XCTAssertEqual(0, a.retain_count); + { + ScopedTestType ref(&a, rtc::RetainPolicy::RETAIN); + XCTAssertEqual(1, a.retain_count); + TestTypeRef b = ref.release(); + } + XCTAssertEqual(1, a.retain_count); +} + +- (void)testShouldBeTestableForTruthiness { + ScopedTestType ref; + XCTAssertFalse(ref); + TestType a; + ref = &a; + XCTAssertTrue(ref); + ref.release(); + XCTAssertFalse(ref); +} + +- (void)testShouldProvideAccessToWrappedType { + TestType a; + ScopedTestType ref(&a); + XCTAssertEqual(&(a.retain_count), &(ref->retain_count)); +} + +@end + +#pragma clang diagnostic pop |