UNPKG

zby-live-sdk

Version:

This is a live SDK for weclassroom.

1,802 lines 64.9 kB
import dataReport from '../network/dataReport.js';
import defaultApi from '../default';
import NOTICE from '../notice';
import { deviceListReport } from './device.js';
import util from '../util/util';
import { compareS } from '../util/sessionStorage.js';


// 扩展标识
const extensionId = 'trtc_ext';
// 端提供的 API 入口
const EM = window.EM;
// 是否执行过 TRTC 的 AddListener 的标志
let hasAddListener = false;
// 监听id
let EMListenerId = 0;
//静音推流时,音量值是否上抛上层标志 false=没有声音不往上层通知/true=有声音有上层通知
let isNoticeMicVolumeTRTC = false;
// rtc小班课=0;rtc大班课=1
const classType = 0;
// 维护的一份拉流的 streamId 与本地预览通道的映射表
let streamIdToPreviewId = {};
let previewIdToStreamId = {};
// 拉流的播放通道初始化从 0 开始依次递增至99(共100路流),-1 为推流的本地视频预览
// const uiChnIndexs = new Array(50).fill(0).map((a, b) => b + 1).reverse();
const uiChnIndexs = new Array(100).fill(0).map((a, b) => b).reverse();
// 维护的一份当前正在使用的音频设备的 deviceId 的列表
let usingAudioDeviceId = {
  speaker: '',
  microphone: ''
};
// 心跳
let heartBeatDataReportObj = {};
let heartBeatDataReportTimer = null;
let isFirstHeartBeatReport = false;
let streamIdRtcPlayerInfo = {};
let streamIdRtcPlayerInfo1 = {};
//记录底层推流回调的值
let dataTrtcCapture = {};
let qualityLocalArr = [];
let qualityRemoteArr = [];

const callMethod = (name, args) => {
  // EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
  if (EM) {
    return new Promise((resolve, reject) => {
      // let noneCamera = (name === 'SetVideoDevice' && !args.pszDeviceID);
      // let noneMicrophone = (name === 'SetAudioDevice' && args.deviceType === 0 && !args.pszDeviceID);
      // let noneSpeaker = (name === 'SetAudioDevice' && args.deviceType === 1 && !args.pszDeviceID);
      // if (noneCamera || noneMicrophone || noneSpeaker) {
      //   return resolve();
      // }
      EM.CallMethod(
        extensionId,
        name,
        JSON.stringify({...args, classType}),
        (code, msg) => {
          defaultApi.writeLog(`${name} Code: ${code}\nMessage: ${msg}\nParams: ${JSON.stringify({...args, classType})}`);
          resolve({
            code,
            msg
          });
        }
      );
    });
  }
};

const loadTrtc = (extensionVersion) => {
  // EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
  if (EM) {
    return new Promise((resolve, reject) => {
      removerListener();
      console.log('jjjjjjj3',extensionId,extensionVersion);
      EM.Load(
        extensionId,
        extensionVersion,
        false,
        (code, msg) => {
          console.log('jjjjjjj4')
          defaultApi.writeLog(`loadTrtc Code: ${code}\nMessage: ${msg}`);
          addListener();
          resolve();
        }
      );
    });
  }
};

//卸载监听
const removerListener = () => {
  hasAddListener = false;
  defaultApi.writeLog(`TRTC::action--removerListener EMListenerId:${EMListenerId}`);
  EM.RemoverListener(extensionId, EMListenerId, (ec, content) => {});
  EMListenerId = 0;
};

//加载监听

