// // webRtcPlayerViewController.m // 双子星云手机 // // Created by xd h on 2024/9/2. // #import "webRtcPlayerViewController.h" #import "webRtcPlayerViewController+AdjustBtnFrame.h" #import "webRtcPlayerViewController+AdjustPlayerViewFrame.h" #import "webRtcPlayerViewController+AppDelegate.h" #import "playerSetView.h" @interface webRtcPlayerViewController () { BOOL outputVolumeKVO;/*标记声音监听通知*/ double lastTimestamp;/*最后一帧时间戳*/ NSNumber *lastBytesReceived;/*最后一帧数据量*/ UILabel* netWorkInfoLabel;//网络延时等信息 正式环境屏蔽 //记录上次 线性增长接收包数 long lasPacketsReceived; //记录上次 总丢包数据 long lastAlllostData; BOOL isExitType;//退出云机 } @property (nonatomic, copy) NSTimer *playerSecondTimer; // 定时器-控制按钮 @property (nonatomic, assign) RTCIceConnectionState linkState; @property (nonatomic, assign) BOOL didHandleRotation;//第一次处理旋转 @property (nonatomic, assign) BOOL needToReportWebRtcType;//上报打洞是否成功 @end @implementation webRtcPlayerViewController @synthesize controlBtn; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self.toolBar setHidden:YES]; [self.navigationBar setHidden:YES]; [self.navBarBGView setHidden:YES]; [self.view setBackgroundColor:[UIColor blackColor]]; if([DFPlayer sharedPlayer].state == DFPlayerStateBuffering ||[DFPlayer sharedPlayer].state == DFPlayerStatePlaying){ _isCodeSuspendAudioType = YES; [[DFPlayer sharedPlayer] df_pause]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // 屏幕常亮 [UIApplication sharedApplication].idleTimerDisabled = YES; [[UIApplication sharedApplication] setStatusBarHidden:YES]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self noEnablePanRightBack]; [self addKVOObserverFun]; ksharedAppDelegate.supportScreenRotateType = YES; } - (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; [self HandleSomethingByExitVC]; } #pragma mark -(void)HandleSomethingByExitVC { [UIApplication sharedApplication].idleTimerDisabled = NO; [[UIApplication sharedApplication] setStatusBarHidden:NO]; isExitType = YES; if(_mediaStream){ [_mediaStream disconnect]; _mediaStream = nil; } [self enablePanRightBack]; [self removeKVOObserverFun]; //ksharedAppDelegate.supportScreenRotateType = NO; [self removeNewIndicator]; if(_playerSecondTimer){ [_playerSecondTimer invalidate]; _playerSecondTimer = nil; } _webRtcMsgMod = nil; } - (void)setWebRtcMsgMod:(webRtcMsgModel *)webRtcMsgMod { _webRtcMsgMod = webRtcMsgMod; [self beginWebRtcPlayFun]; } #pragma mark 开始拉流 - (void)beginWebRtcPlayFun { if(_mediaStream){ //[_mediaStream disconnect]; //[_mediaStream removeFromSuperview]; //_mediaStream = nil; } _mediaStream = [[RTC_OBJC_TYPE(AMediaStream) alloc] initWithFrame:CGRectZero]; [_mediaStream setEventDelegate:self]; [self.view addSubview:_mediaStream]; HLog(@"_mediaStream:%@",_mediaStream) [_mediaStream mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(0.f); make.bottom.mas_equalTo(0.f); make.right.mas_equalTo(0.f); make.top.mas_equalTo(0.f); }]; //判断是否是全屏 BOOL fullscreenType = [HWDataManager getBoolWithKey:Consn_player_full_screen_show]; CGSize phoneSize = CGSizeMake(1080.0, 1920.0); if(fullscreenType){ phoneSize = [RCCommandHelp commondToSetFullScreenPhoneSizeBySize]; } [self linkWebRtcFunWithSize:phoneSize]; [self initBaseUIFun]; [self setTimerCountDown]; //CGFloat curRate = 1080.0/1920.0; //CGFloat curRate = phoneSize.width/phoneSize.height; [self showNewIndicatorWithCanBack:YES canTouch:NO]; } #pragma mark 链接webrtc - (void)linkWebRtcFunWithSize:(CGSize)phoneSize { //链接用 NSString *signallingUrl = [[NSString alloc] initWithFormat:@"%@:%@",_webRtcMsgMod.data.signalling.domainName,_webRtcMsgMod.data.signalling.port]; NSURL *url = [NSURL URLWithString:signallingUrl]; //ice用 NSString *iceUrl = [[NSString alloc] initWithFormat:@"%@:%@",_webRtcMsgMod.data.turn.domainName,_webRtcMsgMod.data.turn.port]; NSMutableDictionary *ice = [NSMutableDictionary new]; if(iceUrl){ [ice setValue:iceUrl forKey:@"CHINANET"]; [ice setValue:iceUrl forKey:@"CMNET"]; [ice setValue:iceUrl forKey:@"UNICOM"]; } NSString *roomName = _webRtcMsgMod.data.uniqueIdentifier; NSInteger result = [_mediaStream start:url ice:ice sn:roomName direct:0 fmt:1//1(h264) 5(h265) videoWidth:(NSInteger)phoneSize.width videoHeight:(NSInteger)phoneSize.height fps:30 bitrate:3200//3000 cardWidth:(NSInteger)phoneSize.width//0//1080//0 cardHeight:(NSInteger)phoneSize.height//0//1920//0 cardDensity:0 token:@"vclusters"]; HLog(@"result:%ld",result) [_mediaStream setShouldGetStats:YES]; //清晰度码率最大上限 流畅1800 标清2200 高清2800 跟上面的 bitrate是同一个意思 //[_mediaStream setMaxBitrate:@2800]; } #pragma mark 重连 - (void)relinkWebRtcFun { // if(_linkState == RTCIceConnectionStateConnected // || _linkState == RTCIceConnectionStateCompleted) // {//链接中 不处理重连事件 // HLog(@"链接中 不处理重连事件") // return; // } if(isExitType){ return; } HLog(@"发起重连 ......") KWeakSelf mainBlock(^{ [weakSelf showNewIndicatorWithCanBack:YES canTouch:NO]; //判断是否是全屏 BOOL fullscreenType = [HWDataManager getBoolWithKey:Consn_player_full_screen_show]; CGSize phoneSize = CGSizeMake(1080.0, 1920.0); if(fullscreenType){ phoneSize = [RCCommandHelp commondToSetFullScreenPhoneSizeBySize]; } [weakSelf linkWebRtcFunWithSize:phoneSize]; }); } #pragma mark 初始化其他UI - (void)initBaseUIFun { if(netWorkInfoLabel){ [netWorkInfoLabel removeFromSuperview]; netWorkInfoLabel = nil; } netWorkInfoLabel = [[UILabel alloc] init]; [netWorkInfoLabel setTextColor:[UIColor greenColor]]; [netWorkInfoLabel setBackgroundColor:HW000000Color60]; [netWorkInfoLabel setFont:[UIFont systemFontOfSize:17]]; [netWorkInfoLabel setNumberOfLines:0]; [self.view addSubview:netWorkInfoLabel]; [netWorkInfoLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(80); make.left.mas_equalTo(20); }]; if(_bottomContrView){ [_bottomContrView removeFromSuperview]; _bottomContrView = nil; } _bottomContrView = [[webRtcPlayerBottomContrView alloc] init]; [self.view addSubview:_bottomContrView]; [self getPlayerBottomNavShowOrHidefun]; KWeakSelf _bottomContrView.didClickButtonFun = ^(NSInteger tag) { [weakSelf didClickBottomFunBy:tag]; }; /*控制按钮*/ if(controlBtn){ [controlBtn removeFromSuperview]; controlBtn = nil; } UIImage *driftBtnImage = [UIImage imageNamed:@"you_icon"]; controlBtn = [[UIButton alloc] init]; [controlBtn setBackgroundColor:[UIColor clearColor]]; [controlBtn setBackgroundImage:driftBtnImage forState:(UIControlStateNormal)]; [controlBtn addTarget:self action:@selector(controlBtnPressed:) forControlEvents:(UIControlEventTouchUpInside)]; [self.view addSubview:controlBtn]; //拖拽事件等 UIPanGestureRecognizer *gester = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(controlBtnPanGestureCallback:)]; [controlBtn addGestureRecognizer:gester]; //gester.delegate = self; [self initPointForControlBtnFun]; } #pragma mark 设置 controlBtn 的初始位置 - (void)initPointForControlBtnFun { CGFloat h_w_controlBtn = 60.f; /*区分横竖屏*/ if (_mediaStream.hw_w > _mediaStream.hw_h){ [controlBtn setFrame:CGRectMake((_mediaStream.hw_w - h_w_controlBtn)/2.f, _mediaStream.hw_h - h_w_controlBtn - 20.f, h_w_controlBtn, h_w_controlBtn)]; }else{ //默认居右 //[mPlayerView.controlBtn setFrame:CGRectMake(mPlayerView.width - h_w_controlBtn - 20.f, (SCREEN_H - h_w_controlBtn)/2.f, h_w_controlBtn, h_w_controlBtn)]; //默认居左 [controlBtn setFrame:CGRectMake( h_w_controlBtn + 10.f, (SCREEN_H - h_w_controlBtn)/2.f, h_w_controlBtn, h_w_controlBtn)]; } } #pragma mark 定时器 - (void)setTimerCountDown { HLog(@"开启一个"); if (_playerSecondTimer) { // 取消定时器 [_playerSecondTimer invalidate]; _playerSecondTimer = nil; } // 初始化值计时数据 //self.adjustTime = 1; //self.concentTime = [iTools getNowTimeStamp]; _playerSecondTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(timerChange) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:_playerSecondTimer forMode:NSRunLoopCommonModes]; } #pragma mark 定时器响应事件 - (void)timerChange { [self extensionAdjustBtnFrameCheckAdjustTime];// 1、悬浮球3s后 自动靠边隐藏 //[self checkConcentTime];// 3、30s之后 没有收到拉流数据 自动断开链接 } #pragma mark 控制按钮点击事件 - (void)controlBtnPressed:(UIButton*)but { playerSetView *nextVC = [[playerSetView alloc] init]; [ksharedAppDelegate.window addSubview:nextVC]; [nextVC mas_makeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(0); make.right.mas_equalTo(0); make.top.mas_equalTo(0.f); make.bottom.mas_equalTo(0.f); }]; KWeakSelf nextVC.didClickButtonFun = ^(NSInteger tag) { switch (tag) { case 10: {//截图 //self->needScreenShotType = YES; } break; #pragma mark 重启盒子 case 11: { [weakSelf didClickRestartFun]; } break; case 12: {//退出云机 [weakSelf exitCloudPhoneFun]; } break; case 100: { BOOL fullscreenType = [HWDataManager getBoolWithKey:Consn_player_full_screen_show]; if(fullscreenType){ [weakSelf showOpenTVP2PFun]; } else{ [weakSelf openTvShowFun]; } } break; case 101: { weakSelf.controlBtn.userInteractionEnabled = NO; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self->controlBtn.userInteractionEnabled = YES; }); [weakSelf showCloseTVP2PFun]; } break; default: break; } }; } #pragma mark 点击了重启云机 - (void)didClickRestartFun { KWeakSelf /*弹窗提示重启*/ ComontAlretViewController *nextVC = [[ComontAlretViewController alloc] initWithTiTle:NSLocalizedString(@"my_set_no_restart_phone_tips",nil) msg:@"" imageStr:@"" cancelTitle:NSLocalizedString(@"other_cancel",nil) okTitle:NSLocalizedString(@"my_set_no_restart_phone_btn_ok",nil) isOkBtnHighlight:NO didClickOk:^{ [weakSelf needToRebootFun]; //提示语 [[iToast makeText:NSLocalizedString(@"player_link_rebooting_Tips",nil)] show]; } didClickCancel:^{ }]; nextVC.modalPresentationStyle = UIModalPresentationCustom; [self presentViewController:nextVC animated:YES completion:^{ nextVC.view.superview.backgroundColor = [UIColor clearColor]; }]; } #pragma mark p2p通道 重启云机 - (void)needToRebootFun { [[webRtcManager shareManager] needToRebootFun]; [self startForceStartTimerFun]; [self pauseStream]; [webRtcManager shareManager].isRebootIngType = YES; [self showNewIndicatorWithCanBack:YES canTouch:NO]; } #pragma mark X 秒后检查是否软件重启成功 - (void)startForceStartTimerFun { KWeakSelf //1. 5秒后跟硬件发起硬重启 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf updateForceStartFun]; }); //2. 20秒后尝试重连 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf relinkWebRtcFun]; [[webRtcManager shareManager] relinkWebRtcFun]; }); } #pragma mark 需要强制重启 - (void)updateForceStartFun{ NSString* curSn = ksharedAppDelegate.DeviceThirdIdMod.data.changeSn; NSMutableDictionary *paraDict = [NSMutableDictionary dictionary]; [paraDict setValue:curSn forKey:@"sn"]; [paraDict setValue:[NSNumber numberWithBool:YES] forKey:@"isForceStart"]; [[netWorkManager shareInstance] CommonPostCallBackCode:updateForceStart Parameters:paraDict success:^(id _Nonnull responseObject) { SuperModel *model = [[SuperModel alloc] initWithDictionary:responseObject error:nil]; if (model.status == 0) { } else { } } failure:^(NSError * _Nonnull error) { HLog(@"%@", error); }]; } #pragma mark 退出云机 - (void)exitCloudPhoneFun { //[self setShowImgAndVoiceTypeFun:NO]; if(_isCodeSuspendAudioType){ [[DFPlayer sharedPlayer] df_play]; } if(isLan){//保证竖屏 [self hx_rotateToInterfaceOrientation:UIInterfaceOrientationPortrait]; } [self.navigationController popViewControllerAnimated:YES]; ksharedAppDelegate.supportScreenRotateType = NO; [self HandleSomethingByExitVC]; } #pragma mark 显示关闭投屏提示语 - (void)showOpenTVP2PFun{ /*弹窗提示TV投屏*/ ComontAlretViewController *nextVC = [[ComontAlretViewController alloc] initWithTiTle:NSLocalizedString(@"File_upload_Record_clear_Tip_title",nil) msg:NSLocalizedString(@"cloudPhone_fullscreen_tvshow_tip",nil) imageStr:@"" cancelTitle:NSLocalizedString(@"other_cancel",nil) okTitle:NSLocalizedString(@"my_set_TVP2P_Open_sure",nil) isOkBtnHighlight:YES didClickOk:^{ [self openTvShowFun]; //关闭全屏屏 [HWDataManager setBoolWithKey:Consn_player_full_screen_show value:NO]; [self setPlayerFullScreenNotFun]; } didClickCancel:^{ }]; nextVC.modalPresentationStyle = UIModalPresentationCustom; [self presentViewController:nextVC animated:YES completion:^{ nextVC.view.superview.backgroundColor = [UIColor clearColor]; }]; } #pragma mark 开始投屏 - (void)openTvShowFun { self.controlBtn.userInteractionEnabled = NO; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self->controlBtn.userInteractionEnabled = YES; }); [[webRtcManager shareManager] onTvFun]; [[iToast makeText:NSLocalizedString(@"cloudPhone_TV_show_tip",nil)] show]; //数据埋点 [[netWorkManager shareInstance] DataEmbeddingPointBy:3 withEventValue:@"Cloud_tv"]; } #pragma mark 显示关闭投屏提示语 - (void)showCloseTVP2PFun{ /*弹窗提示TV投屏*/ ComontAlretViewController *nextVC = [[ComontAlretViewController alloc] initWithTiTle:NSLocalizedString(@"my_set_no_close_TV_p2p_tip",nil) msg:@"" imageStr:@"" cancelTitle:NSLocalizedString(@"other_cancel",nil) okTitle:NSLocalizedString(@"my_set_TVP2P_Open_sure",nil) isOkBtnHighlight:YES didClickOk:^{ [self gotoCloseTVP2PFun]; } didClickCancel:^{ //HLog(@"2222"); }]; nextVC.modalPresentationStyle = UIModalPresentationCustom; [self presentViewController:nextVC animated:YES completion:^{ nextVC.view.superview.backgroundColor = [UIColor clearColor]; }]; } #pragma mark 确认开始TV投屏 -(void)gotoCloseTVP2PFun { [[webRtcManager shareManager] offTvFun]; } #pragma mark 挤下线功能 -(void)offlineOtherPhoneFun { NSString *curOaidStr = [RcGameWQKeyChain getOaidStringFun]; if(!curOaidStr){ curOaidStr = @""; } NSString *commondStr = [[NSString alloc] initWithFormat:@"{\"type\":\"login\",\"value\":\"%@\"}",curOaidStr]; [self send_dataInPlayer:commondStr]; } #pragma mark 单点登录被挤下线弹框 - (void)LogoutByOtherFun:(NSString*)oaid { if(logoutAlertVC){ return; } NSString *curOaidStr = [RcGameWQKeyChain getOaidStringFun]; if(oaid && oaid.length >0 && [oaid isEqualToString:curOaidStr]){ return; } //[self didReceiveLogoutMsgFun]; //yyyy-MM-dd HH:mm:ss NSString*dateStr = [iTools getNowTimeString2]; if(dateStr && dateStr.length == 19){ dateStr = [dateStr substringWithRange:NSMakeRange(11, 5)]; } NSString *LogoutTimerStr = [[NSString alloc] initWithFormat:@"%@%@%@",NSLocalizedString(@"single_sign_on_Tips_one",nil),dateStr,NSLocalizedString(@"single_sign_on_Tips_two",nil)]; NSString * loginAgainStr = NSLocalizedString(@"single_sign_on_login_again",nil); BOOL isOkBtnHighlight = YES; if(!ksharedAppDelegate.DeviceThirdIdMod.data.isPrivacyMode){ loginAgainStr = @""; isOkBtnHighlight = NO; } KWeakSelf if(ksharedAppDelegate.isDidShowPwdType){ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf LogoutByOtherFun:oaid]; HLog(@"weakSelf LogoutByOtherFun") }); return; } //被挤下线 停止拉流 [self pauseStream]; if(isLan){//保证竖屏 [self hx_rotateToInterfaceOrientation:UIInterfaceOrientationPortrait]; } //退出推流页 [self.navigationController popViewControllerAnimated:NO]; //与安卓保存一致 tabbar 退倒nas页 [ksharedAppDelegate.mainTabBar setSelectedIndex:0]; [self HandleSomethingByExitVC]; /*弹窗提示重启*/ //KWeakSelf logoutAlertVC = [[ComontAlretViewController alloc] initWithTiTle:NSLocalizedString(@"single_sign_on_Tips_logout",nil) msg:LogoutTimerStr imageStr:@"" cancelTitle:NSLocalizedString(@"single_sign_on_exit",nil) okTitle:loginAgainStr isOkBtnHighlight:isOkBtnHighlight didClickOk:^{ self->logoutAlertVC = nil; [[NSNotificationCenter defaultCenter] postNotificationName:lockBypwdNotification object:nil]; } didClickCancel:^{ exit(0);/*强制退出app*/ }]; logoutAlertVC.modalPresentationStyle = UIModalPresentationCustom; logoutAlertVC.view.backgroundColor = [UIColor blackColor]; [[iTools appRootViewController] presentViewController:logoutAlertVC animated:YES completion:^{ self->logoutAlertVC.view.superview.backgroundColor = [UIColor clearColor]; }]; } #pragma mark 底部按钮事件 - (void)didClickBottomFunBy:(NSInteger)tag { int keyType = 0; if(tag == 1){//task keyType = 187; } else if(tag == 2){//home keyType = 3; } else if(tag == 3){//back keyType = 4; } if(keyType > 0){ [self didClickKeyEventFunBy:keyType]; } } #pragma mark 按键事件 // home 3 back 4 task 187 volumeUp 24 volumeDown 25 - (void)didClickKeyEventFunBy:(int)keyType { [_mediaStream sendKey:keyType]; } #pragma mark 云机交互的消息发送 走P2P通道 -(void)send_data:(NSString*)commandStr { [[webRtcManager shareManager] send_data:commandStr]; } #pragma mark 云机交互的消息发送 走音视频推拉流通道 -(void)send_dataInPlayer:(NSString*)commandStr { [_mediaStream sendData:commandStr]; } #pragma mark -- /*底部导航栏开关通知*/ - (void)getPlayerBottomNavShowOrHidefun { BOOL haveShowBottonNavType = ![HWDataManager getBoolWithKey:Consn_player_Nav_hide]; if(haveShowBottonNavType){ _bottomContrView.hidden = NO; } else{ _bottomContrView.hidden = YES; } } #pragma mark -- /*全面屏开关通知*/ - (void)setPlayerFullScreenNotFun { BOOL fullscreenType = [HWDataManager getBoolWithKey:Consn_player_full_screen_show]; CGFloat tempRate = 0.0; //重新设置分辨率 if(fullscreenType){ NSInteger cardDensity = 380;//480;//422;//380;//460; CGSize size = [RCCommandHelp commondToSetFullScreenPhoneSizeBySize]; if(isLan){ if(size.width < size.height){ size = CGSizeMake(size.height,size.width); } } [self setCardSize:size.width cardHeight:size.height cardDensity:cardDensity]; tempRate = (size.width *1.0)/(size.height *1.0); } else{ CGSize size = CGSizeMake(1080.0, 1920.0); if(isLan){ size = CGSizeMake(1920.0, 1080.0); } [self setCardSize:(NSInteger)size.width cardHeight:(NSInteger)size.height cardDensity:480]; tempRate = size.width/size.height; } //dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (tempRate > 1) {/*横屏*/ [self setLanMas_makeWithImageRate:tempRate]; }else{/*竖屏*/ [self setPoMas_makeWithImageRate:tempRate]; } // }); } #pragma mark -- /*推流中修改卡的分辨率*/ - (void)setCardSize:(NSInteger)cardWidth cardHeight:(NSInteger)cardHeight cardDensity:(NSInteger)cardDensit { if(isLan){ HLog(@"云机屏幕 设置 宽:%ld 高:%ld",cardHeight,cardWidth); [_mediaStream setCardSize:cardHeight cardHeight:cardWidth cardDensity:cardDensit]; [_mediaStream setVideoSize:cardHeight videoHeight:cardWidth]; } else{ HLog(@"云机屏幕 设置 宽:%ld 高:%ld",cardWidth,cardHeight); [_mediaStream setCardSize:cardWidth cardHeight:cardHeight cardDensity:cardDensit]; [_mediaStream setVideoSize:cardWidth videoHeight:cardHeight]; } } #pragma mark -- /*通知添加与移除*/ - (void)addKVOObserverFun { outputVolumeKVO = YES; [[AVAudioSession sharedInstance] addObserver:self forKeyPath:@"outputVolume" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(void *)[AVAudioSession sharedInstance]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getPlayerBottomNavShowOrHidefun) name:getPlayerBottomNavNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setPlayerFullScreenNotFun) name:setPlayerFullScreenNotification object:nil]; //监听系统 前后台事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; //输入密码完成 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didInpuPwdOkFun) name:didInputPWDNotification object:nil]; } - (void)removeKVOObserverFun { if (outputVolumeKVO) { [[AVAudioSession sharedInstance] removeObserver:self forKeyPath:@"outputVolume" context:(void *)[AVAudioSession sharedInstance]]; outputVolumeKVO = NO; } [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark 系统音量键监听 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if(context == (__bridge void *)[AVAudioSession sharedInstance]) { /*音量开关打开时 允许发送指令 否则直接返回*/ float newValue = [[change objectForKey:@"new"] floatValue]; float oldValue = [[change objectForKey:@"old"] floatValue]; //HLog(@"音量 --old: %f ---new: %f",oldValue,newValue) if (newValue > oldValue) { HLog(@"\n-----音量增加"); [self didClickKeyEventFunBy:24]; } else { HLog(@"\n-----音量降低"); [self didClickKeyEventFunBy:25]; } } } #pragma mark 监听到云机的宽高以及屏幕方向 - (void)handlUIAfterGetCloudPhoneVideoWidth:(int)videoWidth videoHeight:(int)videoHeight rotation:(int)rotation { HLog(@"云机屏幕 回调 宽:%d 高:%d 横竖屏:%d",videoWidth,videoHeight,rotation); CGFloat curRate = (CGFloat)videoWidth/(CGFloat)videoHeight; if(lastVideoWHRate == curRate){ return; } didAdjusBtnType = NO; if(rotation == 0){//竖屏 //切换到竖屏 isLan = NO; [self hx_rotateToInterfaceOrientation:UIInterfaceOrientationPortrait]; [self setPoMas_makeWithImageRate:curRate]; } else if(rotation == 1){//横屏 //切换到横屏屏 isLan = YES; [self hx_rotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight]; [self setLanMas_makeWithImageRate:curRate]; } _didHandleRotation = YES; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self extensionAdjustBtnFrameCheckAdjustTime]; }); } #pragma mark WebRTC 回调 MediaStreamClientEventsDelegate #pragma mark - 宽高变化 -(void)onFrameResolutionChangedFromPeerName:(NSString*)peerName videoWidth:(int)videoWidth videoHeight:(int)videoHeight rotation:(int)rotation { HLog(@"推拉流 onFrameResolutionChangedFromPeerName:%@---%d---%d--%d",peerName,videoWidth,videoHeight,rotation) HLog(@"ok _mediaStream:%@",_mediaStream) mainBlock(^{ [self handlUIAfterGetCloudPhoneVideoWidth:videoWidth videoHeight:videoHeight rotation:rotation]; }); } //code 0 成功 1失败 -(void)onAuthResultFromPeerName:(NSString*)peerName code:(int)code descriptions:(NSString*)descriptions { HLog(@"onAuthResultFromPeerName:%@---%d---%@",peerName,code,descriptions) } #pragma mark 链接发生变化 -(void)onChangeConnectionStateFromPeerName:(NSString*)peerName didChangeIceConnectionState:(RTCIceConnectionState)state { HLog(@"推拉流 onChangeConnectionStateFromPeerName: state:%ld",state) _linkState = state; switch (state) { case RTCIceConnectionStateConnected:{ //链接成功 mainBlock(^{ [self removeNewIndicator]; }); } break; case RTCIceConnectionStateCompleted: //链接完成 break; case RTCIceConnectionStateFailed: case RTCIceConnectionStateDisconnected: case RTCIceConnectionStateClosed: //链接关闭 [self relinkWebRtcFun]; break; default: break; } } - (void)dataChannelDidChangeFromPeerName:(NSString*)peerName State:(RTCDataChannelState)state; { HLog(@"推拉流 dataChannelDidChangeFromPeerName: state:%ld",state) switch (state) { case RTCDataChannelStateConnecting: { } break; case RTCDataChannelStateOpen: { //链接成功 [self offlineOtherPhoneFun]; } break; case RTCDataChannelStateClosing: { } break; case RTCDataChannelStateClosed: { //链接断开 [self relinkWebRtcFun]; } break; default: break; } } -(void)onChannelDataFromPeerName:(NSString*)peerName buffer:(RTC_OBJC_TYPE(RTCDataBuffer) *)buffer { //HLog(@"onIceConnectedFromPeerName:%@",buffer.data); if(buffer && buffer.data){ KWeakSelf mainBlock(^{ [weakSelf handleWebRtcMsgResponseBy:buffer.data]; }); } } -(void)didGetStats:(NSString*)peerName stats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats { if(!_needToReportWebRtcType){ [[webRtcManager shareManager] reportWebRtcRePoportTypeIsChannel:NO withStats:stats]; _needToReportWebRtcType = YES; } NSString *selectedCandidatePairId = nil; NSString *localCandidateId = nil; NSString *remoteCandidateId = nil; /*延时数据*/ NSNumber *currentRoundTripTime = nil; //线性增长接收包数 long packetsReceived = 0; //视频丢包数据 long videolostData = 0; //音频丢包数据 long audiolostData = 0; //总丢包数据 long alllostData = 0; //每秒帧数 ----界面展示的帧率 NSInteger framesPerSecond = 0; //计算传输速度 long preReceive = 0; KWeakSelf for (NSString *key in [stats.statistics allKeys]) { RTCStatistics *rtcStatistics = [stats.statistics objectForKey:key]; //HLog(@"hxd11111---->%@",rtcStatistics.type); if ([rtcStatistics.type isEqualToString:@"transport"]) { //HLog(@"传输数据---->%@",rtcStatistics.values); double timestamp = rtcStatistics.timestamp_us; NSNumber *bytesReceived = [rtcStatistics.values objectForKey:@"bytesReceived"]; NSNumber *packetsReceivedNumber = [rtcStatistics.values objectForKey:@"packetsReceived"]; packetsReceived = [packetsReceivedNumber longValue]; if (lastTimestamp > 0) { long diffReceive = [bytesReceived longValue] - [lastBytesReceived longValue]; double diffTime = timestamp - lastTimestamp; preReceive = (diffReceive / diffTime) * 1000000; //HLog(@"传输数据速度---->%ld",preReceive); // mainBlock((^{ // if (self->mPlayerView.upControlView.hidden == NO){ // NSString *str = [NSString stringWithFormat:@"清晰度 %ldk/s",preReceive/1024]; // NSMutableAttributedString *underAttr = [[NSMutableAttributedString alloc] initWithString:str attributes:@{NSForegroundColorAttributeName:[UIColor hwColor:@"#FFFFFF"]}]; // [underAttr addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12]} range:NSMakeRange(0, str.length - 2)]; // [underAttr addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:10]} range:NSMakeRange(str.length - 2,2)]; // [self->mPlayerView.upControlView.articulationBtn setAttributedTitle:underAttr forState:(UIControlStateNormal)]; // }else{ // NSString *str = [NSString stringWithFormat:@"%ldk/s",preReceive/1024]; // NSMutableAttributedString *underAttr = [[NSMutableAttributedString alloc] initWithString:str attributes:@{NSForegroundColorAttributeName:[UIColor hwColor:@"#FFFFFF"]}]; // [underAttr addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12]} range:NSMakeRange(0, str.length - 2)]; // [underAttr addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:10]} range:NSMakeRange(str.length - 2,2)]; // [self->mPlayerView.controlBtn setAttributedTitle:underAttr forState:(UIControlStateNormal)]; // } // })); }else{ lastTimestamp = timestamp; lastBytesReceived = bytesReceived; } selectedCandidatePairId = [rtcStatistics.values objectForKey:@"selectedCandidatePairId"]; //HLog(@"三网数据--selectedCandidatePairId-->%@",selectedCandidatePairId); } /*丢包数据*/ if ([rtcStatistics.type isEqualToString:@"inbound-rtp"]) { NSString *kind = [rtcStatistics.values objectForKey:@"kind"]; if([kind isEqualToString:@"video"]){ NSNumber*videolostDataNumber = [rtcStatistics.values objectForKey:@"packetsLost"]; videolostData = [videolostDataNumber longValue]; NSNumber*framesPerSecondNumber = [rtcStatistics.values objectForKey:@"framesPerSecond"]; framesPerSecond = [framesPerSecondNumber integerValue]; } else if([kind isEqualToString:@"audio"]){ NSNumber*audiolostDataNumber = [rtcStatistics.values objectForKey:@"packetsLost"]; audiolostData = [audiolostDataNumber longValue]; } } for (NSString *keyin in [rtcStatistics.values allKeys]) { if ([keyin isEqualToString:@"currentRoundTripTime"]){ /*延时数据*/ currentRoundTripTime = [rtcStatistics.values objectForKey:@"currentRoundTripTime"]; //[self updateAutoRateAndNetWorkStatesWithDelatyMS:(NSInteger)currentRoundTripTime.floatValue*1000]; break; } } } //netWorkInfoLabel 网络信息 测试用 if (selectedCandidatePairId) { for (NSString *key in [stats.statistics allKeys]) { RTCStatistics *rtcStatistics = [stats.statistics objectForKey:key]; if ([rtcStatistics.type isEqualToString:@"candidate-pair"]){ NSString *tempselectedCandidatePairId = rtcStatistics.id; if ([tempselectedCandidatePairId isEqualToString:selectedCandidatePairId]) { localCandidateId = [rtcStatistics.values objectForKey:@"localCandidateId"]; remoteCandidateId = [rtcStatistics.values objectForKey:@"remoteCandidateId"]; // HLog(@"三网数据--localCandidateId-->%@",localCandidateId); // HLog(@"三网数据--remoteCandidateId-->%@",remoteCandidateId); } } } } NSString *remoteCandidateStr = nil; NSString *localCandidateStr = nil; if (localCandidateId && remoteCandidateId) { for (NSString *key in [stats.statistics allKeys]) { RTCStatistics *rtcStatistics = [stats.statistics objectForKey:key]; if ([rtcStatistics.type isEqualToString:@"local-candidate"]){ NSString *templocalCandidateId = rtcStatistics.id; if ([templocalCandidateId isEqualToString:localCandidateId]) { //HLog(@"三网数据--localCandidateIdInfo-->%@",rtcStatistics.values); localCandidateStr = @"本地\n"; NSString *candidateType = [rtcStatistics.values objectForKey:@"candidateType"]; if (candidateType) { localCandidateStr = [localCandidateStr stringByAppendingString:[NSString stringWithFormat:@"candidateType:%@",candidateType]]; localCandidateStr = [localCandidateStr stringByAppendingString:@"\n"]; } NSString *address = [rtcStatistics.values objectForKey:@"address"]; if (address) { localCandidateStr = [localCandidateStr stringByAppendingString:[NSString stringWithFormat:@"address:%@",address]]; localCandidateStr = [localCandidateStr stringByAppendingString:@"\n"]; } NSString *ip = [rtcStatistics.values objectForKey:@"ip"]; if (ip) { localCandidateStr = [localCandidateStr stringByAppendingString:[NSString stringWithFormat:@"ip:%@",ip]]; localCandidateStr = [localCandidateStr stringByAppendingString:@"\n"]; } NSString *relatedAddress = [rtcStatistics.values objectForKey:@"relatedAddress"]; if (relatedAddress) { localCandidateStr = [localCandidateStr stringByAppendingString:[NSString stringWithFormat:@"relatedAddress:%@",relatedAddress]]; localCandidateStr = [localCandidateStr stringByAppendingString:@"\n"]; } } }else if ([rtcStatistics.type isEqualToString:@"remote-candidate"]){ NSString *tempRemoteCandidateId = rtcStatistics.id; if ([tempRemoteCandidateId isEqualToString:remoteCandidateId]) { //HLog(@"三网数据--remoteCandidateIdInfo-->%@",rtcStatistics.values); remoteCandidateStr = @"远端\n"; NSString *candidateType = [rtcStatistics.values objectForKey:@"candidateType"]; if (candidateType) { remoteCandidateStr = [remoteCandidateStr stringByAppendingString:[NSString stringWithFormat:@"candidateType:%@",candidateType]]; remoteCandidateStr = [remoteCandidateStr stringByAppendingString:@"\n"]; } NSString *address = [rtcStatistics.values objectForKey:@"address"]; if (address) { remoteCandidateStr = [remoteCandidateStr stringByAppendingString:[NSString stringWithFormat:@"address:%@",address]]; remoteCandidateStr = [remoteCandidateStr stringByAppendingString:@"\n"]; } NSString *ip = [rtcStatistics.values objectForKey:@"ip"]; if (ip) { remoteCandidateStr = [remoteCandidateStr stringByAppendingString:[NSString stringWithFormat:@"ip:%@",ip]]; remoteCandidateStr = [remoteCandidateStr stringByAppendingString:@"\n"]; } NSString *relatedAddress = [rtcStatistics.values objectForKey:@"relatedAddress"]; if (relatedAddress) { remoteCandidateStr = [remoteCandidateStr stringByAppendingString:[NSString stringWithFormat:@"relatedAddress:%@",relatedAddress]]; remoteCandidateStr = [remoteCandidateStr stringByAppendingString:@"\n"]; } } } } } NSString *showStr = @""; if (localCandidateStr) { showStr = [showStr stringByAppendingString:localCandidateStr]; } if (remoteCandidateStr) { showStr = [showStr stringByAppendingString:remoteCandidateStr]; } //网络延迟, 丢包率,FPS,网速 分辨率 流量 //延时数据 NSString*currentRoundTripTimeStr = [[NSString alloc] initWithFormat:@"延时:%ldms\n",(NSInteger)(currentRoundTripTime.floatValue*1000)]; showStr = [showStr stringByAppendingString:currentRoundTripTimeStr]; //计算上次报道到这一次的丢包率---------界面展示百分比丢包率 long allPacketsReceived = packetsReceived - lasPacketsReceived; alllostData = audiolostData + videolostData; NSInteger lostRate = ((alllostData - lastAlllostData) *1.0 / (allPacketsReceived + (alllostData - lastAlllostData))) *100; //记录上一次丢包数 lastAlllostData = alllostData; //记录上一次接收包数 lasPacketsReceived = packetsReceived; NSString*lostDataStr = [[NSString alloc] initWithFormat:@"丢包率:(%ld/%ld) %ld%%\n",alllostData - lastAlllostData,allPacketsReceived + alllostData - lastAlllostData,lostRate]; showStr = [showStr stringByAppendingString:lostDataStr]; //FPS NSString*FPSStr = [[NSString alloc] initWithFormat:@"FPS:%ld\n",framesPerSecond]; showStr = [showStr stringByAppendingString:FPSStr]; //网速 传输数据速度 NSString *netDataSpeedStr = [NSString stringWithFormat:@"传输数据速度:%ldk/s\n",preReceive/1024]; showStr = [showStr stringByAppendingString:netDataSpeedStr]; mainBlock(^{ [self->netWorkInfoLabel setText:showStr]; }); } #pragma mark 收到的webrtc消息处理 - (void)handleWebRtcMsgResponseBy:(NSData*)message { if([message isKindOfClass:[NSMutableString class]] || [message isKindOfClass:[NSString class]]) { message = [(NSString *)message dataUsingEncoding:(NSUTF8StringEncoding)]; } NSError *error = nil; NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:message options:NSJSONReadingMutableContainers error:&error]; HLog(@"webRtc 音视频推拉流 通道接收消息:------------------%@",dataDict); if(!dataDict){ //[weakSelf handleDownloadResponseFunBy:message]; return; } if(![dataDict isKindOfClass:[NSDictionary class]]){ //[__NSCFString allKeys] unrecognized selector sent to ins return; } if(![[dataDict allKeys] containsObject:@"type"]){ return; } NSString *messageType = dataDict[@"type"]; if ([messageType isEqualToString:@"login"]) { if([[dataDict allKeys] containsObject:@"value"]){ NSString *value = dataDict[@"value"]; [self LogoutByOtherFun:value]; } } } //延迟系统触摸事件 - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures{ return UIRectEdgeAll; } @end