Skip to main content

Media (Voice/Video)

Alpha

This feature is in alpha. APIs and behavior may change without notice. Not recommended for production use.

room.media is the media-state layer of Room.

It tracks who is publishing audio, video, or screen share, which tracks are present, and how mute or device state changes over time.

Client Surface

Assume room is an authenticated room client created with client.room(...).

await room.media.audio.enable({ deviceId: 'mic-1' });
await room.media.video.enable({ deviceId: 'cam-2' });
await room.media.screen.start();

room.media.onTrack((track, member) => attachTrack(track, member));
room.media.onTrackRemoved((track, member) => detachTrack(track, member));
room.media.onStateChange((member, state) => renderMediaState(member, state));

await room.media.devices.switch({ audioInputId: 'mic-2' });

The main client APIs are:

  • room.media.list()
  • room.media.audio.enable() / disable() / setMuted()
  • room.media.video.enable() / disable() / setMuted()
  • room.media.screen.start() / stop()
  • room.media.devices.switch(...)
  • room.media.onTrack(...)
  • room.media.onTrackRemoved(...)
  • room.media.onStateChange(...)
  • room.media.onDeviceChange(...)

Access Control

Media has split access gates because subscribe, publish, and control are different concerns:

  • access.media.subscribe(auth, roomId, payload)
  • access.media.publish(auth, roomId, kind, payload)
  • access.media.control(auth, roomId, operation, payload)

This lets you support patterns like:

  • everyone can subscribe
  • only hosts can publish screen share
  • moderators can control mute or unpublish operations

Server Hooks

Media-specific hooks let you observe or transform the media control plane:

rooms: {
meeting: {
hooks: {
media: {
beforePublish: async (kind, sender, room, ctx) => {
// Return false to reject the publish attempt
if (kind === 'screen' && sender.role !== 'host') return false;
// Return undefined to allow
},
onPublished: async (kind, sender, room, ctx) => {
console.log(`${sender.userId} published ${kind}`);
},
onUnpublished: async (kind, sender, room, ctx) => {
console.log(`${sender.userId} stopped ${kind}`);
},
onMuteChange: async (kind, sender, muted, room, ctx) => {
console.log(`${sender.userId} ${muted ? 'muted' : 'unmuted'} ${kind}`);
},
},
},
},
},
HookParametersCan Reject
beforePublish(kind, sender, room, ctx)Yes — return false
onPublished(kind, sender, room, ctx)No
onUnpublished(kind, sender, room, ctx)No
onMuteChange(kind, sender, muted, room, ctx)No

kind: 'audio' | 'video' | 'screen'. muted: boolean.

These are useful for analytics, moderation mirrors, or injecting track metadata.

Media and Admin

room.media describes media state.

room.admin is the moderation layer that acts on people in the room, including media-related controls such as:

  • room.admin.mute(memberId)
  • room.admin.disableVideo(memberId)
  • room.admin.stopScreenShare(memberId)

Media vs Signals

  • Use media for room-scoped A/V publishing and mute state.
  • Use Signals for WebRTC negotiation payloads and other transient control messages.