nimeet 文档中心
English

Web(浏览器) 快速开始与功能指南

Web(浏览器):前置条件、安装、Token、加入频道、发布订阅、事件、错误处理与排障;接口名与签名在构建时取自公开 SDK 接口定义。

接口小节由公开 SDK 接口定义生成

版本 v1.1.0 · 更新于 2026-10-26 · 修订 1

浏览器端提供与声网 Web SDK 4.24.9 一致的公开 API:142 个方法、153 个重载、47 个事件与 96 个错误标识。下面的方法链覆盖创建客户端、加入频道、发布订阅到离开的完整过程,可直接在项目中复用。

前置条件

  • Node.js ≥ 22(构建/测试用)、pnpm 10;pnpm install。
  • 浏览器:Chrome 65+、Edge、Firefox 69+、Safari 11+(屏幕共享需 Safari 13+)。
  • 采集音视频要求安全上下文:https:// 或 http://localhost;纯 http:// 局域网 IP 下 getUserMedia 会直接失败。
  • 已按 控制台开通服务与获取凭证 拿到 appId,并有一个能签发 Token 的后端。

安装

bash
pnpm add @nimeet/rtc-web

代码库中开发时用工作区包:pnpm --filter @nimeet/rtc-web build。版本与兼容版本对齐(公开 SDK 接口定义 的 apiVersion 与接口定义小节一致)。

获取 Token

浏览器不能持有 customerSecret。正确链路是你的后端调控制面:

javascript
// 你的后端(Node 示例):
// POST $NIMEET_API_BASE/v1/tokens  { channelName, uid, role, ttlSeconds }
// 前端只拿到 { appId, rtcToken, rtcUid }
const response = await fetch('/api/rtc-token?channel=weekly-sync&uid=10001');
const { appId, rtcToken, rtcUid } = await response.json();

Token 语义(权限位、exp/pexp 分离、通配)见 AccessToken2 语义。

加入频道

javascript
import AgoraRTC from '@nimeet/rtc-web';

const client = AgoraRTC.createClient({ mode: 'rtc', codec: 'vp8' });

client.on('user-published', async (user, mediaType) => {
  await client.subscribe(user, mediaType);
  if (mediaType === 'video') user.videoTrack?.play('remote-player');
  if (mediaType === 'audio') user.audioTrack?.play();
});
client.on('token-privilege-will-expire', async () => {
  const fresh = await fetch('/api/rtc-token?renew=1').then((r) => r.json());
  await client.renewToken(fresh.rtcToken);
});

const uid = await client.join(appId, 'weekly-sync', rtcToken, rtcUid);

页面需要两个容器:<div id="local-player"></div> 与 <div id="remote-player"></div>。媒体边缘地址按 Token 载荷 surl → window.__NIMEET_SFU_URL__ → 同源 /rtc 的顺序解析。

发布与订阅

javascript
const [microphoneTrack, cameraTrack] = await AgoraRTC.createMicrophoneAndCameraTracks();
cameraTrack.play('local-player');
await client.publish([microphoneTrack, cameraTrack]);

// 手动订阅(默认是自动订阅,关掉后需要自己 subscribe)
await client.unsubscribe(remoteUser, 'video');
cameraTrack.setEnabled(false); // 静音画面但保持发布

轨道是对象:发布的是 ILocalTrack 实例,不是「资源 id」。停止发布要 client.unpublish(track),释放采集要 track.close()。

事件

事件何时触发典型处理
user-joined / user-left远端用户进出分配/释放画面容器
user-published / user-unpublished远端发布/取消发布subscribe 后播放或停止
connection-state-changeDISCONNECTED/CONNECTING/CONNECTED/RECONNECTINGRECONNECTING 时不要销毁轨道
token-privilege-will-expire权限到期前 30 秒续期并 renewToken
network-quality周期上报上行/下行质量提示弱网或上报水晶球
stream-message数据通道消息白板/信令载荷
exceptionSDK 内部异常收集日志,按 code 分流

错误处理

失败统一是 AgoraRTCError:{ code, message, data? },code 是声网 4.x 的字符串标识(INVALID_PARAMS、PERMISSION_DENIED、TOKEN_EXPIRE…),96 项全表见同端 API 参考。平台 REST 错误码另有 sdkCode(负数错误号),映射见 各端 SDK 错误标识。