/**
 * @function 添加扩展监听机制
 * @param userId:Number 用户 id,必选
 * @param userName:String 用户名,必选
 * @param roomId:String 频道(房间) id,必选
 * @param nNetType:Number 网络类型,可选,默认为 1
 * @return void
 */
 const addListener = () => {
  // EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
  if (EM && !hasAddListener) {
    hasAddListener = true;
    EM.AddListener(extensionId, (event, data) => {
      if (data && data.indexOf(extensionId) > -1) {
        try {
          EMListenerId = JSON.parse(data)[extensionId];
          defaultApi.writeLog(`TRTC::addListener-- EMListenerId: ${EMListenerId}`);
        } catch (error) {}
      }
      if (!event || !data) {
        return;
      }
      let _data = JSON.parse(data);
      switch (event) {
      //推流相关
      //直播推流器错误通知,推流器出现错误时,会回调该通知
      case 'onLocalError':
        defaultApi.writeLog(`TRTC::addListener-- onLocalError: ${JSON.stringify(_data)}`);
        NOTICE.pushStreamError({
          errorStreamType:_data.streamType,
          code: _data.code,
          errorMsg: _data
        })
        dataReport.pushStreamError({
          errorStreamType:_data.streamType,
          code: _data.code
        });
        break;
      //直播推流器警告通知
      case 'onLocalWarning':
        defaultApi.writeLog(`TRTC::addListener-- onLocalWarning: ${JSON.stringify(_data)}`);
        NOTICE.pushStreamWarning({
          warnStreamType:_data.streamType,
          code: _data.code
        })
        dataReport.pushStreamWarning({
          warnStreamType:_data.streamType,
          code: _data.code
        });
        break;
      //首帧音频推送完成的回调通知
      case 'onSendLocalFirstAudioFrame':
        defaultApi.writeLog(`TRTC::addListener-- onSendLocalFirstAudioFrame: ${JSON.stringify(_data)}`);
        break;
      //首帧视频推送完成的回调通知
      case 'onSendLocalFirstVideoFrame':
        defaultApi.writeLog(`TRTC::addListener-- onSendLocalFirstVideoFrame: ${JSON.stringify(_data)}`);
        NOTICE.pushFlowSuccess({code:0,publish_streamid: _data.strStreamId});
        dataReport.publishResult({
          code: '0',
          // publish_streamid: window.zbyAVSDK_init_params.trtc.streamId,
        });
        break;
      //麦克风采集音量值回调
      case 'onLocalMicrophoneVolumeUpdate':
        // defaultApi.writeLog(`TRTC::addListener-- onLocalMicrophoneVolumeUpdate: ${JSON.stringify(_data)}`);
        if (isNoticeMicVolumeTRTC) {
          NOTICE.captureMicVolumeChanged({
            volume: Math.round(_data.volume)
          });
        }
        // heartBeatDataReportObj.volume.push(Math.round(_data.pCaptureSoundLevel[0].soundLevel));
        heartBeatDataReportObj.volume = heartBeatDataReportObj.volume + Math.round(_data.volume) + ',';
        break;
        break;
      //推流器连接状态回调通知。推流器连接状态 0 与服务器断开连接/1 正在连接服务器/2 连接服务器成功/3 重连服务器中
      case 'onLocalConnectStatusUpdate':
        defaultApi.writeLog(`TRTC::addListener-- onLocalConnectStatusUpdate: ${JSON.stringify(_data)}`);
        dataReport.localConnectStatus({
          connectStatuStreamType:_data.streamType,
          state: _data.state
        });
        break;
      //网络质量的实时统计回调
      // 统计回调每间隔2秒抛出一次,用于通知 SDK 感知到的当前网络的质量。
      // * SDK 会使用一组内嵌的自研算法对当前网络的延迟高低、带宽大小以及稳定情况进行评估,并计算出一个的评估结果:
      // * 如果评估结果为 1(Excellent) 代表当前的网络情况非常好,如果评估结果为 6(Down)代表当前网络无法支撑 SDK 的正常使用。
      // * @param quality 网络状态。0未检测;1当前网络非常好;2当前网络比较好;3当前网络一般;4当前网络较差;5当前网络很差;6当前网络不满足 TRTC 的最低要求
      case 'onLocalNetworkQuality':
        // defaultApi.writeLog(`TRTC::addListener-- onLocalNetworkQuality: ${_data.quality}`);
        if(_data.quality == 4 || _data.quality == 5 || _data.quality == 6) {
          NOTICE.localNetworkQuality({code: _data.quality});
          dataReport.localNetworkQuality({
            code:  _data.quality,
            // publish_streamid: window.zbyAVSDK_init_params.trtc.streamId,
          });
        }
        qualityLocalArr.push(_data.quality);
        break;
      //直播推流器统计数据回调 number类型
      //appCpu 当前 App 的 CPU 使用率(%)
      // systemCpu 当前系统的 CPU 使用率(%)
      // width 视频宽度
      // height 视频高度
      // fps 帧率(fps)
      // audioBitrate 视频码率(Kbps)
      // videoBitrate 音频码率(Kbps)
      // case 'onLocalStatisticsUpdate':
      //   defaultApi.writeLog(`TRTC::addListener-- onLocalStatisticsUpdate: ${_data}`);
      //   break;
      
      //拉流相关
      //远端流错误通知,拉流出现错误时,会回调该通知
      case 'onRemoteError':
        defaultApi.writeLog(`TRTC::addListener-- onRemoteError: ${JSON.stringify(_data)}`);
        NOTICE.pullStreamError({
          strErrorStreamId: _data.strStreamId,
          code:_data.code
        })
        //是否需要数据上报?@yuqian
        break;
      //远端流警告通知,拉流出现警告时,会回调该通知。
      case 'onRemoteWarning':
        defaultApi.writeLog(`TRTC::addListener-- onRemoteWarning: ${JSON.stringify(_data)}`);
        NOTICE.pullStreamWarning({
          strWarnStreamId: _data.strStreamId,
          code:_data.code
        })
        //是否需要数据上报?@yuqian
        break;
      //拉到音频首帧
      case 'onRemoteAudioStatus':
        // defaultApi.writeLog(`TRTC::addListener-- onRemoteAudioStatus: ${_data}`);
        console.log('onRemoteAudioStatus',_data);
        try{
          NOTICE.firstAudioSize({
            streamId: _data.strStreamId,
            userId: util.getUidByStreamId(_data.strStreamId)
          });
          dataReport.firstAudioSize({
            pull_streamid: _data.strStreamId,
            pull_uid: util.getUidByStreamIdDr(_data.strStreamId),
            code:'0'
          });
        } catch (e) { };
        break;
      //远端流音量大小
      case 'onRemoteAudioVolume':
        // defaultApi.writeLog(`TRTC::addListener-- onRemoteAudioVolume: ${JSON.stringify(_data)}`);
        let cbData = {
          streamId: _data.strStreamId,
          volume: _data.volume
        }
          NOTICE.playerVolumeChanged(cbData);
          NOTICE.volumeChange(cbData);
          // currStreamIdRtcPlayerInfo && currStreamIdRtcPlayerInfo.volume.push(Math.round(item.soundLevel));
          // streamIdRtcPlayerInfo[_data.strStreamId] && streamIdRtcPlayerInfo[_data.strStreamId].volume.push(Math.round(item.soundLevel));
          if(streamIdRtcPlayerInfo[_data.strStreamId]) {
            streamIdRtcPlayerInfo[_data.strStreamId].volume = streamIdRtcPlayerInfo[_data.strStreamId].volume + Math.round(_data.volume)+ ',';
          }
        break;
      // //远端流收到首帧音频
      // case 'onRemoteAudioStart':
      //   defaultApi.writeLog(`TRTC::addListener-- onRemoteAudioStart: ${_data}`);
      //   break;
      // //远端流停止推送音频
      // case 'onRemoteAudioStop':
      //   defaultApi.writeLog(`TRTC::addListener-- onRemoteAudioStop: ${_data}`);
      //   break;
      //mute 视频状态,true:远端开始推流,并拉流收到首帧;false:远端停止推流。
      case 'onRemoteVideoStatus':
        defaultApi.writeLog(`TRTC::addListener-- onRemoteVideoStatus: ${JSON.stringify(_data)}`);
        console.log('onRemoteVideoStatus',_data);
        try{
          NOTICE.pullFlowResult({code:0,pull_streamid: _data.strStreamId});
          NOTICE.firstVideoSize({
            streamId: _data.strStreamId,
            // userId: _data.strStreamId.split('_').length == 5 ? _data.strStreamId : util.getUidByStreamId(_data.strStreamId)
            userId: util.getUidByStreamId(_data.strStreamId)
          });
          dataReport.firstVideoSize({
            pull_streamid: _data.strStreamId,
            pull_uid: util.getUidByStreamIdDr(_data.strStreamId),
            code:'0'
          });
        } catch (e) { };
        break;
      //sei
      case 'onRecvSEIMsg':
        // defaultApi.writeLog(`TRTC::addListener-- onRecvSEIMsg: ${_data}`);
        break;
      //网络质量的实时统计回调
      // * 该统计回调每间隔2秒抛出一次,用于通知 SDK 感知到的当前网络的质量。
      // * SDK 会使用一组内嵌的自研算法对当前网络的延迟高低、带宽大小以及稳定情况进行评估,并计算出一个的评估结果:
      // * 如果评估结果为 1(Excellent) 代表当前的网络情况非常好,如果评估结果为 6(Down)代表当前网络无法支撑 SDK 的正常使用。
      // *
      // * @param strStreamId 流ID,表示远端用户信息。
      // * @param quality 网络状态。0未检测;1当前网络非常好;2当前网络比较好;3当前网络一般;4当前网络较差;5当前网络很差;6当前网络不满足 TRTC 的最低要求
      case 'onRemoteNetworkQuality':
        // defaultApi.writeLog(`TRTC::addListener-- onRemoteNetworkQuality: ${_data}`);
        if(_data.quality == 4 || _data.quality == 5 || _data.quality == 6) {
          NOTICE.remoteNetworkQuality({code: _data.quality});
          dataReport.remoteNetworkQuality({
            code:  _data.quality,
            // publish_streamid: window.zbyAVSDK_init_params.trtc.streamId,
          });
        }
        qualityRemoteArr.push(_data.quality);
        break;
      // 转推 CDN 上发布音视频流的事件回调
      // * @param state 0转推成功,1表示失败
      // * @param err 0表示成功,其余值表示失败
      // * @param errMsg 具体错误原因
      case 'onRtmpStreamingStateChanged':
        defaultApi.writeLog(`TRTC::addListener-- onRtmpStreamingStateChanged: ${JSON.stringify(_data)}`);
        break;
      //* 本地麦克风设备的通断状态发生变化(仅适用于桌面系统)
      // * @param deviceId 设备 ID
      // * @param state 通断状态,0:设备已添加;1:设备已被移除;2:设备已启用。
      case 'onMicphoneDeviceChanged':
        defaultApi.writeLog(`TRTC::addListener-- onMicphoneDeviceChanged: ${JSON.stringify(_data)}`);
        setMicrophoneDevice({deviceId: _data.state ==1 ? '' : _data.deviceId, operationType: 'hotPlug', deviceState: _data.state, changedDeviceId: _data.deviceId});
        break;
      //* 本地摄像头设备的通断状态发生变化(仅适用于桌面系统)
      // * @param deviceId 设备 ID
      // * @param state 通断状态,0:设备已添加;1:设备已被移除;2:设备已启用。
      case 'onVideoDeviceChanged':
        defaultApi.writeLog(`TRTC::addListener-- onVideoDeviceChanged: ${JSON.stringify(_data)}`);
        setVideoDevice({deviceId: _data.state ==1 ? '' : _data.deviceId, operationType: 'hotPlug', deviceState: _data.state, changedDeviceId: _data.deviceId});
        break;
      //扬声器热插拔
      case 'onSpeakerDeviceChanged':
        defaultApi.writeLog(`TRTC::addListener-- onSpeakerDeviceChanged: ${JSON.stringify(_data)}`);
        setSpeakerDevice({deviceId: _data.state ==1 ? '' : _data.deviceId, operationType: 'hotPlug', deviceState: _data.state, changedDeviceId: _data.deviceId});
        break;
      //推流的统计数据。5秒一个周期
      case 'onLocalStatistics':
        // defaultApi.writeLog(`TRTC::addListener-- onLocalStatistics: ${_data}`);
        console.log('onLocalStatistics',JSON.parse(_data.json));
        NOTICE.pushLossAndDelay({
          // userId: util.getUidByStreamId(_data.strStreamId),
          delay:_data.rtt,
          lostrate:_data.packetLoss
        });
        heartBeatDataReportCalc(event, _data);
        break;
      //拉流的统计数据。5秒一个周期
      case 'onRemoteStatistics':
        // defaultApi.writeLog(`TRTC::addListener-- onRemoteStatistics: ${_data}`);
        console.log('onRemoteStatistics',JSON.parse(_data.json));
        try {
          NOTICE.playLossAndDelay({
            userId: util.getUidByStreamId(_data.streamId),
            delay:_data.rtt,
            lostrate:_data.packetLoss
          });
        }catch(e){}
        heartBeatDataReportCalc(event, _data);
        break;
      case 'onStatisticsUpdate':
      // defaultApi.writeLog(`TRTC::addListener-- onStatisticsUpdate: ${JSON.stringify(_data)}`);
      // console.log('onStatisticsUpdate',JSON.parse(_data.json).local);
      // console.log('onStatisticsUpdate',JSON.parse(_data.json));
      // heartBeatDataReportCalc(event, _data);
      break;
      }
      if (zbysdk.openListenerLog) {
        console.log(`[zby-live-sdk]--TRTC::Listener:: event: ${event}, data: ${data}`);
      }
    });
  }
};


