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
|
@implementation RTCICEServer
|
||||||
|
|
||||||
- (id)initWithURI:(NSURL *)URI password:(NSString *)password {
|
- (id)initWithURI:(NSURL *)URI
|
||||||
if (!URI || !password) {
|
username:(NSString *)username
|
||||||
|
password:(NSString *)password {
|
||||||
|
if (!URI || !username || !password) {
|
||||||
NSAssert(NO, @"nil arguments not allowed");
|
NSAssert(NO, @"nil arguments not allowed");
|
||||||
self = nil;
|
self = nil;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_URI = URI;
|
_URI = URI;
|
||||||
|
_username = [username copy];
|
||||||
_password = [password copy];
|
_password = [password copy];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)description {
|
- (NSString *)description {
|
||||||
return [NSString stringWithFormat:@"Server: [%@]\nPassword: [%@]",
|
return [NSString stringWithFormat:@"RTCICEServer: [%@:%@:%@]",
|
||||||
[self.URI absoluteString], self.password];
|
[self.URI absoluteString], self.username, self.password];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@ -58,6 +61,7 @@
|
|||||||
- (webrtc::PeerConnectionInterface::IceServer)iceServer {
|
- (webrtc::PeerConnectionInterface::IceServer)iceServer {
|
||||||
webrtc::PeerConnectionInterface::IceServer iceServer;
|
webrtc::PeerConnectionInterface::IceServer iceServer;
|
||||||
iceServer.uri = [[self.URI absoluteString] UTF8String];
|
iceServer.uri = [[self.URI absoluteString] UTF8String];
|
||||||
|
iceServer.username = [self.username UTF8String];
|
||||||
iceServer.password = [self.password UTF8String];
|
iceServer.password = [self.password UTF8String];
|
||||||
return iceServer;
|
return iceServer;
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,15 @@
|
|||||||
// RTCICEServer allows for the creation of ICEServer structs.
|
// RTCICEServer allows for the creation of ICEServer structs.
|
||||||
@interface RTCICEServer : NSObject
|
@interface RTCICEServer : NSObject
|
||||||
|
|
||||||
// The server URI.
|
// The server URI, username, and password.
|
||||||
@property(nonatomic, strong, readonly) NSURL* URI;
|
@property(nonatomic, strong, readonly) NSURL* URI;
|
||||||
|
@property(nonatomic, copy, readonly) NSString* username;
|
||||||
// The server password.
|
|
||||||
@property(nonatomic, copy, readonly) NSString* password;
|
@property(nonatomic, copy, readonly) NSString* password;
|
||||||
|
|
||||||
// Initializer for RTCICEServer taking uri and 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
|
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||||
// Disallow init and don't add to documentation
|
// Disallow init and don't add to documentation
|
||||||
|
@ -194,14 +194,17 @@
|
|||||||
error:&error];
|
error:&error];
|
||||||
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
|
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
|
||||||
NSString *username = json[@"username"];
|
NSString *username = json[@"username"];
|
||||||
NSString *turnServer = json[@"turn"];
|
|
||||||
NSString *password = json[@"password"];
|
NSString *password = json[@"password"];
|
||||||
NSString *fullUrl =
|
NSArray* uris = json[@"uris"];
|
||||||
[NSString stringWithFormat:@"turn:%@@%@", username, turnServer];
|
for (int i = 0; i < [uris count]; ++i) {
|
||||||
RTCICEServer *ICEServer =
|
NSString *turnServer = [uris objectAtIndex:i];
|
||||||
[[RTCICEServer alloc] initWithURI:[NSURL URLWithString:fullUrl]
|
RTCICEServer *ICEServer =
|
||||||
|
[[RTCICEServer alloc] initWithURI:[NSURL URLWithString:turnServer]
|
||||||
|
username:username
|
||||||
password:password];
|
password:password];
|
||||||
[ICEServers addObject:ICEServer];
|
NSLog(@"Added ICE Server: %@", ICEServer);
|
||||||
|
[ICEServers addObject:ICEServer];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"Unable to get TURN server. Error: %@", error.description);
|
NSLog(@"Unable to get TURN server. Error: %@", error.description);
|
||||||
}
|
}
|
||||||
@ -241,9 +244,10 @@
|
|||||||
[NSRegularExpression regularExpressionWithPattern:@"room is full"
|
[NSRegularExpression regularExpressionWithPattern:@"room is full"
|
||||||
options:0
|
options:0
|
||||||
error:nil];
|
error:nil];
|
||||||
if ([fullRegex numberOfMatchesInString:self.roomHtml
|
if ([fullRegex
|
||||||
options:0
|
numberOfMatchesInString:self.roomHtml
|
||||||
range:NSMakeRange(0, [self.roomHtml length])]) {
|
options:0
|
||||||
|
range:NSMakeRange(0, [self.roomHtml length])]) {
|
||||||
[self showMessage:@"Room full"];
|
[self showMessage:@"Room full"];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -252,7 +256,8 @@
|
|||||||
NSString *fullUrl = [[[connection originalRequest] URL] absoluteString];
|
NSString *fullUrl = [[[connection originalRequest] URL] absoluteString];
|
||||||
NSRange queryRange = [fullUrl rangeOfString:@"?"];
|
NSRange queryRange = [fullUrl rangeOfString:@"?"];
|
||||||
self.baseURL = [fullUrl substringToIndex:queryRange.location];
|
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];
|
self.token = [self findVar:@"channelToken" strippingQuotes:YES];
|
||||||
if (!self.token)
|
if (!self.token)
|
||||||
@ -286,11 +291,15 @@
|
|||||||
NSDictionary *json =
|
NSDictionary *json =
|
||||||
[NSJSONSerialization JSONObjectWithData:pcData options:0 error:&error];
|
[NSJSONSerialization JSONObjectWithData:pcData options:0 error:&error];
|
||||||
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
|
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
|
||||||
NSArray *servers = [json objectForKey:@"ICEServers"];
|
NSArray *servers = [json objectForKey:@"iceServers"];
|
||||||
NSMutableArray *ICEServers = [NSMutableArray array];
|
NSMutableArray *ICEServers = [NSMutableArray array];
|
||||||
for (NSDictionary *server in servers) {
|
for (NSDictionary *server in servers) {
|
||||||
NSString *url = [server objectForKey:@"url"];
|
NSString *url = [server objectForKey:@"url"];
|
||||||
|
NSString *username = json[@"username"];
|
||||||
NSString *credential = [server objectForKey:@"credential"];
|
NSString *credential = [server objectForKey:@"credential"];
|
||||||
|
if (!username) {
|
||||||
|
username = @"";
|
||||||
|
}
|
||||||
if (!credential) {
|
if (!credential) {
|
||||||
credential = @"";
|
credential = @"";
|
||||||
}
|
}
|
||||||
@ -300,7 +309,9 @@
|
|||||||
credential]];
|
credential]];
|
||||||
RTCICEServer *ICEServer =
|
RTCICEServer *ICEServer =
|
||||||
[[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url]
|
[[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url]
|
||||||
|
username:username
|
||||||
password:credential];
|
password:credential];
|
||||||
|
NSLog(@"Added ICE Server: %@", ICEServer);
|
||||||
[ICEServers addObject:ICEServer];
|
[ICEServers addObject:ICEServer];
|
||||||
}
|
}
|
||||||
[self updateICEServers:ICEServers withTurnServer:turnServerUrl];
|
[self updateICEServers:ICEServers withTurnServer:turnServerUrl];
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
@protocol APPRTCSendMessage<NSObject>
|
@protocol APPRTCSendMessage<NSObject>
|
||||||
|
|
||||||
- (void)sendData:(NSData *)data;
|
- (void)sendData:(NSData *)data;
|
||||||
|
// Logging helper.
|
||||||
|
- (void)displayLogMessage:(NSString *)message;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@class APPRTCViewController;
|
@class APPRTCViewController;
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||||
signalingStateChanged:(RTCSignalingState)stateChanged {
|
signalingStateChanged:(RTCSignalingState)stateChanged {
|
||||||
NSLog(@"PCO onSignalingStateChange.");
|
NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||||
@ -119,6 +119,13 @@
|
|||||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||||
iceConnectionChanged:(RTCICEConnectionState)newState {
|
iceConnectionChanged:(RTCICEConnectionState)newState {
|
||||||
NSLog(@"PCO onIceConnectionChange. %d", 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
|
@end
|
||||||
@ -258,8 +265,8 @@
|
|||||||
} else if (([value compare:@"offer"] == NSOrderedSame) ||
|
} else if (([value compare:@"offer"] == NSOrderedSame) ||
|
||||||
([value compare:@"answer"] == NSOrderedSame)) {
|
([value compare:@"answer"] == NSOrderedSame)) {
|
||||||
NSString *sdpString = [objects objectForKey:@"sdp"];
|
NSString *sdpString = [objects objectForKey:@"sdp"];
|
||||||
RTCSessionDescription *sdp =
|
RTCSessionDescription *sdp = [[RTCSessionDescription alloc]
|
||||||
[[RTCSessionDescription alloc] initWithType:value sdp:sdpString];
|
initWithType:value sdp:[APPRTCAppDelegate preferISAC:sdpString]];
|
||||||
[self.peerConnection setRemoteDescriptionWithDelegate:self
|
[self.peerConnection setRemoteDescriptionWithDelegate:self
|
||||||
sessionDescription:sdp];
|
sessionDescription:sdp];
|
||||||
[self displayLogMessage:@"PC - setRemoteDescription."];
|
[self displayLogMessage:@"PC - setRemoteDescription."];
|
||||||
@ -283,8 +290,71 @@
|
|||||||
|
|
||||||
#pragma mark - RTCSessionDescriptonDelegate methods
|
#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
|
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||||
didCreateSessionDescription:(RTCSessionDescription *)sdp
|
didCreateSessionDescription:(RTCSessionDescription *)origSdp
|
||||||
error:(NSError *)error {
|
error:(NSError *)error {
|
||||||
if (error) {
|
if (error) {
|
||||||
[self displayLogMessage:@"SDP onFailure."];
|
[self displayLogMessage:@"SDP onFailure."];
|
||||||
@ -293,6 +363,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
|
[self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
|
||||||
|
RTCSessionDescription* sdp =
|
||||||
|
[[RTCSessionDescription alloc]
|
||||||
|
initWithType:origSdp.type
|
||||||
|
sdp:[APPRTCAppDelegate preferISAC:origSdp.description]];
|
||||||
[self.peerConnection setLocalDescriptionWithDelegate:self
|
[self.peerConnection setLocalDescriptionWithDelegate:self
|
||||||
sessionDescription:sdp];
|
sessionDescription:sdp];
|
||||||
[self displayLogMessage:@"PC setLocalDescription."];
|
[self displayLogMessage:@"PC setLocalDescription."];
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
self.textField.delegate = self;
|
self.textField.delegate = self;
|
||||||
|
[self.textField becomeFirstResponder];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)displayText:(NSString *)text {
|
- (void)displayText:(NSString *)text {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user