javascript
try {
  await client.publish([microphoneTrack, cameraTrack]);
} catch (error) {
  if (error.code === 'PERMISSION_DENIED') {
    // 用户拒绝了摄像头/麦克风权限
  } else if (error.code === 'NOT_SUPPORTED') {
    // 该能力在当前媒体栈未实现(例如 E2EE),不要静默忽略
  }
}

排障

现象原因处理
getUserMedia 直接抛错非安全上下文或权限被拒用 https:// 或 localhost;检查浏览器站点权限
入会报 INVALID_TOKENToken 与 appId/频道不匹配,或已过期GET /v1/tokens/verify?token=&channelName= 自查
有声音没画面远端视频轨道未 subscribe 或容器 id 写错检查 user-published 回调里是否 play('remote-player')
Safari 无法采集需要用户手势触发把采集放在按钮点击事件里
一直 RECONNECTING媒体边缘不可达检查 SFU 地址解析链与反向代理 WebSocket 升级
打包体积过大未做 tree-shaking参考声网的按需引入思路,import { AgoraRTC } from '@nimeet/rtc-web'

下一步

平台信息

项值
平台Web(浏览器)
语言TypeScript
SDK 包@nimeet/rtc-web
接口版本4.24.9
兼容参考声网 javascript 端 API

本端公开接口与声网 javascript 端 API 对齐;安装与运行要求见上面前置条件。

可用性

该端 SDK:正式可用。本页代码片段使用公开 API;运行时的能力边界以实际发布的 SDK 包为准。

加入频道链路

下列方法名、参数类型与返回值与同端 API 参考一致。一个平台若有多个重载,这里展示最常用的那一个;完整重载见同端 API 参考页。

步骤接口方法重载数
加入频道IAgoraRTCClientjoin1
离开频道IAgoraRTCClientleave1
发布本地轨道IAgoraRTCClientpublish3
续期 TokenIAgoraRTCClientrenewToken1
切换角色IAgoraRTCClientsetClientRole1
订阅远端媒体IAgoraRTCClientsubscribe4
创建实例 / 初始化IAgoraRTCcreateClient1
typescript
join(appid: string, channel: string, token: string|null, uid?: UID|null, options?: {autoSubscribe?:boolean;autoReceiveAndPlayAudio?:boolean;networkQualityProbe?:boolean}): Promise<UID>
leave(): Promise<void>
publish(tracks: ILocalTrack|ILocalTrack[]): Promise<void>
renewToken(token: string): Promise<void>
setClientRole(role: ClientRole, options?: ClientRoleOptions): Promise<void>
subscribe(user: IAgoraRTCRemoteUser|UID, mediaType: "video"): Promise<IRemoteVideoTrack>
createClient(config: ClientConfig): IAgoraRTCClient

事件与回调