//初始化相关
const init = async (args) => {
  defaultApi.writeLog(`avsdk TRTC::init ${JSON.stringify(args)}`);
  const usedDevices = window.zbyAVSDK_device_checker_init;
  const {devices} = args;
  const _devices = {
    camera: (devices && devices.camera) || (usedDevices && usedDevices.camera && usedDevices.camera.use) || '',
    microphone: (devices && devices.microphone) || (usedDevices && usedDevices.microphone && usedDevices.microphone.use) || '',
    speaker: (devices && devices.speaker) || (usedDevices && usedDevices.speaker && usedDevices.speaker.use) || ''
  };
  await loadTrtc(args.extensionVersion);
  await startEngine(args.appId,args.userId, args.usersign);
  if(args.role === 'teacher' && args.mode === 1){
    //坐立模式开启外部采集器,站立关闭外部采集器,只有小组课坐立模式才会调用,通过这个把rtmp数据给到zego或者rtc(头像),开启了外部采集,zego不会再本地采集数据
    await setEnableExternVideoCapture(true);
  }
  //小班课 需要加载采集插件的输入数据,为了拼接zego头像
  if (args.classMode === 1 || args.classMode === 2) {
    defaultApi.writeLog('loadCollectionInputEntry')
    loadCollectionInputEntry();
  };
  await setCameraEncodeBiarate(args.encodeCaptureBitrate);
  await setCameraEncodeFps(args.encodeCaptureFps);
  await setCameraCaptureResolution(args.previewResolutionWidth, args.previewResolutionHeight);
  await setCameraEncodeResolution(args.encodedResolutionWidth, args.encodedResolutionHeight);
  // await setCameraEncodeBiarate(args.screenSameBitrate,1);
  // await setCameraEncodeFps(args.screenSameFps, 1);
  // await setCameraCaptureResolution(args.screenSameResolutionWidth, args.screenSameResolutionHeight, 1);
  // await setCameraEncodeResolution(args.screenSameResolutionWidth, args.screenSameResolutionHeight, 1);

  // await setCameraEncodeBiarate(500000);
  // await setCameraEncodeFps(15);
  // await setCameraCaptureResolution(640, 480);
  // await setCameraEncodeResolution(320, 240);
  await setDefaultDevice(_devices,'default');
  await setMicphoneVolumInterval(500);
  window.current_sdk_type = 'trtc';
  defaultApi.writeLog('trtc init finished current_sdk_type : trtc');
};


/**
 * @function 初始化引擎
 * @param appid:String TRTC 秘钥,必选
 * @param userid:String 
 * @param usersign:String 
 * @return Promise | void
 */
 const startEngine = (appid, userid, usersign) => {
  defaultApi.writeLog(`avsdk TRTC::StartEngine appid: ${appid} userid: ${userid} usersign: ${usersign}`);
  return callMethod('StartEngine', {
    appid,
    userid:userid + '',
    usersign
  });
};

const setEnableExternVideoCapture = (enable) => {
  defaultApi.writeLog(`avsdk TRTC::EnableExternVideoCapture`);
  return callMethod('EnableExternVideoCapture', {
    enable
  });
};

/**
 * @function 销毁引擎
 * @return Promise | void
 */
 const destroyEngine = () => {
  return callMethod('DestroyEngine', {});
};

//设置采集分辨率
const setCameraCaptureResolution = (width, height) => {
  return callMethod('SetCameraCaptureResolution',{width, height});
}
//设置编码分辨率
const setCameraEncodeResolution = (width, height) => {
  return callMethod('SetCameraEncodeResolution',{width, height})
}
//设置编码帧率
const setCameraEncodeFps = (fps) => {
  return callMethod('SetCameraEncodeFps',{fps})
}
//设置编码码率
const setCameraEncodeBiarate = (bitrate) => {
  return callMethod('SetCameraEncodeBiarate',{bitrate})
}
//获取指定摄像头的支持的分辨率
const getCameraResolution = (deviceId) => {
  return callMethod('GetCameraResolution',{deviceId})
}


/**
 * @function 设置默认的硬件设备,包括摄像头、麦克风以及扬声器
 * @return Promise
 */
 export const setDefaultDevice = async (devices, operationType) => {
  // 设置默认的摄像头
  if (devices && devices.camera) {
    if (zbyAVSDK_device_checker_init.camera.list.length == 0) {
      await getCameraDeviceListTrtc();
    }
    await setVideoDevice({deviceId: devices.camera, operationType:`${operationType}_1`});
  } else {
    const cameraData = await getCameraDeviceListTrtc();
    if (cameraData.length) {
      let hasSetCamera = false;
      for (let item of cameraData) {
        if (item.isDefault) {
          await setVideoDevice({deviceId: item.deviceId, operationType:`${operationType}_2`});
          hasSetCamera = true;
          break;
        }
      }
      if (!hasSetCamera) {
        await setVideoDevice({deviceId: cameraData[0].deviceId, operationType:`${operationType}_3`});
      }
    }
  }
  // 设置默认的麦克风
  if (devices && devices.microphone) {
    if (zbyAVSDK_device_checker_init.microphone.list.length == 0) {
      await getMicrophoneDeviceListTrtc();
    }
    console.log('pp2');
    await setMicrophoneDevice({deviceId: devices.microphone, operationType:`${operationType}_1`});
  } else {
    console.log('pp3');
    const microPhoneData = await getMicrophoneDeviceListTrtc();
    try {
      if (microPhoneData.length) {
      let hasSetMicrophone = false;
      for (let item of microPhoneData) {
        if (item.isDefault) {
          await setMicrophoneDevice({deviceId: item.deviceId, operationType:`${operationType}_2`});
          hasSetMicrophone = true;
          break;
        }
      }
      if (!hasSetMicrophone) {
        await setMicrophoneDevice({deviceId: microPhoneData[0].deviceId, operationType:`${operationType}_3`});
      }
    }
    } catch (e){
      console.log(e);
    }
    
  }
  // 设置默认的扬声器
  if (devices && devices.speaker) {
    await setSpeakerDevice({deviceId: devices.speaker, operationType:`${operationType}_1`});
  } else {
    const speakerData = await getSpeakerDeviceListTrtc();
    if (speakerData.length) {
      let hasSetSpeaker = false;
      for (let item of speakerData) {
        if (item.isDefault) {
          await setSpeakerDevice({deviceId: item.deviceId, operationType:`${operationType}_2`});
          hasSetSpeaker = true;
          break;
        }
      }
      if (!hasSetSpeaker) {
        await setSpeakerDevice({deviceId: speakerData[0].deviceId, operationType:`${operationType}_3`});
      }
    }
  }
};

//麦克风相关
/**
 * @function 开启麦克风
 * @param intervalMs: Number
 * @return Promise | void
 */
const startMicrophone = (intervalMs = 500) => {
  return callMethod('StartMicrophone', {intervalMs});
}

/**
 * @function 关闭麦克风
 * @return Promise | void
 */
 const stopMicrophone = () => {
  return callMethod('StopMicrophone', {});
}

const openOrCloseMicrophone = async (operation) => {
  isNoticeMicVolumeTRTC = operation;
  if(operation) {
    await startMicrophone();
  } else {
    await stopMicrophone();
  }
}

/**
 * @function 设置麦克风音量大小回调周期
 * @param intervalMs: Number
 * @return Promise | void
 */
const setMicphoneVolumInterval = (intervalMs) => {
  return callMethod('SetMicphoneVolumInterval',{intervalMs});
};

/**
 * @function 获取麦克风设备列表
 * @return Promise | void
 */
 const getMicrophoneDeviceList = () => {
  return callMethod('GetMicrophoneDeviceList', {});
}

/**
 * @function 获取麦克风列表
 * @return Promise | void
 */
 const getMicrophoneDeviceListTrtc = async () => {
  console.log('getMicrophoneDeviceListTrtc_microphoneListArr_trtc_ii');
  let microphoneListArr = JSON.parse(JSON.parse((await getMicrophoneDeviceList()).msg).DeviceList);
  console.log('getMicrophoneDeviceListTrtc_microphoneListArr_trtc_iii',microphoneListArr);
  let microphoneList = [];
  for (let i = 0, len = microphoneListArr.length; i < len; i++) {
    microphoneList.push({
      deviceId: microphoneListArr[i].id,
      deviceName: microphoneListArr[i].name,
      isDefault: microphoneListArr[i].default
    });
    deviceListReport.micList[microphoneListArr[i].szDeviceId] = microphoneListArr[i].szDeviceName;

  }
  if (!window.zbyAVSDK_device_checker_init) {
    window.zbyAVSDK_device_checker_init = {};
  }
  if (!window.zbyAVSDK_device_checker_init.microphone) {
    window.zbyAVSDK_device_checker_init.microphone = {};
  }
  window.zbyAVSDK_device_checker_init.microphone.hasTest = true;
  window.zbyAVSDK_device_checker_init.microphone.list = microphoneList;
  console.log('getMicrophoneDeviceListTrtc_microphoneListArr_trtc',microphoneList);
  return microphoneList;
}


