/*
 * Copyright (c) 2021 NetEase, Inc.  All rights reserved.
 */

/// <reference types="webrtc" />
import {
  NERtcCanvasWatermarkConfig,
  MediaType,
  RenderMode,
  BeautyFilters,
  pluginOptions,
  BasicBeautyResConfig,
  AdvBeautyResConfig,
  AdvBeautyEffects,
  AdvBeautyPreset,
  BackGroundOptions,
  ReverbConfig,
  RecordStatus,
  STREAM_TYPE,
  NERtcEncoderWatermarkConfig,
  AudioFrame,
  enableAIDenoiseOptions
} from './types'
/**
 * 音视频流对象。
 *
 * Stream 接口提供的方法用于定义音视频流对象的行为，例如流的播放控制、音视频的编码配置等。
 *
 * 您可以使用 [[NERTC.createStream]] 创建音视频流对象。 一个 Stream 对象指通话中发布的本地音视频流或订阅的远端音视频流。
 */
declare interface Stream {
  /**
   *  获取音视频流 ID。
   *
   * @example
   * ```Javascript
   * let uid = stream.getId()
   * ```
   */
  getId(): number | string

  /**
   * 设置视频订阅的参数。
   *
   * 注意：
   * * 如果您想取消订阅远端所有媒体，应使用 [[Client.unsubscribe]]
   * * 如果您在已经订阅了远端的视频大流的情况下，想实时切换为小流，应使用[[Client.setRemoteStreamType]]
   * * 参数 highOrLow中，0 表示大流，1 表示小流。默认为大流。为了保持代码清晰，建议使用常量 `NERTC.STREAM_TYPE.HIGH` 和 `NERTC.STREAM_TYPE.LOW`指定。
   * * 如果您想取消订阅远端的音频，但是保持视频不变，则应该设audio为false，video不设（见例子2）
   * @example
   * ```
   * // 例子1：订阅大流
   * rtc.client.on("stream-added", (evt)=>{
   *   console.log(`远端${evt.stream.getId()}发布了 ${evt.mediaType} 流`)
   *   evt.stream.setSubscribeConfig({
   *     audio: true,
   *     audioSlave: true,
   *     video: true,
   *     screen: true,
   *     highOrLow: NERTC.STREAM_TYPE.HIGH
   *   })
   *   rtc.client.subscribe(evt.stream)
   * });
   *
   * // 例子2：在音视频已经订阅的情况下，仅取消订阅音频
   * remoteStream.setSubscribeConfig({audio: false});
   * rtc.client.subscribe(remoteStream)
   * ```
   *
   */
  setSubscribeConfig(subscribeOptions: {
    /**
     * 是否订阅音频。
     */
    audio?: boolean
    /**
     * 是否订阅音频辅流。
     */
    audioSlave?: boolean
    /**
     * 是否订阅视频。
     */
    video?: boolean
    /**
     * 是否订阅屏幕共享。
     */
    screen?: boolean
    /**
     * 订阅大流或小流。
     *
     * 0 表示大流，1 表示小流。默认为大流。
     * 可以使用常量 `NERTC.STREAM_TYPE.HIGH` 和 `NERTC.STREAM_TYPE.LOW`指定
     */
    highOrLow?: STREAM_TYPE
  }): void

  /**
   * 获取音频流 MediaStream 对象，可用于自定义音频渲染。
   *
   * 您可以自行渲染这个对象，例如将 audio dom 节点的 srcObject 属性设为该对象。
   *
   * @note 使用自定义音频渲染功能时，应该在播放远端流时，关闭默认的音频渲染。
   *
   * @example
   * ```JavaScript
   * remoteStream.play({
   *    audio: false,
   *    video: true
   * });
   * const audioStream = remoteStream.getAudioStream();
   * // audioDom为自行创建的DOM节点
   * audioDom.srcObject = audioStream;
   * ```
   */
  getAudioStream(): MediaStream | null
  /**
   * 初始化音视频流对象。
   *
   * 该方法用于初始化本地创建的音视频流对象。
   *
   * @example
   * ```JavaScript
   * await localStream.init();
   * ```
   */
  init(): Promise<void>
  /**
   * 获取音频轨道。
   * 默认获取主流音频轨道（麦克风）
   *
   * @example
   * ```JavaScript
   * let audioTrack = stream.getAudioTrack('audio')
   * ```
   */
  getAudioTrack(mediaType?: 'audio' | 'audioSlave'): MediaStreamTrack | null
  /**
   * 获取视频轨道。
   * 默认获取主流视频轨道（摄像头）
   *
   *  @example
   * ```JavaScript
   * let audioTrack = localStream.getVideoTrack('video')
   * ```
   *
   */
  getVideoTrack(mediaType?: 'video' | 'screen'): MediaStreamTrack | null | undefined
  /**
   * 播放音视频流。
   *
   * @param view div 标签，播放画面的 dom 容器节点。
   * @param playOptions 播放的音视频选项。
   *
   * @note 注意
   * * 采用自定义视频输入时，部分 safari 版本不支持播放 CanvasCaptureMediaStreamTrack，请升级系统。详见 [https://bugs.webkit.org/show_bug.cgi?id=181663](https://bugs.webkit.org/show_bug.cgi?id=181663)
   *
   * @example
   * ```javascript
   *    // 本地流
   *    // 在await rtc.localStream.init之后
   *    await rtc.localStream.init();
   *    rtc.localStream.play(document.getElementById("local-video-wrapper"), {
   *      audio: false,
   *      audioSlave: false,
   *      video: true,
   *      screen: true,
   *    });
   *    rtc.localStream.setLocalRenderMode({
   *      width: 200,
   *      height: 200,
   *      cut: false
   *    });
   *
   *    // 远端流
   *    // 在stream-subscribed之后
   *    rtc.client.on("stream-subscribed", (evt)=>{
   *        evt.stream.play(document.getElementById("remote-video-wrapper", {
   *          audio: true,
   *          audioSlave: true,
   *          video: true,
   *          screen: true,
   *        });
   *        evt.stream.setRemoteRenderMode({
   *          width: 200,
   *          height: 200
   *          cut: false
   *        });
   *    })
   * ```
   */
  play(
    view?: HTMLElement | String | null,
    playOptions?: {
      /**
       * 是否播放音频流。
       *
       * 默认播放不本地音频流，播放远端音频流。
       */
      audio?: boolean
      /**
       * 是否播放音频辅流。
       *
       * 默认不播放本地音频辅流，播放远端音频辅流。
       */
      audioSlave?: boolean
      /**
       * 是否播放视频流。
       *
       * 默认播放视频流。
       */
      video?: boolean
      /**
       * 是否播放辅流。
       *
       * 默认播放辅流。
       *
       * 主流和辅流可在不同画布上播放。需调用play两次，即:
       * ```javascript
       *   localStream.play(videoContainer, {video: true, screen: false});
       *   localStream.play(screenContainer, {video: false, screen: true});
       * ```
       */
      screen?: boolean
      /**
       * 是否播放第三路视频流。
       */
      videoThird?: boolean
      /**
       * 是否播放第四路视频流。
       */
      videoFourth?: boolean
      /**
       * 是否静音播放音频流。
       *
       * 默认不静音播放音频流。
       */
      muted?: boolean
      /**
       * 音频播放类型
       */
      audioType?: 'voice' | 'music' | 'mixing'
      /**
       * 是否混音后播放音频流（v5.8.0开始支持）
       *
       * 默认不混音播放音频流。
       *
       * 注意：音频辅流不支持混音。
       */
      mixAudioStream?: boolean
    }
  ): Promise<void>

  /**
   * 监测到自动播放受限后，重新播放音视频流。
   *
   * @example
   * ```javascript
   *    // 在监听到 notAllowedError (自动播放受限)之后调用 resume 可以恢复播放
   *    rtc.client.on('notAllowedError', err => {
   *      const errorCode = err.getCode();
   *      if(errorCode === 41030){
   *        await remoteStream.resume();
   *      }
   *     })
   * ```
   */
  resume(): Promise<void>

  /**
   * 设置本地视频画布。
   *
   * 该方法设置本地视频画布。只影响本地用户看到的视频画面，不影响远端。
   *
   * 例子见[[Stream.play]]。
   * @param options 配置对象。
   * @param mediaType 媒体流类型。即指定设置的是摄像头画面还是屏幕共享画面。
   *
   * @example
   * ```javascript
   *  localStream.setLocalRenderMode({
   *    width: 200,
   *    height: 200,
   *    cut: false
   *  });
   * ```
   *
   */
  setLocalRenderMode(
    options: RenderMode,
    mediaType?: 'video' | 'screen' | 'videoThird' | 'videoFourth'
  ): 'INVALID_ARGUMENTS' | undefined

  /**
   * 设置远端视频画布。
   *
   * 该方法绑定远端用户和显示视图，只影响本地用户看到的视频画面。退出房间后，SDK 会清除远端用户和视图的绑定关系。
   *
   * 例子见[[Stream.play]]。
   * @param options 配置对象。
   * @param mediaType 媒体流类型。即指定设置的是摄像头画面还是屏幕共享画面。
   *
   * @example
   * ```javascript
   *  remoteStream.setRemoteRenderMode({
   *    width: 200,
   *    height: 200,
   *    cut: false
   *  });
   * ```
   */
  setRemoteRenderMode(
    options: RenderMode,
    mediaType?: 'video' | 'screen' | 'videoThird' | 'videoFourth'
  ): void

