Refactor iOS AppRTC parsing code.

Moved parsing code to JSON categories for the relevant objects.
No longer prefer ISAC as audio codec.

BUG=
R=glaznev@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/31989005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7755 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/examples/objc/AppRTCDemo/APPRTCAppClient.h b/talk/examples/objc/AppRTCDemo/APPRTCAppClient.h
index 8b21db5..880d5f8 100644
--- a/talk/examples/objc/AppRTCDemo/APPRTCAppClient.h
+++ b/talk/examples/objc/AppRTCDemo/APPRTCAppClient.h
@@ -27,6 +27,7 @@
 
 #import <Foundation/Foundation.h>
 
+#import "ARDSignalingParams.h"
 #import "GAEChannelClient.h"
 
 @class APPRTCAppClient;
@@ -50,14 +51,13 @@
 // for the registered handler to be called with received messages.
 @interface APPRTCAppClient : NSObject
 
-@property(nonatomic) BOOL initiator;
-@property(nonatomic, copy, readonly) RTCMediaConstraints* videoConstraints;
+@property(nonatomic, readonly) ARDSignalingParams *params;
 @property(nonatomic, weak) id<APPRTCAppClientDelegate> delegate;
 
 - (instancetype)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate
                   messageHandler:(id<GAEMessageHandler>)handler;
-- (void)connectToRoom:(NSURL*)room;
-- (void)sendData:(NSData*)data;
+- (void)connectToRoom:(NSURL *)room;
+- (void)sendData:(NSData *)data;
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 // Disallow init and don't add to documentation
diff --git a/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m b/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m
index 853496f..20e708d 100644
--- a/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m
+++ b/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m
@@ -33,8 +33,11 @@
 
 #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"
 