/**
 * @function 设置指定音频(当前麦克风)设备
 * @param deviceId:String 音频设备 id,必选
 * @return Promise | void
 */
const setCurrentMicrophoneDevice = (deviceId) => {
  window.zbyAVSDK_device_checker_init.microphone.use = deviceId;
  return callMethod('SetCurrentMicrophoneDevice', {
    deviceId
  });
}

/**
 * @function 指定麦克风设备
 * @param deviceId:String 麦克风设备 id,必选
 * @param operationType:String 操作类型,可选
 * @return Promise | void
 */
 const setMicrophoneDevice = async (args) => {
  console.log('getMicrophoneDeviceListTrtc_microphoneListArr_trtc_i');
  let {deviceId, operationType, deviceState, changedDeviceId} = args;
  let deviceName = '';
  let microPhoneData;
  if (!deviceId) {
    microPhoneData = await getMicrophoneDeviceListTrtc();
    if (microPhoneData.length) {
      let hasGetMicrophone = false;
      for (let item of microPhoneData) {
        if (item.isDefault) {
          deviceId = item.deviceId;
          deviceName = item.deviceName;
          hasGetMicrophone = true;
          break;
        }
      }
      if (!hasGetMicrophone) {
        deviceId = microPhoneData[0].deviceId;
        deviceName = microPhoneData[0].deviceName;
      }
    } else {
      deviceId = '';
    }
    if (deviceId === '') {
      NOTICE.noDevice({
        deviceType: 'microphone'
      });
    }
  }
  try {
    dataReport.setDevice({
      device_type:2,
      device_id:deviceId,
      device_name:deviceListReport.micList[deviceId],
      operationType,
      fore_state: operationType == 'hotPlug' ? +deviceState+1 : '-'
    });
    window.zbyAVSDK_device_checker_init.microphone.name = deviceListReport.micList[deviceId];
  } catch (e) {};
  await setCurrentMicrophoneDevice(deviceId);
  if (operationType == 'hotPlug' || operationType == 'deviceError') {
    if(!microPhoneData){
      microPhoneData = await getMicrophoneDeviceListTrtc();
      deviceName = deviceListReport.micList[deviceId];
    }
    NOTICE[operationType]({
      deviceType: 'microphone',
      useDeviceId: deviceId,
      useDeviceName: deviceName,
      deviceList: microPhoneData,
      deviceState,
      changedDeviceId
    });
  }
}

/**
 * @function 获取当前麦克风的音量
 * @return Promise | void
 */
const getCurrentMicrophoneVolume = () => {
  return callMethod('GetCurrentMicrophoneVolume', {}).then(ret => {
    let volume = 0;
    try {
      volume = JSON.parse(ret.msg).microphoneVolume;
    } catch (e) {
      console.error(`zby-live-sdk: getCurrentMicrophoneVolume ret: ${ret}. error: ${e}`);
    }
    return volume;
  });
}

/**
 * @function 设置当前麦克风的音量
 * @param volume : number 音量值
 * @return Promise | void
 */
 const setCurrentMicrophoneVolume = (volume) => {
  return callMethod('SetCurrentMicrophoneVolume', {volume});
}

/**
 * @function 获取当前麦克风ID
 * @return Promise | void
 */
 const getCurrentMicrophoneId = (volume) => {
  return callMethod('GetCurrentMicrophoneId', {volume});
}

//摄像头相关
/**
 * @function 打开摄像头
 * @param width: Number
 * @param height: Number
 * @return Promise | void
 */
 const startCamera = (width,height) => {
  return callMethod('StartCamera', {width,height});
}

/**
 * @function 关闭摄像头
 * @return Promise | void
 */
 const stopCamera = () => {
  return callMethod('StopCamera', {});
}

const openOrCloseCamera = async (operation) => {
  console.log('openCa',operation);
  if(operation){
    await startCamera();
  } else {
    await stopCamera();
  }
}

/**
 * @function 获取摄像头列表
 * @return Promise | void
 */
 const getCameraDeviceList = () => {
  return callMethod('GetCameraDeviceList', {});
}

/**
 * @function 获取摄像头列表
 * @return Promise | void
 */
export const getCameraDeviceListTrtc= async () => {
  let videoListArr = JSON.parse(JSON.parse((await getCameraDeviceList()).msg).DeviceList);
  console.log('GetCameraDeviceList_cameraList_trtc_iiii',videoListArr);
  let videoList = [];
  for (let i = 0, len = videoListArr.length; i < len; i++) {
    videoList.push({
      deviceId: videoListArr[i].id,
      deviceName: videoListArr[i].name,
      isDefault: videoListArr[i].default
    });
    deviceListReport.cameraList[videoListArr[i].szDeviceId] = videoListArr[i].szDeviceName;
  }
  if (!window.zbyAVSDK_device_checker_init) {
    window.zbyAVSDK_device_checker_init = {};
  }
  if (!window.zbyAVSDK_device_checker_init.camera) {
    window.zbyAVSDK_device_checker_init.camera = {};
  }
  window.zbyAVSDK_device_checker_init.camera.hasTest = true;
  window.zbyAVSDK_device_checker_init.camera.list = videoList;
  console.log('GetCameraDeviceList_cameraList_trtc',videoList);
  return videoList;
};

/**
 * @function 设置指定视频(当前摄像头)设备
 * @param deviceId:String 视频设备 id,必选
 * @return Promise | void
 */
 const setCurrentCameraDevice = (deviceId) => {
  window.zbyAVSDK_device_checker_init.camera.use = deviceId;
  return callMethod('SetCurrentCameraDevice', {
    deviceId
  });
}

/**
 * @function 指定使用的摄像头
 * @param deviceId:String 摄像头 id,必选
 * @param operationType:String 操作类型,可选
 * 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
 * 不传即是普通的设置摄像头设备的行为
 * @return Promise | void
 */
 const setVideoDevice = async (args) => {
  defaultApi.writeLog(`${JSON.stringify(args)}----setVideoDevice-trtc----`);
  let {deviceId, operationType, deviceState, code, changedDeviceId} = args;
  let deviceName = '';
  let cameraData;
  if (!deviceId) {
    cameraData = await getCameraDeviceListTrtc();
    // console.log('hsghsghsg0',cameraData);
    if (cameraData.length) {
      let hasGetCamare = false;
      // console.log('hsghsghsg1',hasGetSpeaker);
      for (let item of cameraData) {
        if (item.isDefault) {
          deviceId = item.deviceId;
          deviceName = item.deviceName;
          hasGetCamare = true;
          break;
        }
      }
      // console.log('hsghsghsg2',hasGetSpeaker);
      if (!hasGetCamare) {
        deviceId = cameraData[0].deviceId;
        deviceName = cameraData[0].deviceName;
        // console.log('hsghsghsg91',deviceId);
      }
    } else {
      deviceId = '';
    }
    // console.log('hsghsghsg1',deviceId);
    if (deviceId === '') {
      NOTICE.noDevice({
        deviceType: 'camera'
      });
    }
  }
  window.zbyAVSDK_device_checker_init.camera.use = deviceId;
  window.zbyAVSDK_device_checker_init.camera.name = deviceListReport.cameraList[deviceId];
  try {
    dataReport.setDevice({
      device_type: 1,
      device_id:deviceId,
      device_name:deviceListReport.cameraList[deviceId],
      operationType,
      fore_state: operationType == 'hotPlug' ? deviceState+1 : '-'
    });
  } catch (e) {};
  await setCurrentCameraDevice(deviceId);
  if (operationType == 'hotPlug' || operationType == 'deviceError') {
    if(!cameraData){
      cameraData = await getCameraDeviceListTrtc();
      deviceName = deviceListReport.cameraList[deviceId];
    }
    NOTICE[operationType]({
      deviceType: 'camera',
      useDeviceId: deviceId,
      useDeviceName: deviceName,
      deviceList: cameraData,
      messge: code,
      deviceState,
      changedDeviceId
    });
  }
  try {
    NOTICE.useredCamera({
      deviceId,
      deviceName
    });
  } catch (e){ };
};

/**
 * @function 获取当前摄像头ID
 * @return Promise | void
 */
 const GetCurrentCameraId = () => {
  return callMethod('GetCurrentCameraId', {});
}

