音视频指南
本文介绍音视频相关功能的使用方法,包括本地推流、远端渲染、设备管理、音量检测和美颜。
本地推流
摄像头
typescript
// 开启摄像头,传入本地预览的 DOM 容器
const result = await classroom.startCamera(
document.getElementById('local-video')!
);
if (!result.ok) {
console.error('摄像头打开失败:', result.message);
}
// 指定设备 ID 打开摄像头
await classroom.startCamera(localDom, 'device-id-xxx');
// 关闭摄像头
await classroom.stopCamera();麦克风
typescript
// 开启麦克风
await classroom.startMicrophone();
// 指定设备 ID
await classroom.startMicrophone('mic-device-id');
// 关闭麦克风
await classroom.stopMicrophone();屏幕共享
typescript
// 开启屏幕共享
const result = await classroom.startScreenShare();
if (!result.ok) {
console.error('屏幕共享失败:', result.message);
}
// 可选:传入本地预览容器
await classroom.startScreenShare(document.getElementById('screen-preview')!);
// 停止屏幕共享
await classroom.stopScreenShare();监听屏幕共享事件:
typescript
import { TEvent } from '@tencent-classroom/sdk';
// 本端屏幕共享状态
classroom.on(TEvent.SCREEN_SHARE_STARTED, () => {
console.log('屏幕共享已开启');
});
classroom.on(TEvent.SCREEN_SHARE_STOPPED, () => {
console.log('屏幕共享已停止');
});
// 远端屏幕共享状态
classroom.on(TEvent.REMOTE_SCREEN_SHARE_STARTED, ({ userId }) => {
console.log(`${userId} 开始了屏幕共享`);
});
classroom.on(TEvent.REMOTE_SCREEN_SHARE_STOPPED, ({ userId }) => {
console.log(`${userId} 停止了屏幕共享`);
});权限检查
在开启设备前,建议先检查当前用户是否有权限:
typescript
// 是否可以打开麦克风(考虑全员静音、权限授予等)
if (classroom.canOpenMic()) {
await classroom.startMicrophone();
} else {
showToast('当前无法开启麦克风');
}
// 是否可以打开摄像头
if (classroom.canOpenCamera()) {
await classroom.startCamera(element);
}
// 是否可以屏幕共享
if (classroom.canShareScreen()) {
await classroom.startScreenShare();
}
// 当前用户是否在台上
classroom.isOnStage(); // => boolean学生连麦上台流程
学生在大班课中默认只能观看,需要通过举手连麦获得上台权限后,才能开启音视频。
typescript
// === 学生端 ===
// 1. 举手申请上台
await classroom.handUp();
// 2. 监听上台状态变化(老师同意后自动触发)
classroom.state.stageStatus$.subscribe((status) => {
if (status === 'active') {
// SDK 已自动切换 RTC 角色为 anchor,现在可以开启音视频
onStageActive();
}
});
async function onStageActive() {
// 3. 上台后开启音视频
await classroom.startCamera(document.getElementById('local-video')!);
await classroom.startMicrophone();
}
// 4. 取消举手
await classroom.handUpCancel();typescript
// === 老师端 ===
// 监听举手列表
classroom.state.askStageList$.subscribe((list) => {
renderHandUpList(list); // 展示举手申请列表
});
// 同意举手(邀请上台)
await classroom.approveHandUp('student_001');
// 拒绝举手
await classroom.rejectHandUp('student_001');
// 主动邀请上台
import { MemberActionType } from '@tencent-classroom/sdk';
await classroom.memberAction({
userId: 'student_001',
action: MemberActionType.Stage_Up,
});
// 请下台
await classroom.memberAction({
userId: 'student_001',
action: MemberActionType.Stage_Down,
});远端流渲染
bindRemoteView / unbindRemoteView
bindRemoteView 是远端流渲染的唯一入口。调用该方法即可一次性完成绑定远端用户的音视频到特定流播放。
typescript
import { StreamType } from '@tencent-classroom/sdk';
// 绑定远端用户的摄像头流
const camBinding = classroom.bindRemoteView(
'teacher_001',
document.getElementById('teacher-cam')!,
StreamType.Camera,
);
// 绑定远端用户的屏幕共享流
const screenBinding = classroom.bindRemoteView(
'teacher_001',
document.getElementById('teacher-screen')!,
StreamType.Screen,
);
// 组件卸载时解绑
classroom.unbindRemoteView(camBinding);
classroom.unbindRemoteView(screenBinding);特性:
- 绑定一次即可持续渲染,SDK 自动处理远端用户开关摄像头/麦克风
- 解绑后资源自动释放
监听台上成员变化,动态创建/销毁视频每个用户的视频播放区域
实际业务中,通常需要监听 stageList$(台上成员列表)的变化,在用户上台时动态创建 DOM 节点并绑定远端视频,在用户下台时解绑并移除 DOM。
typescript
import { StreamType, type MemberInfo, type ViewBinding } from '@tencent-classroom/sdk';
// 视频墙容器
const videoWall = document.getElementById('video-wall')!;
// 维护每个台上成员的 binding 映射,用于下台时清理
const bindingMap = new Map<string, { dom: HTMLElement; binding: ViewBinding }>();
// 订阅台上成员列表变化
classroom.state.stageList$.subscribe((stageList: MemberInfo[]) => {
const currentIds = new Set(stageList.map((m) => m.userId));
// 1. 新上台的成员:创建 DOM → 绑定远端流
for (const member of stageList) {
if (bindingMap.has(member.userId)) continue; // 已绑定,跳过
// 创建视频容器 DOM
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. 已下台的成员:解绑远端流 → 移除 DOM
for (const [userId, { dom, binding }] of bindingMap) {
if (currentIds.has(userId)) continue; // 仍在台上,跳过
classroom.unbindRemoteView(binding);
videoWall.removeChild(dom);
bindingMap.delete(userId);
}
});要点:
classroom.state.stageList$是响应式状态,订阅后会在台上成员变化时自动触发回调stageList包含所有当前台上成员的MemberInfo,通过对比已绑定集合即可得出"新上台"和"已下台"的差集- 每次绑定返回的
ViewBinding句柄需保存,下台时传给unbindRemoteView以释放资源 - 移除 DOM 节点后,对应的视频渲染自动停止,无需额外操作
静音远端音频
typescript
// 静音指定用户(仅本地生效,不影响其他人)
classroom.muteRemoteAudio('user_001', true);
classroom.muteRemoteAudio('user_001', false); // 取消静音
// 全局静音所有远端音频
classroom.muteAllRemoteAudio(true);
classroom.muteAllRemoteAudio(false);音视频全局控制
全员静音 / 全员关视频(老师/助教)
老师可通过全员级 API 强制静音或关闭所有学生设备(信令同步到所有端):
typescript
// 全员静音(所有学生麦克风被关闭,且无法自行打开)
await classroom.muteAudioAll(true);
// 解除全员静音(学生恢复自主开麦能力)
await classroom.muteAudioAll(false);
// 全员关闭视频
await classroom.muteVideoAll(true);
await classroom.muteVideoAll(false);区别
muteAllRemoteAudio 是本地静音(仅本端不听远端声音),不影响其他人。 muteAudioAll 是全员级控制(信令广播,学生端麦克风被强制关闭),只有老师/助教可调用。
设备管理
获取设备列表
typescript
import { DeviceType } from '@tencent-classroom/sdk';
// 获取可用摄像头列表
const cameras = await classroom.getCameraList();
if (cameras.ok) {
console.log('摄像头列表:', cameras.data);
}
// 获取可用麦克风列表
const mics = await classroom.getMicrophoneList();
// 获取可用扬声器列表
const speakers = await classroom.getSpeakerList();切换设备
typescript
// 切换摄像头
await classroom.switchDevice(DeviceType.Camera, 'new-camera-id');
// 切换麦克风
await classroom.switchDevice(DeviceType.Microphone, 'new-mic-id');
// 切换扬声器
await classroom.switchDevice(DeviceType.Speaker, 'new-speaker-id');获取当前设备
typescript
const currentCamId = classroom.getCurrentDeviceId(DeviceType.Camera);
const currentMicId = classroom.getCurrentDeviceId(DeviceType.Microphone);设备热插拔
typescript
// 监听设备插拔事件
classroom.on(TEvent.DEVICE_CHANGE, () => {
console.log('设备列表发生变化');
});
// 手动刷新设备列表
await classroom.refreshDeviceLists();订阅设备状态
typescript
// 通过 State 订阅设备列表变化
classroom.state.cameraList$.subscribe(list => {
console.log('摄像头列表更新:', list);
});
classroom.state.cameraDeviceStatus$.subscribe(status => {
console.log('摄像头状态:', status); // DeviceStatus 枚举
});
classroom.state.micDeviceStatus$.subscribe(status => {
console.log('麦克风状态:', status);
});美颜 / 虚拟背景
TIP
美颜和虚拟背景需要套餐支持 enableBeauty=true。可通过 classroom.isFeatureAvailable('enableBeauty') 检查。
美颜参数
typescript
// 设置美颜参数(0~100)
await classroom.setBeautyParam({
whiten: 30, // 美白
lift: 20, // 窄脸
eye: 20, // 大眼
});
// 关闭美颜(所有参数设为 0)
await classroom.setBeautyParam({ whiten: 0, lift: 0, eye: 0 });虚拟背景
typescript
// 背景模糊
await classroom.setVirtualBackground({ type: 'blur' });
// 自定义背景图片
await classroom.setVirtualBackground({
type: 'image',
imageUrl: 'https://example.com/bg.jpg',
});
// 关闭虚拟背景
await classroom.setVirtualBackground({ type: 'none' });虚拟形象
typescript
// 设置虚拟形象(旗舰版套餐)
await classroom.setAvatar({ effectId: 'avatar_001' });
// 关闭虚拟形象
await classroom.setAvatar({ effectId: '' });