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

@watertreestar/voip-sdk

v0.1.3

Published

Browser SDK for voip-edge device sessions

Readme

voip-edge 浏览器 SDK

@watertreestar/voip-sdk 用于在浏览器中控制一个受管 SIP 设备:建立信令和媒体通道、监听设备/通话状态、播放音频、申请讲话权、控制录音。一个 CallClient 只能对应一个 deviceId;多个设备必须使用多个实例。

文档

| 文档 | 说明 | | --- | --- | | 快速开始 | 安装、创建客户端、连接和退出。 | | 状态与讲话权 | 设备、SIP、媒体和 Talk Lease 状态;何时可申请讲话权。 | | API 与事件 | 配置项、方法、事件与多设备管理。 | | 指标指南 | 指标获取方式、字段含义、Prometheus 输出及 WS PCM / WebRTC 的适用范围。 |

安装

npm install @watertreestar/voip-sdk

SDK 使用浏览器原生 WebSocketRTCPeerConnection 和媒体 API。认证应通过 WebSocket 握手、Cookie、短期 token 或反向代理完成;不要将用户身份或任意 SIP target 当作 SDK 参数发送。

最小示例

import { CallClient } from "@watertreestar/voip-sdk";

const client = new CallClient({
  wsUrl: "wss://edge.example.com/ws",
  deviceId: "gate-01",
  transport: "webrtc",
});

client.on("device_state", (state) => console.info("设备状态", state));
client.on("call_state", (state) => console.info("通话状态", state));
client.on("talk_state", (state) => console.info("讲话权状态", state));
client.on("error", (error) => console.error(error));

await client.join();

// 建议确认 SIP 已接通后再允许用户点击“讲话”。
if (client.deviceState?.callState === "connected") {
  await client.acquireTalk();
}

WS 媒体反向代理路径

服务端返回的 WS 媒体地址通常是相对路径(如 /media?ticket=...)。当它需要经过带路径前缀的公开反向代理时,传入 mediaBaseUrl。SDK 保留该路径前缀,并将 HTTP(S) 协议转换为浏览器可连接的 WS(S) 协议:

const client = new CallClient({
  wsUrl: "ws://edge.internal/socket",
  mediaBaseUrl: "https://a.com/a/b",
  deviceId: "gate-01",
  transport: "ws",
});

// 服务端的 /media?ticket=... 会连接到
// wss://a.com/a/b/media?ticket=...

录音浏览器预览

管理 API 创建的播放地址是短期签名的相对路径,不需要把管理认证信息放到 <audio> 请求中。用 SDK 将其与信令地址拼接为浏览器可访问的 HTTP(S) URL:

const previewURL = client.resolvePlaybackURL(playback.url);
audio.src = previewURL;

也可以在尚未创建 CallClient 时使用 resolvePlaybackURL(wsUrl, playback.url)。签名 URL 会在服务端指定的到期时间后失效。

若浏览器访问的公开地址与 SDK 信令地址不同(例如信令使用内网地址),创建客户端时传入 publicBaseURLclient.resolvePlaybackURL() 将优先使用它:

const client = new CallClient({
  wsUrl: "ws://edge.internal:8080/ws",
  publicBaseURL: "https://preview.example.com",
  deviceId: "gate-01",
});

join() 只有在服务端确认 session 且媒体通道就绪后才 resolve。检查 selectedTransport 获取实际建立的传输;Edge 当前不会在首选 WebRTC 失败时自动降级到 WS PCM。

浏览器啸叫抑制

默认 BrowserPCMAudioAdapter 在 WS PCM 上行链路启用窄带啸叫抑制。它在浏览器 AudioWorklet 内检测麦克风中的持续单频反馈,并在音频发送给 Edge 前应用 notch filter。它只用于近距离外放的保护层,不能替代耳机、降低设备扬声器音量或正确摆放麦克风。

const client = new CallClient({
  wsUrl: "wss://edge.example.com/ws",
  deviceId: "gate-01",
  transport: "ws",
  feedbackSuppression: {
    enabled: true,
    mode: "balanced",
  },
});

// 发生持续啸叫时,切换为更宽频、更强的抑制与上行 ducking。
client.setFeedbackSuppressionMode("aggressive");

// 可按产品 UI 开关控制。
client.setFeedbackSuppression(false);
  • balanced:默认值,使用较窄的 notch,优先保留自然人声。
  • aggressive:使用较宽的 notch;检测到持续反馈时,临时将浏览器上行衰减约 18 dB,反馈停止后平滑恢复。近距离扬声器外放时推荐此预设。
  • getPCMAdapterMetrics() 返回 feedbackSuppressionEnabledfeedbackSuppressionModefeedbackNotchFrequenciesfeedbackDetectionsfeedbackUplinkAttenuationDb,可用于 UI 与现场诊断。

setPlaybackActive()setPlaybackOutputDevice() 同时适用于 WS PCM 和 WebRTC;传输层只在创建客户端时指定。播放输出设备为 default 或空值时,SDK 不调用 setSinkId(),直接使用系统默认扬声器。若指定的输出设备在运行期间断开并触发 NotFoundError,SDK 会自动回退到系统默认输出。

若浏览器拒绝即时选择扬声器的权限,setPlaybackOutputDevice() 会 reject,并同步触发明确的回调事件。媒体尚未就绪时,设备选择会延后应用;该阶段的所有异常都通过 playback_error 通知:

client.on("playback_output_permission_denied", ({ outputDeviceId, error }) => {
  console.warn(`没有权限选择输出设备 ${outputDeviceId}`, error);
  // 提示用户授权,或改用 client.setPlaybackOutputDevice("default")。
});

client.on("playback_error", ({ operation, outputDeviceId, error }) => {
  console.warn(`播放${operation}失败,设备:${outputDeviceId}`, error);
});

try {
  await client.setPlaybackActive(true);
  await client.setPlaybackOutputDevice("speaker-01");
} catch (error) {
  // 初始化或设备选择失败;对应事件也会同步触发。
}

当前公开 API 仅支持 enabled 和两种 mode 预设;检测阈值、扫描频段、notch Q 值和 ducking 幅度是 SDK 内部实现,不支持逐项配置。这些参数互相影响,单独暴露会使误配置导致误抑制人声或无法停止反馈。需要新的现场预设时,请基于设备、麦克风、扬声器位置和状态指标评估后扩展 SDK。

保留 Talk Lease 的本地静音

申请 Talk Lease 后,可停止本地麦克风上行但不释放 lease;这适用于暂时不说话、仍需保留讲话权的场景。WS PCM 与 WebRTC 使用相同 API:

await client.acquireTalk();
await client.muteMicrophone();
// Talk Lease 仍有效,不会把讲话权交给其他会话。
await client.unmuteMicrophone();

microphone_muted 事件携带 { muted, leaseId },可直接驱动静音按钮。取消静音时,SDK 仅在 lease 未过期且仍由当前客户端持有时恢复上行。

关键约束

  • connect() 仅连接 /wsjoin() 才创建设备 session 和媒体。
  • 设备离线时 join() 仍可能成功,随后等待 device_statecall_state 更新。
  • 协议允许已加入的会话调用 acquireTalk(),但产品 UI 应仅在 deviceState.callState === "connected" 时开放讲话按钮。未接通时申请没有有效的 SIP 上行媒体路径。
  • 获得 TalkLease 后才可开始采集或发送上行音频;lease 失效、释放或被抢占后必须立即停止。
  • 讲话权由设备维度独占。talk_busy 表示其他 session 正在讲话;管理员才可以使用 acquireTalk(true) 抢占。

详见 状态与讲话权