diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/examples/objc/AppRTCMobile/ios | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/examples/objc/AppRTCMobile/ios')
44 files changed, 2253 insertions, 0 deletions
diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDAppDelegate.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDAppDelegate.h new file mode 100644 index 0000000000..7eafff8ebc --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDAppDelegate.h @@ -0,0 +1,17 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be 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> + +// The main application class of the AppRTCMobile iOS app demonstrating +// interoperability between the Objective C implementation of PeerConnection +// and the appr.tc demo webapp. +@interface ARDAppDelegate : NSObject <UIApplicationDelegate> +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDAppDelegate.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDAppDelegate.m new file mode 100644 index 0000000000..51e9910b87 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDAppDelegate.m @@ -0,0 +1,56 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import "ARDAppDelegate.h" + +#import "sdk/objc/api/peerconnection/RTCFieldTrials.h" +#import "sdk/objc/api/peerconnection/RTCSSLAdapter.h" +#import "sdk/objc/api/peerconnection/RTCTracing.h" +#import "sdk/objc/base/RTCLogging.h" + +#import "ARDMainViewController.h" + +@implementation ARDAppDelegate { + UIWindow *_window; +} + +#pragma mark - UIApplicationDelegate methods + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + NSDictionary *fieldTrials = @{}; + RTCInitFieldTrialDictionary(fieldTrials); + RTCInitializeSSL(); + RTCSetupInternalTracer(); + _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [_window makeKeyAndVisible]; + ARDMainViewController *viewController = [[ARDMainViewController alloc] init]; + + UINavigationController *root = + [[UINavigationController alloc] initWithRootViewController:viewController]; + root.navigationBar.translucent = NO; + _window.rootViewController = root; + +#if defined(NDEBUG) + // In debug builds the default level is LS_INFO and in non-debug builds it is + // disabled. Continue to log to console in non-debug builds, but only + // warnings and errors. + RTCSetMinDebugLogLevel(RTCLoggingSeverityWarning); +#endif + + return YES; +} + +- (void)applicationWillTerminate:(UIApplication *)application { + RTCShutdownInternalTracer(); + RTCCleanupSSL(); +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.h new file mode 100644 index 0000000000..82f8fcdd1b --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.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 "sdk/objc/base/RTCMacros.h" + +@class RTC_OBJC_TYPE(RTCFileVideoCapturer); + +/** + * Controls a file capturer. + */ +NS_CLASS_AVAILABLE_IOS(10) +@interface ARDFileCaptureController : NSObject + +/** + * Creates instance of the controller. + * + * @param capturer The capturer to be controlled. + */ +- (instancetype)initWithCapturer:(RTC_OBJC_TYPE(RTCFileVideoCapturer) *)capturer; + +/** + * Starts the file capturer. + * + * Possible errors produced by the capturer will be logged. + */ +- (void)startCapture; + +/** + * Immediately stops capturer. + */ +- (void)stopCapture; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.m new file mode 100644 index 0000000000..2ddde6dd59 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.m @@ -0,0 +1,45 @@ +/* + * 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 "ARDFileCaptureController.h" + +#import "sdk/objc/components/capturer/RTCFileVideoCapturer.h" + +@interface ARDFileCaptureController () + +@property(nonatomic, strong) RTC_OBJC_TYPE(RTCFileVideoCapturer) * fileCapturer; + +@end + +@implementation ARDFileCaptureController +@synthesize fileCapturer = _fileCapturer; + +- (instancetype)initWithCapturer:(RTC_OBJC_TYPE(RTCFileVideoCapturer) *)capturer { + if (self = [super init]) { + _fileCapturer = capturer; + } + return self; +} + +- (void)startCapture { + [self startFileCapture]; +} + +- (void)startFileCapture { + [self.fileCapturer startCapturingFromFileNamed:@"foreman.mp4" + onError:^(NSError *_Nonnull error) { + NSLog(@"Error %@", error.userInfo); + }]; +} + +- (void)stopCapture { + [self.fileCapturer stopCapture]; +} +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainView.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainView.h new file mode 100644 index 0000000000..c6691c2d84 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainView.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 <UIKit/UIKit.h> + +@class ARDMainView; + +@protocol ARDMainViewDelegate <NSObject> + +- (void)mainView:(ARDMainView *)mainView didInputRoom:(NSString *)room isLoopback:(BOOL)isLoopback; +- (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView; + +@end + +// The main view of AppRTCMobile. It contains an input field for entering a room +// name on apprtc to connect to. +@interface ARDMainView : UIView + +@property(nonatomic, weak) id<ARDMainViewDelegate> delegate; +// Updates the audio loop button as needed. +@property(nonatomic, assign) BOOL isAudioLoopPlaying; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m new file mode 100644 index 0000000000..d9521060eb --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m @@ -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 "ARDMainView.h" + +#import "UIImage+ARDUtilities.h" + +static CGFloat const kRoomTextFieldHeight = 40; +static CGFloat const kRoomTextFieldMargin = 8; +static CGFloat const kCallControlMargin = 8; + +// Helper view that contains a text field and a clear button. +@interface ARDRoomTextField : UIView <UITextFieldDelegate> +@property(nonatomic, readonly) NSString *roomText; +@end + +@implementation ARDRoomTextField { + UITextField *_roomText; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + _roomText = [[UITextField alloc] initWithFrame:CGRectZero]; + _roomText.borderStyle = UITextBorderStyleNone; + _roomText.font = [UIFont systemFontOfSize:12]; + _roomText.placeholder = @"Room name"; + _roomText.autocorrectionType = UITextAutocorrectionTypeNo; + _roomText.autocapitalizationType = UITextAutocapitalizationTypeNone; + _roomText.clearButtonMode = UITextFieldViewModeAlways; + _roomText.delegate = self; + [self addSubview:_roomText]; + + // Give rounded corners and a light gray border. + self.layer.borderWidth = 1; + self.layer.borderColor = [[UIColor lightGrayColor] CGColor]; + self.layer.cornerRadius = 2; + } + return self; +} + +- (void)layoutSubviews { + _roomText.frame = + CGRectMake(kRoomTextFieldMargin, 0, CGRectGetWidth(self.bounds) - kRoomTextFieldMargin, + kRoomTextFieldHeight); +} + +- (CGSize)sizeThatFits:(CGSize)size { + size.height = kRoomTextFieldHeight; + return size; +} + +- (NSString *)roomText { + return _roomText.text; +} + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + // There is no other control that can take focus, so manually resign focus + // when return (Join) is pressed to trigger `textFieldDidEndEditing`. + [textField resignFirstResponder]; + return YES; +} + +@end + +@implementation ARDMainView { + ARDRoomTextField *_roomText; + UIButton *_startRegularCallButton; + UIButton *_startLoopbackCallButton; + UIButton *_audioLoopButton; +} + +@synthesize delegate = _delegate; +@synthesize isAudioLoopPlaying = _isAudioLoopPlaying; + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + _roomText = [[ARDRoomTextField alloc] initWithFrame:CGRectZero]; + [self addSubview:_roomText]; + + UIFont *controlFont = [UIFont boldSystemFontOfSize:18.0]; + UIColor *controlFontColor = [UIColor whiteColor]; + + _startRegularCallButton = [UIButton buttonWithType:UIButtonTypeSystem]; + _startRegularCallButton.titleLabel.font = controlFont; + [_startRegularCallButton setTitleColor:controlFontColor forState:UIControlStateNormal]; + _startRegularCallButton.backgroundColor + = [UIColor colorWithRed:66.0/255.0 green:200.0/255.0 blue:90.0/255.0 alpha:1.0]; + [_startRegularCallButton setTitle:@"Call room" forState:UIControlStateNormal]; + [_startRegularCallButton addTarget:self + action:@selector(onStartRegularCall:) + forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_startRegularCallButton]; + + _startLoopbackCallButton = [UIButton buttonWithType:UIButtonTypeSystem]; + _startLoopbackCallButton.titleLabel.font = controlFont; + [_startLoopbackCallButton setTitleColor:controlFontColor forState:UIControlStateNormal]; + _startLoopbackCallButton.backgroundColor = + [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0]; + [_startLoopbackCallButton setTitle:@"Loopback call" forState:UIControlStateNormal]; + [_startLoopbackCallButton addTarget:self + action:@selector(onStartLoopbackCall:) + forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_startLoopbackCallButton]; + + + // Used to test what happens to sounds when calls are in progress. + _audioLoopButton = [UIButton buttonWithType:UIButtonTypeSystem]; + _audioLoopButton.titleLabel.font = controlFont; + [_audioLoopButton setTitleColor:controlFontColor forState:UIControlStateNormal]; + _audioLoopButton.backgroundColor = + [UIColor colorWithRed:1.0 green:149.0/255.0 blue:0.0 alpha:1.0]; + [self updateAudioLoopButton]; + [_audioLoopButton addTarget:self + action:@selector(onToggleAudioLoop:) + forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_audioLoopButton]; + + self.backgroundColor = [UIColor whiteColor]; + } + return self; +} + +- (void)setIsAudioLoopPlaying:(BOOL)isAudioLoopPlaying { + if (_isAudioLoopPlaying == isAudioLoopPlaying) { + return; + } + _isAudioLoopPlaying = isAudioLoopPlaying; + [self updateAudioLoopButton]; +} + +- (void)layoutSubviews { + CGRect bounds = self.bounds; + CGFloat roomTextWidth = bounds.size.width - 2 * kRoomTextFieldMargin; + CGFloat roomTextHeight = [_roomText sizeThatFits:bounds.size].height; + _roomText.frame = + CGRectMake(kRoomTextFieldMargin, kRoomTextFieldMargin, roomTextWidth, + roomTextHeight); + + CGFloat buttonHeight = + (CGRectGetMaxY(self.bounds) - CGRectGetMaxY(_roomText.frame) - kCallControlMargin * 4) / 3; + + CGFloat regularCallFrameTop = CGRectGetMaxY(_roomText.frame) + kCallControlMargin; + CGRect regularCallFrame = CGRectMake(kCallControlMargin, + regularCallFrameTop, + bounds.size.width - 2*kCallControlMargin, + buttonHeight); + + CGFloat loopbackCallFrameTop = CGRectGetMaxY(regularCallFrame) + kCallControlMargin; + CGRect loopbackCallFrame = CGRectMake(kCallControlMargin, + loopbackCallFrameTop, + bounds.size.width - 2*kCallControlMargin, + buttonHeight); + + CGFloat audioLoopTop = CGRectGetMaxY(loopbackCallFrame) + kCallControlMargin; + CGRect audioLoopFrame = CGRectMake(kCallControlMargin, + audioLoopTop, + bounds.size.width - 2*kCallControlMargin, + buttonHeight); + + _startRegularCallButton.frame = regularCallFrame; + _startLoopbackCallButton.frame = loopbackCallFrame; + _audioLoopButton.frame = audioLoopFrame; +} + +#pragma mark - Private + +- (void)updateAudioLoopButton { + if (_isAudioLoopPlaying) { + [_audioLoopButton setTitle:@"Stop sound" forState:UIControlStateNormal]; + } else { + [_audioLoopButton setTitle:@"Play sound" forState:UIControlStateNormal]; + } +} + +- (void)onToggleAudioLoop:(id)sender { + [_delegate mainViewDidToggleAudioLoop:self]; +} + +- (void)onStartRegularCall:(id)sender { + [_delegate mainView:self didInputRoom:_roomText.roomText isLoopback:NO]; +} + +- (void)onStartLoopbackCall:(id)sender { + [_delegate mainView:self didInputRoom:_roomText.roomText isLoopback:YES]; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.h new file mode 100644 index 0000000000..e5c92dd304 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.h @@ -0,0 +1,14 @@ +/* + * 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 <UIKit/UIKit.h> + +@interface ARDMainViewController : UIViewController +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m new file mode 100644 index 0000000000..e8b8112e41 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m @@ -0,0 +1,263 @@ +/* + * 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 "ARDMainViewController.h" + +#import <AVFoundation/AVFoundation.h> + +#import "sdk/objc/base/RTCLogging.h" +#import "sdk/objc/components/audio/RTCAudioSession.h" +#import "sdk/objc/components/audio/RTCAudioSessionConfiguration.h" +#import "sdk/objc/helpers/RTCDispatcher.h" + +#import "ARDAppClient.h" +#import "ARDMainView.h" +#import "ARDSettingsModel.h" +#import "ARDSettingsViewController.h" +#import "ARDVideoCallViewController.h" + +static NSString *const barButtonImageString = @"ic_settings_black_24dp.png"; + +// Launch argument to be passed to indicate that the app should start loopback immediatly +static NSString *const loopbackLaunchProcessArgument = @"loopback"; + +@interface ARDMainViewController () <ARDMainViewDelegate, + ARDVideoCallViewControllerDelegate, + RTC_OBJC_TYPE (RTCAudioSessionDelegate)> +@property(nonatomic, strong) ARDMainView *mainView; +@property(nonatomic, strong) AVAudioPlayer *audioPlayer; +@end + +@implementation ARDMainViewController { + BOOL _useManualAudio; +} + +@synthesize mainView = _mainView; +@synthesize audioPlayer = _audioPlayer; + +- (void)viewDidLoad { + [super viewDidLoad]; + if ([[[NSProcessInfo processInfo] arguments] containsObject:loopbackLaunchProcessArgument]) { + [self mainView:nil didInputRoom:@"" isLoopback:YES]; + } +} + +- (void)loadView { + self.title = @"AppRTC Mobile"; + _mainView = [[ARDMainView alloc] initWithFrame:CGRectZero]; + _mainView.delegate = self; + self.view = _mainView; + [self addSettingsBarButton]; + + RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *webRTCConfig = + [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) webRTCConfiguration]; + webRTCConfig.categoryOptions = webRTCConfig.categoryOptions | + AVAudioSessionCategoryOptionDefaultToSpeaker; + [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) setWebRTCConfiguration:webRTCConfig]; + + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session addDelegate:self]; + + [self configureAudioSession]; + [self setupAudioPlayer]; +} + +- (void)addSettingsBarButton { + UIBarButtonItem *settingsButton = + [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:barButtonImageString] + style:UIBarButtonItemStylePlain + target:self + action:@selector(showSettings:)]; + self.navigationItem.rightBarButtonItem = settingsButton; +} + ++ (NSString *)loopbackRoomString { + NSString *loopbackRoomString = + [[NSUUID UUID].UUIDString stringByReplacingOccurrencesOfString:@"-" withString:@""]; + return loopbackRoomString; +} + +#pragma mark - ARDMainViewDelegate + +- (void)mainView:(ARDMainView *)mainView didInputRoom:(NSString *)room isLoopback:(BOOL)isLoopback { + if (!room.length) { + if (isLoopback) { + // If this is a loopback call, allow a generated room name. + room = [[self class] loopbackRoomString]; + } else { + [self showAlertWithMessage:@"Missing room name."]; + return; + } + } + // Trim whitespaces. + NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet]; + NSString *trimmedRoom = [room stringByTrimmingCharactersInSet:whitespaceSet]; + + // Check that room name is valid. + NSError *error = nil; + NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive; + NSRegularExpression *regex = + [NSRegularExpression regularExpressionWithPattern:@"\\w+" + options:options + error:&error]; + if (error) { + [self showAlertWithMessage:error.localizedDescription]; + return; + } + NSRange matchRange = + [regex rangeOfFirstMatchInString:trimmedRoom + options:0 + range:NSMakeRange(0, trimmedRoom.length)]; + if (matchRange.location == NSNotFound || + matchRange.length != trimmedRoom.length) { + [self showAlertWithMessage:@"Invalid room name."]; + return; + } + + ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; + + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + session.useManualAudio = [settingsModel currentUseManualAudioConfigSettingFromStore]; + session.isAudioEnabled = NO; + + // Kick off the video call. + ARDVideoCallViewController *videoCallViewController = + [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom + isLoopback:isLoopback + delegate:self]; + videoCallViewController.modalTransitionStyle = + UIModalTransitionStyleCrossDissolve; + videoCallViewController.modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:videoCallViewController + animated:YES + completion:nil]; +} + +- (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView { + if (mainView.isAudioLoopPlaying) { + [_audioPlayer stop]; + } else { + [_audioPlayer play]; + } + mainView.isAudioLoopPlaying = _audioPlayer.playing; +} + +#pragma mark - ARDVideoCallViewControllerDelegate + +- (void)viewControllerDidFinish:(ARDVideoCallViewController *)viewController { + if (![viewController isBeingDismissed]) { + RTCLog(@"Dismissing VC"); + [self dismissViewControllerAnimated:YES completion:^{ + [self restartAudioPlayerIfNeeded]; + }]; + } + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + session.isAudioEnabled = NO; +} + +#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate) + +- (void)audioSessionDidStartPlayOrRecord:(RTC_OBJC_TYPE(RTCAudioSession) *)session { + // Stop playback on main queue and then configure WebRTC. + [RTC_OBJC_TYPE(RTCDispatcher) + dispatchAsyncOnType:RTCDispatcherTypeMain + block:^{ + if (self.mainView.isAudioLoopPlaying) { + RTCLog(@"Stopping audio loop due to WebRTC start."); + [self.audioPlayer stop]; + } + RTCLog(@"Setting isAudioEnabled to YES."); + session.isAudioEnabled = YES; + }]; +} + +- (void)audioSessionDidStopPlayOrRecord:(RTC_OBJC_TYPE(RTCAudioSession) *)session { + // WebRTC is done with the audio session. Restart playback. + [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeMain + block:^{ + RTCLog(@"audioSessionDidStopPlayOrRecord"); + [self restartAudioPlayerIfNeeded]; + }]; +} + +#pragma mark - Private +- (void)showSettings:(id)sender { + ARDSettingsViewController *settingsController = + [[ARDSettingsViewController alloc] initWithStyle:UITableViewStyleGrouped + settingsModel:[[ARDSettingsModel alloc] init]]; + + UINavigationController *navigationController = + [[UINavigationController alloc] initWithRootViewController:settingsController]; + [self presentViewControllerAsModal:navigationController]; +} + +- (void)presentViewControllerAsModal:(UIViewController *)viewController { + [self presentViewController:viewController animated:YES completion:nil]; +} + +- (void)configureAudioSession { + RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *configuration = + [[RTC_OBJC_TYPE(RTCAudioSessionConfiguration) alloc] init]; + configuration.category = AVAudioSessionCategoryAmbient; + configuration.categoryOptions = AVAudioSessionCategoryOptionDuckOthers; + configuration.mode = AVAudioSessionModeDefault; + + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session lockForConfiguration]; + BOOL hasSucceeded = NO; + NSError *error = nil; + if (session.isActive) { + hasSucceeded = [session setConfiguration:configuration error:&error]; + } else { + hasSucceeded = [session setConfiguration:configuration + active:YES + error:&error]; + } + if (!hasSucceeded) { + RTCLogError(@"Error setting configuration: %@", error.localizedDescription); + } + [session unlockForConfiguration]; +} + +- (void)setupAudioPlayer { + NSString *audioFilePath = + [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"]; + NSURL *audioFileURL = [NSURL URLWithString:audioFilePath]; + _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL + error:nil]; + _audioPlayer.numberOfLoops = -1; + _audioPlayer.volume = 1.0; + [_audioPlayer prepareToPlay]; +} + +- (void)restartAudioPlayerIfNeeded { + [self configureAudioSession]; + if (_mainView.isAudioLoopPlaying && !self.presentedViewController) { + RTCLog(@"Starting audio loop due to WebRTC end."); + [_audioPlayer play]; + } +} + +- (void)showAlertWithMessage:(NSString*)message { + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:nil + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action){ + }]; + + [alert addAction:defaultAction]; + [self presentViewController:alert animated:YES completion:nil]; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.h new file mode 100644 index 0000000000..759af5416f --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.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 <UIKit/UIKit.h> + +@class ARDSettingsModel; + +NS_ASSUME_NONNULL_BEGIN +/** + * Displays settings options. + */ +@interface ARDSettingsViewController : UITableViewController + +/** + * Creates new instance. + * + * @param style the table view style that should be used + * @param settingsModel model class for the user settings. + */ +- (instancetype)initWithStyle:(UITableViewStyle)style + settingsModel:(ARDSettingsModel *)settingsModel; + +#pragma mark - Unavailable + +- (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m new file mode 100644 index 0000000000..9bcbd3aa5c --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m @@ -0,0 +1,361 @@ +/* + * 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 "ARDSettingsViewController.h" +#import "ARDSettingsModel.h" +#import "RTCVideoCodecInfo+HumanReadable.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(int, ARDSettingsSections) { + ARDSettingsSectionAudioSettings = 0, + ARDSettingsSectionVideoResolution, + ARDSettingsSectionVideoCodec, + ARDSettingsSectionBitRate, +}; + +typedef NS_ENUM(int, ARDAudioSettingsOptions) { + ARDAudioSettingsAudioOnly = 0, + ARDAudioSettingsCreateAecDump, + ARDAudioSettingsUseManualAudioConfig, +}; + +@interface ARDSettingsViewController () <UITextFieldDelegate> { + ARDSettingsModel *_settingsModel; +} + +@end + +@implementation ARDSettingsViewController + +- (instancetype)initWithStyle:(UITableViewStyle)style + settingsModel:(ARDSettingsModel *)settingsModel { + self = [super initWithStyle:style]; + if (self) { + _settingsModel = settingsModel; + } + return self; +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"Settings"; + [self addDoneBarButton]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; +} + +#pragma mark - Data source + +- (NSArray<NSString *> *)videoResolutionArray { + return [_settingsModel availableVideoResolutions]; +} + +- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)videoCodecArray { + return [_settingsModel availableVideoCodecs]; +} + +#pragma mark - + +- (void)addDoneBarButton { + UIBarButtonItem *barItem = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone + target:self + action:@selector(dismissModally:)]; + self.navigationItem.leftBarButtonItem = barItem; +} + +#pragma mark - Dismissal of view controller + +- (void)dismissModally:(id)sender { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 4; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + switch (section) { + case ARDSettingsSectionAudioSettings: + return 3; + case ARDSettingsSectionVideoResolution: + return self.videoResolutionArray.count; + case ARDSettingsSectionVideoCodec: + return self.videoCodecArray.count; + default: + return 1; + } +} + +#pragma mark - Table view delegate helpers + +- (void)removeAllAccessories:(UITableView *)tableView + inSection:(int)section +{ + for (int i = 0; i < [tableView numberOfRowsInSection:section]; i++) { + NSIndexPath *rowPath = [NSIndexPath indexPathForRow:i inSection:section]; + UITableViewCell *cell = [tableView cellForRowAtIndexPath:rowPath]; + cell.accessoryType = UITableViewCellAccessoryNone; + } +} + +- (void)tableView:(UITableView *)tableView +updateListSelectionAtIndexPath:(NSIndexPath *)indexPath + inSection:(int)section { + [self removeAllAccessories:tableView inSection:section]; + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + cell.accessoryType = UITableViewCellAccessoryCheckmark; + [tableView deselectRowAtIndexPath:indexPath animated:YES]; +} + +#pragma mark - Table view delegate + +- (nullable NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + switch (section) { + case ARDSettingsSectionAudioSettings: + return @"Audio"; + case ARDSettingsSectionVideoResolution: + return @"Video resolution"; + case ARDSettingsSectionVideoCodec: + return @"Video codec"; + case ARDSettingsSectionBitRate: + return @"Maximum bitrate"; + default: + return @""; + } +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + switch (indexPath.section) { + case ARDSettingsSectionAudioSettings: + return [self audioSettingsTableViewCellForTableView:tableView atIndexPath:indexPath]; + + case ARDSettingsSectionVideoResolution: + return [self videoResolutionTableViewCellForTableView:tableView atIndexPath:indexPath]; + + case ARDSettingsSectionVideoCodec: + return [self videoCodecTableViewCellForTableView:tableView atIndexPath:indexPath]; + + case ARDSettingsSectionBitRate: + return [self bitrateTableViewCellForTableView:tableView atIndexPath:indexPath]; + + default: + return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:@"identifier"]; + } +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + switch (indexPath.section) { + case ARDSettingsSectionVideoResolution: + [self tableView:tableView disSelectVideoResolutionAtIndex:indexPath]; + break; + + case ARDSettingsSectionVideoCodec: + [self tableView:tableView didSelectVideoCodecCellAtIndexPath:indexPath]; + break; + } +} + +#pragma mark - Table view delegate(Video Resolution) + +- (UITableViewCell *)videoResolutionTableViewCellForTableView:(UITableView *)tableView + atIndexPath:(NSIndexPath *)indexPath { + NSString *dequeueIdentifier = @"ARDSettingsVideoResolutionViewCellIdentifier"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:dequeueIdentifier]; + } + NSString *resolution = self.videoResolutionArray[indexPath.row]; + cell.textLabel.text = resolution; + if ([resolution isEqualToString:[_settingsModel currentVideoResolutionSettingFromStore]]) { + cell.accessoryType = UITableViewCellAccessoryCheckmark; + } else { + cell.accessoryType = UITableViewCellAccessoryNone; + } + + return cell; +} + +- (void)tableView:(UITableView *)tableView + disSelectVideoResolutionAtIndex:(NSIndexPath *)indexPath { + [self tableView:tableView + updateListSelectionAtIndexPath:indexPath + inSection:ARDSettingsSectionVideoResolution]; + + NSString *videoResolution = self.videoResolutionArray[indexPath.row]; + [_settingsModel storeVideoResolutionSetting:videoResolution]; +} + +#pragma mark - Table view delegate(Video Codec) + +- (UITableViewCell *)videoCodecTableViewCellForTableView:(UITableView *)tableView + atIndexPath:(NSIndexPath *)indexPath { + NSString *dequeueIdentifier = @"ARDSettingsVideoCodecCellIdentifier"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:dequeueIdentifier]; + } + RTC_OBJC_TYPE(RTCVideoCodecInfo) *codec = self.videoCodecArray[indexPath.row]; + cell.textLabel.text = [codec humanReadableDescription]; + if ([codec isEqualToCodecInfo:[_settingsModel currentVideoCodecSettingFromStore]]) { + cell.accessoryType = UITableViewCellAccessoryCheckmark; + } else { + cell.accessoryType = UITableViewCellAccessoryNone; + } + + return cell; +} + +- (void)tableView:(UITableView *)tableView + didSelectVideoCodecCellAtIndexPath:(NSIndexPath *)indexPath { + [self tableView:tableView + updateListSelectionAtIndexPath:indexPath + inSection:ARDSettingsSectionVideoCodec]; + + RTC_OBJC_TYPE(RTCVideoCodecInfo) *videoCodec = self.videoCodecArray[indexPath.row]; + [_settingsModel storeVideoCodecSetting:videoCodec]; +} + +#pragma mark - Table view delegate(Bitrate) + +- (UITableViewCell *)bitrateTableViewCellForTableView:(UITableView *)tableView + atIndexPath:(NSIndexPath *)indexPath { + NSString *dequeueIdentifier = @"ARDSettingsBitrateCellIdentifier"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:dequeueIdentifier]; + + UITextField *textField = [[UITextField alloc] + initWithFrame:CGRectMake(10, 0, cell.bounds.size.width - 20, cell.bounds.size.height)]; + NSString *currentMaxBitrate = [_settingsModel currentMaxBitrateSettingFromStore].stringValue; + textField.text = currentMaxBitrate; + textField.placeholder = @"Enter max bit rate (kbps)"; + textField.keyboardType = UIKeyboardTypeNumberPad; + textField.delegate = self; + + // Numerical keyboards have no return button, we need to add one manually. + UIToolbar *numberToolbar = + [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 50)]; + numberToolbar.items = @[ + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil + action:nil], + [[UIBarButtonItem alloc] initWithTitle:@"Apply" + style:UIBarButtonItemStyleDone + target:self + action:@selector(numberTextFieldDidEndEditing:)] + ]; + [numberToolbar sizeToFit]; + + textField.inputAccessoryView = numberToolbar; + [cell addSubview:textField]; + } + return cell; +} + +- (void)numberTextFieldDidEndEditing:(id)sender { + [self.view endEditing:YES]; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField { + NSNumber *bitrateNumber = nil; + + if (textField.text.length != 0) { + bitrateNumber = [NSNumber numberWithInteger:textField.text.intValue]; + } + + [_settingsModel storeMaxBitrateSetting:bitrateNumber]; +} + +#pragma mark - Table view delegate(Audio settings) + +- (UITableViewCell *)audioSettingsTableViewCellForTableView:(UITableView *)tableView + atIndexPath:(NSIndexPath *)indexPath { + NSString *dequeueIdentifier = @"ARDSettingsAudioSettingsCellIdentifier"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:dequeueIdentifier]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero]; + switchView.tag = indexPath.row; + [switchView addTarget:self + action:@selector(audioSettingSwitchChanged:) + forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = switchView; + } + + cell.textLabel.text = [self labelForAudioSettingAtIndexPathRow:indexPath.row]; + UISwitch *switchView = (UISwitch *)cell.accessoryView; + switchView.on = [self valueForAudioSettingAtIndexPathRow:indexPath.row]; + + return cell; +} + +- (NSString *)labelForAudioSettingAtIndexPathRow:(NSInteger)setting { + switch (setting) { + case ARDAudioSettingsAudioOnly: + return @"Audio only"; + case ARDAudioSettingsCreateAecDump: + return @"Create AecDump"; + case ARDAudioSettingsUseManualAudioConfig: + return @"Use manual audio config"; + default: + return @""; + } +} + +- (BOOL)valueForAudioSettingAtIndexPathRow:(NSInteger)setting { + switch (setting) { + case ARDAudioSettingsAudioOnly: + return [_settingsModel currentAudioOnlySettingFromStore]; + case ARDAudioSettingsCreateAecDump: + return [_settingsModel currentCreateAecDumpSettingFromStore]; + case ARDAudioSettingsUseManualAudioConfig: + return [_settingsModel currentUseManualAudioConfigSettingFromStore]; + default: + return NO; + } +} + +- (void)audioSettingSwitchChanged:(UISwitch *)sender { + switch (sender.tag) { + case ARDAudioSettingsAudioOnly: { + [_settingsModel storeAudioOnlySetting:sender.isOn]; + break; + } + case ARDAudioSettingsCreateAecDump: { + [_settingsModel storeCreateAecDumpSetting:sender.isOn]; + break; + } + case ARDAudioSettingsUseManualAudioConfig: { + [_settingsModel storeUseManualAudioConfigSetting:sender.isOn]; + break; + } + default: + break; + } +} + +@end +NS_ASSUME_NONNULL_END diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDStatsView.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDStatsView.h new file mode 100644 index 0000000000..72207de64e --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDStatsView.h @@ -0,0 +1,21 @@ +/* + * 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 <UIKit/UIKit.h> + +#import "sdk/objc/base/RTCMacros.h" + +@class RTC_OBJC_TYPE(RTCStatisticsReport); + +@interface ARDStatsView : UIView + +- (void)setStats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDStatsView.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDStatsView.m new file mode 100644 index 0000000000..867ba5b09e --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDStatsView.m @@ -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 "ARDStatsView.h" + +#import "sdk/objc/api/peerconnection/RTCLegacyStatsReport.h" + +#import "ARDStatsBuilder.h" + +@implementation ARDStatsView { + UILabel *_statsLabel; + ARDStatsBuilder *_statsBuilder; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + _statsLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _statsLabel.numberOfLines = 0; + _statsLabel.font = [UIFont fontWithName:@"Roboto" size:12]; + _statsLabel.adjustsFontSizeToFitWidth = YES; + _statsLabel.minimumScaleFactor = 0.6; + _statsLabel.textColor = [UIColor greenColor]; + [self addSubview:_statsLabel]; + self.backgroundColor = [UIColor colorWithWhite:0 alpha:.6]; + _statsBuilder = [[ARDStatsBuilder alloc] init]; + } + return self; +} + +- (void)setStats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats { + _statsBuilder.stats = stats; + _statsLabel.text = _statsBuilder.statsString; +} + +- (void)layoutSubviews { + _statsLabel.frame = self.bounds; +} + +- (CGSize)sizeThatFits:(CGSize)size { + return [_statsLabel sizeThatFits:size]; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallView.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallView.h new file mode 100644 index 0000000000..a31c7fe742 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallView.h @@ -0,0 +1,47 @@ +/* + * 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 <UIKit/UIKit.h> + +#import "sdk/objc/base/RTCVideoRenderer.h" +#import "sdk/objc/helpers/RTCCameraPreviewView.h" + +#import "ARDStatsView.h" + +@class ARDVideoCallView; +@protocol ARDVideoCallViewDelegate <NSObject> + +// Called when the camera switch button is pressed. +- (void)videoCallView:(ARDVideoCallView *)view + shouldSwitchCameraWithCompletion:(void (^)(NSError *))completion; + +// Called when the route change button is pressed. +- (void)videoCallView:(ARDVideoCallView *)view + shouldChangeRouteWithCompletion:(void (^)(void))completion; + +// Called when the hangup button is pressed. +- (void)videoCallViewDidHangup:(ARDVideoCallView *)view; + +// Called when stats are enabled by triple tapping. +- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view; + +@end + +// Video call view that shows local and remote video, provides a label to +// display status, and also a hangup button. +@interface ARDVideoCallView : UIView + +@property(nonatomic, readonly) UILabel *statusLabel; +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCCameraPreviewView) * localVideoView; +@property(nonatomic, readonly) __kindof UIView<RTC_OBJC_TYPE(RTCVideoRenderer)> *remoteVideoView; +@property(nonatomic, readonly) ARDStatsView *statsView; +@property(nonatomic, weak) id<ARDVideoCallViewDelegate> delegate; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallView.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallView.m new file mode 100644 index 0000000000..437aea8d56 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallView.m @@ -0,0 +1,213 @@ +/* + * 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 "ARDVideoCallView.h" + +#import <AVFoundation/AVFoundation.h> + +#import "sdk/objc/components/renderer/metal/RTCMTLVideoView.h" + +#import "UIImage+ARDUtilities.h" + +static CGFloat const kButtonPadding = 16; +static CGFloat const kButtonSize = 48; +static CGFloat const kLocalVideoViewSize = 120; +static CGFloat const kLocalVideoViewPadding = 8; +static CGFloat const kStatusBarHeight = 20; + +@interface ARDVideoCallView () <RTC_OBJC_TYPE (RTCVideoViewDelegate)> +@end + +@implementation ARDVideoCallView { + UIButton *_routeChangeButton; + UIButton *_cameraSwitchButton; + UIButton *_hangupButton; + CGSize _remoteVideoSize; +} + +@synthesize statusLabel = _statusLabel; +@synthesize localVideoView = _localVideoView; +@synthesize remoteVideoView = _remoteVideoView; +@synthesize statsView = _statsView; +@synthesize delegate = _delegate; + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + + _remoteVideoView = [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectZero]; + + [self addSubview:_remoteVideoView]; + + _localVideoView = [[RTC_OBJC_TYPE(RTCCameraPreviewView) alloc] initWithFrame:CGRectZero]; + [self addSubview:_localVideoView]; + + _statsView = [[ARDStatsView alloc] initWithFrame:CGRectZero]; + _statsView.hidden = YES; + [self addSubview:_statsView]; + + _routeChangeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + _routeChangeButton.backgroundColor = [UIColor grayColor]; + _routeChangeButton.layer.cornerRadius = kButtonSize / 2; + _routeChangeButton.layer.masksToBounds = YES; + UIImage *image = [UIImage imageForName:@"ic_surround_sound_black_24dp.png" + color:[UIColor whiteColor]]; + [_routeChangeButton setImage:image forState:UIControlStateNormal]; + [_routeChangeButton addTarget:self + action:@selector(onRouteChange:) + forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_routeChangeButton]; + + // TODO(tkchin): don't display this if we can't actually do camera switch. + _cameraSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom]; + _cameraSwitchButton.backgroundColor = [UIColor grayColor]; + _cameraSwitchButton.layer.cornerRadius = kButtonSize / 2; + _cameraSwitchButton.layer.masksToBounds = YES; + image = [UIImage imageForName:@"ic_switch_video_black_24dp.png" color:[UIColor whiteColor]]; + [_cameraSwitchButton setImage:image forState:UIControlStateNormal]; + [_cameraSwitchButton addTarget:self + action:@selector(onCameraSwitch:) + forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_cameraSwitchButton]; + + _hangupButton = [UIButton buttonWithType:UIButtonTypeCustom]; + _hangupButton.backgroundColor = [UIColor redColor]; + _hangupButton.layer.cornerRadius = kButtonSize / 2; + _hangupButton.layer.masksToBounds = YES; + image = [UIImage imageForName:@"ic_call_end_black_24dp.png" + color:[UIColor whiteColor]]; + [_hangupButton setImage:image forState:UIControlStateNormal]; + [_hangupButton addTarget:self + action:@selector(onHangup:) + forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_hangupButton]; + + _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _statusLabel.font = [UIFont fontWithName:@"Roboto" size:16]; + _statusLabel.textColor = [UIColor whiteColor]; + [self addSubview:_statusLabel]; + + UITapGestureRecognizer *tapRecognizer = + [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(didTripleTap:)]; + tapRecognizer.numberOfTapsRequired = 3; + [self addGestureRecognizer:tapRecognizer]; + } + return self; +} + +- (void)layoutSubviews { + CGRect bounds = self.bounds; + if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) { + // Aspect fill remote video into bounds. + CGRect remoteVideoFrame = + AVMakeRectWithAspectRatioInsideRect(_remoteVideoSize, bounds); + CGFloat scale = 1; + if (remoteVideoFrame.size.width > remoteVideoFrame.size.height) { + // Scale by height. + scale = bounds.size.height / remoteVideoFrame.size.height; + } else { + // Scale by width. + scale = bounds.size.width / remoteVideoFrame.size.width; + } + remoteVideoFrame.size.height *= scale; + remoteVideoFrame.size.width *= scale; + _remoteVideoView.frame = remoteVideoFrame; + _remoteVideoView.center = + CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + } else { + _remoteVideoView.frame = bounds; + } + + // Aspect fit local video view into a square box. + CGRect localVideoFrame = + CGRectMake(0, 0, kLocalVideoViewSize, kLocalVideoViewSize); + // Place the view in the bottom right. + localVideoFrame.origin.x = CGRectGetMaxX(bounds) + - localVideoFrame.size.width - kLocalVideoViewPadding; + localVideoFrame.origin.y = CGRectGetMaxY(bounds) + - localVideoFrame.size.height - kLocalVideoViewPadding; + _localVideoView.frame = localVideoFrame; + + // Place stats at the top. + CGSize statsSize = [_statsView sizeThatFits:bounds.size]; + _statsView.frame = CGRectMake(CGRectGetMinX(bounds), + CGRectGetMinY(bounds) + kStatusBarHeight, + statsSize.width, statsSize.height); + + // Place hangup button in the bottom left. + _hangupButton.frame = + CGRectMake(CGRectGetMinX(bounds) + kButtonPadding, + CGRectGetMaxY(bounds) - kButtonPadding - + kButtonSize, + kButtonSize, + kButtonSize); + + // Place button to the right of hangup button. + CGRect cameraSwitchFrame = _hangupButton.frame; + cameraSwitchFrame.origin.x = + CGRectGetMaxX(cameraSwitchFrame) + kButtonPadding; + _cameraSwitchButton.frame = cameraSwitchFrame; + + // Place route button to the right of camera button. + CGRect routeChangeFrame = _cameraSwitchButton.frame; + routeChangeFrame.origin.x = + CGRectGetMaxX(routeChangeFrame) + kButtonPadding; + _routeChangeButton.frame = routeChangeFrame; + + [_statusLabel sizeToFit]; + _statusLabel.center = + CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); +} + +#pragma mark - RTC_OBJC_TYPE(RTCVideoViewDelegate) + +- (void)videoView:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)videoView didChangeVideoSize:(CGSize)size { + if (videoView == _remoteVideoView) { + _remoteVideoSize = size; + } + [self setNeedsLayout]; +} + +#pragma mark - Private + +- (void)onCameraSwitch:(UIButton *)sender { + sender.enabled = false; + [_delegate videoCallView:self + shouldSwitchCameraWithCompletion:^(NSError *error) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + sender.enabled = true; + }); + }]; +} + +- (void)onRouteChange:(UIButton *)sender { + sender.enabled = false; + __weak ARDVideoCallView *weakSelf = self; + [_delegate videoCallView:self + shouldChangeRouteWithCompletion:^(void) { + ARDVideoCallView *strongSelf = weakSelf; + if (strongSelf) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + sender.enabled = true; + }); + } + }]; +} + +- (void)onHangup:(id)sender { + [_delegate videoCallViewDidHangup:self]; +} + +- (void)didTripleTap:(UITapGestureRecognizer *)recognizer { + [_delegate videoCallViewDidEnableStats:self]; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.h new file mode 100644 index 0000000000..bdb8747524 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.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 <UIKit/UIKit.h> + +@class ARDVideoCallViewController; +@protocol ARDVideoCallViewControllerDelegate <NSObject> + +- (void)viewControllerDidFinish:(ARDVideoCallViewController *)viewController; + +@end + +@interface ARDVideoCallViewController : UIViewController + +@property(nonatomic, weak) id<ARDVideoCallViewControllerDelegate> delegate; + +- (instancetype)initForRoom:(NSString *)room + isLoopback:(BOOL)isLoopback + delegate:(id<ARDVideoCallViewControllerDelegate>)delegate; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m new file mode 100644 index 0000000000..a82d90b290 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m @@ -0,0 +1,250 @@ +/* + * 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 "ARDVideoCallViewController.h" + +#import "sdk/objc/api/peerconnection/RTCMediaConstraints.h" +#import "sdk/objc/base/RTCLogging.h" +#import "sdk/objc/components/audio/RTCAudioSession.h" +#import "sdk/objc/components/capturer/RTCCameraVideoCapturer.h" +#import "sdk/objc/helpers/RTCDispatcher.h" + +#import "ARDAppClient.h" +#import "ARDCaptureController.h" +#import "ARDFileCaptureController.h" +#import "ARDSettingsModel.h" +#import "ARDVideoCallView.h" + +@interface ARDVideoCallViewController () <ARDAppClientDelegate, + ARDVideoCallViewDelegate, + RTC_OBJC_TYPE (RTCAudioSessionDelegate)> +@property(nonatomic, strong) RTC_OBJC_TYPE(RTCVideoTrack) * remoteVideoTrack; +@property(nonatomic, readonly) ARDVideoCallView *videoCallView; +@property(nonatomic, assign) AVAudioSessionPortOverride portOverride; +@end + +@implementation ARDVideoCallViewController { + ARDAppClient *_client; + RTC_OBJC_TYPE(RTCVideoTrack) * _remoteVideoTrack; + ARDCaptureController *_captureController; + ARDFileCaptureController *_fileCaptureController NS_AVAILABLE_IOS(10); +} + +@synthesize videoCallView = _videoCallView; +@synthesize remoteVideoTrack = _remoteVideoTrack; +@synthesize delegate = _delegate; +@synthesize portOverride = _portOverride; + +- (instancetype)initForRoom:(NSString *)room + isLoopback:(BOOL)isLoopback + delegate:(id<ARDVideoCallViewControllerDelegate>)delegate { + if (self = [super init]) { + ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; + _delegate = delegate; + + _client = [[ARDAppClient alloc] initWithDelegate:self]; + [_client connectToRoomWithId:room settings:settingsModel isLoopback:isLoopback]; + } + return self; +} + +- (void)loadView { + _videoCallView = [[ARDVideoCallView alloc] initWithFrame:CGRectZero]; + _videoCallView.delegate = self; + _videoCallView.statusLabel.text = + [self statusTextForState:RTCIceConnectionStateNew]; + self.view = _videoCallView; + + RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session addDelegate:self]; +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskAll; +} + +#pragma mark - ARDAppClientDelegate + +- (void)appClient:(ARDAppClient *)client + didChangeState:(ARDAppClientState)state { + switch (state) { + case kARDAppClientStateConnected: + RTCLog(@"Client connected."); + break; + case kARDAppClientStateConnecting: + RTCLog(@"Client connecting."); + break; + case kARDAppClientStateDisconnected: + RTCLog(@"Client disconnected."); + [self hangup]; + break; + } +} + +- (void)appClient:(ARDAppClient *)client + didChangeConnectionState:(RTCIceConnectionState)state { + RTCLog(@"ICE state changed: %ld", (long)state); + __weak ARDVideoCallViewController *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + ARDVideoCallViewController *strongSelf = weakSelf; + strongSelf.videoCallView.statusLabel.text = + [strongSelf statusTextForState:state]; + }); +} + +- (void)appClient:(ARDAppClient *)client + didCreateLocalCapturer:(RTC_OBJC_TYPE(RTCCameraVideoCapturer) *)localCapturer { + _videoCallView.localVideoView.captureSession = localCapturer.captureSession; + ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; + _captureController = + [[ARDCaptureController alloc] initWithCapturer:localCapturer settings:settingsModel]; + [_captureController startCapture]; +} + +- (void)appClient:(ARDAppClient *)client + didCreateLocalFileCapturer:(RTC_OBJC_TYPE(RTCFileVideoCapturer) *)fileCapturer { +#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) + if (@available(iOS 10, *)) { + _fileCaptureController = [[ARDFileCaptureController alloc] initWithCapturer:fileCapturer]; + [_fileCaptureController startCapture]; + } +#endif +} + +- (void)appClient:(ARDAppClient *)client + didReceiveLocalVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)localVideoTrack { +} + +- (void)appClient:(ARDAppClient *)client + didReceiveRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack { + self.remoteVideoTrack = remoteVideoTrack; + __weak ARDVideoCallViewController *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + ARDVideoCallViewController *strongSelf = weakSelf; + strongSelf.videoCallView.statusLabel.hidden = YES; + }); +} + +- (void)appClient:(ARDAppClient *)client didGetStats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats { + _videoCallView.statsView.stats = stats; + [_videoCallView setNeedsLayout]; +} + +- (void)appClient:(ARDAppClient *)client + didError:(NSError *)error { + NSString *message = + [NSString stringWithFormat:@"%@", error.localizedDescription]; + [self hangup]; + [self showAlertWithMessage:message]; +} + +#pragma mark - ARDVideoCallViewDelegate + +- (void)videoCallViewDidHangup:(ARDVideoCallView *)view { + [self hangup]; +} + +- (void)videoCallView:(ARDVideoCallView *)view + shouldSwitchCameraWithCompletion:(void (^)(NSError *))completion { + [_captureController switchCamera:completion]; +} + +- (void)videoCallView:(ARDVideoCallView *)view + shouldChangeRouteWithCompletion:(void (^)(void))completion { + NSParameterAssert(completion); + AVAudioSessionPortOverride override = AVAudioSessionPortOverrideNone; + if (_portOverride == AVAudioSessionPortOverrideNone) { + override = AVAudioSessionPortOverrideSpeaker; + } + [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeAudioSession + block:^{ + RTC_OBJC_TYPE(RTCAudioSession) *session = + [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session lockForConfiguration]; + NSError *error = nil; + if ([session overrideOutputAudioPort:override + error:&error]) { + self.portOverride = override; + } else { + RTCLogError(@"Error overriding output port: %@", + error.localizedDescription); + } + [session unlockForConfiguration]; + completion(); + }]; +} + +- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view { + _client.shouldGetStats = YES; + _videoCallView.statsView.hidden = NO; +} + +#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate) + +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches { + RTCLog(@"Audio session detected glitch, total: %lld", totalNumberOfGlitches); +} + +#pragma mark - Private + +- (void)setRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack { + if (_remoteVideoTrack == remoteVideoTrack) { + return; + } + [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView]; + _remoteVideoTrack = nil; + [_videoCallView.remoteVideoView renderFrame:nil]; + _remoteVideoTrack = remoteVideoTrack; + [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView]; +} + +- (void)hangup { + self.remoteVideoTrack = nil; + _videoCallView.localVideoView.captureSession = nil; + [_captureController stopCapture]; + _captureController = nil; + [_fileCaptureController stopCapture]; + _fileCaptureController = nil; + [_client disconnect]; + [_delegate viewControllerDidFinish:self]; +} + +- (NSString *)statusTextForState:(RTCIceConnectionState)state { + switch (state) { + case RTCIceConnectionStateNew: + case RTCIceConnectionStateChecking: + return @"Connecting..."; + case RTCIceConnectionStateConnected: + case RTCIceConnectionStateCompleted: + case RTCIceConnectionStateFailed: + case RTCIceConnectionStateDisconnected: + case RTCIceConnectionStateClosed: + case RTCIceConnectionStateCount: + return nil; + } +} + +- (void)showAlertWithMessage:(NSString*)message { + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:nil + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action){ + }]; + + [alert addAction:defaultAction]; + [self presentViewController:alert animated:YES completion:nil]; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/Info.plist b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/Info.plist new file mode 100644 index 0000000000..a2f0a683ed --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/Info.plist @@ -0,0 +1,109 @@ +<?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>BuildMachineOSBuild</key> + <string>12E55</string> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>AppRTCMobile</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIcons</key> + <dict> + <key>CFBundlePrimaryIcon</key> + <dict> + <key>CFBundleIconFiles</key> + <array> + <string>Icon.png</string> + <string>Icon-120.png</string> + <string>Icon-180.png</string> + </array> + </dict> + </dict> + <key>CFBundleIdentifier</key> + <string>com.google.AppRTCMobile</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>iPhoneOS</string> + </array> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>UIStatusBarTintParameters</key> + <dict> + <key>UINavigationBar</key> + <dict> + <key>Style</key> + <string>UIBarStyleDefault</string> + <key>Translucent</key> + <false/> + </dict> + </dict> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + </array> + <key>UIAppFonts</key> + <array> + <string>Roboto-Regular.ttf</string> + </array> + <key>UIBackgroundModes</key> + <array> + <string>audio</string> + <string>voip</string> + </array> + <key>NSCameraUsageDescription</key> + <string>Camera access needed for video calling</string> + <key>NSMicrophoneUsageDescription</key> + <string>Microphone access needed for video calling</string> + <key>UIFileSharingEnabled</key> + <true/> + <key>UILaunchImages</key> + <array> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>7.0</string> + <key>UILaunchImageName</key> + <string>iPhone5</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{320, 568}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>8.0</string> + <key>UILaunchImageName</key> + <string>iPhone6</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{375, 667}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>8.0</string> + <key>UILaunchImageName</key> + <string>iPhone6p</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{414, 736}</string> + </dict> + </array> +</dict> +</plist> diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/RTCVideoCodecInfo+HumanReadable.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/RTCVideoCodecInfo+HumanReadable.h new file mode 100644 index 0000000000..3a93c253b2 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/RTCVideoCodecInfo+HumanReadable.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 "sdk/objc/base/RTCVideoCodecInfo.h" + +@interface RTC_OBJC_TYPE (RTCVideoCodecInfo) +(HumanReadable) + + - (NSString *)humanReadableDescription; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/RTCVideoCodecInfo+HumanReadable.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/RTCVideoCodecInfo+HumanReadable.m new file mode 100644 index 0000000000..5e0c52c5c4 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/RTCVideoCodecInfo+HumanReadable.m @@ -0,0 +1,37 @@ +/* + * 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+HumanReadable.h" + +#import "sdk/objc/components/video_codec/RTCH264ProfileLevelId.h" + +@implementation RTC_OBJC_TYPE (RTCVideoCodecInfo) +(HumanReadable) + + - (NSString *)humanReadableDescription { + if ([self.name isEqualToString:@"H264"]) { + NSString *profileId = self.parameters[@"profile-level-id"]; + RTC_OBJC_TYPE(RTCH264ProfileLevelId) *profileLevelId = + [[RTC_OBJC_TYPE(RTCH264ProfileLevelId) alloc] initWithHexString:profileId]; + if (profileLevelId.profile == RTCH264ProfileConstrainedHigh || + profileLevelId.profile == RTCH264ProfileHigh) { + return @"H264 (High)"; + } else if (profileLevelId.profile == RTCH264ProfileConstrainedBaseline || + profileLevelId.profile == RTCH264ProfileBaseline) { + return @"H264 (Baseline)"; + } else { + return [NSString stringWithFormat:@"H264 (%@)", profileId]; + } + } else { + return self.name; + } +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/UIImage+ARDUtilities.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/UIImage+ARDUtilities.h new file mode 100644 index 0000000000..d56ba02c2e --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/UIImage+ARDUtilities.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 <UIKit/UIKit.h> + +@interface UIImage (ARDUtilities) + +// Returns an color tinted version for the given image resource. ++ (UIImage *)imageForName:(NSString *)name color:(UIColor *)color; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/UIImage+ARDUtilities.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/UIImage+ARDUtilities.m new file mode 100644 index 0000000000..1bbe8c342f --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/UIImage+ARDUtilities.m @@ -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 "UIImage+ARDUtilities.h" + +@implementation UIImage (ARDUtilities) + ++ (UIImage *)imageForName:(NSString *)name color:(UIColor *)color { + UIImage *image = [UIImage imageNamed:name]; + if (!image) { + return nil; + } + UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0f); + [color setFill]; + CGRect bounds = CGRectMake(0, 0, image.size.width, image.size.height); + UIRectFill(bounds); + [image drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; + UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return coloredImage; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSampleHandler.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSampleHandler.h new file mode 100644 index 0000000000..2c4a56368a --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSampleHandler.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 <ReplayKit/ReplayKit.h> + +#import "sdk/objc/base/RTCLogging.h" + +#import "ARDAppClient.h" + +@protocol ARDExternalSampleDelegate; + +API_AVAILABLE(ios(10.0)) +@interface ARDBroadcastSampleHandler : RPBroadcastSampleHandler <ARDAppClientDelegate> + +@property(nonatomic, strong) id<ARDExternalSampleDelegate> capturer; + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSampleHandler.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSampleHandler.m new file mode 100644 index 0000000000..1c276d965f --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSampleHandler.m @@ -0,0 +1,130 @@ +/* + * 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 "ARDBroadcastSampleHandler.h" + +#import <os/log.h> + +#import "ARDExternalSampleCapturer.h" +#import "ARDSettingsModel.h" + +#import "sdk/objc/api/logging/RTCCallbackLogger.h" +#import "sdk/objc/base/RTCLogging.h" + +@implementation ARDBroadcastSampleHandler { + ARDAppClient *_client; + RTC_OBJC_TYPE(RTCCallbackLogger) * _callbackLogger; +} + +@synthesize capturer = _capturer; + +- (instancetype)init { + if (self = [super init]) { + _callbackLogger = [[RTC_OBJC_TYPE(RTCCallbackLogger) alloc] init]; + os_log_t rtc_os_log = os_log_create("com.google.AppRTCMobile", "RTCLog"); + [_callbackLogger start:^(NSString *logMessage) { + os_log(rtc_os_log, "%{public}s", [logMessage cStringUsingEncoding:NSUTF8StringEncoding]); + }]; + } + return self; +} + +- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *, NSObject *> *)setupInfo { + // User has requested to start the broadcast. Setup info from the UI extension can be supplied but + // optional. + ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; + + _client = [[ARDAppClient alloc] initWithDelegate:self]; + _client.broadcast = YES; + + NSString *roomName = nil; + if (setupInfo[@"roomName"]) { + roomName = (NSString *)setupInfo[@"roomName"]; + } else { + u_int32_t randomRoomSuffix = arc4random_uniform(1000); + roomName = [NSString stringWithFormat:@"broadcast_%d", randomRoomSuffix]; + } + [_client connectToRoomWithId:roomName settings:settingsModel isLoopback:NO]; + RTCLog(@"Broadcast started."); +} + +- (void)broadcastPaused { + // User has requested to pause the broadcast. Samples will stop being delivered. +} + +- (void)broadcastResumed { + // User has requested to resume the broadcast. Samples delivery will resume. +} + +- (void)broadcastFinished { + // User has requested to finish the broadcast. + [_client disconnect]; +} + +- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer + withType:(RPSampleBufferType)sampleBufferType { + switch (sampleBufferType) { + case RPSampleBufferTypeVideo: + [self.capturer didCaptureSampleBuffer:sampleBuffer]; + break; + case RPSampleBufferTypeAudioApp: + break; + case RPSampleBufferTypeAudioMic: + break; + default: + break; + } +} + +#pragma mark - ARDAppClientDelegate + +- (void)appClient:(ARDAppClient *)client didChangeState:(ARDAppClientState)state { + switch (state) { + case kARDAppClientStateConnected: + RTCLog(@"Client connected."); + break; + case kARDAppClientStateConnecting: + RTCLog("Client connecting."); + break; + case kARDAppClientStateDisconnected: + RTCLog(@"Client disconnected."); + break; + } +} + +- (void)appClient:(ARDAppClient *)client didChangeConnectionState:(RTCIceConnectionState)state { + RTCLog(@"ICE state changed: %ld", (long)state); +} + +- (void)appClient:(ARDAppClient *)client + didCreateLocalCapturer:(RTC_OBJC_TYPE(RTCCameraVideoCapturer) *)localCapturer { +} + +- (void)appClient:(ARDAppClient *)client + didCreateLocalExternalSampleCapturer:(ARDExternalSampleCapturer *)externalSampleCapturer { + self.capturer = externalSampleCapturer; +} + +- (void)appClient:(ARDAppClient *)client + didReceiveLocalVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)localVideoTrack { +} + +- (void)appClient:(ARDAppClient *)client + didReceiveRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack { +} + +- (void)appClient:(ARDAppClient *)client didGetStats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats { +} + +- (void)appClient:(ARDAppClient *)client didError:(NSError *)error { + RTCLog(@"Error: %@", error); +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSetupViewController.h b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSetupViewController.h new file mode 100644 index 0000000000..bbf397d8a9 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSetupViewController.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 <ReplayKit/ReplayKit.h> +#import <UIKit/UIKit.h> + +API_AVAILABLE(ios(11.0)) +@interface ARDBroadcastSetupViewController : UIViewController <UITextFieldDelegate> + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSetupViewController.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSetupViewController.m new file mode 100644 index 0000000000..55438f17d8 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/ARDBroadcastSetupViewController.m @@ -0,0 +1,107 @@ +/* + * 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 "ARDBroadcastSetupViewController.h" + +@implementation ARDBroadcastSetupViewController { + UITextField *_roomNameField; +} + +- (void)loadView { + UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; + view.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.7]; + + UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Icon-180"]]; + imageView.translatesAutoresizingMaskIntoConstraints = NO; + [view addSubview:imageView]; + + _roomNameField = [[UITextField alloc] initWithFrame:CGRectZero]; + _roomNameField.borderStyle = UITextBorderStyleRoundedRect; + _roomNameField.font = [UIFont systemFontOfSize:14.0]; + _roomNameField.translatesAutoresizingMaskIntoConstraints = NO; + _roomNameField.placeholder = @"Room name"; + _roomNameField.returnKeyType = UIReturnKeyDone; + _roomNameField.delegate = self; + [view addSubview:_roomNameField]; + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; + doneButton.translatesAutoresizingMaskIntoConstraints = NO; + doneButton.titleLabel.font = [UIFont systemFontOfSize:20.0]; + [doneButton setTitle:@"Done" forState:UIControlStateNormal]; + [doneButton addTarget:self + action:@selector(userDidFinishSetup) + forControlEvents:UIControlEventTouchUpInside]; + [view addSubview:doneButton]; + + UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeSystem]; + cancelButton.translatesAutoresizingMaskIntoConstraints = NO; + cancelButton.titleLabel.font = [UIFont systemFontOfSize:20.0]; + [cancelButton setTitle:@"Cancel" forState:UIControlStateNormal]; + [cancelButton addTarget:self + action:@selector(userDidCancelSetup) + forControlEvents:UIControlEventTouchUpInside]; + [view addSubview:cancelButton]; + + UILayoutGuide *margin = view.layoutMarginsGuide; + [imageView.widthAnchor constraintEqualToConstant:60.0].active = YES; + [imageView.heightAnchor constraintEqualToConstant:60.0].active = YES; + [imageView.topAnchor constraintEqualToAnchor:margin.topAnchor constant:20].active = YES; + [imageView.centerXAnchor constraintEqualToAnchor:view.centerXAnchor].active = YES; + + [_roomNameField.leadingAnchor constraintEqualToAnchor:margin.leadingAnchor].active = YES; + [_roomNameField.topAnchor constraintEqualToAnchor:imageView.bottomAnchor constant:20].active = + YES; + [_roomNameField.trailingAnchor constraintEqualToAnchor:margin.trailingAnchor].active = YES; + + [doneButton.leadingAnchor constraintEqualToAnchor:margin.leadingAnchor].active = YES; + [doneButton.bottomAnchor constraintEqualToAnchor:margin.bottomAnchor constant:-20].active = YES; + + [cancelButton.trailingAnchor constraintEqualToAnchor:margin.trailingAnchor].active = YES; + [cancelButton.bottomAnchor constraintEqualToAnchor:margin.bottomAnchor constant:-20].active = YES; + + UITapGestureRecognizer *tgr = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)]; + [view addGestureRecognizer:tgr]; + + self.view = view; +} + +- (IBAction)didTap:(id)sender { + [self.view endEditing:YES]; +} + +- (void)userDidFinishSetup { + // URL of the resource where broadcast can be viewed that will be returned to the application + NSURL *broadcastURL = [NSURL + URLWithString:[NSString stringWithFormat:@"https://appr.tc/r/%@", _roomNameField.text]]; + + // Dictionary with setup information that will be provided to broadcast extension when broadcast + // is started + NSDictionary *setupInfo = @{@"roomName" : _roomNameField.text}; + + // Tell ReplayKit that the extension is finished setting up and can begin broadcasting + [self.extensionContext completeRequestWithBroadcastURL:broadcastURL setupInfo:setupInfo]; +} + +- (void)userDidCancelSetup { + // Tell ReplayKit that the extension was cancelled by the user + [self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@"com.google.AppRTCMobile" + code:-1 + userInfo:nil]]; +} + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [self userDidFinishSetup]; + return YES; +} + +@end diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/BroadcastSetupUIInfo.plist b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/BroadcastSetupUIInfo.plist new file mode 100644 index 0000000000..a123c111e5 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/BroadcastSetupUIInfo.plist @@ -0,0 +1,39 @@ +<?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>CFBundleDisplayName</key> + <string>AppRTCMobile</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>com.google.AppRTCMobile.BroadcastSetupUI</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>XPC!</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>NSExtension</key> + <dict> + <key>NSExtensionAttributes</key> + <dict> + <key>NSExtensionActivationRule</key> + <dict> + <key>NSExtensionActivationSupportsReplayKitStreaming</key> + <true/> + </dict> + </dict> + <key>NSExtensionPointIdentifier</key> + <string>com.apple.broadcast-services-setupui</string> + <key>NSExtensionPrincipalClass</key> + <string>ARDBroadcastSetupViewController</string> + </dict> +</dict> +</plist> diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/BroadcastUploadInfo.plist b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/BroadcastUploadInfo.plist new file mode 100644 index 0000000000..2bab60ea8f --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/broadcast_extension/BroadcastUploadInfo.plist @@ -0,0 +1,33 @@ +<?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>CFBundleDisplayName</key> + <string>AppRTCMobile</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>com.google.AppRTCMobile.BroadcastUpload</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>XPC!</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>NSExtension</key> + <dict> + <key>NSExtensionPointIdentifier</key> + <string>com.apple.broadcast-services-upload</string> + <key>NSExtensionPrincipalClass</key> + <string>ARDBroadcastSampleHandler</string> + <key>RPBroadcastProcessMode</key> + <string>RPBroadcastProcessModeSampleBuffer</string> + </dict> +</dict> +</plist> diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/main.m b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/main.m new file mode 100644 index 0000000000..00b83f7fd2 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/main.m @@ -0,0 +1,20 @@ +/* + * Copyright 2013 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be 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> + +#import "ARDAppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain( + argc, argv, nil, NSStringFromClass([ARDAppDelegate class])); + } +} diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/Roboto-Regular.ttf b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/Roboto-Regular.ttf Binary files differnew file mode 100644 index 0000000000..0e58508a64 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/Roboto-Regular.ttf diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/foreman.mp4 b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/foreman.mp4 Binary files differnew file mode 100644 index 0000000000..ccffbf4722 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/foreman.mp4 diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone5@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone5@2x.png Binary files differnew file mode 100644 index 0000000000..9d005fde06 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone5@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone6@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone6@2x.png Binary files differnew file mode 100644 index 0000000000..fce3eb95b3 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone6@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone6p@3x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone6p@3x.png Binary files differnew file mode 100644 index 0000000000..aee20c2209 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/iPhone6p@3x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_call_end_black_24dp.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_call_end_black_24dp.png Binary files differnew file mode 100644 index 0000000000..531cb0f280 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_call_end_black_24dp.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_call_end_black_24dp@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_call_end_black_24dp@2x.png Binary files differnew file mode 100644 index 0000000000..03dd381c10 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_call_end_black_24dp@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_clear_black_24dp.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_clear_black_24dp.png Binary files differnew file mode 100644 index 0000000000..4ebf8a2270 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_clear_black_24dp.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_clear_black_24dp@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_clear_black_24dp@2x.png Binary files differnew file mode 100644 index 0000000000..ed2b2525fd --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_clear_black_24dp@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_settings_black_24dp.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_settings_black_24dp.png Binary files differnew file mode 100644 index 0000000000..c59419c02b --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_settings_black_24dp.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_settings_black_24dp@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_settings_black_24dp@2x.png Binary files differnew file mode 100644 index 0000000000..e84e188a1d --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_settings_black_24dp@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_surround_sound_black_24dp.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_surround_sound_black_24dp.png Binary files differnew file mode 100644 index 0000000000..8f3343d3a7 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_surround_sound_black_24dp.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_surround_sound_black_24dp@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_surround_sound_black_24dp@2x.png Binary files differnew file mode 100644 index 0000000000..764880467a --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_surround_sound_black_24dp@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_switch_video_black_24dp.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_switch_video_black_24dp.png Binary files differnew file mode 100644 index 0000000000..85271c8253 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_switch_video_black_24dp.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_switch_video_black_24dp@2x.png b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_switch_video_black_24dp@2x.png Binary files differnew file mode 100644 index 0000000000..62b13a6a09 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/ic_switch_video_black_24dp@2x.png diff --git a/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/mozart.mp3 b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/mozart.mp3 Binary files differnew file mode 100644 index 0000000000..5981ba3a91 --- /dev/null +++ b/third_party/libwebrtc/examples/objc/AppRTCMobile/ios/resources/mozart.mp3 |