@@ -60,14 +63,15 @@
 }
 
 - (void)connectToRoom:(NSURL*)url {
-  NSString* urlString =
+  NSString *urlString =
       [[url absoluteString] stringByAppendingString:@"&t=json"];
-  NSURL* requestURL = [NSURL URLWithString:urlString];
-  NSURLRequest* request = [NSURLRequest requestWithURL:requestURL];
-  [self sendURLRequest:request
-      completionHandler:^(NSError* error,
-                          NSHTTPURLResponse* httpResponse,
-                          NSData* responseData) {
+  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%@",
@@ -81,116 +85,74 @@
     if (statusCode != 200) {
       return;
     }
-    [self handleResponseData:responseData
-              forRoomRequest:request];
+    [self handleResponseData:data forRoomRequest:request];
   }];
 }
 
 - (void)sendData:(NSData*)data {
   NSParameterAssert([data length] > 0);
-  NSString* message = [NSString stringWithUTF8String:[data bytes]];
+  NSString *message = [NSString stringWithUTF8String:[data bytes]];
   [self logVerbose:[NSString stringWithFormat:@"Send message:\n%@", message]];
   if (!_postMessageURL) {
     return;
   }
-  NSMutableURLRequest* request =
+  NSMutableURLRequest *request =
       [NSMutableURLRequest requestWithURL:_postMessageURL];
   request.HTTPMethod = @"POST";
   [request setHTTPBody:data];
-  [self sendURLRequest:request
-     completionHandler:^(NSError* error,
-                         NSHTTPURLResponse* httpResponse,
-                         NSData* responseData) {
+  [NSURLConnection sendAsynchronousRequest:request
+                         completionHandler:^(NSURLResponse *response,
+                                             NSData *data,
+                                             NSError *error) {
+    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
     int status = [httpResponse statusCode];
-    NSString* response = [responseData length] > 0 ?
-        [NSString stringWithUTF8String:[responseData bytes]] :
+    NSString *responseString = [data length] > 0 ?
+        [NSString stringWithUTF8String:[data bytes]] :
         nil;
     NSAssert(status == 200,
              @"Bad response [%d] to message: %@\n\n%@",
              status,
              message,
-             response);
+             responseString);
   }];
 }
 
 #pragma mark - Private
 
-- (void)logVerbose:(NSString*)message {
+- (void)logVerbose:(NSString *)message {
   if (_verboseLogging) {
     NSLog(@"%@", message);
   }
 }
 
-- (void)handleResponseData:(NSData*)responseData
-            forRoomRequest:(NSURLRequest*)request {
-  NSDictionary* roomJSON = [self parseJSONData:responseData];
-  [self logVerbose:[NSString stringWithFormat:@"Room JSON:\n%@", roomJSON]];
-  NSParameterAssert(roomJSON);
-  if (roomJSON[@"error"]) {
-    NSArray* errorMessages = roomJSON[@"error_messages"];
-    NSMutableString* message = [NSMutableString string];
-    for (NSString* errorMessage in errorMessages) {
+- (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;
   }
-  NSString* pcConfig = roomJSON[@"pc_config"];
-  NSData* pcConfigData = [pcConfig dataUsingEncoding:NSUTF8StringEncoding];
-  NSDictionary* pcConfigJSON = [self parseJSONData:pcConfigData];
-  [self logVerbose:[NSString stringWithFormat:@"PCConfig JSON:\n%@",
-                                              pcConfigJSON]];
-  NSParameterAssert(pcConfigJSON);
-
-  NSArray* iceServers = [self parseICEServersForPCConfigJSON:pcConfigJSON];
-  [self requestTURNServerForICEServers:iceServers
-                         turnServerUrl:roomJSON[@"turn_url"]];
-
-  _initiator = [roomJSON[@"initiator"] boolValue];
-  [self logVerbose:[NSString stringWithFormat:@"Initiator: %d", _initiator]];
-  _postMessageURL = [self parsePostMessageURLForRoomJSON:roomJSON
-                                                 request:request];
-  [self logVerbose:[NSString stringWithFormat:@"POST message URL:\n%@",
-                                              _postMessageURL]];
-  _videoConstraints = [self parseVideoConstraintsForRoomJSON:roomJSON];
-  [self logVerbose:[NSString stringWithFormat:@"Media constraints:\n%@",
-                                              _videoConstraints]];
-  NSString* token = roomJSON[@"token"];
+  [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];
-}
-
-- (NSDictionary*)parseJSONData:(NSData*)data {
-  NSError* error = nil;
-  NSDictionary* json =
-      [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
-  NSAssert(!error, @"Unable to parse.  %@", error.localizedDescription);
-  return json;
-}
-
-- (NSArray*)parseICEServersForPCConfigJSON:(NSDictionary*)pcConfigJSON {
-  NSMutableArray* result = [NSMutableArray array];
-  NSArray* iceServers = pcConfigJSON[@"iceServers"];
-  for (NSDictionary* iceServer in iceServers) {
-    NSString* url = iceServer[@"urls"];
-    NSString* username = pcConfigJSON[@"username"];
-    NSString* credential = iceServer[@"credential"];
-    username = username ? username : @"";
-    credential = credential ? credential : @"";
-    [self logVerbose:[NSString stringWithFormat:@"url [%@] - credential [%@]",
-                                                url,
-                                                credential]];
-    RTCICEServer* server =
-        [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url]
-                                 username:username
-                                 password:credential];
-    [result addObject:server];
-  }
-  return result;
+  _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
@@ -207,63 +169,26 @@
   return [NSURL URLWithString:postMessageUrl];
 }
 
-- (RTCMediaConstraints*)parseVideoConstraintsForRoomJSON:
-    (NSDictionary*)roomJSON {
-  NSString* mediaConstraints = roomJSON[@"media_constraints"];
-  RTCMediaConstraints* constraints = nil;
-  if ([mediaConstraints length] > 0) {
-    NSData* constraintsData =
-        [mediaConstraints dataUsingEncoding:NSUTF8StringEncoding];
-    NSDictionary* constraintsJSON = [self parseJSONData:constraintsData];
-    id video = constraintsJSON[@"video"];
-    if ([video isKindOfClass:[NSDictionary class]]) {
-      NSDictionary* mandatory = video[@"mandatory"];
-      NSMutableArray* mandatoryContraints =
-          [NSMutableArray arrayWithCapacity:[mandatory count]];
-      [mandatory enumerateKeysAndObjectsUsingBlock:^(
-          id key, id obj, BOOL* stop) {
-        [mandatoryContraints addObject:[[RTCPair alloc] initWithKey:key
-                                                              value:obj]];
-      }];
-      // TODO(tkchin): figure out json formats for optional constraints.
-      constraints =
-          [[RTCMediaConstraints alloc]
-              initWithMandatoryConstraints:mandatoryContraints
-                       optionalConstraints:nil];
-    } else if ([video isKindOfClass:[NSNumber class]] && [video boolValue]) {
-      constraints = [[RTCMediaConstraints alloc] init];
-    }
-  }
-  return constraints;
-}
-
-- (void)requestTURNServerWithUrl:(NSString*)turnServerUrl
+- (void)requestTURNServerWithUrl:(NSString *)turnServerUrl
                completionHandler:
-    (void (^)(RTCICEServer* turnServer))completionHandler {
-  NSURL* turnServerURL = [NSURL URLWithString:turnServerUrl];
-  NSMutableURLRequest* request =
+    (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"];
-  [self sendURLRequest:request
-     completionHandler:^(NSError* error,
-                         NSHTTPURLResponse* response,
-                         NSData* responseData) {
+  [NSURLConnection sendAsynchronousRequest:request
+                         completionHandler:^(NSURLResponse *response,
+                                             NSData *data,
+                                             NSError *error) {
     if (error) {
       NSLog(@"Unable to get TURN server.");
       completionHandler(nil);
       return;
     }
-    NSDictionary* json = [self parseJSONData:responseData];
-    NSString* username = json[@"username"];
-    NSString* password = json[@"password"];
-    NSArray* uris = json[@"uris"];
-    NSParameterAssert([uris count] > 0);
-    RTCICEServer* turnServer =
-        [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:uris[0]]
-                                 username:username
-                                 password:password];
+    NSDictionary *json = [NSDictionary dictionaryWithJSONData:data];
+    RTCICEServer *turnServer = [RTCICEServer serverFromCEODJSONDictionary:json];
     completionHandler(turnServer);
   }];
 }
@@ -295,26 +220,4 @@
   }
 }
 
-- (void)sendURLRequest:(NSURLRequest*)request
-     completionHandler:(void (^)(NSError* error,
-                                 NSHTTPURLResponse* httpResponse,
-                                 NSData* responseData))completionHandler {
-  dispatch_async(_backgroundQueue, ^{
-    NSError* error = nil;
-    NSURLResponse* response = nil;
-    NSData* responseData = [NSURLConnection sendSynchronousRequest:request
-                                                 returningResponse:&response
-                                                             error:&error];
-    NSParameterAssert(!response ||
-                      [response isKindOfClass:[NSHTTPURLResponse class]]);
-    if (error) {
-      NSLog(@"Failed URL request for:%@\nError:%@", request, error);
-    }
-    dispatch_async(dispatch_get_main_queue(), ^{
-      NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
-      completionHandler(error, httpResponse, responseData);
-    });
-  });
-}
-
 @end
diff --git a/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m b/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m
index 9a39528..9134e4d 100644
--- a/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m
+++ b/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m
@@ -31,6 +31,7 @@
 #import "APPRTCAppClient.h"
 #import "GAEChannelClient.h"
 #import "RTCICECandidate.h"
+#import "RTCICECandidate+JSON.h"
 #import "RTCMediaConstraints.h"
 #import "RTCMediaStream.h"
 #import "RTCPair.h"
@@ -38,6 +39,7 @@
 #import "RTCPeerConnectionDelegate.h"
 #import "RTCPeerConnectionFactory.h"
 #import "RTCSessionDescription.h"
+#import "RTCSessionDescription+JSON.h"
 #import "RTCSessionDescriptionDelegate.h"
 #import "RTCStatsDelegate.h"
 #import "RTCVideoCapturer.h"
@@ -158,7 +160,7 @@
       [RTCVideoCapturer capturerWithDeviceName:cameraID];
   self.videoSource = [self.peerConnectionFactory
       videoSourceWithCapturer:capturer
-                  constraints:self.client.videoConstraints];
+                  constraints:self.client.params.mediaConstraints];
   localVideoTrack =
       [self.peerConnectionFactory videoTrackWithID:@"ARDAMSv0"
                                             source:self.videoSource];
@@ -177,7 +179,7 @@
 #pragma mark - GAEMessageHandler methods
 
 - (void)onOpen {
-  if (!self.client.initiator) {
+  if (!self.client.params.isInitiator) {
     [self.logger logMessage:@"Callee; waiting for remote offer"];
     return;
   }
@@ -200,13 +202,8 @@
   [self.logger logMessage:[NSString stringWithFormat:@"GAE onMessage type - %@",
                                                       type]];
   if ([type isEqualToString:@"candidate"]) {
-    NSString* mid = messageData[@"id"];
-    NSNumber* sdpLineIndex = messageData[@"label"];
-    NSString* sdp = messageData[@"candidate"];
     RTCICECandidate* candidate =
-        [[RTCICECandidate alloc] initWithMid:mid
-                                       index:sdpLineIndex.intValue
-                                         sdp:sdp];
+        [RTCICECandidate candidateFromJSONDictionary:messageData];
     if (self.queuedRemoteCandidates) {
       [self.queuedRemoteCandidates addObject:candidate];
     } else {
@@ -214,10 +211,8 @@
     }
   } else if ([type isEqualToString:@"offer"] ||
              [type isEqualToString:@"answer"]) {
-    NSString* sdpString = messageData[@"sdp"];
-    RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
-        initWithType:type
-                 sdp:[[self class] preferISAC:sdpString]];
+    RTCSessionDescription* sdp =
+        [RTCSessionDescription descriptionFromJSONDictionary:messageData];
     [self.peerConnection setRemoteDescriptionWithDelegate:self
                                        sessionDescription:sdp];
     [self.logger logMessage:@"PC - setRemoteDescription."];
@@ -283,26 +278,8 @@
 - (void)peerConnection:(RTCPeerConnection*)peerConnection
        gotICECandidate:(RTCICECandidate*)candidate {
   dispatch_async(dispatch_get_main_queue(), ^{
-    NSLog(@"PCO onICECandidate.\n  Mid[%@] Index[%li] Sdp[%@]",
-          candidate.sdpMid,
-          (long)candidate.sdpMLineIndex,
-          candidate.sdp);
-    NSDictionary* json = @{
-      @"type" : @"candidate",
-      @"label" : @(candidate.sdpMLineIndex),
-      @"id" : candidate.sdpMid,
-      @"candidate" : candidate.sdp
-    };
-    NSError* error;
-    NSData* data =
-        [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
-    if (!error) {
-      [self.client sendData:data];
-    } else {
-      NSAssert(NO,
-               @"Unable to serialize JSON object with error: %@",
-               error.localizedDescription);
-    }
+    NSLog(@"PCO onICECandidate.\n%@", candidate);
+    [self.client sendData:[candidate JSONData]];
   });
 }
 
@@ -330,7 +307,7 @@
 #pragma mark - RTCSessionDescriptionDelegate
 
 - (void)peerConnection:(RTCPeerConnection*)peerConnection
-    didCreateSessionDescription:(RTCSessionDescription*)origSdp
+    didCreateSessionDescription:(RTCSessionDescription*)sdp
                           error:(NSError*)error {
   dispatch_async(dispatch_get_main_queue(), ^{
     if (error) {
@@ -339,19 +316,10 @@
       return;
     }
     [self.logger logMessage:@"SDP onSuccess(SDP) - set local description."];
-    RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
-        initWithType:origSdp.type
-                 sdp:[[self class] preferISAC:origSdp.description]];
     [self.peerConnection setLocalDescriptionWithDelegate:self
                                       sessionDescription:sdp];
     [self.logger logMessage:@"PC setLocalDescription."];
-    NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description};
-    NSError* jsonError;
-    NSData* data = [NSJSONSerialization dataWithJSONObject:json
-                                                   options:0
-                                                     error:&jsonError];
-    NSAssert(!jsonError, @"Error: %@", jsonError.description);
-    [self.client sendData:data];
+    [self.client sendData:[sdp JSONData]];
   });
 }
 
@@ -364,7 +332,7 @@
       return;
     }
     [self.logger logMessage:@"SDP onSuccess() - possibly drain candidates"];
-    if (!self.client.initiator) {
+    if (!self.client.params.isInitiator) {
       if (self.peerConnection.remoteDescription &&
           !self.peerConnection.localDescription) {
         [self.logger logMessage:@"Callee, setRemoteDescription succeeded"];
@@ -404,69 +372,6 @@
 
 #pragma mark - Private
 
-// Match |pattern| to |string| and return the first group of the first
-// match, or nil if no match was found.
-+ (NSString*)firstMatch:(NSRegularExpression*)pattern
-             withString:(NSString*)string {
-  NSTextCheckingResult* result =
-      [pattern firstMatchInString:string
-                          options:0
-                            range:NSMakeRange(0, [string length])];
-  if (!result)
-    return nil;
-  return [string substringWithRange:[result rangeAtIndex:1]];
-}
-
-// Mangle |origSDP| to prefer the ISAC/16k audio codec.
-+ (NSString*)preferISAC:(NSString*)origSDP {
-  int mLineIndex = -1;
-  NSString* isac16kRtpMap = nil;
-  NSArray* lines = [origSDP componentsSeparatedByString:@"\n"];
-  NSRegularExpression* isac16kRegex = [NSRegularExpression
-      regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$"
-                           options:0
-                             error:nil];
-  for (int i = 0;
-       (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil);
-       ++i) {
-    NSString* line = [lines objectAtIndex:i];
-    if ([line hasPrefix:@"m=audio "]) {
-      mLineIndex = i;
-      continue;
-    }
-    isac16kRtpMap = [self firstMatch:isac16kRegex withString:line];
-  }
-  if (mLineIndex == -1) {
-    NSLog(@"No m=audio line, so can't prefer iSAC");
-    return origSDP;
-  }
-  if (isac16kRtpMap == nil) {
-    NSLog(@"No ISAC/16000 line, so can't prefer iSAC");
-    return origSDP;
-  }
-  NSArray* origMLineParts =
-      [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "];
-  NSMutableArray* newMLine =
-      [NSMutableArray arrayWithCapacity:[origMLineParts count]];
-  int origPartIndex = 0;
-  // Format is: m=<media> <port> <proto> <fmt> ...
-  [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
-  [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
-  [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]];
-  [newMLine addObject:isac16kRtpMap];
-  for (; origPartIndex < [origMLineParts count]; ++origPartIndex) {
-    if (![isac16kRtpMap
-            isEqualToString:[origMLineParts objectAtIndex:origPartIndex]]) {
-      [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]];
-    }
-  }
-  NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]];
-  [newLines addObjectsFromArray:lines];
-  [newLines replaceObjectAtIndex:mLineIndex
-                      withObject:[newMLine componentsJoinedByString:@" "]];
-  return [newLines componentsJoinedByString:@"\n"];
-}
-
 - (void)drainRemoteCandidates {
   for (RTCICECandidate* candidate in self.queuedRemoteCandidates) {
     [self.peerConnection addICECandidate:candidate];
diff --git a/talk/examples/objc/AppRTCDemo/ARDSignalingParams.h b/talk/examples/objc/AppRTCDemo/ARDSignalingParams.h
new file mode 100644
index 0000000..df2114c
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ARDSignalingParams.h
@@ -0,0 +1,46 @@
+/*
+ * 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 "RTCMediaConstraints.h"
+
+// Struct for holding the signaling parameters of an AppRTC room.
+@interface ARDSignalingParams : 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;
+
++ (ARDSignalingParams *)paramsFromJSONData:(NSData *)data;
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/ARDSignalingParams.m b/talk/examples/objc/AppRTCDemo/ARDSignalingParams.m
new file mode 100644
index 0000000..58c8684
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ARDSignalingParams.m
@@ -0,0 +1,130 @@
+/*
+ * 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
diff --git a/talk/examples/objc/AppRTCDemo/ARDUtilities.h b/talk/examples/objc/AppRTCDemo/ARDUtilities.h
new file mode 100644
index 0000000..ca4d1d7
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ARDUtilities.h
@@ -0,0 +1,46 @@
+/*
+ * 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>
+
+@interface NSDictionary (ARDUtilites)
+
+// Creates a dictionary with the keys and values in the JSON object.
++ (NSDictionary *)dictionaryWithJSONString:(NSString *)jsonString;
++ (NSDictionary *)dictionaryWithJSONData:(NSData *)jsonData;
+
+@end
+
+@interface NSURLConnection (ARDUtilities)
+
+// Issues an asynchronous request that calls back on main queue.
++ (void)sendAsynchronousRequest:(NSURLRequest *)request
+              completionHandler:(void (^)(NSURLResponse *response,
+                                          NSData *data,
+                                          NSError *error))completionHandler;
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/ARDUtilities.m b/talk/examples/objc/AppRTCDemo/ARDUtilities.m
new file mode 100644
index 0000000..937013e
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ARDUtilities.m
@@ -0,0 +1,72 @@
+/*
+ * 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 "ARDUtilities.h"
+
+@implementation NSDictionary (ARDUtilites)
+
++ (NSDictionary *)dictionaryWithJSONString:(NSString *)jsonString {
+  NSParameterAssert(jsonString.length > 0);
+  NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
+  NSError *error = nil;
+  NSDictionary *dict =
+      [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
+  if (error) {
+    NSLog(@"Error parsing JSON: %@", error.localizedDescription);
+  }
+  return dict;
+}
+
++ (NSDictionary *)dictionaryWithJSONData:(NSData *)jsonData {
+  NSError *error = nil;
+  NSDictionary *dict =
+      [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
+  if (error) {
+    NSLog(@"Error parsing JSON: %@", error.localizedDescription);
+  }
+  return dict;
+}
+
+@end
+
+@implementation NSURLConnection (ARDUtilities)
+
++ (void)sendAsynchronousRequest:(NSURLRequest *)request
+              completionHandler:(void (^)(NSURLResponse *response,
+                                          NSData *data,
+                                          NSError *error))completionHandler {
+  // Kick off an async request which will call back on main thread.
+  [NSURLConnection sendAsynchronousRequest:request
+                                     queue:[NSOperationQueue mainQueue]
+                         completionHandler:^(NSURLResponse *response,
+                                             NSData *data,
+                                             NSError *error) {
+    completionHandler(response, data, error);
+  }];
+}
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h b/talk/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h
new file mode 100644
index 0000000..f0673cd
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h
@@ -0,0 +1,35 @@
+/*
+ * 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 "RTCICECandidate.h"
+
+@interface RTCICECandidate (JSON)
+
++ (RTCICECandidate *)candidateFromJSONDictionary:(NSDictionary *)dictionary;
+- (NSData *)JSONData;
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m b/talk/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m
new file mode 100644
index 0000000..62817a5
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m
@@ -0,0 +1,56 @@
+/*
+ * 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 "RTCICECandidate+JSON.h"
+
+static NSString const *kRTCICECandidateTypeKey = @"type";
+static NSString const *kRTCICECandidateTypeValue = @"candidate";
+static NSString const *kRTCICECandidateMidKey = @"id";
+static NSString const *kRTCICECandidateMLineIndexKey = @"label";
+static NSString const *kRTCICECandidateSdpKey = @"candidate";
+
+@implementation RTCICECandidate (JSON)
+
++ (RTCICECandidate *)candidateFromJSONDictionary:(NSDictionary *)dictionary {
+  NSString *mid = dictionary[kRTCICECandidateMidKey];
+  NSString *sdp = dictionary[kRTCICECandidateSdpKey];
+  NSNumber *num = dictionary[kRTCICECandidateMLineIndexKey];
+  NSInteger mLineIndex = [num integerValue];
+  return [[RTCICECandidate alloc] initWithMid:mid index:mLineIndex sdp:sdp];
+}
+
+- (NSData *)JSONData {
+  NSDictionary *json = @{
+    kRTCICECandidateTypeKey : kRTCICECandidateTypeValue,
+    kRTCICECandidateMLineIndexKey : @(self.sdpMLineIndex),
+    kRTCICECandidateMidKey : self.sdpMid,
+    kRTCICECandidateSdpKey : self.sdp
+  };
+  return [NSJSONSerialization dataWithJSONObject:json options:0 error:nil];
+}
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCICEServer+JSON.h b/talk/examples/objc/AppRTCDemo/RTCICEServer+JSON.h
new file mode 100644
index 0000000..d8a7207
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCICEServer+JSON.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "RTCICEServer.h"
+
+@interface RTCICEServer (JSON)
+
++ (RTCICEServer *)serverFromJSONDictionary:(NSDictionary *)dictionary;
+// CEOD provides different JSON, and this parses that.
++ (RTCICEServer *)serverFromCEODJSONDictionary:(NSDictionary *)dictionary;
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCICEServer+JSON.m b/talk/examples/objc/AppRTCDemo/RTCICEServer+JSON.m
new file mode 100644
index 0000000..29321f6
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCICEServer+JSON.m
@@ -0,0 +1,59 @@
+/*
+ * 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 "RTCICEServer+JSON.h"
+
+static NSString const *kRTCICEServerUsernameKey = @"username";
+static NSString const *kRTCICEServerPasswordKey = @"password";
+static NSString const *kRTCICEServerUrisKey = @"uris";
+static NSString const *kRTCICEServerUrlKey = @"urls";
+static NSString const *kRTCICEServerCredentialKey = @"credential";
+
+@implementation RTCICEServer (JSON)
+
++ (RTCICEServer *)serverFromJSONDictionary:(NSDictionary *)dictionary {
+  NSString *url = dictionary[kRTCICEServerUrlKey];
+  NSString *username = dictionary[kRTCICEServerUsernameKey];
+  NSString *credential = dictionary[kRTCICEServerCredentialKey];
+  username = username ? username : @"";
+  credential = credential ? credential : @"";
+  return [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url]
+                                  username:username
+                                  password:credential];
+}
+
++ (RTCICEServer *)serverFromCEODJSONDictionary:(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]]
+                                  username:username
+                                  password:password];
+}
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.h b/talk/examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.h
new file mode 100644
index 0000000..c0c660f
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "RTCMediaConstraints.h"
+
+@interface RTCMediaConstraints (JSON)
+
++ (RTCMediaConstraints *)constraintsFromJSONDictionary:
+    (NSDictionary *)dictionary;
+
+@end
+
diff --git a/talk/examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.m b/talk/examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.m
new file mode 100644
index 0000000..6588eb9
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.m
@@ -0,0 +1,54 @@
+/*
+ * 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 "RTCMediaConstraints+JSON.h"
+
+#import "RTCPair.h"
+
+static NSString const *kRTCMediaConstraintsMandatoryKey = @"mandatory";
+
+@implementation RTCMediaConstraints (JSON)
+
++ (RTCMediaConstraints *)constraintsFromJSONDictionary:
+    (NSDictionary *)dictionary {
+  NSDictionary *mandatory = dictionary[kRTCMediaConstraintsMandatoryKey];
+  NSMutableArray *mandatoryContraints =
+      [NSMutableArray arrayWithCapacity:[mandatory count]];
+  [mandatory enumerateKeysAndObjectsUsingBlock:^(
+      id key, id obj, BOOL *stop) {
+    [mandatoryContraints addObject:[[RTCPair alloc] initWithKey:key
+                                                          value:obj]];
+  }];
+  // TODO(tkchin): figure out json formats for optional constraints.
+  RTCMediaConstraints *constraints =
+      [[RTCMediaConstraints alloc]
+          initWithMandatoryConstraints:mandatoryContraints
+                   optionalConstraints:nil];
+  return constraints;
+}
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCSessionDescription+JSON.h b/talk/examples/objc/AppRTCDemo/RTCSessionDescription+JSON.h
new file mode 100644
index 0000000..2c18d25
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCSessionDescription+JSON.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "RTCSessionDescription.h"
+
+@interface RTCSessionDescription (JSON)
+
++ (RTCSessionDescription *)descriptionFromJSONDictionary:
+    (NSDictionary *)dictionary;
+- (NSData *)JSONData;
+
+@end
diff --git a/talk/examples/objc/AppRTCDemo/RTCSessionDescription+JSON.m b/talk/examples/objc/AppRTCDemo/RTCSessionDescription+JSON.m
new file mode 100644
index 0000000..0c1d39c
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/RTCSessionDescription+JSON.m
@@ -0,0 +1,50 @@
+/*
+ * 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 "RTCSessionDescription+JSON.h"
+
+static NSString const *kRTCSessionDescriptionTypeKey = @"type";
+static NSString const *kRTCSessionDescriptionSdpKey = @"sdp";
+
+@implementation RTCSessionDescription (JSON)
+
++ (RTCSessionDescription *)descriptionFromJSONDictionary:
+    (NSDictionary *)dictionary {
+  NSString *type = dictionary[kRTCSessionDescriptionTypeKey];
+  NSString *sdp = dictionary[kRTCSessionDescriptionSdpKey];
+  return [[RTCSessionDescription alloc] initWithType:type sdp:sdp];
+}
+
+- (NSData *)JSONData {
+  NSDictionary *json = @{
+    kRTCSessionDescriptionTypeKey : self.type,
+    kRTCSessionDescriptionSdpKey : self.description
+  };
+  return [NSJSONSerialization dataWithJSONObject:json options:0 error:nil];
+}
+
+@end
diff --git a/talk/libjingle_examples.gyp b/talk/libjingle_examples.gyp
index 740452d..f17fbfe 100755
--- a/talk/libjingle_examples.gyp
+++ b/talk/libjingle_examples.gyp
@@ -283,8 +283,20 @@
             '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/ARDUtilities.h',
+            'examples/objc/AppRTCDemo/ARDUtilities.m',
             'examples/objc/AppRTCDemo/GAEChannelClient.h',
             'examples/objc/AppRTCDemo/GAEChannelClient.m',
+            'examples/objc/AppRTCDemo/RTCICECandidate+JSON.h',
+            'examples/objc/AppRTCDemo/RTCICECandidate+JSON.m',
+            'examples/objc/AppRTCDemo/RTCICEServer+JSON.h',
+            'examples/objc/AppRTCDemo/RTCICEServer+JSON.m',
+            'examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.h',
+            'examples/objc/AppRTCDemo/RTCMediaConstraints+JSON.m',
+            'examples/objc/AppRTCDemo/RTCSessionDescription+JSON.h',
+            'examples/objc/AppRTCDemo/RTCSessionDescription+JSON.m',
           ],
           'xcode_settings': {
             'CLANG_ENABLE_OBJC_ARC': 'YES',