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

@toolmain/jssip

v1.0.11

Published

- jssip 库的封装,加入中文错误提示和ice candidate策略

Readme

Overview

  • jssip 库的封装,加入中文错误提示和ice candidate策略

  • 支持一个 UA 多个 RTCSession 同时拨打和接听

  • ~~使用 vue~~

  • 库使用Typescript编写并提供 .d.ts 文件,可用在react或者vue环境中

Change log

v1.0.1 锁定jssip版本号为3.10.0

v1.0.3 修改useMedia::playUrl内部播放逻辑

v1.0.4 删除vue依赖

v1.0.5 解决IOS无法通过动态创建 audio 标签播放音频轨道

Usage 用法

包目录结构。使用时应包含所有文件

++jssip
--package.json
++dist
--index.js
--index.d.ts

由于浏览器的限制(WebRTC以及麦克风权限限制),使用时必须在https站点下

拨号流程

1.实例化SipWorker

2.通过实例方法postMessage进行 初始化 登录 拨号 挂断 静音 取消静音 暂停 取消暂停操作

3.通过实例方法on监听拨号后的事件,详细的事件可在该文档中的Type 拨号命令以及消息事件枚举部分查看

4.每当发起一次通话或者收到电话,jssip库会产生一个session会话,用户可以通过SipWorker类的on方法监听这个session

示例代码

import { SipWorker, cmd, on, CauseData } from "path/to/你放置该库的根路径"
import { ulid } from "ulid" // 或者 uuid
/**
 * SipWorker 是拨号类,必须先实例化
 */
const sip = new SipWorker()
/**
 * 初始化,拨号之前要初始化
 */
sip.postMessage(cmd.INIT, {
  /**
   * 
   * 传id或者HTMLAudioElement
   * 用于播放音频流
   *
   * ios中动态创建的audio标签无法播放音频
   * 安卓和PC可以动态创建标签,可以不传该字段
   */
  audio: document.getElementById("audioId"),
  viaTransport: "wss", // 固定wss
  uri: "sip:[email protected]", // 格式:sip:账号@拨号服务器IP
  sipWorkerid: ulid(), // 随机字符串
  sipServer: ["wss://192.168.1.1:8089/ws"],// 格式:wss://拨号服务器IP:端口/ws
  // 如果没有,留空数组
  sipIce: ["stun:192.168.1.1:3478"], // 格式:stun:拨号服务器IP:STUN端口
  /**
   * 自动注册,如果false,需要手动调用
   * sip.postMessage(cmd.LOGIN, null);
   */
  register: true,
  password: "your_account's password", // 密码
  realm: "192.168.1.1", // 格式:拨号服务器IP
})
/**
 * 登录
 */
sip.postMessage(cmd.LOGIN, null)
/**
 * 登出
 */
sip.postMessage(cmd.LOGOUT, null)
/**
 * 拨号
 */
sip.postMessage(cmd.CALL, {
  target: "10086",// 要拨打的号码
})
/**
 * 挂电话
 */
sip.postMessage(cmd.HANGUP, {
  isTimeout: false,
  sessionId: `sessionId`,
})
/**
 * 通话session状态发生变化
 * 收到电话或者拨号会触发此事件
 */
sip.on(on.ON_RTCSESSION_STATE, (res: CauseData) => {
    switch (res.msg) {
      case on.ON_SESSTION_ACCEPTED: // 已接受
      case on.ON_SESSTION_CONFIRMED: // 已接听
        break;
      case on.ON_SESSTION_FAILED: // 通话失败
      case on.ON_SESSTION_ENDED: // 通话结束
        break;
      case on.ON_NEWRTCSESSION: // 新的通话session到来
        //! 呼出
        if (res.sessionOrigin == "outgoing") {
          console.log("会话ID:",res.sessionId;)
        } else {
          console.log("会话ID:",res.sessionId;)
          const sessionId = res.sessionId;
          // console.log(">>>呼入", uSip.sip.sessionById(sessionId));
          const fromUser = uSip.sip.sessionById(sessionId).session.remote_identity.uri.user;
          console.log("呼入者:",fromUser)
          // 接听
          uSip.sip.postMessage(cmd.ANSWER, {
            sessionId,
          });
          // 挂断/拒接
          uSip.sip.postMessage(cmd.HANGUP, {
            sessionId,
          });
        }
    }
})
// 注册
sip.on(on.ON_REGISTERED, () => {
  console.log("已注册")
});
sip.on(on.ON_UNREGISTERED, () => {
  console.log("未注册")
});
// 静音
sip.on(on.ON_MUTE, () => {
  console.log("静音")
});
sip.on(on.ON_UNMUTE, () => {
  console.log("取消静音")
});
sip.on(on.ON_RESUME, () => {
  console.log("继续")
});
sip.on(on.ON_HOLD, () => {
  console.log("保持")
});

