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
This commit is contained in:
fischman@webrtc.org 2013-09-05 21:49:58 +00:00
parent aa3d1c8169
commit c31d4d0324
6 changed files with 116 additions and 24 deletions

View File

@ -33,22 +33,25 @@
@implementation RTCICEServer
- (id)initWithURI:(NSURL *)URI password:(NSString *)password {
if (!URI || !password) {
- (id)initWithURI:(NSURL *)URI
username:(NSString *)username
password:(NSString *)password {
if (!URI || !username || !password) {
NSAssert(NO, @"nil arguments not allowed");
self = nil;
return nil;
}
if ((self = [super init])) {
_URI = URI;
_username = [username copy];
_password = [password copy];
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"Server: [%@]\nPassword: [%@]",
[self.URI absoluteString], self.password];
return [NSString stringWithFormat:@"RTCICEServer: [%@:%@:%@]",
[self.URI absoluteString], self.username, self.password];
}
@end
@ -58,6 +61,7 @@
- (webrtc::PeerConnectionInterface::IceServer)iceServer {
webrtc::PeerConnectionInterface::IceServer iceServer;
iceServer.uri = [[self.URI absoluteString] UTF8String];
iceServer.username = [self.username UTF8String];
iceServer.password = [self.password UTF8String];
return iceServer;
}

View File

@ -30,14 +30,15 @@
// RTCICEServer allows for the creation of ICEServer structs.
@interface RTCICEServer : NSObject
// The server URI.
// The server URI, username, and password.
@property(nonatomic, strong, readonly) NSURL* URI;
// The server password.
@property(nonatomic, copy, readonly) NSString* username;
@property(nonatomic, copy, readonly) NSString* password;
// Initializer for RTCICEServer taking uri and password.
- (id)initWithURI:(NSString*)URI password:(NSString*)password;
- (id)initWithURI:(NSString*)URI
username:(NSString*)username
password:(NSString*)password;
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// Disallow init and don't add to documentation

View File

@ -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];

View File

@ -35,7 +35,8 @@
@protocol APPRTCSendMessage<NSObject>
- (void)sendData:(NSData *)data;
// Logging helper.
- (void)displayLogMessage:(NSString *)message;
@end
@class APPRTCViewController;

View File

@ -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."];

View File

@ -36,6 +36,7 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.textField.delegate = self;
[self.textField becomeFirstResponder];
}
- (void)displayText:(NSString *)text {