@izuanke/webrtc-sdk
v0.3.2
Published
Framework-neutral WebRTC cloud device SDK
Readme
WebRTC SDK
在 Web 页面中连接和控制云机,支持音视频、鼠标、触摸与键盘操作。适用于常见前端框架和原生 H5。
安装
npm install @izuanke/webrtc-sdk连接云机
AK/SK 不能放到前端项目中,必须由业务后端调用 OpenAPI。
- 后端校验用户权限,调用
POST /open-api/phone/connection-ticket,请求体为{"phoneCode":"云机编号"}。 - 前端接收后端返回的连接票据,用 SDK 初始化云机画面。
<div id="cloud-device" style="width: 360px; height: 640px"></div>import { WebRtcSdk } from "@izuanke/webrtc-sdk";
// ticket 为业务后端返回的 connection-ticket 响应中的 data。
const sdk = new WebRtcSdk({
mountedElement: "#cloud-device",
connectConfig: {
signalServerUrl: ticket.signalServerUrl,
roomId: ticket.roomId,
token: ticket.token,
hostTurn: ticket.turnServerUris,
spareTurn: [],
signAgain: false,
enableLogger: false,
},
// 开启浏览器输入法组合输入,适用于中文、日文、韩文等。
enabledIMEKeyboard: true,
});
sdk.init();
// 离开页面或组件卸载时执行:
// sdk.destroy();票据短期有效且仅可使用一次,请在 expiresAt 前连接;重连时重新获取。票据仅在内存中使用,不写入 URL、日志或浏览器存储。生产页面使用 HTTPS/WSS。
需要额外配置 STUN 服务时,可分别使用 stunServerUri、stunServerUriSecondary 和 stunServerUriTertiary。未设置的地址会被跳过,已设置的地址按上述顺序传给浏览器。
键盘与多语言输入
上面的连接示例通过 enabledIMEKeyboard: true 开启输入法。使用前端框架、原生 H5 或 script / IIFE 接入时都应保留这个选项。
- 连接后在云机中打开可编辑的文本框,再将鼠标移入云机画面,使 SDK 的输入框取得焦点。
- 使用本机输入法输入中文、日文或韩文,并确认候选词。组合过程在浏览器本地完成,确认后的文字通过文本通道发送给云机。
- 重音字符、普通字符同样按文本输入;退格、回车和方向键等操作使用按键通道。鼠标离开画面后,键盘回到本地页面。
输入能否出现在目标应用中,还取决于云机内文本框的焦点、字符支持及设备端文本通道实现。真实连接时可依次检查 你好、こんにちは、안녕하세요、é、普通英文、退格和粘贴,确认候选词不会重复输入。
自定义 video / WebRtcClient 接入
直接使用底层 WebRtcClient 时,没有 SDK 播放器代为管理输入框,需要自行接入可聚焦的 textarea 和浏览器输入事件。请在自己的页面中管理输入框焦点与事件生命周期。
- 点击或进入云机画面时让输入框取得焦点;隐藏输入框用尺寸和透明度处理,不能使用
display: none或disabled。 compositionstart开始组合,期间忽略物理按键和中间input;也检查KeyboardEvent.isComposing、keyCode === 229和InputEvent.isComposing。compositionend发送最终结果,并去重浏览器随后触发的同内容input。普通字符、重音键与 AltGr 产生的文字由input发送,避免同时转发对应的物理按键。- 退格、回车、方向键与快捷键通过按键通道发送;粘贴读取
text/plain,并阻止浏览器默认粘贴,避免重复发送。 - 离开画面、关闭连接或卸载组件时,移除监听器,清空组合状态和输入框,并释放焦点。
import { ChannelDataType, InputData, KeyEventData } from "@izuanke/webrtc-sdk";
// text 是已确认的输入法结果、普通文本或粘贴内容。
client.sendChannelData(ChannelDataType.ActionChinese, new InputData(text));
// keyCode / meta 来自 getKeyEventData(event),仅发送可识别的按键。
client.sendChannelData(ChannelDataType.ActionInput, new KeyEventData(keyCode, meta));ActionChinese 是现有协议的名称;前端传入的是文本,不应据此把组合输入限定为中文。自定义 video 已接入上述事件时,避免再为同一画面启用另一套键盘监听器。
常用操作
| 方法 | 功能 |
| --- | --- |
| goBack() / goHome() / goTask() | 返回 / 主页 / 最近任务 |
| click(x, y) | 编程点击指定位置,返回 Promise<void> |
| swipe(fromX, fromY, toX, toY, options?) | 编程滑动,返回 Promise<void> |
| drag(fromX, fromY, toX, toY, options?) | 编程长按后拖动,返回 Promise<void> |
| scroll(deltaY) | 滚轮滚动:正值向下、负值向上,返回是否发送成功 |
| volumeUp() / volumeDown() | 云机系统音量加 / 减,返回是否发送成功 |
| enableAudio() / disableAudio() | 开启 / 关闭音频 |
| sendClipboardData(text) | 向云机发送文本 |
| destroy() | 释放连接和页面资源 |
click()、swipe()、drag()、scroll()、volumeUp() 和 volumeDown() 从 0.3.1 起提供,请在连接成功后调用。点击和移动坐标使用 0–1 的归一化比例,参数与示例见下文。发送成功不代表云机已经执行完成;enableAudio() / disableAudio() 控制远程音频传输,与云机系统音量加减独立。
本地摄像头传入云机
云机应用打开摄像头后,SDK 自动响应云机的摄像头开关指令,申请本地摄像头并将视频通过当前 WebRTC 连接发送给云机。仅连接云机桌面不会提前申请摄像头;关闭云机摄像头或销毁连接时释放设备。该能力不申请麦克风权限,也不需要调用 RTMP 接口。
从 0.3.2 起,摄像头画面旋转后等比例铺满 640×480 输出并居中裁剪,避免宽高比不一致时由 SDK 添加黑边;部分画面边缘会被裁掉,不会拉伸画面。视频源自身已有的黑边不保证消除。
浏览器页面须使用 HTTPS 或 localhost,并允许摄像头权限。普通内网 IP 的 HTTP 页面不能采集本地摄像头;iframe 接入还需由宿主页允许摄像头访问。权限拒绝、设备缺失、占用或拔出通过独立摄像头错误事件反馈,保留已有云机桌面连接。
sdk.on(WebRtcSdk.EVENTS.CAMERA_STATE_CHANGED, ({ state, active }) => {
// state: idle / requesting / active / error;active 表示已有摄像头仍在发送。
// 可展示“等待摄像头授权”“正在传入云机”,切换失败时 active 仍可能为 true。
});
sdk.on(WebRtcSdk.EVENTS.CAMERA_ERROR, ({ code, message }) => {
// 展示 message,允许用户修正权限或设备后重试;不要因此销毁桌面连接。
});
// 云机开关由 SDK 自动处理;以下 API 供重试、设备选择或用户主动关闭使用。
await sdk.startCamera(); // 重试最近请求的摄像头
await sdk.switchCamera({ useBackCamera: false }); // 前置;true 为后置
// 用户选择外接设备时,可传入 navigator.mediaDevices.enumerateDevices() 得到的 deviceId。
await sdk.switchCamera({ deviceId: selectedDeviceId });
sdk.stopCamera();直接使用 WebRtcClient 时,方法相同,事件分别为 EmitType.cameraStateChanged 和 EmitType.cameraError。单控及群控主控均适用;纯 WebSocket 从控没有媒体连接,不提供摄像头上行。切换主控必须先停止旧客户端,避免旧云机继续使用本地摄像头。
可运行示例
配套示例提供两种前端框架、原生 H5 和 script / IIFE 四类独立教程,可直接粘贴 OpenAPI 返回的 JSON 连接云机。
H5 也可运行在支持标准 WebRTC 的 WebView 或桌面应用容器的渲染进程中;小程序请通过 H5 WebView 接入。
群控:主控视频与从控预览
ControlGroupClient 提供纯 WebSocket 从控操作通道,不创建 WebRTC。客户后端调用 POST /open-api/phone/group-connection-ticket,传入 {mainPhoneCode, followerPhoneCodes, durationMs},浏览器仅接收短期凭证。主从合计最多 100 台,须属于当前 AK 租户且在线运行。
main.ticket 用于创建一路 WebRtcClient。每个 followerGroups 元素创建一个客户端:
import { ControlGroupClient, ChannelDataType, KeyEventData } from '@izuanke/webrtc-sdk';
const client = new ControlGroupClient({
signalServerUrl: group.signalServerUrl,
roomTokens: Object.fromEntries(group.members.map(({ ticket }) => [ticket.roomId, ticket.token])),
});
client.on('ready', () => client.sendData(ChannelDataType.ActionInput, new KeyEventData(3, 0)));
client.on('failure', () => { /* 向后端重新申请凭证,再创建客户端 */ });
client.start();
// 退出或替换成员:
client.stop();
client.removeAllListeners();sendData 支持 ActionInput、ActionChinese、ClickData。主控操作还要通过自己的 WebRTC 数据通道发送,不可重复加入从控。切换前结束手势并停止旧客户端,向后端重新申请新主从集合的凭证;已消费的票据不能复用。
从控通过 POST /open-api/phone/screenshot-token 获得预览地址。仅可见时下载,各台下载解码完成后独立等待一秒,再请求下一张,并限制总并发。截图 Token 剩余不足 30 秒时重新申请;截图 Token 与连接票据不可互换。AK/SK 留在受控后端,票据和截图地址不进入日志、URL 导航参数或浏览器存储。
编程点击、滑动、拖动和滚轮
在 sdk.init() 并连接成功后,直接调用 SDK 实例方法:
await sdk.click(0.5, 0.5);
await sdk.swipe(0.5, 0.8, 0.5, 0.2, { durationMs: 300 });
await sdk.drag(0.2, 0.3, 0.8, 0.7, { holdMs: 500, durationMs: 500 });
const sent = sdk.scroll(120);
sdk.disableAudio();
sdk.enableAudio();| 方法 | 参数与返回 |
| --- | --- |
| click(x, y) | 归一化坐标;返回 Promise<void> |
| swipe(fromX, fromY, toX, toY, options?) | durationMs 缺省 300;返回 Promise<void> |
| drag(fromX, fromY, toX, toY, options?) | 按下后停留 holdMs 缺省 500,再移动 durationMs 缺省 500;返回 Promise<void> |
| scroll(deltaY) | 正值向下、负值向上;零值不发送;返回是否发送成功 |
坐标为云机当前画面中的 0–1 比例,左上角为 (0,0)、右下角为 (1,1),与播放器 CSS 尺寸和本地显示旋转无关。坐标必须为有限数值;移动时长为 1–60000 的整数毫秒,停留时长为 0–60000 的整数毫秒。滚轮沿用现有协议,仅传递方向,不传递像素距离。
编程触摸手势按调用顺序执行。手动触摸进行中时,编程手势拒绝;编程手势执行时暂停播放器的手动触摸输入。Promise 成功表示发送流程完成,不是 Android 执行回执;未连接、发送失败、连接中断或销毁时拒绝,尽力释放触点并取消排队操作,不自动重放。调用方应 await 或捕获拒绝。
鼠标位于云机画面内时,滚轮事件不会同时滚动网页;画面外保持页面滚动。disableAudio() / enableAudio() 控制远程音频传输,不修改 Android 系统音量。这些编程输入方法是新增能力,客户需使用包含这些方法的发布包,不能据此认为旧版包已经支持。
云机音量加减
连接成功后调用 sdk.volumeUp() 或 sdk.volumeDown(),每次分别发送一次 Android 音量加键(24)或音量减键(25)。返回boolean仅表示指令是否发送;未连接、通道关闭、发送失败或实例销毁时返回false,不排队、不自动重试,不代表系统音量已发生改变。
const sent = sdk.volumeUp();
const sentDown = sdk.volumeDown();控制的是云机当前活动音频流,由 Android 与前台应用决定;不调整本机播放器音量,也不提供当前音量百分比或精确音量设置。与 disableAudio() / enableAudio() 的音频传输开关独立,静音时调节系统音量不会自动恢复传输。真实系统音量和WebRTC采集声音是否变化需要分别验收。这是新增高层API,客户需使用包含这些方法的新发布包。