// 失败
sip.on(on.ON_DISCONNECTED, res => {
  /**
   * 触发原因:
   * 1. 由于服务器使用自签证书,需要手动访问拨号服务器的 https 站点手动验证,端口和地址为cmd.INIT时的sipServer字段值,并且wss改为https
   * 2. 由于网络环境不稳定等其他原因
   *
   */
  console.log("socket 连接失败",res)
});
sip.on(on.ON_REGISTRATIONFAILED, res => {
  console.log("socket 连接成功,但是sip注册失败",res)
});
// etc...

额外可供使用的工具

import { useMedia, useRetry } from "path/to/你放置该库的根路径"

// 播放音频流或者音频url
/**
export interface AutoMedia {
  playRemote: (media: readonly MediaStream[]) => void
  playUrl: (url: string) => void
  stopPlayUrl: () => void
}
 */
const media = useMedia()
// eg:
media.playUrl("xxx.mp3")
/**
export interface Retry {
  // 更新最大重试次数
  updateMaxRetry: (id: string, maxRetry: number) => void
  // 获取重试次数
  getRetryTime: (id: string) => number
  // 是否到达最大重试次数
  isMax: (id: string) => boolean
  // 重试,如果可以重试,返回true,否则返回false
  retry: (id: string) => boolean
  // 重置重试次数
  reset: (id: string) => void
  // 延迟重试
  // id 重试唯一id
  // cb 回调函数
  // increase 每次重试递增时间
  // 返回: 0:超过重试次数,大于0:下次重试时间
  delayRetry: (id: string, cb: () => void, increase?: number) => number
}
*/
const retry = useRetry()

Type 拨号命令以及消息事件枚举

// sip 操作命令
export declare enum cmd {
  INIT = "INIT", // 数据初始化操作
  LOGIN = "LOGIN", // 连接socket并登录sip服务器
  CALL = "CALL", // 拨打电话
  ANSWER = "ANSWER", // 接听电话
  LOGOUT = "LOGOUT", // 断开socket并且登出sip服务器
  HANGUP = "HANGUP", // 挂断电话
  HOLD = "HOLD", // 保持通话
  UNHOLD = "ONHOLD", // 取消保持通话,继续
  MUTE = "MUTE", // 静音
  UNMUTE = "UNMUTE", // 取消静音
  REGISTER = "REGISTER", // 注册sip服务
  SESSTION_DEL = "SESSTION_DEL", // 删除sessionId,并且挂电话
  UNREGISTER = "UNREGISTER", // 保持socket连接,但登出sip服务
  NOTIFY = "NOTIFY", // unused
  EMPTY = "EMPTY", // unused
}
// sip 消息事件
export declare enum on {
  ON_TIME_OUT = "Request Timeout", // 请求超时
  ON_REGISTERED = "registered", // 已注册
  ON_UNREGISTERED = "unregistered", // 未注册(或已登出)
  ON_REGISTRATIONFAILED = "registrationFailed", // 注册失败
  ON_REGISTRATIONEXPIRING = "registrationExpiring", // 注册即将过期,库会自动重新注册
  ON_CONNECTING = "connecting", // socket连接中
  ON_CONNECTED = "connected", // socket 已连接
  ON_DISCONNECTED = "disconnected", // socket 已断开
  ON_NEWRTCSESSION = "newRTCSession", // 新的通话,呼入或者呼出
  ON_RTCSESSION_STATE = "new_RTCSession_state", // 通话session的状态
  ON_SESSION_PROGRESS = "session_progress", // 183 session建立中,早期媒体(early media) 时期
  ON_SESSTION_CONNECTING = "session_connecting", // 通话线路正在连接
  ON_SESSTION_ACCEPTED = "session_accepted", // 通话已被接受
  ON_SESSTION_CONFIRMED = "session_confirmed", // 通话已确认(已接通)
  ON_SESSTION_ENDED = "session_ended", // 通话结束
  ON_SESSTION_FAILED = "session_failed", // 通话失败
  ON_SESSION_ANSWER_FAILED = "session_answer_failed", // 通话应答失败
  ON_RESUME = "onResume", // 已继续
  ON_HOLD = "onHold", // 已保持
  ON_MUTE = "onMute", // 已静音
  ON_UNMUTE = "onUnmute", // 已取消静音
  ON_EVENT = "event", // unused
  ON_NEWOPTIONS = "newOptions", // unused
  ON_SIPEVENT = "sipEvent", // unused
  ON_NEWMESSAGE = "newMessage", // unused
  ON_SESSION_RECALL = "session_recall", // unused
  ON_RE_REGISTERED = "re_register", // unused
  ON_RE_LOGIN = "re_login", // unused
  ON_SESSION_TIMEOUT = "session_req_timeout", // unused
  ON_LISTEN_EVENT = "call_event", // unused
}