  /**
   * 停止音视频流。
   *
   * 该方法用于停止播放 Stream.play 播放的音视频流。
   * @param mediaType 媒体流类型。即指定设置的是摄像头画面还是屏幕共享画面。
   * @example
   * ```javascript
   * // 对于本地流，在 rtc.localStream.play() 后
   * rtc.localStream.stop("video") //停止播放视频
   * // 或者
   * rtc.localStream.stop() //停止播放音频+视频+屏幕共享
   *
   * // 对于远端流，在 stream-removed 后
   * rtc.client.on("stream-removed", (evt)=>{
   *   console.log("远端移除了媒体类型：", evt.mediaType)
   *   evt.stream.stop(evt.mediaType)
   * })
   *
   * ```
   *
   */
  stop(type?: MediaType): void
  /**
   * 返回音视频流当前是否可以播放。
   * 该API用于辅助判断当前流的状态，即：是否可以播放，为什么不能播放
   *
   * @param type 媒体流类型。
   * @return
   *  - result：当前是否可以播放。如为true，则可以调用 [[Stream.play]]
   *  - reason：如果当前流不能播放，则不能播放的原因是什么。包括：
   *    - `NOT_PUBLISHED`: 远端没有发布该媒体
   *    - `NOT_SUBSCRIBED`: 还没有订阅远端流
   *    - `CONSUME_START`: 正在订阅远端流中
   *    - `NOT_OPENED`: 本地流没有打开
   *    - `ENDED`: 本地流已结束（如设备被拔出）
   *    - `MUTED`: 本地流在黑屏状态，通常是调用了mute()，或者本地多次获取媒体导致当前媒体在异常状态。
   *    - `PAUSED`: 上一次播放行为在暂停状态，通常是上一次调用play()的行为受到了自动播放策略影响。
   *
   * @example
   * ```Javascript
   * let result = localStream.canPlay('video')
   * ```
   */
  canPlay(type: MediaType): { result: boolean; reason: string }
  /**
   * 返回音视频流当前是否在播放状态。
   * @param type 媒体流类型。
   * @return
   *  - true：该音视频流正在渲染或播放。
   *  - false：该音视频流没有渲染。
   *
   *  @example
   * ```Javascript
   *  let result = await rtc.localStream.isPlaying('audio')
   * ```
   */
  isPlaying(type: MediaType): boolean
  /**
   * 打开音视频输入设备，如麦克风、摄像头、屏幕共享，并且发布出去。
   *
   * 代码示例可见[[Stream.switchDevice]]
   *
   * @param options 配置对象。
   */
  open(options: {
    /**
     * 媒体流类型，即 audio、video 或 screen。
     *
     * 注意，Safari on MacOS 的屏幕共享需手势触发，且无法选择共享的屏幕、无法单独共享应用、无法共享音频。
     *
     */
    type: 'audio' | 'video' | 'screen' | 'screenAudio'
    /**
     * 指定要开启的设备ID。
     *
     * 您可以通过 getDevices 接口获取设备列表。
     */
    deviceId?: string
    /**
     * Electron 屏幕共享的数据源 ID，您可以自行获取。
     */
    sourceId?: string
    /**
     * 指定屏幕共享时是否共享本地播放的背景音。
     *
     * 仅在 type 为 screen 时有效。详细说明请参考 [[StreamOptions.screenAudio]]。
     *
     * 在V4.4.0之前，麦克风和屏幕共享音频不能同时开启。
     */
    screenAudio?: boolean
    /**
     * 自定义音频的track。type 为 audio 时生效。
     *
     * @since V4.6.0
     */
    audioSource?: MediaStreamTrack
    /**
     * 自定义视频的track。type 为 video 时生效。
     *
     * @since V4.6.0
     */
    videoSource?: MediaStreamTrack
    /**
     * 自定义屏幕共享视频的track。type 为 screen 时生效。
     *
     * @since V4.6.0
     */
    screenVideoSource?: MediaStreamTrack
    /**
     * 自定义屏幕共享音频的track。type 为 screen 且 screenAudio 为 true 时生效。
     *
     * @since V4.6.0
     */
    screenAudioSource?: MediaStreamTrack
    /**
     * 调用 open 接口时，是否进行 publish
     *
     * 若为 false，则不进行 publish；若为 true 或者不填，则进行 publish。
     *
     *
     * @since V5.4.0
     */
    enableMediaPub?: boolean
    /**
     * 用于选择打开前置/后置摄像头
     *
     * type 为 video 时有效，仅用于移动端。user 表示前置摄像头，environment 表示后置摄像头。
     *
     * deviceId 与 facingMode 不能同时存在，如果同时存在，facingMode 优先。
     *
     * @since V4.6.0
     */
    facingMode?: 'user' | 'environment'
  }): Promise<undefined>
  /**
   * 关闭音视频输入设备，如麦克风、摄像头、屏幕共享，并且停止发布。
   *
   * @example
   * ```
   *  // 例如，关闭屏幕共享
   *  rtc.localStream.close({ type: "screen"});
   * ```
   * @param {Object} options 配置对象
   */
  close(options: {
    /**
     * 媒体流类型，即 audio、video、 screen 或 screenAudio。
     */
    type: 'audio' | 'video' | 'screen' | 'screenAudio'
  }): Promise<undefined>
  /**
   * 启用音频轨道。
   *
   * @example
   * ```Javascript
   *  await stream.unmuteAudio()
   * ```
   */
  unmuteAudio(): Promise<void>
  /**
   * 禁用音频轨道。
   *
   * @example
   * ```Javascript
   *  await stream.muteAudio()
   * ```
   */
  muteAudio(): Promise<void>
  /**
   * 启用音频辅流轨道。
   *
   * @example
   * ```Javascript
   *  await stream.unmuteAudioSlave()
   * ```
   */
  unmuteAudioSlave(): Promise<void>
  /**
   * 禁用音频辅流轨道。
   *
   * @example
   * ```Javascript
   *  await stream.muteAudioSlave()
   * ```
   */
  muteAudioSlave(): Promise<void>
  /**
   * 获取音频 flag。
   *
   * 该方法用于确认当前音视频流对象（Stream）中是否包含音频资源。
   *
   * @note 该方法仅对本地流有效。
   *
   * @example
   * ```Javascript
   * let result = localStream.hasAudio()
   * ```
   *
   * @return
   * - true: 该音视频流对象中包含音频资源。
   * - false: 该音视频流对象中不包含音频资源。
   */
  hasAudio(): boolean
  /**
   * 获取音频辅流 flag。
   *
   * 该方法用于确认当前音视频流对象（Stream）中是否包含音频辅流资源。
   *
   * @note 该方法仅对本地流有效。
   *
   *  @example
   * ```Javascript
   * let result = localStream.hasAudioSlave()
   * ```
   *
   * @return
   * - true: 该音视频流对象中包含音频辅流资源。
   * - false: 该音视频流对象中不包含音频辅流资源。
   */
  hasAudioSlave(): boolean
  /**
   * 获取当前音量。
   *
   * @note 注意
   * * 在版本5.5.0之前，该音量的取值范围为 0 - 1
   * * 在版本5.5.0及之后，该音量的取值范围为 0 - 100。在会议场景下，音量大于约40可认为参会者在说话。
   * * 从 v4.6.25开始，该接口也支持远端。远端音量可通过 mediaType 指定获取主流音量或辅流音量。获取远端音量时，如发现发送端和接收端不太一致，可尝试在发送端使用[[Stream.setAudioProfile]]将profile设置为`high_quality`。本地音量不支持mediaType。
   * * Safari 浏览器的桌面端14.1以下、iOS端14.5以下，不支持获取音量
   * * 目前发现 Chrome 104 以上版本获取音量会引发内存泄漏。该问题源于Chrome浏览器RTCPeerConnection与AudioWorkletNode协作时引发，目前无法回避。如有长时间通话需求，应避免使用该方法。
   *
   * @example
   * ```Javascript
   *  let audioLevel = stream.getAudioLevel()
   * ```
   */
  getAudioLevel(mediaType?: 'audio' | 'audioSlave'): number
  /**
   * 开启音频PCM数据回调
   * @param duration 音频数据时长，单位为毫秒,最小100ms，最大10000ms, 默认1000ms
   *
   * @return
   * - true: 开启成功。
   * - false: 开启失败。
   *
   * @example
   * ```Javascript
   *  localStream.on('audio-data', (frame) => {})
   *  localStream.enableAudioFrame(1000)
   * ```
   *
   * @note 注意
   * * 在调用enableAudioFrame()之后，'audio-data'才能收到回调。
   * * 如果需要修改duration，需要先调用disableAudioFrame()关闭，再调用enableAudioFrame()开启。
   * * 目前发现 Chrome 104 以上版本获取音量会引发内存泄漏。该问题源于Chrome浏览器RTCPeerConnection与AudioWorkletNode协作时引发，目前无法回避。如有长时间通话需求，应避免使用该方法。
   *
   */
  enableAudioFrame(duration?: number): boolean
  /**
   * 关闭音频PCM数据回调
   *
   * @return
   * - true: 关闭成功。
   * - false: 关闭失败。
   *
   * @example
   * ```Javascript
   *  localStream.disableAudioFrame()
   * ```
   *
   */
  disableAudioFrame(): boolean
  /**
   * 设置音频属性。
   *
   * @param profile 要设置的音频的属性类型，可设置为：
   * * speech_low_quality（表示16 kHz 采样率，单声道，编码码率约 24 Kbps）
   * * speech_standard（表示32 kHz 采样率，单声道，编码码率约 24 Kbps）
   * * music_standard（表示48 kHz 采样率，单声道，编码码率约 40 Kbps）
   * * standard_stereo（表达48 kHz 采样率，双声道，编码码率约 64 Kbps）
   * * high_quality（表示48 kHz 采样率，单声道， 编码码率约 128 Kbps）
   * * high_quality_stereo（表示48 kHz 采样率，双声道，编码码率约 192 Kbps）
   *
   * @example
   * ```Javascript
   *  localStream.setAudioProfile('speech_low_quality')
   * ```
   */
  setAudioProfile(profile: string): void

