Audio & Video
This guide covers audio/video features including local publishing, remote rendering, device management, volume detection, and beauty effects.
Local Publishing
Camera
// Start camera with local preview rendered into a DOM element
const result = await classroom.startCamera(
document.getElementById('local-video')!
);
if (!result.ok) {
console.error('Camera start failed:', result.message);
}
// Start camera with a specific device
await classroom.startCamera(localDom, 'device-id-xxx');
// Stop camera
await classroom.stopCamera();Microphone
// Start microphone
await classroom.startMicrophone();
// Start with a specific device
await classroom.startMicrophone('mic-device-id');
// Stop microphone
await classroom.stopMicrophone();Screen Sharing
// Start screen sharing
const result = await classroom.startScreenShare();
if (!result.ok) {
console.error('Screen share failed:', result.message);
}
// Optional: pass a local preview container
await classroom.startScreenShare(document.getElementById('screen-preview')!);
// Stop screen sharing
await classroom.stopScreenShare();Listen for screen share events:
import { TEvent } from '@tencent-classroom/sdk';
// Local screen share status
classroom.on(TEvent.SCREEN_SHARE_STARTED, () => {
console.log('Screen sharing started');
});
classroom.on(TEvent.SCREEN_SHARE_STOPPED, () => {
console.log('Screen sharing stopped');
});
// Remote screen share status
classroom.on(TEvent.REMOTE_SCREEN_SHARE_STARTED, ({ userId }) => {
console.log(`${userId} started screen sharing`);
});
classroom.on(TEvent.REMOTE_SCREEN_SHARE_STOPPED, ({ userId }) => {
console.log(`${userId} stopped screen sharing`);
});Permission Checks
Before enabling devices, check whether the current user has permission:
// Can the user open the microphone? (considers muteAll, permission grants, etc.)
if (classroom.canOpenMic()) {
await classroom.startMicrophone();
} else {
showToast('Cannot open microphone at this time');
}
// Can the user open the camera?
if (classroom.canOpenCamera()) {
await classroom.startCamera(element);
}
// Can the user share screen?
if (classroom.canShareScreen()) {
await classroom.startScreenShare();
}
// Is the current user on stage?
classroom.isOnStage(); // => booleanStudent Stage-Up Flow (Raise Hand / Speak)
In a large classroom, students can only watch by default. They must raise their hand and get approved by the teacher before they can open audio/video.
// === Student side ===
// 1. Raise hand to request on-stage
await classroom.handUp();
// 2. Watch for stage status change (triggered automatically after teacher approves)
classroom.state.stageStatus$.subscribe((status) => {
if (status === 'active') {
// SDK has automatically switched RTC role to anchor — open audio/video now
onStageActive();
}
});
async function onStageActive() {
// 3. Open audio/video after going on stage
await classroom.startCamera(document.getElementById('local-video')!);
await classroom.startMicrophone();
}
// 4. Cancel raise hand
await classroom.handUpCancel();// === Teacher side ===
// Watch hand-raise list
classroom.state.askStageList$.subscribe((list) => {
renderHandUpList(list);
});
// Approve raise hand (invite on stage)
await classroom.approveHandUp('student_001');
// Reject raise hand
await classroom.rejectHandUp('student_001');
// Directly invite on stage
import { MemberActionType } from '@tencent-classroom/sdk';
await classroom.memberAction({
userId: 'student_001',
action: MemberActionType.Stage_Up,
});
// Remove from stage
await classroom.memberAction({
userId: 'student_001',
action: MemberActionType.Stage_Down,
});Remote Stream Rendering
bindRemoteView / unbindRemoteView
bindRemoteView is the single entry point for all remote stream rendering. The SDK internally decides whether to use RTC real-time streams or Live low-latency streams — fully transparent to the caller.
import { StreamType } from '@tencent-classroom/sdk';
// Bind a remote user's camera stream
const camBinding = classroom.bindRemoteView(
'teacher_001',
document.getElementById('teacher-cam')!,
StreamType.Camera,
);
// Bind a remote user's screen share stream
const screenBinding = classroom.bindRemoteView(
'teacher_001',
document.getElementById('teacher-screen')!,
StreamType.Screen,
);
// Unbind on component unmount
classroom.unbindRemoteView(camBinding);
classroom.unbindRemoteView(screenBinding);Features:
- Bind once and rendering persists; the SDK handles remote user toggling camera/mic
- Resources are automatically released on unbind
Dynamic Video Wall — Track Stage Members
In real applications, subscribe to stageList$ to dynamically create DOM nodes and bind remote video as users go on stage, and remove them when they go off stage.
import { StreamType, type MemberInfo, type ViewBinding } from '@tencent-classroom/sdk';
// Video wall container
const videoWall = document.getElementById('video-wall')!;
// Maintain a binding map per on-stage member for cleanup
const bindingMap = new Map<string, { dom: HTMLElement; binding: ViewBinding }>();
// Subscribe to on-stage member list changes
classroom.state.stageList$.subscribe((stageList: MemberInfo[]) => {
const currentIds = new Set(stageList.map((m) => m.userId));
// 1. Newly on-stage members: create DOM → bind remote stream
for (const member of stageList) {
if (bindingMap.has(member.userId)) continue; // Already bound, skip
const dom = document.createElement('div');
dom.id = `video-${member.userId}`;
dom.className = 'video-item';
videoWall.appendChild(dom);
const binding = classroom.bindRemoteView(member.userId, dom, StreamType.Camera);
bindingMap.set(member.userId, { dom, binding });
}
// 2. Members who left stage: unbind → remove DOM
for (const [userId, { dom, binding }] of bindingMap) {
if (currentIds.has(userId)) continue; // Still on stage, skip
classroom.unbindRemoteView(binding);
videoWall.removeChild(dom);
bindingMap.delete(userId);
}
});Key points:
classroom.state.stageList$is reactive — the callback fires automatically on stage membership changes- Save each
ViewBindinghandle returned bybindRemoteView; pass it tounbindRemoteViewto release resources - Removing the DOM node automatically stops the video render — no extra steps needed
Mute Remote Audio
// Mute a specific user locally (does not affect other participants)
classroom.muteRemoteAudio('user_001', true);
classroom.muteRemoteAudio('user_001', false); // Unmute
// Mute all remote audio locally
classroom.muteAllRemoteAudio(true);
classroom.muteAllRemoteAudio(false);Global Audio/Video Control
Mute All / Disable All Video (Teacher/Assistant)
Teachers can forcibly mute or disable all student devices via signaling (synced to all clients):
// Mute all (all student microphones are closed; students cannot reopen)
await classroom.muteAudioAll(true);
// Unmute all (students regain the ability to open their microphones)
await classroom.muteAudioAll(false);
// Disable all video
await classroom.muteVideoAll(true);
await classroom.muteVideoAll(false);Difference
muteAllRemoteAudio is a local mute (the local client doesn't hear remote audio) and does not affect other participants. muteAudioAll is a global control (signals all clients; student microphones are forcibly closed), callable only by teacher/assistant.
Device Management
Get Device Lists
import { DeviceType } from '@tencent-classroom/sdk';
const cameras = await classroom.getCameraList();
if (cameras.ok) {
console.log('Available cameras:', cameras.data);
}
const mics = await classroom.getMicrophoneList();
const speakers = await classroom.getSpeakerList();Switch Devices
await classroom.switchDevice(DeviceType.Camera, 'new-camera-id');
await classroom.switchDevice(DeviceType.Microphone, 'new-mic-id');
await classroom.switchDevice(DeviceType.Speaker, 'new-speaker-id');Get Current Device
const currentCamId = classroom.getCurrentDeviceId(DeviceType.Camera);
const currentMicId = classroom.getCurrentDeviceId(DeviceType.Microphone);Hot-plug Detection
// Listen for device plug/unplug events
classroom.on(TEvent.DEVICE_CHANGE, () => {
console.log('Device list changed');
});
// Manually refresh device lists
await classroom.refreshDeviceLists();Subscribe to Device State
classroom.state.cameraList$.subscribe(list => {
console.log('Camera list updated:', list);
});
classroom.state.cameraDeviceStatus$.subscribe(status => {
console.log('Camera status:', status); // DeviceStatus enum
});
classroom.state.micDeviceStatus$.subscribe(status => {
console.log('Mic status:', status);
});Beauty Effects / Virtual Background
TIP
Beauty effects and virtual backgrounds require the package feature enableBeauty=true. Check via classroom.isFeatureAvailable('enableBeauty').
Beauty Parameters
// Set beauty parameters (0–100)
await classroom.setBeautyParam({
whiten: 30, // Skin whitening
lift: 20, // Face slimming
eye: 20, // Eye enlargement
});
// Disable beauty (set all to 0)
await classroom.setBeautyParam({ whiten: 0, lift: 0, eye: 0 });Virtual Background
// Blur background
await classroom.setVirtualBackground({ type: 'blur' });
// Custom background image
await classroom.setVirtualBackground({
type: 'image',
imageUrl: 'https://example.com/bg.jpg',
});
// Disable virtual background
await classroom.setVirtualBackground({ type: 'none' });Virtual Avatar
// Set virtual avatar (Flagship package)
await classroom.setAvatar({ effectId: 'avatar_001' });
// Disable virtual avatar
await classroom.setAvatar({ effectId: '' });Related APIs
- Media API Reference — Full method list
- Device Management API — Device enumeration and switching
- Network Diagnostics API — Network connectivity checks
- State Reference — Device-related signals