接口事件名监听器参数
ILocalTracktrack-ended()
ILocalTracktrack-updatedtrack:MediaStreamTrack
ILocalTracktransceiver-updatedtransceiver:RTCRtpTransceiver,type?:StreamType
ILocalVideoTracktrack-ended()
ILocalVideoTracktrack-updatedtrack:MediaStreamTrack
ILocalVideoTracktransceiver-updatedtransceiver:RTCRtpTransceiver,type?:StreamType
ILocalVideoTrackvideo-element-visible-statusdata?:CheckVideoVisibleResult
IRemoteTrackfirst-frame-decoded()
IRemoteTracktrack-updatedtrack:MediaStreamTrack
IRemoteTracktransceiver-updatedtransceiver:RTCRtpTransceiver
IRemoteVideoTrackfirst-frame-decoded()
IRemoteVideoTracksei-receivedsei:Uint8Array,rtpTimestamp?:number
IRemoteVideoTracktrack-updatedtrack:MediaStreamTrack
IRemoteVideoTracktransceiver-updatedtransceiver:RTCRtpTransceiver
IRemoteVideoTrackvideo-element-visible-statusdata?:CheckVideoVisibleResult
IRemoteVideoTrackvideo-state-changedvideoState:VideoState
IAgoraRTCClientchannel-media-relay-eventevent:ChannelMediaRelayEvent
IAgoraRTCClientchannel-media-relay-statestate:ChannelMediaRelayState,code:ChannelMediaRelayError
IAgoraRTCClientconnection-state-changecurState:ConnectionState,revState:ConnectionState,reason?:ConnectionDisconnectedReason
IAgoraRTCClientcrypt-error()
IAgoraRTCClientexceptionevent:{code:number;msg:string;uid:UID}
IAgoraRTCClientis-using-cloud-proxyisUsingProxy:boolean
IAgoraRTCClientjoin-fallback-to-proxyproxyServer:string
IAgoraRTCClientlive-streaming-errorurl:string,err:AgoraRTCError
IAgoraRTCClientlive-streaming-warningurl:string,warning:AgoraRTCError
IAgoraRTCClientmedia-reconnect-enduid:UID
IAgoraRTCClientmedia-reconnect-startuid:UID
IAgoraRTCClientnetwork-qualitystats:NetworkQuality
IAgoraRTCClientpeerconnection-state-changecurState:RTCPeerConnectionState,revState:RTCPeerConnectionState
IAgoraRTCClientpublished-user-listusers:IAgoraRTCRemoteUser[]
IAgoraRTCClientstream-fallbackuid:UID,isFallbackOrRecover:"fallback"|"recover"
IAgoraRTCClientstream-messageuid:UID,payload:Uint8Array
IAgoraRTCClientstream-type-changeduid:UID,streamType:RemoteStreamType
IAgoraRTCClienttoken-privilege-did-expire()
IAgoraRTCClienttoken-privilege-will-expire()
IAgoraRTCClientuser-info-updateduid:UID,msg:|"mute-audio"|"mute-video"|"enable-local-video"|"unmute-audio"|"unmute-video"|"disable-local-video"
IAgoraRTCClientuser-joineduser:IAgoraRTCRemoteUser
IAgoraRTCClientuser-leftuser:IAgoraRTCRemoteUser,reason:string
IAgoraRTCClientuser-publisheduser:IAgoraRTCRemoteUser,mediaType:"audio"|"video"|"datachannel",config?:IDataChannelConfig
IAgoraRTCClientuser-unpublisheduser:IAgoraRTCRemoteUser,mediaType:"audio"|"video"|"datachannel",config?:IDataChannelConfig
IAgoraRTCClientvolume-indicatorresult:{level:number;uid:UID}[]
IAgoraRTCaudio-context-state-changedcurrState:AudioContextState|"interrupted",prevState:AudioContextState|"interrupted"|undefined
IAgoraRTCautoplay-failed()
IAgoraRTCcamera-changeddeviceInfo:DeviceInfo
IAgoraRTCmicrophone-changeddeviceInfo:DeviceInfo
IAgoraRTCplayback-device-changeddeviceInfo:DeviceInfo
IAgoraRTCsecurity-policy-violation()

错误码

