GCS SDK for H5
本文为您介绍即时渲染功能的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可执行程序。
域名加白
使用本SDK,均需配置跨域,请联系项目接口人加白名单。
本地调试请打开Google Chrome跨域模式。
试验特性 - 兼容WebView引擎
可在原生应用的WebView浏览器引擎中运行,环境要求为:
iOS系统: 14.3及以上版本。
安卓系统:Google Chrome主版本号79及以上版本。
快速开始
关于如何引入SDK,请参见SDK引入方式。
关于如何实现与渲染程序之间的数据交流,请参见CGProxy SDK使用示例。
接口说明
接口 | 描述 | 最低版本支持 |
初始化SDK,返回初始化结果。 | 2.0.1 | |
开始资源调度。 | 2.0.1 | |
启动应用。 | 2.0.1 | |
停止应用,通知关闭服务与串流。 | 2.0.1 | |
监听事件消息,如事件GCSEvent。 | 2.0.1 | |
设置显示区域宽高。 | 2.0.1 | |
设置音量。 | 2.0.1 | |
设置鼠标输入。 | 2.1.5 | |
设置键盘输入。 | 2.1.5 | |
设置全屏。 | 2.1.13 | |
直传数据给应用。 | 2.1.2 | |
播放重试。 | 2.1.14 | |
发送操控指令输入。 | 2.1.18 | |
检测SDK是否可用。 | 2.2.1 | |
设置画面旋转角度。 | 2.2.6 |
典型调用顺序如下图所示。

init
prepare
start
stop
setSize
setVolume
on
setMouse
setKeyBoard
setFullscreen
sendDataToService
replay
sendRawEvent
isSupport
setOrientation
事件说明
事件名
GCSEvent
事件映射表
EventType | EventCode | EventMessage | 开始支持版本 |
10 | 101030 | 找不到正确的AccessKey。 | 2.0.1 |
10 | 101040 | 请求服务超时。 | 2.0.1 |
10 | 101050 | 用户token校验未通过。 | 2.0.1 |
10 | 101099 | 请求服务异常。 | 2.0.1 |
10 | 102010 | 绑定长连接服务失败。 | 2.0.1 |
10 | 109010 | 当前浏览器不支持。 | 2.0.1 |
10 | 109011 | 浏览器版本过低。 | 2.2.1 |
10 | 109012 | 不支持webrtc。 | 2.2.1 |
10 | 109013 | 不支持H264。 | 2.2.1 |
10 | 109020 | 参数不合法。 | 2.0.1 |
20 | 201010 | 应用运行环境准备完成。 | 2.0.1 |
40 | 401010 | Uint8Array(应用回传数据)。 | 2.1.2 |
40 | 401020 | object(串流实时数据)。 | 2.1.10 |
40 | 402010 | object(终端节点数据)。 | 2.1.10 |
50 | 501010 | 内部错误。 | 2.0.1 |
50 | 501020 | 调度失败。 | 2.0.1 |
50 | 501030 | 资源包CU不足。 | 2.0.1 |
50 | 501031 | 资源不足。 | 2.1.10 |
50 | 501040 | 会话不存在。 | 2.0.1 |
50 | 501041 | 启动会话请求被流控。 | 2.1.4 |
50 | 501050 | 应用不存在。 | 2.0.1 |
50 | 501051 | 应用未适配完成。 | 2.0.1 |
50 | 501052 | 应用版本不存在。 | 2.0.1 |
50 | 501053 | 应用停止中。 | 2.0.1 |
50 | 501061 | 租户已欠费。 | 2.1.3 |
50 | 501062 | 租户已欠费释放。 | 2.1.3 |
50 | 502010 | 容器创建失败。 | 2.0.1 |
50 | 502011 | 调度异常(ip/port为空)。 | 2.0.1 |
50 | 502020 | 应用启动失败。 | 2.0.1 |
50 | 502030 | 应用停止失败。 | 2.0.1 |
60 | 603010 | 应用停止成功。 | 2.1.13 |
90 | 901000 | 串流鉴权失败。 | 2.2.0 |
90 | 901010 | 连接服务器用户鉴权失败。 | 2.0.1 |
90 | 901080 | 自动播放失败。 | 2.1.14 |
90 | 901011 | 连接服务器用户鉴权超时而断开。 | 2.0.1 |
90 | 901012 | 服务端未收到用户token。 | 2.0.1 |
90 | 901013 | 容器未分配token。 | 2.0.1 |
90 | 901014 | 连接服务器失败。 | 2.0.1 |
90 | 901099 | 连接异常中断。 | 2.0.1 |
90 | 902010 | 服务器启动鉴权成功。 | 2.0.1 |
90 | 902011 | 服务器连接成功。 | 2.0.1 |
90 | 902012 | 画面准备就绪(启动完毕)。 | 2.0.1 |
90 | 902013 | 通道服务准备就绪。 | 2.2.4 |
90 | 903010 | 因长时间未操作导致踢出。 | 2.0.1 |
90 | 903020 | 因账户在其它设备登录而被踢出。 | 2.0.1 |
90 | 904010 | 网络断开,进入重连状态。 | 2.0.1 |
90 | 904020 | 重连成功。 | 2.0.1 |
90 | 904030 | 重连失败。 | 2.0.1 |
示例代码
生成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
?监听到902010事件时
video
挂载成功。如果用户非主动退出会话,会话资源什么时候会被释放掉?
三分钟无响应后台会关闭应用。