  /**
   * 设置音频采集3A开关。
   * 该接口与createStream时的audioProcessing参数类似，可控制音频采集的3A开关。除此之外，也可以控制辅流（屏幕共享）音频的3A开关。
   *
   * @note 注意
   * 1. 该接口自v5.5.0起生效。
   * 2. 该接口仅对本地流有效，调用时机为打开对应音频媒体之前。如果在打开音频媒体之后调用，将会在下一次打开音频媒体时生效。
   * 3. 该接口会覆盖createStream时的audioProcessing设置、也会覆盖setAudioProfile时某些选项对3A开关的修改。
   *
   * @example 示例代码
   * ```
   * // 本例以屏幕共享音频为例。默认情况下，屏幕共享音频的3A开关是关闭的。本例将打开3A开关。
   * // 在调用createStream之后、打开屏幕共享之前
   * rtc.localStream.setAudioProcessing('audioSlave', {
   *   // 打开回声消除
   *   AEC: true,
   *   // 打开自动增益
   *   AGC: true,
   *   // 打开噪声抑制
   *   ANS: true,
   * })
   * // 打开屏幕共享音视频
   * rtc.localStream.open({type: 'screen', screenAudio: true)
   *
   * ```
   */
  setAudioProcessing(
    mediaType: 'audio' | 'audioSlave',
    audioProcessing: {
      AEC?: boolean
      ANS?: boolean
      AGC?: boolean
    }
  ): undefined
  /**
   * 设置音频播放的音量。
   * @param volume 要设置的远端音频的播放音量，范围为 [0-100]。0 表示静音。
   *
   * @note 注意
   * 由于系统限制，ios上目前不支持设置远端音频音量。
   *
   * @example
   * ```Javascript
   *  stream.setAudioVolume(50)
   * ```
   */
  setAudioVolume(volume?: number): string | undefined
  /**
   * 设置音频辅流播放的音量。
   * @param volume 要设置的远端音频辅流的播放音量，范围为 [0-100]。0 表示静音。
   *
   * @note 注意
   * 由于系统限制，ios上目前不支持设置远端音频辅流音量。
   *
   * @example
   * ```Javascript
   *  remoteStream.setAudioSlaveVolume(50)
   * ```
   */
  setAudioSlaveVolume(volume?: number): string | undefined
  /**
   * 设置麦克风采集的音量。
   * @param volume 要设置的采集音量。范围为 [0-1000]。0 表示静音, 100表示正常的最大音量，大于100使用音量增益。
   * @param mediaTypeAudio 要设置的采集类型。可分开设置麦克风与屏幕共享音频。如您未使用屏幕共享音频功能，则忽略该参数。
   *
   *  @example
   * ```Javascript
   * // 设置采集值为 50 的麦克风音量
   *  localStream.setCaptureVolume(50, 'microphone')
   * ```
   */
  setCaptureVolume(
    volume: number,
    mediaTypeAudio?: 'microphone' | 'screenAudio'
  ): string | undefined
  /**
   * 设置订阅流的音频输出设备。
   *
   * 该方法可以在语音场景下设置订阅流的音频输出设备，在通话时切换扬声器。在播放订阅流之前或之后都可以调用该方法。
   *
   * @note
   * - 在播放订阅流之前或之后都可以调用该方法。
   * - 兼容性
   *  - Chrome 浏览器
   *  - safari 浏览器（mac系统，v18.4版本开始支持）
   * - [由于Chrome的限制](https://groups.google.com/g/discuss-webrtc/c/vrw44ZGE0gs/m/2YJ6yUEjBgAJ)，所有远端流会共享同一个音频输出设备。
   * - 从v5.6.41版本开始，该接口支持在开启远端音频流混流后调用。
   *
   * @param deviceId 设备的 ID，可以通过 getDevices 方法获取。获取的 ID 为 ASCII 字符，字符串长度大于 0 小于 256 字节。
   *
   * @example
   * ```javascript
   * const audioOutputDevice = (await NERTC.getSpeakers())[0]
   * console.log("设置扬声器为", audioOutputDevice.label, audioOutputDevice.deviceId)
   * remoteStream.setAudioOutput(audioOutputDevice.deviceId)
   * ```
   *
   */
  setAudioOutput(deviceId: string, callback?: (err: any) => void): Promise<void>
  /**
   * 切换媒体输入设备。
   *
   * 该方法用于切换本地流的媒体输入设备，例如麦克风，摄像头。
   *
   * @note 注意
   * 1. 已经发布的流，切换后不用重新发流。
   * 2. 以摄像头为例，未打开摄像头时，应使用 [[Stream.open]] 打开设备。
   * 3. 部分移动端设备由于无法同时打开两个摄像头，因此会导致设备切换失败。此时可通过关闭摄像头（[[Stream.close]] ）再打开新的摄像头（[Stream.open]）的方式完成切换
   * 4. 由于部分浏览器差异，如果需要实现前后置摄像头切换，建议参考以下教程：https://doc.yunxin.163.com/nertc/guide/jcxMDM4OTQ?platform=web
   *
   * @example
   * ```javascript
   * // rtc.localStream.init() 之后
   * if (rtc.localStream.hasVideo()){
   *   await rtc.localStream.switchDevice("video", "1275f2a4df844f0bfc650f005fef5eb9415379761f4b36c3d12ca1b72948d6a8") // 通过 NERTC.getDevices() 获取
   * } else {
   *   await rtc.localStream.open({
   *     type: "video",
   *     deviceId: "1275f2a4df844f0bfc650f005fef5eb9415379761f4b36c3d12ca1b72948d6a8", // 通过 NERTC.getDevices() 获取
   *   });
   *   // 通常open后需配合播放使用
   *   rtc.localStream.play(document.getElementById("local-video-wrapper"));
   *   rtc.localStream.setLocalRenderMode({
   *     width: 200,
   *     height: 200,
   *     cut: false
   *   });
   * }
   *
   * ```
   *
   * @param {String} type 设备的类型。
   * * "audio": 音频输入设备
   * * "video": 视频输入设备
   * @param deviceId 设备的 ID，可以通过 getDevices 方法获取。获取的 ID 为 ASCII 字符，字符串长度大于 0 小于 256 字节。
   *
   */
  switchDevice(type: 'audio' | 'video', deviceId: string): Promise<void>

  /**
   * 切换屏幕共享。
   *
   * 该方法用于动态切换本地流的屏幕共享输入。
   *
   * @note 注意
   * 1. 已经发布的流，切换后不用重新发流。
   *
   * @example
   * ```javascript
   * // rtc.localStream.init() 之后
   * if (rtc.localStream.hasScreen()){
   *   //之前已经开启过屏幕共享，可以直接调用switchScreenStream切换共享源
   *   await rtc.localStream.switchScreenStream()
   * } else {
   *   await rtc.localStream.open({
   *     type: "screen"
   *   });
   *   // 通常open后需配合播放使用
   *   rtc.localStream.play(document.getElementById("local-screen-wrapper"));
   *   rtc.localStream.setLocalRenderMode({
   *     width: 200,
   *     height: 200,
   *     cut: false
   *   });
   * }
   *
   * ```
   *
   * @param options 可以设置自定义的屏幕共享输入。
   *
   */
  switchScreenStream(options?: { screenVideoSource: MediaStreamTrack }): Promise<void>
  /**
   * 启用视频轨道。
   *
   * 视频轨道默认为开启状态。如果您调用了 [[Stream.muteVideo]]，可调用本方法启用视频轨道。
   *
   * 对本地流启用视频轨道后远端会触发 Client.on("unmute-video") 回调。
   *
   * @note 对于本地创建的流，在 createStream 时将 video 设置为 true 才可使用该方法。
   *
   * @example
   * ```Javascript
   *  await stream.unmuteVideo()
   * ```
   */
  unmuteVideo(): Promise<void>
  /**
   * 禁用视频轨道。
   *
   * - 对于本地流，调用该方法会停止发送视频，远端会触发 Client.on("mute-video") 回调。
   * - 对于远端流，调用该方法仍然会接收视频，但是会停止播放。
   *
   * @note 对于本地创建的流，在 createStream 时将 video 设置为 true 才可使用该方法。
   *
   * @example
   * ```Javascript
   *  await stream.muteVideo()
   * ```
   *
   */
  muteVideo(): Promise<void>
  /**
   * 启用屏幕共享轨道。
   *
   * 如果您调用了 muteScreen，可调用本方法启用屏幕共享轨道。远端会触发 Client.on("ummute-screen") 回调。
   *
   * @example
   * ```Javascript
   *  await stream.unmuteScreen()
   * ```
   */
  unmuteScreen(): Promise<void>
  /**
   * 禁用屏幕共享轨道。
   *
   * 调用该方法会停止发送屏幕共享，远端会触发 Client.on("mute-screen") 回调。
   *
   * @example
   * ```Javascript
   *  await stream.muteScreen()
   * ```
   */
  muteScreen(): Promise<void>
  /**
   * 获取视频 flag。
   *
   * 该方法用于确认当前音视频流对象（Stream）中是否包含视频资源。
   *
   * @note 该方法仅对本地流有效。
   *
   * @example
   * ```Javascript
   * let result = localStream.hasVideo()
   * ```
   *
   * @return
   * - true: 该音视频流对象中包含视频资源。
   * - false: 该音视频流对象中不包含视频资源。
   */
  hasVideo(): boolean