//扬声器相关

/**
 * @function 获取扬声器列表
 * @return Promise | void
 */
 const getSpeakerDeviceList = () => {
  return callMethod('GetSpeakerDeviceList', {});
}

/**
 * @function 获取扬声器列表
 * @return Promise | void
 */
 const getSpeakerDeviceListTrtc = async () => {
  let speakerListArr = JSON.parse(JSON.parse((await getSpeakerDeviceList()).msg).DeviceList);
  console.log('getSpeakerDeviceList_speakerListArr_trtc_ii',speakerListArr);
  let speakerList = [];
  for (let i = 0, len = speakerListArr.length; i < len; i++) {
    speakerList.push({
      deviceId: speakerListArr[i].id,
      deviceName: speakerListArr[i].name,
      isDefault: speakerListArr[i].default
    });
    deviceListReport.speakerList[speakerListArr[i].szDeviceId] = speakerListArr[i].szDeviceName;

  }
  if (!window.zbyAVSDK_device_checker_init) {
    window.zbyAVSDK_device_checker_init = {};
  }
  if (!window.zbyAVSDK_device_checker_init.speaker) {
    window.zbyAVSDK_device_checker_init.speaker = {};
  }
  window.zbyAVSDK_device_checker_init.speaker.hasTest = true;
  window.zbyAVSDK_device_checker_init.speaker.list = speakerList;
  console.log('getSpeakerDeviceList_speakerListArr_trtc_iii',speakerList);
  return speakerList;
};

/**
 * @function 设置指定音频(当前扬声器)设备
 * @param deviceId:String 音频设备 id,必选
 * @return Promise | void
 */
 const setCurrentSpeakerDevice = (deviceId) => {
  window.zbyAVSDK_device_checker_init.speaker.use = deviceId;
  return callMethod('SetCurrentSpeakerDevice', {
    deviceId
  });
}

/**
 * @function 指定扬声器
 * @param deviceId:String 扬声器 id,必选
 * @param operationType:String 操作类型,可选
 * 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
 * 不传即是普通的设置扬声器设备的行为
 * @return Promise | void
 */
 const setSpeakerDevice = async (args) => {
  defaultApi.writeLog(`${JSON.stringify(args)}----setSpeakerDevice-trtc----`);
  console.log('hsghsghsg999',args);
  let {deviceId, operationType, deviceState, code, changedDeviceId} = args;
  let deviceName = '';
  let speakerData;
  if (!deviceId) {
    speakerData = await getSpeakerDeviceListTrtc();
    console.log('hsghsghsg0',speakerData.length);
    if (speakerData.length) {
      let hasGetSpeaker = false;
      console.log('hsghsghsg1',hasGetSpeaker);
      for (let item of speakerData) {
        if (item.isDefault) {
          deviceId = item.deviceId;
          deviceName = item.deviceName;
          hasGetSpeaker = true;
          break;
        }
      }
      console.log('hsghsghsg2',hasGetSpeaker);
      if (!hasGetSpeaker) {
        deviceId = speakerData[0].deviceId;
        deviceName = speakerData[0].deviceName;
        console.log('hsghsghsg91',deviceId);
      }
    } else {
      deviceId = '';
    }
    if (deviceId === '') {
      NOTICE.noDevice({
        deviceType: 'speaker'
      });
    }
  }
  console.log('hsghsghsg999',deviceId);
  try {
    dataReport.setDevice({
      device_type:3,
      device_id:deviceId,
      device_name:deviceListReport.speakerList[deviceId],
      operationType,
      fore_state: operationType == 'hotPlug' ? deviceState+1 : '-'
    });
    window.zbyAVSDK_device_checker_init.speaker.name = deviceListReport.speakerList[deviceId];
  } catch (e) {
    console.log(e);
  };
  console.log('hsghsghsg99911',deviceId);
  usingAudioDeviceId.speaker = deviceId;
  await setCurrentSpeakerDevice(deviceId);
  if (operationType == 'hotPlug' || operationType == 'deviceError') {
    if(!speakerData){
      speakerData = await getSpeakerDeviceListTrtc();
      deviceName = deviceListReport.speakerList[deviceId];
    }
    console.log('hsghsghsg99911',deviceId);
    NOTICE[operationType]({
      deviceType: 'speaker',
      useDeviceId: deviceId,
      useDeviceName: deviceName,
      deviceList: speakerData,
      message: code,
      deviceState,
      changedDeviceId
    });
  }
  console.log('hsghsghsg999112',deviceId);
};

/**
 * @function 获取当前扬声器的音量
 * @return Promise | void
 */
 const getCurrentSpeakerVolume = () => {
  return callMethod('GetCurrentSpeakerVolume', {});
}

/**
 * @function 获取当前扬声器ID
 * @return Promise | void
 */
 const getCurrentSpeakerId = () => {
  return callMethod('GetCurrentSpeakerId', {});
}

/**
 * @function 设置当前扬声器音量
 * @param volume : number 音量值
 * @return Promise | void
 */
 const setCurrentSpeakerVolume = (volume) => {
  return callMethod('SetCurrentSpeakerVolume', {volume});
}


/**
 * @function 打开系统声音采集
 * @return Promise | void
 */
 const startSystemAudioLoopback = () => {
  return callMethod('StartSystemAudioLoopback', {});
}

/**
 * @function 关闭系统声音采集
 * @return Promise | void
 */
 const stopSystemAudioLoopback = () => {
  return callMethod('StopSystemAudioLoopback', {});
}

//采集扬声器声音
const setAudioSpeakerCapture = (speaker) => {
  if(speaker) {
    return startSystemAudioLoopback();
  } else {
    return stopSystemAudioLoopback();
  }
}

//sei
/**
 * @function sei消息
 * @param data: String    数据
 * @param dataSize: Number    数据长度
 * @param repeatCount: Number    发送次数
 * @return Promise | void
 */
 const sendSEIMsg = (data,dataSize,repeatCount) => {
   if (typeof dataSize === 'undefined') {
    dataSize = data.length;
   };
   if (typeof repeatCount === 'undefined') {
    repeatCount = 1;
  }
  return callMethod('SendSEIMsg', {data,dataSize,repeatCount});
}

//采集相关

/**
 * @function 开始屏幕采集
 * @return Promise | void
 */
 const startScreenCapture = () => {
  return callMethod('StartScreenCapture', {});
}

/**
 * @function 停止屏幕采集
 * @return Promise | void
 */
 const stopScreenCapture = () => {
  return callMethod('StopScreenCapture', {});
}

//推拉流相关

/**
 * @function 开启本地或者远程的视频视图
 * @param isLocal:Boolean 是否是本地的视频预览,必选
 * @param streamId:String 要拉取的视频流的 id,可选,只有拉取远程的视频流的时候才是必选的
 * @param domId:String <video> 标签的 id,可选
 * 如果传了 domId,就把视频绑定到对应的 <video> 标签上
 * @return Promise 可从 Promise 中获取 src,Promise.then((src) => {})
 */
const startLocalOrRemotePreview = async (isLocal, streamId, domId, isTeacherNewWindowPreview) => {
  defaultApi.writeLog(`${streamId}----streamId-trtc---- ${isLocal}`);
  let isUpdateChromeVersion = await util.getChromeVersion();
  // 预览本地视频的话,流 id 从全局配置中获取
  // if (isLocal) {
  //   streamId = isInRoom ? (window.zbyAVSDK_init_params && window.zbyAVSDK_init_params.trtc.streamId)
  //     : 'deviceCheckerInit';
  // }
  const externalConstraints = {
    audio: false,
    video: {
      mandatory: {
        chromeMediaSource: 'external',
        chromeMediaSourceId: `ems://trtc_ext/${isLocal || isTeacherNewWindowPreview ? -3 : streamId}`
      }
    }
  };

  console.log('externalConstraints',externalConstraints);

  return new Promise((resolve, reject) => {
    const handleExternalSuccess = (stream) => {
      defaultApi.writeLog(`${stream}----stream-trtc----`);
      stream.oninactive = () => {
        defaultApi.writeLog('Stream inactive');
      };
      const src = isUpdateChromeVersion? stream : window.URL.createObjectURL(stream);
      console.log('内核升级', isUpdateChromeVersion,domId,src);
      if (domId && document.querySelector(domId)) {
        if(isUpdateChromeVersion) {
          document.querySelector(domId).srcObject = src;
          console.log('内核升级1',domId, document.querySelector(domId),document.querySelector(domId).srcObject,src);
        } else {
          console.log('内核升级2');
          document.querySelector(domId).src = src;
        }
      }
      defaultApi.writeLog(`setVidoeSrc, streamId:${streamId}, domId: ${domId}, src: ${src}`);
      resolve(src);
    };

    const handleExternalError = (error) => {
      if (error.name === 'ConstraintNotSatisfiedError') {
        console.error('ConstraintNotSatisfiedError');
      } else if (error.name === 'PermissionDeniedError') {
        console.error(
          'Permissions have not been granted to use your camera and '
          + 'microphone, you need to allow the page access to your devices in '
          + 'order for the demo to work.'
        );
      }
      console.error(`getUserMedia error: ${error.name}`, error);
      if (domId) {
        document.querySelector(domId).src = '';
      }
      reject('');
    };

    if (navigator.webkitGetUserMedia) {
      navigator.webkitGetUserMedia(
        externalConstraints,
        handleExternalSuccess,
        handleExternalError
      );
    }
  });
};

