diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h index 60ef70071c..0feb01586a 100644 --- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h +++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h @@ -60,7 +60,8 @@ typedef NS_ENUM(NSInteger, ARDAppClientState) { @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; +- (instancetype)initWithDelegate:(id)delegate + preferVideoCodec:(NSString*)codec; // Sets camera constraints. - (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints; diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m index ef21ab617b..497a137b6c 100644 --- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m +++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m @@ -100,6 +100,7 @@ static int const kKbpsMultiplier = 1000; ARDTimerProxy *_statsTimer; RTCMediaConstraints *_cameraConstraints; NSNumber *_maxBitrate; + NSString *_videoCodec; } @synthesize shouldGetStats = _shouldGetStats; @@ -128,13 +129,15 @@ static int const kKbpsMultiplier = 1000; @synthesize shouldUseLevelControl = _shouldUseLevelControl; - (instancetype)init { - return [self initWithDelegate:nil]; + return [self initWithDelegate:nil preferVideoCodec:@"H264"]; } -- (instancetype)initWithDelegate:(id)delegate { +- (instancetype)initWithDelegate:(id)delegate + preferVideoCodec:(NSString *)codec { if (self = [super init]) { _roomServerClient = [[ARDAppEngineClient alloc] init]; _delegate = delegate; + _videoCodec = codec; NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl]; _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL]; [self configure]; @@ -452,12 +455,12 @@ static int const kKbpsMultiplier = 1000; [_delegate appClient:self didError:sdpError]; return; } - // Prefer H264 if available. - RTCSessionDescription *sdpPreferringH264 = + // Prefer codec from settings if available. + RTCSessionDescription *sdpPreferringCodec = [ARDSDPUtils descriptionForDescription:sdp - preferredVideoCodec:@"H264"]; + preferredVideoCodec:_videoCodec]; __weak ARDAppClient *weakSelf = self; - [_peerConnection setLocalDescription:sdpPreferringH264 + [_peerConnection setLocalDescription:sdpPreferringCodec completionHandler:^(NSError *error) { ARDAppClient *strongSelf = weakSelf; [strongSelf peerConnection:strongSelf.peerConnection @@ -465,7 +468,7 @@ static int const kKbpsMultiplier = 1000; }]; ARDSessionDescriptionMessage *message = [[ARDSessionDescriptionMessage alloc] - initWithDescription:sdpPreferringH264]; + initWithDescription:sdpPreferringCodec]; [self sendSignalingMessage:message]; [self setMaxBitrateForPeerConnectionVideoSender]; }); @@ -606,12 +609,12 @@ static int const kKbpsMultiplier = 1000; ARDSessionDescriptionMessage *sdpMessage = (ARDSessionDescriptionMessage *)message; RTCSessionDescription *description = sdpMessage.sessionDescription; - // Prefer H264 if available. - RTCSessionDescription *sdpPreferringH264 = + // Prefer codec from settings if available. + RTCSessionDescription *sdpPreferringCodec = [ARDSDPUtils descriptionForDescription:description - preferredVideoCodec:@"H264"]; + preferredVideoCodec:_videoCodec]; __weak ARDAppClient *weakSelf = self; - [_peerConnection setRemoteDescription:sdpPreferringH264 + [_peerConnection setRemoteDescription:sdpPreferringCodec completionHandler:^(NSError *error) { ARDAppClient *strongSelf = weakSelf; [strongSelf peerConnection:strongSelf.peerConnection diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.h index 602a464c8d..0eb7b31aeb 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.h +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.h @@ -48,6 +48,26 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)storeVideoResoultionConstraint:(NSString *)constraint; +/** + * Returns array of available video codecs. + */ +- (NSArray *)availableVideoCodecs; + +/** + * Returns current video codec setting from store if present. + */ +- (NSString *)currentVideoCodecSettingFromStore; + +/** + * Stores the provided video codec setting into the store. + * + * If the provided constraint is not part of the available video codecs + * the store operation will not be executed and NO will be returned. + * @param video codec settings the string to be stored. + * @return YES/NO depending on success. + */ +- (BOOL)storeVideoCodecSetting:(NSString *)videoCodec; + /** * Converts the current media constraints from store into dictionary with RTCMediaConstraints * values. diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.m index eda0275819..7514689add 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.m +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsModel.m @@ -17,6 +17,10 @@ static NSArray *videoResolutionsStaticValues() { return @[ @"640x480", @"960x540", @"1280x720" ]; } +static NSArray *videoCodecsStaticValues() { + return @[ @"H264", @"VP8", @"VP9" ]; +} + @interface ARDSettingsModel () { ARDSettingsStore *_settingsStore; } @@ -46,6 +50,27 @@ static NSArray *videoResolutionsStaticValues() { return YES; } +- (NSArray *)availableVideoCodecs { + return videoCodecsStaticValues(); +} + +- (NSString *)currentVideoCodecSettingFromStore { + NSString *videoCodec = [[self settingsStore] videoCodec]; + if (!videoCodec) { + videoCodec = [self defaultVideoCodecSetting]; + [[self settingsStore] setVideoCodec:videoCodec]; + } + return videoCodec; +} + +- (BOOL)storeVideoCodecSetting:(NSString *)videoCodec { + if (![[self availableVideoCodecs] containsObject:videoCodec]) { + return NO; + } + [[self settingsStore] setVideoCodec:videoCodec]; + return YES; +} + - (nullable NSNumber *)currentMaxBitrateSettingFromStore { return [[self settingsStore] maxBitrate]; } @@ -92,6 +117,10 @@ static NSArray *videoResolutionsStaticValues() { return components[index]; } +- (NSString *)defaultVideoCodecSetting { + return videoCodecsStaticValues()[0]; +} + #pragma mark - Conversion to RTCMediaConstraints - (nullable NSDictionary *)currentMediaConstraintFromStoreAsRTCDictionary { diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.h index 69e10e3bd5..b82ec70e65 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.h +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.h @@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN */ @interface ARDSettingsStore : NSObject +@property(nonatomic) NSString* videoCodec; + /** * Returns current video resolution media constraint string stored in the store. */ diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.m index fd396b5381..c05df4416a 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.m +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsStore.m @@ -11,6 +11,7 @@ #import "ARDSettingsStore.h" static NSString *const kMediaConstraintsKey = @"rtc_video_resolution_media_constraints_key"; +static NSString *const kVideoCodecKey = @"rtc_video_codec_key"; static NSString *const kBitrateKey = @"rtc_max_bitrate_key"; NS_ASSUME_NONNULL_BEGIN @@ -38,6 +39,15 @@ NS_ASSUME_NONNULL_BEGIN [self.storage synchronize]; } +- (NSString *)videoCodec { + return [self.storage objectForKey:kVideoCodecKey]; +} + +- (void)setVideoCodec:(NSString *)videoCodec { + [self.storage setObject:videoCodec forKey:kVideoCodecKey]; + [self.storage synchronize]; +} + - (nullable NSNumber *)maxBitrate { return [self.storage objectForKey:kBitrateKey]; } diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m index 815b14799f..fb59dd2769 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m @@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN typedef NS_ENUM(int, ARDSettingsSections) { ARDSettingsSectionMediaConstraints = 0, - ARDSettingsSectionBitRate + ARDSettingsSectionVideoCodec, + ARDSettingsSectionBitRate, }; @interface ARDSettingsViewController () { @@ -43,9 +44,18 @@ typedef NS_ENUM(int, ARDSettingsSections) { [self addDoneBarButton]; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self addCheckmarkInSection:ARDSettingsSectionMediaConstraints + withArray:[self mediaConstraintsArray] + selecting:[_settingsModel currentVideoResoultionConstraintFromStore]]; + [self addCheckmarkInSection:ARDSettingsSectionVideoCodec + withArray:[self videoCodecArray] + selecting:[_settingsModel currentVideoCodecSettingFromStore]]; +} + - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - [self selectCurrentlyStoredOrDefaultMediaConstraints]; } #pragma mark - Data source @@ -54,6 +64,10 @@ typedef NS_ENUM(int, ARDSettingsSections) { return _settingsModel.availableVideoResoultionsMediaConstraints; } +- (NSArray *)videoCodecArray { + return _settingsModel.availableVideoCodecs; +} + #pragma mark - - (void)addDoneBarButton { @@ -64,16 +78,14 @@ typedef NS_ENUM(int, ARDSettingsSections) { self.navigationItem.leftBarButtonItem = barItem; } -- (void)selectCurrentlyStoredOrDefaultMediaConstraints { - NSString *currentSelection = [_settingsModel 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]; +- (void)addCheckmarkInSection:(int)section + withArray:(NSArray*) array + selecting:(NSString*)selection { + NSUInteger indexOfSelection = [array indexOfObject:selection]; + NSIndexPath *pathToBeDecorated = [NSIndexPath indexPathForRow:indexOfSelection + inSection:section]; + UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:pathToBeDecorated]; + cell.accessoryType = UITableViewCellAccessoryCheckmark; } #pragma mark - Dismissal of view controller @@ -85,75 +97,84 @@ typedef NS_ENUM(int, ARDSettingsSections) { #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 2; + return 3; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if ([self sectionIsMediaConstraints:section]) { - return self.mediaConstraintsArray.count; + switch (section) { + case ARDSettingsSectionMediaConstraints: + return self.mediaConstraintsArray.count; + case ARDSettingsSectionVideoCodec: + return self.videoCodecArray.count; + default: + return 1; } - - return 1; } -#pragma mark - Index path helpers +#pragma mark - Table view delegate helpers -- (BOOL)sectionIsMediaConstraints:(int)section { - return section == ARDSettingsSectionMediaConstraints; +- (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; + } } -- (BOOL)sectionIsBitrate:(int)section { - return section == ARDSettingsSectionBitRate; -} - -- (BOOL)indexPathIsMediaConstraints:(NSIndexPath *)indexPath { - return [self sectionIsMediaConstraints:indexPath.section]; -} - -- (BOOL)indexPathIsBitrate:(NSIndexPath *)indexPath { - return [self sectionIsBitrate:indexPath.section]; +- (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 { - if ([self sectionIsMediaConstraints:section]) { - return @"Media constraints"; + switch (section) { + case ARDSettingsSectionMediaConstraints: + return @"Media constraints"; + case ARDSettingsSectionVideoCodec: + return @"Video codec"; + case ARDSettingsSectionBitRate: + return @"Maximum bitrate"; + default: + return @""; } - - if ([self sectionIsBitrate:section]) { - return @"Maximum bitrate"; - } - - return @""; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - if ([self indexPathIsMediaConstraints:indexPath]) { - return [self mediaConstraintsTableViewCellForTableView:tableView atIndexPath:indexPath]; - } + switch (indexPath.section) { + case ARDSettingsSectionMediaConstraints: + return [self mediaConstraintsTableViewCellForTableView:tableView atIndexPath:indexPath]; - if ([self indexPathIsBitrate:indexPath]) { - return [self bitrateTableViewCellForTableView:tableView atIndexPath:indexPath]; - } + case ARDSettingsSectionVideoCodec: + return [self videoCodecTableViewCellForTableView:tableView atIndexPath:indexPath]; - return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - reuseIdentifier:@"identifier"]; -} + case ARDSettingsSectionBitRate: + return [self bitrateTableViewCellForTableView:tableView atIndexPath:indexPath]; -- (nullable NSIndexPath *)tableView:(UITableView *)tableView - willSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { - if ([self indexPathIsMediaConstraints:indexPath]) { - return [self tableView:tableView willDeselectMediaConstraintsRowAtIndexPath:indexPath]; + default: + return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:@"identifier"]; } - return indexPath; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - if ([self indexPathIsMediaConstraints:indexPath]) { - [self tableView:tableView didSelectMediaConstraintsCellAtIndexPath:indexPath]; + switch (indexPath.section) { + case ARDSettingsSectionMediaConstraints: + [self tableView:tableView didSelectMediaConstraintsCellAtIndexPath:indexPath]; + break; + + case ARDSettingsSectionVideoCodec: + [self tableView:tableView didSelectVideoCodecCellAtIndexPath:indexPath]; + break; } } @@ -173,19 +194,37 @@ typedef NS_ENUM(int, ARDSettingsSections) { - (void)tableView:(UITableView *)tableView didSelectMediaConstraintsCellAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - cell.accessoryType = UITableViewCellAccessoryCheckmark; + [self tableView:tableView + updateListSelectionAtIndexPath:indexPath + inSection:ARDSettingsSectionMediaConstraints]; NSString *mediaConstraintsString = self.mediaConstraintsArray[indexPath.row]; [_settingsModel storeVideoResoultionConstraint:mediaConstraintsString]; } -- (NSIndexPath *)tableView:(UITableView *)tableView - willDeselectMediaConstraintsRowAtIndexPath:(NSIndexPath *)indexPath { - NSIndexPath *oldSelection = [tableView indexPathForSelectedRow]; - UITableViewCell *cell = [tableView cellForRowAtIndexPath:oldSelection]; - cell.accessoryType = UITableViewCellAccessoryNone; - return indexPath; +#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]; + } + cell.textLabel.text = self.videoCodecArray[indexPath.row]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView + didSelectVideoCodecCellAtIndexPath:(NSIndexPath *)indexPath { + [self tableView:tableView + updateListSelectionAtIndexPath:indexPath + inSection:ARDSettingsSectionVideoCodec]; + + NSString *videoCodec = self.videoCodecArray[indexPath.row]; + [_settingsModel storeVideoCodecSetting:videoCodec]; } #pragma mark - Table view delegate(Bitrate) diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m index 9e17951f07..f58ee6613c 100644 --- a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m +++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m @@ -44,9 +44,11 @@ shouldUseLevelControl:(BOOL)shouldUseLevelControl delegate:(id)delegate { if (self = [super init]) { - _delegate = delegate; - _client = [[ARDAppClient alloc] initWithDelegate:self]; ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; + NSString* videoCodec = [settingsModel currentVideoCodecSettingFromStore]; + _delegate = delegate; + _client = [[ARDAppClient alloc] initWithDelegate:self + preferVideoCodec:videoCodec]; RTCMediaConstraints *cameraConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:[settingsModel diff --git a/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m b/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m index 176ea19da8..bcf26b258e 100644 --- a/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m +++ b/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m @@ -369,7 +369,8 @@ static NSUInteger const kBottomViewHeight = 200; } [_client disconnect]; - ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self]; + ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self + preferVideoCodec:@"H264"]; [client connectToRoomWithId:roomId isLoopback:isLoopback isAudioOnly:NO