зеркало из https://github.com/nextcloud/talk-ios.git
First draft for calling feature.
Signed-off-by: Ivan Sein <ivan@nextcloud.com>
This commit is contained in:
Родитель
bf9691ca57
Коммит
a5639e3d9e
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <WebRTC/RTCCameraVideoCapturer.h>
|
||||
|
||||
@class ARDSettingsModel;
|
||||
|
||||
// Controls the camera. Handles starting the capture, switching cameras etc.
|
||||
@interface ARDCaptureController : NSObject
|
||||
|
||||
- (instancetype)initWithCapturer:(RTCCameraVideoCapturer *)capturer
|
||||
settings:(ARDSettingsModel *)settings;
|
||||
- (void)startCapture;
|
||||
- (void)stopCapture;
|
||||
- (void)switchCamera;
|
||||
|
||||
@end
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "ARDCaptureController.h"
|
||||
|
||||
#import "ARDSettingsModel.h"
|
||||
|
||||
@implementation ARDCaptureController {
|
||||
RTCCameraVideoCapturer *_capturer;
|
||||
ARDSettingsModel *_settings;
|
||||
BOOL _usingFrontCamera;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCapturer:(RTCCameraVideoCapturer *)capturer
|
||||
settings:(ARDSettingsModel *)settings {
|
||||
if ([super init]) {
|
||||
_capturer = capturer;
|
||||
_settings = settings;
|
||||
_usingFrontCamera = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startCapture {
|
||||
AVCaptureDevicePosition position =
|
||||
_usingFrontCamera ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
|
||||
AVCaptureDevice *device = [self findDeviceForPosition:position];
|
||||
AVCaptureDeviceFormat *format = [self selectFormatForDevice:device];
|
||||
NSInteger fps = [self selectFpsForFormat:format];
|
||||
|
||||
[_capturer startCaptureWithDevice:device format:format fps:fps];
|
||||
}
|
||||
|
||||
- (void)stopCapture {
|
||||
[_capturer stopCapture];
|
||||
}
|
||||
|
||||
- (void)switchCamera {
|
||||
_usingFrontCamera = !_usingFrontCamera;
|
||||
[self startCapture];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (AVCaptureDevice *)findDeviceForPosition:(AVCaptureDevicePosition)position {
|
||||
NSArray<AVCaptureDevice *> *captureDevices = [RTCCameraVideoCapturer captureDevices];
|
||||
for (AVCaptureDevice *device in captureDevices) {
|
||||
if (device.position == position) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return captureDevices[0];
|
||||
}
|
||||
|
||||
- (AVCaptureDeviceFormat *)selectFormatForDevice:(AVCaptureDevice *)device {
|
||||
NSArray<AVCaptureDeviceFormat *> *formats =
|
||||
[RTCCameraVideoCapturer supportedFormatsForDevice:device];
|
||||
int targetWidth = [_settings currentVideoResolutionWidthFromStore];
|
||||
int targetHeight = [_settings currentVideoResolutionHeightFromStore];
|
||||
AVCaptureDeviceFormat *selectedFormat = nil;
|
||||
int currentDiff = INT_MAX;
|
||||
|
||||
for (AVCaptureDeviceFormat *format in formats) {
|
||||
CMVideoDimensions dimension = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
|
||||
int diff = abs(targetWidth - dimension.width) + abs(targetHeight - dimension.height);
|
||||
if (diff < currentDiff) {
|
||||
selectedFormat = format;
|
||||
currentDiff = diff;
|
||||
}
|
||||
}
|
||||
|
||||
NSAssert(selectedFormat != nil, @"No suitable capture format found.");
|
||||
return selectedFormat;
|
||||
}
|
||||
|
||||
- (NSInteger)selectFpsForFormat:(AVCaptureDeviceFormat *)format {
|
||||
Float64 maxFramerate = 0;
|
||||
for (AVFrameRateRange *fpsRange in format.videoSupportedFrameRateRanges) {
|
||||
maxFramerate = fmax(maxFramerate, fpsRange.maxFrameRate);
|
||||
}
|
||||
return maxFramerate;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ARDSettingsModel.h"
|
||||
|
||||
@class ARDSettingsStore;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@interface ARDSettingsModel ()
|
||||
- (ARDSettingsStore *)settingsStore;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* Model class for user defined settings.
|
||||
*
|
||||
* Handles storing the settings and provides default values if setting is not
|
||||
* set. Also provides list of available options for different settings. Stores
|
||||
* for example video codec, video resolution and maximum bitrate.
|
||||
*/
|
||||
@interface ARDSettingsModel : NSObject
|
||||
|
||||
/**
|
||||
* Returns array of available capture resoultions.
|
||||
*
|
||||
* The capture resolutions are represented as strings in the following format
|
||||
* [width]x[height]
|
||||
*/
|
||||
- (NSArray<NSString *> *)availableVideoResolutions;
|
||||
|
||||
/**
|
||||
* Returns current video resolution string.
|
||||
* If no resolution is in store, default value of 640x480 is returned.
|
||||
* When defaulting to value, the default is saved in store for consistency reasons.
|
||||
*/
|
||||
- (NSString *)currentVideoResolutionSettingFromStore;
|
||||
- (int)currentVideoResolutionWidthFromStore;
|
||||
- (int)currentVideoResolutionHeightFromStore;
|
||||
|
||||
/**
|
||||
* Stores the provided video resolution string into the store.
|
||||
*
|
||||
* If the provided resolution is no part of the available video resolutions
|
||||
* the store operation will not be executed and NO will be returned.
|
||||
* @param resolution the string to be stored.
|
||||
* @return YES/NO depending on success.
|
||||
*/
|
||||
- (BOOL)storeVideoResolutionSetting:(NSString *)resolution;
|
||||
|
||||
/**
|
||||
* Returns array of available video codecs.
|
||||
*/
|
||||
- (NSArray<NSString *> *)availableVideoCodecs;
|
||||
|
||||
/**
|
||||
* Returns current video codec setting from store if present or default (H264) otherwise.
|
||||
*/
|
||||
- (NSString *)currentVideoCodecSettingFromStore;
|
||||
|
||||
/**
|
||||
* Stores the provided video codec setting into the store.
|
||||
*
|
||||
* If the provided video codec is not part of the available video codecs
|
||||
* the store operation will not be executed and NO will be returned.
|
||||
* @param video codec settings the string to be stored.
|
||||
* @return YES/NO depending on success.
|
||||
*/
|
||||
- (BOOL)storeVideoCodecSetting:(NSString *)videoCodec;
|
||||
|
||||
/**
|
||||
* Returns current max bitrate setting from store if present.
|
||||
*/
|
||||
- (nullable NSNumber *)currentMaxBitrateSettingFromStore;
|
||||
|
||||
/**
|
||||
* Stores the provided bitrate value into the store.
|
||||
*
|
||||
* @param bitrate NSNumber representation of the max bitrate value.
|
||||
*/
|
||||
- (void)storeMaxBitrateSetting:(nullable NSNumber *)bitrate;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "ARDSettingsModel+Private.h"
|
||||
#import "ARDSettingsStore.h"
|
||||
#import "WebRTC/RTCMediaConstraints.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
static NSArray<NSString *> *videoResolutionsStaticValues() {
|
||||
return @[ @"640x480", @"960x540", @"1280x720" ];
|
||||
}
|
||||
|
||||
static NSArray<NSString *> *videoCodecsStaticValues() {
|
||||
return @[ @"H264", @"VP8", @"VP9" ];
|
||||
}
|
||||
|
||||
@interface ARDSettingsModel () {
|
||||
ARDSettingsStore *_settingsStore;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ARDSettingsModel
|
||||
|
||||
- (NSArray<NSString *> *)availableVideoResolutions {
|
||||
return videoResolutionsStaticValues();
|
||||
}
|
||||
|
||||
- (NSString *)currentVideoResolutionSettingFromStore {
|
||||
NSString *resolution = [[self settingsStore] videoResolution];
|
||||
if (!resolution) {
|
||||
resolution = [self defaultVideoResolutionSetting];
|
||||
// To ensure consistency add the default to the store.
|
||||
[[self settingsStore] setVideoResolution:resolution];
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
|
||||
- (BOOL)storeVideoResolutionSetting:(NSString *)resolution {
|
||||
if (![[self availableVideoResolutions] containsObject:resolution]) {
|
||||
return NO;
|
||||
}
|
||||
[[self settingsStore] setVideoResolution:resolution];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)availableVideoCodecs {
|
||||
return videoCodecsStaticValues();
|
||||
}
|
||||
|
||||
- (NSString *)currentVideoCodecSettingFromStore {
|
||||
NSString *videoCodec = [[self settingsStore] videoCodec];
|
||||
if (!videoCodec) {
|
||||
videoCodec = [self defaultVideoCodecSetting];
|
||||
[[self settingsStore] setVideoCodec:videoCodec];
|
||||
}
|
||||
return videoCodec;
|
||||
}
|
||||
|
||||
- (BOOL)storeVideoCodecSetting:(NSString *)videoCodec {
|
||||
if (![[self availableVideoCodecs] containsObject:videoCodec]) {
|
||||
return NO;
|
||||
}
|
||||
[[self settingsStore] setVideoCodec:videoCodec];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (nullable NSNumber *)currentMaxBitrateSettingFromStore {
|
||||
return [[self settingsStore] maxBitrate];
|
||||
}
|
||||
|
||||
- (void)storeMaxBitrateSetting:(nullable NSNumber *)bitrate {
|
||||
[[self settingsStore] setMaxBitrate:bitrate];
|
||||
}
|
||||
|
||||
#pragma mark - Testable
|
||||
|
||||
- (ARDSettingsStore *)settingsStore {
|
||||
if (!_settingsStore) {
|
||||
_settingsStore = [[ARDSettingsStore alloc] init];
|
||||
}
|
||||
return _settingsStore;
|
||||
}
|
||||
|
||||
- (int)currentVideoResolutionWidthFromStore {
|
||||
NSString *resolution = [self currentVideoResolutionSettingFromStore];
|
||||
|
||||
return [self videoResolutionComponentAtIndex:0 inString:resolution];
|
||||
}
|
||||
|
||||
- (int)currentVideoResolutionHeightFromStore {
|
||||
NSString *resolution = [self currentVideoResolutionSettingFromStore];
|
||||
return [self videoResolutionComponentAtIndex:1 inString:resolution];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSString *)defaultVideoResolutionSetting {
|
||||
return videoResolutionsStaticValues()[0];
|
||||
}
|
||||
|
||||
- (int)videoResolutionComponentAtIndex:(int)index inString:(NSString *)resolution {
|
||||
if (index != 0 && index != 1) {
|
||||
return 0;
|
||||
}
|
||||
NSArray<NSString *> *components = [resolution componentsSeparatedByString:@"x"];
|
||||
if (components.count != 2) {
|
||||
return 0;
|
||||
}
|
||||
return components[index].intValue;
|
||||
}
|
||||
|
||||
- (NSString *)defaultVideoCodecSetting {
|
||||
return videoCodecsStaticValues()[0];
|
||||
}
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Light-weight persistent store for user settings.
|
||||
*
|
||||
* It will persist between application launches and application updates.
|
||||
*/
|
||||
@interface ARDSettingsStore : NSObject
|
||||
|
||||
@property(nonatomic) NSString *videoResolution;
|
||||
@property(nonatomic) NSString *videoCodec;
|
||||
|
||||
/**
|
||||
* Returns current max bitrate number stored in the store.
|
||||
*/
|
||||
- (nullable NSNumber *)maxBitrate;
|
||||
|
||||
/**
|
||||
* Stores the provided value as maximum bitrate setting.
|
||||
* @param value the number to be stored
|
||||
*/
|
||||
- (void)setMaxBitrate:(nullable NSNumber *)value;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "ARDSettingsStore.h"
|
||||
|
||||
static NSString *const kVideoResolutionKey = @"rtc_video_resolution_key";
|
||||
static NSString *const kVideoCodecKey = @"rtc_video_codec_key";
|
||||
static NSString *const kBitrateKey = @"rtc_max_bitrate_key";
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@interface ARDSettingsStore () {
|
||||
NSUserDefaults *_storage;
|
||||
}
|
||||
@property(nonatomic, strong, readonly) NSUserDefaults *storage;
|
||||
@end
|
||||
|
||||
@implementation ARDSettingsStore
|
||||
|
||||
- (NSUserDefaults *)storage {
|
||||
if (!_storage) {
|
||||
_storage = [NSUserDefaults standardUserDefaults];
|
||||
}
|
||||
return _storage;
|
||||
}
|
||||
|
||||
- (NSString *)videoResolution {
|
||||
return [self.storage objectForKey:kVideoResolutionKey];
|
||||
}
|
||||
|
||||
- (void)setVideoResolution:(NSString *)resolution {
|
||||
[self.storage setObject:resolution forKey:kVideoResolutionKey];
|
||||
[self.storage synchronize];
|
||||
}
|
||||
|
||||
- (NSString *)videoCodec {
|
||||
return [self.storage objectForKey:kVideoCodecKey];
|
||||
}
|
||||
|
||||
- (void)setVideoCodec:(NSString *)videoCodec {
|
||||
[self.storage setObject:videoCodec forKey:kVideoCodecKey];
|
||||
[self.storage synchronize];
|
||||
}
|
||||
|
||||
- (nullable NSNumber *)maxBitrate {
|
||||
return [self.storage objectForKey:kBitrateKey];
|
||||
}
|
||||
|
||||
- (void)setMaxBitrate:(nullable NSNumber *)value {
|
||||
[self.storage setObject:value forKey:kBitrateKey];
|
||||
[self.storage synchronize];
|
||||
}
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "WebRTC/RTCIceCandidate.h"
|
||||
#import "WebRTC/RTCSessionDescription.h"
|
||||
|
||||
typedef enum {
|
||||
kARDSignalingMessageTypeCandidate,
|
||||
kARDSignalingMessageTypeCandidateRemoval,
|
||||
kARDSignalingMessageTypeOffer,
|
||||
kARDSignalingMessageTypeAnswer,
|
||||
kARDSignalingMessageTypeBye,
|
||||
} ARDSignalingMessageType;
|
||||
|
||||
@interface ARDSignalingMessage : NSObject
|
||||
|
||||
@property(nonatomic, readonly) ARDSignalingMessageType type;
|
||||
|
||||
+ (ARDSignalingMessage *)messageFromJSONString:(NSString *)jsonString;
|
||||
- (NSData *)JSONData;
|
||||
|
||||
@end
|
||||
|
||||
@interface ARDICECandidateMessage : ARDSignalingMessage
|
||||
|
||||
@property(nonatomic, readonly) RTCIceCandidate *candidate;
|
||||
|
||||
- (instancetype)initWithCandidate:(RTCIceCandidate *)candidate;
|
||||
|
||||
@end
|
||||
|
||||
@interface ARDICECandidateRemovalMessage : ARDSignalingMessage
|
||||
|
||||
@property(nonatomic, readonly) NSArray<RTCIceCandidate *> *candidates;
|
||||
|
||||
- (instancetype)initWithRemovedCandidates:
|
||||
(NSArray<RTCIceCandidate *> *)candidates;
|
||||
|
||||
@end
|
||||
|
||||
@interface ARDSessionDescriptionMessage : ARDSignalingMessage
|
||||
|
||||
@property(nonatomic, readonly) RTCSessionDescription *sessionDescription;
|
||||
|
||||
- (instancetype)initWithDescription:(RTCSessionDescription *)description;
|
||||
|
||||
@end
|
||||
|
||||
@interface ARDByeMessage : ARDSignalingMessage
|
||||
@end
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "ARDSignalingMessage.h"
|
||||
|
||||
#import "WebRTC/RTCLogging.h"
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
#import "RTCIceCandidate+JSON.h"
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
|
||||
static NSString * const kARDSignalingMessageTypeKey = @"type";
|
||||
static NSString * const kARDTypeValueRemoveCandidates = @"remove-candidates";
|
||||
|
||||
@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) {
|
||||
RTCLogError(@"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:kARDTypeValueRemoveCandidates]) {
|
||||
RTCLogInfo(@"Received remove-candidates message");
|
||||
NSArray<RTCIceCandidate *> *candidates =
|
||||
[RTCIceCandidate candidatesFromJSONDictionary:values];
|
||||
message = [[ARDICECandidateRemovalMessage alloc]
|
||||
initWithRemovedCandidates:candidates];
|
||||
} 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 {
|
||||
RTCLogError(@"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 ARDICECandidateRemovalMessage
|
||||
|
||||
@synthesize candidates = _candidates;
|
||||
|
||||
- (instancetype)initWithRemovedCandidates:(
|
||||
NSArray<RTCIceCandidate *> *)candidates {
|
||||
NSParameterAssert(candidates.count);
|
||||
if (self = [super initWithType:kARDSignalingMessageTypeCandidateRemoval]) {
|
||||
_candidates = candidates;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
return
|
||||
[RTCIceCandidate JSONDataForIceCandidates:_candidates
|
||||
withType:kARDTypeValueRemoveCandidates];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ARDSessionDescriptionMessage
|
||||
|
||||
@synthesize sessionDescription = _sessionDescription;
|
||||
|
||||
- (instancetype)initWithDescription:(RTCSessionDescription *)description {
|
||||
ARDSignalingMessageType messageType = kARDSignalingMessageTypeOffer;
|
||||
RTCSdpType sdpType = description.type;
|
||||
switch (sdpType) {
|
||||
case RTCSdpTypeOffer:
|
||||
messageType = kARDSignalingMessageTypeOffer;
|
||||
break;
|
||||
case RTCSdpTypeAnswer:
|
||||
messageType = kARDSignalingMessageTypeAnswer;
|
||||
break;
|
||||
case RTCSdpTypePrAnswer:
|
||||
NSAssert(NO, @"Unexpected type: %@",
|
||||
[RTCSessionDescription stringForType:sdpType]);
|
||||
break;
|
||||
}
|
||||
if (self = [super initWithType:messageType]) {
|
||||
_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
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#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)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
|
||||
|
||||
NSInteger ARDGetCpuUsagePercentage();
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
|
||||
#import <mach/mach.h>
|
||||
|
||||
#import "WebRTC/RTCLogging.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) {
|
||||
RTCLogError(@"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) {
|
||||
RTCLogError(@"Error parsing JSON: %@", error.localizedDescription);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSURLConnection (ARDUtilities)
|
||||
|
||||
+ (void)sendAsyncRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLResponse *response,
|
||||
NSData *data,
|
||||
NSError *error))completionHandler {
|
||||
// Kick off an async request which will call back on main thread.
|
||||
NSURLSession *session = [NSURLSession sharedSession];
|
||||
[[session dataTaskWithRequest:request
|
||||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (completionHandler) {
|
||||
completionHandler(response, data, error);
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
// 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) {
|
||||
RTCLogError(@"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;
|
||||
RTCLogError(@"Received bad response: %@", serverResponse);
|
||||
if (completionHandler) {
|
||||
completionHandler(NO, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(YES, data);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NSInteger ARDGetCpuUsagePercentage() {
|
||||
// Create an array of thread ports for the current task.
|
||||
const task_t task = mach_task_self();
|
||||
thread_act_array_t thread_array;
|
||||
mach_msg_type_number_t thread_count;
|
||||
if (task_threads(task, &thread_array, &thread_count) != KERN_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Sum cpu usage from all threads.
|
||||
float cpu_usage_percentage = 0;
|
||||
thread_basic_info_data_t thread_info_data = {};
|
||||
mach_msg_type_number_t thread_info_count;
|
||||
for (size_t i = 0; i < thread_count; ++i) {
|
||||
thread_info_count = THREAD_BASIC_INFO_COUNT;
|
||||
kern_return_t ret = thread_info(thread_array[i],
|
||||
THREAD_BASIC_INFO,
|
||||
(thread_info_t)&thread_info_data,
|
||||
&thread_info_count);
|
||||
if (ret == KERN_SUCCESS) {
|
||||
cpu_usage_percentage +=
|
||||
100.f * (float)thread_info_data.cpu_usage / TH_USAGE_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
// Dealloc the created array.
|
||||
vm_deallocate(task, (vm_address_t)thread_array,
|
||||
sizeof(thread_act_t) * thread_count);
|
||||
return lroundf(cpu_usage_percentage);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "WebRTC/RTCIceCandidate.h"
|
||||
|
||||
@interface RTCIceCandidate (JSON)
|
||||
|
||||
+ (RTCIceCandidate *)candidateFromJSONDictionary:(NSDictionary *)dictionary;
|
||||
+ (NSArray<RTCIceCandidate *> *)candidatesFromJSONDictionary:
|
||||
(NSDictionary *)dictionary;
|
||||
+ (NSData *)JSONDataForIceCandidates:(NSArray<RTCIceCandidate *> *)candidates
|
||||
withType:(NSString *)typeValue;
|
||||
- (NSData *)JSONData;
|
||||
|
||||
@end
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "RTCIceCandidate+JSON.h"
|
||||
|
||||
#import "WebRTC/RTCLogging.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";
|
||||
static NSString const *kRTCICECandidatesTypeKey = @"candidates";
|
||||
|
||||
|
||||
@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] initWithSdp:sdp
|
||||
sdpMLineIndex:mLineIndex
|
||||
sdpMid:mid];
|
||||
}
|
||||
|
||||
+ (NSData *)JSONDataForIceCandidates:(NSArray<RTCIceCandidate *> *)candidates
|
||||
withType:(NSString *)typeValue {
|
||||
NSMutableArray *jsonCandidates =
|
||||
[NSMutableArray arrayWithCapacity:candidates.count];
|
||||
for (RTCIceCandidate *candidate in candidates) {
|
||||
NSDictionary *jsonCandidate = [candidate JSONDictionary];
|
||||
[jsonCandidates addObject:jsonCandidate];
|
||||
}
|
||||
NSDictionary *json = @{
|
||||
kRTCICECandidateTypeKey : typeValue,
|
||||
kRTCICECandidatesTypeKey : jsonCandidates
|
||||
};
|
||||
NSError *error = nil;
|
||||
NSData *data =
|
||||
[NSJSONSerialization dataWithJSONObject:json
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:&error];
|
||||
if (error) {
|
||||
RTCLogError(@"Error serializing JSON: %@", error);
|
||||
return nil;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
+ (NSArray<RTCIceCandidate *> *)candidatesFromJSONDictionary:
|
||||
(NSDictionary *)dictionary {
|
||||
NSArray *jsonCandidates = dictionary[kRTCICECandidatesTypeKey];
|
||||
NSMutableArray<RTCIceCandidate *> *candidates =
|
||||
[NSMutableArray arrayWithCapacity:jsonCandidates.count];
|
||||
for (NSDictionary *jsonCandidate in jsonCandidates) {
|
||||
RTCIceCandidate *candidate =
|
||||
[RTCIceCandidate candidateFromJSONDictionary:jsonCandidate];
|
||||
[candidates addObject:candidate];
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
NSDictionary *json = @{
|
||||
kRTCICECandidateTypeKey : kRTCICECandidateTypeValue,
|
||||
kRTCICECandidateMLineIndexKey : @(self.sdpMLineIndex),
|
||||
kRTCICECandidateMidKey : self.sdpMid,
|
||||
kRTCICECandidateSdpKey : self.sdp
|
||||
};
|
||||
NSError *error = nil;
|
||||
NSData *data =
|
||||
[NSJSONSerialization dataWithJSONObject:json
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:&error];
|
||||
if (error) {
|
||||
RTCLogError(@"Error serializing JSON: %@", error);
|
||||
return nil;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSDictionary *)JSONDictionary{
|
||||
NSDictionary *json = @{
|
||||
kRTCICECandidateMLineIndexKey : @(self.sdpMLineIndex),
|
||||
kRTCICECandidateMidKey : self.sdpMid,
|
||||
kRTCICECandidateSdpKey : self.sdp
|
||||
};
|
||||
return json;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "WebRTC/RTCSessionDescription.h"
|
||||
|
||||
@interface RTCSessionDescription (JSON)
|
||||
|
||||
+ (RTCSessionDescription *)descriptionFromJSONDictionary:
|
||||
(NSDictionary *)dictionary;
|
||||
- (NSData *)JSONData;
|
||||
|
||||
@end
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
|
||||
static NSString const *kRTCSessionDescriptionTypeKey = @"type";
|
||||
static NSString const *kRTCSessionDescriptionSdpKey = @"sdp";
|
||||
|
||||
@implementation RTCSessionDescription (JSON)
|
||||
|
||||
+ (RTCSessionDescription *)descriptionFromJSONDictionary:
|
||||
(NSDictionary *)dictionary {
|
||||
NSString *typeString = dictionary[kRTCSessionDescriptionTypeKey];
|
||||
RTCSdpType type = [[self class] typeForString:typeString];
|
||||
NSString *sdp = dictionary[kRTCSessionDescriptionSdpKey];
|
||||
return [[RTCSessionDescription alloc] initWithType:type sdp:sdp];
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
NSString *type = [[self class] stringForType:self.type];
|
||||
NSDictionary *json = @{
|
||||
kRTCSessionDescriptionTypeKey : type,
|
||||
kRTCSessionDescriptionSdpKey : self.sdp
|
||||
};
|
||||
return [NSJSONSerialization dataWithJSONObject:json options:0 error:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -13,6 +13,18 @@
|
|||
2C0574931EDD9E8E00D9E7F2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2C0574911EDD9E8E00D9E7F2 /* LaunchScreen.storyboard */; };
|
||||
2C0574A41EDDA2E300D9E7F2 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C0574A21EDDA2E300D9E7F2 /* LoginViewController.m */; };
|
||||
2C0574A51EDDA2E300D9E7F2 /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C0574A31EDDA2E300D9E7F2 /* LoginViewController.xib */; };
|
||||
2C2E64251F3462AF00D39CE8 /* NCSignalingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C2E64241F3462AF00D39CE8 /* NCSignalingMessage.m */; };
|
||||
2C4D7D5A1F2F6D4500FF4A0D /* CallViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D581F2F6D4500FF4A0D /* CallViewController.m */; };
|
||||
2C4D7D5B1F2F6D4500FF4A0D /* CallViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C4D7D591F2F6D4500FF4A0D /* CallViewController.xib */; };
|
||||
2C4D7D5D1F2F6E9600FF4A0D /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C4D7D5C1F2F6E9600FF4A0D /* WebRTC.framework */; };
|
||||
2C4D7D5E1F2F6E9600FF4A0D /* WebRTC.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2C4D7D5C1F2F6E9600FF4A0D /* WebRTC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
2C4D7D631F2F7C2C00FF4A0D /* ARDCaptureController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D621F2F7C2C00FF4A0D /* ARDCaptureController.m */; };
|
||||
2C4D7D691F2F7DBC00FF4A0D /* ARDSettingsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D651F2F7DBC00FF4A0D /* ARDSettingsModel.m */; };
|
||||
2C4D7D6A1F2F7DBC00FF4A0D /* ARDSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D681F2F7DBC00FF4A0D /* ARDSettingsStore.m */; };
|
||||
2C4D7D711F309DA500FF4A0D /* ARDSignalingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D6C1F309DA500FF4A0D /* ARDSignalingMessage.m */; };
|
||||
2C4D7D721F309DA500FF4A0D /* RTCIceCandidate+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D6E1F309DA500FF4A0D /* RTCIceCandidate+JSON.m */; };
|
||||
2C4D7D731F309DA500FF4A0D /* RTCSessionDescription+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D701F309DA500FF4A0D /* RTCSessionDescription+JSON.m */; };
|
||||
2C4D7D761F30F7B600FF4A0D /* ARDUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C4D7D751F30F7B600FF4A0D /* ARDUtilities.m */; };
|
||||
2C90E5641EDDE0FB0093D85A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C90E5631EDDE0FB0093D85A /* Foundation.framework */; };
|
||||
2C90E5671EDDE1340093D85A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C90E5661EDDE1340093D85A /* CoreGraphics.framework */; };
|
||||
2C90E5691EDDE13A0093D85A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C90E5681EDDE13A0093D85A /* UIKit.framework */; };
|
||||
|
@ -39,6 +51,20 @@
|
|||
DB6A892B5CEBD4812F7C52EF /* libPods-VideoCalls.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6267808DF11BEB859C0BE9F1 /* libPods-VideoCalls.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
2C4D7D5F1F2F6E9600FF4A0D /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
2C4D7D5E1F2F6E9600FF4A0D /* WebRTC.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2C05747D1EDD9E8E00D9E7F2 /* VideoCalls.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoCalls.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2C0574811EDD9E8E00D9E7F2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
|
@ -50,6 +76,28 @@
|
|||
2C0574A11EDDA2E300D9E7F2 /* LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = "<group>"; };
|
||||
2C0574A21EDDA2E300D9E7F2 /* LoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = "<group>"; };
|
||||
2C0574A31EDDA2E300D9E7F2 /* LoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginViewController.xib; sourceTree = "<group>"; };
|
||||
2C2E64231F3462AF00D39CE8 /* NCSignalingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NCSignalingMessage.h; sourceTree = "<group>"; };
|
||||
2C2E64241F3462AF00D39CE8 /* NCSignalingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NCSignalingMessage.m; sourceTree = "<group>"; };
|
||||
2C4D7D551F2F607E00FF4A0D /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = ThirdParty/WebRTC/WebRTC.framework; sourceTree = "<group>"; };
|
||||
2C4D7D571F2F6D4500FF4A0D /* CallViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallViewController.h; sourceTree = "<group>"; };
|
||||
2C4D7D581F2F6D4500FF4A0D /* CallViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallViewController.m; sourceTree = "<group>"; };
|
||||
2C4D7D591F2F6D4500FF4A0D /* CallViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CallViewController.xib; sourceTree = "<group>"; };
|
||||
2C4D7D5C1F2F6E9600FF4A0D /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = ThirdParty/WebRTC/WebRTC.framework; sourceTree = "<group>"; };
|
||||
2C4D7D611F2F7C2C00FF4A0D /* ARDCaptureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDCaptureController.h; sourceTree = "<group>"; };
|
||||
2C4D7D621F2F7C2C00FF4A0D /* ARDCaptureController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARDCaptureController.m; sourceTree = "<group>"; };
|
||||
2C4D7D641F2F7DBC00FF4A0D /* ARDSettingsModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDSettingsModel.h; sourceTree = "<group>"; };
|
||||
2C4D7D651F2F7DBC00FF4A0D /* ARDSettingsModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARDSettingsModel.m; sourceTree = "<group>"; };
|
||||
2C4D7D661F2F7DBC00FF4A0D /* ARDSettingsModel+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARDSettingsModel+Private.h"; sourceTree = "<group>"; };
|
||||
2C4D7D671F2F7DBC00FF4A0D /* ARDSettingsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDSettingsStore.h; sourceTree = "<group>"; };
|
||||
2C4D7D681F2F7DBC00FF4A0D /* ARDSettingsStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARDSettingsStore.m; sourceTree = "<group>"; };
|
||||
2C4D7D6B1F309DA500FF4A0D /* ARDSignalingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDSignalingMessage.h; sourceTree = "<group>"; };
|
||||
2C4D7D6C1F309DA500FF4A0D /* ARDSignalingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARDSignalingMessage.m; sourceTree = "<group>"; };
|
||||
2C4D7D6D1F309DA500FF4A0D /* RTCIceCandidate+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RTCIceCandidate+JSON.h"; sourceTree = "<group>"; };
|
||||
2C4D7D6E1F309DA500FF4A0D /* RTCIceCandidate+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RTCIceCandidate+JSON.m"; sourceTree = "<group>"; };
|
||||
2C4D7D6F1F309DA500FF4A0D /* RTCSessionDescription+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RTCSessionDescription+JSON.h"; sourceTree = "<group>"; };
|
||||
2C4D7D701F309DA500FF4A0D /* RTCSessionDescription+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RTCSessionDescription+JSON.m"; sourceTree = "<group>"; };
|
||||
2C4D7D741F30F7B600FF4A0D /* ARDUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARDUtilities.h; sourceTree = "<group>"; };
|
||||
2C4D7D751F30F7B600FF4A0D /* ARDUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARDUtilities.m; sourceTree = "<group>"; };
|
||||
2C90E5631EDDE0FB0093D85A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
2C90E5661EDDE1340093D85A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
2C90E5681EDDE13A0093D85A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
|
@ -100,6 +148,7 @@
|
|||
2CA1CCCA1F17C503002FE6A2 /* AudioToolbox.framework in Frameworks */,
|
||||
2CA1CC971F016117002FE6A2 /* Security.framework in Frameworks */,
|
||||
2C90E5CF1EDF23A00093D85A /* WebKit.framework in Frameworks */,
|
||||
2C4D7D5D1F2F6E9600FF4A0D /* WebRTC.framework in Frameworks */,
|
||||
2C90E5691EDDE13A0093D85A /* UIKit.framework in Frameworks */,
|
||||
2C90E5671EDDE1340093D85A /* CoreGraphics.framework in Frameworks */,
|
||||
2C90E5641EDDE0FB0093D85A /* Foundation.framework in Frameworks */,
|
||||
|
@ -113,6 +162,7 @@
|
|||
2C0574741EDD9E8E00D9E7F2 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2C4D7D5C1F2F6E9600FF4A0D /* WebRTC.framework */,
|
||||
2C05747E1EDD9E8E00D9E7F2 /* Products */,
|
||||
2C05749C1EDDA01700D9E7F2 /* ThirdParty */,
|
||||
2C05747F1EDD9E8E00D9E7F2 /* VideoCalls */,
|
||||
|
@ -141,6 +191,9 @@
|
|||
2CA1CCDA1F1F6FCA002FE6A2 /* CallsTableViewCell.xib */,
|
||||
2CA1CCA21F025F64002FE6A2 /* CallsTableViewController.h */,
|
||||
2CA1CCA31F025F64002FE6A2 /* CallsTableViewController.m */,
|
||||
2C4D7D571F2F6D4500FF4A0D /* CallViewController.h */,
|
||||
2C4D7D581F2F6D4500FF4A0D /* CallViewController.m */,
|
||||
2C4D7D591F2F6D4500FF4A0D /* CallViewController.xib */,
|
||||
2CA1CCD31F1E664C002FE6A2 /* ContactsTableViewCell.h */,
|
||||
2CA1CCD41F1E664C002FE6A2 /* ContactsTableViewCell.m */,
|
||||
2CA1CCD51F1E664C002FE6A2 /* ContactsTableViewCell.xib */,
|
||||
|
@ -161,6 +214,8 @@
|
|||
2CA1CCC21F166CC5002FE6A2 /* NCRoom.m */,
|
||||
2CA1CC931F014EF9002FE6A2 /* NCSettingsController.h */,
|
||||
2CA1CC941F014EF9002FE6A2 /* NCSettingsController.m */,
|
||||
2C2E64231F3462AF00D39CE8 /* NCSignalingMessage.h */,
|
||||
2C2E64241F3462AF00D39CE8 /* NCSignalingMessage.m */,
|
||||
2CA1CCCB1F181741002FE6A2 /* NCUser.h */,
|
||||
2CA1CCCC1F181741002FE6A2 /* NCUser.m */,
|
||||
2CA1CCCE1F1E1779002FE6A2 /* SearchTableViewController.h */,
|
||||
|
@ -184,14 +239,39 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
2C9219591F58530B008AC1A3 /* UIImageView+Letters */,
|
||||
2C4D7D601F2F7C2C00FF4A0D /* AppRTC */,
|
||||
2CA1CC9B1F0161FB002FE6A2 /* UICKeyChainStore */,
|
||||
);
|
||||
name = ThirdParty;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2C4D7D601F2F7C2C00FF4A0D /* AppRTC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2C4D7D611F2F7C2C00FF4A0D /* ARDCaptureController.h */,
|
||||
2C4D7D621F2F7C2C00FF4A0D /* ARDCaptureController.m */,
|
||||
2C4D7D641F2F7DBC00FF4A0D /* ARDSettingsModel.h */,
|
||||
2C4D7D651F2F7DBC00FF4A0D /* ARDSettingsModel.m */,
|
||||
2C4D7D661F2F7DBC00FF4A0D /* ARDSettingsModel+Private.h */,
|
||||
2C4D7D671F2F7DBC00FF4A0D /* ARDSettingsStore.h */,
|
||||
2C4D7D681F2F7DBC00FF4A0D /* ARDSettingsStore.m */,
|
||||
2C4D7D6B1F309DA500FF4A0D /* ARDSignalingMessage.h */,
|
||||
2C4D7D6C1F309DA500FF4A0D /* ARDSignalingMessage.m */,
|
||||
2C4D7D741F30F7B600FF4A0D /* ARDUtilities.h */,
|
||||
2C4D7D751F30F7B600FF4A0D /* ARDUtilities.m */,
|
||||
2C4D7D6D1F309DA500FF4A0D /* RTCIceCandidate+JSON.h */,
|
||||
2C4D7D6E1F309DA500FF4A0D /* RTCIceCandidate+JSON.m */,
|
||||
2C4D7D6F1F309DA500FF4A0D /* RTCSessionDescription+JSON.h */,
|
||||
2C4D7D701F309DA500FF4A0D /* RTCSessionDescription+JSON.m */,
|
||||
);
|
||||
name = AppRTC;
|
||||
path = ThirdParty/AppRTC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2C90E5621EDDE0FB0093D85A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2C4D7D551F2F607E00FF4A0D /* WebRTC.framework */,
|
||||
2CA1CCC91F17C503002FE6A2 /* AudioToolbox.framework */,
|
||||
2CA1CC961F016117002FE6A2 /* Security.framework */,
|
||||
2C90E5CE1EDF23A00093D85A /* WebKit.framework */,
|
||||
|
@ -244,6 +324,7 @@
|
|||
2C05747B1EDD9E8E00D9E7F2 /* Resources */,
|
||||
29696B57B26EBF9C28436CE8 /* [CP] Embed Pods Frameworks */,
|
||||
A3C686B1B84C4462F93441AB /* [CP] Copy Pods Resources */,
|
||||
2C4D7D5F1F2F6E9600FF4A0D /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
@ -297,6 +378,7 @@
|
|||
2CA1CCDC1F1F6FCA002FE6A2 /* CallsTableViewCell.xib in Resources */,
|
||||
2C0574931EDD9E8E00D9E7F2 /* LaunchScreen.storyboard in Resources */,
|
||||
2CA1CCAC1F067F35002FE6A2 /* Images.xcassets in Resources */,
|
||||
2C4D7D5B1F2F6D4500FF4A0D /* CallViewController.xib in Resources */,
|
||||
2CA1CCD71F1E664C002FE6A2 /* ContactsTableViewCell.xib in Resources */,
|
||||
2C05748E1EDD9E8E00D9E7F2 /* Main.storyboard in Resources */,
|
||||
);
|
||||
|
@ -361,18 +443,27 @@
|
|||
2CA1CCDB1F1F6FCA002FE6A2 /* CallsTableViewCell.m in Sources */,
|
||||
2CA1CCC31F166CC5002FE6A2 /* NCRoom.m in Sources */,
|
||||
2CA1CCA71F026222002FE6A2 /* ContactsTableViewController.m in Sources */,
|
||||
2C4D7D761F30F7B600FF4A0D /* ARDUtilities.m in Sources */,
|
||||
2CA1CCAA1F02D1A4002FE6A2 /* NCAPIController.m in Sources */,
|
||||
2C4D7D731F309DA500FF4A0D /* RTCSessionDescription+JSON.m in Sources */,
|
||||
2C2E64251F3462AF00D39CE8 /* NCSignalingMessage.m in Sources */,
|
||||
2C4D7D721F309DA500FF4A0D /* RTCIceCandidate+JSON.m in Sources */,
|
||||
2C0574821EDD9E8E00D9E7F2 /* main.m in Sources */,
|
||||
2CA1CCA41F025F64002FE6A2 /* CallsTableViewController.m in Sources */,
|
||||
2C90E5D31EE80C870093D85A /* AuthenticationViewController.m in Sources */,
|
||||
2CA1CCD01F1E1779002FE6A2 /* SearchTableViewController.m in Sources */,
|
||||
2C0574A41EDDA2E300D9E7F2 /* LoginViewController.m in Sources */,
|
||||
2CA1CCCD1F181741002FE6A2 /* NCUser.m in Sources */,
|
||||
2C4D7D5A1F2F6D4500FF4A0D /* CallViewController.m in Sources */,
|
||||
2CA1CCD61F1E664C002FE6A2 /* ContactsTableViewCell.m in Sources */,
|
||||
2CA1CC911F014354002FE6A2 /* NCConnectionController.m in Sources */,
|
||||
2C92195C1F58530B008AC1A3 /* UIImageView+Letters.m in Sources */,
|
||||
2C4D7D691F2F7DBC00FF4A0D /* ARDSettingsModel.m in Sources */,
|
||||
2CA1CCB51F0D0D5E002FE6A2 /* SettingsViewController.m in Sources */,
|
||||
2C4D7D711F309DA500FF4A0D /* ARDSignalingMessage.m in Sources */,
|
||||
2CA1CC951F014EF9002FE6A2 /* NCSettingsController.m in Sources */,
|
||||
2C4D7D631F2F7C2C00FF4A0D /* ARDCaptureController.m in Sources */,
|
||||
2C4D7D6A1F2F7DBC00FF4A0D /* ARDSettingsStore.m in Sources */,
|
||||
2CA1CC9A1F0161EA002FE6A2 /* UICKeyChainStore.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -497,8 +588,13 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = 7T56CJ62G4;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/ThirdParty/WebRTC",
|
||||
);
|
||||
INFOPLIST_FILE = VideoCalls/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.strukturag.VideoCalls;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
|
@ -510,8 +606,13 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = 7T56CJ62G4;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/ThirdParty/WebRTC",
|
||||
);
|
||||
INFOPLIST_FILE = VideoCalls/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.strukturag.VideoCalls;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// CallViewController.h
|
||||
// VideoCalls
|
||||
//
|
||||
// Created by Ivan Sein on 31.07.17.
|
||||
// Copyright © 2017 struktur AG. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <WebRTC/WebRTC.h>
|
||||
|
||||
@interface CallViewController : UIViewController
|
||||
|
||||
@property (strong, nonatomic) IBOutlet RTCCameraPreviewView *localVideoView;
|
||||
@property (strong, nonatomic) IBOutlet UIView *remoteView;
|
||||
|
||||
- (instancetype)initWithSessionId:(NSString *)sessionId;
|
||||
|
||||
@end
|
|
@ -0,0 +1,422 @@
|
|||
//
|
||||
// CallViewController.m
|
||||
// VideoCalls
|
||||
//
|
||||
// Created by Ivan Sein on 31.07.17.
|
||||
// Copyright © 2017 struktur AG. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CallViewController.h"
|
||||
|
||||
#import "ARDSettingsModel.h"
|
||||
#import "ARDCaptureController.h"
|
||||
#import "ARDSignalingMessage.h"
|
||||
#import "NCAPIController.h"
|
||||
#import "NCSignalingMessage.h"
|
||||
|
||||
static NSString * const kNCMediaStreamId = @"NCMS";
|
||||
static NSString * const kNCAudioTrackId = @"NCa0";
|
||||
static NSString * const kNCVideoTrackId = @"NCv0";
|
||||
static NSString * const kNCVideoTrackKind = @"video";
|
||||
|
||||
@interface CallViewController () <RTCPeerConnectionDelegate, RTCEAGLVideoViewDelegate>
|
||||
{
|
||||
NSString *_sessionId;
|
||||
|
||||
RTCPeerConnectionFactory *_factory;
|
||||
|
||||
NSMapTable *_sessionIdMap;
|
||||
NSMutableDictionary *_peerConnectionDict;
|
||||
NSMutableArray *_usersInCall;
|
||||
|
||||
RTCVideoTrack *_localVideoTrack;
|
||||
ARDCaptureController *_captureController;
|
||||
UIView<RTCVideoRenderer> *_remoteVideoView;
|
||||
|
||||
CGSize _remoteVideoSize;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation CallViewController
|
||||
|
||||
- (instancetype)initWithSessionId:(NSString *)sessionId
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_sessionId = sessionId;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
_factory = [[RTCPeerConnectionFactory alloc] init];
|
||||
_sessionIdMap = [[NSMapTable alloc] init];
|
||||
_peerConnectionDict = [[NSMutableDictionary alloc] init];
|
||||
_usersInCall = [[NSMutableArray alloc] init];
|
||||
|
||||
RTCEAGLVideoView *remoteView = [[RTCEAGLVideoView alloc] initWithFrame:_remoteView.layer.bounds];
|
||||
remoteView.delegate = self;
|
||||
_remoteVideoView = remoteView;
|
||||
[_remoteView addSubview:_remoteVideoView];
|
||||
|
||||
[self startPullingSignallingMessages];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
- (void)videoView:(RTCEAGLVideoView*)videoView didChangeVideoSize:(CGSize)size
|
||||
{
|
||||
if (videoView == _remoteVideoView) {
|
||||
_remoteVideoSize = size;
|
||||
_remoteVideoView.bounds = _remoteView.bounds;
|
||||
}
|
||||
[_remoteView setNeedsLayout];
|
||||
}
|
||||
|
||||
#pragma mark - RTCPeerConnectionDelegate
|
||||
// Callbacks for this delegate occur on non-main thread and need to be
|
||||
// dispatched back to main queue as needed.
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeSignalingState:(RTCSignalingState)stateChanged
|
||||
{
|
||||
NSLog(@"Signaling state changed: %ld", (long)stateChanged);
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didAddStream:(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];
|
||||
[self setRemoteVideoTrack:videoTrack];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveStream:(RTCMediaStream *)stream
|
||||
{
|
||||
NSLog(@"Stream was removed.");
|
||||
}
|
||||
|
||||
- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection
|
||||
{
|
||||
NSLog(@"WARNING: Renegotiation needed but unimplemented.");
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeIceConnectionState:(RTCIceConnectionState)newState
|
||||
{
|
||||
NSLog(@"ICE state changed: %ld", (long)newState);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// [_delegate appClient:self didChangeConnectionState:newState];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeIceGatheringState:(RTCIceGatheringState)newState
|
||||
{
|
||||
NSLog(@"ICE gathering state changed: %ld", (long)newState);
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didGenerateIceCandidate:(RTCIceCandidate *)candidate
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString *to = [_sessionIdMap objectForKey:peerConnection];
|
||||
NCICECandidateMessage *message = [[NCICECandidateMessage alloc] initWithCandidate:candidate
|
||||
from:_sessionId
|
||||
to:to
|
||||
sid:[NCSignalingMessage getMessageSid]
|
||||
roomType:@"video"];
|
||||
[self sendSignalingMessage:message];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates
|
||||
{
|
||||
NSLog(@"PeerConnection didRemoveIceCandidates delegate has been called.");
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection *)peerConnection didOpenDataChannel:(RTCDataChannel *)dataChannel
|
||||
{
|
||||
NSLog(@"Data channel was opened.");
|
||||
}
|
||||
|
||||
#pragma mark - Audio & Video senders
|
||||
|
||||
- (RTCRtpSender *)createAudioSenderForPeerConnection:(RTCPeerConnection *)peerConnection
|
||||
{
|
||||
NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : kRTCMediaConstraintsValueTrue };
|
||||
RTCMediaConstraints *constraints =
|
||||
[[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
|
||||
optionalConstraints:nil];
|
||||
|
||||
RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
|
||||
RTCAudioTrack *track = [_factory audioTrackWithSource:source trackId:kNCAudioTrackId];
|
||||
RTCRtpSender *sender =
|
||||
[peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
|
||||
streamId:kNCMediaStreamId];
|
||||
sender.track = track;
|
||||
return sender;
|
||||
}
|
||||
|
||||
- (RTCRtpSender *)createVideoSenderForPeerConnection:(RTCPeerConnection *)peerConnection
|
||||
{
|
||||
RTCRtpSender *sender =
|
||||
[peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
|
||||
streamId:kNCMediaStreamId];
|
||||
|
||||
RTCVideoSource *source = [_factory videoSource];
|
||||
RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
|
||||
ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init];
|
||||
|
||||
_localVideoView.captureSession = capturer.captureSession;
|
||||
_captureController = [[ARDCaptureController alloc] initWithCapturer:capturer settings:settingsModel];
|
||||
|
||||
[_captureController startCapture];
|
||||
|
||||
_localVideoTrack = [_factory videoTrackWithSource:source trackId:kNCVideoTrackId];
|
||||
|
||||
return sender;
|
||||
}
|
||||
|
||||
- (void)setRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack
|
||||
{
|
||||
[_remoteVideoView renderFrame:nil];
|
||||
[remoteVideoTrack addRenderer:_remoteVideoView];
|
||||
}
|
||||
|
||||
#pragma mark - Utils
|
||||
|
||||
- (RTCMediaConstraints *)defaultAnswerConstraints
|
||||
{
|
||||
return [self defaultOfferConstraints];
|
||||
}
|
||||
|
||||
- (RTCMediaConstraints *)defaultOfferConstraints
|
||||
{
|
||||
NSDictionary *mandatoryConstraints = @{
|
||||
@"OfferToReceiveAudio" : @"true",
|
||||
@"OfferToReceiveVideo" : @"true"
|
||||
};
|
||||
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
||||
initWithMandatoryConstraints:mandatoryConstraints
|
||||
optionalConstraints:nil];
|
||||
return constraints;
|
||||
}
|
||||
|
||||
#pragma mark - Signalling
|
||||
|
||||
- (void)startPullingSignallingMessages
|
||||
{
|
||||
[[NCAPIController sharedInstance] pullSignallingMessagesWithCompletionBlock:^(NSDictionary *messages, NSError *error, NSInteger errorCode) {
|
||||
NSArray *messagesArray = [[messages objectForKey:@"ocs"] objectForKey:@"data"];
|
||||
for (NSDictionary *message in messagesArray) {
|
||||
NSString *messageType = [message objectForKey:@"type"];
|
||||
|
||||
if ([messageType isEqualToString:@"usersInRoom"]) {
|
||||
[self processUsersInRoom:[message objectForKey:@"data"]];
|
||||
} else if ([messageType isEqualToString:@"message"]) {
|
||||
NCSignalingMessage *signalingMessage = [NCSignalingMessage messageFromJSONString:[message objectForKey:@"data"]];
|
||||
[self processSignalingMessage:signalingMessage];
|
||||
} else {
|
||||
NSLog(@"Uknown message: %@", [message objectForKey:@"data"]);
|
||||
}
|
||||
}
|
||||
|
||||
[self startPullingSignallingMessages];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendSignalingMessages:(NSArray *)messages
|
||||
{
|
||||
[[NCAPIController sharedInstance] sendSignallingMessages:[self messagesJSONSerialization:messages] withCompletionBlock:^(NSError *error, NSInteger errorCode) {
|
||||
NSLog(@"Sent %ld signalling messages", messages.count);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendSignalingMessage:(NCSignalingMessage *)message
|
||||
{
|
||||
NSArray *messagesArray = [NSArray arrayWithObjects:[message messageDict], nil];
|
||||
NSString *JSONSerializedMessages = [self messagesJSONSerialization:messagesArray];
|
||||
[[NCAPIController sharedInstance] sendSignallingMessages:JSONSerializedMessages withCompletionBlock:^(NSError *error, NSInteger errorCode) {
|
||||
if (error) {
|
||||
//TODO: Error handling
|
||||
NSLog(@"Error sending signaling message.");
|
||||
}
|
||||
NSLog(@"Sent %@", JSONSerializedMessages);
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)messagesJSONSerialization:(NSArray *)messages
|
||||
{
|
||||
NSError *error;
|
||||
NSString *jsonString = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:messages
|
||||
options:0
|
||||
error:&error];
|
||||
|
||||
if (! jsonData) {
|
||||
NSLog(@"Got an error: %@", error);
|
||||
} else {
|
||||
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
- (void)processUsersInRoom:(NSArray *)users
|
||||
{
|
||||
for (NSDictionary *user in users) {
|
||||
NSString *sessionId = [user objectForKey:@"sessionId"];
|
||||
|
||||
if([_sessionId isEqualToString:sessionId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (![_peerConnectionDict objectForKey:sessionId]) {
|
||||
NSComparisonResult result = [sessionId compare:_sessionId];
|
||||
if (result == NSOrderedAscending) {
|
||||
NSLog(@"Creating offer...");
|
||||
[self createPeerConnectionWithOfferForSessionId:sessionId];
|
||||
} else {
|
||||
NSLog(@"Waiting for offer...");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processSignalingMessage:(NCSignalingMessage *)message
|
||||
{
|
||||
switch (message.messageType) {
|
||||
case kNCSignalingMessageTypeOffer:
|
||||
case kNCSignalingMessageTypeAnswer: {
|
||||
NCSessionDescriptionMessage *sdpMessage = (NCSessionDescriptionMessage *)message;
|
||||
RTCSessionDescription *description = sdpMessage.sessionDescription;
|
||||
RTCPeerConnection *peerConnection = [self getPeerConnectionForSessionId:message.from];
|
||||
|
||||
[peerConnection setRemoteDescription:description completionHandler:^(NSError *error) {
|
||||
[self peerConnectionForSessionId:message.from didSetSessionDescriptionWithError:error];
|
||||
}];
|
||||
break;
|
||||
}
|
||||
case kNCSignalingMessageTypeCandidate: {
|
||||
NCICECandidateMessage *candidateMessage = (NCICECandidateMessage *)message;
|
||||
RTCPeerConnection *peerConnection = [self getPeerConnectionForSessionId:message.from];
|
||||
|
||||
[peerConnection addIceCandidate:candidateMessage.candidate];
|
||||
break;
|
||||
}
|
||||
case kNCSignalingMessageTypeUknown:
|
||||
NSLog(@"Trying to process an unkown type message.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Peer Connection
|
||||
|
||||
- (RTCPeerConnection *)getPeerConnectionForSessionId:(NSString *)sessionId
|
||||
{
|
||||
RTCPeerConnection *peerConnection = [_peerConnectionDict objectForKey:sessionId];
|
||||
|
||||
if (!peerConnection) {
|
||||
// Create peer connection.
|
||||
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
||||
initWithMandatoryConstraints:nil
|
||||
optionalConstraints:nil];
|
||||
RTCConfiguration *config = [[RTCConfiguration alloc] init];
|
||||
|
||||
peerConnection = [_factory peerConnectionWithConfiguration:config
|
||||
constraints:constraints
|
||||
delegate:self];
|
||||
|
||||
[_sessionIdMap setObject:sessionId forKey:peerConnection];
|
||||
[_peerConnectionDict setObject:peerConnection forKey:sessionId];
|
||||
}
|
||||
|
||||
return peerConnection;
|
||||
}
|
||||
|
||||
- (void)createPeerConnectionWithOfferForSessionId:(NSString *)sessionId
|
||||
{
|
||||
// Create peer connection.
|
||||
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
||||
initWithMandatoryConstraints:nil
|
||||
optionalConstraints:nil];
|
||||
RTCConfiguration *config = [[RTCConfiguration alloc] init];
|
||||
RTCPeerConnection *peerConnection = [_factory peerConnectionWithConfiguration:config
|
||||
constraints:constraints
|
||||
delegate:self];
|
||||
|
||||
[_sessionIdMap setObject:sessionId forKey:peerConnection];
|
||||
[_peerConnectionDict setObject:peerConnection forKey:sessionId];
|
||||
|
||||
[peerConnection offerForConstraints:[self defaultOfferConstraints]
|
||||
completionHandler:^(RTCSessionDescription *sdp,
|
||||
NSError *error) {
|
||||
[peerConnection setLocalDescription:sdp completionHandler:^(NSError *error) {
|
||||
NCSessionDescriptionMessage *message = [[NCSessionDescriptionMessage alloc]
|
||||
initWithSessionDescription:sdp
|
||||
from:_sessionId
|
||||
to:sessionId
|
||||
sid:[NCSignalingMessage getMessageSid]
|
||||
roomType:@"video"];
|
||||
[self sendSignalingMessage:message];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - RTCSessionDescriptionDelegate
|
||||
// Callbacks for this delegate occur on non-main thread and need to be
|
||||
// dispatched back to main queue as needed.
|
||||
|
||||
- (void)peerConnectionForSessionId:(NSString *)sessionId
|
||||
didCreateSessionDescription:(RTCSessionDescription *)sdp
|
||||
error:(NSError *)error
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"didCreateSessionDescription: %ld", (long)sdp.type);
|
||||
RTCPeerConnection *peerConnection = [self getPeerConnectionForSessionId:sessionId];
|
||||
[peerConnection setLocalDescription:sdp completionHandler:^(NSError *error) {
|
||||
[self peerConnectionForSessionId:sessionId didSetSessionDescriptionWithError:error];
|
||||
}];
|
||||
|
||||
NCSessionDescriptionMessage *message = [[NCSessionDescriptionMessage alloc]
|
||||
initWithSessionDescription:sdp
|
||||
from:_sessionId to:sessionId
|
||||
sid:[NCSignalingMessage getMessageSid]
|
||||
roomType:@"video"];
|
||||
[self sendSignalingMessage:message];
|
||||
// [self setMaxBitrateForPeerConnectionVideoSender];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)peerConnectionForSessionId:(NSString *)sessionId
|
||||
didSetSessionDescriptionWithError:(NSError *)error
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// If we're answering and we've just set the remote offer we need to create
|
||||
// an answer and set the local description.
|
||||
NSLog(@"didSetSessionDescription");
|
||||
RTCPeerConnection *peerConnection = [self getPeerConnectionForSessionId:sessionId];
|
||||
if (!peerConnection.localDescription) {
|
||||
NSLog(@"creating local description");
|
||||
RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
|
||||
[peerConnection answerForConstraints:constraints completionHandler:^(RTCSessionDescription *sdp, NSError *error) {
|
||||
[self peerConnectionForSessionId:sessionId didCreateSessionDescription:sdp error:error];
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CallViewController">
|
||||
<connections>
|
||||
<outlet property="localVideoView" destination="LVE-DF-0nt" id="9D4-Vw-5IK"/>
|
||||
<outlet property="remoteView" destination="Xb2-0h-P58" id="d2H-d8-nnz"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xb2-0h-P58" userLabel="remoteVideo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LVE-DF-0nt" userLabel="localVideo" customClass="RTCCameraPreviewView">
|
||||
<rect key="frame" x="239" y="477" width="120" height="170"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<point key="canvasLocation" x="34.5" y="54.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#import "AFNetworking.h"
|
||||
#import "AuthenticationViewController.h"
|
||||
#import "CallViewController.h"
|
||||
#import "CallsTableViewCell.h"
|
||||
#import "LoginViewController.h"
|
||||
#import "NCAPIController.h"
|
||||
|
@ -298,6 +299,8 @@
|
|||
if (!error) {
|
||||
_currentCallToken = room.token;
|
||||
[self startPingCall];
|
||||
CallViewController *callVC = [[CallViewController alloc] initWithSessionId:sessionId];
|
||||
[self presentViewController:callVC animated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ typedef void (^PullSignallingMessagesCompletionBlock)(NSDictionary *messages, NS
|
|||
- (void)leaveCall:(NSString *)token withCompletionBlock:(LeaveCallCompletionBlock)block;
|
||||
|
||||
// Signalling Controller
|
||||
- (void)sendSignallingMessages:(NSArray *)messages withCompletionBlock:(SendSignallingMessagesCompletionBlock)block;
|
||||
- (void)sendSignallingMessages:(NSString *)messages withCompletionBlock:(SendSignallingMessagesCompletionBlock)block;
|
||||
- (void)pullSignallingMessagesWithCompletionBlock:(PullSignallingMessagesCompletionBlock)block;
|
||||
|
||||
// User avatars
|
||||
|
|
|
@ -318,7 +318,7 @@ NSString * const kNCUserAgent = @"Video Calls iOS";
|
|||
|
||||
#pragma mark - Signalling Controller
|
||||
|
||||
- (void)sendSignallingMessages:(NSArray *)messages withCompletionBlock:(SendSignallingMessagesCompletionBlock)block
|
||||
- (void)sendSignallingMessages:(NSString *)messages withCompletionBlock:(SendSignallingMessagesCompletionBlock)block
|
||||
{
|
||||
NSString *URLString = [self getRequestURLForSpreedEndpoint:@"signalling"];
|
||||
NSDictionary *parameters = @{@"messages" : messages};
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// NCSignalingMessage.h
|
||||
// VideoCalls
|
||||
//
|
||||
// Created by Ivan Sein on 04.08.17.
|
||||
// Copyright © 2017 struktur AG. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "WebRTC/RTCIceCandidate.h"
|
||||
#import "WebRTC/RTCSessionDescription.h"
|
||||
|
||||
typedef enum {
|
||||
kNCSignalingMessageTypeUknown,
|
||||
kNCSignalingMessageTypeCandidate,
|
||||
kNCSignalingMessageTypeOffer,
|
||||
kNCSignalingMessageTypeAnswer,
|
||||
} NCSignalingMessageType;
|
||||
|
||||
|
||||
@interface NCSignalingMessage : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *from;
|
||||
@property(nonatomic, readonly) NSString *to;
|
||||
@property(nonatomic, readonly) NSString *sid;
|
||||
@property(nonatomic, readonly) NSString *type;
|
||||
@property(nonatomic, readonly) NSDictionary *payload;
|
||||
@property(nonatomic, readonly) NSString *roomType;
|
||||
|
||||
+ (NCSignalingMessage *)messageFromJSONString:(NSString *)jsonString;
|
||||
+ (NSString *)getMessageSid;
|
||||
- (NSDictionary *)messageDict;
|
||||
- (NCSignalingMessageType)messageType;
|
||||
|
||||
@end
|
||||
|
||||
@interface NCICECandidateMessage : NCSignalingMessage
|
||||
|
||||
@property(nonatomic, readonly) RTCIceCandidate *candidate;
|
||||
|
||||
- (instancetype)initWithValues:(NSDictionary *)values;
|
||||
- (instancetype)initWithCandidate:(RTCIceCandidate *)candidate
|
||||
from:(NSString *)from
|
||||
to:(NSString *)to
|
||||
sid:(NSString *)sid
|
||||
roomType:(NSString *)roomType;
|
||||
|
||||
@end
|
||||
|
||||
@interface NCSessionDescriptionMessage : NCSignalingMessage
|
||||
|
||||
@property(nonatomic, readonly) RTCSessionDescription *sessionDescription;
|
||||
|
||||
- (instancetype)initWithValues:(NSDictionary *)values;
|
||||
- (instancetype)initWithSessionDescription:(RTCSessionDescription *)sessionDescription
|
||||
from:(NSString *)from
|
||||
to:(NSString *)to
|
||||
sid:(NSString *)sid
|
||||
roomType:(NSString *)roomType;
|
||||
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
//
|
||||
// NCSignalingMessage.m
|
||||
// VideoCalls
|
||||
//
|
||||
// Created by Ivan Sein on 04.08.17.
|
||||
// Copyright © 2017 struktur AG. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NCSignalingMessage.h"
|
||||
|
||||
#import "WebRTC/RTCLogging.h"
|
||||
|
||||
#import "ARDUtilities.h"
|
||||
#import "RTCIceCandidate+JSON.h"
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
|
||||
static NSString * const kNCSignalingMessageEventKey = @"ev";
|
||||
static NSString * const kNCSignalingMessageFunctionKey = @"fn";
|
||||
static NSString * const kNCSignalingMessageSessionIdKey = @"sessionId";
|
||||
|
||||
static NSString * const kNCSignalingMessageKey = @"message";
|
||||
static NSString * const kNCSignalingMessageDataKey = @"data";
|
||||
|
||||
static NSString * const kNCSignalingMessageFromKey = @"from";
|
||||
static NSString * const kNCSignalingMessageToKey = @"to";
|
||||
static NSString * const kNCSignalingMessageSidKey = @"sid";
|
||||
static NSString * const kNCSignalingMessageTypeKey = @"type";
|
||||
static NSString * const kNCSignalingMessagePayloadKey = @"payload";
|
||||
static NSString * const kNCSignalingMessageRoomTypeKey = @"roomType";
|
||||
|
||||
static NSString * const kNCSignalingMessageTypeOfferKey = @"offer";
|
||||
static NSString * const kNCSignalingMessageTypeAnswerKey = @"answer";
|
||||
static NSString * const kNCSignalingMessageTypeCandidateKey = @"candidate";
|
||||
static NSString * const kNCSignalingMessageTypeRemoveCandidatesKey = @"remove-candidates";
|
||||
|
||||
static NSString * const kNCSignalingMessageSdpKey = @"sdp";
|
||||
|
||||
@implementation NCSignalingMessage
|
||||
|
||||
@synthesize from = _from;
|
||||
@synthesize to = _to;
|
||||
@synthesize type = _type;
|
||||
@synthesize payload = _payload;
|
||||
@synthesize roomType = _roomType;
|
||||
@synthesize sid = _sid;
|
||||
|
||||
- (instancetype)initWithFrom:(NSString *)from
|
||||
to:(NSString *)to
|
||||
sid:(NSString *)sid
|
||||
type:(NSString *)type
|
||||
payload:(NSDictionary *)payload
|
||||
roomType:(NSString *)roomType
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_from = from;
|
||||
_to = to;
|
||||
_sid = sid;
|
||||
_type = type;
|
||||
_payload = payload;
|
||||
_roomType = roomType;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [[NSString alloc] initWithData:[self JSONData]
|
||||
encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
+ (NCSignalingMessage *)messageFromJSONString:(NSString *)jsonString {
|
||||
NSDictionary *values = [NSDictionary dictionaryWithJSONString:jsonString];
|
||||
if (!values) {
|
||||
RTCLogError(@"Error parsing signaling message JSON.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *typeString = values[kNCSignalingMessageTypeKey];
|
||||
NCSignalingMessage *message = nil;
|
||||
if ([typeString isEqualToString:kNCSignalingMessageTypeCandidateKey]) {
|
||||
message = [[NCICECandidateMessage alloc] initWithValues:values];
|
||||
} else if ([typeString isEqualToString:kNCSignalingMessageTypeOfferKey] ||
|
||||
[typeString isEqualToString:kNCSignalingMessageTypeAnswerKey]) {
|
||||
message = [[NCSessionDescriptionMessage alloc] initWithValues:values];
|
||||
} else {
|
||||
RTCLogError(@"Unexpected type: %@", typeString);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)messageDict {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NCSignalingMessageType)messageType {
|
||||
return kNCSignalingMessageTypeUknown;
|
||||
}
|
||||
|
||||
+ (NSString *)getMessageSid {
|
||||
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
|
||||
return [[NSNumber numberWithDouble: timeStamp] stringValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NCICECandidateMessage
|
||||
|
||||
@synthesize candidate = _candidate;
|
||||
|
||||
- (instancetype)initWithValues:(NSDictionary *)values {
|
||||
RTCIceCandidate *candidate = [RTCIceCandidate candidateFromJSONDictionary:[[values objectForKey:kNCSignalingMessagePayloadKey] objectForKey:kNCSignalingMessageTypeCandidateKey]];
|
||||
return [self initWithCandidate:candidate
|
||||
from:[values objectForKey:kNCSignalingMessageFromKey]
|
||||
to:[values objectForKey:kNCSignalingMessageToKey]
|
||||
sid:[values objectForKey:kNCSignalingMessageSidKey]
|
||||
roomType:[values objectForKey:kNCSignalingMessageRoomTypeKey]];
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
NSError *error = nil;
|
||||
NSData *data =
|
||||
[NSJSONSerialization dataWithJSONObject:[self functionDict]
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:&error];
|
||||
if (error) {
|
||||
RTCLogError(@"Error serializing JSON: %@", error);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSString *)functionJSONSerialization
|
||||
{
|
||||
NSError *error;
|
||||
NSString *jsonString = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[self functionDict]
|
||||
options:0
|
||||
error:&error];
|
||||
|
||||
if (! jsonData) {
|
||||
NSLog(@"Error serializing JSON: %@", error);
|
||||
} else {
|
||||
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
- (NSDictionary *)messageDict {
|
||||
return @{
|
||||
kNCSignalingMessageEventKey: kNCSignalingMessageKey,
|
||||
kNCSignalingMessageFunctionKey: [self functionJSONSerialization],
|
||||
kNCSignalingMessageSessionIdKey: self.from
|
||||
};
|
||||
}
|
||||
|
||||
- (NSDictionary *)functionDict {
|
||||
return @{
|
||||
kNCSignalingMessageToKey: self.to,
|
||||
kNCSignalingMessageSidKey: self.sid,
|
||||
kNCSignalingMessageRoomTypeKey: self.roomType,
|
||||
kNCSignalingMessageTypeKey: self.type,
|
||||
kNCSignalingMessagePayloadKey: @{
|
||||
kNCSignalingMessageTypeKey: self.type,
|
||||
kNCSignalingMessageTypeCandidateKey: [self.candidate JSONDictionary]
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
- (NCSignalingMessageType)messageType {
|
||||
return kNCSignalingMessageTypeCandidate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCandidate:(RTCIceCandidate *)candidate
|
||||
from:(NSString *)from
|
||||
to:(NSString *)to
|
||||
sid:(NSString *)sid
|
||||
roomType:(NSString *)roomType
|
||||
{
|
||||
NSDictionary *payload = [[NSDictionary alloc] init];
|
||||
self = [super initWithFrom:from
|
||||
to:to
|
||||
sid:sid
|
||||
type:kNCSignalingMessageTypeCandidateKey
|
||||
payload:payload
|
||||
roomType:roomType];
|
||||
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_candidate = candidate;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NCSessionDescriptionMessage
|
||||
|
||||
@synthesize sessionDescription = _sessionDescription;
|
||||
|
||||
- (instancetype)initWithValues:(NSDictionary *)values {
|
||||
RTCSessionDescription *description = [RTCSessionDescription descriptionFromJSONDictionary:[values objectForKey:kNCSignalingMessagePayloadKey]];
|
||||
return [self initWithSessionDescription:description
|
||||
from:[values objectForKey:kNCSignalingMessageFromKey]
|
||||
to:[values objectForKey:kNCSignalingMessageToKey]
|
||||
sid:[values objectForKey:kNCSignalingMessageSidKey]
|
||||
roomType:[values objectForKey:kNCSignalingMessageRoomTypeKey]];
|
||||
}
|
||||
|
||||
- (NSData *)JSONData {
|
||||
NSError *error = nil;
|
||||
NSData *data =
|
||||
[NSJSONSerialization dataWithJSONObject:[self messageDict]
|
||||
options:0
|
||||
error:&error];
|
||||
if (error) {
|
||||
RTCLogError(@"Error serializing JSON: %@", error);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSString *)functionJSONSerialization
|
||||
{
|
||||
NSError *error;
|
||||
NSString *jsonString = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[self functionDict]
|
||||
options:0
|
||||
error:&error];
|
||||
|
||||
if (! jsonData) {
|
||||
NSLog(@"Error serializing JSON: %@", error);
|
||||
} else {
|
||||
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
- (NSDictionary *)messageDict {
|
||||
return @{
|
||||
kNCSignalingMessageEventKey: kNCSignalingMessageKey,
|
||||
kNCSignalingMessageFunctionKey: [self functionJSONSerialization],
|
||||
kNCSignalingMessageSessionIdKey: self.from
|
||||
};
|
||||
}
|
||||
|
||||
- (NSDictionary *)functionDict {
|
||||
return @{
|
||||
kNCSignalingMessageToKey: self.to,
|
||||
kNCSignalingMessageSidKey: self.sid,
|
||||
kNCSignalingMessageRoomTypeKey: self.roomType,
|
||||
kNCSignalingMessageTypeKey: self.type,
|
||||
kNCSignalingMessagePayloadKey: @{
|
||||
kNCSignalingMessageTypeKey: self.type,
|
||||
kNCSignalingMessageSdpKey: self.sessionDescription.sdp
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
- (NCSignalingMessageType)messageType {
|
||||
if ([self.type isEqualToString:kNCSignalingMessageTypeOfferKey]) {
|
||||
return kNCSignalingMessageTypeOffer;
|
||||
}
|
||||
return kNCSignalingMessageTypeAnswer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSessionDescription:(RTCSessionDescription *)sessionDescription
|
||||
from:(NSString *)from
|
||||
to:(NSString *)to
|
||||
sid:(NSString *)sid
|
||||
roomType:(NSString *)roomType {
|
||||
RTCSdpType sdpType = sessionDescription.type;
|
||||
NSString *type = nil;
|
||||
switch (sdpType) {
|
||||
case RTCSdpTypeOffer:
|
||||
type = kNCSignalingMessageTypeOfferKey;
|
||||
break;
|
||||
case RTCSdpTypeAnswer:
|
||||
type = kNCSignalingMessageTypeAnswerKey;
|
||||
break;
|
||||
case RTCSdpTypePrAnswer:
|
||||
NSAssert(NO, @"Unexpected type: %@",
|
||||
[RTCSessionDescription stringForType:sdpType]);
|
||||
break;
|
||||
}
|
||||
|
||||
NSMutableDictionary *payload = [[NSMutableDictionary alloc] init];
|
||||
[payload setObject:type forKey:kNCSignalingMessageTypeKey];
|
||||
[payload setObject:sessionDescription.sdp forKey:kNCSignalingMessageSdpKey];
|
||||
|
||||
self = [super initWithFrom:from
|
||||
to:to
|
||||
sid:sid
|
||||
type:type
|
||||
payload:payload
|
||||
roomType:roomType];
|
||||
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_sessionDescription = sessionDescription;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
Загрузка…
Ссылка в новой задаче