  /**
   * 设置视频属性。
   *
   * @note 注意
   * * setVideoProfile方法会根据预设值使用合适的上行码率。如您之前调用过 [[Stream.setVideoEncoderConfiguration]] ，则原设置会被覆盖。
   * * 从 V4.6.20 起，可以在打开摄像头后继续通过调用该方法动态修改分辨率。该功能由于浏览器和摄像头的限制，可能出现以下情况：
   * 1. 从低分辨率切换到高分辨率失败
   * 2. 从高分辨率切换到低分辨率时长宽比不完全符合profile设定
   * 3. 在多个页面开启摄像头或者打开大小流的情况下，分辨率切换失败
   * 4. ios16的bug导致的动态切换视频属性仅对本地生效、远端不生效
   * 5. 由于系统限制，ios 13/14 上无法使用1080p的Profile
   *
   * SDK仅保证在这些情况下的设备可用及码率切换正常。
   *
   * @example
   * ```javascript
   * // init前，设置分辨率及帧率
   * rtc.localStream = createStream({
   *   video: true,
   *   audio: true,
   *   uid: 123,
   *   client: rtc.client
   * });
   * rtc.localStream.setVideoProfile({
   *   resolution: NERTC.VIDEO_QUALITY.VIDEO_QUALITY_1080p,
   *   frameRate: NERTC.VIDEO_FRAME_RATE.CHAT_VIDEO_FRAME_RATE_15,
   *   width: 1920, //如果单独设置了width、height，会替代resolution
   *   height: 1080
   * })
   * await rtc.localStream.init()
   *
   *
   * // init后，动态下调分辨率
   * rtc.localStream.setVideoProfile({
   *   resolution: NERTC.VIDEO_QUALITY.VIDEO_QUALITY_720p,
   * })
   * ```
   */
  setVideoProfile(options: {
    /**
     * 设置本端视频分辨率，详细信息请参考 [[NERTC.VIDEO_QUALITY]]。
     */
    resolution: number
    /**
     * 设置本端视频帧率，详细信息请参考 [[NERTC.VIDEO_FRAME_RATE]]。
     */
    frameRate: number
    /**
     * 自定义设置本端视频宽（如果单独设置了width、height，会替代resolution）。
     */
    width?: number
    /**
     * 自定义设置本端视频高（如果单独设置了width、height，会替代resolution）。
     */
    height?: number
  }): void
  /**
   * 获取屏幕共享 flag。
   *
   * 该方法用于确认当前音视频流对象（Stream）中是否包含屏幕共享资源。
   *
   * @note 该方法仅对本地流有效。
   *
   * @example
   * ```Javascript
   * let result = localStream.hasScreen()
   * ```
   *
   * @return
   * - true: 该音视频流对象中包含屏幕共享资源。
   * - false: 该音视频流对象中不包含屏幕共享资源。
   */
  hasScreen(): boolean

  /**
   * 设置屏幕共享中的屏幕属性。
   *
   * 该方法设置屏幕共享时屏幕的显示属性，必须在 Stream.init 之前调用。
   *
   * 从 V4.6.20 起，可以在打开屏幕共享后继续通过调用该方法动态修改分辨率。该功能由于浏览器的限制，可能出现以下情况：
   * - 从低分辨率切换到高分辨率失败
   * - 从高分辨率切换到低分辨率时长宽比不完全符合profile设定
   * - 屏幕共享的边缘被切断
   *  SDK仅保证在这些情况下的设备可用及码率切换正常。
   *
   * @note 该方法仅可对本地流调用。
   *
   * @param profile 屏幕属性。
   *
   * @example
   * ```javascript
   * rtc.localStream = createStream({
   *   screen: true,
   *   uid: 456,
   *   client: rtc.client
   * });
   * rtc.localStream.setScreenProfile({
   *   resolution: NERTC.VIDEO_QUALITY.VIDEO_QUALITY_1080p,
   *   frameRate: NERTC.VIDEO_FRAME_RATE.CHAT_VIDEO_FRAME_RATE_15,
   *   width: 1920, //如果单独设置了width、height，会替代resolution
   *   height: 1080
   * })
   * await rtc.localStream.init()
   * ```
   *
   */
  setScreenProfile(profile: {
    /**
     * 设置本端屏幕共享分辨率。参考[[NERTC.VIDEO_QUALITY]]。
     *
     */
    resolution: number
    /**
     * 设置本端屏幕共享帧率。参考[[NERTC.VIDEO_FRAME_RATE]]。
     *
     */
    frameRate: number
    /**
     * 自定义设置本端视频宽（如果单独设置了width、height，会替代resolution）。
     */
    width?: number
    /**
     * 自定义设置本端视频高（如果单独设置了width、height，会替代resolution）。
     */
    height?: number
  }): void

  /**
   * 截取指定用户的视频流画面。
   *
   * 截图文件保存在浏览器默认路径下。
   *
   * @note
   * - v4.5.0之前，本地视频流截图，需要在 Client.join 并 Client.publish 发布流成功之后调用。
   * - 远端视频流截图，需要在  Client.subscribe 订阅远端视频流之后调用。
   * - 水印不会被截图。
   *
   * @example
   * ```javascript
   *  await stream.takeSnapshot({
   *    uid: 123,
   *    mediaType: 'video',
   *    name: 'xxx'
   *  })
   * ```
   */
  takeSnapshot(options: {
    /**
     * 截图文件名称，默认格式为 uid-1。
     */
    name: string
    /**
     * 截图的视频流类型。
     */
    mediaType?: 'video' | 'screen'
  }): Promise<'INVALID_OPERATION' | undefined>

  /**
   * 截取指定用户的视频流画面，并生成 Base64。
   *
   *
   * @note
   * - 本地视频流截图，需要在 Client.join 并 Client.publish 发布流成功之后调用。
   * - 远端视频流截图，需要在  Client.subscribe 订阅远端视频流之后调用。
   * - 水印不会被截图。
   *
   * @returns 截图画面生成的 Base64。
   *
   * @example
   * ```javascript
   *  await stream.takeSnapshotBase64({ mediaType: 'video' })
   * ```
   *
   */
  takeSnapshotBase64(options: {
    /**
     * 截图的视频流类型。
     */
    mediaType?: 'video' | 'screen'
  }): string

  /**
   * 开启单人视频录制。
   *
   * @param mediaRecordingOptions 参数对象。
   *
   * @example
   * ```Javascript
   *  await stream.startMediaRecording({type: 'audio', reset: true})
   * ```
   */
  startMediaRecording(mediaRecordingOptions: {
    /**
     * 流类型，即 'audio'、'video' 或 'screen'。其中，`video` 或 `screen` 会带上音频。
     */
    type: 'audio' | 'video' | 'screen'
    /**
     * 如果之前的录制视频未下载，是否重置，默认 false。
     */
    reset: boolean
  }): Promise<string | undefined>

  /**
   * 结束视频录制。
   * @param options 参数对象。
   *
   * @example
   * ```Javascript
   *  await stream.stopMediaRecording({recordId: 'xxxx'})
   * ```
   */
  stopMediaRecording(options: {
    /**
     * 录制 ID。可以通过 [[Stream.listMediaRecording]] 接口获取。
     *
     */
    recordId?: string
  }): Promise<any>

  /**
   * 播放视频录制。
   * @param options 参数对象。
   *
   * @example
   * ```Javascript
   *   let result = await stream.playMediaRecording({
   *     view: document.getElementById('xxx'),
   *     recordId: '12345'
   *   })
   * ```
   */
  playMediaRecording(options: {
    /**
     * 录制 ID。可以通过 [[Stream.listMediaRecording]] 接口获取。
     */
    recordId: string
    /**
     * 音频或者视频画面待渲染的 DOM 节点，如 div、span 等非流媒体节点。
     */
    view: HTMLElement
  }): Promise<void>
  /**
   * 枚举录制的音视频。
   *
   * @returns 录制的音视频信息。
   * - `id` ：ID。
   * - `type` ：录制类型。
   * - `name` ：录制文件名称。
   * - `status` ：录制状态。
   * - `isRecording` ：是否正在录制。
   * - `startTime` ：录制开始时间。
   * - `endTime` ：录制结束时间。
   *
   * @example
   * ```Javascript
   *  let records = stream.listMediaRecording()
   * ```
   */
  listMediaRecording(): {
    id: number
    type: string
    name: string | null
    status: string
    isRecording: boolean
    startTime: number | null
    endTime: number | null
  }[]

