文档

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端建议用户采用如下策略。

  1. 建议有用户交互后再打开声音,如果在无交互情况下调用setVolume将导致视频暂停。

  2. Safari 15.4及以上版本需要有用户交互后才支持自动播放。

  3. 低电量模式容易自动播放失败,需要有用户交互的同时调用replay接口。

对应用的要求

应用需要是Windows操作系统下,基于图形接口DirectX 11的exe可执行程序。​

域名加白

使用本SDK,均需配置跨域,请联系项目接口人加白名单。

本地调试请打开Google Chrome跨域模式。​

试验特性 - 兼容WebView引擎

可在原生应用的WebView浏览器引擎中运行,环境要求为:

  • iOS系统: 14.3及以上版本。

  • 安卓系统:Google Chrome主版本号79及以上版本。​

快速开始

关于如何引入SDK,请参见SDK引入方式

关于如何实现与渲染程序之间的数据交流,请参见CGProxy SDK使用示例

接口说明

接口

描述

最低版本支持

init

初始化SDK,返回初始化结果。

2.0.1

prepare

开始资源调度。

2.0.1

start

启动应用。

2.0.1

stop

停止应用,通知关闭服务与串流。

2.0.1

on

监听事件消息,如事件GCSEvent。

2.0.1

setSize

设置显示区域宽高。

2.0.1

setVolume

设置音量。

2.0.1

setMouse

设置鼠标输入。

2.1.5

setKeyBoard

设置键盘输入。

2.1.5

setFullscreen

设置全屏。

2.1.13

sendDataToService

直传数据给应用。

2.1.2

replay

播放重试。

2.1.14

sendRawEvent

发送操控指令输入。

2.1.18

sendRawEvent

检测SDK是否可用。

2.2.1

setOrientation

设置画面旋转角度。

2.2.6

典型调用顺序如下图所示。

2023-04-14_165800

init

调用时机:实例化SDK后调用。用于初始化配置,返回初始化结果。

参数

类型

是否必填

备注

accessKey

string

应用公钥。

token

string

用户token(用于鉴权)。

更多信息,请参见生成token

说明

该参数默认5分钟失效。

userId

string

用户ID。

sessionId

string

唯一会话标识。

useLog

boolean

是否打印日志。

取值:

  • true:打印日志。

  • false(默认值):不打印。

gcssdk.init({
  accessKey,           // 应用公钥
  token,                  // 用户token
  userId,                 // 用户ID
  sessionId,             // 唯一会话标识
}).then(ready => {
  console.log(ready); // true
}).catch(e => {
  console.log(ready); // false
});

prepare

调用时机:init异步结果返回true时调用。

参数

类型

是否必填

备注

appId

string

应用ID。

appVersion

string

应用版本号。

container

object

承接应用的DOM容器。

appStartParam

string

应用启动命令,传给应用启动方的参数。

projectId

String

项目ID。当您设置该参数时,容器将只使用该项目下的资源运行应用,否则将自动选择任意可用资源。

width

number

DOM容器宽度。

height

number

DOM容器高度。

keymouseMode

number

键鼠支持方式,取值:

  • 0(默认值):不主动触发动作。

  • 1:主动触发动作。

  • 2:主动触发键鼠 + touch。

  • 3:主动触发键鼠 + touch + H5虚拟摇杆。

isMouseShow

number

是否显示鼠标。

取值:

  • 0(默认值):不显示。

  • 1:显示。

idleTime

number

无操作超时时间,超时会自动停止应用。

取值范围:10~3600。默认值:600。

单位:秒。

说明

-1表示长时间无操作不会自动退出,请谨慎操作。

gcssdk.prepare({
  appId,                                  // 应用ID
  appStartParam,                    // 应用启动命令,与用户具体业务相关
  container,                           // Dom容器
});

start

调用时机:在收到201010事件后调用。

gcssdk.start();

stop

调用时机:用户需要手动关闭应用时调用。

gcssdk.stop();

setSize

参数

类型

是否必填

备注

width

number

DOM容器宽度。

height

number

DOM容器高度。

gcssdk.setSize(width, height);

setVolume

参数

类型

是否必填

备注

volume

number

音量大小:0~1,默认为1。

gcssdk.setVolume(volume);

on

调用时机:实例化SDK后调用。监听SDK所有事件,根据消息下行处理相关逻辑。

