npm package: @nextcas/stream
Description: ue串流sdk
Author: wehome
Latest version: 0.0.1-beta.32

## 《串流 SDK》虚拟人开发文档

### 简介

“3D 超写实/高模卡通、2D 高保真数字人、神经网络数字人”等在业务集成上有几大特点：“渲染设备要求较高”、“需要对 UE/Unity/深度学习有较为不错的编程经验”、“开发周期长”，为了降低企业对这类数字人集成难度，我们开放了“串流 SDK 数字人”。串流数字人主要满足以下两大使用场景：<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;“云端串流”，此串流方式可以跨越任何设备(支持 WebRTC 即可)，在设备支持联网的情况下快速将高质量的数字人带入小程序、移动端、便携设备、PC/Web 等各种性能参差不齐的终端，是最轻量级的“瘦客户端”数字人集成方案。<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;“内网串流”，此串流方式限定为局域网内串流，走NextHuman 的SAAS版本逻辑，在 SDK 调用前，需要在局域网中一台 Windows 宿主机上安装了"NextHuman 的本地客户端"(SAAS 版或离线版皆可），且登陆账号或版本开通了“本机串流”的权限，当进入串流模式后，此时该机器就可以充当一台局域网内的“云渲染服务器”，同一局域网内的设备可通过串流 SDK 进行数字人串流和数字人操控，该终端模式目前只支持一路并发渲染。<br/>

### 示例

我们开放了一个串流 Demo 到 Github 仓库，帮助大家快速构建自己的串流数字人应用，当然也欢迎外部的开发者朋友向我们提交一些有趣的 Demo，代码开放仓库在[Github](https://github.com/WeHome007/NextCAS-Stream)

### 快速开始

##### 1.SDK 安装

```bash
npm install @nextcas/stream
```

##### 2.云端串流(初始化)

**特别注意：**使用云端串流的开发者，在开发过程中如果需要获取形象、环境等资源清单，可通过以下方式获取：<br/>
&nbsp;&nbsp;&nbsp;&nbsp;a)、超写实 3D 资产可以从 [NextHuman3D 数字人](https://nexthuman.cn/api-web/) 网站直接查看对应的资产 ID，并进行资产调用；<br/>
&nbsp;&nbsp;&nbsp;&nbsp;b)、如果是 2D 角色资产可从 [NextHuman2D 数字人](https://nexthuman.cn/live2d/)网站直接查看对应的资产 ID，并进行资产调用；<br/>
&nbsp;&nbsp;&nbsp;&nbsp;c)、使用 [道具管理接口](https://nexthuman.cn/developer/#/open/docs/daoju)查询不同类型的数字人/场景等资产 ID；

> 本文档示例中的道具 ID 只做调用参考

```typescript
const stream = await NextStream.createCloud({
      token: app.token,
      container: '#app',
      actorId: 'actor_102217',
      avatarId: 'avatar_63edcef5ea719833f2b1eaff',
      scene: {
        id: 'scene_63c53373ddb14647413c8f2a',
        type: 'scene',
      },
    })
```


参数说明：

| 参数名称         | 类型                                                    | require | 描述                                                                                     |
| ---------------- | ------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------- |
| container        | HtmlElement \| string                                   | true    | 挂载的 dom 容器                                                                          |
| avatarId         | string                                                  | true    | 形象 ID                                                                                  |
| actorId          | string                                                  | true    | 智能体 ID                                                                                |
| scene            | `'transparent'` \| `{type:('nus'\|'scene',id:string}`   | true    | transparent:背景透明；场景                                                               |
| resolutionRatio  | '720p'  \|  '1080p'                                     | false    | 清晰度                                                            |
| chromakeyOptions | `{similarity?:number,smoothness?:number,spill?:number}` | false   | 透明背景时的自定义扣绿参数，默认值 `{similarity:  0.37,smoothness:  0.05,spill: 0.05, }` |
| onRender         | `()=>void`                                              | false   | 初始化成功回调                                                                               |
| onError          | `(error:string)=>void`                                  | false   | 初始化失败回调                                                                               |
| autoRender          | `boolean`                                  | false   | 是否自动播放（默认静音，可手动调用unMute取消静音）                                                                               |

##### 3.内网串流(初始化)

**特别注意：**
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;a)、本地串流目前只提供了超写实 3D 的推流终端，如果是需要对 2D 数字人进行本机串流的客户，可具体咨询官方客服。<br/>
&nbsp;&nbsp;&nbsp;&nbsp;b)、开发时请先联系官方客服获取本地串流的客户端下载地址并卡通本机串流权限，客户端安装好之后，在“系统托盘”中，有一个对应客户端的图标，通常存在两种以上的模式：Studio 终端、本机串流。Studio 模式通常用于开发者去创建形象、智能体、环境等资产，并查看资产 ID 以便于在串流 SDK 中进行调用，当要使用串流 SDK 进行调用时，请务必将模式切换到“本机串流“模式，否则 SDK 无法正常串流。<br/>
&nbsp;&nbsp;&nbsp;&nbsp;c)、本地串流 SDK 调用之前，建议使用“Studio 终端”进入“虚拟直播”**提前将 SDK 要访问的形象/环境资产下载**完成，这样串流 SDK 访问会更流畅，否则 SDK 首次加载形象会等待很久。