  /**
   * 清除录制的音视频。
   * @param options 参数对象。
   *
   * @example
   * ```Javascript
   * await client.cleanMediaRecording()
   * ```
   */
  cleanMediaRecording(options: {
    /**
     * 录制 ID。可以通过 [[Stream.listMediaRecording]] 接口获取。
     */
    recordId: string
  }): Promise<void>

  /**
   * 下载录制的音视频。
   * @param options 参数对象。
   *
   * @example
   * ```Javascript
   * await stream.downloadMediaRecording({
   *   recordId: '123456'
   * })
   * ```
   *
   */
  downloadMediaRecording(options: {
    /**
     * 录制 ID。可以通过 [[Stream.listMediaRecording]] 接口获取。
     */
    recordId: string
  }): Promise<RecordStatus>
  /**
   * 开始播放音乐文件。
   *
   * 该方法指定在线音频文件和麦克风采集的音频流进行混音或替换，即用音频文件替换麦克风采集的音频流。
   *
   * @note 请在 [[Client.publish]] 之后使用该方法。
   *
   * @param options 混音设置。
   *
   * @example
   * ```javascript
   * // await rtc.client.publish(rtc.localStream)
   * rtc.localStream.startAudioMixing({
   *   audioFilePath: $("#audioFilePath").val(),
   *   loopback: true,
   *   replace: false,
   *   cycle: 0,
   *   playStartTime: 0,
   *   volume: 100,
   *   interval: 100,
   *   durationChange: (duration) => {
          console.log('获取伴音文件总时长成功：', duration)
        },
       timeUpdate: (currentTime, duration) => {
          console.log('获取伴音文件当前播放时长：', currentTime, duration)
        },
   *   auidoMixingEnd: () => {console.log("伴音文件播放结束")},
   * })
   * ```
   *
   */
  startAudioMixing(options: {
    /**
     * 必选，在线音乐文件的 URL 地址。
     *
     * @note 目前仅支持在线音频文件，格式一般为 MP3 等浏览器支持的音频文件类型。
     *
     */
    audioFilePath: string
    /**
     * 可选，是否要用音频文件替换本地音频流。
     * - true：音频文件内容将会替换本地录音的音频流。
     * - false：（默认值）音频文件内容将会和麦克风采集的音频流进行混音。
     */
    replace: boolean
    /**
     * 可选，指定音频文件循环播放的次数。
     * @note
     * - 该参数仅对Chrome有效
     * - 通过 cycle 指定循环播放次数时，需要同时指定 loopback 参数置为 true。如果 loopback 为 false，该参数不生效。
     * - cycle 默认为 0，表示无限循环播放，直至调用 stopAudioMixing 后停止。
     */
    cycle: number
    /**
     * 可选，设置音频文件开始播放的时间位置，单位为秒（s）。默认为 0，即从头开始播放。
     */
    playStartTime: number
    /**
     * 可选，音乐文件的播放音量，取值范围为 >=0。默认为 100，表示使用文件的原始音量，小于100表示声音减益，大于100表示声音增益。
     * @note 若您在通话中途修改了音量设置，则当前通话中再次调用时默认沿用此设置。
     */
    volume?: number
    /**
     * 可选，timeUpdate 通知事件的间隔，单位为毫秒（ms）。默认为 100ms。
     */
    interval?: number
    /**
     * 可选，伴音文件播放时长变化的通知反馈。
     * @param duration 伴音文件的总时长，单位为秒（s）。
     * */
    durationChange?: (duration: number) => void
    /**
     * 可选，伴音文件播放进度变化的通知反馈。
     * @param currentTime 伴音文件当前播放的进度，单位为秒（s）。
     * @param duration 伴音文件的总时长，单位为秒（s）。
     */
    timeUpdate?: (currentTime: number, duration: number) => void
    /**
     * 可选，伴音文件播放完成的通知反馈。正常停止伴音或关掉通话获取其他原因停止伴音不会触发。
     */
    audioMixingEnd?: (() => void) | null
    /**
     * 是否循环播放音频文件，默认为 false。
     * - true：循环播放音频文件。此时可通过 cycle 设置循环播放次数，cycle 默认为 0，表示无限循环播放。
     * - false：（默认值）关闭无限循环播放。
     */
    loopback: boolean
  }): Promise<unknown> | undefined
  /**
   * 停止播放音乐文件。
   *
   * 请在房间内调用该方法。
   *
   * @example
   * ```Javascript
   *  await localStream.stopAudioMixing()
   * ```
   */
  stopAudioMixing(): Promise<void>
  /**
   * 暂停播放音乐文件。
   *
   * 请在房间内调用该方法。
   *
   * @example
   * ```Javascript
   * localStream.pauseAudioMixing()
   *   .then((res) => {
   *     console.log('暂停伴音成功')
   *   })
   *   .catch((err) => {
   *     console.error('暂停伴音失败', err)
   *   })
   * ```
   */
  pauseAudioMixing(): Promise<void> | null | undefined
  /**
   * 恢复播放音乐文件。
   *
   * 请在房间内调用该方法。
   *
   * @example
   * ```Javascript
   * await localStream.resumeAudioMixing()
   * ```
   */
  resumeAudioMixing(): Promise<void> | undefined
  /**
   * 调节音乐文件音量。
   *
   * 该方法调节混音里伴奏的播放音量大小。请在房间内调用该方法。
   *
   * @param volume 伴奏发送音量。取值范围为 0~100。默认 100，即原始文件音量。
   *
   * @example
   * ```Javascript
   * // 设置伴音音量为 50
   * localStream.adjustAudioMixingVolume(50))
   *   .then((res) => {
   *     console.log('设置伴音的音量成功')
   *   })
   *   .catch((err) => {
   *     console.error('设置伴音的音量失败', err)
   *   })
   * ```
   */
  adjustAudioMixingVolume(volume: number): Promise<void> | null | undefined
  /**
   * 获取音乐文件时长。
   *
   * 该方法获取伴奏时长，单位为秒。
   *
   * @example
   * ```Javascript
   *  await result = localStream.getAudioMixingDuration()
   *  let totalTime = result.totalTime
   * ```
   *
   * @returns 方法调用成功返回音乐文件时长，单位为秒（s）。
   *
   */
  getAudioMixingDuration(): void
  /**
   * 获取音乐文件当前播放进度。
   *
   * 该方法获取当前伴奏播放进度，单位为秒。
   *
   * @example
   * ```Javascript
   *  await result = localStream.getAudioMixingCurrentPosition()
   *  let playedTime = result.playedTime
   * ```
   *
   * @returns 方法调用成功返回音乐文件播放进度。
   */
  getAudioMixingCurrentPosition(): void
  /**
   * 设置音乐文件的播放位置。
   *
   * 该方法可以设置音频文件的播放位置，这样你可以根据实际情况播放文件，而非从头到尾播放整个文件。
   * @param playStartTime 音乐文件的播放位置，单位为秒。
   * @note 不要超过音频文件的总时长。
   *
   * @example
   * ```Javascript
   * await localStream.setAudioMixingPosition(100)
   * ```
   */
  setAudioMixingPosition(playStartTime: number): Promise<unknown>
  /**
   * 播放指定音效文件。
   * - 支持的音效文件类型包括 MP3，AAC 等浏览器支持的其他音频格式。仅支持在线 URL。
   * - playEffect 与 startAudioMixing 方法的区别在于，该方法更适合播放较小的音效文件，且支持同时播放多个音效。
   * @since V4.3.0
   * @note
   *    - 请在 publish 音频之后调用该方法。
   *    - 您可以多次调用该方法，通过传入不同的音效文件的 soundId 和 filePath，同时播放多个音效文件，实现音效叠加。为获得最佳用户体验，建议同时播放的音效文件不超过 3 个。
   *
   * @return 可能返回的错误码：
   *   - ""BROWSER_NOT_SUPPORT: 不支持的浏览器类型。
   *   - "INVALID_OPERATION"：非法操作，详细原因请查看日志，通常为状态错误。
   *   - "No MediaHelper": localStream 没有 init() 初始化，无法使用音效功能。
   *   - "Stream.playEffect:soundId"：soundId 参数格式错误。
   *   - "Stream.playEffect:filePath"：filePath 参数格式错误。
   *   - "Stream.playEffect:cycle"：cycle 参数格式错误。
   *
   * @example
   * ```Javascript
   *  let option = {cycle:1, filePath:'xxx', soundId:1}
   *  await localStream.playEffect(options)
   * ```
   */
  playEffect(options: {
    /**
     * 必选。指定在线音效文件的 URL地址。
     *
     * 支持的音效文件类型包括 MP3，AAC 等浏览器支持的其他音频格式。
     */
    filePath: string
    /**
     * 可选，指定音效文件循环播放的次数。默认值为 1，即播放 1 次。
     */
    cycle: number
    /**
     * 必选，指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
     *
     * 如果您已通过 preloadEffect 将音效加载至内存，确保 playEffect 的 soundID 与 preloadEffect 设置的 soundID 相同。
     */
    soundId: number
  }): Promise<unknown>
  /**
   * 停止播放指定音效文件。
   * @since V4.3.0
   * @note 请在房间内调用该方法。
   * @return 可能返回的错误码：
   *   - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *   - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *   - "Stream.playEffect:soundId"：soundId参数格式错误
   *
   * @param {Number} soundId 指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
   *
   * @example
   * ```Javascript
   *  await localStream.stopEffect(123)
   * ```
   *
   */
  stopEffect(soundId: number): Promise<unknown>
  /**
   * 暂停播放指定音效文件。
   * @since V4.3.0
   *
   * @note 请在房间内调用该方法。
   *
   * @param {Number} soundId 指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
   * @return {Promise}
   * 可能返回的错误码：
   *    - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *   - "SOUND_NOT_EXISTS": soundId指定的音效文件不存在
   *   - "INVALID_OPERATION"：非法操作，可以通过console日志查看原因，一般是状态不对
   *   - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *   - "Stream.pauseEffect:soundId"：soundId参数格式错误
   *
   * @example
   * ```Javascript
   * localStream.pauseEffect(1)
   *    .then((res) => {
   *      console.log('暂停文件播放成功: ', res)
   *    })
   *    .catch((err) => {
   *      console.error('暂停音效文件失败: ', err)
   *    })
   * ```
   *
   */
  pauseEffect(soundId: number): Promise<unknown>
  /**
   * 恢复播放指定音效文件。
   *
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   * @return 可能返回的错误码：
   *   - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *   - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *   - "Stream.resumeEffect :soundId": soundId 参数格式错误
   * @param {Number} soundId 指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
   * @return {Promise}
   *
   * @example
   * ```Javascript
   *  await localStream.resumeEffect(1)
   * ```
   */
  resumeEffect(soundId: number): Promise<unknown>
  /**
   * 调节指定音效文件的音量。
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   *
   * @param {Number} soundId 指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
   * @param {Number} volume 音效音量。整数，范围为 [0,100]。默认为 100，即原始文件音量。
   * @return {Promise}
   * 可能返回的错误码：
   *  - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *  - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *  - "Stream.setVolumeOfEffect:soundId": 参数格式错误
   *  - "Stream.setVolumeOfEffect:volume": 参数格式错误
   *
   * @example
   * ```Javascript
   *  await localStream.setVolumeOfEffect(1234, 50)
   * ```
   */
  setVolumeOfEffect(soundId: number, volume: number): Promise<unknown>
  /**
   * 预加载指定音效文件。
   *
   * 该方法缓存音效文件，以供快速播放。为保证通信畅通，请注意控制预加载音效文件的大小。
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   *
   * @param {Number} soundId 指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
   * @param {String} filePath 必选。指定在线音效文件的绝对路径。支持MP3、AAC 以及浏览器支持的其他音频格式。
   * @return {Object} 可能返回的错误码：
   *   - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *   - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *   - "Stream.preloadEffect:filePath": 参数格式错误
   *   - "Stream.preloadEffect:soundId": 参数格式错误
   *
   * @example
   * ```Javascript
   *  let option = {soundId:1, filePath:'xxx'}
   *  await localStream.preloadEffect(options)
   * ```
   */
  preloadEffect(soundId: number, filePath: string): Promise<unknown>
  /**
   * 释放指定音效文件。
   *
   * 该方法从内存释放某个预加载的音效文件，以节省内存占用。
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   *
   * @param {Number} soundId 指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
   * @return {Object} 可能返回的错误码：
   *   - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *   - "SOUND_NOT_EXISTS": soundId指定的音效文件不存在
   *   - "INVALID_OPERATION": 非法操作，可以查看console日志得到原因，一般是状态原因，如此时应处于播放、暂停状态，不能使用
   *   - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *   - "Stream.unloadEffect:soundId": 参数格式错误
   *
   * @example
   * ```Javascript
   *  await localStream.unloadEffect(123)
   * ```
   */
  unloadEffect(soundId: number): Promise<unknown>
  /**
   * 获取所有音效文件播放音量。
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   * @return 可能返回的错误码：
   * - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   * @return Array<{ soundId: number; volume: number }>
   * 返回一个包含 soundId 和 volume 的数组。每个 soundId 对应一个 volume。
   * + `soundId`: 为音效的 ID，正整数，取值范围为 [1,10000]。
   * + `volume`: 为音量值，整数，范围为 [0,100]。
   *
   * @example
   * ```Javascript
   * let result = localStream.getEffectsVolume()[0]
   * let soundId = result.soundId
   * let volume = result.volume
   * ```
   *
   */
  getEffectsVolume(): Array<{ soundId: number; volume: number }>
  /**
   * 设置所有音效文件播放音量。
   *
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   * @param {Number} volume 音效音量。整数，范围为 [0,100]。默认 100 为原始文件音量。
   * @return {void} 可能返回的错误码：
   *    - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *    - "Stream.setEffectsVolume:volume": volume 参数格式错误
   *    - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *
   * @example
   * ```Javascript
   *  localStream.setEffectsVolume(50)
   * ```
   *
   */
  setEffectsVolume(volume: number): void
  /**
   * 停止播放所有音效文件。
   *
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   * @return {Promise} 可能返回的错误码：
   *  - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *  - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *  - "Stream.playEffect:soundId"：soundId参数格式错误
   *
   * @example
   * ```Javascript
   *  await localStream.stopAllEffects()
   * ```
   */
  stopAllEffects(): Promise<unknown>
  /**
   * 暂停播放所有音效文件。
   * @note 请在房间内调用该方法。
   * @since V4.3.0
   * @return 可能返回的错误码：
   *    - "BROWSER_NOT_SUPPORT": 浏览器不支持
   *   - "SOUND_NOT_EXISTS": soundId指定的音效文件不存在
   *   - "INVALID_OPERATION"：非法操作，可以通过console日志查看原因，一般是状态不对
   *   - "No MediaHelper": localStream没有init()初始化,无法使用音效功能
   *   - "Stream.pauseEffect:soundId"：soundId参数格式错误
   * @return {Promise}
   *
   *  @example
   * ```Javascript
   *  localStream.pauseAllEffects()
   *    .then((res) => {
   *      console.log('pauseAllEffects 成功')
   *    })
   *    .catch((err) => {
   *      console.error('pauseAllEffects 失败: %o', err)
   *    })
   * ```
   */
  pauseAllEffects(): Promise<unknown>
  /**
   * 恢复播放所有音效文件。
   * @note
   * - 请在房间内调用该方法。
   * - 可能返回的错误码同 resumeEffect 一致
   *
   * @since V4.3.0
   * @return {Promise}
   *
   * @example
   * ```Javascript
   * await localStream.resumeAllEffects()
   * ```
   */
  resumeAllEffects(): Promise<unknown>

