diff --git a/webrtc/examples/BUILD.gn b/webrtc/examples/BUILD.gn index 3945e560c2..faa05d3b7e 100644 --- a/webrtc/examples/BUILD.gn +++ b/webrtc/examples/BUILD.gn @@ -241,6 +241,12 @@ if (is_ios || (is_mac && target_cpu != "x86")) { "objc/AppRTCMobile/ios/ARDMainView.m", "objc/AppRTCMobile/ios/ARDMainViewController.h", "objc/AppRTCMobile/ios/ARDMainViewController.m", + "objc/AppRTCMobile/ios/ARDMediaConstraintsModel.h", + "objc/AppRTCMobile/ios/ARDMediaConstraintsModel.m", + "objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.h", + "objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.m", + "objc/AppRTCMobile/ios/ARDSettingsViewController.h", + "objc/AppRTCMobile/ios/ARDSettingsViewController.m", "objc/AppRTCMobile/ios/ARDStatsView.h", "objc/AppRTCMobile/ios/ARDStatsView.m", "objc/AppRTCMobile/ios/ARDVideoCallView.h", diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h index 2186abe693..d906ec726f 100644 --- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h +++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h @@ -23,6 +23,8 @@ typedef NS_ENUM(NSInteger, ARDAppClientState) { }; @class ARDAppClient; +@class RTCMediaConstraints; + // The delegate is informed of pertinent events and will be called on the // main queue. @protocol ARDAppClientDelegate @@ -56,11 +58,13 @@ typedef NS_ENUM(NSInteger, ARDAppClientState) { @property(nonatomic, assign) BOOL shouldGetStats; @property(nonatomic, readonly) ARDAppClientState state; @property(nonatomic, weak) id delegate; - // Convenience constructor since all expected use cases will need a delegate // in order to receive remote tracks. - (instancetype)initWithDelegate:(id)delegate; +// Sets camera constraints. +- (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints; + // Establishes a connection with the AppRTC servers for the given room id. // If |isLoopback| is true, the call will connect to itself. // If |isAudioOnly| is true, video will be disabled for the call. diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m index 1020621cc1..9ef46b4742 100644 --- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m +++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m @@ -101,6 +101,7 @@ static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB. @implementation ARDAppClient { RTCFileLogger *_fileLogger; ARDTimerProxy *_statsTimer; + RTCMediaConstraints *_cameraConstraints; } @synthesize shouldGetStats = _shouldGetStats; @@ -321,6 +322,10 @@ static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB. #endif } +- (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints { + _cameraConstraints = mediaConstraints; +} + #pragma mark - ARDSignalingChannelDelegate - (void)channel:(id)channel @@ -695,10 +700,10 @@ static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB. // trying to open a local stream. #if !TARGET_IPHONE_SIMULATOR if (!_isAudioOnly) { - RTCMediaConstraints *mediaConstraints = - [self defaultMediaStreamConstraints]; + RTCMediaConstraints *cameraConstraints = + [self cameraConstraints]; RTCAVFoundationVideoSource *source = - [_factory avFoundationVideoSourceWithConstraints:mediaConstraints]; + [_factory avFoundationVideoSourceWithConstraints:cameraConstraints]; localVideoTrack = [_factory videoTrackWithSource:source trackId:kARDVideoTrackId]; @@ -737,18 +742,14 @@ static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB. NSString *valueLevelControl = _shouldUseLevelControl ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse; NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl }; - RTCMediaConstraints* constraints = - [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints - optionalConstraints:nil]; + RTCMediaConstraints *constraints = + [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints + optionalConstraints:nil]; return constraints; } -- (RTCMediaConstraints *)defaultMediaStreamConstraints { - RTCMediaConstraints* constraints = - [[RTCMediaConstraints alloc] - initWithMandatoryConstraints:nil - optionalConstraints:nil]; - return constraints; +- (RTCMediaConstraints *)cameraConstraints { + return _cameraConstraints; } - (RTCMediaConstraints *)defaultAnswerConstraints { diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m index f7e03bc72f..33ff8fa9c6 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m @@ -19,9 +19,11 @@ #import "ARDAppClient.h" #import "ARDMainView.h" +#import "ARDMediaConstraintsModel.h" +#import "ARDSettingsViewController.h" #import "ARDVideoCallViewController.h" -static NSString *barButtonImageString = @"ic_settings_black_24dp.png"; +static NSString *const barButtonImageString = @"ic_settings_black_24dp.png"; @interface ARDMainViewController () < ARDMainViewDelegate, @@ -169,6 +171,16 @@ static NSString *barButtonImageString = @"ic_settings_black_24dp.png"; #pragma mark - Private - (void)showSettings:(id)sender { + ARDSettingsViewController *settingsController = + [[ARDSettingsViewController alloc] initWithStyle:UITableViewStylePlain + mediaConstraintsModel:[[ARDMediaConstraintsModel alloc] init]]; + UINavigationController *navigationController = + [[UINavigationController alloc] initWithRootViewController:settingsController]; + [self presentViewControllerAsModal:navigationController]; +} + +- (void)presentViewControllerAsModal:(UIViewController *)viewController { + [self presentViewController:viewController animated:YES completion:nil]; } - (void)configureAudioSession { diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel+Private.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel+Private.h new file mode 100644 index 0000000000..57d507b814 --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel+Private.h @@ -0,0 +1,22 @@ +/* + * 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 +#import "ARDMediaConstraintsModel.h" + +@class ARDMediaConstraintsSettingsStore; + +NS_ASSUME_NONNULL_BEGIN +@interface ARDMediaConstraintsModel () +- (ARDMediaConstraintsSettingsStore *)settingsStore; +- (nullable NSString *)currentVideoResolutionWidthFromStore; +- (nullable NSString *)currentVideoResolutionHeightFromStore; +@end +NS_ASSUME_NONNULL_END diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel.h new file mode 100644 index 0000000000..2f963e0361 --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel.h @@ -0,0 +1,60 @@ +/* + * 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 + +NS_ASSUME_NONNULL_BEGIN +/** + * Model class for user defined media constraints. + * + * Currently used for streaming media constraints only. + * In future audio media constraints support can be added as well. + * Offers list of avaliable video resolutions that can construct streaming media constraint. + * Exposes methods for reading and storing media constraints from persistent store. + * Also translates current user defined media constraint into RTCMediaConstraints + * dictionary. + */ +@interface ARDMediaConstraintsModel : NSObject + +/** + * Returns array of available capture resoultions. + * + * The capture resolutions are represented as strings in the following format + * [width]x[height] + */ +- (NSArray *)availableVideoResoultionsMediaConstraints; + +/** + * Returns current video resolution media constraint string. + * If no constraint is in store, default value of 640x480 is returned. + * When defaulting to value, the default is saved in store for consistency reasons. + */ +- (NSString *)currentVideoResoultionConstraintFromStore; + +/** + * Stores the provided video resolution media constraint string into the store. + * + * If the provided constraint is no part of the available video resolutions + * the store operation will not be executed and NO will be returned. + * @param constraint the string to be stored. + * @return YES/NO depending on success. + */ +- (BOOL)storeVideoResoultionConstraint:(NSString *)constraint; + +/** + * Converts the current media constraints from store into dictionary with RTCMediaConstraints + * values. + * + * @return NSDictionary with RTC width and height parameters + */ +- (nullable NSDictionary *)currentMediaConstraintFromStoreAsRTCDictionary; + +@end +NS_ASSUME_NONNULL_END diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel.m new file mode 100644 index 0000000000..0299301525 --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsModel.m @@ -0,0 +1,104 @@ +/* + * 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 "ARDMediaConstraintsModel+Private.h" +#import "ARDMediaConstraintsSettingsStore.h" +#import "WebRTC/RTCMediaConstraints.h" + +NS_ASSUME_NONNULL_BEGIN +static NSArray *videoResolutionsStaticValues() { + return @[ @"640x480", @"960x540", @"1280x720" ]; +} + +@interface ARDMediaConstraintsModel () { + ARDMediaConstraintsSettingsStore *_settingsStore; +} +@end + +@implementation ARDMediaConstraintsModel + +- (NSArray *)availableVideoResoultionsMediaConstraints { + return videoResolutionsStaticValues(); +} + +- (NSString *)currentVideoResoultionConstraintFromStore { + NSString *constraint = [[self settingsStore] videoResolutionConstraintsSetting]; + if (!constraint) { + constraint = [self defaultVideoResolutionMediaConstraint]; + // To ensure consistency add the default to the store. + [[self settingsStore] setVideoResolutionConstraintsSetting:constraint]; + } + return constraint; +} + +- (BOOL)storeVideoResoultionConstraint:(NSString *)constraint { + if (![[self availableVideoResoultionsMediaConstraints] containsObject:constraint]) { + return NO; + } + [[self settingsStore] setVideoResolutionConstraintsSetting:constraint]; + return YES; +} + +#pragma mark - Testable + +- (ARDMediaConstraintsSettingsStore *)settingsStore { + if (!_settingsStore) { + _settingsStore = [[ARDMediaConstraintsSettingsStore alloc] init]; + } + return _settingsStore; +} + +- (nullable NSString *)currentVideoResolutionWidthFromStore { + NSString *mediaConstraintFromStore = [self currentVideoResoultionConstraintFromStore]; + + return [self videoResolutionComponentAtIndex:0 inConstraintsString:mediaConstraintFromStore]; +} + +- (nullable NSString *)currentVideoResolutionHeightFromStore { + NSString *mediaConstraintFromStore = [self currentVideoResoultionConstraintFromStore]; + return [self videoResolutionComponentAtIndex:1 inConstraintsString:mediaConstraintFromStore]; +} + +#pragma mark - + +- (NSString *)defaultVideoResolutionMediaConstraint { + return videoResolutionsStaticValues()[0]; +} + +- (nullable NSString *)videoResolutionComponentAtIndex:(int)index + inConstraintsString:(NSString *)constraint { + if (index != 0 && index != 1) { + return nil; + } + NSArray *components = [constraint componentsSeparatedByString:@"x"]; + if (components.count != 2) { + return nil; + } + return components[index]; +} + +#pragma mark - Conversion to RTCMediaConstraints + +- (nullable NSDictionary *)currentMediaConstraintFromStoreAsRTCDictionary { + NSDictionary *mediaConstraintsDictionary = nil; + + NSString *widthConstraint = [self currentVideoResolutionWidthFromStore]; + NSString *heightConstraint = [self currentVideoResolutionHeightFromStore]; + if (widthConstraint && heightConstraint) { + mediaConstraintsDictionary = @{ + kRTCMediaConstraintsMinWidth : widthConstraint, + kRTCMediaConstraintsMinHeight : heightConstraint + }; + } + return mediaConstraintsDictionary; +} + +@end +NS_ASSUME_NONNULL_END diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.h new file mode 100644 index 0000000000..83e4a073ca --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * Light-weight persistent store for media constraints user settings. + * + * It will persist between application launches and application updates. + */ +@interface ARDMediaConstraintsSettingsStore : NSObject + +/** + * Returns current video resolution media constraint string stored in the store. + */ +- (nullable NSString *)videoResolutionConstraintsSetting; + +/** + * Stores the provided value as video resolution media constraint. + * @param value the string to be stored + */ +- (void)setVideoResolutionConstraintsSetting:(NSString *)value; + +@end +NS_ASSUME_NONNULL_END diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.m new file mode 100644 index 0000000000..c8fb206575 --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMediaConstraintsSettingsStore.m @@ -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 "ARDMediaConstraintsSettingsStore.h" + +static NSString *const kUserDefaultsMediaConstraintsKey = + @"rtc_video_resolution_media_constraints_key"; + +NS_ASSUME_NONNULL_BEGIN +@implementation ARDMediaConstraintsSettingsStore + +- (nullable NSString *)videoResolutionConstraintsSetting { + return [[NSUserDefaults standardUserDefaults] objectForKey:kUserDefaultsMediaConstraintsKey]; +} + +- (void)setVideoResolutionConstraintsSetting:(NSString *)constraintsString { + [[NSUserDefaults standardUserDefaults] setObject:constraintsString + forKey:kUserDefaultsMediaConstraintsKey]; +} + +@end +NS_ASSUME_NONNULL_END diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.h new file mode 100644 index 0000000000..91b9fc94a5 --- /dev/null +++ b/webrtc/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 + +@class ARDMediaConstraintsModel; + +NS_ASSUME_NONNULL_BEGIN +/** + * Displays settings options. + */ +@interface ARDSettingsViewController : UITableViewController + +/** + * Creates new instance. + * + * @param style the table view style that should be used + * @param mediaConstraintsModel model class for the media constraints settings. + */ +- (instancetype)initWithStyle:(UITableViewStyle)style + mediaConstraintsModel:(ARDMediaConstraintsModel *)mediaConstraintsModel; + +#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/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m new file mode 100644 index 0000000000..1df9892673 --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m @@ -0,0 +1,162 @@ +/* + * 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 "ARDMediaConstraintsModel.h" + +NS_ASSUME_NONNULL_BEGIN +@interface ARDSettingsViewController () { + ARDMediaConstraintsModel *_mediaConstraintsModel; +} + +@end + +@implementation ARDSettingsViewController + +- (instancetype)initWithStyle:(UITableViewStyle)style + mediaConstraintsModel:(ARDMediaConstraintsModel *)mediaConstraintsModel { + self = [super initWithStyle:style]; + if (self) { + _mediaConstraintsModel = mediaConstraintsModel; + } + return self; +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"Settings"; + [self addDoneBarButton]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + [self selectCurrentlyStoredOrDefaultMediaConstraints]; +} + +#pragma mark - Data source + +- (NSArray *)mediaConstraintsArray { + return _mediaConstraintsModel.availableVideoResoultionsMediaConstraints; +} + +#pragma mark - + +- (void)addDoneBarButton { + UIBarButtonItem *barItem = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone + target:self + action:@selector(dismissModally:)]; + self.navigationItem.leftBarButtonItem = barItem; +} + +- (void)selectCurrentlyStoredOrDefaultMediaConstraints { + NSString *currentSelection = [_mediaConstraintsModel currentVideoResoultionConstraintFromStore]; + + NSUInteger indexOfSelection = [[self mediaConstraintsArray] indexOfObject:currentSelection]; + NSIndexPath *pathToBeSelected = [NSIndexPath indexPathForRow:indexOfSelection inSection:0]; + [self.tableView selectRowAtIndexPath:pathToBeSelected + animated:NO + scrollPosition:UITableViewScrollPositionNone]; + // Manully invoke the delegate method because the previous invocation will not. + [self tableView:self.tableView didSelectRowAtIndexPath:pathToBeSelected]; +} + +#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 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.mediaConstraintsArray.count; +} + +#pragma mark - Table view delegate + +- (BOOL)sectionIsMediaConstraints:(int)section { + return section == 0; +} + +- (BOOL)indexPathIsMediaConstraints:(NSIndexPath *)indexPath { + return [self sectionIsMediaConstraints:indexPath.section]; +} + +- (nullable NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + if ([self sectionIsMediaConstraints:section]) { + return @"Media constraints"; + } + return @""; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if ([self indexPathIsMediaConstraints:indexPath]) { + return [self mediaConstraintsTableViewCellForTableView:tableView atIndexPath:indexPath]; + } + return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:@"identifier"]; +} + +- (nullable NSIndexPath *)tableView:(UITableView *)tableView + willSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { + if ([self indexPathIsMediaConstraints:indexPath]) { + return [self tableView:tableView willDeselectMediaConstraintsRowAtIndexPath:indexPath]; + } + return indexPath; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + if ([self indexPathIsMediaConstraints:indexPath]) { + [self tableView:tableView didSelectMediaConstraintsCellAtIndexPath:indexPath]; + } +} + +#pragma mark - Table view delegate(Media Constraints) + +- (UITableViewCell *)mediaConstraintsTableViewCellForTableView:(UITableView *)tableView + atIndexPath:(NSIndexPath *)indexPath { + NSString *dequeueIdentifier = @"ARDSettingsMediaConstraintsViewCellIdentifier"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:dequeueIdentifier]; + } + cell.textLabel.text = self.mediaConstraintsArray[indexPath.row]; + return cell; +} + +- (void)tableView:(UITableView *)tableView + didSelectMediaConstraintsCellAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + cell.accessoryType = UITableViewCellAccessoryCheckmark; + + NSString *mediaConstraintsString = self.mediaConstraintsArray[indexPath.row]; + [_mediaConstraintsModel storeVideoResoultionConstraint:mediaConstraintsString]; +} + +- (NSIndexPath *)tableView:(UITableView *)tableView + willDeselectMediaConstraintsRowAtIndexPath:(NSIndexPath *)indexPath { + NSIndexPath *oldSelection = [tableView indexPathForSelectedRow]; + UITableViewCell *cell = [tableView cellForRowAtIndexPath:oldSelection]; + cell.accessoryType = UITableViewCellAccessoryNone; + return indexPath; +} + +@end +NS_ASSUME_NONNULL_END diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m index aa9ea2113d..d4008894d6 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m @@ -12,12 +12,13 @@ #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" +#import "ARDAppClient.h" +#import "ARDMediaConstraintsModel.h" +#import "ARDVideoCallView.h" #import "WebRTC/RTCAVFoundationVideoSource.h" #import "WebRTC/RTCDispatcher.h" #import "WebRTC/RTCLogging.h" - -#import "ARDAppClient.h" -#import "ARDVideoCallView.h" +#import "WebRTC/RTCMediaConstraints.h" @interface ARDVideoCallViewController () @@ -45,6 +46,12 @@ if (self = [super init]) { _delegate = delegate; _client = [[ARDAppClient alloc] initWithDelegate:self]; + ARDMediaConstraintsModel *mediaConstraintsModel = [[ARDMediaConstraintsModel alloc] init]; + RTCMediaConstraints *cameraConstraints = [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:nil + optionalConstraints:[mediaConstraintsModel + currentMediaConstraintFromStoreAsRTCDictionary]]; + [_client setCameraConstraints:cameraConstraints]; [_client connectToRoomWithId:room isLoopback:isLoopback isAudioOnly:isAudioOnly diff --git a/webrtc/examples/objc/AppRTCMobile/tests/ARDMediaConstraintsModelTests.mm b/webrtc/examples/objc/AppRTCMobile/tests/ARDMediaConstraintsModelTests.mm new file mode 100644 index 0000000000..e4a25b94d8 --- /dev/null +++ b/webrtc/examples/objc/AppRTCMobile/tests/ARDMediaConstraintsModelTests.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 +#import +#import "ARDMediaConstraintsModel+Private.h" +#import "ARDMediaConstraintsSettingsStore.h" +#import "WebRTC/RTCMediaConstraints.h" +#include "webrtc/base/gunit.h" + + +@interface ARDMediaConstraintsModelTests : NSObject { + ARDMediaConstraintsModel *_model; +} + +- (void)testStoringInavlidConstraintReturnsNo; +- (void)testDefaultMediaFromStore; +- (void)testWidthConstraintFromStore; +- (void)testHeightConstraintFromStore; + +@end + +@implementation ARDMediaConstraintsModelTests + +- (instancetype)init { + self = [super init]; + if (self) { + _model = [[ARDMediaConstraintsModel alloc] init]; + } + return self; +} + +- (id)setupMockStoreWithMediaConstraintString:(NSString *)constraintString { + id storeMock = [OCMockObject mockForClass:[ARDMediaConstraintsSettingsStore class]]; + [([[storeMock stub] andReturn:constraintString]) videoResolutionConstraintsSetting]; + + id partialMock = [OCMockObject partialMockForObject:_model]; + [[[partialMock stub] andReturn:storeMock] settingsStore]; + + return storeMock; +} + +- (void)testDefaultMediaFromStore { + // given + id storeMock = [self setupMockStoreWithMediaConstraintString:nil]; + + [[storeMock expect] setVideoResolutionConstraintsSetting:@"640x480"]; + + // when + NSString *string = [_model currentVideoResoultionConstraintFromStore]; + + // then + EXPECT_TRUE([string isEqualToString:@"640x480"]); + [storeMock verify]; +} + +- (void)testStoringInavlidConstraintReturnsNo { + // given + id storeMock = [self setupMockStoreWithMediaConstraintString:@"960x480"]; + + // when + BOOL result = [_model storeVideoResoultionConstraint:@"960x480"]; + + // then + EXPECT_TRUE(result); +} + +- (void)testWidthConstraintFromStore { + // given + [self setupMockStoreWithMediaConstraintString:@"1270x480"]; + + // when + NSString *width = [_model currentVideoResolutionWidthFromStore]; + + // then + EXPECT_TRUE([width isEqualToString:@"1270"]); +} + +- (void)testHeightConstraintFromStore { + // given + [self setupMockStoreWithMediaConstraintString:@"960x540"]; + // when + NSString *height = [_model currentVideoResolutionHeightFromStore]; + + // then + EXPECT_TRUE([height isEqualToString:@"540"]); +} + +- (void)testConstraintComponentIsNilWhenInvalidConstraintString { + // given + [self setupMockStoreWithMediaConstraintString:@"invalid"]; + + // when + NSString *width = [_model currentVideoResolutionWidthFromStore]; + + // then + EXPECT_TRUE(width == nil); +} + +- (void)testConstraintsDictionaryIsNilWhenInvalidConstraintString { + // given + [self setupMockStoreWithMediaConstraintString:@"invalid"]; + + // when + NSDictionary *constraintsDictionary = [_model currentMediaConstraintFromStoreAsRTCDictionary]; + + // then + EXPECT_TRUE(constraintsDictionary == nil); +} +@end + +class ARDMediaConstraintsModelTest : public ::testing::Test { + protected: + ARDMediaConstraintsModelTests *test; + ARDMediaConstraintsModelTest() { test = [[ARDMediaConstraintsModelTests alloc] init]; } +}; + +TEST_F(ARDMediaConstraintsModelTest, DefaultMediaFromStore) { + @autoreleasepool { + [test testDefaultMediaFromStore]; + } +} + +TEST_F(ARDMediaConstraintsModelTest, StoringInvalidConstraintsReturnsNo) { + @autoreleasepool { + [test testStoringInavlidConstraintReturnsNo]; + } +} + +TEST_F(ARDMediaConstraintsModelTest, WidthConstraintFromStore) { + @autoreleasepool { + [test testWidthConstraintFromStore]; + } +} + +TEST_F(ARDMediaConstraintsModelTest, HeightConstraintFromStore) { + @autoreleasepool { + [test testHeightConstraintFromStore]; + } +} + +TEST_F(ARDMediaConstraintsModelTest, ConstratintIsNil) { + @autoreleasepool { + [test testConstraintComponentIsNilWhenInvalidConstraintString]; + } +} + +TEST_F(ARDMediaConstraintsModelTest, DictionaryIsNil) { + @autoreleasepool { + [test testConstraintsDictionaryIsNilWhenInvalidConstraintString]; + } +}