天翼云实时云渲染 JS SDK

[ecloud-rcr](https://www.npmjs.com/package/ecloud-rcr)

[toc]
# 安装

```sh
npm install ecloud-rcr
```
# 一、快速启动

## 1.1 前端项目引入

```js
import { RCRLaunch } from 'ecloud-rcr';

const bootstrap = async () => {
  try {
    const launch = await RCRLaunch({ appKey });
    const container = document.getElementById('container');
    await launch.automata(container);
    const connection = launch.getConnection();
    //监听连接状态
    connection.event.close.on(() => console.log('断开连接'));
  } catch (error) {
    console.error(error);
  }
};

bootstrap();
```

## 1.2 浏览器直接引入

> - [Demo：浏览器直接引入.html](./demo/BrowserImport.html)

```js
// 注意全局对象的名称
const { RCRLaunch } = window.ECloudRCR;
```

# 二、启动器(Launcher)

```js
import { RCRLaunch } from 'ecloud-rcr';

const launch = RCRLaunch({
  appKey,
  baseOptions: { ... },
  extendOptions: { ... },
});
```

## 2.1 构造属性(Constructors)

| 属性          | 说明                                                      | 类型              | 是否必填 |
| :-------------| :-------------------------------------------------------- | :---------------- | :------- |
| appKey        | 应用 key                                                  | string            | -        |
| baseOptions   | 基础配置                                                  | baseOptions       | -        |
| extendOptions | 拓展配置                                                  | extendOptions     | -        |

### 2.1.1 baseOptions 基础配置

> - [Demo：webrtcEnable:true 设置推流](./demo/WebrtcEnableTrue.html)
> - [Demo：webrtcEnable:false 设置推流](./demo/WebrtcEnableFalse.html)
> - [Demo: 投屏](./demo/CastScreen.html)

| 属性               | 说明                               | 类型    | 是否必填 |
| :----------------- | :--------------------------------- | :------ | :------- |
| startType          | 1：普通连接 3：投屏连接            | number  | 必填     |
| appSecret          | 密钥                               | string  | -        |
| webrtcEnable       | 是否开启webrtc推流，默认为true(详见示例)     | boolean | -        |
| exeParameter       | 自定义应用启动参数                  | string  | -        |
| isCastScreenMaster | (投屏)是否为主控                   | boolean | -        |
| castScreenMaxQty   | (投屏)最大并发数(最大为 5)         | number  | -        |
| castScreenNickname | (投屏)昵称                         | string  | -        |
| castScreenPwd      | (投屏)投屏密钥                     | boolean | -        |
| joinType           | (投屏)加入方式 (1：密钥\|3：链接)   | number  | -        |

### 2.1.2 extendOptions 拓展配置

| 属性              | 说明                             | 类型                     | 是否必填 |
| :---------------- | :------------------------------- | :----------------------- | :------- |
| onPlay            | 开始播放回调                     | () => void               | -        |
| onLiveStart       | 设置直播流url地址(开始)回调      | (result:boolean) => void | -        |
| onLiveStop        | 停⽌直播回调                     | (result:boolean) => void | -        |
| onLiveUrl         | 设置直播url地址回调              | (result:boolean) => void | -        |
| onMount           | 虚拟控件挂载回调(返回挂载父节点) | (el:HTMLElement) => void | -        |
| onRotate          | 视频容器方向变化回调             | (rotate:boolean) => void | -        |
| ueMultiTouch      | 是否开启 UE4 应用多点触控        | boolean                  | -        |
| minBitrate        | 初始化最小码率(默认为：2000)     | number                   | -        |
| maxBitrate        | 初始化最大码率(默认为：5000)     | number                   | -        |
| startBitrate      | 初始化码率(默认为：4000)         | number                   | -        |
| openWebRTCStats   | 开启 WebRTC 日志打印             | boolean                  | -        |
| holdToolDisplay   | 是否保持工具栏显示               | boolean                  | -        |
| audioToastDisplay | 是否开启音频提示弹窗(默认true)   | boolean                  | -        |
| toolOption        | 工具栏选项                       | toolOption               | -        |
| eventOption       | 事件选项                         | eventOption              | -        |

#### 2.1.2.1 extendOptions.toolOption 工具栏选项

> - [Demo：自定义工具栏(实现上报功能)](./demo/ExtendOptions.toolOption.extendTools.html)

| 属性        | 说明       | 类型              | 是否必填 |
| ----------- | ---------- | ----------------- | -------- |
| dropTools            | 删除工具栏 | DropToolsType     | -        |
| dropTools.toolsIndex | 工具栏位置(从 0 开始)          | number[] | -        |
| dropTools.platform   | 平台（1:pc\|2:移动端\|3:全部） | number   | -        |
| extendTools          | 新增工具栏 | ExtendToolsType[] | -        |
| extendTools.icon     | 图标(base64)                   | string     |          |
| extendTools.text     | 标题                           | string     | -        |
| extendTools.platform | 平台（1:pc\|2:移动端\|3:全部） | number     | -        |
| extendTools.order    | 新增位置(从 0 开始)            | number     | -        |
| extendTools.onClick  | 点击触发事件                   | () => void | -        |

#### 2.1.2.2 extendOptions.eventOption 事件选项

| 属性           | 说明                          | 类型    | 是否必填 |
| -------------- | ----------------------------- | ------- | -------- |
| enableKeyBoard | 是否挂载键盘事件(默认为 true) | boolean | -        |

## 2.2 属性(Attributes)

| 属性名                          | 说明                                    | 类型    | 默认值 |
| :------------------------------ | :-------------------------------------- | :------ | :----- |
| runningState                    | 获取运行状态                            | dn      | -    |
| runningState.isOrientationMatch | 获取屏幕方向变化(false:横屏\|true:竖屏) | boolean | true   |

## 2.3 方法(Methods)

> - [Demo：Launcher 实例的方法](./demo/LauncherMethods.html)

| 方法名              | 说明                              | 参数       | 回调参数             |
| :----               | :----                             | :----      | :----                |
| getConnection       | 获取连接实例                      | -          | 连接实例(Connection) |
| getPlayer           | 获取播放实例                      | -          | 播放实例(LivePlayer) |
| report              | 获取实时连接状态                  | -          | object              |
| toggleStatistics    | 控制连接状态显示状态(影响report)| -          | object               |
| toggleFullscreen    | 切换全屏(横屏)[支持情况参考](https://vip.ctcdn.cn/help/10011432/10011408/common/10011428?workspaceId=0) | -     | void       |
| destory             | 断开全部连接                      | \[errMsg\]  | void                 |
| setUpProcessPause   | 暂停当前运行应用                  | -           | void                 |
| setUpProcessRestart | 重启运行应用                      | -           | void                 |
| setMoveSensitivity  | 设置鼠标或者触摸移动的敏感度       | number(0：低，1：中，2：高) | void |
| handleSubscribe     | 手动挂载鼠标、键盘、触控等事件     | HTMLElement | void                 |
| handleUnsubscribe   | 手动取消鼠标、键盘、触控等事件     | -           | void                 |
| requestPointerLock  | 锁定鼠标                          | -           | void                 |
| exitPointerLock     | 解锁鼠标                          | -           | void                 |
| handleChangeSubscribe | 切换猫头工具的控制权            | boolean     | void                 |
| getOrientationMatch | 对比容器与推流的方向是否一致       | -           | boolean             |
| liveStart           | 设置直播流url地址，并开始推送；传参为空时，使用之前的url地址(详细参考示例6.9) | [string] | void |
| liveStop            | 停⽌推直播流数据                  | -           | void |
| liveUrl             | 设置直播url地址，url地址必填      | string      | void |

# 三、连接管理(Connection)

```js
const launch = await RCRLaunch({ ... });
await launch.automata(container);
const connection = launch.getConnection();
```

## 3.1 属性(Attributes)

> - [demo：连接实例属性](./demo/ConnectionAttributes.html)

| 属性名 | 说明                        | 类型      | 默认值 |
| :----- | :-------------------------- | :-------- | :----- |
| dc     | 获取 RTCDataChannel 实例    | [RTCDataChannel](https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel) | -      |
| pc     | 获取 RTCPeerConnection 实例 | [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) | -      |
| event  | Connection 事件             | object    | -      |

## 3.2 方法(Methods)

> - [demo：和应用双向通信](./demo/BothWayCommunication.html)
>   - 应用需要集成相应插件：[ue4](https://vip.ctcdn.cn/help/10011432/10014155/common/10016475)，[unity](https://vip.ctcdn.cn/help/10011432/10014155/common/10016476)
> - [demo：重置超时时间](./demo/Connection.resetTimeout.html)：常用于手动挂载键鼠时间时，避免有操作前提下超时断联的问题
> - [demo：调节码率](./demo/Connection.changeBandwidth.html)

| 方法名           | 说明                          | 参数      | 回调参数  |
| :--------------- | :-----------------------      | :-------- | :-------- |
| emitUIInteraction| 发送消息到应用程序            | string    | Promise\<boolean\> |
| send             | 发送消息到应用程序            | string\|Blob\|ArrayBuffer\|boolean | void |
| changeBandwidth  | 调节码率                      | number    | void   |
| controlAuthority | 转移控制权限(详细参考投屏示例)| [token]    | void   |

## 3.3 事件(Events)

> - [demo：连接实例 event](./demo/ConnectionEvent.html)

| 事件名               | 说明                 | 回调参数                                                               |
| :------------------- | :------------------- | :--------------------------------------------------------------------- |
| connect              | 可视化服务连接中     | -                                                                      |
| dataChannelConnected | 连接成功，资源加载中 | -                                                                      |
| close                | 连接中断回调         | [CloseEvent](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) |
| disconnect           | 信令断开回调         | string                                                                 |
| interaction          | 接收应用端返回数据   | string                                                                 |
| afk                  | 超时断开             | -                                                                      |

# 四、播放管理(LivePlayer)

```js
const launch = await RCRLaunch({ ... });
await launch.automata(container);
const livePlayer = launch.getPlayer();
```

## 4.1 属性(Attributes)

> - [Demo：播放实例属性](./demo/LivePlayerAttributes.html)

| 属性名        | 说明                              | 类型                | 默认值 |
| :------------ | :-------------------------------- | :------------------ | :----- |
| videoElement  | 播放实例节点                      | [HTMLVideoElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement) | -      |
| playerElement | 播放实例父级容器                  | [HTMLDivElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement)     | -      |
| videoVolume   | 获取 video 当前音量值（应用声音,ios设备永远返回1） | number    | 0      |

## 4.2 方法(Methods)

> - [Demo：播放实例设置加载文字、音量、背景图](./demo/LivePlayerMethods.html)
> - [Demo：播放实例设置显示模式和重置](./demo/LivePlayerType.html)

| 方法名                      | 说明                                   | 参数                             | 回调参数 |
| :-------------------------- | :------------------------------------- | :------------------------------- | :------- |
| handleChangeCanResize       | 设置播放实例显示模式自适应             | boolean                          | void     |
| resizePlayer                | 初始化播放实例显示模式                 | -                                | void     |
| showLoadingText             | 设置加载中文字                         | string                           | void     |
| showTextOverlay             | 设置播放页遮盖文字                     | string                           | void     |
| setVideoVolume              | 设置 video 播放音量值（应用声音）      | number                           | void     |
| handleChangeBgImage         | 设置应用加载背景图                     | string                           | void     |
| handleChangeOrientationLock | 移动端调节旋转                         | boolean(false:横屏\|true:竖屏)   | void     |
| handleChangeLandscapeType   | 设置显示模式                           | number(1:自适应\|2:拉伸\|3:裁剪) | void     |

# 五、鼠标键盘控制相关接口(可选扩展)

当您需要对鼠标、键盘进行定制时（eg：组合键、改键映射），可以使用下面的能力。

## 5.1 键盘事件(Keyboard)

> - [Demo：自定义组合键](./demo/CustomCombinationKey.html)

```js
import { RCRLaunch, Keyboard } from 'ecloud-rcr';
const launch = await RCRLaunch({
  ...
  extendOptions: {
    eventOption: {
      enableKeyBoard: false, // 关闭默认键盘挂载
    },
    onPlay() {
      // 处理键盘映射
    },
  },
});
await launch.automata(container);
const connection = launch.getConnection();

// eg：发送键盘事件
connection.send(new Keyboard(27, false, false, false, false, false, false, true).dumps());
```

**参数说明：**

| 属性    | 说明       | 类型    |
| :------ | :--------- | :------ |
| keycode | 键码       | number  |
| alt     | 按下 alt   | boolean |
| shift   | 按下 shift | boolean |
| ctrl    | 按下 ctrl  | boolean |
| nlock   | 按下 nlock | boolean |
| clock   | 按下 clock | boolean |
| slock   | 按下 slock | boolean |
| down    | 按下       | boolean |

## 5.2 鼠标移动事件(MouseMove)

```js
import { MouseMove } from 'ecloud-rcr';
connection.send(new MouseMove(100, 300, 3, 4).dumps());
```

**参数说明：**

| 属性 | 说明 | 类型   |
| :--- | :--- | :----- |
| x    | x    | number |
| y    | y    | number |
| dx   | dx   | number |
| dy   | dy   | number |

## 5.3 鼠标按钮事件(MouseButton)

```js
import { MouseButton } from 'ecloud-rcr';
connection.send(new MouseButton(1, true).dumps());
```

**参数说明：**

| 属性            | 说明                       | 类型    |
| :-------------- | :------------------------- | :------ |
| mouseButtonType | left = 1,middle=2, right=3 | number  |
| down            | 按下                       | boolean |

## 5.4 缩放事件(WheelScroll)

```js
import { WheelScroll } from 'ecloud-rcr';
connection.send(new WheelScroll(10, true).dumps());
```

**参数说明：**

| 属性    | 说明    | 类型    |
| :------ | :------ | :------ |
| step    | step    | number  |
| forward | forward | boolean |

## 5.6、触控事件(TouchSet)

```js
import { TouchSet } from 'ecloud-rcr';
connection.send(new TouchSet(0, [{ x: 10, y: 10, id: 1 }]).dumps());
```

**参数说明：**

| 属性      | 说明                                                               | 类型         |
| :-------- | :----------------------------------------------------------------- | :----------- |
| touchType | down = 0,update=1, up=2                                            | number       |
| touchList | TouchPoint:{x: number y: number id: number} //id：touch identifier | TouchPoint[] |

## 5.7、文本传输事件(TextInput)

> - [demo：聚焦应用输入框，发送内容](./demo/TextInput.html)

```js
import { TextInput } from 'ecloud-rcr';
connection.send(new TextInput('test text').dumps());
```

**参数说明：**

| 属性 | 说明                                                   | 类型   |
| :--- | :----------------------------------------------------- | :----- |
| text | 聚焦应用输入框时能够快速发送内容，比如实现复制粘贴功能 | string |

## 5.8、鼠标控制管理相关(PointerManager)

> - [demo：设置鼠标样式](./demo/PointerManager.html)

```javascript
import { PointerManager } from 'ecloud-rcr';
new PointerManager(target);
```

**参数说明：**

| 属性   | 说明                 | 类型        |
| :----- | :------------------- | :---------- |
| target | 宿主容器             | HTMLElement |
| onExit | 全局退出或者中断回调 | () => void  |

**属性说明：**

| 属性名 | 说明     | 类型                                         | 默认值 |
| :----- | :------- | :------------------------------------------- | :----- |
| cursor | 光标实例 | base64: string, show: boolean, x: number, y: number | cursor |

**方法说明：**

| 方法名    | 说明              | 参数   | 回调参数 |
| :-------- | :---------------- | :----- | :------- |
| setCursor | 设置鼠标大小/形状 | Cursor | void     |
| destory   | 销毁              | -      | void     |

# 六、常用问题

## 6.1 接入微信小程序

- 步骤
	1. 使用 jssdk 开发，打包成 html 部署到线上(生产环境需要域名解析)
	2. 微信小程序通过 `web-view` 嵌入 html 地址(域名链接)
- 开发 html 时候，微信(小程序)环境需要注意监听 `WeixinJSBridgeReady` 事件，然后开始对接 jssdk

```javascript
window.addEventListener('DOMContentLoaded', () => {
  if (navigator.userAgent.includes('miniProgram') || navigator.userAgent.includes('MicroMessenger')) {
    //微信浏览器/微信小程序环境
    document.addEventListener('WeixinJSBridgeReady', bootstrap, false);
  } else {
    bootstrap();
  }
});
```

## 6.2 WebRTC 调试参考

- 方法1：在任一浏览器标签地址打开 `chrome://webrtc-internals`，即可看到 WebRTC 的完整调试数据
- 方法2：调⽤ `Launcher showDashboard`⽅法，显示仪表盘推流性能数据(注：空数据情况下记得调⽤ `launcher toggleStatistics` ⽅法切换状态)
- 方法3：调⽤ `Launcher exportLog` ⽅法，导出 webrtc-internal s统计数据(注：空数据情况下记得调⽤ `launcher toggleStatistics` ⽅法切换状态)

## 6.3 接入多点触控需求（UE4）

1. 接入 3DCAT 的 [UE4 插件](https://vip.ctcdn.cn/help/10011432/10014155/common/10016475)
2. 在后台管理->应用详情->设置->开启多点触控。

## 6.4 投屏分享功能

功能说明：

- 投屏交互分为主控端、观看端
- 主控端需要使用投屏 appKey 初始化应用
- 主控端生成观看端链接
- 主控端可以赋予/回收临时操控权给观看端（需要创建投屏链接时勾选该功能）

主控端示例：

```js
const launch = await RCRLaunch({
  appKey, // 投屏专用 appKey ，需要先增加投屏链接获取
  address, // 投屏需要指定集群地址，和观看端保持同集群
  baseOptions: {
    startType: 3, // 投屏链接
    isCastScreenMaster: true,
  },
  extendOptions: {
    onMount: () => {
      // 获取投屏观看端临时 appKey ，拼接投屏地址
      // 投屏地址可以直接使用当前页面（定义 query 参数来区分端），或使用独立的分享页面来进行物理分隔
      copyUrl = `${window.location.origin}${window.location.pathname}?appKey=${key}&type=share`;
    },
    toolOption: {
      // 生成投屏的功能按钮
      extendTools:[
        {
          icon: "your icon",
          text: "访问邀请",
          platform: 3,
          onClick: () => { 
            // 复制生成的投屏地址
          },
        },
      ],
    },
  },
});

connection.event.clientInfo.on((data) => {
  const list = data.clients || [];

  list.forEach((client) => {
    const { nickname, isControlAuthority, token } = client;
    // 渲染投屏用户列表，注意：
    // 1、仅主控端可以进行权限转移
    // 2、列表样式用户自行定义

    // 判断当前用户是自己
    if (token === launch.clientToken) {
      launch.handleChangeSubscribe(isControlAuthority); // 切换猫头工具的控制权
      isControlAuthority ? launch.handleSubscribe(videoEle) : launch.handleUnsubscribe(); // 订阅交互 or 取消订阅交互
    }
  });
});
```

观看端示例：

```js
const bootstrap = async (nickname) => {
  try {
    const launch = await RCRLaunch({
      // appKey, // 不需要填，默认从 query 中读取观看端 appKey
      address, // 需要显式指定集群地址，确保和主控端在同一集群
      baseOptions: {
        startType: 3, // 投屏链接
        isCastScreenMaster: false,
        nickname: nickname, // 分享端必填
      },
    });
    connection.event.clientInfo.on((data) => {
      // 
    });
  } catch (error) {
    console.error(error);
  }
};

// 分享端要先拿到分享信息，渲染一个需要输入 nickname 的界面给用户填写，填写后再启动应用
```