常量值
ADD_CANDIDATE_FAILEDADD_CANDIDATE_FAILED
API_INVOKE_TIMEOUTAPI_INVOKE_TIMEOUT
BEAUTY_PROCESSOR_INTERNAL_ERRORBEAUTY_PROCESSOR_INTERNAL_ERROR
CAN_NOT_GET_GATEWAY_SERVERCAN_NOT_GET_GATEWAY_SERVER
CAN_NOT_GET_PROXY_SERVERCAN_NOT_GET_PROXY_SERVER
CAN_NOT_PUBLISH_MULTIPLE_VIDEO_TRACKSCAN_NOT_PUBLISH_MULTIPLE_VIDEO_TRACKS
CHROME_PLUGIN_NOT_INSTALLCHROME_PLUGIN_NOT_INSTALL
CHROME_PLUGIN_NO_RESPONSECHROME_PLUGIN_NO_RESPONSE
CONSTRAINT_NOT_SATISFIEDCONSTRAINT_NOT_SATISFIED
CONVERTING_IMAGEDATA_TO_BLOB_FAILEDCONVERTING_IMAGEDATA_TO_BLOB_FAILED
CONVERTING_VIDEO_FRAME_TO_BLOB_FAILEDCONVERTING_VIDEO_FRAME_TO_BLOB_FAILED
CREATE_DATACHANNEL_ERRORCREATE_DATACHANNEL_ERROR
CREATE_OFFER_FAILEDCREATE_OFFER_FAILED
CROSS_CHANNEL_FAILED_JOIN_DESTCROSS_CHANNEL_FAILED_JOIN_DEST
CROSS_CHANNEL_FAILED_JOIN_SRCCROSS_CHANNEL_FAILED_JOIN_SEC
CROSS_CHANNEL_FAILED_PACKET_SENT_TO_DESTCROSS_CHANNEL_FAILED_PACKET_SENT_TO_DEST
CROSS_CHANNEL_SERVER_ERROR_RESPONSECROSS_CHANNEL_SERVER_ERROR_RESPONSE
CROSS_CHANNEL_WAIT_STATUS_ERRORCROSS_CHANNEL_WAIT_STATUS_ERROR
CUSTOM_REPORT_FREQUENCY_TOO_HIGHCUSTOM_REPORT_FREQUENCY_TOO_HIGH
CUSTOM_REPORT_SEND_FAILEDCUSTOM_REPORT_SEND_FAILED
DATACHANNEL_CONNECTION_TIMEOUTDATACHANNEL_CONNECTION_TIMEOUT
DATACHANNEL_FAILEDDATACHANNEL_FAILED
DECODE_AUDIO_FILE_FAILEDDECODE_AUDIO_FILE_FAILED
DEVICE_NOT_FOUNDDEVICE_NOT_FOUND
DISCONNECT_P2PDISCONNECT_P2P
ELECTRON_DESKTOP_CAPTURER_GET_SOURCES_ERRORELECTRON_DESKTOP_CAPTURER_GET_SOURCES_ERROR
ELECTRON_IS_NULLELECTRON_IS_NULL
ENUMERATE_DEVICES_FAILEDENUMERATE_DEVICES_FAILED
EXCHANGE_SDP_FAILEDEXCHANGE_SDP_FAILED
EXIST_DISABLED_VIDEO_TRACKEXIST_DISABLED_VIDEO_TRACK
EXTERNAL_SIGNAL_ABORTEXTERNAL_SIGNAL_ABORT
FETCH_AUDIO_FILE_FAILEDFETCH_AUDIO_FILE_FAILED
GATEWAY_P2P_LOSTGATEWAY_P2P_LOST
GET_LOCAL_CAPABILITIES_FAILEDGET_LOCAL_CAPABILITIES_FAILED
GET_LOCAL_CONNECTION_PARAMS_FAILEDGET_LOCAL_CONNECTION_PARAMS_FAILED
GET_VIDEO_ELEMENT_VISIBLE_ERRORGET_VIDEO_ELEMENT_VISIBLE_ERROR
ICE_FAILEDICE_FAILED
IMAGE_MODERATION_UPLOAD_FAILEDIMAGE_MODERATION_UPLOAD_FAILED
INIT_DATACHANNEL_TIMEOUTINIT_DATACHANNEL_TIMEOUT
INVALID_LOCAL_TRACKINVALID_LOCAL_TRACK
INVALID_OPERATIONINVALID_OPERATION
INVALID_PARAMSINVALID_PARAMS
INVALID_PLUGININVALID_PLUGIN
INVALID_REMOTE_USERINVALID_REMOTE_USER
INVALID_TRACKINVALID_TRACK
INVALID_UINT_UID_FROM_STRING_UIDINVALID_UINT_UID_FROM_STRING_UID
LIVE_STREAMING_CDN_ERRORLIVE_STREAMING_CDN_ERROR
LIVE_STREAMING_INTERNAL_SERVER_ERRORLIVE_STREAMING_INTERNAL_SERVER_ERROR
LIVE_STREAMING_INVALID_ARGUMENTLIVE_STREAMING_INVALID_ARGUMENT
LIVE_STREAMING_INVALID_RAW_STREAMLIVE_STREAMING_INVALID_RAW_STREAM
LIVE_STREAMING_PUBLISH_STREAM_NOT_AUTHORIZEDLIVE_STREAMING_PUBLISH_STREAM_NOT_AUTHORIZED
LIVE_STREAMING_TASK_CONFLICTLIVE_STREAMING_TASK_CONFLICT
LIVE_STREAMING_TRANSCODING_NOT_SUPPORTEDLIVE_STREAMING_TRANSCODING_NOT_SUPPORTED
LIVE_STREAMING_WARN_FAILED_LOAD_IMAGELIVE_STREAMING_WARN_FAILED_LOAD_IMAGE
LIVE_STREAMING_WARN_FREQUENT_REQUESTLIVE_STREAMING_WARN_FREQUENT_REQUEST
LIVE_STREAMING_WARN_STREAM_NUM_REACH_LIMITLIVE_STREAMING_WARN_STREAM_NUM_REACH_LIMIT
LOCAL_AEC_ERRORLOCAL_AEC_ERROR
LOW_STREAM_ENCODING_ERRORLOW_STREAM_ENCODING_ERROR
MEDIA_OPTION_INVALIDMEDIA_OPTION_INVALID
METADATA_OUT_OF_RANGEMETADATA_OUT_OF_RANGE
MULTI_UNILBS_RESPONSE_ERRORMULTI_UNILBS_RESPONSE_ERROR
NETWORK_ERRORNETWORK_ERROR
NETWORK_RESPONSE_ERRORNETWORK_RESPONSE_ERROR
NETWORK_TIMEOUTNETWORK_TIMEOUT
NOT_READABLENOT_READABLE
NOT_SUPPORTEDNOT_SUPPORTED
NO_ICE_CANDIDATENO_ICE_CANDIDATE
OPERATION_ABORTEDOPERATION_ABORTED
P2P_MESSAGE_FAILEDP2P_MESSAGE_FAILED
PB_ERRORPB_ERROR
PC_CLOSEDPC_CLOSED
PERMISSION_DENIEDPERMISSION_DENIED
PROHIBITED_OPERATIONPROHIBITED_OPERATION
READ_LOCAL_AUDIO_FILE_ERRORREAD_LOCAL_AUDIO_FILE_ERROR
REMOTE_USER_IS_NOT_PUBLISHEDREMOTE_USER_IS_NOT_PUBLISHED
SENDER_NOT_FOUNDSENDER_NOT_FOUND
SENDER_REPLACE_FAILEDSENDER_REPLACE_FAILED
SET_ANSWER_FAILEDSET_ANSWER_FAILED
SET_ENCODING_PARAMETER_ERRORSET_ENCODING_PARAMETER_ERROR
SHARE_AUDIO_NOT_ALLOWEDSHARE_AUDIO_NOT_ALLOWED
SUBSCRIBE_FAILEDSUBSCRIBE_FAILED
TIMEOUTTIMEOUT
TOKEN_EXPIRETOKEN_EXPIRE
TRACK_IS_DISABLEDTRACK_IS_DISABLED
TRACK_STATE_UNREACHABLETRACK_STATE_UNREACHABLE
UID_CONFLICTUID_CONFLICT
UNEXPECTED_ERRORUNEXPECTED_ERROR
UNEXPECTED_RESPONSEUNEXPECTED_RESPONSE
UNSUBSCRIBE_FAILEDUNSUBSCRIBE_FAILED
UPDATE_TICKET_FAILEDUPDATE_TICKET_FAILED
VOID_GATEWAY_ADDRESSVOID_GATEWAY_ADDRESS
WEBGL_INTERNAL_ERRORWEBGL_INTERNAL_ERROR
WEB_SECURITY_RESTRICTWEB_SECURITY_RESTRICT
WS_ABORTWS_ABORT
WS_DISCONNECTWS_DISCONNECT
WS_ERRWS_ERR

平台 REST 层用 REST 错误码;SDK 层把连接/鉴权失败映射成声网错误号,映射表见 各端 SDK 错误标识。

本页由文档构建流水线生成 · 2026-10-31 01:24:02 UTC · 文档中心 v1.1.0.

内容依据公开 API 定义.

ni-meet 是自建 RTC PaaS,媒体面基于 LiveKit(Apache-2.0),并与声网 API 兼容。