gcssdk.on('GCSEvent', res => {
  const { type, code, message } = res;
  console.log(`onGCSEvent: ${type} ${code} ${message}`);
  
  switch(code) {
    case '401010':              // 应用回传数据
      console.log(message);
      break;
    case '401020':              // 串流实时数据
      const {
        width,                     // 分辨率宽度
        height,                    // 分辨率高度
        fps,                        // 帧率
        bandwidth,              // 带宽(bps)
        rtt                          // 网络时延(ms)
      } = message;
      break;
    case '402010':             // 终端节点数据
      const {
        isp,                       // 供应商:TELECOM(中国电信)、UNICOM(中国联通)、MOBILE(中国移动)、BGP
        district,                 // 大区:华东、华北、华南、华中、西南
      } = message;
      break;
    default:
      break;
  }
});

setMouse

参数

类型

是否必填

备注

x

number

相对x坐标。

y

number

相对y坐标。

button

number

取值:

  • 1:鼠标左键。

  • 2:鼠标中间键。

  • 3:鼠标右键。

  • 4:滚轮和触摸屏。

  • 10:鼠标移动。

isPressed

boolean

鼠标是否按下,默认false,即没有按下。

left

number

video元素的左坐标。

top

number

video元素的上坐标。

width

number

video元素的宽。

height

number

video元素的高。

// 鼠标移动
video.onmousemove = function(event) {
  const e = event || window.event || arguments.callee.caller.arguments[0];
  e.preventDefault();
  
  // const left = video.getBoundingClientRect().left;
  // const top = video.getBoundingClientRect().top;
  // const width = video.offsetWidth;
  // const height = video.offsetHeight;
   const x = e.clientX;
   const y = e.clientY;

  gcssdk.setMouse({
    // left, top,
    // width, height,
    x, y,
    isPressed: false,
    button: 10
  });
};

setKeyBoard

参数

类型

是否必填

备注

keyCode

number

键盘类型。

isPressed

boolean

键盘是否按下。

取值:

  • true:按下键盘。

  • false(默认值):没有按下键盘。

// 键盘按下
document.onkeydown = function(event) {
  const e = event || window.event || arguments.callee.caller.arguments[0];
  e.preventDefault();

  gcssdk.setKeyBoard({
    keyCode: e.keyCode,
    isPressed: true
  });
};

setFullscreen

开启全屏并兼容iOS屏幕自动旋转。

参数

类型

是否必填

备注

type

Number

全屏类型。

取值:

  • 0(默认值):不全屏。

  • 2:全屏并兼容iOS屏幕自动旋转(推荐在 keymouseMode = 2 或 3 时使用)。

contrainterId

object

需要全屏的DOM容器ID,请保证唯一性。

默认对video进行全屏。

// 开启全屏并兼容iOS屏幕自动旋转(建议按需使用contrainterId)
gcssdk.setFullscreen({
  type: 2,									
  contrainterId: 'content'
});   

// 取消全屏  
gcssdk.setFullscreen({
  type: 0
});

sendDataToService

直传数据给应用,根据业务需要使用。

参数

类型

是否必填

备注

data

Uint8Array

传递给应用的数据。

const _U8Afrom = Uint8Array.from.bind(Uint8Array);
function stringToUint8Array(str) {
  return _U8Afrom(str, c => c.charCodeAt(0));
}
const uintData = stringToUint8Array(JSON.stringify(object));
gcssdk.sendDataToService(uintData); // 向应用传递数据
gcssdk.on('GCSEvent', res => {
  const { type, code, message } = res;
  switch(code) {
    case '401010':  // 接收应用的回调数据:此处返回的message为Uint8Array类型
      // 为避免uint8ArrayToString的精度损失,该方法仅适用于0~128字节字符串
      const callbackMsg = new TextDecoder().decode(message); 
      console.log(callbackMsg);
      break;
    default:
      break;
  }
})

replay

建议监听到901080 自动播放失败后,触发用户交互的同时进行调用。

如iOS低电量模式下自动播放失败,可使用此功能解决。

document.getElementById("confirmPlay").onclick = function() {
  gcssdk.replay();
};

sendRawEvent

通过不同事件发送操控指令输入sendRawEvent(eventConfig)eventConfig对象结构如下表所示。

事件

事件类型

eventConfig

参数说明

备注

手柄事件

手柄连接

{ type: "apppadconnect" }

手柄初始化后继续调用其他事件。

手柄断开

{ type: "apppaddisconnect" }

调用该事件使手柄断开。

手柄摇杆

{ type: "apppadstick", side: "left"/"right", x: [-32767~32767], y: [-32767~32767] }

  • side:左摇杆或右摇杆。

  • x,y:坐标值。整数区间为[-32767,32767]。