> 本文档示例中的道具 ID 只做调用参考

```typescript
import { NextStream, PresetCameraConfig } from "@nextcas/stream";

const stream = await NextStream.createLocal({
  ip: "192.168.20.157", //客户端所在局域网IP，如果是渲染应用和客户端在同一机器，使用127.0.0.1即可
  actorId: "actor_102217",
  container: "#app",
  avatarId: "avatar_63edcef5ea719833f2b1eaff",
  scene:'transparent'
  chromakeyOptions:{
      similarity:  0.37,
      smoothness:  0.05,
      spill: 0.05,
    }
  }
});
// 设置初始镜头
stream.setCamera(PresetCameraConfig.Full);

// 启动
stream.start();
```

参数说明：

| 参数名称         | 类型                                                    | require | 描述                                                                                     |
| ---------------- | ------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------- |
| container        | HtmlElement \| string                                   | true    | 挂载的 dom 容器                                                                          |
| avatarId         | string                                                  | true    | 形象 ID                                                                                  |
| actorId          | string                                                  | true    | 智能体 ID                                                                                |
| scene            | `'transparent'` \| `{type:('nus'\|'scene',id:string}`   | true    | transparent:背景透明；场景                                                               |
| resolutionRatio  | '720p'  \|  '1080p'   \|  '2k'  \|  '4k'                 | false    | 清晰度                                                            |
| ip               | string                                                  | true    | 需要连接的客户端的 ip 地址                                                               |
| chromakeyOptions | `{similarity?:number,smoothness?:number,spill?:number}` | false   | 透明背景时的自定义扣绿参数，默认值 `{similarity:  0.37,smoothness:  0.05,spill: 0.05, }` |
| onRender         | `()=>void`                                              | false   | 初始化成功回调                                                                               |
| onError          | `(error:string)=>void`                                  | false   | 初始化失败回调                                                                               |
| autoRender          | `boolean`                                  | false   | 是否自动播放（默认静音，可手动调用unMute取消静音）                                                                               |



### SDK 方法

> 本文档示例中的道具 ID 只做调用参考


#### start()
**说明** ：开启渲染

```typescript
//接口定义
start():void;
```

```typescript
//调用示范，注意：start必须为交互（鼠标或者键盘事件）触发；
try {
  const stream = await NextStream.createLocal({
  ip: "192.168.20.157", //客户端所在局域网IP，如果是渲染应用和客户端在同一机器，使用127.0.0.1即可
  actorId: "actor_102217",
  container: "#app",
  avatarId: "avatar_63edcef5ea719833f2b1eaff",
  // scene:'transparent'
  scene: {
    id: "scene_63c53373ddb14647413c8f2a",
    type: "scene",
    },
  });

 document.addEventListener("click",()=>{
  stream.start()
 })
} catch {

}
```

#### close()

**说明** ：关闭连接

```typescript
//接口定义
close():void;
```

```typescript
//调用示范，
stream.on('close', () => {
  // 链接已关闭
})
stream.close()
```

#### enableViewControl()
**说明** ：开启视角控制；通过 鼠标+shift转动视角

```typescript
//接口定义
enableViewControl():void;
```

```typescript
//调用示范，
stream.enableViewControl()
```


#### closeViewControl()
**说明** ：关闭视角控制

