iAppRTCDemo: WebSocket based signaling.
Updates the iOS code to use the new signaling model. Removes old Channel API code. Note that this no longer logs messages to UI. UI update forthcoming. BUG= R=glaznev@webrtc.org, jiayl@webrtc.org Review URL: https://webrtc-codereview.appspot.com/35369004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7852 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
0babb4a4e6
commit
87776a8935
@ -1,223 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
#import "APPRTCAppClient.h"
|
||||
|
||||
#import <dispatch/dispatch.h>
|
||||
|
||||
#import "ARDSignalingParams.h"
|
||||
#import "ARDUtilities.h"
|
||||
#import "GAEChannelClient.h"
|
||||
#import "RTCICEServer.h"
|
||||
#import "RTCICEServer+JSON.h"
|
||||
#import "RTCMediaConstraints.h"
|
||||
#import "RTCPair.h"
|
||||
|
||||
@implementation APPRTCAppClient {
|
||||
dispatch_queue_t _backgroundQueue;
|
||||
GAEChannelClient* _gaeChannel;
|
||||
NSURL* _postMessageURL;
|
||||
BOOL _verboseLogging;
|
||||
__weak id<GAEMessageHandler> _messageHandler;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate
|
||||
messageHandler:(id<GAEMessageHandler>)handler {
|
||||
if (self = [super init]) {
|
||||
_delegate = delegate;
|
||||
_messageHandler = handler;
|
||||
_backgroundQueue = dispatch_queue_create("RTCBackgroundQueue",
|
||||
DISPATCH_QUEUE_SERIAL);
|
||||
// Uncomment to see Request/Response logging.
|
||||
// _verboseLogging = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)connectToRoom:(NSURL*)url {
|
||||
NSString *urlString =
|
||||
[[url absoluteString] stringByAppendingString:@"&t=json"];
|
||||
NSURL *requestURL = [NSURL URLWithString:urlString];
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:requestURL];
|
||||
[NSURLConnection sendAsynchronousRequest:request
|
||||
completionHandler:^(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
int statusCode = [httpResponse statusCode];
|
||||
[self logVerbose:[NSString stringWithFormat:
|
||||
@"Response received\nURL\n%@\nStatus [%d]\nHeaders\n%@",
|
||||
[httpResponse URL],
|
||||
statusCode,
|
||||
[httpResponse allHeaderFields]]];
|
||||
NSAssert(statusCode == 200,
|
||||
@"Invalid response of %d received while connecting to: %@",
|
||||
statusCode,
|
||||
urlString);
|
||||
if (statusCode != 200) {
|
||||
return;
|
||||
}
|
||||
[self handleResponseData:data forRoomRequest:request];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData*)data {
|
||||
NSParameterAssert([data length] > 0);
|
||||
NSString *message = [NSString stringWithUTF8String:[data bytes]];
|
||||
[self logVerbose:[NSString stringWithFormat:@"Send message:\n%@", message]];
|
||||
if (!_postMessageURL) {
|
||||
return;
|
||||
}
|
||||
NSMutableURLRequest *request =
|
||||
[NSMutableURLRequest requestWithURL:_postMessageURL];
|
||||
request.HTTPMethod = @"POST";
|
||||
[request setHTTPBody:data];
|
||||
[NSURLConnection sendAsynchronousRequest:request
|
||||
completionHandler:^(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
int status = [httpResponse statusCode];
|
||||
NSString *responseString = [data length] > 0 ?
|
||||
[NSString stringWithUTF8String:[data bytes]] :
|
||||
nil;
|
||||
NSAssert(status == 200,
|
||||
@"Bad response [%d] to message: %@\n\n%@",
|
||||
status,
|
||||
message,
|
||||
responseString);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)logVerbose:(NSString *)message {
|
||||
if (_verboseLogging) {
|
||||
NSLog(@"%@", message);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleResponseData:(NSData *)responseData
|
||||
forRoomRequest:(NSURLRequest *)request {
|
||||
ARDSignalingParams *params =
|
||||
[ARDSignalingParams paramsFromJSONData:responseData];
|
||||
if (params.errorMessages.count > 0) {
|
||||
NSMutableString *message = [NSMutableString string];
|
||||
for (NSString *errorMessage in params.errorMessages) {
|
||||
[message appendFormat:@"%@\n", errorMessage];
|
||||
}
|
||||
[self.delegate appClient:self didErrorWithMessage:message];
|
||||
return;
|
||||
}
|
||||
[self requestTURNServerForICEServers:params.iceServers
|
||||
turnServerUrl:[params.turnRequestURL absoluteString]];
|
||||
NSString *token = params.channelToken;
|
||||
[self logVerbose:
|
||||
[NSString stringWithFormat:@"About to open GAE with token: %@",
|
||||
token]];
|
||||
_gaeChannel =
|
||||
[[GAEChannelClient alloc] initWithToken:token
|
||||
delegate:_messageHandler];
|
||||
_params = params;
|
||||
// Generate URL for posting data.
|
||||
NSDictionary *roomJSON = [NSDictionary dictionaryWithJSONData:responseData];
|
||||
_postMessageURL = [self parsePostMessageURLForRoomJSON:roomJSON
|
||||
request:request];
|
||||
[self logVerbose:[NSString stringWithFormat:@"POST message URL:\n%@",
|
||||
_postMessageURL]];
|
||||
}
|
||||
|
||||
- (NSURL*)parsePostMessageURLForRoomJSON:(NSDictionary*)roomJSON
|
||||
request:(NSURLRequest*)request {
|
||||
NSString* requestUrl = [[request URL] absoluteString];
|
||||
NSRange queryRange = [requestUrl rangeOfString:@"?"];
|
||||
NSString* baseUrl = [requestUrl substringToIndex:queryRange.location];
|
||||
NSString* roomKey = roomJSON[@"room_key"];
|
||||
NSParameterAssert([roomKey length] > 0);
|
||||
NSString* me = roomJSON[@"me"];
|
||||
NSParameterAssert([me length] > 0);
|
||||
NSString* postMessageUrl =
|
||||
[NSString stringWithFormat:@"%@/message?r=%@&u=%@", baseUrl, roomKey, me];
|
||||
return [NSURL URLWithString:postMessageUrl];
|
||||
}
|
||||
|
||||
- (void)requestTURNServerWithUrl:(NSString *)turnServerUrl
|
||||
completionHandler:
|
||||
(void (^)(RTCICEServer *turnServer))completionHandler {
|
||||
NSURL *turnServerURL = [NSURL URLWithString:turnServerUrl];
|
||||
NSMutableURLRequest *request =
|
||||
[NSMutableURLRequest requestWithURL:turnServerURL];
|
||||
[request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
|
||||
[request addValue:@"https://apprtc.appspot.com"
|
||||
forHTTPHeaderField:@"origin"];
|
||||
[NSURLConnection sendAsynchronousRequest:request
|
||||
completionHandler:^(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Unable to get TURN server.");
|
||||
completionHandler(nil);
|
||||
return;
|
||||
}
|
||||
NSDictionary *json = [NSDictionary dictionaryWithJSONData:data];
|
||||
RTCICEServer *turnServer = [RTCICEServer serverFromCEODJSONDictionary:json];
|
||||
completionHandler(turnServer);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)requestTURNServerForICEServers:(NSArray*)iceServers
|
||||
turnServerUrl:(NSString*)turnServerUrl {
|
||||
BOOL isTurnPresent = NO;
|
||||
for (RTCICEServer* iceServer in iceServers) {
|
||||
if ([[iceServer.URI scheme] isEqualToString:@"turn"]) {
|
||||
isTurnPresent = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isTurnPresent) {
|
||||
[self requestTURNServerWithUrl:turnServerUrl
|
||||
completionHandler:^(RTCICEServer* turnServer) {
|
||||
NSArray* servers = iceServers;
|
||||
if (turnServer) {
|
||||
servers = [servers arrayByAddingObject:turnServer];
|
||||
}
|
||||
NSLog(@"ICE servers:\n%@", servers);
|
||||
[self.delegate appClient:self didReceiveICEServers:servers];
|
||||
}];
|
||||
} else {
|
||||
NSLog(@"ICE servers:\n%@", iceServers);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate appClient:self didReceiveICEServers:iceServers];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -1,390 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "APPRTCConnectionManager.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "APPRTCAppClient.h"
|
||||
#import "GAEChannelClient.h"
|
||||
#import "RTCICECandidate.h"
|
||||
#import "RTCICECandidate+JSON.h"
|
||||
#import "RTCMediaConstraints.h"
|
||||
#import "RTCMediaStream.h"
|
||||
#import "RTCPair.h"
|
||||
#import "RTCPeerConnection.h"
|
||||
#import "RTCPeerConnectionDelegate.h"
|
||||
#import "RTCPeerConnectionFactory.h"
|
||||
#import "RTCSessionDescription.h"
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
#import "RTCSessionDescriptionDelegate.h"
|
||||
#import "RTCStatsDelegate.h"
|
||||
#import "RTCVideoCapturer.h"
|
||||
#import "RTCVideoSource.h"
|
||||
|
||||
@interface APPRTCConnectionManager ()
|
||||
<APPRTCAppClientDelegate, GAEMessageHandler, RTCPeerConnectionDelegate,
|
||||
RTCSessionDescriptionDelegate, RTCStatsDelegate>
|
||||
|
||||
@property(nonatomic, strong) APPRTCAppClient* client;
|
||||
@property(nonatomic, strong) RTCPeerConnection* peerConnection;
|
||||
@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory;
|
||||
@property(nonatomic, strong) RTCVideoSource* videoSource;
|
||||
@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates;
|
||||
|
||||
@end
|
||||
|
||||
@implementation APPRTCConnectionManager {
|
||||
NSTimer* _statsTimer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDelegate:(id<APPRTCConnectionManagerDelegate>)delegate
|
||||
logger:(id<APPRTCLogger>)logger {
|
||||
if (self = [super init]) {
|
||||
self.delegate = delegate;
|
||||
self.logger = logger;
|
||||
self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init];
|
||||
// TODO(tkchin): turn this into a button.
|
||||
// Uncomment for stat logs.
|
||||
// _statsTimer =
|
||||
// [NSTimer scheduledTimerWithTimeInterval:10
|
||||
// target:self
|
||||
// selector:@selector(didFireStatsTimer:)
|
||||
// userInfo:nil
|
||||
// repeats:YES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
- (BOOL)connectToRoomWithURL:(NSURL*)url {
|
||||
if (self.client) {
|
||||
// Already have a connection.
|
||||
return NO;
|
||||
}
|
||||
self.client = [[APPRTCAppClient alloc] initWithDelegate:self
|
||||
messageHandler:self];
|
||||
[self.client connectToRoom:url];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
if (!self.client) {
|
||||
return;
|
||||
}
|
||||
[self.client
|
||||
sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[self.peerConnection close];
|
||||
self.peerConnection = nil;
|
||||
self.client = nil;
|
||||
self.videoSource = nil;
|
||||
self.queuedRemoteCandidates = nil;
|
||||
}
|
||||
|
||||
#pragma mark - APPRTCAppClientDelegate
|
||||
|
||||
- (void)appClient:(APPRTCAppClient*)appClient
|
||||
didErrorWithMessage:(NSString*)message {
|
||||
[self.delegate connectionManager:self
|
||||
didErrorWithMessage:message];
|
||||
}
|
||||
|
||||
- (void)appClient:(APPRTCAppClient*)appClient
|
||||
didReceiveICEServers:(NSArray*)servers {
|
||||
self.queuedRemoteCandidates = [NSMutableArray array];
|
||||
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.peerConnection =
|
||||
[self.peerConnectionFactory peerConnectionWithICEServers:servers
|
||||
constraints:constraints
|
||||
delegate:self];
|
||||
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;
|
||||
|
||||
// TODO(tkchin): local video capture for OSX. See
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=3417.
|
||||
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
|
||||
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.params.mediaConstraints];
|
||||
localVideoTrack =
|
||||
[self.peerConnectionFactory videoTrackWithID:@"ARDAMSv0"
|
||||
source:self.videoSource];
|
||||
if (localVideoTrack) {
|
||||
[lms addVideoTrack:localVideoTrack];
|
||||
}
|
||||
[self.delegate connectionManager:self
|
||||
didReceiveLocalVideoTrack:localVideoTrack];
|
||||
#endif
|
||||
|
||||
[lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]];
|
||||
[self.peerConnection addStream:lms];
|
||||
[self.logger logMessage:@"onICEServers - added local stream."];
|
||||
}
|
||||
|
||||
#pragma mark - GAEMessageHandler methods
|
||||
|
||||
- (void)onOpen {
|
||||
if (!self.client.params.isInitiator) {
|
||||
[self.logger logMessage:@"Callee; waiting for remote offer"];
|
||||
return;
|
||||
}
|
||||
[self.logger logMessage:@"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.logger logMessage:@"PC - createOffer."];
|
||||
}
|
||||
|
||||
- (void)onMessage:(NSDictionary*)messageData {
|
||||
NSString* type = messageData[@"type"];
|
||||
NSAssert(type, @"Missing type: %@", messageData);
|
||||
[self.logger logMessage:[NSString stringWithFormat:@"GAE onMessage type - %@",
|
||||
type]];
|
||||
if ([type isEqualToString:@"candidate"]) {
|
||||
RTCICECandidate* candidate =
|
||||
[RTCICECandidate candidateFromJSONDictionary:messageData];
|
||||
if (self.queuedRemoteCandidates) {
|
||||
[self.queuedRemoteCandidates addObject:candidate];
|
||||
} else {
|
||||
[self.peerConnection addICECandidate:candidate];
|
||||
}
|
||||
} else if ([type isEqualToString:@"offer"] ||
|
||||
[type isEqualToString:@"answer"]) {
|
||||
RTCSessionDescription* sdp =
|
||||
[RTCSessionDescription descriptionFromJSONDictionary:messageData];
|
||||
[self.peerConnection setRemoteDescriptionWithDelegate:self
|
||||
sessionDescription:sdp];
|
||||
[self.logger logMessage:@"PC - setRemoteDescription."];
|
||||
} else if ([type isEqualToString:@"bye"]) {
|
||||
[self.delegate connectionManagerDidReceiveHangup:self];
|
||||
} else {
|
||||
NSAssert(NO, @"Invalid message: %@", messageData);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onClose {
|
||||
[self.logger logMessage:@"GAE onClose."];
|
||||
[self.delegate connectionManagerDidReceiveHangup:self];
|
||||
}
|
||||
|
||||
- (void)onError:(int)code withDescription:(NSString*)description {
|
||||
NSString* message = [NSString stringWithFormat:@"GAE onError: %d, %@",
|
||||
code, description];
|
||||
[self.logger logMessage:message];
|
||||
[self.delegate connectionManager:self
|
||||
didErrorWithMessage:message];
|
||||
}
|
||||
|
||||
#pragma mark - RTCPeerConnectionDelegate
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
signalingStateChanged:(RTCSignalingState)stateChanged {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
addedStream:(RTCMediaStream*)stream {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"PCO onAddStream.");
|
||||
NSAssert([stream.audioTracks count] == 1 || [stream.videoTracks count] == 1,
|
||||
@"Expected audio or video track");
|
||||
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.delegate connectionManager:self
|
||||
didReceiveRemoteVideoTrack:stream.videoTracks[0]];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
removedStream:(RTCMediaStream*)stream {
|
||||
dispatch_async(dispatch_get_main_queue(),
|
||||
^{ NSLog(@"PCO onRemoveStream."); });
|
||||
}
|
||||
|
||||
- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"PCO onRenegotiationNeeded - ignoring because AppRTC has a "
|
||||
"predefined negotiation strategy");
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
gotICECandidate:(RTCICECandidate*)candidate {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"PCO onICECandidate.\n%@", candidate);
|
||||
[self.client sendData:[candidate JSONData]];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
iceGatheringChanged:(RTCICEGatheringState)newState {
|
||||
dispatch_async(dispatch_get_main_queue(),
|
||||
^{ NSLog(@"PCO onIceGatheringChange. %d", newState); });
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
iceConnectionChanged:(RTCICEConnectionState)newState {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"PCO onIceConnectionChange. %d", newState);
|
||||
if (newState == RTCICEConnectionConnected)
|
||||
[self.logger logMessage:@"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 - RTCSessionDescriptionDelegate
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
didCreateSessionDescription:(RTCSessionDescription*)sdp
|
||||
error:(NSError*)error {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
[self.logger logMessage:@"SDP onFailure."];
|
||||
NSAssert(NO, error.description);
|
||||
return;
|
||||
}
|
||||
[self.logger logMessage:@"SDP onSuccess(SDP) - set local description."];
|
||||
[self.peerConnection setLocalDescriptionWithDelegate:self
|
||||
sessionDescription:sdp];
|
||||
[self.logger logMessage:@"PC setLocalDescription."];
|
||||
[self.client sendData:[sdp JSONData]];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
didSetSessionDescriptionWithError:(NSError*)error {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
[self.logger logMessage:@"SDP onFailure."];
|
||||
NSAssert(NO, error.description);
|
||||
return;
|
||||
}
|
||||
[self.logger logMessage:@"SDP onSuccess() - possibly drain candidates"];
|
||||
if (!self.client.params.isInitiator) {
|
||||
if (self.peerConnection.remoteDescription &&
|
||||
!self.peerConnection.localDescription) {
|
||||
[self.logger logMessage:@"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.logger logMessage:@"PC - createAnswer."];
|
||||
} else {
|
||||
[self.logger logMessage:@"SDP onSuccess - drain candidates"];
|
||||
[self drainRemoteCandidates];
|
||||
}
|
||||
} else {
|
||||
if (self.peerConnection.remoteDescription) {
|
||||
[self.logger logMessage:@"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.logger logMessage:message];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)drainRemoteCandidates {
|
||||
for (RTCICECandidate* candidate in self.queuedRemoteCandidates) {
|
||||
[self.peerConnection addICECandidate:candidate];
|
||||
}
|
||||
self.queuedRemoteCandidates = nil;
|
||||
}
|
||||
|
||||
- (void)didFireStatsTimer:(NSTimer*)timer {
|
||||
if (self.peerConnection) {
|
||||
[self.peerConnection getStatsWithDelegate:self
|
||||
mediaStreamTrack:nil
|
||||
statsOutputLevel:RTCStatsOutputLevelDebug];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2013, Google Inc.
|
||||
* Copyright 2014, Google Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@ -27,42 +27,50 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "ARDSignalingParams.h"
|
||||
#import "GAEChannelClient.h"
|
||||
#import "RTCVideoTrack.h"
|
||||
|
||||
@class APPRTCAppClient;
|
||||
@protocol APPRTCAppClientDelegate
|
||||
typedef NS_ENUM(NSInteger, ARDAppClientState) {
|
||||
// Disconnected from servers.
|
||||
kARDAppClientStateDisconnected,
|
||||
// Connecting to servers.
|
||||
kARDAppClientStateConnecting,
|
||||
// Connected to servers.
|
||||
kARDAppClientStateConnected,
|
||||
};
|
||||
|
||||
- (void)appClient:(APPRTCAppClient*)appClient
|
||||
didErrorWithMessage:(NSString*)message;
|
||||
- (void)appClient:(APPRTCAppClient*)appClient
|
||||
didReceiveICEServers:(NSArray*)servers;
|
||||
@class ARDAppClient;
|
||||
@protocol ARDAppClientDelegate <NSObject>
|
||||
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didChangeState:(ARDAppClientState)state;
|
||||
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack;
|
||||
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack;
|
||||
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didError:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
@class RTCMediaConstraints;
|
||||
// Handles connections to the AppRTC server for a given room.
|
||||
@interface ARDAppClient : NSObject
|
||||
|
||||
// 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, readonly) ARDAppClientState state;
|
||||
@property(nonatomic, weak) id<ARDAppClientDelegate> delegate;
|
||||
|
||||
@property(nonatomic, readonly) ARDSignalingParams *params;
|
||||
@property(nonatomic, weak) id<APPRTCAppClientDelegate> delegate;
|
||||
- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate;
|
||||
|
||||
- (instancetype)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate
|
||||
messageHandler:(id<GAEMessageHandler>)handler;
|
||||
- (void)connectToRoom:(NSURL *)room;
|
||||
- (void)sendData:(NSData *)data;
|
||||
// Establishes a connection with the AppRTC servers for the given room id.
|
||||
// TODO(tkchin): provide available keys/values for options. This will be used
|
||||
// for call configurations such as overriding server choice, specifying codecs
|
||||
// and so on.
|
||||
- (void)connectToRoomWithId:(NSString *)roomId
|
||||
options:(NSDictionary *)options;
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
// Disallow init and don't add to documentation
|
||||
- (instancetype)init __attribute__((
|
||||
unavailable("init is not a supported initializer for this class.")));
|
||||
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
|
||||
// Disconnects from the AppRTC servers and any connected clients.
|
||||
- (void)disconnect;
|
||||
|
||||
@end
|
675
talk/examples/objc/AppRTCDemo/ARDAppClient.m
Normal file
675
talk/examples/objc/AppRTCDemo/ARDAppClient.m
Normal file
@ -0,0 +1,675 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "ARDAppClient.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import "ARDMessageResponse.h"
|
||||
#import "ARDRegisterResponse.h"
|
||||
#import "ARDSignalingMessage.h"
|
||||
#import "ARDUtilities.h"
|
||||
#import "ARDWebSocketChannel.h"
|
||||
#import "RTCICECandidate+JSON.h"
|
||||
#import "RTCICEServer+JSON.h"
|
||||
#import "RTCMediaConstraints.h"
|
||||
#import "RTCMediaStream.h"
|
||||
#import "RTCPair.h"
|
||||
#import "RTCPeerConnection.h"
|
||||
#import "RTCPeerConnectionDelegate.h"
|
||||
#import "RTCPeerConnectionFactory.h"
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
#import "RTCSessionDescriptionDelegate.h"
|
||||
#import "RTCVideoCapturer.h"
|
||||
#import "RTCVideoTrack.h"
|
||||
|
||||
// TODO(tkchin): move these to a configuration object.
|
||||
static NSString *kARDRoomServerHostUrl =
|
||||
@"https://3-dot-apprtc.appspot.com";
|
||||
static NSString *kARDRoomServerRegisterFormat =
|
||||
@"https://3-dot-apprtc.appspot.com/register/%@";
|
||||
static NSString *kARDRoomServerMessageFormat =
|
||||
@"https://3-dot-apprtc.appspot.com/message/%@/%@";
|
||||
static NSString *kARDRoomServerByeFormat =
|
||||
@"https://3-dot-apprtc.appspot.com/bye/%@/%@";
|
||||
|
||||
static NSString *kARDDefaultSTUNServerUrl =
|
||||
@"stun:stun.l.google.com:19302";
|
||||
// TODO(tkchin): figure out a better username for CEOD statistics.
|
||||
static NSString *kARDTurnRequestUrl =
|
||||
@"https://computeengineondemand.appspot.com"
|
||||
@"/turn?username=iapprtc&key=4080218913";
|
||||
|
||||
static NSString *kARDAppClientErrorDomain = @"ARDAppClient";
|
||||
static NSInteger kARDAppClientErrorUnknown = -1;
|
||||
static NSInteger kARDAppClientErrorRoomFull = -2;
|
||||
static NSInteger kARDAppClientErrorCreateSDP = -3;
|
||||
static NSInteger kARDAppClientErrorSetSDP = -4;
|
||||
static NSInteger kARDAppClientErrorNetwork = -5;
|
||||
static NSInteger kARDAppClientErrorInvalidClient = -6;
|
||||
static NSInteger kARDAppClientErrorInvalidRoom = -7;
|
||||
|
||||
@interface ARDAppClient () <ARDWebSocketChannelDelegate,
|
||||
RTCPeerConnectionDelegate, RTCSessionDescriptionDelegate>
|
||||
@property(nonatomic, strong) ARDWebSocketChannel *channel;
|
||||
@property(nonatomic, strong) RTCPeerConnection *peerConnection;
|
||||
@property(nonatomic, strong) RTCPeerConnectionFactory *factory;
|
||||
@property(nonatomic, strong) NSMutableArray *messageQueue;
|
||||
|
||||
@property(nonatomic, assign) BOOL isTurnComplete;
|
||||
@property(nonatomic, assign) BOOL hasReceivedSdp;
|
||||
@property(nonatomic, readonly) BOOL isRegisteredWithRoomServer;
|
||||
|
||||
@property(nonatomic, strong) NSString *roomId;
|
||||
@property(nonatomic, strong) NSString *clientId;
|
||||
@property(nonatomic, assign) BOOL isInitiator;
|
||||
@property(nonatomic, strong) NSMutableArray *iceServers;
|
||||
@property(nonatomic, strong) NSURL *webSocketURL;
|
||||
@property(nonatomic, strong) NSURL *webSocketRestURL;
|
||||
@end
|
||||
|
||||
@implementation ARDAppClient
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
@synthesize state = _state;
|
||||
@synthesize channel = _channel;
|
||||
@synthesize peerConnection = _peerConnection;
|
||||
@synthesize factory = _factory;
|
||||
@synthesize messageQueue = _messageQueue;
|
||||
@synthesize isTurnComplete = _isTurnComplete;
|
||||
@synthesize hasReceivedSdp = _hasReceivedSdp;
|
||||
@synthesize roomId = _roomId;
|
||||
@synthesize clientId = _clientId;
|
||||
@synthesize isInitiator = _isInitiator;
|
||||
@synthesize iceServers = _iceServers;
|
||||
@synthesize webSocketURL = _websocketURL;
|
||||
@synthesize webSocketRestURL = _websocketRestURL;
|
||||
|
||||
- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
|
||||
if (self = [super init]) {
|
||||
_delegate = delegate;
|
||||
_factory = [[RTCPeerConnectionFactory alloc] init];
|
||||
_messageQueue = [NSMutableArray array];
|
||||
_iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
- (void)setState:(ARDAppClientState)state {
|
||||
if (_state == state) {
|
||||
return;
|
||||
}
|
||||
_state = state;
|
||||
[_delegate appClient:self didChangeState:_state];
|
||||
}
|
||||
|
||||
- (void)connectToRoomWithId:(NSString *)roomId
|
||||
options:(NSDictionary *)options {
|
||||
NSParameterAssert(roomId.length);
|
||||
NSParameterAssert(_state == kARDAppClientStateDisconnected);
|
||||
self.state = kARDAppClientStateConnecting;
|
||||
|
||||
// Request TURN.
|
||||
__weak ARDAppClient *weakSelf = self;
|
||||
NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
|
||||
[self requestTURNServersWithURL:turnRequestURL
|
||||
completionHandler:^(NSArray *turnServers) {
|
||||
ARDAppClient *strongSelf = weakSelf;
|
||||
[strongSelf.iceServers addObjectsFromArray:turnServers];
|
||||
strongSelf.isTurnComplete = YES;
|
||||
[strongSelf startSignalingIfReady];
|
||||
}];
|
||||
|
||||
// Register with room server.
|
||||
[self registerWithRoomServerForRoomId:roomId
|
||||
completionHandler:^(ARDRegisterResponse *response) {
|
||||
ARDAppClient *strongSelf = weakSelf;
|
||||
if (!response || response.result != kARDRegisterResultTypeSuccess) {
|
||||
NSLog(@"Failed to register with room server. Result:%d",
|
||||
(int)response.result);
|
||||
[strongSelf disconnect];
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: @"Room is full.",
|
||||
};
|
||||
NSError *error =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorRoomFull
|
||||
userInfo:userInfo];
|
||||
[strongSelf.delegate appClient:strongSelf didError:error];
|
||||
return;
|
||||
}
|
||||
NSLog(@"Registered with room server.");
|
||||
strongSelf.roomId = response.roomId;
|
||||
strongSelf.clientId = response.clientId;
|
||||
strongSelf.isInitiator = response.isInitiator;
|
||||
for (ARDSignalingMessage *message in response.messages) {
|
||||
if (message.type == kARDSignalingMessageTypeOffer ||
|
||||
message.type == kARDSignalingMessageTypeAnswer) {
|
||||
strongSelf.hasReceivedSdp = YES;
|
||||
[strongSelf.messageQueue insertObject:message atIndex:0];
|
||||
} else {
|
||||
[strongSelf.messageQueue addObject:message];
|
||||
}
|
||||
}
|
||||
strongSelf.webSocketURL = response.webSocketURL;
|
||||
strongSelf.webSocketRestURL = response.webSocketRestURL;
|
||||
[strongSelf registerWithColliderIfReady];
|
||||
[strongSelf startSignalingIfReady];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
if (_state == kARDAppClientStateDisconnected) {
|
||||
return;
|
||||
}
|
||||
if (self.isRegisteredWithRoomServer) {
|
||||
[self unregisterWithRoomServer];
|
||||
}
|
||||
if (_channel) {
|
||||
if (_channel.state == kARDWebSocketChannelStateRegistered) {
|
||||
// Tell the other client we're hanging up.
|
||||
ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
|
||||
NSData *byeData = [byeMessage JSONData];
|
||||
[_channel sendData:byeData];
|
||||
}
|
||||
// Disconnect from collider.
|
||||
_channel = nil;
|
||||
}
|
||||
_clientId = nil;
|
||||
_roomId = nil;
|
||||
_isInitiator = NO;
|
||||
_hasReceivedSdp = NO;
|
||||
_messageQueue = [NSMutableArray array];
|
||||
_peerConnection = nil;
|
||||
self.state = kARDAppClientStateDisconnected;
|
||||
}
|
||||
|
||||
#pragma mark - ARDWebSocketChannelDelegate
|
||||
|
||||
- (void)channel:(ARDWebSocketChannel *)channel
|
||||
didReceiveMessage:(ARDSignalingMessage *)message {
|
||||
switch (message.type) {
|
||||
case kARDSignalingMessageTypeOffer:
|
||||
case kARDSignalingMessageTypeAnswer:
|
||||
_hasReceivedSdp = YES;
|
||||
[_messageQueue insertObject:message atIndex:0];
|
||||
break;
|
||||
case kARDSignalingMessageTypeCandidate:
|
||||
[_messageQueue addObject:message];
|
||||
break;
|
||||
case kARDSignalingMessageTypeBye:
|
||||
[self processSignalingMessage:message];
|
||||
return;
|
||||
}
|
||||
[self drainMessageQueueIfReady];
|
||||
}
|
||||
|
||||
- (void)channel:(ARDWebSocketChannel *)channel
|
||||
didChangeState:(ARDWebSocketChannelState)state {
|
||||
switch (state) {
|
||||
case kARDWebSocketChannelStateOpen:
|
||||
break;
|
||||
case kARDWebSocketChannelStateRegistered:
|
||||
break;
|
||||
case kARDWebSocketChannelStateClosed:
|
||||
case kARDWebSocketChannelStateError:
|
||||
// TODO(tkchin): reconnection scenarios. Right now we just disconnect
|
||||
// completely if the websocket connection fails.
|
||||
[self disconnect];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RTCPeerConnectionDelegate
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
signalingStateChanged:(RTCSignalingState)stateChanged {
|
||||
NSLog(@"Signaling state changed: %d", stateChanged);
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
addedStream:(RTCMediaStream *)stream {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Received %lu video tracks and %lu audio tracks",
|
||||
(unsigned long)stream.videoTracks.count,
|
||||
(unsigned long)stream.audioTracks.count);
|
||||
if (stream.videoTracks.count) {
|
||||
RTCVideoTrack *videoTrack = stream.videoTracks[0];
|
||||
[_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
removedStream:(RTCMediaStream *)stream {
|
||||
NSLog(@"Stream was removed.");
|
||||
}
|
||||
|
||||
- (void)peerConnectionOnRenegotiationNeeded:
|
||||
(RTCPeerConnection *)peerConnection {
|
||||
NSLog(@"WARNING: Renegotiation needed but unimplemented.");
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
iceConnectionChanged:(RTCICEConnectionState)newState {
|
||||
NSLog(@"ICE state changed: %d", newState);
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
iceGatheringChanged:(RTCICEGatheringState)newState {
|
||||
NSLog(@"ICE gathering state changed: %d", newState);
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
gotICECandidate:(RTCICECandidate *)candidate {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
ARDICECandidateMessage *message =
|
||||
[[ARDICECandidateMessage alloc] initWithCandidate:candidate];
|
||||
[self sendSignalingMessage:message];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
didOpenDataChannel:(RTCDataChannel*)dataChannel {
|
||||
}
|
||||
|
||||
#pragma mark - RTCSessionDescriptionDelegate
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
didCreateSessionDescription:(RTCSessionDescription *)sdp
|
||||
error:(NSError *)error {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
NSLog(@"Failed to create session description. Error: %@", error);
|
||||
[self disconnect];
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: @"Failed to create session description.",
|
||||
};
|
||||
NSError *sdpError =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorCreateSDP
|
||||
userInfo:userInfo];
|
||||
[_delegate appClient:self didError:sdpError];
|
||||
return;
|
||||
}
|
||||
[_peerConnection setLocalDescriptionWithDelegate:self
|
||||
sessionDescription:sdp];
|
||||
ARDSessionDescriptionMessage *message =
|
||||
[[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
|
||||
[self sendSignalingMessage:message];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection
|
||||
didSetSessionDescriptionWithError:(NSError *)error {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
NSLog(@"Failed to set session description. Error: %@", error);
|
||||
[self disconnect];
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: @"Failed to set session description.",
|
||||
};
|
||||
NSError *sdpError =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorSetSDP
|
||||
userInfo:userInfo];
|
||||
[_delegate appClient:self didError:sdpError];
|
||||
return;
|
||||
}
|
||||
// If we're answering and we've just set the remote offer we need to create
|
||||
// an answer and set the local description.
|
||||
if (!_isInitiator && !_peerConnection.localDescription) {
|
||||
RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
|
||||
[_peerConnection createAnswerWithDelegate:self
|
||||
constraints:constraints];
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (BOOL)isRegisteredWithRoomServer {
|
||||
return _clientId.length;
|
||||
}
|
||||
|
||||
- (void)startSignalingIfReady {
|
||||
if (!_isTurnComplete || !self.isRegisteredWithRoomServer) {
|
||||
return;
|
||||
}
|
||||
self.state = kARDAppClientStateConnected;
|
||||
|
||||
// Create peer connection.
|
||||
RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
|
||||
_peerConnection = [_factory peerConnectionWithICEServers:_iceServers
|
||||
constraints:constraints
|
||||
delegate:self];
|
||||
RTCMediaStream *localStream = [self createLocalMediaStream];
|
||||
[_peerConnection addStream:localStream];
|
||||
if (_isInitiator) {
|
||||
[self sendOffer];
|
||||
} else {
|
||||
[self waitForAnswer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendOffer {
|
||||
[_peerConnection createOfferWithDelegate:self
|
||||
constraints:[self defaultOfferConstraints]];
|
||||
}
|
||||
|
||||
- (void)waitForAnswer {
|
||||
[self drainMessageQueueIfReady];
|
||||
}
|
||||
|
||||
- (void)drainMessageQueueIfReady {
|
||||
if (!_peerConnection || !_hasReceivedSdp) {
|
||||
return;
|
||||
}
|
||||
for (ARDSignalingMessage *message in _messageQueue) {
|
||||
[self processSignalingMessage:message];
|
||||
}
|
||||
[_messageQueue removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)processSignalingMessage:(ARDSignalingMessage *)message {
|
||||
NSParameterAssert(_peerConnection ||
|
||||
message.type == kARDSignalingMessageTypeBye);
|
||||
switch (message.type) {
|
||||
case kARDSignalingMessageTypeOffer:
|
||||
case kARDSignalingMessageTypeAnswer: {
|
||||
ARDSessionDescriptionMessage *sdpMessage =
|
||||
(ARDSessionDescriptionMessage *)message;
|
||||
RTCSessionDescription *description = sdpMessage.sessionDescription;
|
||||
[_peerConnection setRemoteDescriptionWithDelegate:self
|
||||
sessionDescription:description];
|
||||
break;
|
||||
}
|
||||
case kARDSignalingMessageTypeCandidate: {
|
||||
ARDICECandidateMessage *candidateMessage =
|
||||
(ARDICECandidateMessage *)message;
|
||||
[_peerConnection addICECandidate:candidateMessage.candidate];
|
||||
break;
|
||||
}
|
||||
case kARDSignalingMessageTypeBye:
|
||||
// Other client disconnected.
|
||||
// TODO(tkchin): support waiting in room for next client. For now just
|
||||
// disconnect.
|
||||
[self disconnect];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
|
||||
if (_isInitiator) {
|
||||
[self sendSignalingMessageToRoomServer:message completionHandler:nil];
|
||||
} else {
|
||||
[self sendSignalingMessageToCollider:message];
|
||||
}
|
||||
}
|
||||
|
||||
- (RTCMediaStream *)createLocalMediaStream {
|
||||
RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
|
||||
RTCVideoTrack* localVideoTrack = nil;
|
||||
|
||||
// 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.
|
||||
// TODO(tkchin): local video capture for OSX. See
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=3417.
|
||||
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
|
||||
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];
|
||||
RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
|
||||
RTCVideoSource *videoSource =
|
||||
[_factory videoSourceWithCapturer:capturer
|
||||
constraints:mediaConstraints];
|
||||
localVideoTrack =
|
||||
[_factory videoTrackWithID:@"ARDAMSv0" source:videoSource];
|
||||
if (localVideoTrack) {
|
||||
[localStream addVideoTrack:localVideoTrack];
|
||||
}
|
||||
[_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
|
||||
#endif
|
||||
[localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
|
||||
return localStream;
|
||||
}
|
||||
|
||||
- (void)requestTURNServersWithURL:(NSURL *)requestURL
|
||||
completionHandler:(void (^)(NSArray *turnServers))completionHandler {
|
||||
NSParameterAssert([requestURL absoluteString].length);
|
||||
NSMutableURLRequest *request =
|
||||
[NSMutableURLRequest requestWithURL:requestURL];
|
||||
// We need to set origin because TURN provider whitelists requests based on
|
||||
// origin.
|
||||
[request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
|
||||
[request addValue:kARDRoomServerHostUrl forHTTPHeaderField:@"origin"];
|
||||
[NSURLConnection sendAsyncRequest:request
|
||||
completionHandler:^(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
NSArray *turnServers = [NSArray array];
|
||||
if (error) {
|
||||
NSLog(@"Unable to get TURN server.");
|
||||
completionHandler(turnServers);
|
||||
return;
|
||||
}
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithJSONData:data];
|
||||
turnServers = [RTCICEServer serversFromCEODJSONDictionary:dict];
|
||||
completionHandler(turnServers);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Room server methods
|
||||
|
||||
- (void)registerWithRoomServerForRoomId:(NSString *)roomId
|
||||
completionHandler:(void (^)(ARDRegisterResponse *))completionHandler {
|
||||
NSString *urlString =
|
||||
[NSString stringWithFormat:kARDRoomServerRegisterFormat, roomId];
|
||||
NSURL *roomURL = [NSURL URLWithString:urlString];
|
||||
NSLog(@"Registering with room server.");
|
||||
__weak ARDAppClient *weakSelf = self;
|
||||
[NSURLConnection sendAsyncPostToURL:roomURL
|
||||
withData:nil
|
||||
completionHandler:^(BOOL succeeded, NSData *data) {
|
||||
ARDAppClient *strongSelf = weakSelf;
|
||||
if (!succeeded) {
|
||||
NSError *error = [self roomServerNetworkError];
|
||||
[strongSelf.delegate appClient:strongSelf didError:error];
|
||||
completionHandler(nil);
|
||||
return;
|
||||
}
|
||||
ARDRegisterResponse *response =
|
||||
[ARDRegisterResponse responseFromJSONData:data];
|
||||
completionHandler(response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendSignalingMessageToRoomServer:(ARDSignalingMessage *)message
|
||||
completionHandler:(void (^)(ARDMessageResponse *))completionHandler {
|
||||
NSData *data = [message JSONData];
|
||||
NSString *urlString =
|
||||
[NSString stringWithFormat:
|
||||
kARDRoomServerMessageFormat, _roomId, _clientId];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSLog(@"C->RS POST: %@", message);
|
||||
__weak ARDAppClient *weakSelf = self;
|
||||
[NSURLConnection sendAsyncPostToURL:url
|
||||
withData:data
|
||||
completionHandler:^(BOOL succeeded, NSData *data) {
|
||||
ARDAppClient *strongSelf = weakSelf;
|
||||
if (!succeeded) {
|
||||
NSError *error = [self roomServerNetworkError];
|
||||
[strongSelf.delegate appClient:strongSelf didError:error];
|
||||
return;
|
||||
}
|
||||
ARDMessageResponse *response =
|
||||
[ARDMessageResponse responseFromJSONData:data];
|
||||
NSError *error = nil;
|
||||
switch (response.result) {
|
||||
case kARDMessageResultTypeSuccess:
|
||||
break;
|
||||
case kARDMessageResultTypeUnknown:
|
||||
error =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorUnknown
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Unknown error.",
|
||||
}];
|
||||
case kARDMessageResultTypeInvalidClient:
|
||||
error =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorInvalidClient
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Invalid client.",
|
||||
}];
|
||||
break;
|
||||
case kARDMessageResultTypeInvalidRoom:
|
||||
error =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorInvalidRoom
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Invalid room.",
|
||||
}];
|
||||
break;
|
||||
};
|
||||
if (error) {
|
||||
[strongSelf.delegate appClient:strongSelf didError:error];
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(response);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)unregisterWithRoomServer {
|
||||
NSString *urlString =
|
||||
[NSString stringWithFormat:kARDRoomServerByeFormat, _roomId, _clientId];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
||||
NSURLResponse *response = nil;
|
||||
// We want a synchronous request so that we know that we're unregistered from
|
||||
// room server before we do any further unregistration.
|
||||
NSLog(@"C->RS: BYE");
|
||||
NSError *error = nil;
|
||||
[NSURLConnection sendSynchronousRequest:request
|
||||
returningResponse:&response
|
||||
error:&error];
|
||||
if (error) {
|
||||
NSLog(@"Error unregistering from room server: %@", error);
|
||||
}
|
||||
NSLog(@"Unregistered from room server.");
|
||||
}
|
||||
|
||||
- (NSError *)roomServerNetworkError {
|
||||
NSError *error =
|
||||
[[NSError alloc] initWithDomain:kARDAppClientErrorDomain
|
||||
code:kARDAppClientErrorNetwork
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Room server network error",
|
||||
}];
|
||||
return error;
|
||||
}
|
||||
|
||||
#pragma mark - Collider methods
|
||||
|
||||
- (void)registerWithColliderIfReady {
|
||||
if (!self.isRegisteredWithRoomServer) {
|
||||
return;
|
||||
}
|
||||
// Open WebSocket connection.
|
||||
_channel =
|
||||
[[ARDWebSocketChannel alloc] initWithURL:_websocketURL
|
||||
restURL:_websocketRestURL
|
||||
delegate:self];
|
||||
[_channel registerForRoomId:_roomId clientId:_clientId];
|
||||
}
|
||||
|
||||
- (void)sendSignalingMessageToCollider:(ARDSignalingMessage *)message {
|
||||
NSData *data = [message JSONData];
|
||||
[_channel sendData:data];
|
||||
}
|
||||
|
||||
#pragma mark - Defaults
|
||||
|
||||
- (RTCMediaConstraints *)defaultMediaStreamConstraints {
|
||||
RTCMediaConstraints* constraints =
|
||||
[[RTCMediaConstraints alloc]
|
||||
initWithMandatoryConstraints:nil
|
||||
optionalConstraints:nil];
|
||||
return constraints;
|
||||
}
|
||||
|
||||
- (RTCMediaConstraints *)defaultAnswerConstraints {
|
||||
return [self defaultOfferConstraints];
|
||||
}
|
||||
|
||||
- (RTCMediaConstraints *)defaultOfferConstraints {
|
||||
NSArray *mandatoryConstraints = @[
|
||||
[[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
|
||||
[[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
|
||||
];
|
||||
RTCMediaConstraints* constraints =
|
||||
[[RTCMediaConstraints alloc]
|
||||
initWithMandatoryConstraints:mandatoryConstraints
|
||||
optionalConstraints:nil];
|
||||
return constraints;
|
||||
}
|
||||
|
||||
- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
|
||||
NSArray *optionalConstraints = @[
|
||||
[[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
|
||||
];
|
||||
RTCMediaConstraints* constraints =
|
||||
[[RTCMediaConstraints alloc]
|
||||
initWithMandatoryConstraints:nil
|
||||
optionalConstraints:optionalConstraints];
|
||||
return constraints;
|
||||
}
|
||||
|
||||
- (RTCICEServer *)defaultSTUNServer {
|
||||
NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
|
||||
return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
|
||||
username:@""
|
||||
password:@""];
|
||||
}
|
||||
|
||||
@end
|
@ -27,20 +27,17 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RTCMediaConstraints.h"
|
||||
typedef NS_ENUM(NSInteger, ARDMessageResultType) {
|
||||
kARDMessageResultTypeUnknown,
|
||||
kARDMessageResultTypeSuccess,
|
||||
kARDMessageResultTypeInvalidRoom,
|
||||
kARDMessageResultTypeInvalidClient
|
||||
};
|
||||
|
||||
// Struct for holding the signaling parameters of an AppRTC room.
|
||||
@interface ARDSignalingParams : NSObject
|
||||
@interface ARDMessageResponse : NSObject
|
||||
|
||||
@property(nonatomic, assign) BOOL isInitiator;
|
||||
@property(nonatomic, readonly) NSArray *errorMessages;
|
||||
@property(nonatomic, readonly) RTCMediaConstraints *offerConstraints;
|
||||
@property(nonatomic, readonly) RTCMediaConstraints *mediaConstraints;
|
||||
@property(nonatomic, readonly) NSMutableArray *iceServers;
|
||||
@property(nonatomic, readonly) NSURL *signalingServerURL;
|
||||
@property(nonatomic, readonly) NSURL *turnRequestURL;
|
||||
@property(nonatomic, readonly) NSString *channelToken;
|
||||
@property(nonatomic, readonly) ARDMessageResultType result;
|
||||
|
||||
+ (ARDSignalingParams *)paramsFromJSONData:(NSData *)data;
|
||||
+ (ARDMessageResponse *)responseFromJSONData:(NSData *)data;
|
||||
|
||||
@end
|
69
talk/examples/objc/AppRTCDemo/ARDMessageResponse.m
Normal file
69
talk/examples/objc/AppRTCDemo/ARDMessageResponse.m
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "ARDMessageResponse.h"
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
|
||||
static NSString const *kARDMessageResultKey = @"result";
|
||||
|
||||
@interface ARDMessageResponse ()
|
||||
|
||||
@property(nonatomic, assign) ARDMessageResultType result;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDMessageResponse
|
||||
|
||||
@synthesize result = _result;
|
||||
|
||||
+ (ARDMessageResponse *)responseFromJSONData:(NSData *)data {
|
||||
NSDictionary *responseJSON = [NSDictionary dictionaryWithJSONData:data];
|
||||
if (!responseJSON) {
|
||||
return nil;
|
||||
}
|
||||
ARDMessageResponse *response = [[ARDMessageResponse alloc] init];
|
||||
response.result =
|
||||
[[self class] resultTypeFromString:responseJSON[kARDMessageResultKey]];
|
||||
return response;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (ARDMessageResultType)resultTypeFromString:(NSString *)resultString {
|
||||
ARDMessageResultType result = kARDMessageResultTypeUnknown;
|
||||
if ([resultString isEqualToString:@"SUCCESS"]) {
|
||||
result = kARDMessageResultTypeSuccess;
|
||||
} else if ([resultString isEqualToString:@"INVALID_CLIENT"]) {
|
||||
result = kARDMessageResultTypeInvalidClient;
|
||||
} else if ([resultString isEqualToString:@"INVALID_ROOM"]) {
|
||||
result = kARDMessageResultTypeInvalidRoom;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2013, Google Inc.
|
||||
* Copyright 2014, Google Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@ -27,26 +27,23 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// 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<NSObject>
|
||||
typedef NS_ENUM(NSInteger, ARDRegisterResultType) {
|
||||
kARDRegisterResultTypeUnknown,
|
||||
kARDRegisterResultTypeSuccess,
|
||||
kARDRegisterResultTypeFull
|
||||
};
|
||||
|
||||
- (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
|
||||
|
||||
@property(nonatomic, weak) id<GAEMessageHandler> delegate;
|
||||
|
||||
- (instancetype)initWithToken:(NSString*)token
|
||||
delegate:(id<GAEMessageHandler>)delegate;
|
||||
// Result of registering with the GAE server.
|
||||
@interface ARDRegisterResponse : NSObject
|
||||
|
||||
@property(nonatomic, readonly) ARDRegisterResultType result;
|
||||
@property(nonatomic, readonly) BOOL isInitiator;
|
||||
@property(nonatomic, readonly) NSString *roomId;
|
||||
@property(nonatomic, readonly) NSString *clientId;
|
||||
@property(nonatomic, readonly) NSArray *messages;
|
||||
@property(nonatomic, readonly) NSURL *webSocketURL;
|
||||
@property(nonatomic, readonly) NSURL *webSocketRestURL;
|
||||
|
||||
+ (ARDRegisterResponse *)responseFromJSONData:(NSData *)data;
|
||||
|
||||
@end
|
111
talk/examples/objc/AppRTCDemo/ARDRegisterResponse.m
Normal file
111
talk/examples/objc/AppRTCDemo/ARDRegisterResponse.m
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "ARDRegisterResponse.h"
|
||||
|
||||
#import "ARDSignalingMessage.h"
|
||||
#import "ARDUtilities.h"
|
||||
#import "RTCICEServer+JSON.h"
|
||||
|
||||
static NSString const *kARDRegisterResultKey = @"result";
|
||||
static NSString const *kARDRegisterResultParamsKey = @"params";
|
||||
static NSString const *kARDRegisterInitiatorKey = @"is_initiator";
|
||||
static NSString const *kARDRegisterRoomIdKey = @"room_id";
|
||||
static NSString const *kARDRegisterClientIdKey = @"client_id";
|
||||
static NSString const *kARDRegisterMessagesKey = @"messages";
|
||||
static NSString const *kARDRegisterWebSocketURLKey = @"wss_url";
|
||||
static NSString const *kARDRegisterWebSocketRestURLKey = @"wss_post_url";
|
||||
|
||||
@interface ARDRegisterResponse ()
|
||||
|
||||
@property(nonatomic, assign) ARDRegisterResultType result;
|
||||
@property(nonatomic, assign) BOOL isInitiator;
|
||||
@property(nonatomic, strong) NSString *roomId;
|
||||
@property(nonatomic, strong) NSString *clientId;
|
||||
@property(nonatomic, strong) NSArray *messages;
|
||||
@property(nonatomic, strong) NSURL *webSocketURL;
|
||||
@property(nonatomic, strong) NSURL *webSocketRestURL;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDRegisterResponse
|
||||
|
||||
@synthesize result = _result;
|
||||
@synthesize isInitiator = _isInitiator;
|
||||
@synthesize roomId = _roomId;
|
||||
@synthesize clientId = _clientId;
|
||||
@synthesize messages = _messages;
|
||||
@synthesize webSocketURL = _webSocketURL;
|
||||
@synthesize webSocketRestURL = _webSocketRestURL;
|
||||
|
||||
+ (ARDRegisterResponse *)responseFromJSONData:(NSData *)data {
|
||||
NSDictionary *responseJSON = [NSDictionary dictionaryWithJSONData:data];
|
||||
if (!responseJSON) {
|
||||
return nil;
|
||||
}
|
||||
ARDRegisterResponse *response = [[ARDRegisterResponse alloc] init];
|
||||
NSString *resultString = responseJSON[kARDRegisterResultKey];
|
||||
response.result = [[self class] resultTypeFromString:resultString];
|
||||
NSDictionary *params = responseJSON[kARDRegisterResultParamsKey];
|
||||
|
||||
response.isInitiator = [params[kARDRegisterInitiatorKey] boolValue];
|
||||
response.roomId = params[kARDRegisterRoomIdKey];
|
||||
response.clientId = params[kARDRegisterClientIdKey];
|
||||
|
||||
// Parse messages.
|
||||
NSArray *messages = params[kARDRegisterMessagesKey];
|
||||
NSMutableArray *signalingMessages =
|
||||
[NSMutableArray arrayWithCapacity:messages.count];
|
||||
for (NSString *message in messages) {
|
||||
ARDSignalingMessage *signalingMessage =
|
||||
[ARDSignalingMessage messageFromJSONString:message];
|
||||
[signalingMessages addObject:signalingMessage];
|
||||
}
|
||||
response.messages = signalingMessages;
|
||||
|
||||
// Parse websocket urls.
|
||||
NSString *webSocketURLString = params[kARDRegisterWebSocketURLKey];
|
||||
response.webSocketURL = [NSURL URLWithString:webSocketURLString];
|
||||
NSString *webSocketRestURLString = params[kARDRegisterWebSocketRestURLKey];
|
||||
response.webSocketRestURL = [NSURL URLWithString:webSocketRestURLString];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (ARDRegisterResultType)resultTypeFromString:(NSString *)resultString {
|
||||
ARDRegisterResultType result = kARDRegisterResultTypeUnknown;
|
||||
if ([resultString isEqualToString:@"SUCCESS"]) {
|
||||
result = kARDRegisterResultTypeSuccess;
|
||||
} else if ([resultString isEqualToString:@"FULL"]) {
|
||||
result = kARDRegisterResultTypeFull;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
@ -27,40 +27,40 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Used to log messages to destination like UI.
|
||||
@protocol APPRTCLogger<NSObject>
|
||||
- (void)logMessage:(NSString*)message;
|
||||
@end
|
||||
#import "RTCICECandidate.h"
|
||||
#import "RTCSessionDescription.h"
|
||||
|
||||
@class RTCVideoTrack;
|
||||
@class APPRTCConnectionManager;
|
||||
typedef enum {
|
||||
kARDSignalingMessageTypeCandidate,
|
||||
kARDSignalingMessageTypeOffer,
|
||||
kARDSignalingMessageTypeAnswer,
|
||||
kARDSignalingMessageTypeBye,
|
||||
} ARDSignalingMessageType;
|
||||
|
||||
// Used to provide AppRTC connection information.
|
||||
@protocol APPRTCConnectionManagerDelegate<NSObject>
|
||||
@interface ARDSignalingMessage : NSObject
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack;
|
||||
@property(nonatomic, readonly) ARDSignalingMessageType type;
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack;
|
||||
|
||||
- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager;
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
didErrorWithMessage:(NSString*)errorMessage;
|
||||
+ (ARDSignalingMessage *)messageFromJSONString:(NSString *)jsonString;
|
||||
- (NSData *)JSONData;
|
||||
|
||||
@end
|
||||
|
||||
// Abstracts the network connection aspect of AppRTC. The delegate will receive
|
||||
// information about connection status as changes occur.
|
||||
@interface APPRTCConnectionManager : NSObject
|
||||
@interface ARDICECandidateMessage : ARDSignalingMessage
|
||||
|
||||
@property(nonatomic, weak) id<APPRTCConnectionManagerDelegate> delegate;
|
||||
@property(nonatomic, weak) id<APPRTCLogger> logger;
|
||||
@property(nonatomic, readonly) RTCICECandidate *candidate;
|
||||
|
||||
- (instancetype)initWithDelegate:(id<APPRTCConnectionManagerDelegate>)delegate
|
||||
logger:(id<APPRTCLogger>)logger;
|
||||
- (BOOL)connectToRoomWithURL:(NSURL*)url;
|
||||
- (void)disconnect;
|
||||
- (instancetype)initWithCandidate:(RTCICECandidate *)candidate;
|
||||
|
||||
@end
|
||||
|
||||
@interface ARDSessionDescriptionMessage : ARDSignalingMessage
|
||||
|
||||
@property(nonatomic, readonly) RTCSessionDescription *sessionDescription;
|
||||
|
||||
- (instancetype)initWithDescription:(RTCSessionDescription *)description;
|
||||
|
||||
@end
|
||||
|
||||
@interface ARDByeMessage : ARDSignalingMessage
|
||||
@end
|
143
talk/examples/objc/AppRTCDemo/ARDSignalingMessage.m
Normal file
143
talk/examples/objc/AppRTCDemo/ARDSignalingMessage.m
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "ARDSignalingMessage.h"
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
#import "RTCICECandidate+JSON.h"
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
|
||||
static NSString const *kARDSignalingMessageTypeKey = @"type";
|
||||
|
||||
@implementation ARDSignalingMessage
|
||||
|
||||
@synthesize type = _type;
|
||||
|
||||
- (instancetype)initWithType:(ARDSignalingMessageType)type {
|
||||
if (self = [super init]) {
|
||||
_type = type;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [[NSString alloc] initWithData:[self JSONData]
|
||||
encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
+ (ARDSignalingMessage *)messageFromJSONString:(NSString *)jsonString {
|
||||
NSDictionary *values = [NSDictionary dictionaryWithJSONString:jsonString];
|
||||
if (!values) {
|
||||
NSLog(@"Error parsing signaling message JSON.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *typeString = values[kARDSignalingMessageTypeKey];
|
||||
ARDSignalingMessage *message = nil;
|
||||
if ([typeString isEqualToString:@"candidate"]) {
|
||||
RTCICECandidate *candidate =
|
||||
[RTCICECandidate candidateFromJSONDictionary:values];
|
||||
message = [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
|
||||
} else if ([typeString isEqualToString:@"offer"] ||
|
||||
[typeString isEqualToString:@"answer"]) {
|
||||
RTCSessionDescription *description =
|
||||
[RTCSessionDescription descriptionFromJSONDictionary:values];
|
||||
message =
|
||||
[[ARDSessionDescriptionMessage alloc] initWithDescription:description];
|
||||
} else if ([typeString isEqualToString:@"bye"]) {
|
||||
message = [[ARDByeMessage alloc] init];
|
||||
} else {
|
||||
NSLog(@"Unexpected type: %@", typeString);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDICECandidateMessage
|
||||
|
||||
@synthesize candidate = _candidate;
|
||||
|
||||
- (instancetype)initWithCandidate:(RTCICECandidate *)candidate {
|
||||
if (self = [super initWithType:kARDSignalingMessageTypeCandidate]) {
|
||||
_candidate = candidate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
return [_candidate JSONData];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDSessionDescriptionMessage
|
||||
|
||||
@synthesize sessionDescription = _sessionDescription;
|
||||
|
||||
- (instancetype)initWithDescription:(RTCSessionDescription *)description {
|
||||
ARDSignalingMessageType type = kARDSignalingMessageTypeOffer;
|
||||
NSString *typeString = description.type;
|
||||
if ([typeString isEqualToString:@"offer"]) {
|
||||
type = kARDSignalingMessageTypeOffer;
|
||||
} else if ([typeString isEqualToString:@"answer"]) {
|
||||
type = kARDSignalingMessageTypeAnswer;
|
||||
} else {
|
||||
NSAssert(NO, @"Unexpected type: %@", typeString);
|
||||
}
|
||||
if (self = [super initWithType:type]) {
|
||||
_sessionDescription = description;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
return [_sessionDescription JSONData];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDByeMessage
|
||||
|
||||
- (instancetype)init {
|
||||
return [super initWithType:kARDSignalingMessageTypeBye];
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
NSDictionary *message = @{
|
||||
@"type": @"bye"
|
||||
};
|
||||
return [NSJSONSerialization dataWithJSONObject:message
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:NULL];
|
||||
}
|
||||
|
||||
@end
|
@ -1,130 +0,0 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "ARDSignalingParams.h"
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
#import "RTCICEServer+JSON.h"
|
||||
#import "RTCMediaConstraints+JSON.h"
|
||||
|
||||
static NSString const *kARDSignalingParamsErrorKey = @"error";
|
||||
static NSString const *kARDSignalingParamsErrorMessagesKey = @"error_messages";
|
||||
static NSString const *kARDSignalingParamsInitiatorKey = @"initiator";
|
||||
static NSString const *kARDSignalingParamsPeerConnectionConfigKey =
|
||||
@"pc_config";
|
||||
static NSString const *kARDSignalingParamsICEServersKey = @"iceServers";
|
||||
static NSString const *kARDSignalingParamsMediaConstraintsKey =
|
||||
@"media_constraints";
|
||||
static NSString const *kARDSignalingParamsMediaConstraintsVideoKey =
|
||||
@"video";
|
||||
static NSString const *kARDSignalingParamsTokenKey = @"token";
|
||||
static NSString const *kARDSignalingParamsTurnRequestUrlKey = @"turn_url";
|
||||
|
||||
@interface ARDSignalingParams ()
|
||||
|
||||
@property(nonatomic, strong) NSArray *errorMessages;
|
||||
@property(nonatomic, strong) RTCMediaConstraints *offerConstraints;
|
||||
@property(nonatomic, strong) RTCMediaConstraints *mediaConstraints;
|
||||
@property(nonatomic, strong) NSMutableArray *iceServers;
|
||||
@property(nonatomic, strong) NSURL *signalingServerURL;
|
||||
@property(nonatomic, strong) NSURL *turnRequestURL;
|
||||
@property(nonatomic, strong) NSString *channelToken;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDSignalingParams
|
||||
|
||||
@synthesize errorMessages = _errorMessages;
|
||||
@synthesize isInitiator = _isInitiator;
|
||||
@synthesize offerConstraints = _offerConstraints;
|
||||
@synthesize mediaConstraints = _mediaConstraints;
|
||||
@synthesize iceServers = _iceServers;
|
||||
@synthesize signalingServerURL = _signalingServerURL;
|
||||
|
||||
+ (ARDSignalingParams *)paramsFromJSONData:(NSData *)data {
|
||||
NSDictionary *paramsJSON = [NSDictionary dictionaryWithJSONData:data];
|
||||
if (!paramsJSON) {
|
||||
return nil;
|
||||
}
|
||||
ARDSignalingParams *params = [[ARDSignalingParams alloc] init];
|
||||
|
||||
// Parse errors.
|
||||
BOOL hasError = NO;
|
||||
NSArray *errorMessages = paramsJSON[kARDSignalingParamsErrorMessagesKey];
|
||||
if (errorMessages.count > 0) {
|
||||
params.errorMessages = errorMessages;
|
||||
return params;
|
||||
}
|
||||
|
||||
// Parse ICE servers.
|
||||
NSString *peerConnectionConfigString =
|
||||
paramsJSON[kARDSignalingParamsPeerConnectionConfigKey];
|
||||
NSDictionary *peerConnectionConfig =
|
||||
[NSDictionary dictionaryWithJSONString:peerConnectionConfigString];
|
||||
NSArray *iceServerJSONArray =
|
||||
peerConnectionConfig[kARDSignalingParamsICEServersKey];
|
||||
NSMutableArray *iceServers = [NSMutableArray array];
|
||||
for (NSDictionary *iceServerJSON in iceServerJSONArray) {
|
||||
RTCICEServer *iceServer =
|
||||
[RTCICEServer serverFromJSONDictionary:iceServerJSON];
|
||||
[iceServers addObject:iceServer];
|
||||
}
|
||||
params.iceServers = iceServers;
|
||||
|
||||
// Parse initiator.
|
||||
BOOL isInitiator = [paramsJSON[kARDSignalingParamsInitiatorKey] boolValue];
|
||||
params.isInitiator = isInitiator;
|
||||
|
||||
// Parse video constraints.
|
||||
RTCMediaConstraints *videoConstraints = nil;
|
||||
NSString *mediaConstraintsJSONString =
|
||||
paramsJSON[kARDSignalingParamsMediaConstraintsKey];
|
||||
NSDictionary *mediaConstraintsJSON =
|
||||
[NSDictionary dictionaryWithJSONString:mediaConstraintsJSONString];
|
||||
id videoJSON =
|
||||
mediaConstraintsJSON[kARDSignalingParamsMediaConstraintsVideoKey];
|
||||
if ([videoJSON isKindOfClass:[NSDictionary class]]) {
|
||||
videoConstraints =
|
||||
[RTCMediaConstraints constraintsFromJSONDictionary:videoJSON];
|
||||
} else if ([videoJSON isKindOfClass:[NSNumber class]] &&
|
||||
[videoJSON boolValue]) {
|
||||
videoConstraints = [[RTCMediaConstraints alloc] init];
|
||||
}
|
||||
params.mediaConstraints = videoConstraints;
|
||||
|
||||
// Parse channel token.
|
||||
NSString *token = paramsJSON[kARDSignalingParamsTokenKey];
|
||||
params.channelToken = token;
|
||||
|
||||
// Parse turn request url.
|
||||
params.turnRequestURL =
|
||||
[NSURL URLWithString:paramsJSON[kARDSignalingParamsTurnRequestUrlKey]];
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
@end
|
@ -38,9 +38,15 @@
|
||||
@interface NSURLConnection (ARDUtilities)
|
||||
|
||||
// Issues an asynchronous request that calls back on main queue.
|
||||
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
|
||||
+ (void)sendAsyncRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error))completionHandler;
|
||||
|
||||
// Posts data to the specified URL.
|
||||
+ (void)sendAsyncPostToURL:(NSURL *)url
|
||||
withData:(NSData *)data
|
||||
completionHandler:(void (^)(BOOL succeeded,
|
||||
NSData *data))completionHandler;
|
||||
|
||||
@end
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
@implementation NSURLConnection (ARDUtilities)
|
||||
|
||||
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
|
||||
+ (void)sendAsyncRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error))completionHandler {
|
||||
@ -65,7 +65,45 @@
|
||||
completionHandler:^(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
if (completionHandler) {
|
||||
completionHandler(response, data, error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// Posts data to the specified URL.
|
||||
+ (void)sendAsyncPostToURL:(NSURL *)url
|
||||
withData:(NSData *)data
|
||||
completionHandler:(void (^)(BOOL succeeded,
|
||||
NSData *data))completionHandler {
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = @"POST";
|
||||
request.HTTPBody = data;
|
||||
[[self class] sendAsyncRequest:request
|
||||
completionHandler:^(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Error posting data: %@", error.localizedDescription);
|
||||
if (completionHandler) {
|
||||
completionHandler(NO, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
if (httpResponse.statusCode != 200) {
|
||||
NSString *serverResponse = data.length > 0 ?
|
||||
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] :
|
||||
nil;
|
||||
NSLog(@"Received bad response: %@", serverResponse);
|
||||
if (completionHandler) {
|
||||
completionHandler(NO, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(YES, data);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
75
talk/examples/objc/AppRTCDemo/ARDWebSocketChannel.h
Normal file
75
talk/examples/objc/AppRTCDemo/ARDWebSocketChannel.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 <Foundation/Foundation.h>
|
||||
|
||||
#import "ARDSignalingMessage.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, ARDWebSocketChannelState) {
|
||||
// State when disconnected.
|
||||
kARDWebSocketChannelStateClosed,
|
||||
// State when connection is established but not ready for use.
|
||||
kARDWebSocketChannelStateOpen,
|
||||
// State when connection is established and registered.
|
||||
kARDWebSocketChannelStateRegistered,
|
||||
// State when connection encounters a fatal error.
|
||||
kARDWebSocketChannelStateError
|
||||
};
|
||||
|
||||
@class ARDWebSocketChannel;
|
||||
@protocol ARDWebSocketChannelDelegate <NSObject>
|
||||
|
||||
- (void)channel:(ARDWebSocketChannel *)channel
|
||||
didChangeState:(ARDWebSocketChannelState)state;
|
||||
|
||||
- (void)channel:(ARDWebSocketChannel *)channel
|
||||
didReceiveMessage:(ARDSignalingMessage *)message;
|
||||
|
||||
@end
|
||||
|
||||
// Wraps a WebSocket connection to the AppRTC WebSocket server.
|
||||
@interface ARDWebSocketChannel : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *roomId;
|
||||
@property(nonatomic, readonly) NSString *clientId;
|
||||
@property(nonatomic, readonly) ARDWebSocketChannelState state;
|
||||
@property(nonatomic, weak) id<ARDWebSocketChannelDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url
|
||||
restURL:(NSURL *)restURL
|
||||
delegate:(id<ARDWebSocketChannelDelegate>)delegate;
|
||||
|
||||
// Registers with the WebSocket server for the given room and client id once
|
||||
// the web socket connection is open.
|
||||
- (void)registerForRoomId:(NSString *)roomId
|
||||
clientId:(NSString *)clientId;
|
||||
|
||||
// Sends data over the WebSocket connection if registered, otherwise POSTs to
|
||||
// the web socket server instead.
|
||||
- (void)sendData:(NSData *)data;
|
||||
|
||||
@end
|
213
talk/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
Normal file
213
talk/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2014, 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 "ARDWebSocketChannel.h"
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
#import "SRWebSocket.h"
|
||||
|
||||
// TODO(tkchin): move these to a configuration object.
|
||||
static NSString const *kARDWSSMessageErrorKey = @"error";
|
||||
static NSString const *kARDWSSMessagePayloadKey = @"msg";
|
||||
|
||||
@interface ARDWebSocketChannel () <SRWebSocketDelegate>
|
||||
@end
|
||||
|
||||
@implementation ARDWebSocketChannel {
|
||||
NSURL *_url;
|
||||
NSURL *_restURL;
|
||||
SRWebSocket *_socket;
|
||||
}
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
@synthesize state = _state;
|
||||
@synthesize roomId = _roomId;
|
||||
@synthesize clientId = _clientId;
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url
|
||||
restURL:(NSURL *)restURL
|
||||
delegate:(id<ARDWebSocketChannelDelegate>)delegate {
|
||||
if (self = [super init]) {
|
||||
_url = url;
|
||||
_restURL = restURL;
|
||||
_delegate = delegate;
|
||||
_socket = [[SRWebSocket alloc] initWithURL:url];
|
||||
_socket.delegate = self;
|
||||
NSLog(@"Opening WebSocket.");
|
||||
[_socket open];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
- (void)setState:(ARDWebSocketChannelState)state {
|
||||
if (_state == state) {
|
||||
return;
|
||||
}
|
||||
_state = state;
|
||||
[_delegate channel:self didChangeState:_state];
|
||||
}
|
||||
|
||||
- (void)registerForRoomId:(NSString *)roomId
|
||||
clientId:(NSString *)clientId {
|
||||
NSParameterAssert(roomId.length);
|
||||
NSParameterAssert(clientId.length);
|
||||
_roomId = roomId;
|
||||
_clientId = clientId;
|
||||
if (_state == kARDWebSocketChannelStateOpen) {
|
||||
[self registerWithCollider];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)data {
|
||||
NSParameterAssert(_clientId.length);
|
||||
NSParameterAssert(_roomId.length);
|
||||
if (_state == kARDWebSocketChannelStateRegistered) {
|
||||
NSString *payload =
|
||||
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
NSDictionary *message = @{
|
||||
@"cmd": @"send",
|
||||
@"msg": payload,
|
||||
};
|
||||
NSData *messageJSONObject =
|
||||
[NSJSONSerialization dataWithJSONObject:message
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
NSString *messageString =
|
||||
[[NSString alloc] initWithData:messageJSONObject
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"C->WSS: %@", messageString);
|
||||
[_socket send:messageString];
|
||||
} else {
|
||||
NSString *dataString =
|
||||
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"C->WSS POST: %@", dataString);
|
||||
NSString *urlString =
|
||||
[NSString stringWithFormat:@"%@/%@/%@",
|
||||
[_restURL absoluteString], _roomId, _clientId];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
[NSURLConnection sendAsyncPostToURL:url
|
||||
withData:data
|
||||
completionHandler:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
if (_state == kARDWebSocketChannelStateClosed ||
|
||||
_state == kARDWebSocketChannelStateError) {
|
||||
return;
|
||||
}
|
||||
[_socket close];
|
||||
NSLog(@"C->WSS DELETE rid:%@ cid:%@", _roomId, _clientId);
|
||||
NSString *urlString =
|
||||
[NSString stringWithFormat:@"%@/%@/%@",
|
||||
[_restURL absoluteString], _roomId, _clientId];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = @"DELETE";
|
||||
request.HTTPBody = nil;
|
||||
[NSURLConnection sendAsyncRequest:request completionHandler:nil];
|
||||
}
|
||||
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
|
||||
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
|
||||
NSLog(@"WebSocket connection opened.");
|
||||
self.state = kARDWebSocketChannelStateOpen;
|
||||
if (_roomId.length && _clientId.length) {
|
||||
[self registerWithCollider];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
|
||||
NSString *messageString = message;
|
||||
NSData *messageData = [messageString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
id jsonObject = [NSJSONSerialization JSONObjectWithData:messageData
|
||||
options:0
|
||||
error:nil];
|
||||
if (![jsonObject isKindOfClass:[NSDictionary class]]) {
|
||||
NSLog(@"Unexpected message: %@", jsonObject);
|
||||
return;
|
||||
}
|
||||
NSDictionary *wssMessage = jsonObject;
|
||||
NSString *errorString = wssMessage[kARDWSSMessageErrorKey];
|
||||
if (errorString.length) {
|
||||
NSLog(@"WSS error: %@", errorString);
|
||||
return;
|
||||
}
|
||||
NSString *payload = wssMessage[kARDWSSMessagePayloadKey];
|
||||
ARDSignalingMessage *signalingMessage =
|
||||
[ARDSignalingMessage messageFromJSONString:payload];
|
||||
NSLog(@"WSS->C: %@", payload);
|
||||
[_delegate channel:self didReceiveMessage:signalingMessage];
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
|
||||
NSLog(@"WebSocket error: %@", error);
|
||||
self.state = kARDWebSocketChannelStateError;
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket
|
||||
didCloseWithCode:(NSInteger)code
|
||||
reason:(NSString *)reason
|
||||
wasClean:(BOOL)wasClean {
|
||||
NSLog(@"WebSocket closed with code: %ld reason:%@ wasClean:%d",
|
||||
(long)code, reason, wasClean);
|
||||
NSParameterAssert(_state != kARDWebSocketChannelStateError);
|
||||
self.state = kARDWebSocketChannelStateClosed;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)registerWithCollider {
|
||||
if (_state == kARDWebSocketChannelStateRegistered) {
|
||||
return;
|
||||
}
|
||||
NSParameterAssert(_roomId.length);
|
||||
NSParameterAssert(_clientId.length);
|
||||
NSDictionary *registerMessage = @{
|
||||
@"cmd": @"register",
|
||||
@"roomid" : _roomId,
|
||||
@"clientid" : _clientId,
|
||||
};
|
||||
NSData *message =
|
||||
[NSJSONSerialization dataWithJSONObject:registerMessage
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
NSString *messageString =
|
||||
[[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"Registering on WSS for rid:%@ cid:%@", _roomId, _clientId);
|
||||
// Registration can fail if server rejects it. For example, if the room is
|
||||
// full.
|
||||
[_socket send:messageString];
|
||||
self.state = kARDWebSocketChannelStateRegistered;
|
||||
}
|
||||
|
||||
@end
|
@ -1,167 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GAEChannelClient () <UIWebViewDelegate>
|
||||
|
||||
@property(nonatomic, strong) UIWebView* webView;
|
||||
|
||||
#else
|
||||
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
@interface GAEChannelClient ()
|
||||
|
||||
@property(nonatomic, strong) WebView* webView;
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation GAEChannelClient
|
||||
|
||||
- (instancetype)initWithToken:(NSString*)token
|
||||
delegate:(id<GAEMessageHandler>)delegate {
|
||||
NSParameterAssert([token length] > 0);
|
||||
NSParameterAssert(delegate);
|
||||
self = [super init];
|
||||
if (self) {
|
||||
#if TARGET_OS_IPHONE
|
||||
_webView = [[UIWebView alloc] init];
|
||||
_webView.delegate = self;
|
||||
#else
|
||||
_webView = [[WebView alloc] init];
|
||||
_webView.policyDelegate = self;
|
||||
#endif
|
||||
_delegate = delegate;
|
||||
NSString* htmlPath =
|
||||
[[NSBundle mainBundle] pathForResource:@"channel" ofType:@"html"];
|
||||
NSURL* htmlUrl = [NSURL fileURLWithPath:htmlPath];
|
||||
NSString* path = [NSString
|
||||
stringWithFormat:@"%@?token=%@", [htmlUrl absoluteString], token];
|
||||
#if TARGET_OS_IPHONE
|
||||
[_webView
|
||||
#else
|
||||
[[_webView mainFrame]
|
||||
#endif
|
||||
loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
#if TARGET_OS_IPHONE
|
||||
_webView.delegate = nil;
|
||||
[_webView stopLoading];
|
||||
#else
|
||||
_webView.policyDelegate = nil;
|
||||
[[_webView mainFrame] stopLoading];
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#pragma mark - UIWebViewDelegate
|
||||
|
||||
- (BOOL)webView:(UIWebView*)webView
|
||||
shouldStartLoadWithRequest:(NSURLRequest*)request
|
||||
navigationType:(UIWebViewNavigationType)navigationType {
|
||||
#else
|
||||
// WebPolicyDelegate is an informal delegate.
|
||||
#pragma mark - WebPolicyDelegate
|
||||
|
||||
- (void)webView:(WebView*)webView
|
||||
decidePolicyForNavigationAction:(NSDictionary*)actionInformation
|
||||
request:(NSURLRequest*)request
|
||||
frame:(WebFrame*)frame
|
||||
decisionListener:(id<WebPolicyDecisionListener>)listener {
|
||||
#endif
|
||||
NSString* scheme = [request.URL scheme];
|
||||
NSAssert(scheme, @"scheme is nil: %@", request);
|
||||
if (![scheme isEqualToString:@"js-frame"]) {
|
||||
#if TARGET_OS_IPHONE
|
||||
return YES;
|
||||
#else
|
||||
[listener use];
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
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);
|
||||
}
|
||||
});
|
||||
#if TARGET_OS_IPHONE
|
||||
return NO;
|
||||
#else
|
||||
[listener ignore];
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (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;
|
||||
}
|
||||
|
||||
@end
|
@ -50,7 +50,16 @@ static NSString const *kRTCICECandidateSdpKey = @"candidate";
|
||||
kRTCICECandidateMidKey : self.sdpMid,
|
||||
kRTCICECandidateSdpKey : self.sdp
|
||||
};
|
||||
return [NSJSONSerialization dataWithJSONObject:json options:0 error:nil];
|
||||
NSError *error = nil;
|
||||
NSData *data =
|
||||
[NSJSONSerialization dataWithJSONObject:json
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:&error];
|
||||
if (error) {
|
||||
NSLog(@"Error serializing JSON: %@", error);
|
||||
return nil;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -31,6 +31,6 @@
|
||||
|
||||
+ (RTCICEServer *)serverFromJSONDictionary:(NSDictionary *)dictionary;
|
||||
// CEOD provides different JSON, and this parses that.
|
||||
+ (RTCICEServer *)serverFromCEODJSONDictionary:(NSDictionary *)dictionary;
|
||||
+ (NSArray *)serversFromCEODJSONDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
@end
|
||||
|
@ -46,14 +46,19 @@ static NSString const *kRTCICEServerCredentialKey = @"credential";
|
||||
password:credential];
|
||||
}
|
||||
|
||||
+ (RTCICEServer *)serverFromCEODJSONDictionary:(NSDictionary *)dictionary {
|
||||
+ (NSArray *)serversFromCEODJSONDictionary:(NSDictionary *)dictionary {
|
||||
NSString *username = dictionary[kRTCICEServerUsernameKey];
|
||||
NSString *password = dictionary[kRTCICEServerPasswordKey];
|
||||
NSArray *uris = dictionary[kRTCICEServerUrisKey];
|
||||
NSParameterAssert(uris.count > 0);
|
||||
return [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:uris[0]]
|
||||
NSMutableArray *servers = [NSMutableArray arrayWithCapacity:uris.count];
|
||||
for (NSString *uri in uris) {
|
||||
RTCICEServer *server =
|
||||
[[RTCICEServer alloc] initWithURI:[NSURL URLWithString:uri]
|
||||
username:username
|
||||
password:password];
|
||||
[servers addObject:server];
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -1,94 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://apprtc.appspot.com/_ah/channel/jsapi"></script>
|
||||
</head>
|
||||
<!--
|
||||
Helper HTML that redirects Google AppEngine's Channel API to Objective C.
|
||||
This is done by hosting this page in an iOS application. The hosting
|
||||
class creates a UIWebView control and implements the UIWebViewDelegate
|
||||
protocol. Then when there is a channel message it is queued in JS,
|
||||
and an IFRAME is added to the DOM, triggering a navigation event
|
||||
|shouldStartLoadWithRequest| in Objective C which can then fetch the
|
||||
message using |popQueuedMessage|. This queuing is necessary to avoid URL
|
||||
length limits in UIWebView (which are undocumented).
|
||||
-->
|
||||
<body onbeforeunload="closeSocket()" onload="openSocket()">
|
||||
<script type="text/javascript">
|
||||
// QueryString is copy/pasta from
|
||||
// chromium's chrome/test/data/media/html/utils.js.
|
||||
var QueryString = function () {
|
||||
// Allows access to query parameters on the URL; e.g., given a URL like:
|
||||
// http://<url>/my.html?test=123&bob=123
|
||||
// parameters can now be accessed via QueryString.test or
|
||||
// QueryString.bob.
|
||||
var params = {};
|
||||
|
||||
// RegEx to split out values by &.
|
||||
var r = /([^&=]+)=?([^&]*)/g;
|
||||
|
||||
// Lambda function for decoding extracted match values. Replaces '+'
|
||||
// with space so decodeURIComponent functions properly.
|
||||
function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }
|
||||
|
||||
var match;
|
||||
while (match = r.exec(window.location.search.substring(1)))
|
||||
params[d(match[1])] = d(match[2]);
|
||||
|
||||
return params;
|
||||
} ();
|
||||
|
||||
var channel = null;
|
||||
var socket = null;
|
||||
// In-order queue of messages to be delivered to ObjectiveC.
|
||||
// Each is a JSON.stringify()'d dictionary containing a 'type'
|
||||
// field and optionally a 'payload'.
|
||||
var messageQueue = [];
|
||||
|
||||
function openSocket() {
|
||||
if (!QueryString.token || !QueryString.token.match(/^[A-z0-9_-]+$/)) {
|
||||
// Send error back to ObjC. This will assert in GAEChannelClient.m.
|
||||
sendMessageToObjC("JSError:Missing/malformed token parameter " +
|
||||
QueryString.token);
|
||||
throw "Missing/malformed token parameter: " + QueryString.token;
|
||||
}
|
||||
channel = new goog.appengine.Channel(QueryString.token);
|
||||
socket = channel.open({
|
||||
'onopen': function() {
|
||||
sendMessageToObjC("onopen");
|
||||
},
|
||||
'onmessage': function(msg) {
|
||||
sendMessageToObjC("onmessage", msg);
|
||||
},
|
||||
'onclose': function() {
|
||||
sendMessageToObjC("onclose");
|
||||
},
|
||||
'onerror': function(err) {
|
||||
sendMessageToObjC("onerror", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeSocket() {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
// Add an IFRAME to the DOM to trigger a navigation event. Then remove
|
||||
// it as it is no longer needed. Only one event is generated.
|
||||
function sendMessageToObjC(type, payload) {
|
||||
messageQueue.push(JSON.stringify({'type': type, 'payload': payload}));
|
||||
var iframe = document.createElement("IFRAME");
|
||||
iframe.setAttribute("src", "js-frame:");
|
||||
// For some reason we need to set a non-empty size for the iOS6
|
||||
// simulator...
|
||||
iframe.setAttribute("height", "1px");
|
||||
iframe.setAttribute("width", "1px");
|
||||
document.documentElement.appendChild(iframe);
|
||||
iframe.parentNode.removeChild(iframe);
|
||||
}
|
||||
|
||||
function popQueuedMessage() {
|
||||
return messageQueue.shift();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -32,38 +32,28 @@
|
||||
#import "APPRTCViewController.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "APPRTCConnectionManager.h"
|
||||
#import "ARDAppClient.h"
|
||||
#import "RTCEAGLVideoView.h"
|
||||
#import "RTCVideoTrack.h"
|
||||
|
||||
// Padding space for local video view with its parent.
|
||||
static CGFloat const kLocalViewPadding = 20;
|
||||
|
||||
@interface APPRTCViewController ()
|
||||
<APPRTCConnectionManagerDelegate, APPRTCLogger, RTCEAGLVideoViewDelegate>
|
||||
@interface APPRTCViewController () <ARDAppClientDelegate,
|
||||
RTCEAGLVideoViewDelegate>
|
||||
@property(nonatomic, assign) UIInterfaceOrientation statusBarOrientation;
|
||||
@property(nonatomic, strong) RTCEAGLVideoView* localVideoView;
|
||||
@property(nonatomic, strong) RTCEAGLVideoView* remoteVideoView;
|
||||
@end
|
||||
|
||||
@implementation APPRTCViewController {
|
||||
APPRTCConnectionManager* _connectionManager;
|
||||
ARDAppClient *_client;
|
||||
RTCVideoTrack* _localVideoTrack;
|
||||
RTCVideoTrack* _remoteVideoTrack;
|
||||
CGSize _localVideoSize;
|
||||
CGSize _remoteVideoSize;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString*)nibName
|
||||
bundle:(NSBundle*)bundle {
|
||||
if (self = [super initWithNibName:nibName bundle:bundle]) {
|
||||
_connectionManager =
|
||||
[[APPRTCConnectionManager alloc] initWithDelegate:self
|
||||
logger:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
@ -96,48 +86,46 @@ static CGFloat const kLocalViewPadding = 20;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication*)application {
|
||||
[self logMessage:@"Application lost focus, connection broken."];
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
#pragma mark - APPRTCConnectionManagerDelegate
|
||||
#pragma mark - ARDAppClientDelegate
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didChangeState:(ARDAppClientState)state {
|
||||
switch (state) {
|
||||
case kARDAppClientStateConnected:
|
||||
NSLog(@"Client connected.");
|
||||
break;
|
||||
case kARDAppClientStateConnecting:
|
||||
NSLog(@"Client connecting.");
|
||||
break;
|
||||
case kARDAppClientStateDisconnected:
|
||||
NSLog(@"Client disconnected.");
|
||||
[self resetUI];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
|
||||
_localVideoTrack = localVideoTrack;
|
||||
[_localVideoTrack addRenderer:self.localVideoView];
|
||||
self.localVideoView.hidden = NO;
|
||||
}
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
|
||||
_remoteVideoTrack = remoteVideoTrack;
|
||||
[_remoteVideoTrack addRenderer:self.remoteVideoView];
|
||||
}
|
||||
|
||||
- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
|
||||
[self showAlertWithMessage:@"Remote hung up."];
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didError:(NSError *)error {
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:@"%@", error]];
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
didErrorWithMessage:(NSString*)message {
|
||||
[self showAlertWithMessage:message];
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
#pragma mark - APPRTCLogger
|
||||
|
||||
- (void)logMessage:(NSString*)message {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString* output =
|
||||
[NSString stringWithFormat:@"%@\n%@", self.logView.text, message];
|
||||
self.logView.text = output;
|
||||
[self.logView
|
||||
scrollRangeToVisible:NSMakeRange([self.logView.text length], 0)];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - RTCEAGLVideoViewDelegate
|
||||
|
||||
- (void)videoView:(RTCEAGLVideoView*)videoView
|
||||
@ -162,9 +150,10 @@ static CGFloat const kLocalViewPadding = 20;
|
||||
textField.hidden = YES;
|
||||
self.instructionsView.hidden = YES;
|
||||
self.logView.hidden = NO;
|
||||
NSString* url =
|
||||
[NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", room];
|
||||
[_connectionManager connectToRoomWithURL:[NSURL URLWithString:url]];
|
||||
[_client disconnect];
|
||||
// TODO(tkchin): support reusing the same client object.
|
||||
_client = [[ARDAppClient alloc] initWithDelegate:self];
|
||||
[_client connectToRoomWithId:room options:nil];
|
||||
[self setupCaptureSession];
|
||||
}
|
||||
|
||||
@ -179,7 +168,7 @@ static CGFloat const kLocalViewPadding = 20;
|
||||
|
||||
- (void)disconnect {
|
||||
[self resetUI];
|
||||
[_connectionManager disconnect];
|
||||
[_client disconnect];
|
||||
}
|
||||
|
||||
- (void)showAlertWithMessage:(NSString*)message {
|
||||
|
@ -28,7 +28,7 @@
|
||||
#import "APPRTCViewController.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "APPRTCConnectionManager.h"
|
||||
#import "ARDAppClient.h"
|
||||
#import "RTCNSGLVideoView.h"
|
||||
#import "RTCVideoTrack.h"
|
||||
|
||||
@ -222,26 +222,16 @@ static NSUInteger const kLogViewHeight = 280;
|
||||
@end
|
||||
|
||||
@interface APPRTCViewController ()
|
||||
<APPRTCConnectionManagerDelegate, APPRTCMainViewDelegate, APPRTCLogger>
|
||||
<ARDAppClientDelegate, APPRTCMainViewDelegate>
|
||||
@property(nonatomic, readonly) APPRTCMainView* mainView;
|
||||
@end
|
||||
|
||||
@implementation APPRTCViewController {
|
||||
APPRTCConnectionManager* _connectionManager;
|
||||
ARDAppClient* _client;
|
||||
RTCVideoTrack* _localVideoTrack;
|
||||
RTCVideoTrack* _remoteVideoTrack;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString*)nibName
|
||||
bundle:(NSBundle*)bundle {
|
||||
if (self = [super initWithNibName:nibName bundle:bundle]) {
|
||||
_connectionManager =
|
||||
[[APPRTCConnectionManager alloc] initWithDelegate:self
|
||||
logger:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self disconnect];
|
||||
}
|
||||
@ -257,43 +247,50 @@ static NSUInteger const kLogViewHeight = 280;
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
#pragma mark - APPRTCConnectionManagerDelegate
|
||||
#pragma mark - ARDAppClientDelegate
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didChangeState:(ARDAppClientState)state {
|
||||
switch (state) {
|
||||
case kARDAppClientStateConnected:
|
||||
NSLog(@"Client connected.");
|
||||
break;
|
||||
case kARDAppClientStateConnecting:
|
||||
NSLog(@"Client connecting.");
|
||||
break;
|
||||
case kARDAppClientStateDisconnected:
|
||||
NSLog(@"Client disconnected.");
|
||||
[self resetUI];
|
||||
_client = nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
|
||||
_localVideoTrack = localVideoTrack;
|
||||
}
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
|
||||
_remoteVideoTrack = remoteVideoTrack;
|
||||
[_remoteVideoTrack addRenderer:self.mainView.remoteVideoView];
|
||||
}
|
||||
|
||||
- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
|
||||
[self showAlertWithMessage:@"Remote closed connection"];
|
||||
- (void)appClient:(ARDAppClient *)client
|
||||
didError:(NSError *)error {
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:@"%@", error]];
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||
didErrorWithMessage:(NSString*)message {
|
||||
[self showAlertWithMessage:message];
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
#pragma mark - APPRTCLogger
|
||||
|
||||
- (void)logMessage:(NSString*)message {
|
||||
[self.mainView displayLogMessage:message];
|
||||
}
|
||||
|
||||
#pragma mark - APPRTCMainViewDelegate
|
||||
|
||||
- (void)appRTCMainView:(APPRTCMainView*)mainView
|
||||
didEnterRoomId:(NSString*)roomId {
|
||||
NSString* urlString =
|
||||
[NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", roomId];
|
||||
[_connectionManager connectToRoomWithURL:[NSURL URLWithString:urlString]];
|
||||
[_client disconnect];
|
||||
ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self];
|
||||
[client connectToRoomWithId:roomId options:nil];
|
||||
_client = client;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@ -308,11 +305,15 @@ static NSUInteger const kLogViewHeight = 280;
|
||||
[alert runModal];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
- (void)resetUI {
|
||||
[_remoteVideoTrack removeRenderer:self.mainView.remoteVideoView];
|
||||
_remoteVideoTrack = nil;
|
||||
[self.mainView.remoteVideoView renderFrame:nil];
|
||||
[_connectionManager disconnect];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
[self resetUI];
|
||||
[_client disconnect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
15
talk/examples/objc/AppRTCDemo/third_party/SocketRocket/LICENSE
vendored
Normal file
15
talk/examples/objc/AppRTCDemo/third_party/SocketRocket/LICENSE
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
Copyright 2012 Square Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
132
talk/examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.h
vendored
Normal file
132
talk/examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.h
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/SecCertificate.h>
|
||||
|
||||
typedef enum {
|
||||
SR_CONNECTING = 0,
|
||||
SR_OPEN = 1,
|
||||
SR_CLOSING = 2,
|
||||
SR_CLOSED = 3,
|
||||
} SRReadyState;
|
||||
|
||||
typedef enum SRStatusCode : NSInteger {
|
||||
SRStatusCodeNormal = 1000,
|
||||
SRStatusCodeGoingAway = 1001,
|
||||
SRStatusCodeProtocolError = 1002,
|
||||
SRStatusCodeUnhandledType = 1003,
|
||||
// 1004 reserved.
|
||||
SRStatusNoStatusReceived = 1005,
|
||||
// 1004-1006 reserved.
|
||||
SRStatusCodeInvalidUTF8 = 1007,
|
||||
SRStatusCodePolicyViolated = 1008,
|
||||
SRStatusCodeMessageTooBig = 1009,
|
||||
} SRStatusCode;
|
||||
|
||||
@class SRWebSocket;
|
||||
|
||||
extern NSString *const SRWebSocketErrorDomain;
|
||||
extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
|
||||
@protocol SRWebSocketDelegate;
|
||||
|
||||
#pragma mark - SRWebSocket
|
||||
|
||||
@interface SRWebSocket : NSObject <NSStreamDelegate>
|
||||
|
||||
@property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly) SRReadyState readyState;
|
||||
@property (nonatomic, readonly, retain) NSURL *url;
|
||||
|
||||
// This returns the negotiated protocol.
|
||||
// It will be nil until after the handshake completes.
|
||||
@property (nonatomic, readonly, copy) NSString *protocol;
|
||||
|
||||
// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request;
|
||||
|
||||
// Some helper constructors.
|
||||
- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
|
||||
- (id)initWithURL:(NSURL *)url;
|
||||
|
||||
// Delegate queue will be dispatch_main_queue by default.
|
||||
// You cannot set both OperationQueue and dispatch_queue.
|
||||
- (void)setDelegateOperationQueue:(NSOperationQueue*) queue;
|
||||
- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue;
|
||||
|
||||
// By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes.
|
||||
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
|
||||
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
|
||||
|
||||
// SRWebSockets are intended for one-time-use only. Open should be called once and only once.
|
||||
- (void)open;
|
||||
|
||||
- (void)close;
|
||||
- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
|
||||
|
||||
// Send a UTF8 String or Data.
|
||||
- (void)send:(id)data;
|
||||
|
||||
// Send Data (can be nil) in a ping message.
|
||||
- (void)sendPing:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
|
||||
@protocol SRWebSocketDelegate <NSObject>
|
||||
|
||||
// message will either be an NSString if the server is using text
|
||||
// or NSData if the server is using binary.
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
|
||||
@optional
|
||||
|
||||
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - NSURLRequest (CertificateAdditions)
|
||||
|
||||
@interface NSURLRequest (CertificateAdditions)
|
||||
|
||||
@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - NSMutableURLRequest (CertificateAdditions)
|
||||
|
||||
@interface NSMutableURLRequest (CertificateAdditions)
|
||||
|
||||
@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - NSRunLoop (SRWebSocket)
|
||||
|
||||
@interface NSRunLoop (SRWebSocket)
|
||||
|
||||
+ (NSRunLoop *)SR_networkRunLoop;
|
||||
|
||||
@end
|
1761
talk/examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.m
vendored
Normal file
1761
talk/examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.m
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -226,11 +226,9 @@
|
||||
'type': 'executable',
|
||||
'product_name': 'AppRTCDemo',
|
||||
'mac_bundle': 1,
|
||||
'mac_bundle_resources': [
|
||||
'examples/objc/AppRTCDemo/channel.html',
|
||||
],
|
||||
'dependencies': [
|
||||
'libjingle.gyp:libjingle_peerconnection_objc',
|
||||
'socketrocket',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="ios"', {
|
||||
@ -265,7 +263,6 @@
|
||||
'MACOSX_DEPLOYMENT_TARGET' : '10.8',
|
||||
'OTHER_LDFLAGS': [
|
||||
'-framework AVFoundation',
|
||||
'-framework WebKit',
|
||||
],
|
||||
},
|
||||
}],
|
||||
@ -279,16 +276,18 @@
|
||||
'examples/objc/APPRTCDemo',
|
||||
],
|
||||
'sources': [
|
||||
'examples/objc/AppRTCDemo/APPRTCAppClient.h',
|
||||
'examples/objc/AppRTCDemo/APPRTCAppClient.m',
|
||||
'examples/objc/AppRTCDemo/APPRTCConnectionManager.h',
|
||||
'examples/objc/AppRTCDemo/APPRTCConnectionManager.m',
|
||||
'examples/objc/AppRTCDemo/ARDSignalingParams.h',
|
||||
'examples/objc/AppRTCDemo/ARDSignalingParams.m',
|
||||
'examples/objc/AppRTCDemo/ARDAppClient.h',
|
||||
'examples/objc/AppRTCDemo/ARDAppClient.m',
|
||||
'examples/objc/AppRTCDemo/ARDMessageResponse.h',
|
||||
'examples/objc/AppRTCDemo/ARDMessageResponse.m',
|
||||
'examples/objc/AppRTCDemo/ARDRegisterResponse.h',
|
||||
'examples/objc/AppRTCDemo/ARDRegisterResponse.m',
|
||||
'examples/objc/AppRTCDemo/ARDSignalingMessage.h',
|
||||
'examples/objc/AppRTCDemo/ARDSignalingMessage.m',
|
||||
'examples/objc/AppRTCDemo/ARDUtilities.h',
|
||||
'examples/objc/AppRTCDemo/ARDUtilities.m',
|
||||
'examples/objc/AppRTCDemo/GAEChannelClient.h',
|
||||
'examples/objc/AppRTCDemo/GAEChannelClient.m',
|
||||
'examples/objc/AppRTCDemo/ARDWebSocketChannel.h',
|
||||
'examples/objc/AppRTCDemo/ARDWebSocketChannel.m',
|
||||
'examples/objc/AppRTCDemo/RTCICECandidate+JSON.h',
|
||||
'examples/objc/AppRTCDemo/RTCICECandidate+JSON.m',
|
||||
'examples/objc/AppRTCDemo/RTCICEServer+JSON.h',
|
||||
@ -302,6 +301,47 @@
|
||||
'CLANG_ENABLE_OBJC_ARC': 'YES',
|
||||
},
|
||||
}, # target AppRTCDemo
|
||||
{
|
||||
# TODO(tkchin): move this into the real third party location and
|
||||
# have it mirrored on chrome infra.
|
||||
'target_name': 'socketrocket',
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.h',
|
||||
'examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.m',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
'xcode_settings': {
|
||||
# SocketRocket autosynthesizes some properties. Disable the
|
||||
# warning so we can compile successfully.
|
||||
'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO',
|
||||
'MACOSX_DEPLOYMENT_TARGET' : '10.8',
|
||||
},
|
||||
}],
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'examples/objc/AppRTCDemo/third_party/SocketRocket',
|
||||
],
|
||||
},
|
||||
'xcode_settings': {
|
||||
'CLANG_ENABLE_OBJC_ARC': 'YES',
|
||||
'WARNING_CFLAGS': [
|
||||
'-Wno-deprecated-declarations',
|
||||
],
|
||||
},
|
||||
'link_settings': {
|
||||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [
|
||||
'-framework CFNetwork',
|
||||
],
|
||||
},
|
||||
'libraries': [
|
||||
'$(SDKROOT)/usr/lib/libicucore.dylib',
|
||||
],
|
||||
}
|
||||
}, # target socketrocket
|
||||
], # targets
|
||||
}], # OS=="ios" or (OS=="mac" and target_arch!="ia32" and mac_sdk>="10.8")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user