blob: 681876eae1fc8d0ed6080cd811e759ad097e53ba [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2013, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000028#import <AVFoundation/AVFoundation.h>
29
henrike@webrtc.org28e20752013-07-10 00:45:36 +000030#import "APPRTCAppDelegate.h"
31
32#import "APPRTCViewController.h"
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000033#import "RTCICECandidate.h"
34#import "RTCICEServer.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035#import "RTCMediaConstraints.h"
36#import "RTCMediaStream.h"
37#import "RTCPair.h"
38#import "RTCPeerConnection.h"
39#import "RTCPeerConnectionDelegate.h"
40#import "RTCPeerConnectionFactory.h"
41#import "RTCSessionDescription.h"
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000042#import "RTCVideoRenderer.h"
43#import "RTCVideoCapturer.h"
44#import "RTCVideoTrack.h"
45#import "VideoView.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046
47@interface PCObserver : NSObject<RTCPeerConnectionDelegate>
48
49- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate;
50
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000051@property(nonatomic, strong) VideoView *videoView;
52
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053@end
54
55@implementation PCObserver {
56 id<APPRTCSendMessage> _delegate;
57}
58
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000059@synthesize videoView = _videoView;
60
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate {
62 if (self = [super init]) {
63 _delegate = delegate;
64 }
65 return self;
66}
67
68- (void)peerConnectionOnError:(RTCPeerConnection *)peerConnection {
69 NSLog(@"PCO onError.");
70 NSAssert(NO, @"PeerConnection failed.");
71}
72
73- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000074 signalingStateChanged:(RTCSignalingState)stateChanged {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +000075 NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076}
77
78- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000079 addedStream:(RTCMediaStream *)stream {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000080 NSLog(@"PCO onAddStream.");
81 dispatch_async(dispatch_get_main_queue(), ^(void) {
82 NSAssert([stream.audioTracks count] >= 1,
83 @"Expected at least 1 audio stream");
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000084 NSAssert([stream.videoTracks count] <= 1,
85 @"Expected at most 1 video stream");
86 if ([stream.videoTracks count] != 0) {
87 [[self videoView]
88 renderVideoTrackInterface:[stream.videoTracks objectAtIndex:0]];
89 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000090 });
91}
92
93- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000094 removedStream:(RTCMediaStream *)stream {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000095 NSLog(@"PCO onRemoveStream.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +000096}
97
98- (void)
99 peerConnectionOnRenegotiationNeeded:(RTCPeerConnection *)peerConnection {
100 NSLog(@"PCO onRenegotiationNeeded.");
101 // TODO(hughv): Handle this.
102}
103
104- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000105 gotICECandidate:(RTCICECandidate *)candidate {
106 NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%d] Sdp[%@]",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107 candidate.sdpMid,
108 candidate.sdpMLineIndex,
109 candidate.sdp);
110 NSDictionary *json =
111 @{ @"type" : @"candidate",
112 @"label" : [NSNumber numberWithInt:candidate.sdpMLineIndex],
113 @"id" : candidate.sdpMid,
114 @"candidate" : candidate.sdp };
115 NSError *error;
116 NSData *data =
117 [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
118 if (!error) {
119 [_delegate sendData:data];
120 } else {
121 NSAssert(NO, @"Unable to serialize JSON object with error: %@",
122 error.localizedDescription);
123 }
124}
125
126- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000127 iceGatheringChanged:(RTCICEGatheringState)newState {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000128 NSLog(@"PCO onIceGatheringChange. %d", newState);
129}
130
131- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000132 iceConnectionChanged:(RTCICEConnectionState)newState {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133 NSLog(@"PCO onIceConnectionChange. %d", newState);
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000134 if (newState == RTCICEConnectionConnected)
135 [self displayLogMessage:@"ICE Connection Connected."];
136 NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!");
137}
138
139- (void)displayLogMessage:(NSString *)message {
140 [_delegate displayLogMessage:message];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141}
142
143@end
144
145@interface APPRTCAppDelegate ()
146
147@property(nonatomic, strong) APPRTCAppClient *client;
148@property(nonatomic, strong) PCObserver *pcObserver;
149@property(nonatomic, strong) RTCPeerConnection *peerConnection;
150@property(nonatomic, strong) RTCPeerConnectionFactory *peerConnectionFactory;
151@property(nonatomic, strong) NSMutableArray *queuedRemoteCandidates;
152
153@end
154
155@implementation APPRTCAppDelegate
156
fischman@webrtc.org9ca93a82013-10-29 00:14:15 +0000157@synthesize window = _window;
158@synthesize viewController = _viewController;
159@synthesize client = _client;
160@synthesize pcObserver = _pcObserver;
161@synthesize peerConnection = _peerConnection;
162@synthesize peerConnectionFactory = _peerConnectionFactory;
163@synthesize queuedRemoteCandidates = _queuedRemoteCandidates;
164
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165#pragma mark - UIApplicationDelegate methods
166
167- (BOOL)application:(UIApplication *)application
168 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000169 [RTCPeerConnectionFactory initializeSSL];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
171 self.viewController =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000172 [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController"
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000173 bundle:nil];
174 self.window.rootViewController = self.viewController;
175 [self.window makeKeyAndVisible];
176 return YES;
177}
178
179- (void)applicationWillResignActive:(UIApplication *)application {
180 [self displayLogMessage:@"Application lost focus, connection broken."];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000181 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000182}
183
184- (void)applicationDidEnterBackground:(UIApplication *)application {
185}
186
187- (void)applicationWillEnterForeground:(UIApplication *)application {
188}
189
190- (void)applicationDidBecomeActive:(UIApplication *)application {
191}
192
193- (void)applicationWillTerminate:(UIApplication *)application {
194}
195
196- (BOOL)application:(UIApplication *)application
197 openURL:(NSURL *)url
198 sourceApplication:(NSString *)sourceApplication
199 annotation:(id)annotation {
200 if (self.client) {
201 return NO;
202 }
203 self.client = [[APPRTCAppClient alloc] init];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000204 self.client.ICEServerDelegate = self;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000205 self.client.messageHandler = self;
206 [self.client connectToRoom:url];
207 return YES;
208}
209
210- (void)displayLogMessage:(NSString *)message {
211 NSLog(@"%@", message);
212 [self.viewController displayText:message];
213}
214
215#pragma mark - RTCSendMessage method
216
217- (void)sendData:(NSData *)data {
218 [self.client sendData:data];
219}
220
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000221#pragma mark - ICEServerDelegate method
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000223- (void)onICEServers:(NSArray *)servers {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000224 self.queuedRemoteCandidates = [NSMutableArray array];
225 self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000226 RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc]
227 initWithMandatoryConstraints:
228 @[[[RTCPair alloc]
229 initWithKey:@"OfferToReceiveAudio"
230 value:@"true"],
231 [[RTCPair alloc]
232 initWithKey:@"OfferToReceiveVideo"
233 value:@"true"]]
234 optionalConstraints:
235 @[[[RTCPair alloc]
236 initWithKey:@"internalSctpDataChannels"
237 value:@"true"],
238 [[RTCPair alloc]
239 initWithKey:@"DtlsSrtpKeyAgreement"
240 value:@"true"]]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000241 self.pcObserver = [[PCObserver alloc] initWithDelegate:self];
242 self.peerConnection =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000243 [self.peerConnectionFactory peerConnectionWithICEServers:servers
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244 constraints:constraints
245 delegate:self.pcObserver];
246 RTCMediaStream *lms =
247 [self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000248
249 NSString *cameraID = nil;
250 for (AVCaptureDevice *captureDevice in
251 [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] ) {
252 if (captureDevice.position == AVCaptureDevicePositionFront) {
253 cameraID = [captureDevice localizedName];
254 break;
255 }
256 }
257 NSAssert(cameraID, @"Unable to get the front camera id");
258
259 RTCVideoCapturer *capturer =
260 [RTCVideoCapturer capturerWithDeviceName:cameraID];
261 RTCVideoSource *videoSource =
262 [self.peerConnectionFactory
263 videoSourceWithCapturer:capturer constraints:self.client.videoConstraints];
264 RTCVideoTrack *localVideoTrack =
265 [self.peerConnectionFactory
266 videoTrackWithID:@"ARDAMSv0" source:videoSource];
267 if (localVideoTrack) {
268 [lms addVideoTrack:localVideoTrack];
269 }
270
271 [self.viewController.localVideoView
272 renderVideoTrackInterface:localVideoTrack];
273
274 self.pcObserver.videoView = self.viewController.remoteVideoView;
275
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000276 [lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]];
277 [self.peerConnection addStream:lms constraints:constraints];
278 [self displayLogMessage:@"onICEServers - add local stream."];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279}
280
281#pragma mark - GAEMessageHandler methods
282
283- (void)onOpen {
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000284 if (!self.client.initiator) {
285 [self displayLogMessage:@"Callee; waiting for remote offer"];
286 return;
287 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288 [self displayLogMessage:@"GAE onOpen - create offer."];
289 RTCPair *audio =
290 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000291 RTCPair *video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo"
292 value:@"true"];
293 NSArray *mandatory = @[ audio , video ];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294 RTCMediaConstraints *constraints =
295 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory
296 optionalConstraints:nil];
297 [self.peerConnection createOfferWithDelegate:self constraints:constraints];
298 [self displayLogMessage:@"PC - createOffer."];
299}
300
301- (void)onMessage:(NSString *)data {
302 NSString *message = [self unHTMLifyString:data];
303 NSError *error;
304 NSDictionary *objects = [NSJSONSerialization
305 JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
306 options:0
307 error:&error];
308 NSAssert(!error,
309 @"%@",
310 [NSString stringWithFormat:@"Error: %@", error.description]);
311 NSAssert([objects count] > 0, @"Invalid JSON object");
312 NSString *value = [objects objectForKey:@"type"];
313 [self displayLogMessage:
314 [NSString stringWithFormat:@"GAE onMessage type - %@", value]];
315 if ([value compare:@"candidate"] == NSOrderedSame) {
316 NSString *mid = [objects objectForKey:@"id"];
317 NSNumber *sdpLineIndex = [objects objectForKey:@"label"];
318 NSString *sdp = [objects objectForKey:@"candidate"];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000319 RTCICECandidate *candidate =
320 [[RTCICECandidate alloc] initWithMid:mid
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321 index:sdpLineIndex.intValue
322 sdp:sdp];
323 if (self.queuedRemoteCandidates) {
324 [self.queuedRemoteCandidates addObject:candidate];
325 } else {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000326 [self.peerConnection addICECandidate:candidate];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327 }
328 } else if (([value compare:@"offer"] == NSOrderedSame) ||
329 ([value compare:@"answer"] == NSOrderedSame)) {
330 NSString *sdpString = [objects objectForKey:@"sdp"];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000331 RTCSessionDescription *sdp = [[RTCSessionDescription alloc]
332 initWithType:value sdp:[APPRTCAppDelegate preferISAC:sdpString]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000333 [self.peerConnection setRemoteDescriptionWithDelegate:self
334 sessionDescription:sdp];
335 [self displayLogMessage:@"PC - setRemoteDescription."];
336 } else if ([value compare:@"bye"] == NSOrderedSame) {
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000337 [self closeVideoUI];
338 UIAlertView *alertView =
339 [[UIAlertView alloc] initWithTitle:@"Remote end hung up"
340 message:@"dropping PeerConnection"
341 delegate:nil
342 cancelButtonTitle:@"OK"
343 otherButtonTitles:nil];
344 [alertView show];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000345 } else {
346 NSAssert(NO, @"Invalid message: %@", data);
347 }
348}
349
350- (void)onClose {
351 [self displayLogMessage:@"GAE onClose."];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000352 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000353}
354
355- (void)onError:(int)code withDescription:(NSString *)description {
356 [self displayLogMessage:
357 [NSString stringWithFormat:@"GAE onError: %@", description]];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000358 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000359}
360
361#pragma mark - RTCSessionDescriptonDelegate methods
362
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000363// Match |pattern| to |string| and return the first group of the first
364// match, or nil if no match was found.
365+ (NSString *)firstMatch:(NSRegularExpression *)pattern
366 withString:(NSString *)string {
367 NSTextCheckingResult* result =
368 [pattern firstMatchInString:string
369 options:0
370 range:NSMakeRange(0, [string length])];
371 if (!result)
372 return nil;
373 return [string substringWithRange:[result rangeAtIndex:1]];
374}
375
376// Mangle |origSDP| to prefer the ISAC/16k audio codec.
377+ (NSString *)preferISAC:(NSString *)origSDP {
378 int mLineIndex = -1;
379 NSString* isac16kRtpMap = nil;
380 NSArray* lines = [origSDP componentsSeparatedByString:@"\n"];
381 NSRegularExpression* isac16kRegex = [NSRegularExpression
382 regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$"
383 options:0
384 error:nil];
385 for (int i = 0;
386 (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil);
387 ++i) {
388 NSString* line = [lines objectAtIndex:i];
389 if ([line hasPrefix:@"m=audio "]) {
390 mLineIndex = i;
391 continue;
392 }
393 isac16kRtpMap = [self firstMatch:isac16kRegex withString:line];
394 }
395 if (mLineIndex == -1) {
396 NSLog(@"No m=audio line, so can't prefer iSAC");
397 return origSDP;
398 }
399 if (isac16kRtpMap == nil) {
400 NSLog(@"No ISAC/16000 line, so can't prefer iSAC");
401 return origSDP;
402 }
403 NSArray* origMLineParts =
404 [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "];
405 NSMutableArray* newMLine =
406 [NSMutableArray arrayWithCapacity:[origMLineParts count]];
407 int origPartIndex = 0;
408 // Format is: m=<media> <port> <proto> <fmt> ...
409 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
410 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
411 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
412 [newMLine addObject:isac16kRtpMap];
413 for (; origPartIndex < [origMLineParts count]; ++origPartIndex) {
414 if ([isac16kRtpMap compare:[origMLineParts objectAtIndex:origPartIndex]]
415 != NSOrderedSame) {
416 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]];
417 }
418 }
419 NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]];
420 [newLines addObjectsFromArray:lines];
421 [newLines replaceObjectAtIndex:mLineIndex
422 withObject:[newMLine componentsJoinedByString:@" "]];
423 return [newLines componentsJoinedByString:@"\n"];
424}
425
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000426- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000427 didCreateSessionDescription:(RTCSessionDescription *)origSdp
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000428 error:(NSError *)error {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000429 if (error) {
430 [self displayLogMessage:@"SDP onFailure."];
431 NSAssert(NO, error.description);
432 return;
433 }
434
435 [self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000436 RTCSessionDescription* sdp =
437 [[RTCSessionDescription alloc]
438 initWithType:origSdp.type
439 sdp:[APPRTCAppDelegate preferISAC:origSdp.description]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000440 [self.peerConnection setLocalDescriptionWithDelegate:self
441 sessionDescription:sdp];
442 [self displayLogMessage:@"PC setLocalDescription."];
443 dispatch_async(dispatch_get_main_queue(), ^(void) {
444 NSDictionary *json = @{ @"type" : sdp.type, @"sdp" : sdp.description };
445 NSError *error;
446 NSData *data =
447 [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
448 NSAssert(!error,
449 @"%@",
450 [NSString stringWithFormat:@"Error: %@", error.description]);
451 [self sendData:data];
452 });
453}
454
455- (void)peerConnection:(RTCPeerConnection *)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000456 didSetSessionDescriptionWithError:(NSError *)error {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000457 if (error) {
458 [self displayLogMessage:@"SDP onFailure."];
459 NSAssert(NO, error.description);
460 return;
461 }
462
463 [self displayLogMessage:@"SDP onSuccess() - possibly drain candidates"];
464 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000465 if (!self.client.initiator) {
466 if (self.peerConnection.remoteDescription
467 && !self.peerConnection.localDescription) {
468 [self displayLogMessage:@"Callee, setRemoteDescription succeeded"];
469 RTCPair *audio =
470 [[RTCPair alloc]
471 initWithKey:@"OfferToReceiveAudio" value:@"true"];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000472 RTCPair *video =
473 [[RTCPair alloc]
474 initWithKey:@"OfferToReceiveVideo" value:@"true"];
475 NSArray *mandatory = @[ audio , video ];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000476 RTCMediaConstraints *constraints =
477 [[RTCMediaConstraints alloc]
478 initWithMandatoryConstraints:mandatory
479 optionalConstraints:nil];
480 [self.peerConnection
481 createAnswerWithDelegate:self constraints:constraints];
482 [self displayLogMessage:@"PC - createAnswer."];
483 } else {
484 [self displayLogMessage:@"SDP onSuccess - drain candidates"];
485 [self drainRemoteCandidates];
486 }
487 } else {
488 if (self.peerConnection.remoteDescription) {
489 [self displayLogMessage:@"SDP onSuccess - drain candidates"];
490 [self drainRemoteCandidates];
491 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000492 }
493 });
494}
495
496#pragma mark - internal methods
497
498- (void)disconnect {
499 [self.client
500 sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000501 [self.peerConnection close];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502 self.peerConnection = nil;
503 self.peerConnectionFactory = nil;
504 self.pcObserver = nil;
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000505 self.client.ICEServerDelegate = nil;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000506 self.client.messageHandler = nil;
507 self.client = nil;
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000508 [RTCPeerConnectionFactory deinitializeSSL];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000509}
510
511- (void)drainRemoteCandidates {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000512 for (RTCICECandidate *candidate in self.queuedRemoteCandidates) {
513 [self.peerConnection addICECandidate:candidate];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000514 }
515 self.queuedRemoteCandidates = nil;
516}
517
518- (NSString *)unHTMLifyString:(NSString *)base {
519 // TODO(hughv): Investigate why percent escapes are being added. Removing
520 // them isn't necessary on Android.
521 // convert HTML escaped characters to UTF8.
522 NSString *removePercent =
523 [base stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
524 // remove leading and trailing ".
525 NSRange range;
526 range.length = [removePercent length] - 2;
527 range.location = 1;
528 NSString *removeQuotes = [removePercent substringWithRange:range];
529 // convert \" to ".
530 NSString *removeEscapedQuotes =
531 [removeQuotes stringByReplacingOccurrencesOfString:@"\\\""
532 withString:@"\""];
533 // convert \\ to \.
534 NSString *removeBackslash =
535 [removeEscapedQuotes stringByReplacingOccurrencesOfString:@"\\\\"
536 withString:@"\\"];
537 return removeBackslash;
538}
539
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000540#pragma mark - public methods
541
542- (void)closeVideoUI {
543 [self disconnect];
544 [self.viewController resetUI];
545}
546
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000547@end