diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppClient.h b/talk/examples/ios/AppRTCDemo/APPRTCAppClient.h new file mode 100755 index 0000000000..41a795eba6 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/APPRTCAppClient.h @@ -0,0 +1,66 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "GAEChannelClient.h" + +// Called when there are RTCICEServers. +@protocol ICEServerDelegate + +- (void)onICEServers:(NSArray*)servers; + +@end + +@class RTCMediaConstraints; + +// Negotiates signaling for chatting with apprtc.appspot.com "rooms". +// Uses the client<->server specifics of the apprtc AppEngine webapp. +// +// To use: create an instance of this object (registering a message handler) and +// call connectToRoom(). apprtc.appspot.com will signal that is successful via +// onOpen through the browser channel. Then you should call sendData() and wait +// for the registered handler to be called with received messages. +@interface APPRTCAppClient : NSObject + +@property(nonatomic, weak, readonly) id ICEServerDelegate; +@property(nonatomic, weak, readonly) id messageHandler; +@property(nonatomic, assign) BOOL initiator; +@property(nonatomic, copy, readonly) RTCMediaConstraints* videoConstraints; + +- (id)initWithICEServerDelegate:(id)delegate + messageHandler:(id)handler; +- (void)connectToRoom:(NSURL*)room; +- (void)sendData:(NSData*)data; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +@end diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m b/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m new file mode 100755 index 0000000000..9ef0a7a74a --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m @@ -0,0 +1,348 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "APPRTCAppClient.h" + +#import + +#import "GAEChannelClient.h" +#import "RTCICEServer.h" +#import "APPRTCAppDelegate.h" +#import "RTCMediaConstraints.h" + +@interface APPRTCAppClient () + +@property(nonatomic, strong) dispatch_queue_t backgroundQueue; +@property(nonatomic, copy) NSString* baseURL; +@property(nonatomic, strong) GAEChannelClient* gaeChannel; +@property(nonatomic, copy) NSString* postMessageUrl; +@property(nonatomic, copy) NSString* pcConfig; +@property(nonatomic, strong) NSMutableString* roomHtml; +@property(atomic, strong) NSMutableArray* sendQueue; +@property(nonatomic, copy) NSString* token; + +@property(nonatomic, assign) BOOL verboseLogging; + +@end + +@implementation APPRTCAppClient + +- (id)initWithICEServerDelegate:(id)delegate + messageHandler:(id)handler { + if (self = [super init]) { + _ICEServerDelegate = delegate; + _messageHandler = handler; + _backgroundQueue = dispatch_queue_create("RTCBackgroundQueue", + DISPATCH_QUEUE_SERIAL); + _sendQueue = [NSMutableArray array]; + // Uncomment to see Request/Response logging. + // _verboseLogging = YES; + } + return self; +} + +#pragma mark - Public methods + +- (void)connectToRoom:(NSURL*)url { + NSURLRequest* request = [self getRequestFromUrl:url]; + [NSURLConnection connectionWithRequest:request delegate:self]; +} + +- (void)sendData:(NSData*)data { + [self maybeLogMessage:@"Send message"]; + + dispatch_async(self.backgroundQueue, ^{ + [self.sendQueue addObject:[data copy]]; + + if ([self.postMessageUrl length] < 1) { + return; + } + for (NSData* data in self.sendQueue) { + NSString* url = + [NSString stringWithFormat:@"%@/%@", + self.baseURL, self.postMessageUrl]; + [self sendData:data withUrl:url]; + } + [self.sendQueue removeAllObjects]; + }); +} + +#pragma mark - Internal methods + +- (NSString*)findVar:(NSString*)name strippingQuotes:(BOOL)strippingQuotes { + NSError* error; + NSString* pattern = + [NSString stringWithFormat:@".*\n *var %@ = ([^\n]*);\n.*", name]; + NSRegularExpression* regexp = + [NSRegularExpression regularExpressionWithPattern:pattern + options:0 + error:&error]; + NSAssert(!error, + @"Unexpected error compiling regex: ", + error.localizedDescription); + + NSRange fullRange = NSMakeRange(0, [self.roomHtml length]); + NSArray* matches = + [regexp matchesInString:self.roomHtml options:0 range:fullRange]; + if ([matches count] != 1) { + [self showMessage:[NSString stringWithFormat:@"%d matches for %@ in %@", + [matches count], + name, + self.roomHtml]]; + return nil; + } + NSRange matchRange = [matches[0] rangeAtIndex:1]; + NSString* value = [self.roomHtml substringWithRange:matchRange]; + if (strippingQuotes) { + NSAssert([value length] > 2, + @"Can't strip quotes from short string: [%@]", + value); + NSAssert(([value characterAtIndex:0] == '\'' && + [value characterAtIndex:[value length] - 1] == '\''), + @"Can't strip quotes from unquoted string: [%@]", + value); + value = [value substringWithRange:NSMakeRange(1, [value length] - 2)]; + } + return value; +} + +- (NSURLRequest*)getRequestFromUrl:(NSURL*)url { + self.roomHtml = [NSMutableString stringWithCapacity:20000]; + NSString* path = + [NSString stringWithFormat:@"https:%@", [url resourceSpecifier]]; + NSURLRequest* request = + [NSURLRequest requestWithURL:[NSURL URLWithString:path]]; + return request; +} + +- (void)maybeLogMessage:(NSString*)message { + if (self.verboseLogging) { + NSLog(@"%@", message); + } +} + +- (void)sendData:(NSData*)data withUrl:(NSString*)url { + NSMutableURLRequest* request = + [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; + request.HTTPMethod = @"POST"; + [request setHTTPBody:data]; + NSURLResponse* response; + NSError* error; + NSData* responseData = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + int status = [httpResponse statusCode]; + NSAssert(status == 200, + @"Bad response [%d] to message: %@\n\n%@", + status, + [NSString stringWithUTF8String:[data bytes]], + [NSString stringWithUTF8String:[responseData bytes]]); +} + +- (void)showMessage:(NSString*)message { + NSLog(@"%@", message); + UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Unable to join" + message:message + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alertView show]; +} + +- (void)updateICEServers:(NSMutableArray*)ICEServers + withTurnServer:(NSString*)turnServerUrl { + if ([turnServerUrl length] < 1) { + [self.ICEServerDelegate onICEServers:ICEServers]; + return; + } + dispatch_async(self.backgroundQueue, ^(void) { + NSMutableURLRequest* request = [NSMutableURLRequest + requestWithURL:[NSURL URLWithString:turnServerUrl]]; + [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"]; + [request addValue:@"https://apprtc.appspot.com" + forHTTPHeaderField:@"origin"]; + NSURLResponse* response; + NSError* error; + NSData* responseData = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + if (!error) { + NSDictionary* json = + [NSJSONSerialization JSONObjectWithData:responseData + options:0 + error:&error]; + NSAssert(!error, @"Unable to parse. %@", error.localizedDescription); + NSString* username = json[@"username"]; + NSString* password = json[@"password"]; + 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]; + NSLog(@"Added ICE Server: %@", ICEServer); + [ICEServers addObject:ICEServer]; + } + } else { + NSLog(@"Unable to get TURN server. Error: %@", error.description); + } + + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self.ICEServerDelegate onICEServers:ICEServers]; + }); + }); +} + +#pragma mark - NSURLConnectionDataDelegate methods + +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { + NSString* roomHtml = [NSString stringWithUTF8String:[data bytes]]; + [self maybeLogMessage:[NSString stringWithFormat:@"Received %d chars", + [roomHtml length]]]; + [self.roomHtml appendString:roomHtml]; +} + +- (void)connection:(NSURLConnection*)connection + didReceiveResponse:(NSURLResponse*)response { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + int statusCode = [httpResponse statusCode]; + [self + maybeLogMessage: + [NSString stringWithFormat: + @"Response received\nURL\n%@\nStatus [%d]\nHeaders\n%@", + [httpResponse URL], + statusCode, + [httpResponse allHeaderFields]]]; + NSAssert(statusCode == 200, @"Invalid response of %d received.", statusCode); +} + +- (void)connectionDidFinishLoading:(NSURLConnection*)connection { + [self maybeLogMessage:[NSString stringWithFormat:@"finished loading %d chars", + [self.roomHtml length]]]; + NSRegularExpression* fullRegex = + [NSRegularExpression regularExpressionWithPattern:@"room is full" + options:0 + error:nil]; + if ([fullRegex + numberOfMatchesInString:self.roomHtml + options:0 + range:NSMakeRange(0, [self.roomHtml length])]) { + [self showMessage:@"Room full"]; + APPRTCAppDelegate* ad = + (APPRTCAppDelegate*)[[UIApplication sharedApplication] delegate]; + [ad closeVideoUI]; + return; + } + + 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.initiator = [[self findVar:@"initiator" strippingQuotes:NO] boolValue]; + self.token = [self findVar:@"channelToken" strippingQuotes:YES]; + if (!self.token) + return; + [self maybeLogMessage:[NSString stringWithFormat:@"Token: %@", self.token]]; + + NSString* roomKey = [self findVar:@"roomKey" strippingQuotes:YES]; + NSString* me = [self findVar:@"me" strippingQuotes:YES]; + if (!roomKey || !me) + return; + self.postMessageUrl = + [NSString stringWithFormat:@"/message?r=%@&u=%@", roomKey, me]; + [self maybeLogMessage:[NSString stringWithFormat:@"POST message URL: %@", + self.postMessageUrl]]; + + NSString* pcConfig = [self findVar:@"pcConfig" strippingQuotes:NO]; + if (!pcConfig) + return; + [self maybeLogMessage:[NSString + stringWithFormat:@"PC Config JSON: %@", pcConfig]]; + + NSString* turnServerUrl = [self findVar:@"turnUrl" strippingQuotes:YES]; + if (turnServerUrl) { + [self maybeLogMessage:[NSString + stringWithFormat:@"TURN server request URL: %@", + turnServerUrl]]; + } + + NSError* error; + NSData* pcData = [pcConfig dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary* json = + [NSJSONSerialization JSONObjectWithData:pcData options:0 error:&error]; + NSAssert(!error, @"Unable to parse. %@", error.localizedDescription); + NSArray* servers = [json objectForKey:@"iceServers"]; + NSMutableArray* ICEServers = [NSMutableArray array]; + for (NSDictionary* server in servers) { + NSString* url = [server objectForKey:@"urls"]; + NSString* username = json[@"username"]; + NSString* credential = [server objectForKey:@"credential"]; + if (!username) { + username = @""; + } + if (!credential) { + credential = @""; + } + [self maybeLogMessage:[NSString + stringWithFormat:@"url [%@] - credential [%@]", + url, + 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]; + + NSString* mc = [self findVar:@"mediaConstraints" strippingQuotes:NO]; + if (mc) { + error = nil; + NSData* mcData = [mc dataUsingEncoding:NSUTF8StringEncoding]; + json = + [NSJSONSerialization JSONObjectWithData:mcData options:0 error:&error]; + NSAssert(!error, @"Unable to parse. %@", error.localizedDescription); + if ([[json objectForKey:@"video"] boolValue]) { + _videoConstraints = [[RTCMediaConstraints alloc] init]; + } + } + + [self + maybeLogMessage:[NSString + stringWithFormat:@"About to open GAE with token: %@", + self.token]]; + self.gaeChannel = + [[GAEChannelClient alloc] initWithToken:self.token + delegate:self.messageHandler]; +} + +@end diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h new file mode 100755 index 0000000000..dd810f74c0 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h @@ -0,0 +1,60 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "GAEChannelClient.h" +#import "APPRTCAppClient.h" +#import "RTCSessionDescriptionDelegate.h" +#import "RTCVideoSource.h" +// Used to send a message to an apprtc.appspot.com "room". +@protocol APPRTCSendMessage + +- (void)sendData:(NSData*)data; +// Logging helper. +- (void)displayLogMessage:(NSString*)message; +@end + +@class APPRTCViewController; +@class RTCVideoTrack; + +// The main application class of the AppRTCDemo iOS app demonstrating +// interoperability between the Objective C implementation of PeerConnection +// and the apprtc.appspot.com demo webapp. +@interface APPRTCAppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow* window; +@property(strong, nonatomic) APPRTCViewController* viewController; +@property (strong, nonatomic) RTCVideoSource* videoSource; + +- (void)closeVideoUI; + +@end diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m new file mode 100755 index 0000000000..87d1f53d6c --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m @@ -0,0 +1,575 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "APPRTCAppDelegate.h" + +#import "APPRTCViewController.h" +#import "RTCEAGLVideoView.h" +#import "RTCICECandidate.h" +#import "RTCICEServer.h" +#import "RTCMediaConstraints.h" +#import "RTCMediaStream.h" +#import "RTCPair.h" +#import "RTCPeerConnection.h" +#import "RTCPeerConnectionDelegate.h" +#import "RTCPeerConnectionFactory.h" +#import "RTCSessionDescription.h" +#import "RTCStatsDelegate.h" +#import "RTCVideoRenderer.h" +#import "RTCVideoCapturer.h" +#import "RTCVideoTrack.h" + +@interface PCObserver : NSObject + +- (id)initWithDelegate:(id)delegate; + +@property(nonatomic, strong) RTCEAGLVideoView* videoView; + +@end + +@implementation PCObserver { + id _delegate; +} + +- (id)initWithDelegate:(id)delegate { + if (self = [super init]) { + _delegate = delegate; + } + return self; +} + +#pragma mark - RTCPeerConnectionDelegate + +- (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSLog(@"PCO onError."); + NSAssert(NO, @"PeerConnection failed."); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + signalingStateChanged:(RTCSignalingState)stateChanged { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSLog(@"PCO onSignalingStateChange: %d", stateChanged); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + addedStream:(RTCMediaStream*)stream { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSLog(@"PCO onAddStream."); + NSAssert([stream.audioTracks count] <= 1, + @"Expected at most 1 audio stream"); + NSAssert([stream.videoTracks count] <= 1, + @"Expected at most 1 video stream"); + if ([stream.videoTracks count] != 0) { + self.videoView.videoTrack = stream.videoTracks[0]; + } + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + removedStream:(RTCMediaStream*)stream { + dispatch_async(dispatch_get_main_queue(), + ^(void) { NSLog(@"PCO onRemoveStream."); }); +} + +- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSLog(@"PCO onRenegotiationNeeded - ignoring because AppRTC has a " + "predefined negotiation strategy"); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + gotICECandidate:(RTCICECandidate*)candidate { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%d] Sdp[%@]", + candidate.sdpMid, + candidate.sdpMLineIndex, + candidate.sdp); + NSDictionary* json = @{ + @"type" : @"candidate", + @"label" : [NSNumber numberWithInt:candidate.sdpMLineIndex], + @"id" : candidate.sdpMid, + @"candidate" : candidate.sdp + }; + NSError* error; + NSData* data = + [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; + if (!error) { + [_delegate sendData:data]; + } else { + NSAssert(NO, + @"Unable to serialize JSON object with error: %@", + error.localizedDescription); + } + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + iceGatheringChanged:(RTCICEGatheringState)newState { + dispatch_async(dispatch_get_main_queue(), + ^(void) { NSLog(@"PCO onIceGatheringChange. %d", newState); }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + iceConnectionChanged:(RTCICEConnectionState)newState { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSLog(@"PCO onIceConnectionChange. %d", newState); + if (newState == RTCICEConnectionConnected) + [self displayLogMessage:@"ICE Connection Connected."]; + NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!"); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didOpenDataChannel:(RTCDataChannel*)dataChannel { + NSAssert(NO, @"AppRTC doesn't use DataChannels"); +} + +#pragma mark - Private + +- (void)displayLogMessage:(NSString*)message { + [_delegate displayLogMessage:message]; +} + +@end + +@interface APPRTCAppDelegate () + +@property(nonatomic, strong) APPRTCAppClient* client; +@property(nonatomic, strong) PCObserver* pcObserver; +@property(nonatomic, strong) RTCPeerConnection* peerConnection; +@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory; +@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates; + +@end + +@implementation APPRTCAppDelegate { + NSTimer* _statsTimer; +} + +#pragma mark - UIApplicationDelegate methods + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + [RTCPeerConnectionFactory initializeSSL]; + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.viewController = + [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController" + bundle:nil]; + self.window.rootViewController = self.viewController; + _statsTimer = + [NSTimer scheduledTimerWithTimeInterval:10 + target:self + selector:@selector(didFireStatsTimer:) + userInfo:nil + repeats:YES]; + [self.window makeKeyAndVisible]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication*)application { + [self displayLogMessage:@"Application lost focus, connection broken."]; + [self closeVideoUI]; +} + +- (void)applicationDidEnterBackground:(UIApplication*)application { +} + +- (void)applicationWillEnterForeground:(UIApplication*)application { +} + +- (void)applicationDidBecomeActive:(UIApplication*)application { +} + +- (void)applicationWillTerminate:(UIApplication*)application { +} + +- (BOOL)application:(UIApplication*)application + openURL:(NSURL*)url + sourceApplication:(NSString*)sourceApplication + annotation:(id)annotation { + if (self.client) { + return NO; + } + self.client = [[APPRTCAppClient alloc] initWithICEServerDelegate:self + messageHandler:self]; + [self.client connectToRoom:url]; + return YES; +} + +- (void)displayLogMessage:(NSString*)message { + NSAssert([NSThread isMainThread], @"Called off main thread!"); + NSLog(@"%@", message); + [self.viewController displayText:message]; +} + +#pragma mark - RTCSendMessage method + +- (void)sendData:(NSData*)data { + [self.client sendData:data]; +} + +#pragma mark - ICEServerDelegate method + +- (void)onICEServers:(NSArray*)servers { + self.queuedRemoteCandidates = [NSMutableArray array]; + self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init]; + RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] + initWithMandatoryConstraints: + @[ + [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"], + [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"] + ] + optionalConstraints: + @[ + [[RTCPair alloc] initWithKey:@"internalSctpDataChannels" + value:@"true"], + [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" + value:@"true"] + ]]; + self.pcObserver = [[PCObserver alloc] initWithDelegate:self]; + self.peerConnection = + [self.peerConnectionFactory peerConnectionWithICEServers:servers + constraints:constraints + delegate:self.pcObserver]; + RTCMediaStream* lms = + [self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"]; + + // The iOS simulator doesn't provide any sort of camera capture + // support or emulation (http://goo.gl/rHAnC1) so don't bother + // trying to open a local stream. + RTCVideoTrack* localVideoTrack; +#if !TARGET_IPHONE_SIMULATOR + NSString* cameraID = nil; + for (AVCaptureDevice* captureDevice in + [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + if (captureDevice.position == AVCaptureDevicePositionFront) { + cameraID = [captureDevice localizedName]; + break; + } + } + NSAssert(cameraID, @"Unable to get the front camera id"); + + RTCVideoCapturer* capturer = + [RTCVideoCapturer capturerWithDeviceName:cameraID]; + self.videoSource = [self.peerConnectionFactory + videoSourceWithCapturer:capturer + constraints:self.client.videoConstraints]; + localVideoTrack = + [self.peerConnectionFactory videoTrackWithID:@"ARDAMSv0" + source:self.videoSource]; + if (localVideoTrack) { + [lms addVideoTrack:localVideoTrack]; + } + self.viewController.localVideoView.videoTrack = localVideoTrack; +#else + self.viewController.localVideoView.hidden = YES; +#endif + + self.pcObserver.videoView = self.viewController.remoteVideoView; + [lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]]; + [self.peerConnection addStream:lms constraints:constraints]; + [self displayLogMessage:@"onICEServers - added local stream."]; +} + +#pragma mark - GAEMessageHandler methods + +- (void)onOpen { + if (!self.client.initiator) { + [self displayLogMessage:@"Callee; waiting for remote offer"]; + return; + } + [self displayLogMessage:@"GAE onOpen - create offer."]; + RTCPair* audio = + [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"]; + RTCPair* video = + [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]; + NSArray* mandatory = @[ audio, video ]; + RTCMediaConstraints* constraints = + [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory + optionalConstraints:nil]; + [self.peerConnection createOfferWithDelegate:self constraints:constraints]; + [self displayLogMessage:@"PC - createOffer."]; +} + +- (void)onMessage:(NSDictionary*)messageData { + NSString* type = messageData[@"type"]; + NSAssert(type, @"Missing type: %@", messageData); + [self displayLogMessage:[NSString stringWithFormat:@"GAE onMessage type - %@", + type]]; + if ([type isEqualToString:@"candidate"]) { + NSString* mid = messageData[@"id"]; + NSNumber* sdpLineIndex = messageData[@"label"]; + NSString* sdp = messageData[@"candidate"]; + RTCICECandidate* candidate = + [[RTCICECandidate alloc] initWithMid:mid + index:sdpLineIndex.intValue + sdp:sdp]; + if (self.queuedRemoteCandidates) { + [self.queuedRemoteCandidates addObject:candidate]; + } else { + [self.peerConnection addICECandidate:candidate]; + } + } else if ([type isEqualToString:@"offer"] || + [type isEqualToString:@"answer"]) { + NSString* sdpString = messageData[@"sdp"]; + RTCSessionDescription* sdp = [[RTCSessionDescription alloc] + initWithType:type + sdp:[APPRTCAppDelegate preferISAC:sdpString]]; + [self.peerConnection setRemoteDescriptionWithDelegate:self + sessionDescription:sdp]; + [self displayLogMessage:@"PC - setRemoteDescription."]; + } else if ([type isEqualToString:@"bye"]) { + [self closeVideoUI]; + UIAlertView* alertView = + [[UIAlertView alloc] initWithTitle:@"Remote end hung up" + message:@"dropping PeerConnection" + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alertView show]; + } else { + NSAssert(NO, @"Invalid message: %@", messageData); + } +} + +- (void)onClose { + [self displayLogMessage:@"GAE onClose."]; + [self closeVideoUI]; +} + +- (void)onError:(int)code withDescription:(NSString*)description { + [self displayLogMessage:[NSString stringWithFormat:@"GAE onError: %d, %@", + code, description]]; + [self closeVideoUI]; +} + +#pragma mark - RTCSessionDescriptionDelegate 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= ... + [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 + isEqualToString:[origMLineParts objectAtIndex:origPartIndex]]) { + [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*)origSdp + error:(NSError*)error { + dispatch_async(dispatch_get_main_queue(), ^(void) { + if (error) { + [self displayLogMessage:@"SDP onFailure."]; + NSAssert(NO, error.description); + return; + } + [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."]; + NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description}; + NSError* error; + NSData* data = + [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; + NSAssert(!error, + @"%@", + [NSString stringWithFormat:@"Error: %@", error.description]); + [self sendData:data]; + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didSetSessionDescriptionWithError:(NSError*)error { + dispatch_async(dispatch_get_main_queue(), ^(void) { + if (error) { + [self displayLogMessage:@"SDP onFailure."]; + NSAssert(NO, error.description); + return; + } + + [self displayLogMessage:@"SDP onSuccess() - possibly drain candidates"]; + if (!self.client.initiator) { + if (self.peerConnection.remoteDescription && + !self.peerConnection.localDescription) { + [self displayLogMessage:@"Callee, setRemoteDescription succeeded"]; + RTCPair* audio = [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" + value:@"true"]; + RTCPair* video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" + value:@"true"]; + NSArray* mandatory = @[ audio, video ]; + RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:mandatory + optionalConstraints:nil]; + [self.peerConnection createAnswerWithDelegate:self + constraints:constraints]; + [self displayLogMessage:@"PC - createAnswer."]; + } else { + [self displayLogMessage:@"SDP onSuccess - drain candidates"]; + [self drainRemoteCandidates]; + } + } else { + if (self.peerConnection.remoteDescription) { + [self displayLogMessage:@"SDP onSuccess - drain candidates"]; + [self drainRemoteCandidates]; + } + } + }); +} + +#pragma mark - RTCStatsDelegate methods + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didGetStats:(NSArray*)stats { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* message = [NSString stringWithFormat:@"Stats:\n %@", stats]; + [self displayLogMessage:message]; + }); +} + +#pragma mark - internal methods + +- (void)disconnect { + [self.client + sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]]; + [self.peerConnection close]; + self.peerConnection = nil; + self.pcObserver = nil; + self.client = nil; + self.videoSource = nil; + self.peerConnectionFactory = nil; + [RTCPeerConnectionFactory deinitializeSSL]; +} + +- (void)drainRemoteCandidates { + for (RTCICECandidate* candidate in self.queuedRemoteCandidates) { + [self.peerConnection addICECandidate:candidate]; + } + self.queuedRemoteCandidates = nil; +} + +- (NSString*)unHTMLifyString:(NSString*)base { + // TODO(hughv): Investigate why percent escapes are being added. Removing + // them isn't necessary on Android. + // convert HTML escaped characters to UTF8. + NSString* removePercent = + [base stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + // remove leading and trailing ". + NSRange range; + range.length = [removePercent length] - 2; + range.location = 1; + NSString* removeQuotes = [removePercent substringWithRange:range]; + // convert \" to ". + NSString* removeEscapedQuotes = + [removeQuotes stringByReplacingOccurrencesOfString:@"\\\"" + withString:@"\""]; + // convert \\ to \. + NSString* removeBackslash = + [removeEscapedQuotes stringByReplacingOccurrencesOfString:@"\\\\" + withString:@"\\"]; + return removeBackslash; +} + +- (void)didFireStatsTimer:(NSTimer *)timer { + if (self.peerConnection) { + [self.peerConnection getStatsWithDelegate:self + mediaStreamTrack:nil + statsOutputLevel:RTCStatsOutputLevelDebug]; + } +} + +#pragma mark - public methods + +- (void)closeVideoUI { + [self.viewController resetUI]; + [self disconnect]; +} + +@end diff --git a/talk/examples/ios/AppRTCDemo/APPRTCViewController.h b/talk/examples/ios/AppRTCDemo/APPRTCViewController.h new file mode 100755 index 0000000000..1737a138ad --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/APPRTCViewController.h @@ -0,0 +1,46 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class RTCEAGLVideoView; + +// The view controller that is displayed when AppRTCDemo is loaded. +@interface APPRTCViewController : UIViewController + +@property(weak, nonatomic) IBOutlet UITextField* roomInput; +@property(weak, nonatomic) IBOutlet UITextView* instructionsView; +@property(weak, nonatomic) IBOutlet UITextView* logView; +@property(weak, nonatomic) IBOutlet UIView* blackView; + +@property(nonatomic, strong) RTCEAGLVideoView* localVideoView; +@property(nonatomic, strong) RTCEAGLVideoView* remoteVideoView; + +- (void)displayText:(NSString*)text; +- (void)resetUI; + +@end diff --git a/talk/examples/ios/AppRTCDemo/APPRTCViewController.m b/talk/examples/ios/AppRTCDemo/APPRTCViewController.m new file mode 100755 index 0000000000..bdd8b50369 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/APPRTCViewController.m @@ -0,0 +1,150 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "APPRTCViewController.h" + +#import +#import "RTCEAGLVideoView.h" + +@interface APPRTCViewController () +@property(nonatomic, assign) UIInterfaceOrientation statusBarOrientation; +@end + +@implementation APPRTCViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.statusBarOrientation = + [UIApplication sharedApplication].statusBarOrientation; + self.roomInput.delegate = self; + [self.roomInput becomeFirstResponder]; +} + +- (void)viewDidLayoutSubviews { + if (self.statusBarOrientation != + [UIApplication sharedApplication].statusBarOrientation) { + self.statusBarOrientation = + [UIApplication sharedApplication].statusBarOrientation; + [[NSNotificationCenter defaultCenter] + postNotificationName:@"StatusBarOrientationDidChange" + object:nil]; + } +} + +- (void)displayText:(NSString*)text { + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSString* output = + [NSString stringWithFormat:@"%@\n%@", self.logView.text, text]; + self.logView.text = output; + [self.logView + scrollRangeToVisible:NSMakeRange([self.logView.text length], 0)]; + }); +} + +- (void)resetUI { + [self.roomInput resignFirstResponder]; + self.roomInput.text = nil; + self.roomInput.hidden = NO; + self.instructionsView.hidden = NO; + self.logView.hidden = YES; + self.logView.text = nil; + self.blackView.hidden = YES; + + [self.remoteVideoView removeFromSuperview]; + self.remoteVideoView = nil; + + [self.localVideoView removeFromSuperview]; + self.localVideoView = nil; +} + +// TODO(fischman): Use video dimensions from the incoming video stream +// and resize the Video View accordingly w.r.t. aspect ratio. +enum { + // Remote video view dimensions. + kRemoteVideoWidth = 640, + kRemoteVideoHeight = 480, + // Padding space for local video view with its parent. + kLocalViewPadding = 20 +}; + +- (void)setupCaptureSession { + self.blackView.hidden = NO; + + CGSize videoSize = + CGSizeMake(kRemoteVideoWidth, kRemoteVideoHeight); + CGRect remoteVideoFrame = + AVMakeRectWithAspectRatioInsideRect(videoSize, + self.blackView.bounds); + CGRect localVideoFrame = remoteVideoFrame; + // TODO(tkchin): use video dimensions from incoming video stream + // and handle rotation. + localVideoFrame.size.width = remoteVideoFrame.size.height / 4; + localVideoFrame.size.height = remoteVideoFrame.size.width / 4; + localVideoFrame.origin.x = CGRectGetMaxX(remoteVideoFrame) + - localVideoFrame.size.width - kLocalViewPadding; + localVideoFrame.origin.y = CGRectGetMaxY(remoteVideoFrame) + - localVideoFrame.size.height - kLocalViewPadding; + + self.remoteVideoView = + [[RTCEAGLVideoView alloc] initWithFrame:remoteVideoFrame]; + [self.blackView addSubview:self.remoteVideoView]; + self.remoteVideoView.transform = CGAffineTransformMakeScale(-1, 1); + + self.localVideoView = + [[RTCEAGLVideoView alloc] initWithFrame:localVideoFrame]; + [self.blackView addSubview:self.localVideoView]; +} + +#pragma mark - UITextFieldDelegate + +- (void)textFieldDidEndEditing:(UITextField*)textField { + NSString* room = textField.text; + if ([room length] == 0) { + return; + } + textField.hidden = YES; + self.instructionsView.hidden = YES; + self.logView.hidden = NO; + // TODO(hughv): Instead of launching a URL with apprtc scheme, change to + // prepopulating the textField with a valid URL missing the room. This allows + // the user to have the simplicity of just entering the room or the ability to + // override to a custom appspot instance. Remove apprtc:// when this is done. + NSString* url = + [NSString stringWithFormat:@"apprtc://apprtc.appspot.com/?r=%@", room]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; + + dispatch_async(dispatch_get_main_queue(), ^{ [self setupCaptureSession]; }); +} + +- (BOOL)textFieldShouldReturn:(UITextField*)textField { + // There is no other control that can take focus, so manually resign focus + // when return (Join) is pressed to trigger |textFieldDidEndEditing|. + [textField resignFirstResponder]; + return YES; +} + +@end diff --git a/talk/examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch b/talk/examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch new file mode 100755 index 0000000000..3ac2c3b1a5 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch @@ -0,0 +1,40 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Prefix header for all source files of the 'AppRTCDemo' target in the +// 'AppRTCDemo' project +// + +#import + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0 +#warning "This project uses features only available in iOS SDK 6.0 and later." +#endif + +#import +#import diff --git a/talk/examples/objc/AppRTCDemo/ios/Default.png b/talk/examples/ios/AppRTCDemo/Default.png old mode 100644 new mode 100755 similarity index 100% rename from talk/examples/objc/AppRTCDemo/ios/Default.png rename to talk/examples/ios/AppRTCDemo/Default.png diff --git a/talk/examples/ios/AppRTCDemo/GAEChannelClient.h b/talk/examples/ios/AppRTCDemo/GAEChannelClient.h new file mode 100755 index 0000000000..8c7d5d3472 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/GAEChannelClient.h @@ -0,0 +1,49 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +// These methods will be called by the AppEngine chanel. The documentation +// for these methods is found here. (Yes, it is a JS API.) +// https://developers.google.com/appengine/docs/java/channel/javascript +@protocol GAEMessageHandler + +- (void)onOpen; +- (void)onMessage:(NSDictionary*)data; +- (void)onClose; +- (void)onError:(int)code withDescription:(NSString *)description; + +@end + +// Initialize with a token for an AppRTC data channel. This will load +// ios_channel.html and use the token to establish a data channel between the +// application and AppEngine. +@interface GAEChannelClient : NSObject + +- (id)initWithToken:(NSString *)token delegate:(id)delegate; + +@end diff --git a/talk/examples/ios/AppRTCDemo/GAEChannelClient.m b/talk/examples/ios/AppRTCDemo/GAEChannelClient.m new file mode 100755 index 0000000000..fcd0787ef6 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/GAEChannelClient.m @@ -0,0 +1,116 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GAEChannelClient.h" + +#import "RTCPeerConnectionFactory.h" + +@interface GAEChannelClient () + +@property(nonatomic, assign) id delegate; +@property(nonatomic, strong) UIWebView* webView; + +@end + +@implementation GAEChannelClient + +- (id)initWithToken:(NSString*)token delegate:(id)delegate { + self = [super init]; + if (self) { + _webView = [[UIWebView alloc] init]; + _webView.delegate = self; + _delegate = delegate; + NSString* htmlPath = + [[NSBundle mainBundle] pathForResource:@"ios_channel" ofType:@"html"]; + NSURL* htmlUrl = [NSURL fileURLWithPath:htmlPath]; + NSString* path = [NSString + stringWithFormat:@"%@?token=%@", [htmlUrl absoluteString], token]; + + [_webView + loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]]; + } + return self; +} + +- (void)dealloc { + _webView.delegate = nil; + [_webView stopLoading]; +} + +#pragma mark - UIWebViewDelegate method + ++ (NSDictionary*)jsonStringToDictionary:(NSString*)str { + NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding]; + NSError* error; + NSDictionary* dict = + [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + NSAssert(!error, @"Invalid JSON? %@", str); + return dict; +} + +- (BOOL)webView:(UIWebView*)webView + shouldStartLoadWithRequest:(NSURLRequest*)request + navigationType:(UIWebViewNavigationType)navigationType { + NSString* scheme = [request.URL scheme]; + NSAssert(scheme, @"scheme is nil: %@", request); + if (![scheme isEqualToString:@"js-frame"]) { + return YES; + } + + dispatch_async(dispatch_get_main_queue(), ^(void) { + NSString* queuedMessage = [webView + stringByEvaluatingJavaScriptFromString:@"popQueuedMessage();"]; + NSAssert([queuedMessage length], @"Empty queued message from JS"); + + NSDictionary* queuedMessageDict = + [GAEChannelClient jsonStringToDictionary:queuedMessage]; + NSString* method = queuedMessageDict[@"type"]; + NSAssert(method, @"Missing method: %@", queuedMessageDict); + NSDictionary* payload = queuedMessageDict[@"payload"]; // May be nil. + + if ([method isEqualToString:@"onopen"]) { + [self.delegate onOpen]; + } else if ([method isEqualToString:@"onmessage"]) { + NSDictionary* payloadData = + [GAEChannelClient jsonStringToDictionary:payload[@"data"]]; + [self.delegate onMessage:payloadData]; + } else if ([method isEqualToString:@"onclose"]) { + [self.delegate onClose]; + } else if ([method isEqualToString:@"onerror"]) { + NSNumber* codeNumber = payload[@"code"]; + int code = [codeNumber intValue]; + NSAssert([codeNumber isEqualToNumber:[NSNumber numberWithInt:code]], + @"Unexpected non-integral code: %@", payload); + [self.delegate onError:code withDescription:payload[@"description"]]; + } else { + NSAssert(NO, @"Invalid message sent from UIWebView: %@", queuedMessage); + } + }); + return NO; +} + +@end diff --git a/talk/examples/ios/AppRTCDemo/Info.plist b/talk/examples/ios/AppRTCDemo/Info.plist new file mode 100755 index 0000000000..a32be860f5 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/Info.plist @@ -0,0 +1,75 @@ + + + + + BuildMachineOSBuild + 12E55 + CFBundleDevelopmentRegion + en + CFBundleDisplayName + AppRTCDemo + CFBundleExecutable + AppRTCDemo + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + Icon.png + + + + CFBundleIdentifier + com.google.AppRTCDemo + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + AppRTCDemo + CFBundlePackageType + APPL + CFBundleResourceSpecification + ResourceRules.plist + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + iPhoneOS + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.google.apprtcdemo + CFBundleURLSchemes + + apprtc + + + + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + + diff --git a/talk/examples/ios/AppRTCDemo/ResourceRules.plist b/talk/examples/ios/AppRTCDemo/ResourceRules.plist new file mode 100755 index 0000000000..e7ec329dcc --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/ResourceRules.plist @@ -0,0 +1,25 @@ + + + + + rules + + .* + + Info.plist + + omit + + weight + 10 + + ResourceRules.plist + + omit + + weight + 100 + + + + diff --git a/talk/examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib b/talk/examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib new file mode 100755 index 0000000000..62807fe1a0 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib @@ -0,0 +1,716 @@ + + + + 1536 + 13B42 + 4514 + 1265 + 696.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 3747 + + + IBNSLayoutConstraint + IBProxyObject + IBUITextField + IBUITextView + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 292 + {{20, 20}, {280, 141}} + + + + _NS:9 + + 1 + MSAxIDEAA + + YES + NO + IBCocoaTouchFramework + Use Safari and open a URL with a scheme of apprtc to load the test app and connect. i.e. apprtc://apprtc.appspot.com/?r=12345678 Or just enter the room below to connect to apprtc. + + 2 + IBCocoaTouchFramework + + + 1 + 14 + + + HelveticaNeue + 14 + 16 + + + + + 292 + {{20, 180}, {280, 30}} + + + + _NS:9 + NO + YES + IBCocoaTouchFramework + 0 + + 3 + apprtc room + + 3 + MAA + + 2 + + + YES + 17 + + 2 + 3 + IBCocoaTouchFramework + + + + + + + -2147483356 + {{20, 20}, {280, 190}} + + + + _NS:9 + + YES + YES + IBCocoaTouchFramework + NO + + + 2 + IBCocoaTouchFramework + + + + + + + -2147483356 + {{20, 228}, {280, 300}} + + + + _NS:9 + + 3 + MAA + + IBCocoaTouchFramework + + + {{0, 20}, {320, 548}} + + + + + 3 + MC43NQA + + + NO + + + IBUIScreenMetrics + + YES + + + + + + {320, 568} + {568, 320} + + + IBCocoaTouchFramework + Retina 4-inch Full Screen + 2 + + IBCocoaTouchFramework + + + + + + + view + + + + 7 + + + + roomInput + + + + 108 + + + + instructionsView + + + + 127 + + + + logView + + + + 138 + + + + blackView + + + + 151 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + + + 4 + 0 + + 4 + 1 + + 20 + + 1000 + + 8 + 23 + 3 + NO + + + + 3 + 0 + + 3 + 1 + + 228 + + 1000 + + 3 + 9 + 3 + NO + + + + 5 + 0 + + 5 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + NO + + + + 6 + 0 + + 6 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + NO + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 4 + 0 + + 4 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + NO + + + + 3 + 0 + + 3 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 3 + 0 + + 3 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + + 0 + 29 + 3 + NO + + + + + + + + + + 57 + + + + + 8 + 0 + + 0 + 1 + + 141 + + 1000 + + 3 + 9 + 1 + NO + + + + + + 62 + + + + + 63 + + + + + 66 + + + + + 104 + + + + + + 107 + + + + + 123 + + + + + 126 + + + + + 128 + + + + + 8 + 0 + + 0 + 1 + + 190 + + 1000 + + 3 + 9 + 1 + NO + + + + + + 133 + + + + + 137 + + + + + 139 + + + + + 141 + + + + + 142 + + + + + 148 + + + + + 149 + + + + + 153 + + + + + 154 + + + + + 155 + + + + + + + APPRTCViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + + + + + + + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 155 + + + + + APPRTCViewController + UIViewController + + UIView + UITextField + UITextView + UITextView + + + + blackView + UIView + + + roomInput + UITextField + + + instructionsView + UITextView + + + logView + UITextView + + + + IBProjectSource + ./Classes/APPRTCViewController.h + + + + NSLayoutConstraint + NSObject + + IBProjectSource + ./Classes/NSLayoutConstraint.h + + + + + 0 + IBCocoaTouchFramework + YES + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + 3 + YES + 3747 + + diff --git a/talk/examples/ios/AppRTCDemo/ios_channel.html b/talk/examples/ios/AppRTCDemo/ios_channel.html new file mode 100755 index 0000000000..86846dd687 --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/ios_channel.html @@ -0,0 +1,94 @@ + + + + + + + + + diff --git a/talk/examples/ios/AppRTCDemo/main.m b/talk/examples/ios/AppRTCDemo/main.m new file mode 100755 index 0000000000..e9a1f63efb --- /dev/null +++ b/talk/examples/ios/AppRTCDemo/main.m @@ -0,0 +1,37 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "APPRTCAppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain( + argc, argv, nil, NSStringFromClass([APPRTCAppDelegate class])); + } +} diff --git a/talk/examples/objc/Icon.png b/talk/examples/ios/Icon.png old mode 100644 new mode 100755 similarity index 100% rename from talk/examples/objc/Icon.png rename to talk/examples/ios/Icon.png diff --git a/talk/examples/ios/README b/talk/examples/ios/README new file mode 100755 index 0000000000..9c0d134171 --- /dev/null +++ b/talk/examples/ios/README @@ -0,0 +1,3 @@ +This directory contains an example iOS client for http://apprtc.appspot.com + +See ../../app/webrtc/objc/README for information on how to use it. diff --git a/talk/media/webrtc/webrtcmediaengine.h b/talk/media/webrtc/webrtcmediaengine.h index 84319a11fd..0e43493b5b 100644 --- a/talk/media/webrtc/webrtcmediaengine.h +++ b/talk/media/webrtc/webrtcmediaengine.h @@ -175,6 +175,7 @@ class WebRtcMediaEngine : public cricket::MediaEngineInterface { #else #include "talk/media/webrtc/webrtcvideoengine.h" +#include "talk/media/webrtc/webrtcvideoengine2.h" #include "talk/media/webrtc/webrtcvoiceengine.h" namespace cricket { @@ -195,6 +196,25 @@ class WebRtcMediaEngine : public WebRtcCompositeMediaEngine { } }; +#ifdef WEBRTC_CHROMIUM_BUILD +typedef CompositeMediaEngine + WebRtcCompositeMediaEngine2; + +class WebRtcMediaEngine2 : public WebRtcCompositeMediaEngine2 { + public: + WebRtcMediaEngine2(webrtc::AudioDeviceModule* adm, + webrtc::AudioDeviceModule* adm_sc, + WebRtcVideoEncoderFactory* encoder_factory, + WebRtcVideoDecoderFactory* decoder_factory) { + voice_.SetAudioDeviceModule(adm, adm_sc); + video_.SetVoiceEngine(&voice_); + video_.EnableTimedRender(); +// video_.SetExternalEncoderFactory(encoder_factory); +// video_.SetExternalDecoderFactory(decoder_factory); + } +}; +#endif + } // namespace cricket #endif // !defined(LIBPEERCONNECTION_LIB) && diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc index 0283f575da..54a5201e47 100644 --- a/talk/media/webrtc/webrtcvideoengine.cc +++ b/talk/media/webrtc/webrtcvideoengine.cc @@ -62,6 +62,7 @@ #include "talk/media/webrtc/webrtcvoiceengine.h" #include "webrtc/experiments.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/system_wrappers/interface/field_trial.h" #if !defined(LIBPEERCONNECTION_LIB) #include "talk/media/webrtc/webrtcmediaengine.h" @@ -71,13 +72,30 @@ cricket::MediaEngineInterface* CreateWebRtcMediaEngine( webrtc::AudioDeviceModule* adm, webrtc::AudioDeviceModule* adm_sc, cricket::WebRtcVideoEncoderFactory* encoder_factory, cricket::WebRtcVideoDecoderFactory* decoder_factory) { - return new cricket::WebRtcMediaEngine(adm, adm_sc, encoder_factory, - decoder_factory); +#ifdef WEBRTC_CHROMIUM_BUILD + if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { + return new cricket::WebRtcMediaEngine2( + adm, adm_sc, encoder_factory, decoder_factory); + } else { +#endif + return new cricket::WebRtcMediaEngine( + adm, adm_sc, encoder_factory, decoder_factory); +#ifdef WEBRTC_CHROMIUM_BUILD + } +#endif } WRME_EXPORT void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) { - delete static_cast(media_engine); +#ifdef WEBRTC_CHROMIUM_BUILD + if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { + delete static_cast(media_engine); + } else { +#endif + delete static_cast(media_engine); +#ifdef WEBRTC_CHROMIUM_BUILD + } +#endif } #endif @@ -104,7 +122,6 @@ static const char kFecPayloadName[] = "ulpfec"; static const int kDefaultNumberOfTemporalLayers = 1; // 1:1 -static const int kMaxExternalVideoCodecs = 8; static const int kExternalVideoPayloadTypeBase = 120; static bool BitrateIsSet(int value) { @@ -117,7 +134,10 @@ static int GetBitrate(int value, int deflt) { // Static allocation of payload type values for external video codec. static int GetExternalVideoPayloadType(int index) { +#if ENABLE_DEBUG + static const int kMaxExternalVideoCodecs = 8; ASSERT(index >= 0 && index < kMaxExternalVideoCodecs); +#endif return kExternalVideoPayloadTypeBase + index; } diff --git a/talk/p2p/base/relayserver.cc b/talk/p2p/base/relayserver.cc index 990c818ca2..3dd8506571 100644 --- a/talk/p2p/base/relayserver.cc +++ b/talk/p2p/base/relayserver.cc @@ -46,8 +46,6 @@ const int MAX_LIFETIME = 15 * 60 * 1000; // The number of bytes in each of the usernames we use. const uint32 USERNAME_LENGTH = 16; -static const uint32 kMessageAcceptConnection = 1; - // Calls SendTo on the given socket and logs any bad results. void Send(talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, const talk_base::SocketAddress& addr) { @@ -548,7 +546,10 @@ void RelayServer::RemoveBinding(RelayServerBinding* binding) { } void RelayServer::OnMessage(talk_base::Message *pmsg) { +#if ENABLE_DEBUG + static const uint32 kMessageAcceptConnection = 1; ASSERT(pmsg->message_id == kMessageAcceptConnection); +#endif talk_base::MessageData* data = pmsg->pdata; talk_base::AsyncSocket* socket = static_cast *>