# EZOpenSDK HarmonyOS 对讲

## 对讲模块 import 参考

```typescript
import {
  EZOpenSDK,
  EZPlayer,
  EZPlayerCallback,
  EZDeviceInfo,
  EZCameraInfo,
  EZError,
  EZErrorCode,
  EZMessageCode,
  EzvizSupportKit,
  OnLoudnessListener
} from '@ohos/EZOpenSDK'
import EZBusinessTool from '../businesstool/EZBusinessTool'
```

## 一、对讲能力判断

通过 EzvizSupportKit 判断设备对讲能力：
```typescript
let talkCapability = EzvizSupportKit.isSupportTalk(deviceInfo.abilities)
// 或使用工具类（兼容子设备）
let talkCapability = EZBusinessTool.isSupportTalk(deviceInfo, cameraInfo)
```

| 值 | 说明 |
|----|------|
| 0 | 不支持对讲 |
| 1 | 支持全双工对讲 |
| 3 | 支持半双工对讲 |
| 4 | 同时支持全双工和半双工 |

## 二、全双工对讲

全双工对讲允许双方同时发送和接收语音。

### 完整流程
```typescript
@Entry
@Component
struct EZRealPlayPage {
  player: EZPlayer | null = null      // 预览播放器
  talkPlayer: EZPlayer | null = null  // 对讲播放器（必须独立）
  @State isOpenSound: boolean = true

  aboutToAppear() {
    // 1. 创建对讲播放器（需独立于预览播放器）
    this.talkPlayer = EZOpenSDK.createPlayer(this.cameraInfo.deviceSerial, this.cameraInfo.cameraNo)
    // 2. 设置回调
    this.talkPlayer.setPlayerCallback(this.talkPlayerCallback)
  }

  // 开始对讲
  async startTalk() {
    // IPC 设备发起对讲
    await this.talkPlayer?.startVoiceTalk()
    // NVR 设备通道对讲
    // await this.talkPlayer?.startVoiceTalkNeedVoiceChannel(true)
  }

  // 停止对讲
  async stopTalk() {
    await this.talkPlayer?.stopVoiceTalk()
  }

  async aboutToDisappear() {
    await EZOpenSDK.releasePlayer(this.talkPlayer)
  }

  private talkPlayerCallback: EZPlayerCallback = {
    didReceivedMessage: async (messageCode: number) => {
      if (messageCode == EZMessageCode.PLAYER_VOICE_TALK_START) {
        // 对讲成功
        // 非国标设备需要关闭预览播放器声音（避免回音）
        if (this.deviceInfo?.devProtoEnum == 0) {
          await this.player?.closeSound()
        }
      } else if (messageCode == EZMessageCode.PLAYER_VOICE_TALK_END) {
        // 对讲结束，恢复预览声音
        if (this.isOpenSound) {
          await this.player?.openSound()
        }
      }
    },
    didPlayFailed: (error: EZError) => {
      if (error.code == EZErrorCode.EZ_SDK_NOT_SUPPORT_TALK) {
        // 设备不支持对讲
      } else {
        // 对讲失败
      }
    }
  }
}
```

### 非国标 vs 国标设备对讲声音处理

| 设备类型 | 对讲成功后 | 说明 |
|---------|-----------|------|
| 非国标设备（devProtoEnum==0） | 关闭 player 声音 | talkPlayer 传输音频，player 同时播放会回音 |
| 国标设备（devProtoEnum==5/6） | 不关闭 player 声音 | talkPlayer 只采集手机端声音，设备端声音通过 player 播放 |

## 三、半双工对讲

半双工对讲为交替模式，代码实现与全双工一致。区别在于发起成功后默认为"手机端听-设备端说"模式。

### 听说模式切换
```typescript
// 切换到 手机端说-设备端听
this.talkPlayer?.audioTalkPressed(true)

// 切换到 手机端听-设备端说
this.talkPlayer?.audioTalkPressed(false)
```

## 四、本地音频音量回调

对讲时获取手机本地采集的音频音量，用于音柱效果展示：
```typescript
// 在 startVoiceTalk 之前设置
this.talkPlayer?.setVoiceTalkLoudnessCallback(this.loudnessCallback, 0.15)

private loudnessCallback: OnLoudnessListener = {
  onReportLoudness: (loudness: number): void => {
    // loudness 为分贝值（负数）
    // [-90, -40) 显示1格
    // [-40, -35) 显示2格
    // [-35, -30) 显示3格
    // [-30, -20) 显示4格
    // [-20, 0]   显示5格
  }
}
```

## 五、注意事项

1. 对讲播放器必须独立于预览/回放播放器，播放器功能是单一的
2. **对讲前必须申请麦克风权限**：务必在发起对讲前向系统申请 `ohos.permission.MICROPHONE` 权限，否则第一次对讲异常
3. 非国标设备对讲开启后关闭预览播放器声音，对讲结束后恢复
4. 国标设备不能关闭预览播放器声音
5. v1.1 起对讲播放器无须配置验证码
6. IPC 设备对讲通道号：AccessToken 模式下使用实际通道号；小权限 TKToken 模式下，对讲 streamToken 需使用 0 通道生成
7. `onPageHide` 中必须停止对讲，避免后台持续占用麦克风

## 六、EZMessageCode 对讲相关常量

| 常量 | 数值 | 含义 |
|------|------|------|
| PLAYER_VOICE_TALK_START | 4 | 对讲开始 |
| PLAYER_VOICE_TALK_END | 5 | 对讲结束 |

## 七、对讲相关错误码

| 错误码 | 含义 |
|--------|------|
| 400025 | 设备不支持对讲 |
| 360010 / 361010 | 设备正在对讲中 |
| 360012 / 361012 | 设备不在线 |
| 360013 / 361013 | 设备开启隐私保护 |
