本文为您介绍即时渲染功能的 H5 SDK 使用方式。
使用限制
浏览器依赖
推荐浏览器
操作系统 | 浏览器 | 支持的最低版本 |
Windows | Google Chrome | 63 |
Mac | Google Chrome | 63 |
Safari | 11 | |
Android | Google Chrome | 63 |
微信内置浏览器 | 7.0.9(微信版本) | |
钉钉内置浏览器 | 11.2.5(钉钉版本) | |
华为浏览器 | 12.0.4 | |
iOS | Google Chrome | 63 |
Safari | 11 | |
微信内置浏览器 | 7.0.9(微信版本) | |
钉钉内置浏览器 | 11.2.5(钉钉版本) |
浏览器用户行为保护策略(iOS端)
受限于浏览器用户行为保护策略,iOS端建议用户采用如下策略。
建议有用户交互后再打开声音,如果在无交互情况下调用
setVolume
将导致视频暂停。Safari 15.4及以上版本需要有用户交互后才支持自动播放。
低电量模式容易自动播放失败,需要有用户交互的同时调用
replay
接口。
对应用的要求
应用需要是Windows操作系统下,基于图形接口DirectX 11的exe可执行程序。
试验特性 - 兼容WebView引擎
可在原生应用的WebView浏览器引擎中运行,环境要求为:
iOS系统: 14.3及以上版本。
安卓系统:Google Chrome主版本号79及以上版本。
开发说明
对于本地开发调试,由于跨域限制:
需要打开Chrome浏览器跨域模式
需要为localhost绑定一个.aliyun.com结尾(如gcs.aliyun.com)的host,然后访问这个绑定的域名进行开发调试
通过线上域名进行开发调试需要联系项目接口人添加相关域名白名单
快速开始
SDK引入方式
https://g.alicdn.com/aliyun-ecs/gcs-js-sdk/0.0.21/index.js
<script type="text/javascript" src="https://g.alicdn.com/aliyun-ecs/gcs-js-sdk/0.0.21/index.js"></script>
接口说明
接口 | 描述 |
init | 初始化SDK,返回初始化结果。 |
prepare | 开始资源调度 |
on | 监听事件消息,如事件GCSEvent |
start | 启动应用 |
stop | 停止应用,通知关停服务与串流 |
udpOpen | 建立自定义数据通道 |
udpClose | 关闭自定义数据通道 |
udpSend | 自定义数据通道发送消息 |
setKeyBoard | 自定义键盘 |
setSize | 设置显示区域宽高 |
onStat | 获取视频流参数 |
JoystickCreate | 创建虚拟摇杆 |
JoystickOn | 虚拟摇杆按钮添加监听事件 |
replay | 重新拉流 |
setFullscreen | 设置全屏 |
setBitrate | 动态设置比特率 |
setVolume | 设置音量 |
setMouse | 设置鼠标输入 |
disconnect | 关闭Rgc服务 |
典型调用顺序如下图所示。
init
prepare
on
off
start
stop
udpOpen
udpSend
udpClose
setKeyBoard
setSize
setMouse
onStat
JoystickCreate
JoystickOn
replay
setFullscreen
setBitrate
setVolume
disconnect
事件说明
事件名
GCSEvent
事件映射表
EventType | EventCode | EventMessage |
10 | 101030 | 找不到正确的accessKey |
10 | 101040 | 请求服务超时 |
10 | 101050 | 用户token校验未通过 |
10 | 101099 | 请求服务异常 |
10 | 102010 | 绑定长连接服务失败 |
10 | 109010 | 当前浏览器不支持 |
10 | 109011 | 浏览器版本过低 |
10 | 109012 | 不支持webrtc |
10 | 109013 | 不支持H264 |
10 | 109020 | 参数不合法 |
20 | 201010 | 应用运行环境准备完成 |
50 | 501010 | 内部错误 |
50 | 501020 | 调度失败 |
50 | 501030 | 资源包CU不足 |
50 | 501031 | 资源不足 |
50 | 501040 | 会话不存在 |
50 | 501041 | 启动会话请求被流控 |
50 | 501050 | 应用不存在 |
50 | 501051 | 应用未适配完成 |
50 | 501052 | 应用版本不存在 |
50 | 501053 | 应用停止中 |
50 | 501061 | 租户已欠费停服 |
50 | 501062 | 租户已欠费释放 |
50 | 501070 | 项目下没有该应用 |
50 | 502010 | 容器创建失败 |
50 | 502011 | 调度异常(ip/port为空) |
50 | 502020 | 应用启动失败 |
50 | 502030 | 应用停止失败 |
60 | 603010 | 应用停止成功 |
90 | 901000 | 串流鉴权失败 |
90 | 901010 | 连接服务器用户鉴权失败 |
90 | 901011 | 连接服务器用户鉴权超时而断开 |
90 | 901012 | 服务端未收到用户token |
90 | 901013 | 容器未分配token |
90 | 901014 | 服务连接失败 |
90 | 901015 | 服务断开连接 |
90 | 901099 | 连接异常中断 |
90 | 902011 | 服务连接成功 |
90 | 902012 | 画面准备就绪(启动完毕) |
90 | 902013 | 通道服务准备就绪 |
90 | 902014 | 通道服务关闭 |
90 | 903010 | 鼠标长时间未操作 |
90 | 904010 | 网络丢包 |
90 | 904011 | 网络抖动过大 |
90 | 904012 | 处理延迟过大 |
90 | 904013 | 网络延迟过大 |
90 | 904014 | 网络包重传 |
90 | 904015 | 连接异常 |
90 | 904016 | 重新连接中 |
90 | 904017 | 重新连接成功 |
90 | 904018 | 重新连接失败 |
示例代码
生成token
调用接口前,您需配置环境变量,通过环境变量读取访问凭证。更多信息,请参见使用说明。
云渲染的userId
、tenantId
、secretKey
的环境变量名:USER_ID
、TENANT_ID
、SECRET_KEY
。
Java示例
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* 字符串加密解密工具类
*/
public class DesUtil {
private static final Map<String, Cipher> ENCRYPT_MAP = new HashMap<>();
public static void main(String[] args) {
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。建议您创建并使用RAM用户进行API访问或日常运维。
// 此处以把AccessKey ID和AccessKeySecret保存在环境变量为例说明。您也可以根据业务需要,保存到配置文件里。
// 建议不要把AccessKey ID和AccessKeySecret保存到代码里,会存在密钥泄漏风险。
// 用户ID: 您自定义的ID
String userId = System.getenv("USER_ID");
// 租户ID: 请填写您的阿里云账号ID
Long tenantId = Long.valueOf(System.getenv("TENANT_ID"));
// secretKey: 阿里云提供
String secretKey = System.getenv("SECRET_KEY");
System.out.println(token);
}
public static String encrypt(String userId, Long tenantId, String secretKey) {
String originStr = String.format("%s_%s_%s", userId, tenantId, System.currentTimeMillis());
try {
byte[] array = initEncryptCipher(secretKey).doFinal(originStr.getBytes(StandardCharsets.UTF_8));
return byteArr2HexStr(array);
} catch (Exception e) {
throw new RuntimeException("failed to encrypt. originStr=" + originStr, e);
}
}
/**
* 将byte数组转换为表示16进制值的字符串
*
* @param arrB 需要转换的byte数组
* @return 转换后的字符串
*/
public static String byteArr2HexStr(byte[] arrB) {
int iLen = arrB.length;
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
StringBuilder sb = new StringBuilder(iLen * 2);
for (byte anArrB : arrB) {
int intTmp = anArrB;
// 把负数转换为正数
while (intTmp < 0) {
intTmp = intTmp + 256;
}
// 小于0F的数需要在前面补0
if (intTmp < 16) {
sb.append("0");
}
sb.append(Integer.toString(intTmp, 16));
}
return sb.toString();
}
/**
* 从指定字符串生成密钥,密钥所需的字节数组长度为8位,不足8位时后面补0,超出8位只取前8位
*
* @param tmp 构成该字符串的字节数组
* @return 生成的密钥
*/
private static Key getKey(byte[] tmp) {
// 创建一个空的8位字节数组(默认值为0)
byte[] arrB = new byte[8];
// 将原始字节数组转换为8位
for (int i = 0; i < tmp.length && i < arrB.length; i++) {
arrB[i] = tmp[i];
}
return new javax.crypto.spec.SecretKeySpec(arrB, "DES");
}
private static Cipher initEncryptCipher(String secretKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
Cipher encryptCipher = ENCRYPT_MAP.get(secretKey);
if (encryptCipher == null) {
encryptCipher = Cipher.getInstance("DES");
ENCRYPT_MAP.put(secretKey, encryptCipher);
}
Key key = getKey(secretKey.getBytes(StandardCharsets.UTF_8));
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
return encryptCipher;
}
}
Node.js示例
var CryptoJS = require("crypto-js");
/**
* 使用DES加密后生成鉴权token
* @param {string} userId 自定义用户ID
* @param {string} tenantId 阿里云账号ID
* @param {string} secretKey 阿里云提供
* @returns token
*/
function encrypt(userId, tenantId, secretKey) {
const message = `${userId}_${tenantId}_${new Date().getTime()}`;
const key = CryptoJS.enc.Utf8.parse(secretKey);
const encrypted = CryptoJS.DES.encrypt(message, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.ciphertext.toString();
}
const token = encrypt(userId, tenantId, secretKey);
console.log(token);
启动串流
调用接口前,您需配置环境变量,通过环境变量读取访问凭证。更多信息,请参见身份验证配置。
云渲染的AccessKey ID
和AccessKey Secret
的环境变量名:ALIBABA_CLOUD_ACCESS_KEY_ID
和ALIBABA_CLOUD_ACCESS_KEY_SECRET
。
Node.js示例
const initConfig = {
credential: new Credential(),
token: 'xxx',
userId: 'xxx',
sessionId: 'xxx',
}
const prepareConfig = {
appId: 'xxx',
appVersion: 'xxx',
container: document.querySelector('#renderDom'),
// appStartParam: '',
}
const gcssdk = new GCSSDK();
gcssdk.init(initConfig).then(ready => {
gcssdk.prepare(prepareConfig);
}).catch(e => {
console.log('GCS init failed.')
})
gcssdk.on('GCSEvent', res => {
const { type, code, message } = res;
console.log(`onGCSEvent: ${type} ${code} ${message}`);
switch(code) {
case '201010':
gcssdk.start(); // 启动游戏
break;
case '401010': // 接收应用的数据
console.log(message);
break;
default:
break;
}
})
常见问题
sessionId
与CustomSessionId
的作用是什么?云渲染服务中出现的
CustomSessionId
等于接口init
中的sessionId
:如果只允许用户单开,即一个用户只能开启一个会话,
sessionId
可以与userId
保持一致。如果前后两个页面的sessionId
一致,第二个页面会把第一个页面踢出。如果允许用户多开,
sessionId
设置随机数即可。
CustomSessionId
与PlatformSessionId
的区别是什么?CustomSessionId
是用户提供的。PlatformSessionId
是由GCS服务生成的,用于错误追踪,如果链路出错可通过该信息排查。prepare
方法中container
容器指的是前端Js dom
容器节点吗?是的,是用于承接
video
的容器。SDK会在这个容器下创建应用渲染所需的video
。什么时候可以获取
video
?监听到902012事件时
video
挂载成功。如果用户非主动退出会话,会话资源什么时候会被释放掉?
三分钟无响应后台会关闭应用。