  /**
   * 获取指定音效文件时长。
   * 该方法获取音效时长，单位为毫秒。请在房间内调用该方法。
   *
   * @example
   * ```Javascript
   * let option = {cycle:1, filePath:'xxx', soundId:1}
   * await duration = localStream.getAudioEffectsDuration(options)
   * ```
   *
   * @return 方法调用成功返回音效文件时长，单位为毫秒（ms）。
   */
  getAudioEffectsDuration(options: {
    /**
     * 必选。指定在线音效文件的 URL地址。
     *
     * 支持的音效文件类型包括 MP3，AAC 等浏览器支持的其他音频格式。
     */
    filePath: string
    /**
     * 可选，指定音效文件循环播放的次数。默认值为 1，即播放 1 次。
     */
    cycle: number
    /**
     * 必选，指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
     *
     */
    soundId: number
  }): Promise<unknown>
  /**
   * 获取音效文件当前播放进度。
   *
   * 该方法获取当前音效播放进度，单位为毫秒。请在房间内调用该方法。
   *
   * @example
   * ```Javascript
   * let option = {cycle:1, filePath:'xxx', soundId:1}
   * await result = localStream.getAudioEffectsCurrentPosition(options)
   * let playedTime = result.playedTime
   * ```
   *
   * @returns 方法调用成功返回音效文件播放进度。
   */
  getAudioEffectsCurrentPosition(options: {
    /**
     * 必选。指定在线音效文件的 URL地址。
     *
     * 支持的音效文件类型包括 MP3，AAC 等浏览器支持的其他音频格式。
     */
    filePath: string
    /**
     * 可选，指定音效文件循环播放的次数。默认值为 1，即播放 1 次。
     */
    cycle: number
    /**
     * 必选，指定音效的 ID。每个音效均有唯一的 ID。正整数，取值范围为 [1,10000]。
     *
     */
    soundId: number
  }): Promise<unknown>

