blob: e8077b9620ab6225b62b8042cd6a0925d9f313bd [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 {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +000067 dispatch_async(dispatch_get_main_queue(), ^(void) {
68 NSLog(@"PCO onError.");
69 NSAssert(NO, @"PeerConnection failed.");
70 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +000071}
72
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000073- (void)peerConnection:(RTCPeerConnection*)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000074 signalingStateChanged:(RTCSignalingState)stateChanged {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +000075 dispatch_async(dispatch_get_main_queue(), ^(void) {
76 NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
77 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078}
79
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000080- (void)peerConnection:(RTCPeerConnection*)peerConnection
81 addedStream:(RTCMediaStream*)stream {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000082 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +000083 NSLog(@"PCO onAddStream.");
fischman@webrtc.org76d4f382014-03-25 17:40:38 +000084 NSAssert([stream.audioTracks count] <= 1,
85 @"Expected at most 1 audio stream");
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000086 NSAssert([stream.videoTracks count] <= 1,
87 @"Expected at most 1 video stream");
88 if ([stream.videoTracks count] != 0) {
89 [self.videoView
90 renderVideoTrackInterface:[stream.videoTracks objectAtIndex:0]];
91 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092 });
93}
94
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000095- (void)peerConnection:(RTCPeerConnection*)peerConnection
96 removedStream:(RTCMediaStream*)stream {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +000097 dispatch_async(dispatch_get_main_queue(),
98 ^(void) { NSLog(@"PCO onRemoveStream."); });
henrike@webrtc.org28e20752013-07-10 00:45:36 +000099}
100
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000101- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000102 dispatch_async(dispatch_get_main_queue(), ^(void) {
103 NSLog(@"PCO onRenegotiationNeeded - ignoring because AppRTC has a "
104 "predefined negotiation strategy");
105 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000106}
107
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000108- (void)peerConnection:(RTCPeerConnection*)peerConnection
109 gotICECandidate:(RTCICECandidate*)candidate {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000110 dispatch_async(dispatch_get_main_queue(), ^(void) {
111 NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%d] Sdp[%@]",
112 candidate.sdpMid,
113 candidate.sdpMLineIndex,
114 candidate.sdp);
115 NSDictionary* json = @{
116 @"type" : @"candidate",
117 @"label" : [NSNumber numberWithInt:candidate.sdpMLineIndex],
118 @"id" : candidate.sdpMid,
119 @"candidate" : candidate.sdp
120 };
121 NSError* error;
122 NSData* data =
123 [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
124 if (!error) {
125 [_delegate sendData:data];
126 } else {
127 NSAssert(NO,
128 @"Unable to serialize JSON object with error: %@",
129 error.localizedDescription);
130 }
131 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132}
133
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000134- (void)peerConnection:(RTCPeerConnection*)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000135 iceGatheringChanged:(RTCICEGatheringState)newState {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000136 dispatch_async(dispatch_get_main_queue(),
137 ^(void) { NSLog(@"PCO onIceGatheringChange. %d", newState); });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138}
139
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000140- (void)peerConnection:(RTCPeerConnection*)peerConnection
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000141 iceConnectionChanged:(RTCICEConnectionState)newState {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000142 dispatch_async(dispatch_get_main_queue(), ^(void) {
143 NSLog(@"PCO onIceConnectionChange. %d", newState);
144 if (newState == RTCICEConnectionConnected)
145 [self displayLogMessage:@"ICE Connection Connected."];
146 NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!");
147 });
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000148}
149
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000150- (void)displayLogMessage:(NSString*)message {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000151 [_delegate displayLogMessage:message];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000152}
153
154@end
155
156@interface APPRTCAppDelegate ()
157
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000158@property(nonatomic, strong) APPRTCAppClient* client;
159@property(nonatomic, strong) PCObserver* pcObserver;
160@property(nonatomic, strong) RTCPeerConnection* peerConnection;
161@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory;
162@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163
164@end
165
166@implementation APPRTCAppDelegate
167
168#pragma mark - UIApplicationDelegate methods
169
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000170- (BOOL)application:(UIApplication*)application
171 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000172 [RTCPeerConnectionFactory initializeSSL];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000173 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
174 self.viewController =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000175 [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController"
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000176 bundle:nil];
177 self.window.rootViewController = self.viewController;
178 [self.window makeKeyAndVisible];
179 return YES;
180}
181
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000182- (void)applicationWillResignActive:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183 [self displayLogMessage:@"Application lost focus, connection broken."];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000184 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000185}
186
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000187- (void)applicationDidEnterBackground:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000188}
189
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000190- (void)applicationWillEnterForeground:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000191}
192
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000193- (void)applicationDidBecomeActive:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194}
195
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000196- (void)applicationWillTerminate:(UIApplication*)application {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197}
198
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000199- (BOOL)application:(UIApplication*)application
200 openURL:(NSURL*)url
201 sourceApplication:(NSString*)sourceApplication
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202 annotation:(id)annotation {
203 if (self.client) {
204 return NO;
205 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000206 self.client = [[APPRTCAppClient alloc] initWithICEServerDelegate:self
207 messageHandler:self];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208 [self.client connectToRoom:url];
209 return YES;
210}
211
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000212- (void)displayLogMessage:(NSString*)message {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000213 NSAssert([NSThread isMainThread], @"Called off main thread!");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214 NSLog(@"%@", message);
215 [self.viewController displayText:message];
216}
217
218#pragma mark - RTCSendMessage method
219
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000220- (void)sendData:(NSData*)data {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000221 [self.client sendData:data];
222}
223
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000224#pragma mark - ICEServerDelegate method
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000226- (void)onICEServers:(NSArray*)servers {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227 self.queuedRemoteCandidates = [NSMutableArray array];
228 self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000229 RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
230 initWithMandatoryConstraints:
231 @[
232 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
233 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
234 ]
235 optionalConstraints:
236 @[
237 [[RTCPair alloc] initWithKey:@"internalSctpDataChannels"
238 value:@"true"],
239 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement"
240 value:@"true"]
241 ]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242 self.pcObserver = [[PCObserver alloc] initWithDelegate:self];
243 self.peerConnection =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000244 [self.peerConnectionFactory peerConnectionWithICEServers:servers
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000245 constraints:constraints
246 delegate:self.pcObserver];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000247 RTCMediaStream* lms =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000248 [self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000249
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000250 NSString* cameraID = nil;
251 for (AVCaptureDevice* captureDevice in
252 [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
253 if (captureDevice.position == AVCaptureDevicePositionFront) {
254 cameraID = [captureDevice localizedName];
255 break;
256 }
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000257 }
258 NSAssert(cameraID, @"Unable to get the front camera id");
259
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000260 RTCVideoCapturer* capturer =
261 [RTCVideoCapturer capturerWithDeviceName:cameraID];
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000262 self.videoSource = [self.peerConnectionFactory
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000263 videoSourceWithCapturer:capturer
264 constraints:self.client.videoConstraints];
265 RTCVideoTrack* localVideoTrack =
266 [self.peerConnectionFactory videoTrackWithID:@"ARDAMSv0"
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000267 source:self.videoSource];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000268 if (localVideoTrack) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000269 [lms addVideoTrack:localVideoTrack];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000270 }
271
272 [self.viewController.localVideoView
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000273 renderVideoTrackInterface:localVideoTrack];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000274
275 self.pcObserver.videoView = self.viewController.remoteVideoView;
276
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000277 [lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]];
278 [self.peerConnection addStream:lms constraints:constraints];
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000279 [self displayLogMessage:@"onICEServers - added local stream."];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280}
281
282#pragma mark - GAEMessageHandler methods
283
284- (void)onOpen {
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000285 if (!self.client.initiator) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000286 [self displayLogMessage:@"Callee; waiting for remote offer"];
287 return;
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000288 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 [self displayLogMessage:@"GAE onOpen - create offer."];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000290 RTCPair* audio =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000292 RTCPair* video =
293 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"];
294 NSArray* mandatory = @[ audio, video ];
295 RTCMediaConstraints* constraints =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory
297 optionalConstraints:nil];
298 [self.peerConnection createOfferWithDelegate:self constraints:constraints];
299 [self displayLogMessage:@"PC - createOffer."];
300}
301
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000302- (void)onMessage:(NSDictionary*)messageData {
303 NSString* type = messageData[@"type"];
304 NSAssert(type, @"Missing type: %@", messageData);
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000305 [self displayLogMessage:[NSString stringWithFormat:@"GAE onMessage type - %@",
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000306 type]];
307 if ([type isEqualToString:@"candidate"]) {
308 NSString* mid = messageData[@"id"];
309 NSNumber* sdpLineIndex = messageData[@"label"];
310 NSString* sdp = messageData[@"candidate"];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000311 RTCICECandidate* candidate =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000312 [[RTCICECandidate alloc] initWithMid:mid
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313 index:sdpLineIndex.intValue
314 sdp:sdp];
315 if (self.queuedRemoteCandidates) {
316 [self.queuedRemoteCandidates addObject:candidate];
317 } else {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000318 [self.peerConnection addICECandidate:candidate];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000319 }
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000320 } else if ([type isEqualToString:@"offer"] ||
321 [type isEqualToString:@"answer"]) {
322 NSString* sdpString = messageData[@"sdp"];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000323 RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000324 initWithType:type
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000325 sdp:[APPRTCAppDelegate preferISAC:sdpString]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000326 [self.peerConnection setRemoteDescriptionWithDelegate:self
327 sessionDescription:sdp];
328 [self displayLogMessage:@"PC - setRemoteDescription."];
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000329 } else if ([type isEqualToString:@"bye"]) {
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000330 [self closeVideoUI];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000331 UIAlertView* alertView =
332 [[UIAlertView alloc] initWithTitle:@"Remote end hung up"
333 message:@"dropping PeerConnection"
334 delegate:nil
335 cancelButtonTitle:@"OK"
336 otherButtonTitles:nil];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000337 [alertView show];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000338 } else {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000339 NSAssert(NO, @"Invalid message: %@", messageData);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340 }
341}
342
343- (void)onClose {
344 [self displayLogMessage:@"GAE onClose."];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000345 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000346}
347
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000348- (void)onError:(int)code withDescription:(NSString*)description {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000349 [self displayLogMessage:[NSString stringWithFormat:@"GAE onError: %d, %@",
350 code, description]];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000351 [self closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000352}
353
354#pragma mark - RTCSessionDescriptonDelegate methods
355
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000356// Match |pattern| to |string| and return the first group of the first
357// match, or nil if no match was found.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000358+ (NSString*)firstMatch:(NSRegularExpression*)pattern
359 withString:(NSString*)string {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000360 NSTextCheckingResult* result =
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000361 [pattern firstMatchInString:string
362 options:0
363 range:NSMakeRange(0, [string length])];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000364 if (!result)
365 return nil;
366 return [string substringWithRange:[result rangeAtIndex:1]];
367}
368
369// Mangle |origSDP| to prefer the ISAC/16k audio codec.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000370+ (NSString*)preferISAC:(NSString*)origSDP {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000371 int mLineIndex = -1;
372 NSString* isac16kRtpMap = nil;
373 NSArray* lines = [origSDP componentsSeparatedByString:@"\n"];
374 NSRegularExpression* isac16kRegex = [NSRegularExpression
375 regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$"
376 options:0
377 error:nil];
378 for (int i = 0;
379 (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil);
380 ++i) {
381 NSString* line = [lines objectAtIndex:i];
382 if ([line hasPrefix:@"m=audio "]) {
383 mLineIndex = i;
384 continue;
385 }
386 isac16kRtpMap = [self firstMatch:isac16kRegex withString:line];
387 }
388 if (mLineIndex == -1) {
389 NSLog(@"No m=audio line, so can't prefer iSAC");
390 return origSDP;
391 }
392 if (isac16kRtpMap == nil) {
393 NSLog(@"No ISAC/16000 line, so can't prefer iSAC");
394 return origSDP;
395 }
396 NSArray* origMLineParts =
397 [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "];
398 NSMutableArray* newMLine =
399 [NSMutableArray arrayWithCapacity:[origMLineParts count]];
400 int origPartIndex = 0;
401 // Format is: m=<media> <port> <proto> <fmt> ...
402 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
403 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
404 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
405 [newMLine addObject:isac16kRtpMap];
406 for (; origPartIndex < [origMLineParts count]; ++origPartIndex) {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000407 if (![isac16kRtpMap
408 isEqualToString:[origMLineParts objectAtIndex:origPartIndex]]) {
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000409 [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]];
410 }
411 }
412 NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]];
413 [newLines addObjectsFromArray:lines];
414 [newLines replaceObjectAtIndex:mLineIndex
415 withObject:[newMLine componentsJoinedByString:@" "]];
416 return [newLines componentsJoinedByString:@"\n"];
417}
418
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000419- (void)peerConnection:(RTCPeerConnection*)peerConnection
420 didCreateSessionDescription:(RTCSessionDescription*)origSdp
421 error:(NSError*)error {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000422 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000423 if (error) {
424 [self displayLogMessage:@"SDP onFailure."];
425 NSAssert(NO, error.description);
426 return;
427 }
428 [self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
429 RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
430 initWithType:origSdp.type
431 sdp:[APPRTCAppDelegate preferISAC:origSdp.description]];
432 [self.peerConnection setLocalDescriptionWithDelegate:self
433 sessionDescription:sdp];
434
435 [self displayLogMessage:@"PC setLocalDescription."];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000436 NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description};
437 NSError* error;
438 NSData* data =
439 [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
440 NSAssert(!error,
441 @"%@",
442 [NSString stringWithFormat:@"Error: %@", error.description]);
443 [self sendData:data];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000444 });
445}
446
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000447- (void)peerConnection:(RTCPeerConnection*)peerConnection
448 didSetSessionDescriptionWithError:(NSError*)error {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000449 dispatch_async(dispatch_get_main_queue(), ^(void) {
fischman@webrtc.org61e78fc2014-03-31 20:16:49 +0000450 if (error) {
451 [self displayLogMessage:@"SDP onFailure."];
452 NSAssert(NO, error.description);
453 return;
454 }
455
456 [self displayLogMessage:@"SDP onSuccess() - possibly drain candidates"];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000457 if (!self.client.initiator) {
458 if (self.peerConnection.remoteDescription &&
459 !self.peerConnection.localDescription) {
460 [self displayLogMessage:@"Callee, setRemoteDescription succeeded"];
461 RTCPair* audio = [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio"
462 value:@"true"];
463 RTCPair* video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo"
464 value:@"true"];
465 NSArray* mandatory = @[ audio, video ];
466 RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
467 initWithMandatoryConstraints:mandatory
468 optionalConstraints:nil];
469 [self.peerConnection createAnswerWithDelegate:self
470 constraints:constraints];
471 [self displayLogMessage:@"PC - createAnswer."];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000472 } else {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000473 [self displayLogMessage:@"SDP onSuccess - drain candidates"];
474 [self drainRemoteCandidates];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000475 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000476 } else {
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000477 if (self.peerConnection.remoteDescription) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000478 [self displayLogMessage:@"SDP onSuccess - drain candidates"];
479 [self drainRemoteCandidates];
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000480 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000481 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482 });
483}
484
485#pragma mark - internal methods
486
487- (void)disconnect {
488 [self.client
489 sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000490 [self.peerConnection close];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000491 self.peerConnection = nil;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000492 self.pcObserver = nil;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493 self.client = nil;
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000494 self.videoSource = nil;
495 self.peerConnectionFactory = nil;
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000496 [RTCPeerConnectionFactory deinitializeSSL];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000497}
498
499- (void)drainRemoteCandidates {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000500 for (RTCICECandidate* candidate in self.queuedRemoteCandidates) {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000501 [self.peerConnection addICECandidate:candidate];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502 }
503 self.queuedRemoteCandidates = nil;
504}
505
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000506- (NSString*)unHTMLifyString:(NSString*)base {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000507 // TODO(hughv): Investigate why percent escapes are being added. Removing
508 // them isn't necessary on Android.
509 // convert HTML escaped characters to UTF8.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000510 NSString* removePercent =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000511 [base stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
512 // remove leading and trailing ".
513 NSRange range;
514 range.length = [removePercent length] - 2;
515 range.location = 1;
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000516 NSString* removeQuotes = [removePercent substringWithRange:range];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 // convert \" to ".
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000518 NSString* removeEscapedQuotes =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000519 [removeQuotes stringByReplacingOccurrencesOfString:@"\\\""
520 withString:@"\""];
521 // convert \\ to \.
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000522 NSString* removeBackslash =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000523 [removeEscapedQuotes stringByReplacingOccurrencesOfString:@"\\\\"
524 withString:@"\\"];
525 return removeBackslash;
526}
527
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000528#pragma mark - public methods
529
530- (void)closeVideoUI {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000531 [self.viewController resetUI];
fischman@webrtc.org385a7222014-03-25 05:16:29 +0000532 [self disconnect];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000533}
534
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000535@end