// sip事件消息格式
export declare type CauseData = {
  /**
   * JsSIP实例id
   */
  id: string
  /**
   * sip账号
   */
  uri: string
  /**
   * 呼入时:呼叫方的uri
   */
  fromUri?: string
  /**
   * 事件ON_* on.ts 中声明
   */
  msg: string
  /**
   * 200 500 etc.
   */
  code: number
  /**
   *
   * 当CauseData由session发出时,该字段标识JsSIP每个UA下不同的RTCSession id
   */
  sessionId?: string
  /**
   * 当CauseData由session发出时,该字段标识`主叫`或者`被叫`
   */
  sessionOrigin?: "incoming" | "outgoing"
  /**
   * 事件的中文翻译
   */
  msgCN?: string
  /**
   * 标识sip,socket,session,ua消息的发出方
   */
  originator?: Originator.LOCAL | Originator.REMOTE | Originator.SYSTEM
  /**
   * 如果出现错误,错误的原因
   */
  cause?: string
  /**
   * 错误原因中文
   */
  causeCN?: string
  /**
   * sip 或者 websocket状态码
   */
  statusCode?: number
  /**
   * sip 或者 websocket 状态码对应的具体原因
   */
  reason?: string
  /**
   * 消息生成时间
   */
  time?: string | number | Date | undefined
  /**
   * 额外cause数据
   */
  extra?: {
    suffix?: string
    prefix?: string
  }
  /**
   * 额外data,每个msg可能有额外的数据
   */
  data?: any
}

export declare class SipWorker extends JSSipWraper {
  constructor(cfg?: JsSipConfig)
  postMessage<T extends cmd>(type: T, data: CMDData<T>): void
}
postMessage 命令
export declare interface CMDMap {
    [cmd.INIT]: CMDInit;
    [cmd.CALL]: CMDCall;
    [cmd.ANSWER]: CMDAnswer;
    [cmd.HANGUP]: CMDHangup;
    [cmd.LOGIN]: CMDLogin;
    [cmd.LOGOUT]: CMDLogout;
    [cmd.EMPTY]: CMDEmpty;
    [cmd.HOLD]: CMDHold;
    [cmd.SESSTION_DEL]: CMDSESSTION_DEL;
    [cmd.UNHOLD]: CMDUnhold;
    [cmd.MUTE]: CMDMute;
    [cmd.UNMUTE]: CMDUnmute;
    [cmd.REGISTER]: CMDRegister;
    [cmd.UNREGISTER]: CMDUnregister;
    [cmd.NOTIFY]: CMDNotify;
}
export declare type CMDCall = {
    target: string;
    options?: CallOptions; =>> import type { CallOptions } from 'jssip/lib/UA';
};
export declare type CMDHangup = {
    /**
     * 是否以客户端假定是拨打超时的方式挂断电话
     */
    sessionId: string;
    isTimeout: boolean;
};
export declare type CMDAnswer = {
    sessionId: string;
};

测试

可访问 https://vivcode.cn/jssip 测试