客户端实现
更新时间: 2024/08/02 11:38:27
本文档为您展示智慧云课堂在互动大班课场景下的常见功能,您可以参考智慧云课堂方案实现互动大班课教学场景,也可以在此基础上实现业务功能的灵活扩展。
技术架构
如果示例项目中默认实现的 UI 不符合您的预期,您可以按需实现自己的用户界面,即只使用我们封装好的组件所提供的音视频能力,自行实现 UI 部分。
教育组件的架构如下图所示。
- EduUI:教育组件的UI配置,包括一对一教学、多人小班课、互动大班课、直播大班课等场景的 ViewController、View 以及 model 部分。
- EduLogic:基于云信 NERTC SDK、IM SDK 定制的,针对教育场景的逻辑实现,包括如下2个服务:
- NEEduRtcService 是音视频服务,提供可供 App 调用的音视频相关方法。
- NEEduIMService 是 IM 服务,提供可供 App 调用的即时通信、聊天室相关方法。
- NEWhiteBoard:云信的互动白板服务,提供可供 App 调用的互动白板相关方法。
业务流程
下图展示了启动智慧云课堂的基本流程:
当 App 客户端请求加入云课堂时,业务流程如下:
- App 客户端向 App 服务端申请场景配置创建。根据创建场景配置参数,查询房间信息,如果房间不存在,那么生成房间信息;如果房间已存在,提示已存在。
- 加入课堂,先检测 IM 是否已经登录,没登录需要登录,登录失败返回加入失败。向服务端发送加入课堂接口,成功后进入课程页面, 根据状态加入音视频房间。
- 请求快照,获取聊天室 ID,加入聊天室。
- 初始化组件。加入白板房间,白板服务器会进行IM账号鉴权。更新本地流,开始拉流。
客户端实现方法
步骤一 集成组件
-
新建 iOS 工程。
- 运行 XCode,单击 Create a new Xcode project。
- 选择 Single View App,单击 Next。
- 配置工程相关信息,单击 Next。
- 选择合适的工程本地路径,并单击 Create 创建工程。
-
集成 SDK。
-
复制示例项目中的
Modules
文件夹到项目目录下。 -
进入工程目录,创建
Podfile
文件。pod
pod init
-
添加 pod 本地依赖,复制以下代码到
Podfile
文件。pod
platform :ios, '10.0' target '工程名' do use_frameworks! pod 'EduLogic', :path => 'Modules/EduLogic/EduLogic.podspec' pod 'NEWhiteBoard', :path => 'Modules/NEWhiteBoard/NEWhiteBoard.podspec' pod 'NEScreenShareBroadcaster', '~> 0.1.0'
-
执行以下命令安装组件。
pod
pod install
-
-
添加摄像头和麦克风的权限。
智慧云课堂 SDK 正常工作需要摄像头、麦克风权限。
-
在工程中的
Info.list
文件中配置相关的权限信息。Privacy - Microphone Usage Description Privacy - Camera Usage Description
-
在工程的 Signing&Capabilities 添加 Background Modes,并勾选 Audio、Airplay、and Picture in Picture。
-
步骤二 初始化组件
在使用组件其他功能之前,首先需要通过 setupAppKey
方法完成组件初始化。
示例代码:
objective-cNEEduKitOptions *option = [[NEEduKitOptions alloc] init];
option.authorization = [KeyCenter authorization];
option.baseURL = [KeyCenter baseURL];
[[EduManager shared] setupAppKey:[KeyCenter appKey] options:option];
步骤三 学生端进入课堂
学生端通过 enterClassroom
加入已创建的课堂。
objective-cNEEduEnterRoomParam *room = [[NEEduEnterRoomParam alloc] init];
room.autoPublish = YES;
room.autoSubscribeVideo = YES;
room.autoSubscribeAudio = YES;
room.roomUuid = resRoom.roomUuid;
room.roomName = resRoom.roomName;
room.sceneType = self.lessonType;
room.role = NEEduRoleTypeStudent;
room.userName = @"";
__weak typeof(self)weakSelf = self;
[[EduManager shared] enterClassroom:room success:^(NEEduRoomProfile * _Nonnull roomProfile) {
// 进入成功处理
} failure:^(NSError * _Nonnull error) {
// 进入失败处理
}
步骤四 在线聊天室
在多人小班课、互动大班课、直播大班课中,可以通过聊天室实现消息收发,学生和学生、学生和老师之间通过聊天室发送文字或图片消息,教师端可以禁言或解禁聊天室。
师生调用 enterChatRoomWithParam:success:failed:
加入聊天室,并通过 sendChatroomTextMessage:error:和 sendChatroomImageMessage:error:
发送文字和图片消息。
示例代码:
- (void)addChatroom:(NEEduHttpRoom *)room {
__weak typeof(self) weakSelf = self;
NEEduChatRoomParam *chatparam = [[NEEduChatRoomParam alloc] init];
chatparam.chatRoomID = room.properties.chatRoom.chatRoomId;
chatparam.nickname = [NSString stringWithFormat:@"%@(学生)",[NEEduManager shared].localUser.userName];
[[NEEduManager shared].imService enterChatRoomWithParam:chatparam success:^(NEEduChatRoomResponse * _Nonnull response) {
__strong typeof(self)strongSelf = weakSelf;
[strongSelf addChatMenue];
} failed:^(NSError * _Nonnull error) {
__strong typeof(self)strongSelf = weakSelf;
[strongSelf.view makeToast:error.localizedDescription];
}];
}
步骤五 录制回放
在线教育场景中,可实现上课过程中老师和学生的音视频、白板、屏幕共享录制,还原真实上课场景,供学生和教师回放复习。目前所有课堂模式都支持录制回放。录制功能包含在云端录制服务中,用户端只需要接入Recordplay 回放组件,回放功能依赖的代码包含在组件里面。
教师开始上课时,服务器会自动开始录制。课程结束时,需要等待服务端 20 分钟左右进行转码。转码完成后,用户可以通过 Recordplay 回放组件观看录制内容。在示例项目中,展示了如何快速接入 Recordplay 组件。
接入步骤如下:
-
Recordplay 回放组件包含 NERecordPlay、NERecordPlayUI 2 个依赖模块,podfile 中添加依赖到自己的项目中:
pod
pod 'NERecordPlay', :path => 'Modules/NERecordPlay/NERecordPlay.podspec' pod 'NERecordPlayUI', :path => 'Modules/NERecordPlayUI/NERecordPlayUI.podspec'
-
课程结束后,回放观看端调
getRecordListWithRoomUuid:rtcCid:success:failure:
获取上一次的回放记录,并创建回放播放器实例。示例代码:
objective-c
- (void)recordPlayEvent:(UIButton *)button { NSString *lastRoomUuid = [[NSUserDefaults standardUserDefaults] objectForKey:kLastRoomUuid]; NSString *lastRtcCid = [[NSUserDefaults standardUserDefaults] objectForKey:kLastRtcCid]; NSString *lastUserUuid = [[NSUserDefaults standardUserDefaults] objectForKey:kLastUserUuid]; NSString *lastUserToken = [[NSUserDefaults standardUserDefaults] objectForKey:kLastUserToken]; NERecordRequest *request = [[NERecordRequest alloc] initWithAppKey:[KeyCenter appKey] authorization:[KeyCenter authorization] baseUrl:[KeyCenter baseURL] userUuid:lastUserUuid token:lastUserToken]; [request getRecordListWithRoomUuid:lastRoomUuid rtcCid:lastRtcCid success:^(id _Nonnull data) { if (!data) { [self.view makeToast:@"课程结束后,需进行文件转码,预计20分钟后可观看回放"]; return; } NERecordViewController *vc = [[NERecordViewController alloc] init]; vc.modalPresentationStyle = UIModalPresentationOverFullScreen; vc.recordData = data; [self presentViewController:vc animated:YES completion:nil]; } failure:^(NSError * _Nonnull error) { [self.view makeToast:error.localizedDescription]; }]; }
-
如果成功,那么跳转到 Recordplay 模块中的 NERecordViewController 回放界面。
示例代码:
objective-c
NERecordViewController *vc = [[NERecordViewController alloc] init]; vc.modalPresentationStyle = UIModalPresentationOverFullScreen; vc.recordData = data; [self presentViewController:vc animated:YES completion:nil];
-
初始化播放器。
示例代码:
objective-c
func createPlayer() { guard let data = recordData else { print("error:recordData = nil") return } player = NEEduRecorderPlayerManager(data: data, view: contentView) player?.delegate = self recordList = player?.playingRecordItems player?.prepareToPlay() } func addNetListen() { NetworkReachabilityManager.default?.startListening(onUpdatePerforming: { state in print("state:\(state)") if state == .notReachable { self.navView.netState.image = UIImage.ne_imageName(name: "net_0") }else { self.navView.netState.image = UIImage.ne_imageName(name: "net_3") } }) }
-
开始播放。
示例代码:
objective-c
@objc func playButtonEvent(button: UIButton) { print(#function,button) button.isSelected = !button.isSelected button.isSelected ? player?.play() : player?.pause() }
-
暂停播放。
示例代码:
objective-c
@objc func playButtonEvent(button: UIButton) { print(#function,button) button.isSelected = !button.isSelected button.isSelected ? player?.play() : player?.pause() }
-
拖动进度。
示例代码:
objective-c
@objc func sliderEvent(slider: UISlider, event: UIEvent) { guard let touch: UITouch = event.allTouches?.first else { return } switch touch.phase { case .began,.moved: seeking = true case .ended,.cancelled: seeking = false player?.seekTo(time: Double(slider.value) * player!.duration) default: return } }
步骤六 结束课堂
调用 player?.stop()
结束课程。
示例代码:
objective-c@objc func backButtonEvent(button: UIButton) {
player?.stop()
self.dismiss(animated: true, completion: nil)
}