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:
parent
aa3d1c8169
commit
c31d4d0324
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -35,7 +35,8 @@
|
||||
@protocol APPRTCSendMessage<NSObject>
|
||||
|
||||
- (void)sendData:(NSData *)data;
|
||||
|
||||
// Logging helper.
|
||||
- (void)displayLogMessage:(NSString *)message;
|
||||
@end
|
||||
|
||||
@class APPRTCViewController;
|
||||
|
@ -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."];
|
||||
|
@ -36,6 +36,7 @@
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.textField.delegate = self;
|
||||
[self.textField becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)displayText:(NSString *)text {
|
||||
|
Loading…
x
Reference in New Issue
Block a user