```typescript
//接口定义
closeViewControl():void;
```

```typescript
//调用示范，
stream.closeViewControl()
```

#### on()
**说明** ：事件监听，参考最下方事件列表
```typescript
//接口定义
on(eventName:string,cb:(args:any[])=>any):void;
```

```typescript
//调用示范，
stream.on('close', () => {
  // 链接已关闭
})
```


#### ask()

**说明** ：AI 问答

```typescript
//接口定义
ask(text:string,option?:{
  // 语速 默认值0 范围 -100-100
  speed?:number,
  // 音量 默认值50 范围0-100
  volume?:number,
  // 开始回调
  onStart?: () => void,
  // 结束回调
  onEnd?: () => void
}) :{reqId:string,observer:Observable};
//reqId:当前问答的唯一Id
//observer Rxjs中的可观察对象 参考 https://rxjs.dev/guide/observer
```

```typescript
//调用示范，参数为问题文本
const { observer, reqId } = stream.ask(text);
observer?.subscribe((reply) => {
  console.log(res.content, res.reqId);
});
```

#### speak()

**说明** 主动触发虚拟人演讲

```typescript
//接口定义
speak(text: string,option?:{
  // 语速 默认值0 范围 -100-100
  speed?:number,
  // 音量 默认值50 范围0-100
  volume?:number,
  // 开始回调
  onStart?: () => void,
  // 结束回调
  onEnd?: () => void
});
```

```typescript
//调用示范
stream.speak("你好，我是来自潘多拉星球的nexthuman");
```

#### stopSpeak()

**说明** 主动停止虚拟人说话

```typescript
//接口定义
stopSpeak();
```

```typescript
//调用示范
stream.stopSpeak();
```

#### createSpeakStream()

**说明** 创建流式的虚拟人演讲

```typescript
//接口定义
createSpeakStream(option?:{
  // 语速 默认值0 范围 -100-100
  speed?:number,
  // 音量 默认值50 范围0-100
  volume?:number,
   // 开始回调
  onStart?: () => void,
  // 结束回调
  onEnd?: () => void
}):{
  next:(content:string)=>void,
  last:(content?:string)=>void
};
```

```typescript
//调用示范
speakStream.next(
  "NextHuman是一个消费级超写实数字人直播引擎,使用户一键实现自由搭配"
);
speakStream.next(
  "能够提供精致的妆容效果,满足用户的随心所欲变更妆容;提供丰富的私人衣柜"
);
speakStream.last("换装;独创的AI算法,实现对超写实微表情的完美复原。");
```


#### setCamera()

**说明** 设置镜头位置

```typescript
//接口定义
interface CameraConfig {
  // 光圈
    currentAperture: number;
    currentFocalBone: string;
    // 焦距
    currentFocalLength: number;
    // 是否启用平滑过去
    smooth: boolean;
    // 摄像机的3D空间位置
    location: {
        x: number;
        y: number;
        z: number;
    };
    // 摄像机的旋转参数
    rotation: {
        pitch: number;
        yaw: number;
        roll: number;
    };
}
setCamera(config: CameraConfig)
```

```typescript
//调用示范
import { PresetCameraConfig } from "@nextcas/stream";
stream.setCamera(PresetCameraConfig.Full);

//系统提供了几组常用预设镜头:Full(全身镜头)、Face(脸部镜头)、Upper(上半身镜头)、Lower(下半身镜头)、Footer(脚部镜头)
```

#### setAvatar()

**说明** 切换 Avatar

```typescript
//接口定义
async setAvatar(avatarId: string)
```

```typescript
//调用示范

stream.setAvatar("avatar_63edcef5ea719833f2b1eaff").then((_) => {
  console.log("切换成功");
});

//系统提供了几组常用预设镜头:Full(全身镜头)、Face(脸部镜头)、Upper(上半身镜头)、Lower(下半身镜头)、Footer(脚部镜头)
```

#### setAvatarPosition()

**说明** 设置 Avatar位置

```typescript
//接口定义
// position内的单位cm
interface Position{
  x:number,
  y:number,
  z:number
}

async setAvatarPosition(position: Position)
```

```typescript
//调用示范

stream.setAvatarPosition({x:1,y:1,z:0}).then((_) => {
  console.log("设置成功");
});

//系统提供了几组常用预设镜头:Full(全身镜头)、Face(脸部镜头)、Upper(上半身镜头)、Lower(下半身镜头)、Footer(脚部镜头)
```


