12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568 |
- //
- // NewClassRoomViewController.m
- // StudentDaya
- //
- // Created by Kyle on 2020/6/12.
- // Copyright © 2020 DayaMusic. All rights reserved.
- //
- #import "NewClassRoomViewController.h"
- #import "ClassTitleView.h"
- #import "ClassroomMainContainer.h"
- #import "ClassVideoListView.h"
- #import "KSNormalAlertView.h"
- // 聊天列表
- #import "LoginHelper.h"
- #import "ChatAreaView.h"
- #import "RTCService.h"
- #import "Classroom.h"
- #import "ClassroomService.h"
- #import <MBProgressHUD/MBProgressHUD.h>
- #import "AppDelegate.h"
- #import "KSWhiteboardControl.h"
- #import "UIDevice+TFDevice.h"
- #import "KSRemoteUserManager.h"
- #import "LocalRenderManager.h"
- #import "NSDate+Transform.h"
- #import "UIViewController+zhStatusBarStyle.h"
- #import "KSTipsView.h"
- #import "ClassroomTimerManager.h"
- #define TitleViewHeight (82)
- #define KSExamSongFileKey (@"KSDownloadAccompany")
- #define TTimeLableWidth (180)
- #define TTimeLableHeight (20)
- typedef NS_ENUM(NSInteger, ACCOMPANYACTION) {
- ACCOMPANYACTION_NONE, // 没有在播放
- ACCOMPANYACTION_SONG, // 播放原声
- ACCOMPANYACTION_ACCOMPANY, // 播放伴奏
- };
- @interface NewClassRoomViewController ()<ClassTitleViewDelegate, RCRTCRoomEventDelegate, ClassroomDelegate, ClassroomMainContainerDelegate, UIGestureRecognizerDelegate, RCRTCStatusReportDelegate, RTCServiceDelegate,KSWhiteboardControlDelegate, ClassVideoListViewDelegate,ClassroomTimerManagerDelegate>
- @property (nonatomic, strong) ClassTitleView *titleView;
- @property (nonatomic, strong) ClassroomMainContainer *containerView;
- @property (nonatomic, strong) ClassVideoListView *videoListView;
- @property (nonatomic, strong) ChatAreaView *chatAreaView;
- @property (nonatomic, strong) KSWhiteboardControl *wBoardCtrl;
- @property (nonatomic, strong) MBProgressHUD *hud;
- @property (nonatomic, strong) NSMutableArray<NSNumber*>* packetLossStore;
- @property (nonatomic, strong) UILabel *networkLabel;
- @property (nonatomic, assign) BOOL cancleAlert;
- @property (nonatomic, assign) BOOL isQuitRoom; // 是否正在退出房间
- @property (nonatomic, assign) BOOL hasShowAlert;
- @property (nonatomic, assign) BOOL isSwitchLine; // 是否线路切换
- @property (nonatomic, assign) BOOL isChat;
- /** 定时器 */
- @property (nonatomic, strong) NSTimer *timer;
- /** 定时器多少秒循环一次 */
- @property (nonatomic, assign) float timerLength;
- /** 当前是否播放状态 */
- @property (nonatomic, assign) BOOL isPlay;
- /** 当前发出的声次数(用来判断播放"嘀"还是"咚"声音) */
- @property (nonatomic, assign) int currentTotalNo;
- /** 节拍类型 */
- @property (nonatomic, assign) KSMetronomeType metronomeStat;
- /** 播放速率,范围40~240 */
- @property (nonatomic, assign) int rate;
- // 播放节拍器音量
- @property (nonatomic, assign) NSInteger volume;
- /** 记录上一次的频率 */
- @property (nonatomic, assign) int lastRate;
- @property (nonatomic, strong) NSString *pathDing;
- @property (nonatomic, strong) NSString *pathDong;
- @property (nonatomic, strong) KSTipsView *tipsView;
- @property (nonatomic, strong) ClassroomTimerManager *timeManager;
- @property (nonatomic, strong) UILabel *timeLabel;
- @property (nonatomic, strong) KSNormalAlertView *alertView;
- @end
- @implementation NewClassRoomViewController
- #pragma mark - Life cycle
- - (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
- self.zh_statusBarHidden = YES;
- // 切换到横屏
- AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
- delegate.allowAutoRotate = YES;
- [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
- [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
- [RCRTCEngine sharedInstance].statusReportDelegate = self;
- }
- - (void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
- if (_isPlay) {
- [self pause];
- [self removeAll];
- }
- // 竖屏
- AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
- delegate.allowAutoRotate = NO;
- [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait];
- [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
- self.zh_statusBarHidden = NO;
- [self hideAlertView];
- [self judgeShowAlert];
- }
- - (void)hideAlertView {
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- }
- - (void)judgeShowAlert {
- if (self.cancleAlert || self.hasShowAlert) {
- return;
- }
- if (![NSString isEmptyString:self.classEndTime]) {
- NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
- dateFormatter.timeZone = [NSTimeZone systemTimeZone];
- [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
- NSDate *endDate = [dateFormatter dateFromString:self.classEndTime];
- NSDate *currentDate = [NSDate date];
- NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:endDate];
- if (timeInterval >= 0) { // 课程结束弹出评价
- self.hasShowAlert = YES;
- [[NSNotificationCenter defaultCenter] postNotificationName:@"showEvaluateAlert" object:self.courseId];
- }
- }
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
-
- self.view.backgroundColor = HexRGB(0x35343e);
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(leaveRoomAction) name:@"classroomLogout" object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(quitRoomAction) name:@"classroomQuit" object:nil];
-
- // 切换到横屏
- AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
- delegate.allowAutoRotate = YES;
- [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
- [self addSubviews];
- [self bindDelegates];
- [self publishStream];
- [self renderMainContainerView];
- self.timeManager.isDisplay = YES;
- [[RTCService sharedInstance] useSpeaker:YES];
- UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
- [self.view addGestureRecognizer:tapGes];
- tapGes.delegate = self;
- [self showRoleHud];
- self.isQuitRoom = NO;
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundQuitRoomAction) name:@"backgroundQuit" object:nil];
- }
- - (void)backgroundQuitRoomAction {
- self.isQuitRoom = YES;
- self.isSwitchLine = NO;
- [[LoginHelper sharedInstance] logout:^{
- } error:^(RCRTCCode code) {
- }];
-
- self.cancleAlert = YES;
- }
- #pragma mark ----- 账号异地登陆
- - (void)leaveRoomAction {
- [self showMessage:@"该账号在其他设备上登录"];
- self.isQuitRoom = YES;
- self.isSwitchLine = NO;
- [[LoginHelper sharedInstance] logout:^{
- } error:^(RCRTCCode code) {
- }];
- self.cancleAlert = YES;
- [[NSNotificationCenter defaultCenter] postNotificationName:@"backLoginView" object:nil];
- }
- - (void)quitRoomAction {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- self.isQuitRoom = YES;
- self.isSwitchLine = NO;
- [[LoginHelper sharedInstance] logout:^{
- } error:^(RCRTCCode code) {
- }];
- self.cancleAlert = YES;
- [[NSNotificationCenter defaultCenter] postNotificationName:@"showOtherLoginAlert" object:nil];
- });
- }
- // 提示信息
- - (void)showMessage:(NSString *)message {
- MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
- [self.view bringSubviewToFront:hud];
- hud.removeFromSuperViewOnHide =YES;
- hud.mode = MBProgressHUDModeText;
- hud.label.text = message;
- hud.minSize = CGSizeMake(132.f, 60.0f);
- hud.label.textColor = [UIColor whiteColor];
- hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
- hud.bezelView.backgroundColor = [UIColor colorWithHexString:@"#000000" alpha:0.8];
- [hud hideAnimated:YES afterDelay:2];
- }
- - (void)tapGesture: (UITapGestureRecognizer *)tapGesture {
- self.titleView.isDisplay = YES;
- self.timeManager.isDisplay = YES;
- [self hideChatAreaView];
- }
- -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
- if ([touch.view isDescendantOfView:self.chatAreaView] || [touch.view isDescendantOfView:self.videoListView]) {
- return NO;
- }
- if ([touch.view isDescendantOfView:self.wBoardCtrl.wbView] || [touch.view isDescendantOfView:self.containerView]) {
- [self tapGesture:(UITapGestureRecognizer *)gestureRecognizer];
- }
- return YES;
- }
- - (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- }
- - (void)showRoleHud {
- Role role = [ClassroomService sharedService].currentRoom.currentMember.role;
- if(role == RoleAudience) {
- [self.tipsView showTipsMessage:@"你当前身份是旁听人,其他人看/听不见你" inView:self.view];
-
- [self performSelector:@selector(showOnlyYouHUD) withObject:nil afterDelay:5.0f];
- }else{
- [self showOnlyYouHUD];
- }
- }
- - (void)showOnlyYouHUD {
- if ([ClassroomService sharedService].currentRoom.memberList.count == 1) {
- [self.tipsView showTipsMessage:@"当前课堂只有你一人,你可以等待或离开" inView:self.view];
- }
- }
- #pragma mark --- ClassTitleViewDelegate
- - (void)classTitleView:(UIButton *)button didTapAtTag:(ClassTitleViewActionTag)tag {
- [self.chatAreaView.inputBarControl setInputBarStatus:InputBarControlStatusDefault];
- switch (tag) {
- case ClassTitleViewActionTagMicrophone:
- {
- if (button.selected == NO) {
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认关闭麦克风吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
- } confirm:^{
- button.selected = YES;
- [[ClassroomService sharedService] enableDevice:NO withType:DeviceTypeMicrophone];
- }];
- }
- else {
- button.selected = NO;
- [[ClassroomService sharedService] enableDevice:YES withType:DeviceTypeMicrophone];
- }
- }
- break;
- case ClassTitleViewActionTagCamera:
- {
- if (button.selected == NO) {
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认关闭摄像头吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
- } confirm:^{
- button.selected = YES;
- [[ClassroomService sharedService] enableDevice:NO withType:DeviceTypeCamera];
- }];
- }
- else {
- button.selected = NO;
- [[ClassroomService sharedService] enableDevice:YES withType:DeviceTypeCamera];
- }
- }
- break;
- case ClassTitleViewActionTagSwitchLine: // 切换线路
- {
- MJWeakSelf;
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认切换线路吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
- } confirm:^{
- [weakSelf restartRoom];
- }];
- }
- break;
- case ClassTitleViewActionTagSwitchCamera:
- {
- [[RTCService sharedInstance] switchCamera];
- }
- break;
- case ClassTitleViewActionTagChat:
- {
- button.selected = !button.selected;
- button.selected ? [self showChatAreaView] : [self hideChatAreaView];
- self.isChat = button.selected;
- }
- break;
- case ClassTitleViewActionTagHangup:
- {
- MJWeakSelf;
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认退出课堂吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
- } confirm:^{
- weakSelf.isQuitRoom = YES;
- SealClassLog(@"ActionTagHangup");
- weakSelf.hud = [self createProgressHUD];
- [[LoginHelper sharedInstance] logout:^{
- } error:^(RCRTCCode code) {
- }];
- }];
- }
- break;
- default:
- break;
- }
- }
- #pragma mark ---- ClassroomTimerManager delegate
- - (void)formatTime:(NSString *)timeString {
- self.timeLabel.text = timeString;
- }
- - (void)timeLabelAddAnimation:(CABasicAnimation *)animation {
- self.timeLabel.textColor = HexRGB(0x2DC7AA);
- [self.timeLabel.layer addAnimation:animation forKey:nil];
- }
- - (void)hiddenTitleView {
- [self.titleView hiddenView];
- }
- - (void)quitClassroomNotifer {
- // 课程结束后15分钟自动退出房间 连堂课不退出(对内)
- if (self.isQuitRoom == NO) {
- self.isQuitRoom = YES;
- // 退出房间
- [self.tipsView showTipsMessage:@"课程已结束" inView:self.view];
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // 课程结束退出房间
- SealClassLog(@"ClassEnd!");
- self.hud = [self createProgressHUD];
- [[LoginHelper sharedInstance] logout:^{
- } error:^(RCRTCCode code) {
- }];
- });
- }
- }
- #pragma mark ------ 退出RTC房间并重新加入
- - (void)restartRoom {
- self.isSwitchLine = YES;
- if (_isPlay) {
- [self pause];
- [self removeAll];
- }
- self.hud = [self createProgressHUD];
- [self.wBoardCtrl.wbView leaveRoom];
- [[LoginHelper sharedInstance] logout:^{
- } error:^(RCRTCCode code) {
- }];
- }
- - (void)reJoinRTCRoom {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- NSString *roomId = [LoginHelper sharedInstance].roomId;
- NSString *userName = [LoginHelper sharedInstance].userName;
- [[ClassroomService sharedService] joinClassroom:roomId userName:userName isAudience:NO success:^(Classroom * _Nonnull classroom) {
- self.isSwitchLine = NO;
- [self restartRTCConnection];
- } error:^(ErrorCode errorCode, NSString * _Nonnull message) {
- [self.tipsView showTipsMessage:@"切换线路失败!" inView:self.view];
- [self.hud hideAnimated:YES];
- self.isSwitchLine = NO;
- }];
- });
- }
- - (void)restartRTCConnection {
-
- [[RTCService sharedInstance] joinRongRTCRoom:[ClassroomService sharedService].currentRoom.roomId success:^(RCRTCRoom * _Nonnull room) {
- [self.hud hideAnimated:YES];
- [self.tipsView showTipsMessage:@"切换线路成功" inView:self.view];
- // 重置UI 重新发布音视频流
- [LocalRenderManager shareInstance].hadRenderMainView = NO;
- self.timeManager.isDisplay = YES;
- [self publishStream];
- [self bindDelegates];
- [self.titleView refreshTitleView];
- [self.containerView refreshToolView];
- [self.hud hideAnimated:YES];
- [RCRTCEngine sharedInstance].statusReportDelegate = self;
- [[RTCService sharedInstance] useSpeaker:YES];
- [self renderMainContainerView];
- [self.videoListView reloadVideoList];
-
- } error:^(RCRTCCode code) {
- [self.hud hideAnimated:YES];
- [self.tipsView showTipsMessage:@"切换线路失败!" inView:self.view];
- }];
- }
- #pragma mark - ClassroomDelegate
- - (void)roomDidLeave {
- NSLog(@"roomDidLeave");
- if (_isSwitchLine) {
- if ([RTCService sharedInstance].rtcRoom) {
- [[KSRemoteUserManager shareInstance] removeAllUser];
- [[RTCService sharedInstance] leaveRongRTCRoom:[ClassroomService sharedService].currentRoom.roomId success:^{
- [self reJoinRTCRoom];
- } error:^(RCRTCCode code) {
- [self.hud hideAnimated:YES];
- [self.tipsView showTipsMessage:@"切换线路失败!" inView:self.view];
- self.isSwitchLine = NO;
- }];
- }
- }
- else {
- [[KSRemoteUserManager shareInstance] removeAllUser];
- [[RCIMClient sharedRCIMClient] clearMessagesUnreadStatus:ConversationType_GROUP targetId:[ClassroomService sharedService].currentRoom.roomId];
- int unreadMsgCount = [[RCIMClient sharedRCIMClient] getUnreadCount:@[
- @(ConversationType_PRIVATE), @(ConversationType_APPSERVICE), @(ConversationType_GROUP),@(ConversationType_SYSTEM)
- ]];
- if (unreadMsgCount >= 1) {
- [(KSTabBarViewController *)self.tabBarController noteNewsWithIndex:2 count:unreadMsgCount];
- } else {
- [(KSTabBarViewController *)self.tabBarController clearNewsWithIndex:2];
- }
- // 关闭白板连接
- [self.wBoardCtrl.wbView.sdk setCommonCallbackDelegate:nil];
- [self.wBoardCtrl.wbView.room disconnect:^{
- }];
- if ([RTCService sharedInstance].rtcRoom) {
- [[RTCService sharedInstance] leaveRongRTCRoom:[ClassroomService sharedService].currentRoom.roomId success:^{
- } error:^(RCRTCCode code) {
- }];
- }
- [self dismissClassroom];
- }
- }
- - (void)dismissClassroom {
- [self.hud hideAnimated:YES];
- [ClassroomService sharedService].currentRoom = nil;
- [self dismissViewControllerAnimated:NO completion:^{
- [self.timeManager stopDurationTimer];
- [self.timeLabel.layer removeAllAnimations];
- }];
- }
- - (void)memberDidJoin:(RoomMember *)member {
- NSLog(@"memberDidJoin %@",member);
- if(member.role != RoleAudience) {
- [self.videoListView reloadVideoList];
- }
- if (member.role == RoleTeacher) {
- [self.tipsView showTipsMessage:[NSString stringWithFormat:@"老师%@已经进入教室", member.name] inView:self.view];
- if (_isPlay) {
- [self pause];
- [self removeAll];
- [[RTCService sharedInstance] playAllRemoteUserAudioStream];
- }
- if ([ClassroomService sharedService].currentRoom.isPlaySong) {
- [[RCRTCAudioMixer sharedInstance] stop];
- [ClassroomService sharedService].currentRoom.isPlaySong = NO;
- [[RTCService sharedInstance] playAllRemoteUserAudioStream];
- }
- }
- }
- - (void)memberDidLeave:(RoomMember *)member {
- NSLog(@"memberDidLeave %@",member);
- // 此处移除
- [[KSRemoteUserManager shareInstance] removeUser:member.userId];
-
- if(member.role != RoleAudience) {
- [self.videoListView reloadVideoList];
- }
- if (member.role == RoleTeacher) {
- // 老师退出房间后,学生如果在节拍器播放时停止节拍器
- if (_isPlay) {
- [self pause];
- [self removeAll];
- [[RTCService sharedInstance] playAllRemoteUserAudioStream];
- }
- if ([ClassroomService sharedService].currentRoom.isPlaySong) {
- [[RCRTCAudioMixer sharedInstance] stop];
- [ClassroomService sharedService].currentRoom.isPlaySong = NO;
- [[RTCService sharedInstance] playAllRemoteUserAudioStream];
- }
- }
- }
- - (void)memberDidKick:(RoomMember *)member {
- NSLog(@"memberDidKick %@",member);
- [[KSRemoteUserManager shareInstance] removeUser:member.userId];
- if ([ClassroomService sharedService].currentRoom.currentMember.role == RoleTeacher) {
- [self.tipsView showTipsMessage:[NSString stringWithFormat:@"你已将%@移出课堂", member.name] inView:self.view];
- }
- [self.videoListView reloadVideoList];
- if (self.containerView.member.role == member.role) {
- [self.containerView cancelRenderView];
- }
- }
- - (void)errorDidOccur:(ErrorCode)code {
- NSLog(@"errorDidOccur %@",@(code));
- [self.hud hideAnimated:YES];
- if (code != ErrorCodeSuccess) {
- if (code == ErrorCodeOverMaxUserCount) {
- [self.tipsView showTipsMessage:@"超过人数限制" inView:self.view];
- }else {
- [self.tipsView showTipsMessage:@"操作失败,请稍后再试" inView:self.view];
- }
- }
- }
- - (void)deviceDidEnable:(BOOL)enable type:(DeviceType)type forUser:(RoomMember *)member operator:(nonnull NSString *)operatorId {
- NSLog(@"deviceDidEnable devicetype:%@ enable:%@ memeber:%@",@(type),@(enable),member);
- RoomMember *curMember = [ClassroomService sharedService].currentRoom.currentMember;
- NSString *hudMessage = @"";
- //只有老师和自己才有提示
- if (curMember.role == RoleTeacher && ![curMember.userId isEqualToString:operatorId]) {
- if (type == DeviceTypeCamera) {
- hudMessage = !enable ? [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetCameraClose", @"SealClass", nil),member.name] : [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetCameraOpen", @"SealClass", nil),member.name];
- } else if (type == DeviceTypeMicrophone) {
- hudMessage = !enable ? [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetMicorophoneClose", @"SealClass", nil),member.name] : [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetMicorophoneOpen", @"SealClass", nil),member.name];
- }
- else if (type == DeviceTypeMusicMode) {
- hudMessage = !enable ? [NSString stringWithFormat:@"%@的音乐模式已关闭",member.name] : [NSString stringWithFormat:@"%@的音乐模式已开启",member.name];
- }
- [self.tipsView showTipsMessage:hudMessage inView:self.view];
- } else {
- if ([curMember.userId isEqualToString:member.userId]) {
- if (type == DeviceTypeCamera) { // 摄像头
- // 非自己操作才需要做提示
- if(![curMember.userId isEqualToString:operatorId]){
- hudMessage = !enable ? @"老师已关闭你的摄像头" : NSLocalizedStringFromTable(@"CameraOpend", @"SealClass", nil);
- [self.tipsView showTipsMessage:hudMessage inView:self.view];
- }
-
- [self.containerView refreshToolView];
- [[RTCService sharedInstance] setCameraDisable:!enable];
- } else if (type == DeviceTypeMicrophone) { // 麦克风
- // 非自己操作才需要做提示
- if(![curMember.userId isEqualToString:operatorId]){
- hudMessage = !enable ? @"老师已关闭你的麦克风" : NSLocalizedStringFromTable(@"MicorophoneOpend", @"SealClass", nil);
- [self.tipsView showTipsMessage:hudMessage inView:self.view];
- }
-
- [self.containerView refreshToolView];
- [[RTCService sharedInstance] setMicrophoneDisable:!enable];
- }
- else if (type == DeviceTypeMusicMode) {
- // 非自己操作才需要做提示
- if(![curMember.userId isEqualToString:operatorId]) {
- // hudMessage = !enable ? NSLocalizedStringFromTable(@"老师关闭了你的音乐模式", @"SealClass", nil) : NSLocalizedStringFromTable(@"音乐模式已打开", @"SealClass", nil);
- // [self.tipsView showTipsMessage:hudMessage inView:self.view];
- }
-
- [self.containerView refreshToolView];
- [self changeModeAction:enable];
- }
- else if (type == DeviceTypeHandup) {
- // if ([curMember.userId isEqualToString:operatorId]) {
- // if (enable) {
- // [self.tipsView showTipsMessage:@"您已举手" inView:self.view];
- // }
- // }
- }
- else if (type == DeviceTypeExamSong) {
- if (enable) {
- // 播放伴奏
- [self playSongAction];
- }
- else {
- // 停止伴奏
- [[RCRTCAudioMixer sharedInstance] stop];
- [self playSongEnable:NO];
- }
-
- }
- else if (type == DeviceTypeAccompany || type == DeviceTypeMusicScore) { // 原音或伴奏
- if (enable) {
- // 检测是否存在该曲目
- RoomMember *member = [ClassroomService sharedService].currentRoom.currentMember;
- NSString *songId = [NSString stringWithFormat:@"%d", member.musicScoreAccompanimentId];
- NSString *songUrl = member.url;
- NSString *accompnayUrl = member.mp3Url;
- // 播放伴奏
- [self checkAccompnayDownloadNotifer:songUrl accompanyUrl:accompnayUrl songId:songId needPlay:YES];
- }
- else {
- // 停止伴奏
- [[RCRTCAudioMixer sharedInstance] stop];
- [self playSongEnable:NO];
- }
- }
-
- }
- }
- if ([ClassroomService sharedService].currentRoom.currentDisplayType != DisplayWhiteboard && type == DeviceTypeCamera) {
- // 延迟调用,防止刷新出现灰屏
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self renderMainContainerView];
- });
- }
- [self.titleView refreshTitleView];
- [self.containerView refreshToolView];
- }
- - (void)playSongAction {
-
- if ([ClassroomService sharedService].currentRoom.isPlaySong || [ClassroomService sharedService].currentRoom.isPlayBeat) {
- // 停止伴奏
- [[RCRTCAudioMixer sharedInstance] stop];
- }
-
- // 获取路径 播放伴奏
- RoomMember *member = [ClassroomService sharedService].currentRoom.currentMember;
- NSString *songId = [NSString stringWithFormat:@"%d", member.musicScoreAccompanimentId];
-
- NSDictionary *fileDic = UserDefaultObjectForKey(KSExamSongFileKey);
- NSDictionary *storeDic = [fileDic dictionaryValueForKey:songId];
- NSString *fileName = nil;
- NSString *filePath = nil;
- if (member.accompanimentPlayStatus) {
- fileName = [[storeDic dictionaryValueForKey:@"accompany"] stringValueForKey:@"fileName"];
- filePath = [self getFilePathWithName:fileName];
- }
- else if (member.playStatus) {
- fileName = [[storeDic dictionaryValueForKey:@"music"] stringValueForKey:@"fileName"];
- filePath = [self getFilePathWithName:fileName];
- }
- // 判断文件是否存在
- NSLog(@"-------songid %@", songId);
- NSLog(@"------filePath %@", filePath);
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if ([fileManager fileExistsAtPath:filePath] && ![NSString isEmptyString:fileName]) {
- NSInteger soundVolome = [ClassroomService sharedService].currentRoom.soundVolume;
- NSLog(@"soundVolome------%zd",soundVolome);
- [[RCRTCAudioMixer sharedInstance] stop];
- @try {
- // 混音伴奏音量
- [RCRTCAudioMixer sharedInstance].mixingVolume = soundVolome;
- [RCRTCAudioMixer sharedInstance].playingVolume = soundVolome;
- [[RCRTCAudioMixer sharedInstance] startMixingWithURL:[NSURL fileURLWithPath:filePath] playback:YES mixerMode:RCRTCMixerModeMixing loopCount:NSUIntegerMax];
- } @catch (NSException *exception) {
- NSLog(@"%@", exception);
- } @finally {
-
- }
-
- }
- [self playSongEnable:YES];
- }
- - (void)playSongEnable:(BOOL)enable {
- NSString *teacherId = [ClassroomService sharedService].currentRoom.teacher.userId;
- if (enable) {
- if ([ClassroomService sharedService].currentRoom.isPlayBeat) {
- [self pause];
- [self removeAll];
- }
- [ClassroomService sharedService].currentRoom.isPlaySong = YES;
- [[RTCService sharedInstance] unPlayRemoteUserAudioStreamExcept:teacherId];
- }
- else {
- [ClassroomService sharedService].currentRoom.isPlaySong = NO;
- [[RTCService sharedInstance] playAllRemoteUserAudioStream];
- }
- }
- // 请求用户打开设备,助教关闭用户设备没有回调。
- - (void)deviceDidInviteEnable:(DeviceType)type ticket:(NSString *)ticket {
- NSLog(@"deviceDidInviteEnable devicetype:%@",@(type));
- if (type == DeviceTypeCamera) {
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"老师邀请你打开摄像头" leftTitle:@"拒绝" rightTitle:@"同意" cancel:^{
- [[ClassroomService sharedService] rejectEnableDevice:type];
- } confirm:^{
- [[ClassroomService sharedService] approveEnableDevice:type];
- }];
- }else if (type == DeviceTypeMicrophone) {
- [self.tipsView showTipsMessage:@"老师开启了你的麦克风" inView:self.view];
- [[ClassroomService sharedService] approveEnableDevice:type];
- }
- else if (type == DeviceTypeExamSong) { // 接口自动处理同意
- }
- else if (type == DeviceTypeAccompany || type == DeviceTypeMusicScore) { // 伴奏接口自动处理成同意
-
- }
- else {
- [[ClassroomService sharedService] approveEnableDevice:type];
- }
- }
- //只有助教能收到这个回调,可以不在这里处理文字,因为设备的回调还会走
- - (void)deviceInviteEnableDidApprove:(RoomMember *)member type:(DeviceType)type {
- NSLog(@"deviceInviteEnableDidApprove devicetype:%@ member:%@ ",@(type),member);
- }
- //拒绝只有助教能收到这个回调,且只走这个回调
- - (void)deviceInviteEnableDidReject:(RoomMember *)member type:(DeviceType)type {
- NSLog(@"deviceInviteEnableDidReject devicetype:%@ member:%@ ",@(type),member);
- [self.tipsView showTipsMessage:[NSString stringWithFormat:@"%@拒绝了你的邀请",member.name] inView:self.view];
- }
- #pragma mark ----- 伴奏下载
- - (void)accompanyDownloadCallback:(NSString *)songUrl accompanyUrl:(NSString *)accompnayUrl songId:(NSString *)songId {
- [self checkAccompnayDownloadNotifer:songUrl accompanyUrl:accompnayUrl songId:songId needPlay:NO];
- }
- - (void)checkAccompnayDownloadNotifer:(NSString *)songUrl accompanyUrl:(NSString *)accompnayUrl songId:(NSString *)songId needPlay:(BOOL)isNeedPlay {
-
- if ([self checkSongHasSave:songId songUrl:songUrl accompanyUrl:accompnayUrl]) { // 判断文件是否已下载
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
- if (isNeedPlay) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self playSongAction];
- });
- }
- }
- else { // 如果不存在则去下载
- if (![NSString isEmptyString:songUrl]) {
- [self downloadSongAction:songUrl songId:songId isAccompany:NO success:^{
-
- if (![NSString isEmptyString:accompnayUrl]) {
-
- [self downloadSongAction:accompnayUrl songId:songId isAccompany:YES success:^{
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
- if (isNeedPlay) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self playSongAction];
- });
- }
- } faliure:^{
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
- }];
- }
- else {
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
- if (isNeedPlay) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self playSongAction];
- });
- }
- }
- } faliure:^{
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
- }];
- }
- else if (![NSString isEmptyString:accompnayUrl]){
-
- [self downloadSongAction:accompnayUrl songId:songId isAccompany:YES success:^{
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
- if (isNeedPlay) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self playSongAction];
- });
- }
- } faliure:^{
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
- }];
- }
- else { // 原声和伴奏均为空
- [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
- }
- }
- }
- - (void)downloadSongAction:(NSString *)url songId:(NSString *)songId isAccompany:(BOOL)isAccompany success:(void(^)(void))success faliure:(void(^)(void))faliure {
- [KSNetworkingManager downloadFileRequestWithFileUrl:url progress:^(int64_t bytesRead, int64_t totalBytes) {
- NSLog(@"%.2f", bytesRead *1.0 / totalBytes);
- } success:^(NSURL * _Nonnull fileUrl) {
- if ([self saveUrlWithFilePath:fileUrl songUrl:url songId:songId isAccompany:isAccompany]) {
- success();
- }
- else {
- faliure();
- }
- } faliure:^(NSError * _Nonnull error) {
- faliure();
- }];
- }
- - (BOOL)saveUrlWithFilePath:(NSURL *)fileUrl songUrl:(NSString *)songUrl songId:(NSString *)songId isAccompany:(BOOL)isAccompany {
- NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
- NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
- // 先创建子目录
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if (![fileManager fileExistsAtPath:filePath]) {
- [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
- }else{
- NSLog(@"已创建文件夹");
- }
- NSString *fileName = [[songUrl componentsSeparatedByString:@"/"] lastObject];
- NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
- BOOL success = [sourceData writeToFile:tempPath atomically:NO];
- if (success) {
- NSDictionary *fileDic = UserDefaultObjectForKey(KSExamSongFileKey); // 只能存NSDictionary
- NSMutableDictionary *storeDic = [NSMutableDictionary dictionary];
- if (!fileDic) {
- fileDic = [NSMutableDictionary dictionary];
- }
- else {
- storeDic = [NSMutableDictionary dictionaryWithDictionary:[fileDic dictionaryValueForKey:songId]];
- }
-
- NSDictionary *parm = @{@"url" : songUrl,
- @"fileName" : fileName};
- NSString *keyString = isAccompany ? @"accompany" :@"music";
- [storeDic setValue:parm forKey:keyString];
-
- NSMutableDictionary *saveDic = [NSMutableDictionary dictionaryWithDictionary:fileDic];
- [saveDic setValue:storeDic forKey:songId];
-
- UserDefaultSetObjectForKey(saveDic, KSExamSongFileKey);
- }
- return success;
- }
- - (NSString *)getFilePathWithName:(NSString *)fileName {
- NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
- return [filePath stringByAppendingPathComponent:fileName];;
- }
- - (BOOL)checkSongHasSave:(NSString *)songId songUrl:(NSString *)songUrl accompanyUrl:(NSString *)accompanyUrl {
- BOOL hasSaveFile = NO;
- NSMutableDictionary *fileDic = UserDefaultObjectForKey(KSExamSongFileKey);
- NSDictionary *storeDic = [fileDic dictionaryValueForKey:songId];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if (![NSString isEmptyString:songUrl]) {
- NSString *fileName = [[storeDic dictionaryValueForKey:@"music"] stringValueForKey:@"fileName"];
- NSString *filePath = [self getFilePathWithName:fileName];
- if ([[fileDic allKeys] containsObject:songId] && [fileManager fileExistsAtPath:filePath] && ![NSString isEmptyString:fileName]) {
- hasSaveFile = YES;
- }
- else {
- hasSaveFile = NO;
- }
- }
- if (![NSString isEmptyString:accompanyUrl]) {
- NSString *fileName = [[storeDic dictionaryValueForKey:@"accompany"] stringValueForKey:@"fileName"];
- NSString *filePath = [self getFilePathWithName:fileName];
- if ([[fileDic allKeys] containsObject:songId] && [fileManager fileExistsAtPath:filePath] && ![NSString isEmptyString:fileName]) {
- hasSaveFile = YES;
- }
- else {
- hasSaveFile = NO;
- }
- }
- return hasSaveFile;
- }
- #pragma mark - private method
- - (void)bindDelegates {
- self.titleView.delegate = self;
- [[RTCService sharedInstance] setRTCRoomDelegate:self];
- [RTCService sharedInstance].delegate = self;
- [ClassroomService sharedService].classroomDelegate = self;
- }
- - (void)addSubviews {
- [self.view addSubview:self.containerView];
- [self.view addSubview:self.timeLabel];
- [self.containerView bringSubviewToFront:self.timeLabel];
- [self.view addSubview:self.titleView];
- [self wBoardCtrl];
- [self chatAreaView];
- [self.view addSubview:self.videoListView];
- [self.view bringSubviewToFront:self.titleView];
- }
- - (void)showChatAreaView{
- [self.view addSubview:self.chatAreaView];
- }
- - (void)hideChatAreaView{
- [UIView animateWithDuration:0.2 animations:^{
- self.titleView.chatBtn.selected = NO;
- [self.chatAreaView removeFromSuperview];
- }];
- }
- - (CGRect)mainContainerViewFrame {
- CGFloat x = iPhoneXSafeTopMargin;
- CGFloat y = 0;
- CGFloat width = UIScreenWidth - x - iPhoneXSafeBottomMargin;
- CGFloat height = UIScreenHeight - y;
- return CGRectMake(x, y, width, height);
- }
- - (void)displayWhiteboard:(NSString *)boardId {
- CGRect frame = [self getWBoardFrame];
- [self.wBoardCtrl loadWBoardWithFrame:frame name:boardId];
- for (UIView * view in self.view.subviews) {
- if ([view isKindOfClass:[ChatAreaView class]]) {
- [self.view bringSubviewToFront:view];
- }
- }
- [self.view bringSubviewToFront:self.timeLabel];
- }
- - (CGRect)getWBoardFrame {
- CGRect mainVideoFrame = self.containerView.currentVideoFrame;
- mainVideoFrame.origin.x += iPhoneXSafeTopMargin;
- mainVideoFrame.origin.y = 10;
- return mainVideoFrame;
- }
- - (void)publishStream {
- if ([ClassroomService sharedService].currentRoom.currentMember.role != RoleAudience) {
- [[RTCService sharedInstance] publishLocalUserDefaultAVStream];
- }
- }
- - (void)renderMainContainerView {
- self.titleView.onShowLabel.hidden = YES;
- RoomMember *teacher = [ClassroomService sharedService].currentRoom.teacher;
- if ([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTypeStudent) {
- // 将之前订阅的流切换成小流
- [self changeLastDisplayToTinyStream];
- // 有打开的屏幕
- for (RoomMember *displayMember in [ClassroomService sharedService].currentRoom.memberList) {
- if ([displayMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentDisplayURI]) {
- if ([displayMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentMember.userId]) {
- self.titleView.onShowLabel.hidden = NO;
- // 重置举手状态
- [ClassroomService sharedService].currentRoom.currentMember.handUpOn = NO;
- [self.containerView refreshToolView];
- }
- [self.wBoardCtrl hideBoard];
- [self.containerView containerViewRenderView:displayMember];
- [self.videoListView showTeacherPrompt:NO];
- break;
- }
- }
-
- }
- else {
- // 将之前订阅的流切换成小流
- [self changeLastDisplayToTinyStream];
-
- if (([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTeacher && teacher.cameraEnable)) {
- NSLog(@"%@",[ClassroomService sharedService].currentRoom.currentDisplayURI);
- [self.wBoardCtrl hideBoard];
- [self.containerView containerViewRenderView:[ClassroomService sharedService].currentRoom.teacher];
- [self.videoListView showTeacherPrompt:YES];
- } else if ([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayWhiteboard) {
- [self.containerView cancelRenderView];
- [self displayWhiteboard:[ClassroomService sharedService].currentRoom.currentDisplayURI];
- [self.videoListView showTeacherPrompt:NO];
- } else if (([ClassroomService sharedService].currentRoom.currentDisplayType == DisplaySharedScreen)) {
- [self.wBoardCtrl hideBoard];
- [[RTCService sharedInstance] renderUserSharedScreenOnView:self.containerView.videoView forUser:[ClassroomService sharedService].currentRoom.currentDisplayURI];
- // 分享视频
- [self.videoListView showTeacherPrompt:NO];
- } else {
- [self.wBoardCtrl hideBoard];
- [self.containerView cancelRenderView];
- }
- }
- }
- - (void)changeLastDisplayToTinyStream {
- RoomMember *lastMember = self.containerView.member;
- if (![NSString isEmptyString:lastMember.userId] && ![lastMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentDisplayURI] && ![lastMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentMember.userId]) {
- [[RTCService sharedInstance] exchangeRemoteUserAVStreamToTinySteam:lastMember.userId callback:^{
-
- }];
- }
- }
- - (void)whiteboardDidDisplay:(NSString *)boardId {
- NSLog(@"whiteboardDidDisplay %@ ",boardId);
- [self renderMainContainerView];
- }
- - (void)sharedScreenDidDisplay:(NSString *)userId {
- NSLog(@"sharedScreenDidDisplay %@ ",userId);
- [self renderMainContainerView];
- }
- - (void)studentDidDisplay:(NSString *)studentId {
- NSLog(@"studentDidDisplay %@", studentId);
- [self renderMainContainerView];
- }
- - (void)teacherDidDisplay {
- NSLog(@"teacherDidDisplay %@ ",[ClassroomService sharedService].currentRoom.teacher.userId);
- [self renderMainContainerView];
- }
- - (void)noneDidDisplay {
- NSLog(@"noneDidDisplay");
- [self renderMainContainerView];
- }
- - (void)checkNodePlayAndSongDownload {
- RoomMember *currentMember = [ClassroomService sharedService].currentRoom.currentMember;
- if (currentMember.enable) {
- // 延迟播放
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self playNode:currentMember.enable rate:currentMember.rate beatType:currentMember.customType volume:currentMember.volume];
- });
- }
- else {
-
- if (currentMember.musicScoreAccompanimentId != 0) { // 如果有记录当前播放曲目
- BOOL needPlay = currentMember.accompanimentPlayStatus || currentMember.playStatus;
- [self checkAccompnayDownloadNotifer:currentMember.url accompanyUrl:currentMember.mp3Url songId:[NSString stringWithFormat:@"%d", currentMember.musicScoreAccompanimentId] needPlay:needPlay];
- }
- }
- }
- // 播放节拍器的回调
- - (void)playNodeAction:(BOOL)enable userId:(NSString *)userId rate:(int)rate beatType:(BeatType)type volume:(NSInteger)volume {
- NSLog(@"playNodeAction userId:%@ enable:%@ beatType:%@ rate:%@ volume:%@",userId,@(enable),@(type), @(rate), @(volume));
- RoomMember *curMember = [ClassroomService sharedService].currentRoom.currentMember;
- if (curMember.role == RoleStudent) {
- if ([NSString isEmptyString:userId]) { // 操作全部
- [self playNode:enable rate:rate beatType:type volume:volume];
- }
- else {
- NSArray *userIdArray = [userId componentsSeparatedByString:@","];
- for (NSString *subUserId in userIdArray) {
- if ([curMember.userId isEqualToString:subUserId]) { // 操作个人
- [self playNode:enable rate:rate beatType:type volume:volume];
- break;
- }
- }
- }
- }
- }
- - (void)playNode:(BOOL)enable rate:(int)rate beatType:(BeatType)type volume:(NSInteger)volume {
- NSString *teacherId = [ClassroomService sharedService].currentRoom.teacher.userId;
- if (enable) {
- if ([ClassroomService sharedService].currentRoom.isPlaySong) { // 停止当前伴奏播放
- [ClassroomService sharedService].currentRoom.isPlaySong = NO;
- [[RCRTCAudioMixer sharedInstance] stop];
- }
- [self setMetronomeStat:type];
- [self setRate:rate];
- self.volume = volume;
- [self.videoListView updateBeatNode:type beat:rate];
- [self play];
-
- [[RTCService sharedInstance] unPlayRemoteUserAudioStreamExcept:teacherId];
- }
- else {
- [self pause];
- [self removeAll];
- [[RTCService sharedInstance] playAllRemoteUserAudioStream];
- }
- }
- #pragma mark --- RTCServiceDelegate 自己发布流成功回调
- - (void)successPublishLocalStream {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // 检测是否需要通知
- [self notiferTeacherRefreshSongStatus];
-
- [self checkNodePlayAndSongDownload];
- });
- }
- - (void)notiferTeacherRefreshSongStatus {
- NSArray *songArray = [ClassroomService sharedService].currentRoom.currentMember.songMessage;
- if (songArray.count) {
- [[ClassroomService sharedService] adjustAccompanySongNotifer:nil status:YES];
- }
- }
- #pragma mark - RongRTCRoomDelegate
- - (void)didPublishStreams:(NSArray <RCRTCInputStream *>*)streams {
-
- NSString *displayUserId;
- if (([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTeacher)) {
- displayUserId = [ClassroomService sharedService].currentRoom.teacher.userId;
- }
- else if ([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTypeStudent) {
- displayUserId = [ClassroomService sharedService].currentRoom.currentDisplayURI;
- }
- // 当用户非正常退出的时候,如果再次进入需要重新订阅流
- RCRTCInputStream *firstStream = [streams lastObject];
- if ([[KSRemoteUserManager shareInstance] isContentUser:firstStream.userId]) {
- [[KSRemoteUserManager shareInstance] removeUser:firstStream.userId];
- }
-
- for (RCRTCInputStream *stream in streams) {
- if (stream.mediaType == RTCMediaTypeVideo) {
- if ([stream.userId isEqualToString:displayUserId]) {
- [self renderMainContainerView];
- }
- [self.videoListView updateUserVideo:stream.userId];
- }
- }
- }
- - (void)didConnectToStream:(RCRTCInputStream *)stream {
- if (stream.streamId && stream.streamId.length > 0) {
- NSLog(@"didConnectToStream userId:%@ streamID:%@",stream.userId,stream.streamId);
- }
- }
- #pragma mark ----- ClassVideoListViewDelegate
- - (void)videoListView:(ClassVideoListView *)view didTap:(RoomMember *)member {
- self.titleView.isDisplay = YES;
- self.timeManager.isDisplay = YES;
- /*
- // 将视频投到主屏幕上
- NSString *currentDisplayUserId = member.userId;
- NSString *lastDisplayUserId = [ClassroomService sharedService].currentRoom.currentDisplayURI;
-
- if([currentDisplayUserId isEqualToString:lastDisplayUserId]) {
- [self.videoListView updateUserVideo:lastDisplayUserId];
- return;
- }
- DisplayType type = member.role == RoleTeacher ? DisplayTeacher : DisplayTypeStudent;
- [ClassroomService sharedService].currentRoom.currentDisplayURI = currentDisplayUserId;
- [ClassroomService sharedService].currentRoom.currentDisplayType = type;
- // 如果之前有切换成大流的视频
- if (![NSString isEmptyString:lastDisplayUserId] && ![lastDisplayUserId isEqualToString:[ClassroomService sharedService].currentRoom.currentMemberId]) {
- [[RTCService sharedInstance] exchangeRemoteUserAVStreamToTinySteam:lastDisplayUserId];
- }
-
- if(![[ClassroomService sharedService].currentRoom.currentMemberId isEqualToString:member.userId]) {
- // 记录当前播放音频的学生
- [ClassroomService sharedService].currentRoom.currentPlayUserId = currentDisplayUserId;
- [[RTCService sharedInstance] exchangeRemoteUserAVStreamToNomalSteam:member.userId];
- }
- [self renderMainContainerView];
- */
- }
- #pragma mark - Getters & setters
- - (ClassroomTimerManager *)timeManager {
- if (!_timeManager) {
- _timeManager = [[ClassroomTimerManager alloc] initWithDelegate:self];
-
- _timeManager.autoClose = [ClassroomService sharedService].currentRoom.autoCloseFlag;
- // 统一15分钟关闭
- _timeManager.autoCloseNetworkRoomTime = [ClassroomService sharedService].currentRoom.autoCloseNetworkRoomTime;
- }
- return _timeManager;
- }
- - (UILabel *)timeLabel {
- if (!_timeLabel) {
- _timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(kScreenWidth - self.videoListView.width - 40 - TTimeLableWidth - iPhoneXSafeBottomMargin, 25, TTimeLableWidth, TTimeLableHeight)];
- _timeLabel.font = [UIFont systemFontOfSize:16];
- _timeLabel.textAlignment = NSTextAlignmentRight;
- _timeLabel.textColor = HexRGB(0xFF5500);
- }
- return _timeLabel;
- }
- - (KSTipsView *)tipsView {
- if (!_tipsView) {
- _tipsView = [[KSTipsView alloc] initWithFrame:CGRectZero];
- }
- return _tipsView;
- }
- - (ClassTitleView *)titleView {
- if (!_titleView) {
- _titleView = [[ClassTitleView alloc] initWithFrame:CGRectMake(iPhoneXSafeTopMargin, 0, kScreenWidth - iPhoneXSafeTopMargin - iPhoneXSafeBottomMargin, TitleViewHeight)];
- }
- return _titleView;
- }
- - (ClassVideoListView *)videoListView {
- if(!_videoListView) {
- CGFloat width = 0.0f;
- CGFloat height = 0.0f;
- height = kScreenHeight - 10;
- width = height / 3.0f * 4;
- if (width > kScreenWidth - 160) {
- width = kScreenWidth - 160;
- }
- _videoListView = [[ClassVideoListView alloc] initWithFrame:CGRectMake(width + iPhoneXSafeTopMargin + 5, 10, kScreenWidth - (width + 10 + iPhoneXSafeTopMargin) - iPhoneXSafeBottomMargin, height)];
- _videoListView.delegate = self;
- }
- return _videoListView;
- }
- - (ClassroomMainContainer *)containerView {
- if (!_containerView) {
- _containerView = [[ClassroomMainContainer alloc] initWithFrame:[self mainContainerViewFrame]];
- _containerView.delegate = self;
- }
- return _containerView;
- }
- - (ChatAreaView *)chatAreaView {
- if(!_chatAreaView) {
- _chatAreaView = [[ChatAreaView alloc] initWithFrame:CGRectMake(kScreenWidth - iPhoneXSafeBottomMargin - 300,0, 300, UIScreenHeight) conversationType:ConversationType_GROUP targetId:[ClassroomService sharedService].currentRoom.roomId];
- }
- return _chatAreaView;
- }
- - (KSWhiteboardControl *)wBoardCtrl {
- if (!_wBoardCtrl) {
- CGRect frame = [self getWBoardFrame];
- _wBoardCtrl = [[KSWhiteboardControl alloc] initWithDelegate:self viewFrame:frame];
- NSString *roomId = [ClassroomService sharedService].currentRoom.roomId;
- // if ([roomId containsString:@"S"] || [roomId containsString:@"I"]) {
- // roomId = [roomId substringFromIndex:1];
- // }
- _wBoardCtrl.roomId = roomId;
- _wBoardCtrl.isRatationImage = self.isRorationImage;
- [_wBoardCtrl moveToSuperView:self.view];
- }
- return _wBoardCtrl;
- }
- #pragma mark ---- 网络监测
- - (void)didReportStatusForm:(RCRTCStatusForm *)form {
- const CGFloat lossRateBase = 0.15;
- if (self.packetLossStore.count + form.sendStats.count > 10) {
- NSInteger diff = (self.packetLossStore.count + form.sendStats.count) - 10;
- [self.packetLossStore removeObjectsInRange:NSMakeRange(0,diff)];
- }
-
- for (RCRTCStreamStat* stat in form.sendStats) {
- [self.packetLossStore addObject:@(stat.packetLoss)];
- }
-
- float totalSum = 0;
- for (NSNumber* lossRate in self.packetLossStore) {
- totalSum += [lossRate floatValue];
- }
-
- if (totalSum > 0 && self.packetLossStore.count) {
- float avgLoss = totalSum / self.packetLossStore.count;
- [self showNetworkingStatus:avgLoss];
- if (avgLoss > lossRateBase) { // 网络质量不佳
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self.view addSubview:self.networkLabel];
- [self.view bringSubviewToFront:self.networkLabel];
- self.networkLabel.hidden = NO;
- self.networkLabel.text = @"当前通话连接质量不佳";
- });
- }
- else { // 正常
- dispatch_async(dispatch_get_main_queue(), ^{
- self.networkLabel.hidden = YES;
- self.networkLabel.text = @"";
- [self.networkLabel removeFromSuperview];
- });
- }
- }
- }
- - (void)showNetworkingStatus:(float)packageLose {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (packageLose < 0.05) {
- self.titleView.netStatus = NetWorkingStatus_Good;
- }
- else if (packageLose <= 0.1) {
- self.titleView.netStatus = NetWorkingStatus_Well;
- }
- else if (packageLose <= 0.15) {
- self.titleView.netStatus = NetWorkingStatus_Bad;
- }
- else {
- self.titleView.netStatus = NetWorkingStatus_Poor;
- }
- });
- }
- - (NSMutableArray<NSNumber*>*)packetLossStore {
- if (!_packetLossStore) {
- _packetLossStore = [[NSMutableArray alloc] initWithCapacity:10];
- }
- return _packetLossStore;
- }
- - (UILabel *)networkLabel {
- if (!_networkLabel) {
- _networkLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 200) / 2.0f, (kScreenHeight - 60) / 2.0f, 200, 60)];
- _networkLabel.backgroundColor = HexRGBAlpha(0x000000, 0.8f);
- _networkLabel.textColor = HexRGB(0xffffff);
- _networkLabel.textAlignment = NSTextAlignmentCenter;
- _networkLabel.font = [UIFont systemFontOfSize:14.0f];
- _networkLabel.layer.cornerRadius = 5.0f;
- }
- return _networkLabel;
- }
- #pragma mark ---- 修改声音模式
- - (void)changeModeAction:(BOOL)enable {
- if (enable) { // 音乐模式切换
- [[RCRTCEngine sharedInstance].defaultAudioStream setAudioQuality:RCRTCAudioQualityMusicHigh Scenario:RCRTCAudioScenarioMusicClassRoom];
- }
- else {
- [[RCRTCEngine sharedInstance].defaultAudioStream setAudioQuality:RCRTCAudioQualityMusicHigh Scenario:RCRTCAudioScenarioDefault];
- }
- }
- - (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- if (_timer) {
- [_timer invalidate];
- _timer = nil;
- }
- }
- - (void)setMetronomeStat:(KSMetronomeType)metronomeStat {
- _metronomeStat = metronomeStat;
-
- self.currentTotalNo = 0;
-
- if (_timer && _isPlay) {
-
- [self resetTimer];
- }
- }
- - (void)setRate:(int)rate{
-
- _rate = rate;
-
- CGFloat rateFloat = rate;
-
- self.timerLength = 1 /(rateFloat /60);
-
- if (self.lastRate == 0) {
-
- self.lastRate = _rate;
- }
-
- // 超过10间隔再重置定时器,设置最新间隔秒数
- if (abs(self.lastRate -_rate) >= 10 && _isPlay) {
-
- self.lastRate = _rate;
-
- [self resetTimer];
- }
-
- if (abs(self.lastRate -_rate) >= 10 && !_isPlay) {
-
- if (_timer) {
- [_timer invalidate];
- _timer = nil;
- }
- }
- }
- - (void)play{
-
- _isPlay = YES;
- [ClassroomService sharedService].currentRoom.isPlayBeat = YES;
- [self.timer setFireDate:[NSDate distantPast]];
- }
- - (void)pause{
- [[RCRTCAudioMixer sharedInstance] stop];
- [self.timer setFireDate:[NSDate distantFuture]];//暂停计时器
- _isPlay = NO;
- [ClassroomService sharedService].currentRoom.isPlayBeat = NO;
- }
- - (NSTimer *)timer{
-
- if (!_timer) {
-
- _timer = [NSTimer scheduledTimerWithTimeInterval:self.timerLength target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
- [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
- }
- return _timer;
- }
- - (void)timerAction {
-
- if (_metronomeStat ==KSMetronomeType1V4) {
- [self playAction:YES];
- }
- else {
- if (_metronomeStat == KSMetronomeType2V4 && self.currentTotalNo %2 == 0) {
- [self playAction:YES];
- }else if (_metronomeStat == KSMetronomeType3V4 && self.currentTotalNo %3 == 0){
- [self playAction:YES];
- }else if (_metronomeStat == KSMetronomeType4V4 && self.currentTotalNo %4 == 0){
- [self playAction:YES];
- }else if (_metronomeStat == KSMetronomeType6V8 && self.currentTotalNo %6 == 0){
- [self playAction:YES];
- }else{
- [self playAction:NO];
- }
- }
- [self.videoListView showBeatNode:self.currentTotalNo];
- self.currentTotalNo ++;
- }
- - (void)playAction:(BOOL)isDing {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSString *path = isDing ? self.pathDing : self.pathDong;
- [[RCRTCAudioMixer sharedInstance] stop];
- @try {
- [RCRTCAudioMixer sharedInstance].mixingVolume = self.volume;
- [RCRTCAudioMixer sharedInstance].playingVolume = self.volume;
- [[RCRTCAudioMixer sharedInstance] startMixingWithURL:[NSURL fileURLWithPath:path] playback:YES mixerMode:RCRTCMixerModeMixing loopCount:1];
- } @catch (NSException *exception) {
- NSLog(@"%@", exception);
- } @finally {
- }
-
- });
- }
- - (NSString *)pathDing {
- if (!_pathDing) {
- _pathDing = [[NSBundle mainBundle] pathForResource:@"tick" ofType:@"wav"];
- }
- return _pathDing;
- }
- - (NSString *)pathDong {
- if (!_pathDong) {
- _pathDong = [[NSBundle mainBundle] pathForResource:@"tock" ofType:@"wav"];
- }
- return _pathDong;
- }
- #pragma mark -- 重置定时器
- - (void)resetTimer {
-
- [_timer invalidate];
- _timer = nil;
- [self.timer setFireDate:[NSDate distantPast]];
- }
- - (void)removeAll {
-
- if (_timer) {
- [_timer invalidate];
- _timer = nil;
- }
- }
- #pragma mark --- KSWhiteboardControlDelegate
- - (void)didTurnPage:(NSInteger)pageNum {
-
- }
- - (void)whiteboardViewDidChangeZoomScale:(float)scale {
-
- }
- // 白板连接状态回调
- - (void)connectionWhiteboardStatus:(JOINROOMSTATUS)status {
- if (status == JOINROOMSTATUS_FAILED) {
- [self.tipsView showTipsMessage:@"白板连接失败,请检查您的网络" inView:self.view];
- }
- else if (status == JOINROOMSTATUS_PARAMERROR) {
- [self.tipsView showTipsMessage:@"获取白板参数错误" inView:self.view];
- }
- }
- #pragma mark -- mainContainer
- - (void)mainContainerOpertionMember:(RoomMember *)member tapButton:(UIButton *)button {
- // 🙋操作
- if (button.selected) { // 取消举手
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认取消举手吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
-
- } confirm:^{
- [[ClassroomService sharedService] enableDevice:NO withType:DeviceTypeHandup];
- }];
- }
- else {
- if (self.alertView && self.alertView.isShow) {
- [self.alertView dismissAlertView];
- }
- self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认举手吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
-
- } confirm:^{
- [[ClassroomService sharedService] enableDevice:YES withType:DeviceTypeHandup];
- }];
- }
-
- }
- #pragma mark ------ 创建MBProgress
- - (MBProgressHUD *)createProgressHUD {
- [MBProgressHUD hideHUD];
- MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
- hud.mode = MBProgressHUDModeIndeterminate;
- hud.contentColor = [UIColor whiteColor];
- hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
- hud.bezelView.backgroundColor = [UIColor colorWithHexString:@"#000000" alpha:0.8];
- [hud hideAnimated:YES afterDelay:60.0f]; // 自动关闭时间60s
- return hud;
- }
- /*
- #pragma mark - Navigation
- // In a storyboard-based application, you will often want to do a little preparation before navigation
- - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
- // Get the new view controller using [segue destinationViewController].
- // Pass the selected object to the new view controller.
- }
- */
- @end
|