AppRTCDemo(iOS): prefer ISAC as audio codec
This makes audio flow well bidirectionally to an iPod Touch (5th gen).
Also:
- Update to new turnserver JSON style:
- separate username field
- multiple URLs for the same server (e.g. both UDP & TCP)
- Added more explicit logging for ICE Connected since it's useful for debugging
- Give focus to the input field on app launch since that's the only useful
thing to have focus on, anyway.
- Fix minor typos
- Cleaned up trailing whitespace and hard tabs
BUG=2191
R=wu@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/2127004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4687 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m b/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m
index 93f693f..99f5166 100644
--- a/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m
+++ b/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m
@@ -194,14 +194,17 @@
error:&error];
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
NSString *username = json[@"username"];
- NSString *turnServer = json[@"turn"];
NSString *password = json[@"password"];
- NSString *fullUrl =
- [NSString stringWithFormat:@"turn:%@@%@", username, turnServer];
- RTCICEServer *ICEServer =
- [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:fullUrl]
+ NSArray* uris = json[@"uris"];
+ for (int i = 0; i < [uris count]; ++i) {
+ NSString *turnServer = [uris objectAtIndex:i];
+ RTCICEServer *ICEServer =
+ [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:turnServer]
+ username:username
password:password];
- [ICEServers addObject:ICEServer];
+ NSLog(@"Added ICE Server: %@", ICEServer);
+ [ICEServers addObject:ICEServer];
+ }
} else {
NSLog(@"Unable to get TURN server. Error: %@", error.description);
}
@@ -241,9 +244,10 @@
[NSRegularExpression regularExpressionWithPattern:@"room is full"
options:0
error:nil];
- if ([fullRegex numberOfMatchesInString:self.roomHtml
- options:0
- range:NSMakeRange(0, [self.roomHtml length])]) {
+ if ([fullRegex
+ numberOfMatchesInString:self.roomHtml
+ options:0
+ range:NSMakeRange(0, [self.roomHtml length])]) {
[self showMessage:@"Room full"];
return;
}
@@ -252,7 +256,8 @@
NSString *fullUrl = [[[connection originalRequest] URL] absoluteString];
NSRange queryRange = [fullUrl rangeOfString:@"?"];
self.baseURL = [fullUrl substringToIndex:queryRange.location];
- [self maybeLogMessage:[NSString stringWithFormat:@"Base URL: %@", self.baseURL]];
+ [self maybeLogMessage:
+ [NSString stringWithFormat:@"Base URL: %@", self.baseURL]];
self.token = [self findVar:@"channelToken" strippingQuotes:YES];
if (!self.token)
@@ -286,11 +291,15 @@
NSDictionary *json =
[NSJSONSerialization JSONObjectWithData:pcData options:0 error:&error];
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
- NSArray *servers = [json objectForKey:@"ICEServers"];
+ NSArray *servers = [json objectForKey:@"iceServers"];
NSMutableArray *ICEServers = [NSMutableArray array];
for (NSDictionary *server in servers) {
NSString *url = [server objectForKey:@"url"];
+ NSString *username = json[@"username"];
NSString *credential = [server objectForKey:@"credential"];
+ if (!username) {
+ username = @"";
+ }
if (!credential) {
credential = @"";
}
@@ -300,7 +309,9 @@
credential]];
RTCICEServer *ICEServer =
[[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url]
+ username:username
password:credential];
+ NSLog(@"Added ICE Server: %@", ICEServer);
[ICEServers addObject:ICEServer];
}
[self updateICEServers:ICEServers withTurnServer:turnServerUrl];
diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h
index ad1c512..22754e3 100644
--- a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h
+++ b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h
@@ -35,7 +35,8 @@
@protocol APPRTCSendMessage<NSObject>
- (void)sendData:(NSData *)data;
-
+// Logging helper.
+- (void)displayLogMessage:(NSString *)message;
@end
@class APPRTCViewController;
diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m
index 710f4ad..34aa752 100644
--- a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m
+++ b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m
@@ -62,7 +62,7 @@
- (void)peerConnection:(RTCPeerConnection *)peerConnection
signalingStateChanged:(RTCSignalingState)stateChanged {
- NSLog(@"PCO onSignalingStateChange.");
+ NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
}
- (void)peerConnection:(RTCPeerConnection *)peerConnection
@@ -119,6 +119,13 @@
- (void)peerConnection:(RTCPeerConnection *)peerConnection
iceConnectionChanged:(RTCICEConnectionState)newState {
NSLog(@"PCO onIceConnectionChange. %d", newState);
+ if (newState == RTCICEConnectionConnected)
+ [self displayLogMessage:@"ICE Connection Connected."];
+ NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!");
+}
+
+- (void)displayLogMessage:(NSString *)message {
+ [_delegate displayLogMessage:message];
}
@end
@@ -258,8 +265,8 @@
} else if (([value compare:@"offer"] == NSOrderedSame) ||
([value compare:@"answer"] == NSOrderedSame)) {
NSString *sdpString = [objects objectForKey:@"sdp"];
- RTCSessionDescription *sdp =
- [[RTCSessionDescription alloc] initWithType:value sdp:sdpString];
+ RTCSessionDescription *sdp = [[RTCSessionDescription alloc]
+ initWithType:value sdp:[APPRTCAppDelegate preferISAC:sdpString]];
[self.peerConnection setRemoteDescriptionWithDelegate:self
sessionDescription:sdp];
[self displayLogMessage:@"PC - setRemoteDescription."];
@@ -283,8 +290,71 @@
#pragma mark - RTCSessionDescriptonDelegate methods
+// Match |pattern| to |string| and return the first group of the first
+// match, or nil if no match was found.
++ (NSString *)firstMatch:(NSRegularExpression *)pattern
+ withString:(NSString *)string {
+ NSTextCheckingResult* result =
+ [pattern firstMatchInString:string
+ options:0
+ range:NSMakeRange(0, [string length])];
+ if (!result)
+ return nil;
+ return [string substringWithRange:[result rangeAtIndex:1]];
+}
+
+// Mangle |origSDP| to prefer the ISAC/16k audio codec.
++ (NSString *)preferISAC:(NSString *)origSDP {
+ int mLineIndex = -1;
+ NSString* isac16kRtpMap = nil;
+ NSArray* lines = [origSDP componentsSeparatedByString:@"\n"];
+ NSRegularExpression* isac16kRegex = [NSRegularExpression
+ regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$"
+ options:0
+ error:nil];
+ for (int i = 0;
+ (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil);
+ ++i) {
+ NSString* line = [lines objectAtIndex:i];
+ if ([line hasPrefix:@"m=audio "]) {
+ mLineIndex = i;
+ continue;
+ }
+ isac16kRtpMap = [self firstMatch:isac16kRegex withString:line];
+ }
+ if (mLineIndex == -1) {
+ NSLog(@"No m=audio line, so can't prefer iSAC");
+ return origSDP;
+ }
+ if (isac16kRtpMap == nil) {
+ NSLog(@"No ISAC/16000 line, so can't prefer iSAC");
+ return origSDP;
+ }
+ NSArray* origMLineParts =
+ [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "];
+ NSMutableArray* newMLine =
+ [NSMutableArray arrayWithCapacity:[origMLineParts count]];
+ int origPartIndex = 0;
+ // Format is: m=<media> <port> <proto> <fmt> ...
+ [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
+ [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
+ [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
+ [newMLine addObject:isac16kRtpMap];
+ for (; origPartIndex < [origMLineParts count]; ++origPartIndex) {
+ if ([isac16kRtpMap compare:[origMLineParts objectAtIndex:origPartIndex]]
+ != NSOrderedSame) {
+ [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]];
+ }
+ }
+ NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]];
+ [newLines addObjectsFromArray:lines];
+ [newLines replaceObjectAtIndex:mLineIndex
+ withObject:[newMLine componentsJoinedByString:@" "]];
+ return [newLines componentsJoinedByString:@"\n"];
+}
+
- (void)peerConnection:(RTCPeerConnection *)peerConnection
- didCreateSessionDescription:(RTCSessionDescription *)sdp
+ didCreateSessionDescription:(RTCSessionDescription *)origSdp
error:(NSError *)error {
if (error) {
[self displayLogMessage:@"SDP onFailure."];
@@ -293,6 +363,10 @@
}
[self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
+ RTCSessionDescription* sdp =
+ [[RTCSessionDescription alloc]
+ initWithType:origSdp.type
+ sdp:[APPRTCAppDelegate preferISAC:origSdp.description]];
[self.peerConnection setLocalDescriptionWithDelegate:self
sessionDescription:sdp];
[self displayLogMessage:@"PC setLocalDescription."];
diff --git a/talk/examples/ios/AppRTCDemo/APPRTCViewController.m b/talk/examples/ios/AppRTCDemo/APPRTCViewController.m
index 928686b..ab84c09 100644
--- a/talk/examples/ios/AppRTCDemo/APPRTCViewController.m
+++ b/talk/examples/ios/AppRTCDemo/APPRTCViewController.m
@@ -36,6 +36,7 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.textField.delegate = self;
+ [self.textField becomeFirstResponder];
}
- (void)displayText:(NSString *)text {