> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stmlink.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> 对外开放的服务端接口有两组前缀，都用同一套鉴权：`/server/v1/...`（SRTC 与 SMeeting 的主接口）和 `/stm/srvapi/v1/...`（SMeeting 的用户体系，服务端极简对接会用到）。鉴权是 app_id + nonce + timestamp + signature 四个请求头，用 app_key 做 HMAC-SHA256 签名，只能从业务方自己的后端调用。除这两组前缀外的接口均为内部接口，不要建议客户调用。
> app_key 是服务端密钥，绝不能出现在客户端代码、前端配置或移动 App 里。客户端加入频道用的 token 必须由业务方后端签发后下发（SRTC 走 `/server/v1/channel/grant`，SMeeting 走 `/stm/srvapi/v1/member/grant`）。
> SRTC 与 SMeeting 是上下两层不同的产品，术语不通用：SRTC 是音视频底座，说「频道 channel」「加入 / 退出」；SMeeting 建在 SRTC 之上，说「房间 room」「会议 meeting」「进入 / 退出」。回答时按用户所在的层用对应术语，不要把「房间」「会议」安到 SRTC 的接口上。
> 同一能力在各端 SDK 里的包名、类名、方法名并不相同。写示例代码时请使用文档中该端自己的 API，不要把一个端的写法套到另一个端上。苹果平台每个产品都有两套 SDK（Swift 原生与 Objective-C），两套 API 不能混用。

# 设备管理

> 枚举摄像头与麦克风、切换设备、热插拔监听与设备选择器

会议 SDK 在引擎上包了一层设备接口。音频**输出路由**是另一套，
见[音频路由](/zh/meeting/harmony/advanced/audio-routing)。

***

### 枚举

```typescript theme={null}
import { DeviceKind, DeviceInfo } from 'srtc';

const cams: DeviceInfo[] = await meeting.getDevices(DeviceKind.videoInput);
const mics: DeviceInfo[] = await meeting.getDevices(DeviceKind.audioInput);
const all: DeviceInfo[] = await meeting.getDevices();

// 读缓存，不触发系统查询
const cached: DeviceInfo[] = meeting.cachedDevices(DeviceKind.videoInput);
```

`DeviceInfo` 字段：`deviceId`、`name`、`kind`、`isDefault`。

<Note>
  **做设备下拉框用 `cachedDevices()`** —— 它不会每次渲染都去问系统。
  配合热插拔事件刷新即可。
</Note>

***

### 切换摄像头

```typescript theme={null}
meeting.switchCamera();                       // 前后置切换
meeting.switchCameraDevice(deviceId);         // 切到指定设备
```

<Note>
  这两个是**原地换设备、不重建轨道**，所以不需要重新发布 ——
  比直接用底层 SRTC 的 `changeDeviceId` 少一层心智负担
  （那边换设备会换底层轨道，已发布的必须重新发布）。
</Note>

***

### 热插拔

```typescript theme={null}
const delegate: SMeetingDelegate = {
  onDeviceAdd: (m, device: DeviceInfo) => {
    this.devices = m.cachedDevices();          // 建新数组赋值
    if (device.kind === DeviceKind.audioInput) {
      toast(`已接入 ${device.name}`);
    }
  },
  onDeviceRemove: (m, device: DeviceInfo) => {
    this.devices = m.cachedDevices();
  }
};

aboutToAppear(): void {
  this.meeting.delegates.add(delegate);
  this.meeting.startDeviceMonitoring();
}

aboutToDisappear(): void {
  this.meeting.stopDeviceMonitoring();
  this.meeting.delegates.remove(delegate);
}
```

<Warning>
  `onDeviceAdd` / `onDeviceRemove` 的第二个参数**直接是 `DeviceInfo`**，
  不是像其它事件那样的 `xxxEventData` 包装对象。
</Warning>

<Warning>
  ArkTS 的 `@State` 只观测第一层赋值 —— 设备列表永远用「建新数组再整体赋值」，
  不要 `push` / `splice` 原数组。
</Warning>

***

### 一个最小的设备选择器

```typescript theme={null}
@State cams: DeviceInfo[] = [];
@State currentCamId: string = '';

async loadDevices(): Promise<void> {
  this.cams = await this.meeting.getDevices(DeviceKind.videoInput);
}

build() {
  Column() {
    ForEach(this.cams, (d: DeviceInfo) => {
      Row() {
        Text(d.name)
        if (d.isDefault) {
          Text('（默认）').fontSize(12)
        }
      }
      .onClick(() => {
        this.meeting.switchCameraDevice(d.deviceId);
        this.currentCamId = d.deviceId;
      })
    }, (d: DeviceInfo) => d.deviceId)
  }
}
```

***

### 实测的设备形态

一台 HUAWEI nova 12 Pro（HarmonyOS 6.1）上：

```
cameras: 2   后置 device/0（默认）、前置 device/1
mics:    1   Microphone (2)（默认）
speakers: 2
```

<Note>
  **系统默认摄像头是后置。** 视频会议通常要显式用前置 ——
  `requestOpenCamera()` 用的默认预设已经是前置（`CameraPosition.front`），
  但如果你自己构造采集参数就要注意。
</Note>

<Warning>
  **采集分辨率受设备档位限制，档位表因机型而异。** 底层会把请求宽高吸附到最近的档位，
  所以实际尺寸可能与预设不同。读 `cameraTrack.captureWidth` / `captureHeight`
  拿实际值。

  这也是必须在 `UIAbility.onCreate` 里调 `SRTC.init(this.context)` 的原因 ——
  没有 Context 就查不到档位表，分辨率会退化为"由底层自行吸附"。
</Warning>

***

### 权限

摄像头与麦克风是 `user_grant` 权限，除了在 `module.json5` 声明，
还必须在运行时申请：

```typescript theme={null}
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';

const perms: Permissions[] = [
  'ohos.permission.CAMERA',
  'ohos.permission.MICROPHONE'
];
await abilityAccessCtrl.createAtManager()
  .requestPermissionsFromUser(getContext(this) as common.UIAbilityContext, perms);
```

权限没给时 `requestOpenMic` / `requestOpenCamera` 会失败，
底层抛的是 `SRTCError` 的 `captureError`（`108018`）。

***

### 相关阅读

* [设备与音频路由接口](/zh/meeting/harmony/api-reference/devices)
* [音频路由](/zh/meeting/harmony/advanced/audio-routing)
* [SRTC 设备管理](/zh/rtc/harmony/advanced/device-management) —— 底层接口
