Standalone RTC channels
im.channels lets signed-in users join audio/video channels created by your server, for meetings, voice rooms and other scenarios that don't depend on an IM group, with no need for friendships or groups. For channel rules and requirements, see the server docs on standalone RTC channels.
Check your SDK version
The channel API is available in source; before using it, make sure the SDK version you install includes the APIs below. For SDK release and real-device verification status, see Platforms and versions.
| You want to | See |
|---|---|
| Join a channel and turn on the microphone and camera | Join and capture |
| Show other members' video and audio on the page | Show authorized media |
| Get back into the same channel after a page refresh | Query and resume |
| Show channel state, handle members coming and going and the channel closing | State, events and cleanup |
| Use it in React or Vue | Framework bindings |
Join and capture
Below, im is a signed-in client and ticketFromBusinessServer is the ticket your server issued to this user. Don't pass ticket when joining an open channel.
if (!im.channels.supported) throw new Error('当前环境不支持频道媒体');
const cancelJoin = new AbortController();
const channel = await im.channels.join('meeting-demo', {
ticket: ticketFromBusinessServer,
signal: cancelJoin.signal,
});
const stopState = channel.subscribe(() => {
const snapshot = channel.getSnapshot();
console.log(snapshot.session.status, snapshot.media_state);
});
// 在用户点击采集按钮后调用;默认麦克风、摄像头关闭。
await channel.setMicrophoneEnabled(true);
// 仅 video 频道的 publisher 可以开启摄像头。
await channel.setCameraEnabled(true);
// 页面离开时:先取消订阅,再退出。
stopState();
await channel.leave();join() arguments: a channel ID plus options, or one object containing channel_id.
| Option | Description |
|---|---|
ticket | The ticket, required for ticket channels |
client_session_id | ID of this join. If omitted, the SDK generates one and keeps it the same when retrying this join; if you provide it, you must keep it the same too |
signal | An AbortSignal to cancel a join that hasn't finished |
microphoneEnabled, cameraEnabled | Whether to turn on the microphone and camera when joining; both off by default |
When a join succeeds: join() resolves only after media is connected and the server confirms the session as joined. From joining to being in (preparing the room, connecting media, server confirmation) you have 60 seconds in total, and the SDK's retries don't restart the clock.
Canceling and failures: cancelJoin.abort() cancels a join that hasn't finished; once you have a handle, call leave() instead. When a join fails, times out or is canceled, the SDK releases the reserved seat and stops the local microphone and camera.
Environment requirements:
- The page is HTTPS or
localhost, the browser supports WebRTC, the user allows the microphone and camera, and the network can reach the media service. - Turning on the microphone and camera, and playing audio, must be triggered by a user click. If the user denies permission, they can still watch and listen without speaking.
- Subscribers (
subscriber) can't turn on the microphone or camera, andaudiochannels can't turn on the camera. supportedbeingtrueonly means the browser has media capability; it doesn't mean the app has channels on or the user has granted device access.
Show authorized media
The SDK hands you members' audio and video tracks through the track event, identifying members by session_id and username. Don't treat the media room's identity as a username, and don't convert session_id to a JavaScript number.
const container = document.querySelector('#channel-media')!;
const players = new Map<string, HTMLMediaElement>();
const offTrack = channel.on('track', ({ session_id, kind, track, local }) => {
if (local && kind === 'audio') return; // 避免播放本机麦克风回声。
const key = `${session_id}:${kind}`;
players.get(key)?.remove();
const player = document.createElement(kind === 'video' ? 'video' : 'audio');
player.autoplay = true;
player.muted = local;
if (player instanceof HTMLVideoElement) player.playsInline = true;
player.srcObject = new MediaStream([track]);
container.append(player);
players.set(key, player);
void player.play().catch(() => {
// 显示“点击播放”按钮,并在用户操作中再次调用 player.play()。
});
});
const offRemoved = channel.on('trackRemoved', ({ session_id, kind }) => {
const key = `${session_id}:${kind}`;
const player = players.get(key);
if (player) { player.srcObject = null; player.remove(); }
players.delete(key);
});
// 页面卸载时执行:
offTrack();
offRemoved();
for (const player of players.values()) { player.srcObject = null; player.remove(); }
players.clear();
await channel.leave();A member's tracks are only handed to you once they've joined through the HTTP API and their media connection is confirmed. When the member list isn't confirmed yet, a member has left, or authorization is revoked, remove their video instead of continuing to show the old picture.
Query and resume
| Method / property | Description |
|---|---|
supported | Whether the browser has media capability |
current | This page's current channel handle |
getChannel(id) / get(id) | Get a channel you can see |
getCurrentSession() / currentSession() | Get this sign-in's channel session on the server, null if none |
listParticipants(id, page?) / members(id, page?) | List channel members; requires a valid session in the channel |
join(id, options?) | Join a channel |
resume({channel_id, session_id, signal?}) | Reconnect the original session: takes no new seat and doesn't turn on the microphone or camera automatically |
After a page refresh, resume the session if it still exists:
const existing = await im.channels.getCurrentSession();
if (existing && ['joining', 'joined'].includes(existing.status)) {
const restored = await im.channels.resume({
channel_id: existing.channel_id,
session_id: existing.session_id,
});
// 用户确认后才重新开启采集。
}- You can only resume sessions of this sign-in, not those of other sign-ins (other devices).
- If the session is
leaving, wait for it to finish leaving; if it'sleft, it's over and you need to join again. - Don't store tickets, handles or media tokens in
localStorageor write them to logs.
State, events and cleanup
channel.getSnapshot() returns the channel's current state: channel (the channel), session (your session), members, media_state and error; subscribe to changes with channel.subscribe(). The handle also provides refresh(), microphone and camera switches, and leave().
| Handle event | Payload | When |
|---|---|---|
track, trackRemoved | session_id, username, kind, track, local | A member's audio or video track becomes available or stops being available |
participantJoined, participantLeft | Participant | Someone joins or leaves |
sessionLeaving, sessionLeft | Your session | You start leaving, or finish leaving |
channelClosing, channelClosed | Channel | The channel starts closing, or finishes closing |
connectionChanged | connecting, connected, reconnecting, disconnected | The media connection's state changes |
The client event channel.changed fires when this page's current channel handle changes. For useChannel in React and Vue, see Framework bindings.
What the SDK handles: heartbeats and renewing media credentials; leaving the channel when the page is unloaded or frozen by the browser (the pagehide and freeze events), on sign-out, and when the client is destroyed. Just minimizing the window doesn't count as leaving the page.
What you handle:
- Call
leave()yourself on route changes and component unmounts. Even if the leave request fails, local media stops; the server may take a moment to release the seat. - Only the page that started the join holds the media; other tabs don't take over.
- On busy, ban or authorization refusals, don't keep joining with a new
client_session_id. For error codes, see Channel error codes.