#### setBundle()

**说明** 切换 Bundle，（切换动作；切换服装；切换装扮）

```typescript
//接口定义
async addBundle(bundleId: string, category: string)

// category，参考（https://nexthuman.cn/developer/#/open/docs/daoju） 支持所有道具分类，
```

```typescript
//调用示范
// 切换角色 (id替换为真实id)
stream.addBundle("avatar_63edcef5ea719833f2b1eaff", "avatar").then((_) => {
  console.log("切换成功");
});
```

#### setFaceani()
**说明** 设置虚拟人表情动画

```typescript
//接口定义
setFaceani(
  // 动画ID
  faceaniId: string,
  options?: {
    // 是否循环播放，默认值false
    loop: boolean
    // 开始播放回调
    onStart?: () => void
    // 结束播放回调
    onEnd?: () => void
  },
  // 返回值为停止动画函数
):()=>void
```

```typescript
//调用示范
const stop = stream.setFaceani(
  'faceani_63c28ab3cbb05306e388d14b',
  {
    loop: false,
    onStart() {
      console.log('动作开始')
    },
    onEnd() {
      console.log('动作结束')
    },
  },
)
if (stop) {
  setTimeout(() => {
    stop()
  }, 3000)
}
```


#### setSkeani()
**说明** 设置虚拟人身体动画

```typescript
//接口定义
setSkeani(
  // 动画ID
  skeaniId: string,
  options?: {
    // 是否循环播放，默认值false
    loop: boolean
    // 开始播放回调
    onStart?: () => void
    // 结束播放回调
    onEnd?: () => void
  },
  // 返回值为停止动画函数
):()=>void
```

```typescript
//调用示范
const stop = stream.value?.setSkeani(
  'skeani_64c8de84d3029c35f8f8edd4',
  {
    loop: false,
    onStart() {
      console.log('动作开始')
    },
    onEnd() {
      console.log('动作结束')
    },
  },
)
if (stop) {
  setTimeout(() => {
    stop()
  }, 3000)
}
```

#### playWriting()

**说明** 执行AI播报

```typescript
//接口定义
playWriting(
    id: string,
    options?: {
      // 播报开始回调
      onStart?: () => void
      // 播报结束回调
      onEnd?: () => void
      // 执行错误回调
      onError?: (msg: string) => void
    },
    // 返回值为停止播报函数
  ):()=>void
```

```typescript
// 调用示范
  const stop = stream.playWriting('writing_667a63a6689f975897b10783', {
    onStart: () => {
      console.log('开始ai播报')
    },
    onEnd: () => {
      console.log('ai播报结束')
    },
  })

  setTimeout(() => {
    stop && stop()
  }, 4000)
```

#### setScene()
说明:设置场景、环境

```typescript
//接口定义
setScene(
  type: 'nus' | 'scene',
  id: string  //场景id
):()=>void
```

```typescript
//调用示范
stream.value?.setScene(
  {type:"nus",id:"62f0b5aa8f1f103ca2870bcc"},
)
```


#### resetScene()

**说明** 重置场景设置

```typescript
//接口定义
resetScene():void;
```


#### setHeadFollow()

**说明** 设置头部跟随

```typescript
//接口定义
setHeadFollow(flag: boolean):Promise<void>
```

```typescript
// 调用示范
stream.setHeadFollow(true).then(()=>{
  console.log("头部跟随开启成功")
})
```



#### setEyeballFollow()

**说明** 设置眼球注视

```typescript
//接口定义
setEyeballFollow(flag: boolean):Promise<void>
```

```typescript
// 调用示范
stream.setEyeballFollow(false).then(()=>{
  console.log("眼球注视开启成功")
})
```

#### mute()

**说明** 设置静音

```typescript
//接口定义
mute():void
```

#### unMute()

**说明** 取消静音

```typescript
//接口定义
unMute():void
```


#### getToken()

**说明** 获取token

```typescript
//接口定义
getToken():string;
```

```typescript
//调用示范
const token = stream.getToken()
```


#### 事件列表
| 事件名称 | 事件参数 | 描述     |
| -------- | -------- | -------- |
| close    | `null`   | 连接断开 |