/**
 * @function 开始推流
 * @param streamId: String   流id
 * @return Promise | void
 */
 const startPush = (streamId ,channel = 0) => {
  if(!streamId){
    streamId = zbyAVSDK_init_params.trtc.streamId;
  }
   // 心跳开始
  if (!isFirstHeartBeatReport) {
    isFirstHeartBeatReport = true;
    console.log('ppppppp上报呀')
    heartBeatDataReport('start');
  }
  console.log('startPush',streamId,channel);
  return callMethod('StartPush', {streamId, channel});
}

/**
 * @function 停止推流
 * @param streamId: String   流id
 * @return Promise | void
 */
 const stopPush = (channel) => {
  //  if(!streamId){
  //   streamId = zbyAVSDK_init_params.trtc.streamId;
  //  }
   if(!channel){
    channel = 0;
   }

  return callMethod('StopPush', {channel});
}

/**
 * @function 不推/推 音频
 * @param enable : bool   
 * @return Promise | void
 */
 const muteLocalAudio = (enable) => {
  return callMethod('MuteLocalAudio', {enable});
}

/**
 * @function 不推/推 视频
 * @param enable: bool  
 * @return Promise | void
 */
 const muteLocalVideo = (enable) => {
  return callMethod('MuteLocalVideo', {enable});
}

/**
 * @function 开始转推本地摄像头TRTC流至RTMP。目前只支持转推1路
 * @return Promise | void
 */
 const addPublishRtmpStreamUrl = () => {
  return callMethod('AddPublishRtmpStreamUrl', {});
}

/**
 * @function 停止转推RTMP
 * @return Promise | void
 */
 const removePublishStreamUrl = () => {
  return callMethod('RemovePublishStreamUrl', {});
}

/**
 * @function 开始拉流
 * @param streamId: String   流id
 * @return Promise | void
 */
 const startPlay = (streamId) => {
  return callMethod('StartPlay', {streamId});
}

/**
 * @function 初始化拉流
 * @param streamId:String 从传来的信道消息中获取,必选
 * @param domId:String <video> 标签的 id,可选
 * 如果传了就把视频绑定到对应的 <video> 标签上
 * @param pInfo:String 多媒体流附加信息,可选,默认为 'none'
 * @return src:String 视频预览地址
 */
 export const initPullFlow = async (streamId, domId, mute, pInfo, notAutoPlay, audioOnly) => {
  defaultApi.writeLog(`TRTC:: initPullFlow_start ,streamId :${streamId}`);
  let playChannel;
  resetStreamIdRtcPlayerInfo1(streamId);
  resetStreamIdRtcPlayerInfo(streamId);
  try {
    if (!isFirstHeartBeatReport) {
      isFirstHeartBeatReport = true;
      heartBeatDataReport('start');
    }
  } catch (error) {}
  if (streamIdToPreviewId[streamId] == undefined) {
   
    // defaultApi.writeLog(`TRTC::  The streamId queue does not contain this streamId ${uiChnIndexs}`);
    playChannel = uiChnIndexs.pop();
    streamIdToPreviewId[streamId] = playChannel;
    console.log('streamIdIsNoExited',playChannel,streamIdToPreviewId[streamId],streamId,uiChnIndexs);
  } else {
    defaultApi.writeLog('TRTC::  The streamId queue contains this streamId');
    playChannel = streamIdToPreviewId[streamId];
    console.log('streamIdIsExited',playChannel,streamIdToPreviewId[streamId],streamId,uiChnIndexs);
    await stopPlay(streamId);
    resetStreamIdRtcPlayerInfo(streamId);
  }
  // 判断是否创建或者获取播放通道成功,不成功就删掉重试
  // if (playChannel == undefined) {
  //   delete streamIdToPreviewId[streamId];
  //   defaultApi.writeLog('error', 'TRTC::  uiChnIndex is not exist');
  //   // delete streamIdToBlobUrl[streamId];
  //   return initPullFlow(streamId, domId, mute, pInfo, notAutoPlay, audioOnly);
  // }
  previewIdToStreamId[playChannel] = streamId;
  //是否拉取声音
  // await muteRemoteAudio(streamId, !!mute);
  // //是否拉取画面
  // await muteRemoteVideo(streamId, !!audioOnly);
  if (!notAutoPlay) {
    await startPlay(streamId);
  }
  await muteRemoteAudio(streamId, !!mute);
  //是否拉取画面
  await muteRemoteVideo(streamId, !!audioOnly);
  const videoSrc = await startLocalOrRemotePreview(false, streamId, domId);
  return {
    videoSrc,
    playerId: playChannel
  };
};

/**
 * @function 停止拉流
 * @param streamId: String   流id
 * @return Promise | void
 */
 const stopPlay = (streamId, recovery) => {
  const id = streamIdToPreviewId[streamId];
  delete streamIdRtcPlayerInfo[streamId];
  if (recovery && id && !uiChnIndexs.includes(id)) {
    uiChnIndexs.push(id);
    defaultApi.writeLog(`avsdk TRTC::stopPlayStream streamId: ${streamId} id: ${id} uiChnIndexs: ${uiChnIndexs}`);
    delete streamIdToPreviewId[streamId];
  }
  return callMethod('StopPlay', {streamId});
}

/**
 * @function 停止拉所有的流
 * @return Promise | void
 */
 const stopPlayAll = () => {
  return callMethod('StopPlayAll', {});
}

/**
 * @function 不拉/拉 音频 true是静音/false是拉声音
 * @param streamId :string
 * @param mute : bool   
 * @return Promise | void
 */
 const muteRemoteAudio = (streamId, mute) => {
   console.log('audio_type',mute)
  streamIdRtcPlayerInfo1[streamId].audio_type = !mute;
  console.log('audio_type1', streamIdRtcPlayerInfo1)
  return callMethod('MuteRemoteAudio', {streamId, mute});
}

const pullAudioFlow = async (playerId, operation, streamid) => {
  defaultApi.writeLog(`pullAudioFlow -- playerId ${playerId} operation ${operation} streamId ${streamid}`);
  if(streamid){
    if(streamIdToPreviewId[streamid] == undefined) {
        NOTICE.pullAudioFlowError({streamid})
        return 
    }else{
      playerId = streamIdToPreviewId[streamid]
      console.log('pullAudioFlow-playerId',playerId,streamIdToPreviewId)
    }
  }
  let streamId = previewIdToStreamId[playerId];
  try {
    streamIdRtcPlayerInfo1[streamId].audio_type = !operation;
    console.log('pullAudioFlow ::previewIdToStreamId',streamId,previewIdToStreamId)
    console.log('pullAudioFlow ::streamIdRtcPlayerInfo1',streamId,streamIdRtcPlayerInfo1);
  } catch (e) {
    console.log('pullAudioFlow ::streamIdRtcPlayerInfo1--error',e);
  }
  try {
    dataReport.setPullVoice({
      code:+!operation,
      pull_uid: util.getUidByStreamId(streamId),
      pull_streamid:streamId,
      playerId
      // operator:'client'
    });
  } catch (e) {};
  return await muteRemoteAudio(streamId, operation);
}

/**
 * @function 不拉/拉 视频
 * @param streamId :string
 * @param mute: bool  
 * @return Promise | void
 */
 const muteRemoteVideo = (streamId, mute) => {
  try {
    streamIdRtcPlayerInfo1[streamId].video_type = !mute;
    console.log('activateVideoPlayStream ::streamIdRtcPlayerInfo1',streamIdRtcPlayerInfo1);
  } catch (e) {
    console.log('activateVideoPlayStream ::streamIdRtcPlayerInfo1 --error',e);
  }
  return callMethod('MuteRemoteVideo', {streamId, mute});
}

/**
 * 
 * @function 全员开麦突出声音流
 * @param bFocus :bool
 * @param streamIdarr : array  
 * @return Promise | void
 */
 const setFocusStreams = (bFocus, streamIdarr) => {
  return callMethod('SetFocusStreams', {bFocus, streamId1, streamId1, streamId1, streamId1});
}
//加载采集插件的输入数据
export const loadCollectionInputEntry = () => {
  return callMethod('LoadCollectionInputEntry', {});
};

