尽管疫情还未结束,但不少学生已经在线开启了新的一学期。不少教育巨头为老师与学生搭建的在线教学场景,都是经过声网 Agora SDK 实现的。为了方便更多用户能够基于 Agora SDK 快速实现多种在线教学场景,咱们现已开源声网云课堂 Demo,你们可在文末获取源码。html
除了 demo 开源,咱们也提供了Web、Android、iOS 应用供你们体验。推荐老师使用 Web 端应用,学生可以使用以上任一版本。前端
声网云课堂 Demo 实现的教学场景包括:web
除了支持实时音视频的互动,本示例项目还支持在线课堂中许多必不可少的功能。咱们仅以 Web 端来介绍一下。json
教学白板:在以上三个场景中,都支持白板功能。1 对 1 互动教学中,老师和学生均可以操做白板;1 对 1 在线小班课、低延迟大班课中,能够受权学生操做白板。后端
共享PPT等文件:老师能够在授课的同时,共享 PPT、PDF、word、图片、mp四、mp3 等多媒体文件。学生端只能观看,不能上传。安全
屏幕共享:老师能够发起屏幕共享,展现桌面、网页或其余应用窗口。app
文字消息:除了连麦之外,学生还能够经过文字消息在课堂中提问交流。less
录制回放:老师登陆 Web端,能够进行课程录制。在白板的下方,有录制按钮,制完成后,在右侧的消息窗口中会自动弹出回放地址。async
另外,老师端还支持一些控制学生端音视频流的权限,好比在 1 对 N 在线小班课中,老师还能够访问学生列表,开启或关闭学生的麦克风,好比在但愿某个学生发言,那么就能够直接开启对方的麦克风。接下来,咱们来经过 Demo 中的代码来看一下如何实现以上各个功能。ide
如下功能咱们会主要基于Java 代码来进行讲解。
这个示例中,课程的直播、师生的连麦,都是基于 Agora SDK 实现的。咱们经过如下代码可让用户加入RTC频道,实现音视频的互通。
@Override public void joinChannel(Map<String, String> data) { sdk.joinChannel(data.get(TOKEN), data.get(CHANNEL_ID), null, Integer.valueOf(data.get(USER_ID))); }
在课堂中的文字消息、控制指令(好比学生发出申请使用白板)等,都是基于 Agora 实时消息RTM SDK 实现的。在这里咱们集成 RTM SDK 后,经过如下代码让用户加入 RTM 频道。
public void joinChannel(String rtcToken) { RtmManager.instance().joinChannel(new HashMap<String, String>() {{ put(SdkManager.CHANNEL_ID, getChannelId()); }}); RtcManager.instance().joinChannel(new HashMap<String, String>() {{ put(SdkManager.TOKEN, rtcToken); put(SdkManager.CHANNEL_ID, getChannelId()); put(SdkManager.USER_ID, getLocal().getUserId()); }}); }
你们能够参考 Demo 中的白板部分的代码来实现白板功能,以下所示:
public void initBoard(String uuid, String token) { if (TextUtils.isEmpty(uuid)) return; boardManager.getRoomPhase(new Promise<RoomPhase>() { @Override public void then(RoomPhase phase) { if (phase != RoomPhase.connected) { pb_loading.setVisibility(View.VISIBLE); boardManager.roomJoin(uuid, token, new Callback<RoomJoin>() { @Override public void onSuccess(RoomJoin res) { RoomParams params = new RoomParams(uuid, res.roomToken); boardManager.init(whiteSdk, params); } @Override public void onFailure(Throwable throwable) { ToastManager.showShort(throwable.getMessage()); } }); } } @Override public void catchEx(SDKError t) { ToastManager.showShort(t.getMessage()); } }); }
老师端的屏幕共享,仅支持在 Web 端发起,如下是 Web 代码是老师端的实现逻辑。
async startScreenShare (token: string) { this.shareClient = new AgoraRTCClient(); await this.shareClient.createLocalStream({ video: false, audio: false, screen: true, screenAudio: true, streamID: SHARE_ID, microphoneId: '', cameraId: '' }) await this.shareClient.createClient(APP_ID); await this.shareClient.join(SHARE_ID, this.channel, token); await this.shareClient.publish(); this.shared = true; }
如下 Java 代码是学生端处理屏幕共享流(仅支持观看屏幕共享)的代码。
public void onScreenShareJoined(int uid) { if (surface_share_video == null) { surface_share_video = RtcManager.instance().createRendererView(this); } layout_whiteboard.setVisibility(View.GONE); layout_share_video.setVisibility(View.VISIBLE); removeFromParent(surface_share_video); surface_share_video.setTag(uid); layout_share_video.addView(surface_share_video, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); RtcManager.instance().setupRemoteVideo(surface_share_video, VideoCanvas.RENDER_MODE_FIT, uid); } @Override public void onScreenShareOffline(int uid) { Object tag = surface_share_video.getTag(); if (tag instanceof Integer) { if ((int) tag == uid) { layout_whiteboard.setVisibility(View.VISIBLE); layout_share_video.setVisibility(View.GONE); removeFromParent(surface_share_video); surface_share_video = null; } } }
在线录制只有使用 Web 端的老师才能够发起,如下是 Web 老师端开启云端录制的代码逻辑:
public async start(): Promise<any> { if (this.resourceId === undefined) { throw { recordingErr: { message: 'start recording failed', }, reason: 'resourceId is undefined', } } const response = await AgoraFetch(`${PREFIX}/v1/apps/${this.agoraAppId}/cloud_recording/resourceid/${this.resourceId}/mode/${this.mode}/start`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: this.basicAuthorization(this.customerId, this.customerCertificate), }, body: JSON.stringify({ cname: this.channelName, uid: this.uid, clientRequest: { token: this.token, recordingConfig: this.recordingConfig, storageConfig: this.storageConfig, }, }) }); const res = await response.json(); if (typeof res.sid === "string") { this.recordId = res.sid; } else { throw { recordingErr: { message: 'start recording failed', }, reason: 'recordId is invalid', } } return res; }
Q:支持的平台和版本(iOS/Android/Web)
本示例项目支持如下平台和版本:
注意,目前示例 Demo 还未适配 iPad,因此部分 iPad 运行 Demo 时会出现黑边,须要你根据本身的业务需求在代码中进行调整、适配。
Q:一直报错人数已满(iOS/Android/Web)
这是因为本示例项目没有用户系统,没法及时查询在线人数。本示例项目使用 Agora RTM SDK 查询在线人数。在用户异常退出后,RTM 没法及时知晓该用户状态。通常状况下,须要等待 10 秒左右 RTM 才能更新当前用户状态。若是你有用户系统,就能够不须要使用 RTM 查询在线人数。
Q:音视频没法链接(iOS/Android/Web)
这颇有多是由于 Agora RTC SDK 的 token 配置存在问题。请检查你的 token 配置。请在声网文档中心搜索「校验用户权限」,参考相关文档。
请注意,Agora RTC SDK 的 token 和 channelId 须要对应。若是 channelId 变了,配置的 token 也须要变。
Q:白板链接异常(iOS/Android/Web)
Demo 中的白板采用的是合做伙伴 Netless 的接口,若是发现链接异常,多是 token 配置有问题。白板的 token 分为 sdkToken 和 roomToken,请在https://developer.netless.lin... 搜索这两个关键词,检查 token 的设置是否存在问题。
Q:教室里云端录制(Web)
若是你的团队有后端开发能力,咱们建议使用声网本地服务端录制 SDK,将录制功能集成在后端。
若是没有后端开发团队,也能够参考声网文档中心的「云端录制快速开始」,实现云端录制功能。要注意,本示例项目将录制集成在了前端,存在暴露 OSS 访问操做的安全隐患,部署云录制业务也可能存在困难。 如遇到问题能够访问 RTC 开发者社区(rtcdeveloper.com),发帖寻求帮助。
声网云端录制服务支持合流录制和单流录制。合流录制是比较简单的集成方案,具体可参考声网文档中心教程。
Q:白板课件管理(Web)
本示例项目中,白板课件管理部署在前端。建议你实现排课业务后,在课程里预先上传课件,Web 端只读取课件便可。
本示例项目中涉及的 OSS 相关的接入,建议参考 stsToken 的上传方案集成(https://help.aliyun.com/docum...)。建议不要使用示例项目中的 accessKey 和 secretKey 的上传方案集成,可能存在安全隐患。
欢迎在测试源码的同时,提出更多关于 Demo 优化、改进的更多想法。咱们会对每位提出有效建议的开发者送上丰厚奖励。