本文为您介绍远程监考考生端、监考端的代码示例及集成说明。
项目构成
本次开源代码包含两个前端仓库:
监考端:exam-invigilator
考生端:exam-examinee
前提条件
已接入、运行、部署开源的Appserver服务。具体操作,请参见服务端集成示例源码。
已创建OSS Bucket,并配置好直播推拉流域名。如未配置,请参见控制台创建存储空间、快速开始视频直播完成Bucket与推拉流域名配置。
项目框架
本项目使用UmiJS框架开发,技术栈为React+TypeScript。详细说明请参见UmiJS 官方文档。
项目运行
开源项目地址
项目地址:阿里云考场场景方案。
准备环境
安装Node环境,详细信息请参见UmiJS 快速上手教程。
配置AppServer的地址
修改src/config.ts里的 appServer
配置项,将其填写为您的服务端域名和路径前缀,更多配置项请参考下文扩展配置项章节。
// 配置 APPServer
appServer: {
origin: "", // 配置 APPServer 服务域名,例子: https://xxx.xxx.xxx,结尾请勿包含 /
apiPrefixPath: "/exam/", // 配置api接口路径前缀
},
创建考场生成体验链接
远程监控Web开源项目不包含考试管理、考场管理、用户管理等业务功能,需要您根据开源的AppServer 自行实现。本地开发时,监考端、考生端正常运行需要通过URL传入roomId(考场ID)、userId(用户ID)、token(身份检验字符串)。因此,你首先需要通过服务端createRoom 接口(详情见服务端接口文档)创建一个考场,创建成功后返回的ID即为考场ID、createTeacher为监考员ID;而 userId、token 是需要您自行实现,开源工程中并不会校验token,随意传入字符串即可,userId设定为examinee1 、examinee2至 examinee5。
若创建考场接口返回的数据如下所示:
{
"success":true,
"data":{
"id":"12345",
"createdAt":"2023-10-20T14:34:40+08:00",
"updatedAt":"2023-10-20T14:34:40+08:00",
"name":"test exam",
"examId":"12345",
"status":0,
"audioStatus":0,
"imGroupId":"12345",
"createTeacher":"teacher1"
}
}
则本地体验URL为:
// 若考生端本地服务端口为 8001,则体验地址为
http://localhost:8001?roomId=12345&userId=examinee1&token=xxxx
// 若监考端本地服务端口为 8002,则体验地址为
http://localhost:8002?roomId=12345&userId=teacher1&token=xxxx&role=0
考场链接生成脚本
我们还开源了创建考场的脚本,脚本为监考端工程内的script/genroom.mjs
文件。在运行项目前需要先生成考场的URL,具体操作步骤如下。
打开监考端工程内的
script/genroom.mjs
文件,并将您的AppServer
域名赋值给apiOrigin
变量,根据具体监考端和考生端项目的运行端口号修改对应的变量。// 请输入您的服务端的域名,结尾请勿是 / const apiOrigin = ''; // 监考端域名,请根据您的实际情况更改,特别是本地运行时,要注意监考端的端口是否一致 const invigilatorBasePath = 'http://localhost:8000'; // 考生端域名,请根据您的实际情况更改,特别是本地运行时,要注意考生端的端口是否一致 const examineeBasePath = 'http://localhost:8001';
执行如下命令,运行genroom.mjs脚本生成本地体验URL,
npm install axios --save node script/genroom.mjs
得到的体验格式如下。
监考端体验地址 -> http://localhost:8000?roomId=***&userId=teacher&token=xxx&role=0 考生端副机位体验地址 -> http://localhost:8001?roomId=***&userId=examinee1&token=xxxx 考生端主机位体验地址 -> http://localhost:8001?roomId=***&userId=examinee1&token=xxxx#/pc
其中8000端口对应的是监考端项目的运行端口,8001端口对应的是考生端项目的运行端口,开发者可以根据自己项目的运行端口号进行调整。
推流/播放地址规则说明
在远程监考的集成过程中,考生画面的推流/播放地址、老师音频的推流/播放地址均需要您按照一定的规则进行生成,本节为您介绍远程监考的推流/播放地址生成规则。
了解阿里云推流和播放地址格式。详细说明,请参见生成推流地址和播放地址、鉴权代码示例。
基于阿里云的客户服务经验,推荐您使用如下格式。
考生画面的推流地址:artc://webrtcpush.example.com/appname/examid-roomid-userid?token=*********
appname:通常是一个固定值,如您的应用名称或公司名称等。您需要设置的appname需要与上文配置的录制模板、转码模板指定的appname一致,此时录制和转码才会生效。
examid:建议设置为本场考试编号。
roomid:某场考试中,具体某个考场的编号。
userid:具体参与考试的用户编号。
考生画面的播放地址(原始画面):artc://webrtcpull.example.com/appname/examid-roomid-userid?token=*********。
考生画面的播放地址(转码后的画面): artc://webrtcpull.example.com/appname/examid-roomid-userid_templateName?token=*********。
templateName为上文您设置的转码模板的名称。
老师音频的推流地址:artc://webrtcpush.example.com/appname/examid-roomid-userid?token=*********。
老师音频的播放地址:artc://webrtcpull.example.com/appname/examid-roomid-userid?token=*********。
运行项目
按照以下步骤进行操作:
参考考场链接生成脚本,生成体验考场及对应监考端和考生端页面的体验URL。
先在监考端工程中运行如下指令,然后在考生端工程中运行如下指令。
// 安装 npm 包 npm install // 安装完成后,执行 dev 指令,运行成功后根据提示使用浏览器访问即可 npm run dev
运行成功,界面如下。
复制第一步生成的URL到浏览器中打开即可开始体验。
构建配置
// 运行build指令即可构建最终产物至./dist目录下
npm run build
构建的文件主要包括index.html、umi.js和umi.css,其余的是按需加载的资源文件。您可以将生成的./dist目录下的所有文件上传至您的服务器或CDN服务中,也可以使用您的内部部署系统进行部署。
您需要根据您的实际情况,配置位于根目录下的 .umirc.ts文件中的publicPath参数。
若您最终访问的页面是单独加载生成的js、css资源,则无需配置publicPath。
若需直接使用index.html,请参考以下示例,并根据您的实际情况进行配置。
import fs from 'fs';
import path from 'path';
const packagejson = fs.readFileSync(path.resolve('./package.json'));
const json = JSON.parse(packagejson.toString());
export default {
// 省略其他配置参数
// 生成的 index.html 里使用的umi.js 、umi.css地址的公共路径的默认值是 /
// 若 index.html 部署的地址是 http://g.alicdn.com/publicPath/exam/0.0.1/index.html
// 若不配置 publicPath 直接访问测试、线上环境 index.html,所加载的umi.js将会是 http://g.alicdn.com/umi.js
// 显然不是跟index.html目录下了,所以请根据您实际情况配置为真实路径 或者使用相对路径 ./
// 例子中使用了项目的name 、version在部署目录中,请根据您实际情况配置
publicPath:
process.env.NODE_ENV === 'production'
? `/publicPath/${json.name}/${json.version}/`
: './',
}
扩展配置项
在远程监考系统中,考生端和监考端的功能配置项均存储在对应项目中的src/config.ts
文件中,您可以根据实际业务需求进行配置,具体说明如下:
考生端
const config: IConfig = {
aliyunUid: "",
// pagePath当前用于本地开发调试时设置PC考生页上的副机位二维码的 url
pagePath: "",
// 配置 APPServer
appServer: {
origin: "", // 配置 APPServer 服务域名,例子: https://xxx.xxx.xxx
apiPrefixPath: "/exam/", // 配置api接口路径前缀
},
// 配置日志服务
reporter: {
enable: false, // 是否开启埋点
host: "", // 所在地域的服务入口
projectName: "", // sls 的工程名称
logstore: "", // sls 的 logstore
},
// 配置本地录制
localRecorder: {
enable: false, // 是否开启本地录制
// 模式支持 OSS 或 VOD
mode: "OSS",
// OSS 模式时有用,注意:开头、结束字符必须要是 /
// 若遇到 403 问题,检查服务端获取 STS 接口的 policy 里的仓库路径与 basePath 是否匹配
basePath: "/record/local/",
// VOD 模式时有用
vod: {
region: "cn-shanghai",
// 创建的额外参数,详情请看 https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-createuploadvideo#api-detail-35
// 注意 Title、StorageLocation 这里设置无效,若有特殊要求,请至 VODUploader 文件中修改
params: {
Tags: "AUIExam",
},
},
},
// 防作弊服务配置项
cheatDetect: {
licenseKey: "", // 防作弊 SDK 的 license
licenseDomain: "", // 防作弊 SDK 允许运行的域名(localhost不受限制)
},
// 移动端摄像头切换模块的配置项
mobileCameraSwitcher: {
enable: false, // 是否展示该模块
},
// 默认摄像头流参数
defaultVideoProfile: {
name: "custom_360_800k", // 名称,可根据实际情况修改
data: {
// 低配低版本安卓手机,若高于 360P 可能取到的画面是黑屏
// 正式考试时建议请勿配置高于 360P(640*360/360*640)
width: 640, // 视频宽度
height: 360, // 视频高度
frameRate: 30, // 帧率
maxBitrate: 800, // 最大码率
},
},
};
appServer
配置您部署的服务端appServer的域名和API前缀路径。
pagePath
当前页面的域名+路径,主要应用于主机位页面(src->pages->pc)本地开发调试时,设置进入考生移动端二维码的URL,当域名为localhost或127.0.0.1时,将自动替换为此配置值,其他情况则使用当前的origin+pathname。
aliyunUid
开通直播、点播、OSS等服务的阿里云userId(主账号的userid),当使用VOD上传时,该字段为必填项。
reporter
SLS日志的配置项。若要使用埋点统计功能,则需要将enable设为true,同时填写projectName、host和logstore。有关projectName和logstore的获取方法,请参见快速入门。
使用SLS日志,将会产生相应费用。计费说明请参见按量付费。
localRecorder
考生端还可以通过localRecorder设置是否开启本地录制功能。该功能在推流失败时(例如网络异常),会将本地音视频内容录制并存储在浏览器中,然后在有网络条件时上传至您配置的OSS Bucket。
支持OSS和VOD两种存储模式,请根据实际情况选择。两种模式的上传均使用STS临时授权方案。对于VOD模式,请参阅使用STS临时授权方案上传视频;对于OSS模式,请参阅使用STS临时访问凭证访问OSS 文档。
cheatDetect
在工程中,我们支持使用防作弊SDK来辅助远程监考。请查看远程监考智能防作弊SDK以了解其功能及购买License,并配置相应的licenseKey、licenseDomain等参数。
mobileCameraSwitcher
考生在移动端默认使用前置摄像头,如果您将enable参数设置为true,那么考生界面将支持切换前后摄像头。
defaultVideoProfile
您可以在此配置项中设置默认摄像头流的分辨率、码率和帧率。
监考端
const config: IConfig = {
aliyunUid: "",
// 配置 APPServer
appServer: {
origin: "", // 配置 APPServer 服务域名,例子: https://xxx.xxx.xxx,结尾请勿包含 /
apiPrefixPath: "/exam/", // 配置api接口路径前缀
},
// 配置日志服务
reporter: {
enable: false, // 是否开启埋点
host: "", // 所在地域的服务入口
projectName: "", // sls 的工程名称
logstore: "", // sls 的 logstore
},
// 防作弊检测消息相关功能
detectList: {
enable: true,
},
// 多机位配置项
mutilMonitor: {
enable: true, // 是否需要多机位,若不开启,则仅展示 mobile 副机位的流
preferMainMonitor: true, // 是否优先展示 pc 主机位的流
},
};
appServer
配置您部署的服务端appServer的域名和API前缀路径。
aliyunUid
开通直播、点播、OSS等服务的阿里云userId,注意是主账号的userid。
reporter
SLS日志的配置项。若要使用埋点统计功能,则需要将enable设为true,并填写projectName、host和logstore。有关projectName和logstore的获取方法,请参见快速入门。
使用SLS日志,将会产生相应费用。计费说明,请参见按使用功能计费。
detectList
如果考生端开启了防作弊检测,您可以在监考端启用此参数,启用后将在左侧显示疑似作弊消息列表模块。
mutilMonitor
监考端默认展示考生副机位(移动端)的画面。如果您需要支持切换至考生画面,并默认显示主机位(PC)画面,您可以使用enable和preferMainMonitor参数。
技术细节
本节为您介绍项目中的一些技术细节,方便您对源码进行修改,进行更具定制化的开发扩展。
主副机位
考生端目前支持同时打开主副机位两个页面,默认哈希路由 #/ 时为副机位,#/pc 时为主机位。
主机位
主机位页面是为考生正面监考,运行在 PC 设备上,包含环境设备检测页(路由:#/pc/deviceTest),以及 PC 监考页(路由:#/pc)。建议默认打开检测页(URL示例:localhost:8001?roomId=12345&userId=examinee1&token=xxxx#/pc/deviceTest),检测通过后将自测跳转 PC 监考页。
设备检测
如截图所示,正式进入考场时会对摄像头、麦克风、扬声器、屏幕共享这四项设备进行检测,通过后跳转 PC 监考页。
PC监考页
PC监考页左边侧边栏将展示当前 PC 摄像头的画面,底下为移动端副机位的二维码,当副机位监考页也进入推流后将加载移动端的画面。
考题区域需要由您根据您的实际情况进行开发。
副机位
副机位也称为侧机位,运行于移动端浏览器或 webview 中,考生扫主机位上的二维码即可进入(注意:本地开发时要配置 pagePath,或者直接在 PC 上打开本地的副机位页面)。
监考端
监考端支持切换查看考生的主副机位画面,当使用口播、连麦等功能时将于当前查看的机位进行通信。
重要类说明
工程中的src->core文件夹下的文件为核心逻辑,您可重点阅读以下类的源码。
core/Interaction.ts
对阿里云互动消息SDK做二次封装,使更好地适配考试业务。详细说明,请参见互动消息SDK文档。
core/RadioTimer.ts
用于定时系统广播。
core/rts-publisher.ts
推流组件src->components->rts->Publish中所用的核心推流逻辑,详细说明,请参见aliyun-rts-sdk文档。
core/rts-subscriber.ts
拉流组件src->components->rts->Subscribe中所用的核心拉流逻辑,详细说明,请参见aliyun-rts-sdk文档。
core/AudioPlayer.ts
用于iOS系统播放音频,解决部分iOS环境播放音频的异常。
utils/Reporter.ts
用于埋点统计。
utils/LocalMock.ts
用于本地开发初期提供mock数据,可以在页面URL上增加mock=1参数(例如:http://localhost:8000/?mock=1),那么就会使用到本地的mock数据。大部分数据都可以静态mock,有鉴权的推拉流地址需要替换。
getIMToken方法中的accessToken字段需要设置为互动消息服务取到的Token,可参考互动消息建立长连接文档生成。