  /**
   * 获取当前帧的数据。
   *
   * @note 注意
   *
   * 1. 对应的媒体只有在播放状态下才能调用该方法
   * 2. 该方法为阻塞方法，在多个Stream上频繁调用该方法可能导致页面卡顿
   * 3. 当前如不可截图，则返回null。
   * 4. 由于底层接口限制，该功能在部分部分低版本浏览上性能较低，偶现截图时间在10秒以上，如Safari 13等。
   * 5. 打码状态不能截图
   *
   * @example
   * ```
   * // 假设rtc.localStream开启了屏幕共享，那么在rtc.localStream.play()之后
   * setInterval(()=>{
   *   const imageData = rtc.localstream.getCurrentFrameData({mediaType: 'screen'})
   *   if (imageData){
   *     console.log(`getCurrentFrameData：${imageData.width}x${imageData.height}`)
   *    // ctx.putImageData(imageData, 0, 0, 0, 0, imageData.width,  imageData.height)
   *   }
   * }, 100)
   * ```
   */
  getCurrentFrameData(options: {
    mediaType: 'video' | 'screen' | 'videoThird' | 'videoFourth'
  }): ImageData | null

  /**
   * 视频上行参数设置。
   *
   *
   * @note 注意
   * * setVideoEncoderConfiguration 方法只作用于本地视频流。
   * * 在v4.6.20之后，该方法可以在会中使用，可动态调整参数。
   * * 使用 [[Stream.setVideoProfile]] 后，预设的 maxBitrate 会覆盖当前设置。
   * 请保证 setVideoEncoderConfiguration 在 setVideoProfile之后调用。
   *
   * @example 设置上行屏幕共享最大编码比特率为3M，流畅度优先：
   *
   * ```javascript
   * // rtc.localStream = NERTC.createStream({screen: true})
   * rtc.localStream.setVideoEncoderConfiguration({
   *   mediaType: "screen",
   *   streamType: "high",
   *   maxBitrate: 3000,
   *   contentHint: "motion",
   * })
   * // await rtc.localStream.init()
   * // await rtc.client.publish(rtc.localStream)
   * ```
   *
   */
  setVideoEncoderConfiguration(options: {
    mediaType: 'video' | 'screen'
    streamType: 'high' | 'low'
    maxBitrate?: number
    contentHint?: 'motion' | 'detail'
  }): void
  /**
   * 添加视频画布水印。
   *
   * @note 注意事项
   * * setCanvasWatermarkConfigs 方法作用于本地视频画布，不影响视频流。视频流截图时，图片中不包含水印。
   * *
   *
   * @param options 画布水印设置。支持设置文字水印、图片水印和时间戳水印，设置为 null 表示清除水印。
   *
   * @example
   * ```Javascript
   * // 设置图片水印参数
   *  let options = {
   *    "mediaType": "screen",
   *    "imageWatermarks": [
   *      {
   *        "imageUrls": [
   *          "img/logo_yunxin.png"
   *        ],
   *        "loop": true
   *      }
   *    ]
   *  }
   * // 添加水印
   *  localStream.setCanvasWatermarkConfigs(options)
   * ```
   */
  setCanvasWatermarkConfigs(options: NERtcCanvasWatermarkConfig): void
  /**
   * 添加视频编码水印。
   *
   * @note 注意事项
   * * setEncoderWatermarkConfigs 方法仅作用于本地Stream对象，且直接影响视频流。视频流截图时，图片中包含水印。
   * * 编码水印坐标是按原始摄像头采集计算的。如播放时画布通过cut=true参数做了裁剪，可能也会裁剪一部分水印。此时需要自行计算坐标偏移。
   * * 水印数量最多为1个。如有图文组合水印需求，可自行合成为1个图片。
   * * 由于浏览器策略限制，图片必须存于同一域名下。
   * * 文字水印不具备折行功能。
   * * 编码水印仅支持桌面端Chrome及Safari浏览器
   *
   * @param options 编码水印设置。支持设置文字水印、图片水印和时间戳水印，设置为 null 表示清除水印。
   *
   * @example
   * ```
   * // rtc.localStream.init()后
   * rtc.localStream.setEncoderWatermarkConfigs({
   *    "mediaType": "video",
   *    "textWatermarks": [
   *      {
   *        "content": "网易云信",
   *        "offsetX": 200,
   *        "offsetY": 200
   *      }
   *    ]
   *  })
   * rtc.localStream.setEncoderWatermarkConfigs({
   *    "mediaType": "screen",
   *    "imageWatermarks": [
   *      {
   *        "imageUrls": [
   *          "img/logo_yunxin.png"
   *        ],
   *        "loop": true
   *      }
   *    ]
   *  })
   * ```
   *
   */
  setEncoderWatermarkConfigs(options: NERtcEncoderWatermarkConfig): void

  /**
   * 配置基础美颜依赖的静态资源路径。
   * 默认走 cdn 加载且无需调用，
   * 当需要私有化部署或所在区域无法访问默认 cdn 节点时，需自行下载资源部署，并通过调用该方法进行资源配置。
   * 下载路径：https://yx-web-nosdn.netease.im/common/5e1e95a883139fe7a0847f3e68b9f1db/basic-beauty-res.zip
   * @param {BasicBeautyResConfig} config 资源配置信息
   */
  basicBeautyStaticRes(config: BasicBeautyResConfig): void

  /**
   * 配置高级美颜依赖的静态资源路径。
   * 默认走 cdn 加载且无需调用，
   * 当需要私有化部署或所在区域无法访问默认 cdn 节点时，需自行下载资源部署，并通过调用该方法进行资源配置。
   * 下载路径：https://yx-web-nosdn.netease.im/common/407fde90ffeb1147fa93199d346b9d5b/adv-beauty-res-6-25.zip
   * @param {AdvBeautyResConfig} config 资源配置信息
   */
  advBeautyStaticRes(config: AdvBeautyResConfig): void

  /**
   * 开启/关闭美颜
   * @param {Boolean} option 设置 true 表示开启美颜，设置 false 表示关闭美颜。
   *
   * @example
   * ```Javascript
   *  await localStream.setBeautyEffect(true)
   * ```
   *
   */
  setBeautyEffect(option: boolean): Promise<void>
  /**
   * 设置美颜效果
   *
   * @param options 美颜选项。
   *
   * @example
   * ```Javascript
   * //设置基础美颜参数并传递
   *  let effects = {
   *    brightnessLevel: 0.5,
   *    rednessLevel: 0.4,
   *    smoothnessLevel: 0.5
   *  }
   *  await localStream.setBeautyEffectOptions(effects)
   * ```
   *
   */
  setBeautyEffectOptions(options: {
    /**
     *
     * 明亮度。取值范围 [0,1]
     */
    brightnessLevel: number
    /**
     * 红润度。取值范围 [0,1]
     */
    rednessLevel: number
    /**
     * 平滑度。取值范围 [0,1]
     */
    smoothnessLevel: number
  }): void

  /**
   * 设置滤镜
   *
   * @param {BeautyFilters} options 滤镜选项。
   * @param {Number} intensity 滤镜强度。取值范围 [0,1]
   *
   * @example
   * ```Javascript
   * // 开启基础美颜功能
   *  rtc.localStream.setBeautyEffect(true)
   * // 设置滤镜参数并传递
   *  rtc.localStream.setFilter('ziran', 1);
   * // 需要关闭滤镜，将强度设置成 0 即可
   * ```
   */
  setFilter(options: BeautyFilters, intensity?: number): void

  /**
   * 注册高级美颜/背景替换/AI音效(原AI降噪)/啸叫检测)插件
   * @param {pluginOptions} options 插件参数说明
   * @param {String} options.key 插件标识
   * @param {String} options.pluginUrl 插件地址
   * @param {String} options.wasmUrl wasm地址
   * @param {Boolean} options.async 异步加载，v5.6.41开始支持，请设置为 true
   *
   * @example
   * ```Javascript
   * let options = {key:'VirtualBackground', pluginUrl:'xxx', wasmUrl:'xxx', async: true}
   *  await localStream.registerPlugin(options)
   * ```
   *
   */
  registerPlugin(options: pluginOptions): Promise<void>

  /**
   * 注销(高级美颜/背景替换/AI音效(原AI降噪)/啸叫检测)插件
   * @param key 插件标识，可设置为：
   * * AdvancedBeauty (表示注销高级美颜插件)
   * * VirtualBackground (表示注销背景替换插件)
   * * AIAudioEffects (表示注销AI音效插件)
   * * AIhowling (表示注销啸叫检测插件)
   *
   * @example
   * ```Javascript
   *  await localStream.unregisterPlugin('xxx')
   * ```
   *
   */
  unregisterPlugin(key: string): Promise<void>

  /**
   * 开启高级美颜
   * @param faceNumber 取值范围 [1,5]，表示可支持的人脸识别数，最多可以支持 5 张人脸。
   *
   * @example
   * ```Javascript
   * await localStream.enableAdvancedBeauty(2)
   * ```
   */
  enableAdvancedBeauty(faceNumber: number): Promise<void>

  /**
   * 关闭高级美颜
   *
   *
   * @example
   * ```Javascript
   * await localStream.disableAdvancedBeauty()
   * ```
   */
  disableAdvancedBeauty(): Promise<void>

