Skip to content

音视频指南

本文介绍音视频相关功能的使用方法,包括本地推流、远端渲染、设备管理、音量检测和美颜。

本地推流

摄像头

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: '' });

相关 API