npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vmaos/webrtc-sdk

v0.3.2

Published

Framework-neutral WebRTC cloud device SDK

Readme

WebRTC SDK

在 Web 页面中连接和控制云机,支持音视频、鼠标、触摸与键盘操作。适用于常见前端框架和原生 H5。

从 0.2 升级到 0.3

0.3 对公开导出名、浏览器全局对象、播放器 CSS 类和帧事件名进行了调整。已有集成需要按照本文的 WebRtcSdk、WebRTCSDK、webrtc-* 类名及 webrtc-frame 事件更新导入、全局调用、样式选择器和事件监听后再升级;连接票据获取方式不变。

安装

npm install @vmaos/webrtc-sdk

连接云机

AK/SK 不能放到前端项目中,必须由业务后端调用 OpenAPI。

  1. 后端校验用户权限,调用 POST /open-api/phone/connection-ticket,请求体为 {"phoneCode":"云机编号"}。
  2. 前端接收后端返回的连接票据,用 SDK 初始化云机画面。
<div id="cloud-device" style="width: 360px; height: 640px"></div>
import { WebRtcSdk } from "@vmaos/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 接入时都应保留这个选项。

  1. 连接后在云机中打开可编辑的文本框,再将鼠标移入云机画面,使 SDK 的输入框取得焦点。
  2. 使用本机输入法输入中文、日文或韩文,并确认候选词。组合过程在浏览器本地完成,确认后的文字通过文本通道发送给云机。
  3. 重音字符、普通字符同样按文本输入;退格、回车和方向键等操作使用按键通道。鼠标离开画面后,键盘回到本地页面。

输入能否出现在目标应用中,还取决于云机内文本框的焦点、字符支持及设备端文本通道实现。真实连接时可依次检查 你好、こんにちは、안녕하세요、é、普通英文、退格和粘贴,确认候选词不会重复输入。

自定义 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 "@vmaos/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 '@vmaos/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,客户需使用包含这些方法的新发布包。