  /**
   * 设置高级美颜效果
   *
   * @param {advBeautyEffects} key 高级美颜效果选项。
   * @param {Number} intensity 高级美颜效果强度。取值范围 [0,1]
   *
   * @example
   * ```Javascript
   * localStream.setAdvBeautyEffect({enlargeEye: 0.25})
   * ```
   *
   */
  setAdvBeautyEffect(key: AdvBeautyEffects, intensity?: number): void

  /**
   * 预设高级美颜参数
   * @param {AdvBeautyPreset} preset 预设参数
   *
   * @example
   * ```Javascript
   *  localStream.presetAdvBeautyEffect({
   *   // 大眼
   *   enlargeEye: 0.25,
   *   // 圆眼
   *   roundedEye: 0.5,
   *   // 窄脸
   *   narrowedFace: 0.25,
   *   // 瘦脸
   *   shrinkFace: 0.15,
   *   // v 脸
   *   vShapedFace: 0.33,
   *   // 小脸
   *   minifyFace: 0.15,
   *   // 亮眼
   *   brightenEye: 0.75,
   *   // 美牙
   *   whitenTeeth: 0.75
   * })
   * ```
   */
  presetAdvBeautyEffect(preset: AdvBeautyPreset): void

  /**
   * 开启背景分割
   *
   * @example
   * ```Javascript
   * await localStream.enableBodySegment()
   * ```
   */
  enableBodySegment(): Promise<void>

  /**
   * 关闭背景分割
   *
   * @example
   * ```Javascript
   * await localStream.disableBodySegment()
   * ```
   *
   */
  disableBodySegment(): Promise<void>

  /**
   * 设置背景
   * @param {BackGroundOptions} options 背景设置说明。
   *
   * @example
   * ```Javascript
   * localStream.setBackGround({ type: 'image', source: 'img' })
   * ```
   */
  setBackGround(options: BackGroundOptions): void

  /**
   * 开启AI降噪
   *  @param {enableAIDenoiseOptions} options 开启AI降噪参数说明(参数可选)
   * @example
   * ```Javascript
   * await localStream.enableAIDenoise()
   * ```
   */
  enableAIDenoise(options?: enableAIDenoiseOptions): Promise<boolean>

  /**
   * 关闭AI降噪
   *
   * @example
   * ```Javascript
   * await localStream.disableAIDenoise()
   * ```
   */
  disableAIDenoise(): Promise<boolean>

  /**
   * 设置背景人声消除阈值，需要在'ai-denoise-enabled'回调后调用。
   *
   * @param {Number} level 人声消除阈值，取值范围 [0, 100]，默认值为 0，值越大时过滤背景人声越多。
   *
   * @example
   * ```Javascript
   * localStream.setAIDenoiseLevel(2)
   * ```
   */
  setVoiceGate(level: number): void

  /**
   * 开启美声变声
   *
   * @example
   * ```Javascript
   * await localStream.enableAudioEffect()
   * ```
   */
  enableAudioEffect(): Promise<boolean>

  /**
   * 关闭美声变声
   *
   * @example
   * ```Javascript
   * await localStream.disableAudioEffect()
   * ```
   */
  disableAudioEffect(): Promise<boolean>

  /**
   * 设置美声变声效果
   * @param {Number | String} type 美声变声类型。
   * @param {Number | Array<number> | Object} value type对应的具体效果。
   *
   * 具体参数请参考AI音效开发文档。
   *
   * @example
   * ```Javascript
   * localStream.setAudioEffect(0, 1)
   * ```
   */
  setAudioEffect(type: Number | String, value: Number | Array<number> | ReverbConfig): void

  /**
   * 设置轻量版变声，该方法和插件版美声变声互斥，只能同时使用一个。
   * @param {String} type 美声变声类型，目前仅支持'Pitch'。
   * @param {Number} value type对应的具体效果。type = 'Pitch'时，取值范围 [-1, 1]，step = 0.1， 默认值为 0。
   *
   * @example
   * ```Javascript
   * await client.publish(localStream)
   * localStream.setAudioEffectLite('Pitch', 0)
   * ```
   */
  setAudioEffectLite(type: string, value: number): void

  /**
   * 关闭轻量版变声
   *
   * @example
   * ```Javascript
   * localStream.disableAudioEffectLite()
   * ```
   */
  disableAudioEffectLite(): void

  /**
   * 开启啸叫检测
   *
   * @example
   * ```Javascript
   * await localStream.enableAIhowling()
   * ```
   */
  enableAIhowling(): Promise<boolean>

  /**
   * 关闭啸叫检测
   *
   * @example
   * ```Javascript
   * await localStream.disableAIhowling()
   * ```
   */
  disableAIhowling(): Promise<boolean>

  /**
   * 注册啸叫回调,hasHowling为true表示检测到啸叫，false表示未检测到啸叫
   *
   * @example
   * ```Javascript
   * localStream.onAudioHasHowling(callback)
   * ```
   */
  onAudioHasHowling(callback: (hasHowling: boolean) => void): void

  /**
   *  销毁音视频流对象。
   *
   * @example
   * ```Javascript
   * stream.destroy()
   * ```
   */
  destroy(): void

  /**
   * 设备错误。
   * 1. 只有本地流才会有"device-error"回调
   * 2. 通常应该使用`Client`上的回调 `Client.on("deviceError")`。Client上有更丰富设备错误类型。只有当应用需要不止一个本地流，需要严格
   * 区分哪个本地流的设备出现设备问题时，才会用到这个回调。
   */
  on(
    event: 'device-error',
    callback: (type: 'audio' | 'video' | 'screen', error: any) => void
  ): void

  /**
   * 高级美颜/背景替换/AI音效(原AI降噪)/啸叫检测插件加载通知。
   *
   */
  on(
    event: 'plugin-load',
    callback: (
      type: 'AdvancedBeauty' | 'VirtualBackground' | 'AIAudioEffects' | 'AIhowling'
    ) => void
  ): void

  /**
   * 高级美颜/背景替换/AI音效(原AI降噪)/啸叫检测插件加载失败通知。
   *
   */
  on(
    event: 'plugin-load-error',
    callback: (evt: {
      type: 'AdvancedBeauty' | 'VirtualBackground' | 'AIAudioEffects' | 'AIhowling'
      msg: string
    }) => void
  ): void

  /**
   * 高级美颜/背景替换/AI音效(原AI降噪)/啸叫检测插件内部错误通知。
   *
   */
  on(
    event: 'plugin-inner-error',
    callback: (evt: {
      key: 'AdvancedBeauty' | 'VirtualBackground' | 'AIAudioEffects' | 'AIhowling'
      msg: string
    }) => void
  ): void

  /**
   * 基础美颜资源加载通知，成功时 failUrls 为空数组。
   *
   */
  on(event: 'basic-beauty-res-complete', callback: (failUrls: string[]) => void): void

  /**
   * AI降噪开启成功通知
   *
   */
  on(event: 'ai-denoise-enabled', callback: () => void): void

  /**
   * AI降噪开启时设置了啸叫消除，当音频经过啸叫消除处理后，仍然存在啸叫时，会通知该事件。
   * state: true 表示存在啸叫，false 表示不存在啸叫。
   * 通知 state 为 true后，如果后面的音频没有啸叫，会再次通知 state 为 false。
   */
  on(event: 'howling-suppression-result', callback: (state: boolean) => void): void

  /**
   * 美声变声开启成功通知，在开启后再调用 setAudioEffect 才会生效。
   *
   */
  on(event: 'audio-effect-enabled', callback: () => void): void

  /**
   * `notAllowedError` 事件表示浏览器自动播放受限
   *
   * @example
   * ```javascript
   * rtc.remoteStream.on("notAllowedError", (evt) => {
   *   // 获取错误码
   *   const errorCode = evt.getCode();
   *   // 判断为自动播放受限
   *   if(errorCode === 41030){
   *      // 手势操作恢复
   *      $("#button").on("click", async () => {
   *         await remoteStream.resume();
   *         $("#button").hide();
   *      });
   *   }
   * });
   * ```
   */
  on(
    event: 'notAllowedError',
    callback: (evt: {
      /**
       * 错误码
       */
      erroCode: Number
    }) => void
  ): void

  /**
   * 音频PCM数据回调
   *
   * @example
   * ```Javascript
   *  localStream.on('audio-data', (data) => {})
   *  localStream.enableAudioFrame(1000)
   * ```
   *
   * @note 注意
   * * 受内部缓存影响，音频数据实际时长可能会略小于预设时长，以data.duration为准。
   * * 采样率和输出设备有关，以data.sampleRate为准。
   * * 在某些浏览器下，即使输出设备为单声道，也可能输出双声道数据，此时右声道为[0,0...]或者同左声道数据一致。
   */
  on(event: 'audio-data', callback: (data: AudioFrame) => void): void

  /**
   * 'video-resize' 回调表示视频分辨率发生变化
   */
  on(
    event: 'video-resize',
    callback: (evt: {
      uid: number
      mediaType: 'video' | 'screen'
      width: number
      height: number
    }) => void
  ): void

  /**
   * 'plugin-process-unstable' 回调表示插件处理过程不稳定。原因可能是性能不足。
   */
  on(
    event: 'plugin-process-unstable',
    callback: (evt: {
      streamID: string
      key: string // 插件标识
      msg: string //错误信息
    }) => void
  ): void
}
export { Stream }
