1.podfile 文件
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'iosWebRTCDemo' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod 'GoogleWebRTC','1.1.24595'
pod 'ReactiveObjC'
pod 'SocketRocket','0.5.1'
pod 'HGAlertViewController', '~> 1.0.1'
target 'iosWebRTCDemoTests' do
inherit! :search_paths
# Pods for testing
end
target 'iosWebRTCDemoUITests' do
# Pods for testing
end
end
[[WebRTCHelper shareInstance] connectServer:@"172.27.104.54" port:@"9090/SocketServer"];
- WebRTCHelper.h
//
// WebRTCHelper.h
// WebRTC_new
//
// Created by
// Copyright ? 2020年
//
#import <Foundation/Foundation.h>
#import <WebRTC/WebRTC.h>
#import <SocketRocket/SocketRocket.h>
#import <WebRTC/RTCMacros.h>
#import <WebRTC/RTCVideoCodec.h>
//@import SocketIO;
typedef enum : NSUInteger {
WebSocketConnectSuccess = 0,
WebSocketConnectField,
WebSocketConnectClosed,
} WebSocketConnectState;
@protocol WebRTCHelperDelegate;
@protocol WebRTCHelperFrindDelegate;
@interface WebRTCHelper : NSObject
/**
* 單例
*/
+(instancetype)shareInstance;
/*注釋*/
@property (nonatomic,weak) id<WebRTCHelperDelegate> delegate;
/*注釋*/
@property (nonatomic,weak) id<WebRTCHelperFrindDelegate> friendDelegate;
/**
* 與服務(wù)器建立連接
* @param server 服務(wù)器地址
* @param port 端口號(hào)
*/
-(void)connectServer:(NSString *)server port:(NSString *)port;
/**
* 切換攝像頭
*/
- (void)swichCamera:(BOOL)_usingFrontCamera;
/**
* 是否顯示本地視頻
*/
- (void)showLocaolCamera:(BOOL)_usingCamera;
/**
* 退出房間
*/
-(void)exitRoom;
@end
@protocol WebRTCHelperDelegate <NSObject>
@optional
/**
* 獲取到發(fā)送信令消息
* @param webRTCHelper 本類(lèi)
* @param message 消息內(nèi)容
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper receiveMessage:(NSString *)message;
/**
* 獲取本地的localVideoStream數(shù)據(jù)
* @param webRTCHelper 本類(lèi)
* @param steam 視頻流
* @param userId 用戶(hù)標(biāo)識(shí)
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper setLocalStream:(RTCMediaStream *)steam userId:(NSString *)userId;
/**
* 獲取遠(yuǎn)程的remoteVideoStream數(shù)據(jù)
* @param webRTCHelper 本類(lèi)
* @param stream 視頻流
* @param userId 用戶(hù)標(biāo)識(shí)
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper addRemoteStream:(RTCMediaStream *)stream userId:(NSString *)userId;
/**
* 某個(gè)用戶(hù)退出后苫拍,關(guān)閉用戶(hù)的連接
* @param webRTCHelper 本類(lèi)
* @param userId 用戶(hù)標(biāo)識(shí)
*/
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper closeWithUserId:(NSString *)userId;
/**
* 獲取socket連接狀態(tài)
* @param webRTCHelper 本類(lèi)
* @param connectState 連接狀態(tài),分為
WebSocketConnectSuccess 成功,
WebSocketConnectField, 失敗
WebSocketConnectClosed 關(guān)閉
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper socketConnectState:(WebSocketConnectState)connectState;
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper capturerSession:(AVCaptureSession *)captureSession;
@end
@protocol WebRTCHelperFrindDelegate <NSObject>
@optional
/**
* 獲取房間內(nèi)所有的用戶(hù)(除了自己)
* @param friendList 用戶(hù)列表
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper gotFriendList:(NSArray *)friendList;
/**
* 獲取新加入的用戶(hù)信息
* @param friendId 新用戶(hù)的id
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper gotNewFriend:(NSString *)friendId;
/**
* 獲取離開(kāi)房間用戶(hù)的信息
* @param friendId 離開(kāi)用戶(hù)的ID
*/
-(void)webRTCHelper:(WebRTCHelper *)webRTCHelper removeFriend:(NSString *)friendId;
@end
3 .WebRTCHelper.m
//
// WebRTCHelper.m
// WebRTC_new
//
// Created by
// Copyright ? 2020年
//
#import "WebRTCHelper.h"
#define kAPPID @"1234567890abcdefg"
#define kDeviceUUID [[[UIDevice currentDevice] identifierForVendor] UUIDString]
//google提供的
static NSString *const RTCSTUNServerURL = @"stun:stun.l.google.com:19302";
//static NSString *const RTCSTUNServerURL2 = @"stun:23.21.150.121";
static NSString *const RTCSTUNServerURL2 =@"stun:global.stun.twilio.com:3478?transport=udp";
@interface WebRTCHelper()<RTCPeerConnectionDelegate,RTCVideoCapturerDelegate,SRWebSocketDelegate>
{
SRWebSocket *_socket;
NSString *_server;
RTCPeerConnectionFactory *_factory;
RTCMediaStream *_localStream;
NSString *_myId;
NSMutableDictionary *_connectionDic; // 存放 所有用戶(hù) 對(duì)應(yīng)的鏈接集合
NSMutableArray *_connectionIdArray; // 存放 房間人數(shù)的 id
NSMutableArray *ICEServers;
//是否顯示我的視頻流(默認(rèn)為yes涕侈,顯示职车;no為不顯示)
BOOL _usingCamera;
RTCCameraVideoCapturer * _capture;
}
@end
@implementation WebRTCHelper
static WebRTCHelper * instance = nil;
+(instancetype)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
[instance initData];
});
return instance;
}
-(void)initData{
_connectionDic = [NSMutableDictionary dictionary];
_connectionIdArray = [NSMutableArray array];
_usingCamera = YES;
}
#pragma mark -提供給外部的方法
/**
* 與服務(wù)器進(jìn)行連接
*/
- (void)connectServer:(NSString *)server port:(NSString *)port{
_server = server;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://%@:%@",server,port]] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20];
_socket = [[SRWebSocket alloc] initWithURLRequest:request];
_socket.delegate = self;
[_socket open];
}
/**
* 退出房間
*/
- (void)exitRoom
{
_localStream = nil;
[_capture stopCapture];
_capture = nil;
NSArray *arr =_connectionIdArray.copy;
[arr enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
[self closePeerConnection:obj];
}];
[_socket close];
}
/**
* 切換攝像頭
*/
- (void)swichCamera:(BOOL)_usingFrontCamera{
[self switchFrontBackCamera:_usingFrontCamera];
}
/**
* 是否顯示本地?cái)z像頭
*/
- (void)showLocaolCamera:(BOOL)_usingCamera{
_usingCamera = _usingCamera;
//如果為空班眯,則創(chuàng)建點(diǎn)對(duì)點(diǎn)工廠
if (!_factory)
{
//設(shè)置SSL傳輸
[RTCPeerConnectionFactory initialize];
[self create_factory];
}
//如果本地視頻流為空
if (!_localStream)
{
//創(chuàng)建本地流
[self createLocalStream];
}
// [_capture stopCapture];
//創(chuàng)建連接
[self createPeerConnections];
//添加
[self addStreams];
[self createOffers];
}
#pragma mark -內(nèi)部方法
/**
* 關(guān)閉peerConnection
*
* @param connectionId <#connectionId description#>
*/
- (void)closePeerConnection:(NSString *)connectionId
{
RTCPeerConnection *peerConnection = [_connectionDic objectForKey:connectionId];
if (peerConnection)
{
[peerConnection close];
}
[_connectionIdArray removeObject:connectionId];
[_connectionDic removeObjectForKey:connectionId];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self->_delegate respondsToSelector:@selector(webRTCHelper:closeWithUserId:)])
{
[self->_delegate webRTCHelper:self closeWithUserId:connectionId];
}
});
}
/**
* 創(chuàng)建點(diǎn)對(duì)點(diǎn)連接
*
* @param connectionId connectionId description
*
* @return <#return value description#>
*/
- (RTCPeerConnection *)createPeerConnection:(NSString *)connectionId
{
//如果點(diǎn)對(duì)點(diǎn)工廠為空
if (!_factory)
{
[self create_factory];
}
//得到ICEServer
if (!ICEServers) {
ICEServers = [NSMutableArray array];
[ICEServers addObject:[self defaultSTUNServer]];
}
//用工廠來(lái)創(chuàng)建連接
RTCConfiguration *configuration = [[RTCConfiguration alloc] init];
configuration.iceServers = ICEServers;
RTCPeerConnection *connection = [_factory peerConnectionWithConfiguration:configuration constraints:[self creatPeerConnectionConstraint] delegate:self];
return connection;
}
- (RTCMediaConstraints *)creatPeerConnectionConstraint
{
RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:@{kRTCMediaConstraintsOfferToReceiveAudio:kRTCMediaConstraintsValueTrue,kRTCMediaConstraintsOfferToReceiveVideo:kRTCMediaConstraintsValueTrue} optionalConstraints:@{@"DtlsSrtpKeyAgreement":@"true"}];
return constraints;
}
//初始化STUN Server (ICE Server)
- (RTCIceServer *)defaultSTUNServer{
return [[RTCIceServer alloc] initWithURLStrings:@[RTCSTUNServerURL,RTCSTUNServerURL2]];
}
/**
* 為所有連接添加流
*/
- (void)addStreams
{
//給每一個(gè)點(diǎn)對(duì)點(diǎn)連接,都加上本地流
NSDictionary *dic = [_connectionDic copy];
[dic enumerateKeysAndObjectsUsingBlock:^(NSString *key, RTCPeerConnection *obj, BOOL * _Nonnull stop) {
if (!self->_localStream)
{
[self createLocalStream];
}
[obj addStream:self->_localStream];
}];
}
/**
* 創(chuàng)建所有連接
*/
- (void)createPeerConnections
{
//從我們的連接數(shù)組里快速遍歷
NSArray *arr = _connectionIdArray.copy;
[arr enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
//根據(jù)連接ID去初始化 RTCPeerConnection 連接對(duì)象
RTCPeerConnection *connection = [self createPeerConnection:obj];
//設(shè)置這個(gè)ID對(duì)應(yīng)的 RTCPeerConnection對(duì)象
[self->_connectionDic setObject:connection forKey:obj];
}];
}
/**
* 創(chuàng)建本地視頻流
*/
-(void)createLocalStream{
_localStream = [_factory mediaStreamWithStreamId:@"ARDAMS"];
//音頻
RTCAudioTrack * audioTrack = [_factory audioTrackWithTrackId:@"ARDAMSa0"];
[_localStream addAudioTrack:audioTrack];
NSArray<AVCaptureDevice *> *captureDevices = [RTCCameraVideoCapturer captureDevices];
AVCaptureDevicePosition position = AVCaptureDevicePositionFront;
AVCaptureDevice * device = captureDevices[0];
for (AVCaptureDevice *obj in captureDevices) {
if (obj.position == position) {
device = obj;
break;
}
}
//檢測(cè)攝像頭權(quán)限
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied)
{
NSLog(@"相機(jī)訪問(wèn)受限");
if ([_delegate respondsToSelector:@selector(webRTCHelper:setLocalStream:userId:)])
{
[_delegate webRTCHelper:self setLocalStream:nil userId:_myId];
}
}
else
{
if (device)
{
RTCVideoSource *videoSource = [_factory videoSource];
[videoSource adaptOutputFormatToWidth:640 height:480 fps:20];
_capture = [[RTCCameraVideoCapturer alloc] initWithDelegate:videoSource];
// AVCaptureDeviceFormat * format = [[RTCCameraVideoCapturer supportedFormatsForDevice:device] lastObject];
AVCaptureDeviceFormat* format = device.activeFormat;
RTCVideoTrack *videoTrack = [_factory videoTrackWithSource:videoSource trackId:@"ARDAMSv0"];
__weak RTCCameraVideoCapturer *weakCapture = _capture;
[_localStream addVideoTrack:videoTrack];
if ([self->_delegate respondsToSelector:@selector(webRTCHelper:capturerSession:)])
{
[self->_delegate webRTCHelper:self capturerSession:weakCapture.captureSession];
}
[weakCapture stopCapture];
[weakCapture startCaptureWithDevice:device format:format fps:20 completionHandler:^(NSError * error) {
NSLog(@"11111111");
}];
}
else
{
NSLog(@"該設(shè)備不能打開(kāi)攝像頭");
if ([_delegate respondsToSelector:@selector(webRTCHelper:setLocalStream:userId:)])
{
[_delegate webRTCHelper:self setLocalStream:nil userId:_myId];
}
}
}
}
// 切換攝像頭
-(void)switchFrontBackCamera:(BOOL)usingFrontCamera
{
if (_localStream == nil)
return;
NSArray<AVCaptureDevice *> *captureDevices = [RTCCameraVideoCapturer captureDevices];
AVCaptureDevicePosition position = usingFrontCamera ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
AVCaptureDevice * device = nil;
for (AVCaptureDevice *obj in captureDevices) {
if (obj.position == position) {
device = obj;
break;
}
}
// AVCaptureDeviceFormat * format = [[RTCCameraVideoCapturer supportedFormatsForDevice:device] lastObject];
AVCaptureDeviceFormat* format = device.activeFormat;
[_capture stopCapture];
[_capture startCaptureWithDevice:device format:format fps:15 completionHandler:^(NSError * _Nonnull error)
{
}];
}
/**
* 視頻的相關(guān)約束
*/
- (RTCMediaConstraints *)localVideoConstraints
{
NSDictionary *mandatory = @{kRTCMediaConstraintsMaxWidth:@640,kRTCMediaConstraintsMinWidth:@640,kRTCMediaConstraintsMaxHeight:@480,kRTCMediaConstraintsMinHeight:@480,kRTCMediaConstraintsMinFrameRate:@15};
RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory optionalConstraints:@{@"DtlsSrtpKeyAgreement":@"true"}];
return constraints;
}
/**
* 創(chuàng)建offer
*/
-(void)createOffer:(RTCPeerConnection *)peerConnection{
if (peerConnection == nil) {
peerConnection = [self createPeerConnection:nil];
}
[peerConnection offerForConstraints:[self offerOranswerConstraint] completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) {
if (error == nil) {
// RTCSessionDescription *newSdp = [self descriptionForDescription:sdp preferredVideoCodec:@"H264"];
__weak RTCPeerConnection * weakPeerConnction = peerConnection;
[peerConnection setLocalDescription:sdp completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
[self setSessionDescriptionWithPeerConnection:weakPeerConnction];
}
}];
}
}];
}
/**
* 為所有連接創(chuàng)建offer
*/
- (void)createOffers
{
//給每一個(gè)點(diǎn)對(duì)點(diǎn)連接,都去創(chuàng)建offer
NSDictionary *dic = [_connectionDic copy];
[dic enumerateKeysAndObjectsUsingBlock:^(NSString *key, RTCPeerConnection *obj, BOOL * _Nonnull stop) {
[self createOffer:obj];
}];
}
/**
* 設(shè)置offer/answer的約束d
*/
- (RTCMediaConstraints *)offerOranswerConstraint
{
NSMutableDictionary * dic = [@{kRTCMediaConstraintsOfferToReceiveAudio:kRTCMediaConstraintsValueTrue,kRTCMediaConstraintsOfferToReceiveVideo:kRTCMediaConstraintsValueTrue} mutableCopy];
[dic setObject:(_usingCamera ? kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse) forKey:kRTCMediaConstraintsOfferToReceiveVideo];
RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:dic optionalConstraints:@{@"DtlsSrtpKeyAgreement":@"true"}];
return constraints;
}
// Called when setting a local or remote description.
//當(dāng)一個(gè)遠(yuǎn)程或者本地的SDP被設(shè)置就會(huì)調(diào)用
- (void)setSessionDescriptionWithPeerConnection:(RTCPeerConnection *)peerConnection
{
NSLog(@"%s",__func__);
NSString *currentId = [self getKeyFromConnectionDic:peerConnection];
if (currentId == nil)
{
NSLog(@"找不到用戶(hù)");
return;
}
//判斷蓖柔,當(dāng)前連接狀態(tài)為,收到了遠(yuǎn)程點(diǎn)發(fā)來(lái)的offer风纠,這個(gè)是進(jìn)入房間的時(shí)候况鸣,尚且沒(méi)人,來(lái)人就調(diào)到這里
if (peerConnection.signalingState == RTCSignalingStateHaveRemoteOffer)
{
//創(chuàng)建一個(gè)answer,會(huì)把自己的SDP信息返回出去
[peerConnection answerForConstraints:[self offerOranswerConstraint] completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) {
__weak RTCPeerConnection *obj = peerConnection;
[peerConnection setLocalDescription:sdp completionHandler:^(NSError * _Nullable error) {
[self setSessionDescriptionWithPeerConnection:obj];
}];
}];
}
//判斷連接狀態(tài)為本地發(fā)送offer
else if (peerConnection.signalingState == RTCSignalingStateHaveLocalOffer)
{
if (peerConnection.localDescription.type == RTCSdpTypeAnswer)
{
NSDictionary *dic = @{@"event": @"answer", @"data": @{@"sdp": peerConnection.localDescription.sdp}, @"sender": _myId, @"receiver":currentId};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_socket send:jsonString];
}
//發(fā)送者,發(fā)送自己的offer
else if(peerConnection.localDescription.type == RTCSdpTypeOffer)
{
NSDictionary *dic = @{@"event": @"offer", @"data": @{@"sdp": peerConnection.localDescription.sdp}, @"sender": _myId, @"receiver":currentId};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_socket send:jsonString];
}
}
else if (peerConnection.signalingState == RTCSignalingStateStable)
{
if (peerConnection.localDescription.type == RTCSdpTypeAnswer)
{
NSDictionary *dic = @{@"event": @"answer", @"data": @{@"sdp": peerConnection.localDescription.sdp}, @"sender": _myId, @"receiver":currentId};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_socket send:jsonString];
}
}
}
#pragma mark RTCPeerConnectionDelegate
/**獲取遠(yuǎn)程視頻流*/
- (void)peerConnection:( RTCPeerConnection *)peerConnection didAddStream:( RTCMediaStream *)stream {
NSString * userId = [self getKeyFromConnectionDic:peerConnection];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self->_delegate respondsToSelector:@selector(webRTCHelper:addRemoteStream:userId:)]) {
[self->_delegate webRTCHelper:self addRemoteStream:stream userId:userId];
}
});
}
/**RTCIceConnectionState 狀態(tài)變化*/
- (void)peerConnection:(nonnull RTCPeerConnection *)peerConnection didChangeIceConnectionState:(RTCIceConnectionState)newState {
NSLog(@"%s",__func__);
NSLog(@"newState=====%ld",(long)newState);
NSString * connectId = [self getKeyFromConnectionDic:peerConnection];
if (newState == RTCIceConnectionStateDisconnected) {
// [self createOffer:peerConnection];
//斷開(kāi)connection的連接
// dispatch_async(dispatch_get_main_queue(), ^{
// if ([self->_delegate respondsToSelector:@selector(webRTCHelper:closeWithUserId:)]) {
// [self->_delegate webRTCHelper:self closeWithUserId:connectId];
// }
// [self closePeerConnection:connectId];
// });
}
}
//[candidate.sdpMid isEqualToString:@"audio"]?@"audio":@"video"
/**獲取到新的candidate*/
- (void)peerConnection:(RTCPeerConnection *)peerConnection didGenerateIceCandidate:(RTCIceCandidate *)candidate{
NSLog(@"+++%s",__func__);
NSString *currentId = [self getKeyFromConnectionDic: peerConnection];
NSDictionary *dic = @{
@"event": @"_ice_candidate",
@"data":@{
@"candidate":@{
@"sdpMid":candidate.sdpMid ,
@"sdpMLineIndex":[NSNumber numberWithInteger:candidate.sdpMLineIndex],
@"candidate": candidate.sdp
}
} ,
@"sender":_myId ,
@"receiver":currentId
};
NSLog(@"%d-------%@",candidate.sdpMLineIndex,candidate.sdpMid);
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_socket send:jsonString];
}
/**刪除某個(gè)視頻流*/
- (void)peerConnection:(nonnull RTCPeerConnection *)peerConnection didRemoveStream:(nonnull RTCMediaStream *)stream {
NSLog(@"%s",__func__);
}
- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection{
NSLog(@"%s,line = %d object = %@",__FUNCTION__,__LINE__,peerConnection);
}
- (void)peerConnection:(nonnull RTCPeerConnection *)peerConnection didRemoveIceCandidates:(nonnull NSArray<RTCIceCandidate *> *)candidates {
NSLog(@"%s,line = %d object = %@",__FUNCTION__,__LINE__,candidates);
}
- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeSignalingState:(RTCSignalingState)stateChanged{
NSString *currentId = [self getKeyFromConnectionDic: peerConnection];
NSLog(@"stateChanged = %ld=======currentId=====%@",(long)stateChanged,currentId);
}
- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeIceGatheringState:(RTCIceGatheringState)newState{
NSLog(@"newState = %ld",newState);
}
#pragma mark -消息相關(guān)
-(void)peerConnection:(RTCPeerConnection *)peerConnection didOpenDataChannel:(RTCDataChannel *)dataChannel{
}
#pragma mark -視頻分辨率代理
- (void)capturer:(nonnull RTCVideoCapturer *)capturer didCaptureVideoFrame:(nonnull RTCVideoFrame *)frame {
}
-(void)create_factory
{
if(!_factory)
{
RTCInitializeSSL();
RTCDefaultVideoEncoderFactory *encodeFac = [[RTCDefaultVideoEncoderFactory alloc]init];
RTCDefaultVideoDecoderFactory *decodeFac = [[RTCDefaultVideoDecoderFactory alloc]init];
NSArray *arrCodecs = [encodeFac supportedCodecs];
// RTCVideoCodecInfo *info = arrCodecs[2];
// RTCVideoCodecInfo *info1 = arrCodecs[2];
// [encodeFac setPreferredCodec :info];
_factory = [[RTCPeerConnectionFactory alloc]initWithEncoderFactory:encodeFac decoderFactory:decodeFac];
// _factory = [[RTCPeerConnectionFactory alloc] init];
}
}
#pragma mark --open
-(void)openWith:(NSDictionary*)dic{
//得到data
//得到所有的連接
NSArray *connections = dic[@"remoteIds"];
//加到連接數(shù)組中去
[_connectionIdArray addObjectsFromArray:connections];
//拿到給自己分配的ID
_myId = dic[@"sender"];
//如果為空议忽,則創(chuàng)建點(diǎn)對(duì)點(diǎn)工廠
if (!_factory)
{
//設(shè)置SSL傳輸
[RTCPeerConnectionFactory initialize];
[self create_factory];
}
//如果本地視頻流為空
if (!_localStream)
{
//創(chuàng)建本地流
[self createLocalStream];
}
//創(chuàng)建連接
[self createPeerConnections];
//添加
[self addStreams];
// [self createOffers];
//獲取房間內(nèi)所有用戶(hù)的代理回調(diào)
dispatch_async(dispatch_get_main_queue(), ^{
if ([self->_friendDelegate respondsToSelector:@selector(webRTCHelper:gotFriendList:)]) {
[self->_friendDelegate webRTCHelper:self gotFriendList:connections];
}
});
}
#pragma mark --_ice_candidate
-(void)_ice_candidateWith:(NSDictionary *)dic{
NSDictionary *dataDic = dic[@"data"];
NSString *socketId = dic[@"sender"];
NSDictionary *candidateDic =dataDic[@"candidate"];
NSString *sdpMid = candidateDic[@"sdpMid"];
int sdpMLineIndex = [candidateDic[@"sdpMLineIndex"] intValue];
NSString *sdp = candidateDic[@"candidate"];
//生成遠(yuǎn)端網(wǎng)絡(luò)地址對(duì)象
RTCIceCandidate *candidate = [[RTCIceCandidate alloc] initWithSdp:sdp sdpMLineIndex:sdpMLineIndex sdpMid:sdpMid];
//拿到當(dāng)前對(duì)應(yīng)的點(diǎn)對(duì)點(diǎn)連接
RTCPeerConnection *peerConnection = [_connectionDic objectForKey:socketId];
//添加到點(diǎn)對(duì)點(diǎn)連接中
[peerConnection addIceCandidate:candidate];
}
#pragma mark ------join
-(void)joinWith:(NSDictionary *)dic{
//拿到新人的ID
NSString *socketId = dic[@"sender"];
//再去創(chuàng)建一個(gè)連接
RTCPeerConnection *peerConnection = [self createPeerConnection:socketId];
if (!_localStream)
{
[self createLocalStream];
}
//把本地流加到連接中去
[peerConnection addStream:_localStream];
//連接ID新加一個(gè)
[_connectionIdArray addObject:socketId];
//并且設(shè)置到Dic中去
[_connectionDic setObject:peerConnection forKey:socketId];
[self createOffer:peerConnection];
dispatch_async(dispatch_get_main_queue(), ^{
//設(shè)置新加入用戶(hù)代理
if ([self->_friendDelegate respondsToSelector:@selector(webRTCHelper:gotNewFriend:)]) {
[self->_friendDelegate webRTCHelper:self gotNewFriend:socketId];
}
});
}
#pragma mark --remove
-(void)removeWith:(NSDictionary *)dic{
//得到socketId懒闷,關(guān)閉這個(gè)peerConnection
NSString *socketId = dic[@"sender"];
[self closePeerConnection:socketId];
//設(shè)置關(guān)閉某個(gè)用戶(hù)聊天代理回調(diào)
dispatch_async(dispatch_get_main_queue(), ^{
// if ([self->_delegate respondsToSelector:@selector(webRTCHelper:closeWithUserId:)])
// {
// [self->_delegate webRTCHelper:self closeWithUserId:socketId];
// }
//設(shè)置退出房間用戶(hù)代理回調(diào)
if ([self->_friendDelegate respondsToSelector:@selector(webRTCHelper:removeFriend:)]) {
[self->_friendDelegate webRTCHelper:self removeFriend:socketId];
}
});
}
#pragma mark --offer
-(void)offerWith:(NSDictionary *)dic{
NSDictionary *dataDic = dic[@"data"];
//拿到SDP
NSString *sdp = dataDic[@"sdp"];
NSString *socketId = dic[@"sender"];
//拿到這個(gè)點(diǎn)對(duì)點(diǎn)的連接
RTCPeerConnection *peerConnection = [_connectionDic objectForKey:socketId];
//根據(jù)類(lèi)型和SDP 生成SDP描述對(duì)象
RTCSessionDescription *remoteSdp = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeOffer sdp:sdp];
// RTCSessionDescription *newSdp = [self descriptionForDescription:remoteSdp preferredVideoCodec:@"H264"];
//設(shè)置給這個(gè)點(diǎn)對(duì)點(diǎn)連接
__weak RTCPeerConnection *weakPeerConnection = peerConnection;
[peerConnection setRemoteDescription:remoteSdp completionHandler:^(NSError * _Nullable error) {
[self setSessionDescriptionWithPeerConnection:weakPeerConnection];
}];
//設(shè)置當(dāng)前角色狀態(tài)為被呼叫,(被發(fā)offer)
// _role = RoleCallee;
}
#pragma mark --answer
-(void)answerWith:(NSDictionary *)dic{
NSDictionary *dataDic = dic[@"data"];
if([dataDic count]==0){
return;
}
NSString *sdp = dataDic[@"sdp"];
NSString *socketId = dic[@"sender"];
RTCPeerConnection *peerConnection = [_connectionDic objectForKey:socketId];
RTCSessionDescription *remoteSdp = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeAnswer sdp:sdp];
// RTCSessionDescription *newSdp = [self descriptionForDescription:remoteSdp preferredVideoCodec:@"H264"];
__weak RTCPeerConnection * weakPeerConnection = peerConnection;
[peerConnection setRemoteDescription:remoteSdp completionHandler:^(NSError * _Nullable error) {
// [self setSessionDescriptionWithPeerConnection:weakPeerConnection];
}];
}
#pragma mark WebSocketDelegate
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
NSLog(@"收到服務(wù)器消息:%@",message);
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
NSString *eventName = dic[@"event"];
NSLog(@"====================eventName===============%@",eventName);
//1.發(fā)送加入房間后的反饋
if ([eventName isEqualToString:@"open"])
{
[self openWith:dic];
}
//4.接收到新加入的人發(fā)了ICE候選栈幸,(即經(jīng)過(guò)ICEServer而獲取到的地址)
else if ([eventName isEqualToString:@"_ice_candidate"])
{
[self _ice_candidateWith:dic];
}
//2.其他新人加入房間的信息
else if ([eventName isEqualToString:@"join"])
{
[self joinWith:dic];
}
//有人離開(kāi)房間的事件
else if ([eventName isEqualToString:@"remove"])
{
[self removeWith:dic];
}
//這個(gè)新加入的人發(fā)了個(gè)offer
else if ([eventName isEqualToString:@"offer"])
{
[self offerWith:dic];
}
//回應(yīng)offer
else if ([eventName isEqualToString:@"answer"])
{
[self answerWith:dic];
}
}
- (void)webSocketDidOpen:(SRWebSocket *)webSocket{
NSLog(@"socket連接成功");
dispatch_async(dispatch_get_main_queue(), ^{
if ([self->_delegate respondsToSelector:@selector(webRTCHelper:socketConnectState:)]) {
[self->_delegate webRTCHelper:self socketConnectState:WebSocketConnectSuccess];
}
});
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{
NSLog(@"socket連接失敗");
dispatch_async(dispatch_get_main_queue(), ^{
if ([self->_delegate respondsToSelector:@selector(webRTCHelper:socketConnectState:)]) {
[self->_delegate webRTCHelper:self socketConnectState:WebSocketConnectSuccess];
}
});
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
NSLog(@"socket關(guān)閉愤估。code = %ld,reason = %@",code,reason);
}
- (NSString *)getKeyFromConnectionDic:(RTCPeerConnection *)peerConnection
{
//find socketid by pc
static NSString *socketId;
[_connectionDic enumerateKeysAndObjectsUsingBlock:^(NSString *key, RTCPeerConnection *obj, BOOL * _Nonnull stop) {
if ([obj isEqual:peerConnection])
{
NSLog(@"%@",key);
socketId = key;
}
}];
return socketId;
}
- (RTCSessionDescription *)descriptionForDescription:(RTCSessionDescription *)description preferredVideoCodec:(NSString *)codec
{
NSString *sdpString = description.sdp;
NSString *lineSeparator = @"\n";
NSString *mLineSeparator = @" ";
// Copied from PeerConnectionClient.java.
// TODO(tkchin): Move this to a shared C++ file.
NSMutableArray *lines =
[NSMutableArray arrayWithArray:
[sdpString componentsSeparatedByString:lineSeparator]];
NSInteger mLineIndex = -1;
NSString *codecRtpMap = nil;
// a=rtpmap:<payload type> <encoding name>/<clock rate>
// [/<encoding parameters>]
NSString *pattern =
[NSString stringWithFormat:@"^a=rtpmap:(\\d+) %@(/\\d+)+[\r]?$", codec];
NSRegularExpression *regex =
[NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:nil];
for (NSInteger i = 0; (i < lines.count) && (mLineIndex == -1 || !codecRtpMap);
++i) {
NSString *line = lines[i];
if ([line hasPrefix:@"m=video"]) {
mLineIndex = i;
continue;
}
NSTextCheckingResult *codecMatches =
[regex firstMatchInString:line
options:0
range:NSMakeRange(0, line.length)];
if (codecMatches) {
codecRtpMap =
[line substringWithRange:[codecMatches rangeAtIndex:1]];
continue;
}
}
if (mLineIndex == -1) {
RTCLog(@"No m=video line, so can't prefer %@", codec);
return description;
}
if (!codecRtpMap) {
RTCLog(@"No rtpmap for %@", codec);
return description;
}
NSArray *origMLineParts =
[lines[mLineIndex] componentsSeparatedByString:mLineSeparator];
if (origMLineParts.count > 3) {
NSMutableArray *newMLineParts =
[NSMutableArray arrayWithCapacity:origMLineParts.count];
NSInteger origPartIndex = 0;
// Format is: m=<media> <port> <proto> <fmt> ...
[newMLineParts addObject:origMLineParts[origPartIndex++]];
[newMLineParts addObject:origMLineParts[origPartIndex++]];
[newMLineParts addObject:origMLineParts[origPartIndex++]];
[newMLineParts addObject:codecRtpMap];
for (; origPartIndex < origMLineParts.count; ++origPartIndex) {
if (![codecRtpMap isEqualToString:origMLineParts[origPartIndex]]) {
[newMLineParts addObject:origMLineParts[origPartIndex]];
}
}
NSString *newMLine =
[newMLineParts componentsJoinedByString:mLineSeparator];
[lines replaceObjectAtIndex:mLineIndex
withObject:newMLine];
} else {
RTCLogWarning(@"Wrong SDP media description format: %@", lines[mLineIndex]);
}
NSString *mangledSdpString = [lines componentsJoinedByString:lineSeparator];
return [[RTCSessionDescription alloc] initWithType:description.type
sdp:mangledSdpString];
}
@end
- ChatViewController.h
#import <UIKit/UIKit.h>
@interface ChatViewController : UIViewController
@end
- ChatViewController.m
//
// ChatViewController.m
// WebRTC_new
//
// Created by
// Copyright ? 2020年
//
#import "ChatViewController.h"
#import <WebRTC/WebRTC.h>
#import "WebRTCHelper.h"
#import <HGAlertViewController/HGAlertViewController.h>
#define kWidth [UIScreen mainScreen].bounds.size.width
#define kHeight [UIScreen mainScreen].bounds.size.height
@interface ChatCell:UICollectionViewCell
/*注釋*/
@property (nonatomic,strong) RTCEAGLVideoView *videoView;
/*注釋*/
@property (nonatomic,strong) RTCVideoTrack *track;
/*注釋*/
@property (nonatomic,strong) CALayer *baseLayer;
@end
@implementation ChatCell
- (instancetype)init
{
self = [super init];
if (self) {
self.videoView = [[RTCEAGLVideoView alloc] initWithFrame:CGRectMake(0, 0, (kWidth-40)/3, (kWidth-40)/3 + 50)];
[self.contentView addSubview:self.videoView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.contentView.layer.borderColor = [UIColor whiteColor].CGColor;
self.contentView.layer.borderWidth = 1;
self.videoView = [[RTCEAGLVideoView alloc] initWithFrame:CGRectMake(0, 0, (kWidth-40)/3, (kWidth-40)/3 + 50)];
}
return self;
}
- (void)setTrack:(RTCVideoTrack *)track{
if (track != nil) {
self.contentView.layer.mask = nil;
[self.contentView addSubview:self.videoView];
[track addRenderer:self.videoView];
}else{
self.contentView.layer.mask = nil;
for (UIView * view in self.contentView.subviews) {
[view removeFromSuperview];
}
[self setShaperLayer];
}
}
-(void)setShaperLayer{
//高亮狀態(tài)下的imageView
UIImageView * highlightImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
highlightImageView.center = self.contentView.center;
highlightImageView.image = [UIImage imageNamed:@"voice_ highlight"];
//默認(rèn)狀態(tài)下的imageView
UIImageView * defaultImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
defaultImageView.image = [UIImage imageNamed:@"voice_default"];
//先添加highlightImageView,在添加defaultImageview
defaultImageView.center = self.contentView.center;
[self.contentView addSubview:defaultImageView];
[self.contentView addSubview:highlightImageView];
self.baseLayer = nil;
if (self.baseLayer == nil) {
self.baseLayer = [CALayer layer];
self.baseLayer.frame = highlightImageView.bounds;
}
//創(chuàng)建左邊layer
CAShapeLayer * leftLayer = [CAShapeLayer layer];
leftLayer.fillColor = [UIColor greenColor].CGColor;
leftLayer.position = CGPointMake(-25, 25);
leftLayer.bounds = highlightImageView.bounds;
leftLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50,50)].CGPath;
[self.baseLayer addSublayer:leftLayer];
//左邊動(dòng)畫(huà)
CABasicAnimation * leftAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
leftAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(-25, 25)];
leftAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(5, 25)];
leftAnimation.duration = 1.0;
leftAnimation.repeatCount = MAXFLOAT;
[leftLayer addAnimation:leftAnimation forKey:@"noVoiceLeftAnimation"];
//創(chuàng)建右邊layer
CAShapeLayer * rightLayer = [CAShapeLayer layer];
// rightLayer.strokeColor = [UIColor greenColor].CGColor;
rightLayer.bounds = highlightImageView.bounds;
rightLayer.position = CGPointMake(75, 25);
rightLayer.fillColor = [UIColor greenColor].CGColor;
rightLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50, 50)].CGPath;
[self.baseLayer addSublayer:rightLayer];
//動(dòng)畫(huà)
CABasicAnimation * rightAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
rightAnimation.duration = 1.0;
rightAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(75, 25)];
rightAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(45, 25)];
rightAnimation.repeatCount = MAXFLOAT;
[rightLayer addAnimation:rightAnimation forKey:@"noVoiceRightAnimation"];
highlightImageView.layer.mask = self.baseLayer;
}
@end
@interface ChatViewController ()<UICollectionViewDelegate,UICollectionViewDataSource,WebRTCHelperDelegate,WebRTCHelperFrindDelegate,UIGestureRecognizerDelegate>
{
RTCMediaStream * _localSteam;
//判斷是顯示前攝像頭還是顯示后攝像頭(yes為前攝像頭。false為后攝像頭)
BOOL _usingFrontCamera;
//是否顯示我的視頻流(默認(rèn)為yes速址,顯示玩焰;no為不顯示)
BOOL _usingCamera;
}
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
/*保存遠(yuǎn)端視頻流*/
@property (nonatomic,strong) NSMutableDictionary *videoTracks;
/*房間內(nèi)其他用戶(hù)*/
@property (nonatomic,strong) NSMutableArray *members;
//顯示本地視頻的view
@property (weak, nonatomic) IBOutlet RTCCameraPreviewView *localVideoView;
@end
@implementation ChatViewController
/*注釋*/
- (NSMutableArray *)members
{
if(!_members){
_members = [NSMutableArray array];
}
return _members;
}
/*注釋*/
- (NSMutableDictionary *)videoTracks
{
if(!_videoTracks){
_videoTracks = [NSMutableDictionary dictionary];
}
return _videoTracks;
}
- (void)viewDidLoad {
[super viewDidLoad];
_usingFrontCamera = YES;
_usingCamera = YES;
NSLog(@"uid = %@",[[[UIDevice currentDevice] identifierForVendor] UUIDString]);
[WebRTCHelper shareInstance].delegate = self;
[WebRTCHelper shareInstance].friendDelegate = self;
UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
layout.minimumLineSpacing = 10;
layout.minimumInteritemSpacing = 10;
layout.itemSize = CGSizeMake((kWidth-40)/3, (kWidth-40)/3+50);
self.collectionView.collectionViewLayout = layout;
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
[self.collectionView registerClass:[ChatCell class] forCellWithReuseIdentifier:@"chatCell"];
[self connect];
//設(shè)置屏幕常亮
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
self.navigationItem.hidesBackButton = YES;
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
}
#pragma mark -按鈕點(diǎn)擊操作
/**
* 關(guān)閉按鈕
*/
- (IBAction)closeChatBtnClick:(UIButton *)sender {
[[WebRTCHelper shareInstance] exitRoom];
[self.navigationController popViewControllerAnimated:YES];
}
/**
* 攝像頭轉(zhuǎn)換(前后攝像頭)
*/
- (IBAction)swithVideoBtnClick:(UIButton *)sender {
_usingFrontCamera = !_usingFrontCamera;
[[WebRTCHelper shareInstance] swichCamera:_usingFrontCamera];
}
/**
* 語(yǔ)音是否開(kāi)啟
*/
- (IBAction)swichAudioBtnClick:(UIButton *)sender {
[sender setImage:[UIImage imageNamed:@"audioOn"] forState:UIControlStateNormal];
}
/**
是否顯示本地?cái)z像頭
*/
- (IBAction)swichLocaolCameraBtnClick:(UIButton *)sender {
_usingCamera = !_usingCamera;
// [sender setImage:[UIImage imageNamed:_usingCamera?@"videoOn":@"videoOff"] forState:UIControlStateNormal];
// [[WebRTCHelper shareInstance] showLocaolCamera:_usingCamera];
}
/**
* 連接服務(wù)器
*/
-(void)connect{
[[WebRTCHelper shareInstance] connectServer:@"172.27.104.54" port:@"9090/SocketServer"];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
NSLog(@"self.members=====%@", self.members);
return self.members.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
ChatCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"chatCell" forIndexPath:indexPath];
NSString * userId = [self.members objectAtIndex:indexPath.item];
RTCVideoTrack * track = [self.videoTracks objectForKey:userId];
NSLog(@"%@==6666==%@",track,userId);
cell.track = track;
return cell;
}
#pragma mark -WebRTCHelperFrindDelegate
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper gotFriendList:(NSArray *)friendList{
[self.members removeAllObjects];
[self.members addObjectsFromArray:friendList];
[_collectionView reloadData];
}
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper gotNewFriend:(NSString *)friendId{
[self.members addObject:friendId];
[_collectionView reloadData];
}
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper removeFriend:(NSString *)friendId{
[self.members removeObject:friendId];
[_collectionView reloadData];
if (self.members.count == 0) {
// [[WebRTCHelper shareInstance] exitRoom];
// [self.navigationController popViewControllerAnimated:YES];
}
}
#pragma mark -WebRTCHelperDelegate
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper receiveMessage:(NSString *)message{
NSLog(@"messaga = %@",message);
}
/**
* 舊版本獲取本地視頻流的代理,在這個(gè)代理里面會(huì)獲取到RTCVideoTrack類(lèi)芍锚,然后添加到RTCEAGLVideoView類(lèi)型的localVideoView上面
*/
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper setLocalStream:(RTCMediaStream *)steam userId:(NSString *)userId{
if (steam) {
_localSteam = steam;
RTCVideoTrack * track = [_localSteam.videoTracks lastObject];
[track addRenderer:self.localVideoView];
}
}
/**
* 新版獲取本地視頻流的方法
* @param captureSession RTCCameraPreviewView類(lèi)的參數(shù)昔园,通過(guò)設(shè)置這個(gè),就可以達(dá)到顯示本地視頻的功能
*/
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper capturerSession:(AVCaptureSession *)captureSession{
self.localVideoView.captureSession = captureSession;
}
/**
* 獲取遠(yuǎn)端視頻流的方法并炮,主要是獲取到RTCVideoTrack類(lèi)型的數(shù)據(jù)默刚,然后保存起來(lái),在刷新列表的時(shí)候逃魄,添加到對(duì)應(yīng)item里面的RTCEAGLVideoView類(lèi)型的view上面
*/
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper addRemoteStream:(RTCMediaStream *)stream userId:(NSString *)userId{
RTCVideoTrack * track = [stream.videoTracks lastObject];
NSLog(@"track====%@",track);
if (track != nil) {
[self.videoTracks setObject:track forKey:userId];
}
[self.collectionView reloadData];
}
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper closeWithUserId:(NSString *)userId{
[self.videoTracks removeObjectForKey:userId];
if (self.videoTracks.count >= self.members.count) {
[self.collectionView reloadData];
}
}
- (void)webRTCHelper:(WebRTCHelper *)webRTCHelper socketConnectState:(WebSocketConnectState)connectState{
if (connectState == WebSocketConnectField) {
HGAlertViewController * alert = [HGAlertViewController alertControllerWithTitle:@"提示" message:@"連接socket失敗" preferredStyle:(UIAlertControllerStyleAlert)];
alert.addAction(@"取消",^(UIAlertAction *alertAction){
[[WebRTCHelper shareInstance] exitRoom];
[self.navigationController popViewControllerAnimated:YES];
}).addAction(@"確定",^(UIAlertAction *alertAction){
[[WebRTCHelper shareInstance] exitRoom];
[self.navigationController popViewControllerAnimated:YES];
});
[self presentViewController:alert animated:YES completion:nil];
}
}
-(void)dealloc{
NSLog(@"移除了");
//取消設(shè)置屏幕常亮
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}
@end