//加载截屏插件
const LoadCollectionOutputEntry2 = () => {
  defaultApi.writeLog('info', 'avsdk TRTC::LoadCollectionOutputEntry2');
  return callMethod('LoadCollectionOutputEntry2', { });
};

//开启同屏
const startMultiScreen = async () => {
  defaultApi.writeLog('info', 'avsdk TRTC::startMultiScreen');
  // await setVideoCaptureFactory(true,1);
  await LoadCollectionOutputEntry2();
};

//高光时刻 enable (bool)/strDomain (string)/strKey (string)
const enableHighlight = async (enable,strDomain = '',strKey = '') => {
  defaultApi.writeLog('info', 'avsdk TRTC::enableHighlight');
  return callMethod('EnableHighlight', {enable, strDomain, strKey});
};

/**
 * @function 获取当前视频流所有的通道
 * @param  streamId 流 id
 * @return channelIndex
 */
 export const getAllChannelIndex = () => {
  defaultApi.writeLog(`TRTC : getAllChannelIndex ${JSON.stringify(streamIdToPreviewId)}`);
  return streamIdToPreviewId;
};

//开始连麦
const teacherStartLinkMic = async (mode) => {
  if (mode === 0) {
    await startMicrophone();
  } else {
    //开始连麦后,需要打开麦克风,开启外部采集器后(每次都要开启)
    await startMicrophone();
    await setEnableExternVideoCapture(true)
  }
};
//结束连麦
const teacherStopLinkMic = async (mode = 0) => {
  // if (mode === 0) {
  //   // await muteVideoPublish(false,0);
  // } else {
    await stopMicrophone();
  // }
};

/**
 * @function 离开教室
 * @return Promise | void
 */
 const leaveRoom = async () => {
  for (let key in streamIdToPreviewId) {
    await stopPlay(key, true);//huishou
  }
  await stopPush();
  removerListener();
  await destroyEngine();
  heartBeatDataReport('stop');
  isFirstHeartBeatReport = false;
  streamIdToPreviewId = {};
  previewIdToStreamId = {};
  streamIdRtcPlayerInfo = {};
  streamIdRtcPlayerInfo1 = {};
  // window.zegoHasPushFlow = 0;
}

const heartBeatRealKeys = ['video_fps', 'video_bitrate', 'audio_fps', 'audio_bitrate'];

const _heartBeatDataReport = () => {
  console.log('上报吧2')
  // let cpuRate = 0;
  // let memRate = 0;
  // let rateCount = 0;
  // let appCpuRate = 0;
  // let appMemUsed = 0;
  // let rateTimer = setInterval(async () => {
    // rateCount++;
    // let {cpu_rate, mem_rate, gpus, app_cpu_rate, app_mem_used} = (await toolApi.getCurCpuMemInfo()).msg;
    // cpu_rate = cpu_rate < 0 ? 0 : cpu_rate;
    // cpuRate += parseFloat(cpu_rate);
    // memRate += parseFloat(mem_rate);
    // if (window.zbyAVSDK_init_params.zego.role === 'student') {
    //   appCpuRate += parseFloat(app_cpu_rate);
    //   appMemUsed += parseFloat(app_mem_used);
    // }

    // if (rateCount >= 3) {
      // heartBeatRealKeys.forEach(realKey => {
      //   if (heartBeatDataReportObj.hasOwnProperty(realKey) && heartBeatDataReportObj.count > 0) {
      //     heartBeatDataReportObj[realKey] = util.toFixed(heartBeatDataReportObj[realKey]/heartBeatDataReportObj.count);
      //   }
      // });
      const pullInfo = [];
      console.log('拉流的类型1',streamIdRtcPlayerInfo)
      Object.keys(streamIdRtcPlayerInfo).forEach(streamid => {
        console.log('拉流的类型1.5')
        heartBeatRealKeys.forEach(realKey => {
          if (!streamIdRtcPlayerInfo[streamid].hasOwnProperty(realKey)) {
            streamIdRtcPlayerInfo[streamid][realKey] = [];
          }
          // if (streamIdRtcPlayerInfo[streamid].count > 0) {
          //   streamIdRtcPlayerInfo[streamid][realKey] = util.toFixed(streamIdRtcPlayerInfo[streamid][realKey]/streamIdRtcPlayerInfo[streamid].count);
          // }
        });
        console.log('拉流的类型2',streamIdRtcPlayerInfo1[streamid])
        //获取拉流类型,后期可写为函数提出去
        if(streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
          streamIdRtcPlayerInfo1[streamid].stream_type = 'both';
        } else if(!streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
          streamIdRtcPlayerInfo1[streamid].stream_type = 'video';
        } else if(streamIdRtcPlayerInfo1[streamid].audio_type && !streamIdRtcPlayerInfo1[streamid].video_type) {
          streamIdRtcPlayerInfo1[streamid].stream_type = 'audio';
        } else {
          streamIdRtcPlayerInfo1[streamid].stream_type = 'none';
        }
        // console.log('hsghsghsg_type_type', streamIdRtcPlayerInfo1[streamid].stream_type);

        pullInfo.push({
          streamid,
          // uid: util.getUidByStreamId(streamid),
          ...streamIdRtcPlayerInfo[streamid],
          pull_type: streamIdRtcPlayerInfo1[streamid].stream_type,
          volume: streamIdRtcPlayerInfo[streamid].volume.slice(0,streamIdRtcPlayerInfo[streamid].volume.length-1)
        });
        resetStreamIdRtcPlayerInfo(streamid);
      });
      if (isFirstHeartBeatReport) {
        try {
          //静音推流时过滤掉音频帧率和码率,上报为0;
          // if (!isNoticeMicVolumeZego) {
          //   heartBeatDataReportObj['audio_fps'] = [];
          //   heartBeatDataReportObj['audio_bitrate'] = [];
          // }

          //获取推流类型,后期可写为函数提出去
          if(zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
            zbysdk.deviceStatus.stream_type = 'both';
          } else if(!zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
            zbysdk.deviceStatus.stream_type = 'audio';
          } else if(zbysdk.deviceStatus.camera && !zbysdk.deviceStatus.microphone) {
            zbysdk.deviceStatus.stream_type = 'video';
          } else {
            zbysdk.deviceStatus.stream_type = 'none';
          };
          // console.log('push_type222',zbysdk.deviceStatus,zbysdk.deviceStatus.stream_type);
          defaultApi.writeLog(`push_type_trtc,camera: ${zbysdk.deviceStatus.camera},microphone: ${zbysdk.deviceStatus.microphone},type: ${zbysdk.deviceStatus.stream_type},a_fps: ${dataTrtcCapture.fps}, a_bit: ${dataTrtcCapture.audioBitrate}, v_fps: ${dataTrtcCapture.fps}, v_bit: ${dataTrtcCapture.videoBitrate}`);
          // if (window.zbyAVSDK_init_params.zego.role === 'teacher') {
          dataReport.heartbeat({
            ...{...heartBeatDataReportObj, push_type: zbysdk.deviceStatus.stream_type, volume: heartBeatDataReportObj.volume.slice(0,heartBeatDataReportObj.volume.length-1)},
            // pull_info: JSON.stringify(pullInfo),
            pull_info: pullInfo,
            // cpu_rate: util.toFixed(cpuRate/rateCount),
            // mem_rate: util.toFixed(memRate/rateCount),
          });
          // } else {
          //   dataReport.heartbeat({
          //     ...heartBeatDataReportObj,
          //     pull_info: JSON.stringify(pullInfo),
          //     cpu_rate: util.toFixed(cpuRate/rateCount),
          //     mem_rate: util.toFixed(memRate/rateCount),
          //     app_cpu: util.toFixed(appCpuRate/rateCount),
          //     app_mem: util.toFixed(appMemUsed/rateCount),
          //     video_mem: gpus
          //   });
          // }
        } catch (e) {
          console.log(e);
        }
      }
      resetHeartBeatDataReportObj();
      // cpuRate = 0;
      // memRate = 0;
      // appCpuRate = 0;
      // appMemUsed = 0;

      // clearInterval(rateTimer);
    // }
  // }, 10 * 1000);
};

const heartBeatDataReport = (type) => {
  try {
    if (type === 'start' && !heartBeatDataReportTimer) {
      _heartBeatDataReport();
      heartBeatDataReportTimer = setInterval(() => {
        _heartBeatDataReport();
      }, 30 * 1000);
    }
    if (type === 'stop') {
      clearInterval(heartBeatDataReportTimer);
      heartBeatDataReportTimer = null;
    }
  } catch (error) {
    console.log(error);
  }
};