手柄触发

{ type: "apppadtrigger", side: "left"/"right", force: [0, 255] }

  • side:左摇杆或右摇杆。

  • force:触发力度。整数区间为[0,255]。

L2/LT或R2/RT。

手柄按键

{ type: "apppadbutton", bitMask, isPressed: true/false }

  • bitMask:按键类型。

  • isPressed:是否按下按键。取值:

    • true:按下按键。

    • false(默认值):没有按下按键。

当按键组合时,传递多个bitMask值之和。更多信息,请参见bitMask表

当手柄事件类型为apppadbutton时,bitMask的值如下表所示。

bitMask

说明

1

0x0001

方向键(向上)。

2

0x0002

方向键(向下)。

4

0x0004

方向键(向左)。

8

0x0008

方向键(向右)。

16

0x0010

开始。

32

0x0020

选择或返回。

64

0x0040

左摇杆垂直按下(L3/LS)。

128

0x0080

右摇杆垂直按下(R3/RS)。

256

0x0100

左触发键(L1/LB)。

512

0x0200

右触发键(R1/RB)。

4096

0x1000

A键。

8192

0x2000

B键。

16384

0x4000

X键。

32768

0x8000

Y键。

当您同时按键组合时,传递多个bitMask值之和。

例如,同时按A键和B键:bitMask=4096+8192=12288。

eventConfig:sendRawEvent({ type: "apppadbutton", bitMask: 12288, isPressed: true })

isSupport

检测SDK是否可用。

gcssdk.isSupport().then(res => {
  // 可用
}).catch(err => {
  console.log('GCSSDK not support: ', err) 
  // { type: '10', code: '109010', message: '当前浏览器不支持' }
  // { type: '10', code: '109011', message: '浏览器版本过低' }
  // { type: '10', code: '109012', message: '不支持webrtc' }
  // { type: '10', code: '109013', message: '不支持H264' }
})

setOrientation

设置画面基于左上角的旋转角度。

重要

本接口不建议和setFullscreen接口同时使用,否则画面旋转角度会出现问题。

请求参数

类型

是否必填

备注

container

string

旋转目标:

  • coordinate:仅旋转坐标计算。

    说明

    请确保您已经对包含prepare传入的DOM容器在内的组件做旋转。

  • video:旋转渲染video。(画面会置顶显示)

    说明

    当出现H5其他元素被video遮挡问题,您可以通过设置transform: translateZ(1px)解决。

  • html:旋转html。(画面会置顶显示)

    说明

    html 布局为position: fixed,因此不推荐您在需要滚动条时使用此方案。

deg

number

基于左上角的旋转角度。取值为0或9。

返回参数

类型

备注

success

boolean

是否成功设置画面基于左上角的旋转角度。

取值:

  • true:设置成功。

  • false:设置失败。

code

string

返回字段。

取值:

  • success:设置成功。

  • params invalid:参数不合法。

const { success, code } = gcssdk.setOrientation({
  container: 'coordinate',
  deg: 90
});

事件说明

事件名

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

说明

调用接口前,您需配置环境变量,通过环境变量读取访问凭证。更多信息,请参见使用说明

云渲染的userIdtenantIdsecretKey的环境变量名:USER_IDTENANT_IDSECRET_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 IDAccessKey Secret的环境变量名:ALIBABA_CLOUD_ACCESS_KEY_IDALIBABA_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;
  }
})

常见问题

  1. sessionIdCustomSessionId的作用是什么?

    云渲染服务中出现的CustomSessionId等于接口init中的sessionId

    • 如果只允许用户单开,即一个用户只能开启一个会话,sessionId可以与userId保持一致。如果前后两个页面的sessionId一致,第二个页面会把第一个页面踢出。

    • 如果允许用户多开,sessionId设置随机数即可。

  2. CustomSessionIdPlatformSessionId的区别是什么?

    CustomSessionId是用户提供的。PlatformSessionId是由GCS服务生成的,用于错误追踪,如果链路出错可通过该信息排查。

  3. prepare方法中container容器指的是前端Js dom容器节点吗?

    是的,是用于承接video的容器。SDK会在这个容器下创建应用渲染所需的video

  4. 什么时候可以获取video?

    监听到902010事件video挂载成功。

  5. 如果用户非主动退出会话,会话资源什么时候会被释放掉?

    三分钟无响应后台会关闭应用。

  • 本页导读 (0)
文档反馈