blob: 8400778ac1b8ee11376a45f8b15a2ccc8a3b1f1b [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"
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000045#import "APPRTCVideoView.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046
47@interface PCObserver : NSObject<RTCPeerConnectionDelegate>
48
49- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate;
50
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000051@property(nonatomic, strong) APPRTCVideoView* videoView;
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000052
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053@end
54
55@implementation PCObserver {
56 id<APPRTCSendMessage> _delegate;
57}
58
59- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate {
60 if (self = [super init]) {
61 _delegate = delegate;
62 }
63 return self;
64}
65
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000066- (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000067 NSLog(@"PCO onError.");
68 NSAssert(NO, @"PeerConnection failed.");
69}
70
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000071- (void)peerConnection:(RTCPeerConnection*)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000072 signalingStateChanged:(RTCSignalingState)stateChanged {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +000073 NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000074}
75
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000076- (void)peerConnection:(RTCPeerConnection*)peerConnection
77 addedStream:(RTCMediaStream*)stream {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078 NSLog(@"PCO onAddStream.");
79 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org76d4f382014-03-25 17:40:38 +000080 NSAssert([stream.audioTracks count] <= 1,
81 @"Expected at most 1 audio stream");
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000082 NSAssert([stream.videoTracks count] <= 1,
83 @"Expected at most 1 video stream");
84 if ([stream.videoTracks count] != 0) {
85 [self.videoView
86 renderVideoTrackInterface:[stream.videoTracks objectAtIndex:0]];
87 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088 });
89}
90
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000091- (void)peerConnection:(RTCPeerConnection*)peerConnection
92 removedStream:(RTCMediaStream*)stream {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000093 NSLog(@"PCO onRemoveStream.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +000094}
95
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000096- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097 NSLog(@"PCO onRenegotiationNeeded.");
98 // TODO(hughv): Handle this.
99}
100
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000101- (void)peerConnection:(RTCPeerConnection*)peerConnection
102 gotICECandidate:(RTCICECandidate*)candidate {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000103 NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%d] Sdp[%@]",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000104 candidate.sdpMid,
105 candidate.sdpMLineIndex,
106 candidate.sdp);
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000107 NSDictionary* json = @{
108 @"type" : @"candidate",
109 @"label" : [NSNumber numberWithInt:candidate.sdpMLineIndex],
110 @"id" : candidate.sdpMid,
111 @"candidate" : candidate.sdp
112 };
113 NSError* error;
114 NSData* data =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115 [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
116 if (!error) {
117 [_delegate sendData:data];
118 } else {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000119 NSAssert(NO,
120 @"Unable to serialize JSON object with error: %@",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121 error.localizedDescription);
122 }
123}
124
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000125- (void)peerConnection:(RTCPeerConnection*)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000126 iceGatheringChanged:(RTCICEGatheringState)newState {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127 NSLog(@"PCO onIceGatheringChange. %d", newState);
128}
129
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000130- (void)peerConnection:(RTCPeerConnection*)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000131 iceConnectionChanged:(RTCICEConnectionState)newState {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132 NSLog(@"PCO onIceConnectionChange. %d", newState);
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000133 if (newState == RTCICEConnectionConnected)
134 [self displayLogMessage:@"ICE Connection Connected."];
135 NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!");
136}
137
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000138- (void)displayLogMessage:(NSString*)message {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000139 [_delegate displayLogMessage:message];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000140}
141
142@end
143
144@interface APPRTCAppDelegate ()
145
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000146@property(nonatomic, strong) APPRTCAppClient* client;
147@property(nonatomic, strong) PCObserver* pcObserver;
148@property(nonatomic, strong) RTCPeerConnection* peerConnection;
149@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory;
150@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151
152@end
153
154@implementation APPRTCAppDelegate
155
156#pragma mark - UIApplicationDelegate methods
157
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000158- (BOOL)application:(UIApplication*)application
159 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000160 [RTCPeerConnectionFactory initializeSSL];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000161 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
162 self.viewController =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000163 [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController"
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164 bundle:nil];
165 self.window.rootViewController = self.viewController;
166 [self.window makeKeyAndVisible];
167 return YES;
168}
169
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000170- (void)applicationWillResignActive:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000171 [self displayLogMessage:@"Application lost focus, connection broken."];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000172 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000173}
174
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000175- (void)applicationDidEnterBackground:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000176}
177
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000178- (void)applicationWillEnterForeground:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000179}
180
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000181- (void)applicationDidBecomeActive:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000182}
183
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000184- (void)applicationWillTerminate:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000185}
186
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000187- (BOOL)application:(UIApplication*)application
188 openURL:(NSURL*)url
189 sourceApplication:(NSString*)sourceApplication
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190 annotation:(id)annotation {
191 if (self.client) {
192 return NO;
193 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000194 self.client = [[APPRTCAppClient alloc] initWithICEServerDelegate:self
195 messageHandler:self];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000196 [self.client connectToRoom:url];
197 return YES;
198}
199
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000200- (void)displayLogMessage:(NSString*)message {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000201 NSLog(@"%@", message);
202 [self.viewController displayText:message];
203}
204
205#pragma mark - RTCSendMessage method
206
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000207- (void)sendData:(NSData*)data {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208 [self.client sendData:data];
209}
210
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000211#pragma mark - ICEServerDelegate method
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000212
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000213- (void)onICEServers:(NSArray*)servers {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214 self.queuedRemoteCandidates = [NSMutableArray array];
215 self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000216 RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
217 initWithMandatoryConstraints:
218 @[
219 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
220 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
221 ]
222 optionalConstraints:
223 @[
224 [[RTCPair alloc] initWithKey:@"internalSctpDataChannels"
225 value:@"true"],
226 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement"
227 value:@"true"]
228 ]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229 self.pcObserver = [[PCObserver alloc] initWithDelegate:self];
230 self.peerConnection =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000231 [self.peerConnectionFactory peerConnectionWithICEServers:servers
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232 constraints:constraints
233 delegate:self.pcObserver];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000234 RTCMediaStream* lms =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000235 [self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000236
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000237 NSString* cameraID = nil;
238 for (AVCaptureDevice* captureDevice in
239 [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
240 if (captureDevice.position == AVCaptureDevicePositionFront) {
241 cameraID = [captureDevice localizedName];
242 break;
243 }
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000244 }
245 NSAssert(cameraID, @"Unable to get the front camera id");
246
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000247 RTCVideoCapturer* capturer =
248 [RTCVideoCapturer capturerWithDeviceName:cameraID];
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000249 self.videoSource = [self.peerConnectionFactory
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000250 videoSourceWithCapturer:capturer
251 constraints:self.client.videoConstraints];
252 RTCVideoTrack* localVideoTrack =
253 [self.peerConnectionFactory videoTrackWithID:@"ARDAMSv0"
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000254 source:self.videoSource];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000255 if (localVideoTrack) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000256 [lms addVideoTrack:localVideoTrack];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000257 }
258
259 [self.viewController.localVideoView
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000260 renderVideoTrackInterface:localVideoTrack];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000261
262 self.pcObserver.videoView = self.viewController.remoteVideoView;
263
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000264 [lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]];
265 [self.peerConnection addStream:lms constraints:constraints];
266 [self displayLogMessage:@"onICEServers - add local stream."];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000267}
268
269#pragma mark - GAEMessageHandler methods
270
271- (void)onOpen {
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000272 if (!self.client.initiator) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000273 [self displayLogMessage:@"Callee; waiting for remote offer"];
274 return;
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000275 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 [self displayLogMessage:@"GAE onOpen - create offer."];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000277 RTCPair* audio =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000279 RTCPair* video =
280 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"];
281 NSArray* mandatory = @[ audio, video ];
282 RTCMediaConstraints* constraints =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory
284 optionalConstraints:nil];
285 [self.peerConnection createOfferWithDelegate:self constraints:constraints];
286 [self displayLogMessage:@"PC - createOffer."];
287}
288
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000289- (void)onMessage:(NSString*)data {
290 NSString* message = [self unHTMLifyString:data];
291 NSError* error;
292 NSDictionary* objects = [NSJSONSerialization
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000293 JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
294 options:0
295 error:&error];
296 NSAssert(!error,
297 @"%@",
298 [NSString stringWithFormat:@"Error: %@", error.description]);
299 NSAssert([objects count] > 0, @"Invalid JSON object");
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000300 NSString* value = [objects objectForKey:@"type"];
301 [self displayLogMessage:[NSString stringWithFormat:@"GAE onMessage type - %@",
302 value]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 if ([value compare:@"candidate"] == NSOrderedSame) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000304 NSString* mid = [objects objectForKey:@"id"];
305 NSNumber* sdpLineIndex = [objects objectForKey:@"label"];
306 NSString* sdp = [objects objectForKey:@"candidate"];
307 RTCICECandidate* candidate =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000308 [[RTCICECandidate alloc] initWithMid:mid
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309 index:sdpLineIndex.intValue
310 sdp:sdp];
311 if (self.queuedRemoteCandidates) {
312 [self.queuedRemoteCandidates addObject:candidate];
313 } else {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000314 [self.peerConnection addICECandidate:candidate];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315 }
316 } else if (([value compare:@"offer"] == NSOrderedSame) ||
317 ([value compare:@"answer"] == NSOrderedSame)) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000318 NSString* sdpString = [objects objectForKey:@"sdp"];
319 RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
320 initWithType:value
321 sdp:[APPRTCAppDelegate preferISAC:sdpString]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322 [self.peerConnection setRemoteDescriptionWithDelegate:self
323 sessionDescription:sdp];
324 [self displayLogMessage:@"PC - setRemoteDescription."];
325 } else if ([value compare:@"bye"] == NSOrderedSame) {
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000326 [self closeVideoUI];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000327 UIAlertView* alertView =
328 [[UIAlertView alloc] initWithTitle:@"Remote end hung up"
329 message:@"dropping PeerConnection"
330 delegate:nil
331 cancelButtonTitle:@"OK"
332 otherButtonTitles:nil];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000333 [alertView show];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334 } else {
335 NSAssert(NO, @"Invalid message: %@", data);
336 }
337}
338
339- (void)onClose {
340 [self displayLogMessage:@"GAE onClose."];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000341 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342}
343
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000344- (void)onError:(int)code withDescription:(NSString*)description {
345 [self displayLogMessage:[NSString stringWithFormat:@"GAE onError: %@",
346 description]];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000347 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348}
349
350#pragma mark - RTCSessionDescriptonDelegate methods
351
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000352// Match |pattern| to |string| and return the first group of the first
353// match, or nil if no match was found.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000354+ (NSString*)firstMatch:(NSRegularExpression*)pattern
355 withString:(NSString*)string {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000356 NSTextCheckingResult* result =
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000357 [pattern firstMatchInString:string
358 options:0
359 range:NSMakeRange(0, [string length])];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000360 if (!result)
361 return nil;
362 return [string substringWithRange:[result rangeAtIndex:1]];
363}
364
365// Mangle |origSDP| to prefer the ISAC/16k audio codec.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000366+ (NSString*)preferISAC:(NSString*)origSDP {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000367 int mLineIndex = -1;
368 NSString* isac16kRtpMap = nil;
369 NSArray* lines = [origSDP componentsSeparatedByString:@"\n"];
370 NSRegularExpression* isac16kRegex = [NSRegularExpression
371 regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$"
372 options:0
373 error:nil];
374 for (int i = 0;
375 (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil);
376 ++i) {
377 NSString* line = [lines objectAtIndex:i];
378 if ([line hasPrefix:@"m=audio "]) {
379 mLineIndex = i;
380 continue;
381 }
382 isac16kRtpMap = [self firstMatch:isac16kRegex withString:line];
383 }
384 if (mLineIndex == -1) {
385 NSLog(@"No m=audio line, so can't prefer iSAC");
386 return origSDP;
387 }
388 if (isac16kRtpMap == nil) {
389 NSLog(@"No ISAC/16000 line, so can't prefer iSAC");
390 return origSDP;
391 }
392 NSArray* origMLineParts =
393 [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "];
394 NSMutableArray* newMLine =
395 [NSMutableArray arrayWithCapacity:[origMLineParts count]];
396 int origPartIndex = 0;
397 // Format is: m=<media> <port> <proto> <fmt> ...
398 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
399 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
400 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
401 [newMLine addObject:isac16kRtpMap];
402 for (; origPartIndex < [origMLineParts count]; ++origPartIndex) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000403 if ([isac16kRtpMap compare:[origMLineParts objectAtIndex:origPartIndex]] !=
404 NSOrderedSame) {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000405 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]];
406 }
407 }
408 NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]];
409 [newLines addObjectsFromArray:lines];
410 [newLines replaceObjectAtIndex:mLineIndex
411 withObject:[newMLine componentsJoinedByString:@" "]];
412 return [newLines componentsJoinedByString:@"\n"];
413}
414
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000415- (void)peerConnection:(RTCPeerConnection*)peerConnection
416 didCreateSessionDescription:(RTCSessionDescription*)origSdp
417 error:(NSError*)error {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000418 if (error) {
419 [self displayLogMessage:@"SDP onFailure."];
420 NSAssert(NO, error.description);
421 return;
422 }
423
424 [self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000425 RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
426 initWithType:origSdp.type
427 sdp:[APPRTCAppDelegate preferISAC:origSdp.description]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000428 [self.peerConnection setLocalDescriptionWithDelegate:self
429 sessionDescription:sdp];
430 [self displayLogMessage:@"PC setLocalDescription."];
431 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000432 NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description};
433 NSError* error;
434 NSData* data =
435 [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
436 NSAssert(!error,
437 @"%@",
438 [NSString stringWithFormat:@"Error: %@", error.description]);
439 [self sendData:data];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000440 });
441}
442
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000443- (void)peerConnection:(RTCPeerConnection*)peerConnection
444 didSetSessionDescriptionWithError:(NSError*)error {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000445 if (error) {
446 [self displayLogMessage:@"SDP onFailure."];
447 NSAssert(NO, error.description);
448 return;
449 }
450
451 [self displayLogMessage:@"SDP onSuccess() - possibly drain candidates"];
452 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000453 if (!self.client.initiator) {
454 if (self.peerConnection.remoteDescription &&
455 !self.peerConnection.localDescription) {
456 [self displayLogMessage:@"Callee, setRemoteDescription succeeded"];
457 RTCPair* audio = [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio"
458 value:@"true"];
459 RTCPair* video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo"
460 value:@"true"];
461 NSArray* mandatory = @[ audio, video ];
462 RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
463 initWithMandatoryConstraints:mandatory
464 optionalConstraints:nil];
465 [self.peerConnection createAnswerWithDelegate:self
466 constraints:constraints];
467 [self displayLogMessage:@"PC - createAnswer."];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000468 } else {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000469 [self displayLogMessage:@"SDP onSuccess - drain candidates"];
470 [self drainRemoteCandidates];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000471 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000472 } else {
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000473 if (self.peerConnection.remoteDescription) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000474 [self displayLogMessage:@"SDP onSuccess - drain candidates"];
475 [self drainRemoteCandidates];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000476 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000477 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000478 });
479}
480
481#pragma mark - internal methods
482
483- (void)disconnect {
484 [self.client
485 sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000486 [self.peerConnection close];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000487 self.peerConnection = nil;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000488 self.pcObserver = nil;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000489 self.client = nil;
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000490 self.videoSource = nil;
491 self.peerConnectionFactory = nil;
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000492 [RTCPeerConnectionFactory deinitializeSSL];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493}
494
495- (void)drainRemoteCandidates {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000496 for (RTCICECandidate* candidate in self.queuedRemoteCandidates) {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000497 [self.peerConnection addICECandidate:candidate];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498 }
499 self.queuedRemoteCandidates = nil;
500}
501
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000502- (NSString*)unHTMLifyString:(NSString*)base {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000503 // TODO(hughv): Investigate why percent escapes are being added. Removing
504 // them isn't necessary on Android.
505 // convert HTML escaped characters to UTF8.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000506 NSString* removePercent =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000507 [base stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
508 // remove leading and trailing ".
509 NSRange range;
510 range.length = [removePercent length] - 2;
511 range.location = 1;
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000512 NSString* removeQuotes = [removePercent substringWithRange:range];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000513 // convert \" to ".
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000514 NSString* removeEscapedQuotes =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000515 [removeQuotes stringByReplacingOccurrencesOfString:@"\\\""
516 withString:@"\""];
517 // convert \\ to \.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000518 NSString* removeBackslash =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000519 [removeEscapedQuotes stringByReplacingOccurrencesOfString:@"\\\\"
520 withString:@"\\"];
521 return removeBackslash;
522}
523
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000524#pragma mark - public methods
525
526- (void)closeVideoUI {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000527 [self.viewController resetUI];
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000528 [self disconnect];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000529}
530
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531@end