const heartBeatDataReportCalc = (name, _data) => {
  console.log('hsgmzk111',name,_data);
  let _d = JSON.parse(_data.json);
  console.log('hsgmzk222',name,_d);
  // 拉流
  const pullKeys = ['fps', 'videoBitrate', 'afps', 'audioBitrate'];
  if (name === 'onRemoteStatistics') {
    console.log('hsgmzk222',streamIdRtcPlayerInfo,_d.streamId)
      if (streamIdRtcPlayerInfo && streamIdRtcPlayerInfo.hasOwnProperty(_d.streamId)) {
        console.log('hsgmzk22211');
        let streamid = _d.streamId;
        let isReport = true;
        // streamIdRtcPlayerInfo[streamid].count++;
        heartBeatRealKeys.forEach((realKey, index) => {
          if (_d.hasOwnProperty(pullKeys[index])) {
            if (streamIdRtcPlayerInfo[streamid][realKey] === undefined) {
              streamIdRtcPlayerInfo[streamid][realKey] = [];
              isReport = false;
            }
            // streamIdRtcPlayerInfo[streamid][realKey].push(parseFloat(parseInt(item[pullKeys[index]])));
          }
        });
        console.log('hsgmzk333',_d)
        if (isReport) {
          let audio_fps_trtc_pull = 0;
          let audio_bitrate_trtc_pull = 0;
          let video_fps_trtc_pull = 0;
          let video_bitrate_trtc_pull = 0;
          console.log('hsgmzk444',_d)
          if(streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
            console.log('hsgmzk555',_d)
            audio_fps_trtc_pull = parseFloat(parseInt(_d.afps));
            audio_bitrate_trtc_pull = parseFloat(parseInt(_d.audioBitrate));
            video_fps_trtc_pull = parseFloat(parseInt(_d.fps));
            video_bitrate_trtc_pull = parseFloat(parseInt(_d.videoBitrate));
          } else if(!streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
            video_fps_trtc_pull = parseFloat(parseInt(_d.fps));
            video_bitrate_trtc_pull = parseFloat(parseInt(_d.videoBitrate));
          } else if(streamIdRtcPlayerInfo1[streamid].audio_type && !streamIdRtcPlayerInfo1[streamid].video_type) {
            audio_fps_trtc_pull = parseFloat(parseInt(_d.afps));
            audio_bitrate_trtc_pull = parseFloat(parseInt(_d.audioBitrate));
          }
          streamIdRtcPlayerInfo[streamid].audio_fps.push(audio_fps_trtc_pull);
          streamIdRtcPlayerInfo[streamid].audio_bitrate.push(audio_bitrate_trtc_pull);
          streamIdRtcPlayerInfo[streamid].video_fps.push(video_fps_trtc_pull);
          streamIdRtcPlayerInfo[streamid].video_bitrate.push(video_bitrate_trtc_pull);
          streamIdRtcPlayerInfo[streamid].pull_loss.push(_d.packetLoss);
          streamIdRtcPlayerInfo[streamid].pull_delay.push(_d.rtt);
  
          streamIdRtcPlayerInfo[streamid].ctime.push(Math.round((new Date().getTime()+dataReport.timestamp)/1000));
          streamIdRtcPlayerInfo[streamid].video_ifg += _d.videoBlockRate;
          streamIdRtcPlayerInfo[streamid].audio_ifg += _d.audioBlockRate;
          // console.log('hsg_tpull_keys',JSON.parse(JSON.stringify(streamIdRtcPlayerInfo[streamid])),_d.avTimestampDiff);
        }
      }
  }
  // 推流
  const pushKeys = ['fps', 'videoBitrate', 'afps', 'audioBitrate'];
  if (name === 'onLocalStatistics') {
    // console.log('hsgmzk',_d);
    // heartBeatDataReportObj.count++;
    // heartBeatRealKeys.forEach((realKey, index) => {
    //   if (heartBeatDataReportObj.hasOwnProperty(realKey) && _d.hasOwnProperty(pushKeys[index])) {
    //     heartBeatDataReportObj[realKey].push(parseFloat(parseInt(_d[pushKeys[index]])));
    //   }
    // });
    let audio_fps_trtc_push = 0;
    let audio_bitrate_trtc_push = 0;
    let video_fps_trtc_push = 0;
    let video_bitrate_trtc_push = 0;
    dataTrtcCapture =_d;

    if(zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
      audio_fps_trtc_push = parseFloat(parseInt(_d.afps));
      audio_bitrate_trtc_push = parseFloat(parseInt(_d.audioBitrate));
      video_fps_trtc_push = parseFloat(parseInt(_d.fps));
      video_bitrate_trtc_push= parseFloat(parseInt(_d.videoBitrate));
    } else if(!zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
      audio_fps_trtc_push = parseFloat(parseInt(_d.afps));
      audio_bitrate_trtc_push = parseFloat(parseInt(_d.audioBitrate));
    } else if(zbysdk.deviceStatus.camera && !zbysdk.deviceStatus.microphone) {
      video_fps_trtc_push = parseFloat(parseInt(_d.fps));
      video_bitrate_trtc_push = parseFloat(parseInt(_d.videoBitrate));
    }
    // console.log('hsg6666666',audio_fps_trtc_push,audio_bitrate_trtc_push,video_fps_trtc_push,video_bitrate_trtc_push);
    heartBeatDataReportObj.audio_fps.push(audio_fps_trtc_push);
    heartBeatDataReportObj.audio_bitrate.push(audio_bitrate_trtc_push);
    heartBeatDataReportObj.video_fps.push(video_fps_trtc_push);
    heartBeatDataReportObj.video_bitrate.push(video_bitrate_trtc_push);
    heartBeatDataReportObj.push_loss.push(_d.packetLoss);
    heartBeatDataReportObj.push_delay.push(_d.rtt);

    heartBeatDataReportObj.ctime.push(Math.round((new Date().getTime()+dataReport.timestamp)/1000));
    // console.log('hsg_td5',JSON.parse(JSON.stringify(heartBeatDataReportObj)));
  }
};

//推流字段
const resetHeartBeatDataReportObj = () => {
  // console.log('hsg_td55556重置');
  heartBeatDataReportObj = {
    ctime: [],
    push_type: 'none',
    video_fps: [],
    video_bitrate: [],
    audio_fps: [],
    audio_bitrate: [],
    push_loss: [],
    push_delay: [],
    volume: ''
  };
};

//记录拉流类型,用作数据上报
const resetStreamIdRtcPlayerInfo1 = (streamId) => {
  streamIdRtcPlayerInfo1[streamId] = {
    audio_type: false,
    video_type: false,
    stream_type: 'none'
  };
  // console.log('hsgshgs_heartbeat',streamIdRtcPlayerInfo1);
};

resetHeartBeatDataReportObj();

//拉
const resetStreamIdRtcPlayerInfo = (streamId) => {
  streamIdRtcPlayerInfo[streamId] = {
    pull_uid: util.getUidByStreamId(streamId),
    streamid: streamId,
    ctime: [],
    pull_type: streamIdRtcPlayerInfo1[streamId].stream_type,
    volume: '',
    video_fps: [],
    video_bitrate: [],
    audio_fps: [],
    audio_bitrate: [],
    pull_loss: [],
    pull_delay: [],
    //音画不同步字段
    avtimestampdiff:[],
    // 累加
    audio_ifg: 0,
    video_ifg: 0
  };
};

export default {
  init,
  setCameraCaptureResolution,
  setCameraEncodeResolution,
  setCameraEncodeFps,
  setCameraEncodeBiarate,
  getCameraResolution,
  getMicrophoneDeviceListTrtc,
  setMicrophoneDevice,
  openOrCloseMicrophone,
  getCurrentMicrophoneVolume,
  setCurrentMicrophoneVolume,
  setCurrentCameraDevice,
  getCameraDeviceListTrtc,
  setVideoDevice,
  openOrCloseCamera,
  getSpeakerDeviceListTrtc,
  setSpeakerDevice,
  getCurrentSpeakerVolume,
  setCurrentSpeakerVolume,
  setAudioSpeakerCapture,
  startLocalOrRemotePreview,
  startPush,
  stopPush,
  startPlay,
  stopPlay,
  initPullFlow,
  pullAudioFlow,
  muteRemoteVideo,
  muteRemoteAudio,
  stopPlayAll,
  sendSEIMsg,
  startMultiScreen,
  enableHighlight,
  getAllChannelIndex,
  leaveRoom,
  teacherStartLinkMic,
  teacherStopLinkMic
}