zby-live-sdk
Version:
This is a live SDK for weclassroom.
3,467 lines • 118 kB
JavaScript
/**
* @File: Zego 音视频 SDK
* @Author: xuyuanrui
* @Email: raywhbxyr@163.com
* @Date: 2018-08-08 10:14:41
**/
import { dabanDataReport } from '../network/api.js';
import dataReport from '../network/dataReport.js';
import {
getS,
setS,
compareS
} from '../util/sessionStorage.js';
import defaultApi from '../default';
import util from '../util/util';
import NOTICE from '../notice';
import { deviceListReport } from './device.js';
import zbysdk from '../zby-live-sdk.js';
import { DataStore, userAndDeviceStatusInfo, getBackgroundStatus } from '../util/statsStore.js';
// 扩展标识
const extensionId = 'zego_ext';
// 端提供的 API 入口
const EM = window.EM;
// 维护的一份当前正在使用的音频设备的 deviceId 的列表
let usingAudioDeviceId = {
speaker: '',
microphone: ''
};
// 维护的一份拉流的 streamId 与本地预览通道的映射表
let streamIdToPreviewId = {};
let previewIdToStreamId = {};
// const streamIdToBlobUrl = {};
// 拉流的播放通道初始化从 0 开始依次递增至44(共45路流),-1 为推流的本地视频预览
// const uiChnIndexs = new Array(50).fill(0).map((a, b) => b + 1).reverse();
const chnTotal = 48;
const uiChnIndexs = new Array(chnTotal).fill(2).map((a, b) => a + b).reverse();
// 初始化是否完成的标志,0 为未完成,1 为完成
let isInitFinished = 0;
// 设备检测初始化是否完成的标志,0 为未完成,1 为完成
let isDeviceCheckInitFinished = 0;
// 进入房间是否成功
let hasLoginChannel = 0;
// 是否已经推流
let hasPushFlow = 0;
// 是否logoutChannel成功
let isAVEngineStopped = 0;
// 上层透传的回调函数缓存
// let zegoCallback = null;
// 是否执行过 Zego 的 AddListener 的标志
let hasAddListener = false;
// 是否处于网络错误状态中的标志
let hasNetWrong = false;
// rtc小班课=0;rtc大班课=1
const classType = 0;
// 监听id
let EMListenerId = 0;
let teacherId = null;
//静音推流时,音量值是否上抛上层标志 false=没有声音不往上层通知/true=有声音有上层通知
let isNoticeMicVolumeZego = false;
// 本地录制错误信息
const recordCodeMsg = {
0: '录制成功',
1: '路径太长',
2: '初始化avcontext失败',
3: '打开文件失败',
4: '写文件头失败',
5: '开始录制',
6: '停止录制',
7: '剩余空间不够',
8: '文件句柄异常',
9: 'IO异常',
};
// 心跳
let heartBeatDataReportObj = {};
let heartBeatDataReportTimer = null;
let isFirstHeartBeatReport = false;
let streamIdRtcPlayerInfo = {};
let streamIdRtcPlayerInfo1 = {};
let currStreamIdRtcPlayerInfo;
let lecturerStreamId = null;
let lecturerUid = null;
const heartBeatItemCount = 15;
const statsStore = new DataStore(5500);
const PullResult = {
PULL_STREAM_CODE_OK: 0, // 成功
PULL_STREAM_CODE_STOP: 1, // 停止
PULL_STREAM_CODE_FAIL: 12101000, // 调用拉流Api时发生异常 - 是个笼统的错误码
PULL_STREAM_CODE_TIMEOUT: 12102003, // 拉流超时
PULL_STREAM_CODE_RTC_SIGNAL_FAIL: 12200001, // 加入房间失败
PULL_STREAM_CODE_RTC_MEDIA_FAIL: 12301011, // 媒体链路建立失败
PULL_STREAM_CODE_NON_EXIST: 32001004, // 流不存在
};
const pullingStore = new DataStore(/*一小时过期时间*/60 * 60 * 1000);
let pullingStreamInterval = -1;
//记录底层推流回调的值
let dataZegoCapture = {};
//记录当前角色
let currentRole = '';
//记录当前课态类型:小组课=0/小班课=1
let currentClassMode = null;
//记录当前rtmp推流sdk类型
let currentGroupSdkType = '';
//主流ID
let localStreamId = '';
//屏幕流ID
let screenStreamId = '';
/**
* @function 调用端提供的回调方法
* @param name:String 回调方法名
* @param args 回调参数
* @return Promise | void
*/
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();
}
defaultApi.writeLog(`${name} Params: ${JSON.stringify(args)}`);
EM.CallMethod(
extensionId,
name,
JSON.stringify({ ...args, classType }),
(code, msg) => {
defaultApi.writeLog(`${name} Code: ${code} Message: ${msg}`);
resolve({
code,
msg
});
}
);
});
}
};
/**
* @function 加载 Zego 扩展
* @param extensionVersion:String 扩展版本号,必选
* @return Promise | void
*/
export const loadZego = (extensionVersion) => {
// EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
if (EM) {
return new Promise((resolve, reject) => {
if (getS('zegoVersion')) {
if (compareS('zegoVersion', extensionVersion)) {
return resolve();
}
}
EM.Load(
extensionId,
extensionVersion,
false,
(code, msg) => {
defaultApi.writeLog(`loadZego Code: ${code}\nMessage: ${msg}`);
if (code === 0) {
setS('zegoVersion', extensionVersion);
}
resolve();
}
);
});
}
};
/**
* @function 卸载 Zego 扩展
* @return Promise | void
*/
export const unloadZego = () => {
// EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
if (EM) {
return new Promise((resolve, reject) => {
EM.UnLoad(
extensionId,
(code, msg) => {
defaultApi.writeLog(`unloadZego Code: ${code}\nMessage: ${msg}`);
resolve();
}
);
});
}
};
/**
* @function 添加扩展监听机制
* @param userId:Number 用户 id,必选
* @param userName:String 用户名,必选
* @param roomId:String 频道(房间) id,必选
* @param nNetType:Number 网络类型,可选,默认为 1
* @return void
*/
const addListener = (userId, userName, confId, nNetType, devices, role) => {
// 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(`ZEGO::addListener-- EMListenerId: ${EMListenerId}`);
} catch (error) { }
}
if (!event || !data) {
return;
}
let _data = JSON.parse(data);
// if (_data && _data.classType === 1) {
// return false;
// }
switch (event) {
case 'OnInitSDK':
// 只有 OnInitSDK 事件成功返回后才可以执行后续操作,比如 loginChannel
var status = JSON.parse(data).nError;
var nclassType = JSON.parse(data).nClassType;
defaultApi.writeLog(`ZEGO::addListener-- OnInitSDK: ${status}--${nclassType}`);
if (status === 0 && nclassType == 0) {
if (userId === 'deviceCheckerInit') {
deviceCheckerInitSDK();
} else {
afterInitSDK(userId, userName, confId, nNetType, devices, role);
}
}
break;
case 'OnLoginChannel':
var status = JSON.parse(data).uiErrorCode;
defaultApi.writeLog(`ZEGO::addListener-- OnLoginChannel: ${status}`);
if (status === 0) {
hasLoginChannel = 1;
}
break;
case 'OnPublishStateUpdate':
let res = JSON.parse(data);
let keys = Object.keys(res);
if (!window.zbyAVSDK_init_params || !window.zbyAVSDK_init_params.zego) {
return;
}
if (keys.indexOf('eState') > -1) {
switch (res.eState) {
case 0:
hasPushFlow = 1;
window.zegoHasPushFlow = 1;
try {
let ReportArgs = {};
if (_data.pszStreamID.split('_').length == 5) {
ReportArgs.publish_rtc_streamid = _data.pszStreamID;
}
dataReport.publishResult({
code: '0',
...ReportArgs
// publish_streamid: window.zbyAVSDK_init_params.zego.streamId,
});
NOTICE.pushFlowSuccess({ code: 0, publish_streamid: _data.pszStreamID });
} catch (e) { };
if (window.zbyAVSDK_init_params.zego.isTurnPush) {
singleRecord('start');
}
break;
case 1:
hasPushFlow = 0;
window.zegoHasPushFlow = 0;
break;
// 推流重试
case 6:
case 7:
case 105:
// pushFlowExceptionHandler();
try {
dataReport.publishResult({
code: res.eState,
// publish_streamid: window.zbyAVSDK_init_params.zego.streamId,
});
} catch (e) { };
break;
case 10:
if (!hasNetWrong) {
hasNetWrong = true;
defaultApi.writeLog('error', 'Network error');
NOTICE.networkError({
message: 'Network error'
});
}
try {
dataReport.publishResult({
code: res.eState,
// publish_streamid: window.zbyAVSDK_init_params.zego.streamId,
});
} catch (e) { };
// pushFlowExceptionHandler();
break;
default:
try {
dataReport.publishResult({
code: res.eState,
// publish_streamid: window.zbyAVSDK_init_params.zego.streamId,
});
} catch (e) { };
break;
}
}
break;
case 'OnPublishQualityUpdate':
if (_data.hasOwnProperty('pq') && (_data.pszStreamID).split('_').length != 5) {
// 主辅两路质量回调,丢包延时只上抛一次,过滤掉辅路数据
_data.opt = 'quality';
_data.type = 'mediaupqos';
if (typeof _data.pq.kbps === 'number' && typeof _data.pq.fps === 'number') {
NOTICE.pushQuality({
video: {
streamId: _data.pszStreamID,
userId: util.getUidByStreamId(_data.pszStreamID),
bitrate: Math.round(_data.pq.kbps),
fps: Math.round(_data.pq.fps)
}
});
}
// console.log('推流延迟丢包zego',_data);
NOTICE.pushLossAndDelay({
userId: util.getUidByStreamId(_data.pszStreamID),
delay: _data.pq.rtt,
lostrate: _data.pq.pktLostRate
});
// heartBeatDataReportCalc(event, _data.pq, _data.pszStreamID);
statsStore.setData(_data.pszStreamID, _data.pq);
}
break;
case 'OnCaptureSoundLevelUpdate':
if (isNoticeMicVolumeZego) {
NOTICE.captureMicVolumeChanged({
volume: Math.round(_data.pCaptureSoundLevel[0].soundLevel)
});
}
// heartBeatDataReportObj.volume.push(Math.round(_data.pCaptureSoundLevel[0].soundLevel));
heartBeatDataReportObj.volume = heartBeatDataReportObj.volume + Math.round(_data.pCaptureSoundLevel[0].soundLevel) + ',';
break;
case 'OnSoundLevelUpdate':
_data.pSoundLevelList.forEach(item => {
const cbData = {
streamid: item.szStreamID,
userId: util.getUidByStreamId(item.szStreamID),
volume: Math.round(item.soundLevel)
};
NOTICE.playerVolumeChanged(cbData);
NOTICE.volumeChange(cbData);
// currStreamIdRtcPlayerInfo && currStreamIdRtcPlayerInfo.volume.push(Math.round(item.soundLevel));
// streamIdRtcPlayerInfo[item.szStreamID] && streamIdRtcPlayerInfo[item.szStreamID].volume.push(Math.round(item.soundLevel));
if (streamIdRtcPlayerInfo[item.szStreamID]) {
streamIdRtcPlayerInfo[item.szStreamID].volume = streamIdRtcPlayerInfo[item.szStreamID].volume + Math.round(item.soundLevel) + ',';
}
});
break;
case 'OnPlayStateUpdate':
if (_data.hasOwnProperty('eState')) {
if (!window.zbyAVSDK_init_params || !window.zbyAVSDK_init_params.zego) {
return;
}
let code = _data.eState;
if (code === 3) {
defaultApi.writeLog('error', `ZEGO:: OnPlayStateUpdate eState: ${code}`);
}
let normalCode = [0, 1, 3, 1011];
if (code === 10 && !hasNetWrong) {
hasNetWrong = true;
defaultApi.writeLog('error', 'Network error');
NOTICE.networkError({
message: 'Network error'
});
dabanDataReport({
action: 'zby_live_sdk_player_network_error',
uid: window.zbyAVSDK_init_params.zego.userId,
streamid: _data.pszStreamID,
teacherId: window.zbyAVSDK_init_params.zego.teacherId,
errorcode: code,
sdktype: 'zego'
});
}
// 拉流重试
if (normalCode.indexOf(code) === -1) {
setTimeout(() => {
// pullFlowExceptionHandler(_data.pszStreamID);
NOTICE.playError({
userId: util.getUidByStreamId(_data.pszStreamID),
message: 'play error'
});
dabanDataReport({
action: 'zby_live_sdk_player_play_error',
uid: window.zbyAVSDK_init_params.zego.userId,
streamid: _data.pszStreamID,
teacherId: window.zbyAVSDK_init_params.zego.teacherId,
errorcode: code,
sdktype: 'zego'
});
}, 1000);
};
try {
if (_data.pszStreamID.indexOf('ai') < 0) {
dataReport.pullResult({
code: code,
// pull_uid: _data.pszStreamID.split('_').length == 5 ? _data.pszStreamID : util.getUidByStreamId(_data.pszStreamID),
pull_uid: util.getUidByStreamId(_data.pszStreamID),
pull_streamid: _data.pszStreamID
});
pullingStore.delete(_data.pszStreamID);
NOTICE.pullFlowResult({ code: code, pull_streamid: _data.pszStreamID });
}
// dataReport.pullResult({
// code: code,
// pull_uid: util.getUidByStreamId(_data.pszStreamID),
// pull_streamid: _data.pszStreamID
// });
} catch (e) { };
}
break;
case 'OnPlayQulityUpdate': // 拉流
if (_data.hasOwnProperty('pq')) {
// console.log('拉流回调',`${new Date().getMinutes()}:${new Date().getSeconds()}`,_data);
_data.opt = 'quality';
_data.type = 'mediadownqos';
NOTICE.pullQuality({
video: {
streamId: _data.pszStreamID,
userId: util.getUidByStreamId(_data.pszStreamID),
bitrate: _data.pq.kbps,
fps: _data.pq.fps,
}
});
// console.log('拉流延迟丢包zego',_data);
NOTICE.playLossAndDelay({
userId: util.getUidByStreamId(_data.pszStreamID),
delay: _data.pq.rtt,
lostrate: _data.pq.pktLostRate
});
currStreamIdRtcPlayerInfo = streamIdRtcPlayerInfo[_data.pszStreamID];
// heartBeatDataReportCalc(event, _data.pq, _data.pszStreamID);
statsStore.setData(_data.pszStreamID, _data.pq);
}
break;
case 'OnVideoDeviceStateChanged': // 视频设备状态变更
// _data.state 0=添加设备 1=移除设备
// console.log('hsghsghsg摄像头热插拔',_data);
setVideoDevice({ deviceId: _data.state == 1 ? '' : _data.deviceInfo.szDeviceId, idx: 0, operationType: 'hotPlug', deviceState: _data.state, changedDeviceId: _data.deviceInfo.szDeviceId });
// try {
// dataReport.deviceTest({
// device: '1',
// fore_state: _data.state == 0 ? 1 : 2 ,
// camera_name: _data.deviceInfo && _data.deviceInfo.szDeviceName,
// });
// } catch (e){};
break;
case 'OnAudioDeviceStateChanged': // 音频设备状态变更
// console.log('hsghsghsg扬声器热插拔1',_data);
if (_data.deviceType === 0) {
// _data.state 0=添加设备 1=移除设备
setMicrophoneDevice({ deviceId: _data.state == 1 ? '' : _data.deviceInfo.szDeviceId, operationType: 'hotPlug', deviceState: _data.state, changedDeviceId: _data.deviceInfo.szDeviceId });
}
if (_data.deviceType === 1) {
// console.log('hsghsghsg扬声器热插拔',_data);
setSpeakerDevice({ deviceId: _data.state == 1 ? '' : _data.deviceInfo.szDeviceId, operationType: 'hotPlug', deviceState: _data.state, changedDeviceId: _data.deviceInfo.szDeviceId });
}
// try {
// dataReport.deviceTest({
// device: _data.deviceType === 0 ? '2' : '3',
// fore_state: _data.state == 0 ? 1 : 2 ,
// microphone_name: _data.deviceInfo && _data.deviceInfo.szDeviceName,
// speaker_name: _data.deviceInfo && _data.deviceInfo.szDeviceName
// });
// } catch (e){};
break;
case 'OnAudioDefaultDeviceStateChanged': // 音频默认设备状态变更
if (_data.deviceType === 0) {
NOTICE.defaultMicChanged({ deviceId: _data.deviceId });
} else if (_data.deviceType === 1) {
NOTICE.defaultSpeakerChanged({ deviceId: _data.deviceId });
}
break;
case 'OnDeviceError':
//errorCode 无效设备ID (-2), 没有权限 (-3), 采集帧率为0 (-4), 设备被占用 (-5), 设备未插入 (-6), 需要重启系统 (-7), 媒体服务无法恢复 (-8), 设备被SIRI占用 (-9)
let codemsg = {
'-2': '无效设备ID',
'-3': '没有权限',
'-4': '采集帧率为0',
'-5': '设备被占用',
'-6': '设备未插入',
'-7': '需要重启系统',
'-8': '媒体服务无法恢复',
'-9': '设备被SIRI占用'
};
defaultApi.writeLog(`avsdk ZEGO:: OnDeviceError ${_data.deviceName} ${_data.errorCode}`);
dataReport.deviceError({ deviceName: _data.deviceName, errorCode: _data.errorCode, code: _data.errorCode });
NOTICE.deviceError({code: _data.errorCode, msg: codemsg[_data.errorCode], deviceId: _data.deviceId, deviceName: _data.deviceName});
// _data.deviceName === 'camera' ? setVideoDevice({deviceId: '', idx: 0, operationType: 'deviceError', deviceState: '', code: codemsg[_data.errorCode]}) : setMicrophoneDevice({deviceId: '', operationType: 'deviceError', deviceState: '', code: codemsg[_data.errorCode]});
break;
case 'OnAVKitEvent':
// defaultApi.writeLog('avsdk ZEGO:: OnAVKitEvent');
if (_data.event === 4 && hasNetWrong) {
hasNetWrong = false;
NOTICE.networkRecovery({
message: 'Network recovery'
});
}
break;
case 'OnAVEngineStop':
isAVEngineStopped = 1;
defaultApi.writeLog('avsdk ZEGO:: listened AVEngineStop');
break;
case 'OnMixStream':
defaultApi.writeLog('avsdk ZEGO:: listened mix_stream_response');
break;
case 'OnVideoSizeChanged':
if (_data.nWidth > 0 && _data.nHeight > 0) {
try {
NOTICE.firstVideoSize({
streamId: _data.pStreamID,
// userId: _data.pszStreamID.split('_').length == 5 ? _data.pszStreamID : util.getUidByStreamId(_data.pszStreamID)
userId: util.getUidByStreamId(_data.pStreamID)
});
dataReport.firstVideoSize({
pull_streamid: _data.pStreamID,
pull_uid: util.getUidByStreamIdDr(_data.pStreamID),
code: '0'
});
} catch (e) { };
} else {
defaultApi.writeLog('avsdk ZEGO:: OnVideoSizeChanged - nWidth and nHeight is 0');
}
break;
case 'OnRenderRemoteVideoFirstFrame':
if (!window.zbyAVSDK_init_params || !window.zbyAVSDK_init_params.zego) {
return;
}
// 大班数据上报
if (window.zbyAVSDK_init_params.zego.role === 'student') {
dabanDataReport({
action: 'interact_play',
teammate_streamid: _data.pszStreamID,
uid: window.zbyAVSDK_init_params.zego.userId
});
}
try {
NOTICE.firstVideoSize({
streamId: _data.pszStreamID,
// userId: _data.pszStreamID.split('_').length == 5 ? _data.pszStreamID : util.getUidByStreamId(_data.pszStreamID)
userId: util.getUidByStreamId(_data.pszStreamID)
});
dataReport.firstVideoSize({
pull_streamid: _data.pszStreamID,
pull_uid: util.getUidByStreamIdDr(_data.pszStreamID),
code: '0'
});
} catch (e) { };
defaultApi.writeLog(`ZEGO: pullStream success & haveFirstVideoSize streamId: ${_data.pszStreamID}`);
// try {
// dataReport.pullResult({
// status: 0,
// pull_uid: util.getUidByStreamId(_data.pszStreamID),
// pull_streamid: _data.pszStreamID
// });
// } catch (e) {};
break;
case 'OnRecvRemoteAudioFirstFrame':
try {
NOTICE.firstAudioSize({
streamId: _data.pszStreamID,
userId: util.getUidByStreamId(_data.pszStreamID)
});
dataReport.firstAudioSize({
pull_streamid: _data.pszStreamID,
pull_uid: util.getUidByStreamIdDr(_data.pszStreamID),
code: '0'
});
} catch (e) { };
break;
// 转推回调 0=转推停止 1=正在转推 2=正在重试
case 'onRecvMediaSideInfo':
defaultApi.writeLog(`avsdk ZEGO:: onRecvMediaSideInfo, ${JSON.stringify(_data)}`);
NOTICE.seiReceived({ data: _data });
break;
case 'zego_recovery_start':
defaultApi.writeLog(`avsdk ZEGO:: zego_recovery_start ${JSON.stringify(_data)}`);
NOTICE.zegoRecoveryStart();
break;
case 'OnRelayCDNStateUpdate':
if (_data.statesInfo && _data.statesInfo[0] && _data.statesInfo[0].state === 1) {
NOTICE.recordResponse({
streamId: _data.streamID,
userId: util.getUidByStreamId(_data.streamID)
});
}
// try {
// dataReport.turnPushResult({
// errorcode:_data.statesInfo[0].state,
// case: 'turn push',
// streamid: _data.streamID
// })
// } catch (e) {};
break;
case 'OnCaptureVideoFirstFrame':
if (!window.zbyAVSDK_init_params || !window.zbyAVSDK_init_params.zego) {
return;
}
// 大班数据上报
dabanDataReport({
action: 'open_camera',
camera_status: 1,
uid: window.zbyAVSDK_init_params.zego.userId
});
break;
case 'OnKaDunEvent':
if (_data.event === 8) {
currStreamIdRtcPlayerInfo && (currStreamIdRtcPlayerInfo.video_ifg += _data.kaDunTime);
}
if (_data.event === 10) {
currStreamIdRtcPlayerInfo && (currStreamIdRtcPlayerInfo.audio_ifg += _data.kaDunTime);
}
break;
case 'OnMediaRecord':
NOTICE.recordStateUpdate(_data);
if (_data.errorCode === 0) {
NOTICE.recordFinished({path: _data.storagePath, idx: _data.channelIndex});
} else if (_data.errorCode === 5) {
NOTICE.recordBegin({idx: _data.channelIndex});
} else if (_data.errorCode === 6) {
NOTICE.recordEnd({idx: _data.channelIndex});
} else {
NOTICE.recordError({code: _data.errorCode, idx: _data.channelIndex, msg: recordCodeMsg[_data.errorCode]});
}
break;
case 'onBeautifyEvent':
if (_data.code === 0) {
defaultApi.writeLog(`avsdk ZEGO:: onBeautifyEvent success ${JSON.stringify(_data)}`);
} else {
const message = [
'',
'License错误',
'渲染错误',
'参数错误',
];
NOTICE.beautifyError({
code: _data.code,
message: message[_data.code],
});
}
break;
case 'onJoinchannelError':
defaultApi.writeLog(`ZEGO: event:: onJoinchannelError: ${data}`);
dataReport.joinRtcChannel(_data);
break;
};
if (zbysdk.openListenerLog) {
defaultApi.writeLog(`Zego::Listener:: event: ${event}, data: ${JSON.stringify(data)}`);
}
});
}
};
const startPullingStreamObserver = () => {
pullingStore.restore();
if (pullingStreamInterval !== -1) {
clearInterval(pullingStreamInterval);
}
pullingStreamInterval = setInterval(() => {
pullingStore.forEach((value, key) => {
try {
if (!pullingStore.checkData(value)) {
pullingStore.delete(key);
dataReport.pullResult({
code: PullResult.PULL_STREAM_CODE_TIMEOUT,
media_type: value.info,
pull_uid: util.getUidByStreamIdDr(key),
pull_streamid: key,
});
}
} catch (e) {
defaultApi.writeLog(`pullingStreamInterval error ${e}`, undefined, 'error');
}
});
}, 500);
};
/**
* @function 添加扩展监听机制
* @return void
*/
const removerListener = () => {
hasAddListener = false;
defaultApi.writeLog(`ZEGO::action--removerListener EMListenerId:${EMListenerId}`);
EM.RemoverListener(extensionId, EMListenerId, (ec, content) => { });
EMListenerId = 0;
};
/**
* @function 推流异常恢复
*/
export const pushFlowExceptionHandler = async () => {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
defaultApi.writeLog('断网重推');
defaultApi.writeLog('avsdk ZEGO::try to pushFlow again');
await stopPublish();
await startPublish2();
};
/**
* @function 拉流异常恢复
*/
export const pullFlowExceptionHandler = async (streamId) => {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
defaultApi.writeLog('断网重拉');
defaultApi.writeLog(`avsdk ZEGO::try to pullFlow again ,streamId : ${streamId}`);
await stopPlayStream(streamId);
await playStream(streamId);
};
/**
* @function 初始化 Engine
* @param appId:String Zego 秘钥,必选
* @param appSignature:String 签名,必选
* @param businessType:Number 业务类型,可选,默认为 0
* @param videoRenderType:Number zego视频渲染类型,默认为2(只外部渲染),zego小组课需要设置为5(同时支持内部+外部渲染)
* @return Promise | void
*/
export const startEngine = (appId, appSignature, businessType, videoRenderType = 2) => {
if (typeof businessType === 'undefined') {
businessType = 0;
}
defaultApi.writeLog('avsdk ZEGO::StartEngine');
return callMethod('StartEngine', {
appId,
appSignature,
businessType,
videoRenderType
});
};
//开启消除回声
export const setEnableAEC = (bEnable) => {
return callMethod('EnableAEC', { bEnable });
};
//设置回声消除模式 mode :激进模式 =0/中等模式 =1/轻度模式 =2
export const setAECMode = (mode) => {
return callMethod('SetAECMode', { mode });
};
//开启噪音消除
export const setEnableNoiseSuppress = (bEnable) => {
return callMethod('EnableNoiseSuppress', { bEnable });
};
//设置噪音消除模式 mode :0:低 1:中 2高
export const setNoiseSuppressMode = (level) => {
return callMethod('SetNoiseSuppressMode', { level });
};
//新增瞬时降噪接口 瞬时降噪 可以处理掉 敲键盘、敲桌子、敲门等瞬态噪声。非瞬态噪声,此接口无法处理,比如划桌子等摩擦噪音。
export const enableTransientNoiseSuppress = (enable) => {
return callMethod('EnableTransientNoiseSuppress', { enable });
};
/**
* @function 加载采集插件的输入数据 //调用后,zego 可以向采集插件传送摄像头数据,用于拼接
* @return: Promise
*/
export const loadCollectionInputEntry = () => {
return callMethod('LoadCollectionInputEntry', {});
};
/**
* @function 加载采集插件的输出数据 //调用后,zego 可以向采集插件传送学生流数据(加载截屏插件,获取截屏插件设置拉流头像回调函数)
* @param stream_ids array 设置需要拼接的学生头像流id,按数组顺序拼接
* @return: Promise
*/
export const loadCollectionInputEntry2 = (stream_ids) => {
return callMethod('LoadCollectionInputEntry2', { stream_ids });
};
/**
* @function 加载采集插件的输出数据 //调用后,zego 可以从采集插件获取拼接数据
* @return: Promise
*/
export const loadCollectionOutputEntry = () => {
return callMethod('LoadCollectionOutputEntry', {});
};
/**
* @function 设置直推url //调用后,zego 可以从采集插件获取拼接数据
* @param pRtmpUrl:String 直推地址
* @param idx:Number 使用外部采集的通道idx,与StartEngine中channel_idx保持一致
* @return: Promise
*/
export const setCDNPublishTarget = (pRtmpUrl, idx) => {
return callMethod('SetCDNPublishTarget', { pRtmpUrl, idx });
};
/**
* @function 销毁引擎
* @return Promise | void
*/
export const destroyEngine = () => {
return callMethod('DestroyEngine', {});
};
/**
* @function 加入频道
* @param userId:Number 用户 id,必选
* @param userName:String 用户名,必选
* @param channelId:String 频道 id,必选
* @param nNetType:Number 网络类型,可选,默认为 1
* @return Promise | void
*/
export const loginChannel = (userId, userName, channelId, nNetType) => {
if (typeof nNetType === 'undefined') {
nNetType = 1;
}
return callMethod('LoginChannel', {
pszUserID: userId + '',
pszUserName: userName,
pszChannelID: channelId,
nNetType
});
};
/**
* @function 退出直播频道
* @return Promise | void
*/
export const logoutChannel = () => {
defaultApi.writeLog('avsdk ZEGO:: CallMethod : LogoutChannel');
return callMethod('LogoutChannel', {});
};
/**
* @function 云控时退出房间
* @return Promise
*/
export const leaveRoomWithChangeSDK = async () => {
defaultApi.writeLog(`leaveRoomWithChangeSDK--currentClassMode--currentGroupSdkType ${currentClassMode}, ${currentGroupSdkType}`);
let needDestroyZegoEngine = true; //云控从zego切到其他sdk时,默认释放zego引擎。但是如果zego在推拉RTMP流,则不能释放
if (currentGroupSdkType == 'zego' || window.zby_isM1Type) {
switch (currentClassMode) {
case 0: // 小组
case 2: // 组合小班
needDestroyZegoEngine = false;
break;
case 1: // 小班
if (window.zby_sdk_init_params.role === 'teacher' || window.zby_sdk_init_params.role === 'assistant') {
needDestroyZegoEngine = false;
}
break;
}
}
await stopSoundLevelMonitor();
for (let key in streamIdToPreviewId) {
await stopPlayStream(key, true);
}
if ((isZegoGroupClass() && (currentRole === 'teacher' || currentRole === 'assistant')) || (isZegoCombinationClass() && currentRole === 'assistant')) {
// zego小组课,切RTC流云控后,授课端不能停止预览
console.log('zego小组课不可停止预览,会影响rtmp');
}
else {
await stopPreview();
}
await stopPublish();
removerListener();
if (needDestroyZegoEngine === true) {
await destroyEngine();
}
if ((window.zby_sdk_init_params.role === 'teacher') && window.zby_sdk_init_params.mode === 1) {
//坐立模式关闭外部采集器,站立关闭外部采集器
await setVideoCaptureFactory(false);
}
heartBeatDataReport('stop');
isFirstHeartBeatReport = false;
streamIdToPreviewId = {};
previewIdToStreamId = {};
streamIdRtcPlayerInfo = {};
streamIdRtcPlayerInfo1 = {};
// heartBeatDataReportObj = {};
hasLoginChannel = 0;
hasPushFlow = 0;
isAVEngineStopped = 0;
window.zegoHasPushFlow = 0;
};
/**
* @function 退出房间
* @return Promise
*/
export const leaveRoom = async (type) => {
await stopSoundLevelMonitor();
for (let key in streamIdToPreviewId) {
await stopPlayStream(key, true);
}
await stopPublish();
removerListener();
// await logoutChannel();
// await new Promise((resolve, reject) => {
// let timer = setInterval(() => {
// if (isAVEngineStopped) {
// clearInterval(timer);
// resolve();
// }
// }, 300);
// });
if (type == 'reload') {
defaultApi.writeLog('leaveroom -- reload --小组课刷新不可销毁zego引擎');
} else {
await stopPreview();
await destroyEngine();
}
if ((window.zby_sdk_init_params.role === 'teacher') && window.zby_sdk_init_params.mode === 1) {
//坐立模式关闭外部采集器,站立关闭外部采集器
await setVideoCaptureFactory(false);
}
heartBeatDataReport('stop');
isFirstHeartBeatReport = false;
streamIdToPreviewId = {};
previewIdToStreamId = {};
streamIdRtcPlayerInfo = {};
streamIdRtcPlayerInfo1 = {};
// heartBeatDataReportObj = {};
hasLoginChannel = 0;
hasPushFlow = 0;
isAVEngineStopped = 0;
window.zegoHasPushFlow = 0;
};
export const stopCheckDevice = async () => {
removerListener();
await stopSoundLevelMonitor();
await stopPreview();
defaultApi.writeLog('rtc stopCheckDevice and removerListener and captureDestroy');
return callMethod('DestroyEngine', {});
};
/**
* @function 获取系统默认的音频设备
* @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
* @return Promise | void
*/
export const getDefaultAudioDeviceId = (deviceType) => {
return callMethod('GetDefaultAudioDeviceId', {
deviceType,
// 以下两个参数仅仅是为了向下向后兼容,使用 hardcore 即可
deviceId: '',
deviceIdLength: 0
});
};
/**
* @function 获取系统默认的麦克风设备 id
* @return Promise
*/
export const getDefaultMicrophone = () => {
return getDefaultAudioDeviceId(0);
};
/**
* @function 获取系统默认的扬声器设备 id
* @return Promise
*/
export const getDefaultSpeaker = () => {
return getDefaultAudioDeviceId(1);
};
/**
* @function 获取摄像头列表
* @return Promise | void
*/
export const getVideoDeviceList = async () => {
let videoListArr = JSON.parse((await callMethod('GetVideoDeviceList', {
// 这个参数是 C++ 要使用的引用参数,随便传个整型就行
device_count: 1
})).msg).ret;
let videoList = [];
for (let i = 0, len = videoListArr.length; i < len; i++) {
videoList.push({
deviceId: videoListArr[i].szDeviceId,
deviceName: videoListArr[i].szDeviceName,
isDefault: i === 0
});
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;
return videoList;
};
/**
* @function 指定使用的摄像头
* @param deviceId:String 摄像头 id,必选
* @param idx:Number 推流通道,可选,默认为 0
* @param operationType:String 操作类型,可选
* 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
* 不传即是普通的设置摄像头设备的行为
* @return Promise | void
*/
export const setVideoDevice = async (args) => {
defaultApi.writeLog(`${JSON.stringify(args)}----setVideoDevice-zego----`);
let { deviceId, idx, operationType, deviceState, code, changedDeviceId } = args;
let deviceName = '';
let cameraData = await getVideoDeviceList();
if (cameraData.length) {
let defaultDevice;
for (let item of cameraData) {
if (deviceId && item.deviceId === deviceId) {
deviceName = item.deviceName;
break;
}
if (item.isDefault) {
defaultDevice = item;
}
}
if (!defaultDevice) {
defaultDevice = cameraData[0];
}
if (!deviceId || !deviceName) {
NOTICE.deviceError({
code: -2,
msg: '设备ID无效',
deviceId,
deviceName,
});
deviceId = defaultDevice.deviceId;
deviceName = defaultDevice.deviceName;
}
} else {
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];
if (typeof idx === 'undefined') {
idx = 0;
}
try {
dataReport.setDevice({
device_type: 1,
device_id: deviceId,
device_name: deviceListReport.cameraList[deviceId],
operationType,
fore_state: operationType == 'hotPlug' ? deviceState + 1 : '-'
});
} catch (e) { };
await callMethod('SetVideoDevice', {
pszDeviceID: deviceId,
idx
});
if (operationType == 'hotPlug' || operationType == 'deviceError') {
if (!cameraData) {
cameraData = await getVideoDeviceList();
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 开/关摄像头
* @param enable:Boolean false -> 关闭,true -> 打开,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const enableCamera = (enable, idx = 0) => {
// try {
// dataReport.deviceChange({
// device: 1,
// device_state: enable? 1 : 0
// });
// } catch (e){};
return callMethod('EnableCamera', {
bEnable: enable,
idx
});
};
/**
* @function 开/关麦克风
* @param bEnable:Boolean false -> 关闭,true -> 打开,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const enableMic = async (bEnable, idx) => {
defaultApi.writeLog('enableMic --start');
isNoticeMicVolumeZego = bEnable;
// if (bEnable) {
// await setMicDeviceMute(false);
// defaultApi.writeLog('openMic and muteFalse');
// }
if (typeof idx === 'undefined') {
idx = 0;
}
// try {
// dataReport.deviceChange({
// device: 2,
// device_state: bEnable ? 1 : 0
// });
// } catch (e){};
return callMethod('EnableMic', {
bEnable,
idx
});
};
/**
* @function 是否推声音
* @param mute:Boolean true/不推声音,false/推声音,必选
* @return Promise | void
*/
export const setMuteStreamAudio = (mute) => {
return callMethod('MuteStreamAudio', { mute });
};
/**
* @function 是否推画面
* @param mute:Boolean true/不推画面,false/推画面,必选
* @return Promise | void
*/
export const setMuteStreamVideo = (mute) => {
return callMethod('MuteStreamVideo', { mute });
};
/**
* @function 获取音频设备
* @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
* @return Promise | void
*/
export const getAudioDeviceList = (deviceType) => {
return callMethod('GetAudioDeviceList', {
deviceType,
// 这个参数是 C++ 要使用的引用参数,随便传个整型就行
device_count: 0
});
};
/**
* @function 指定音频设备
* @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
* @param deviceId:String 音频设备 id,必选
* @return Promise | void
*/
export const setAudioDevice = (deviceType, deviceId) => {
if (deviceType == 0) {
window.zbyAVSDK_device_checker_init.microphone.use = deviceId;
} else {
window.zbyAVSDK_device_checker_init.speaker.use = deviceId;
}
return callMethod('SetAudioDevice', {
deviceType,
pszDeviceID: deviceId
}).then((result) => {
if (deviceType == 1 && result.code === 0) {
NOTICE.speakerChanged({
deviceId
});
}
return result;
});
};
/**
* 获取当前扬声器设备id
* @return {String} 当前扬声器设备id
*/
const getCurrentSpeakerDevice = () => {
defaultApi.writeLog(`avsdk ZEGO::getSpeakerDevice ${usingAudioDeviceId.speaker}`);
return usingAudioDeviceId.speaker;
};
/**
* @function 获取扬声器列表
* @return Promise | void
*/
export const getSpeakerDeviceList = async () => {
const defaultSpeakerId = JSON.parse((await getDefaultSpeaker()).msg).ret.deviceId;
let speakerListArr = JSON.parse((await getAudioDeviceList(1)).msg).ret;
let speakerList = [];
for (let i = 0, len = speakerListArr.length; i < len; i++) {
speakerList.push({
deviceId: speakerListArr[i].szDeviceId,
deviceName: speakerListArr[i].szDeviceName,
isDefault: defaultSpeakerId === speakerListArr[i].szDeviceId
});
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;
return speakerList;
};
/**
* @function 指定扬声器
* @param deviceId:String 扬声器 id,必选
* @param operationType:String 操作类型,可选
* 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
* 不传即是普通的设置扬声器设备的行为
* @return Promise | void
*/
export const setSpeakerDevice = async (args) => {
defaultApi.writeLog(`${JSON.stringify(args)}----setSpeakerDevice-zego----`);
// console.log('hsghsghsg999',args);
let { deviceId, operationType, deviceState, code, changedDeviceId } = args;
let deviceName = '';
let speakerData;
if (!deviceId) {
speakerData = await getSpeakerDeviceList();
// 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) { };
usingAudioDeviceId.speaker = deviceId;
await setAudioDevice(1, deviceId);
if (operationType == 'hotPlug' || operationType == 'deviceError') {
if (!speakerData) {
speakerData = await getSpeakerDeviceList();
deviceName = deviceListReport.speakerList[deviceId];
}
NOTICE[operationType]({
deviceType: 'speaker',
useDeviceId: deviceId,
useDeviceName: deviceName,
deviceList: speakerData,
message: code,
deviceState,
changedDeviceId
});
}
};
/**
* @function 获取扬声器音量
* @return Promise | void
*/
export const getSpeakerDeviceVolume = () => {
return callMethod('GetSpeakerDeviceVolume', {
deviceId: usingAudioDeviceId.speaker
});
};
/**
* @function 设置扬声器音量
* @param volume:Number 扬声器音量,必选
* @return Promise | void
*/
export const setSpeakerDeviceVolume = (volume) => {
return callMethod('SetSpeakerDeviceVolume', {
deviceId: usingAudioDeviceId.speaker,
volume
});
};
/**
* @function 设置扬声器静音
* @param mute:Boolean 必选
* @return Promise | void
*/
export const setSpeakerDeviceMute = (mute) => {
// try {
// dataReport.deviceChange({
// device: 3,
// device_state: mute ? 0 :1
// });
// } catch (e){};
return callMethod('SetSpeakerDeviceMute', {
deviceId: usingAudioDeviceId.speaker,
mute
});
};
/**
* @function 获取麦克风列表
* @return Promise | void
*/
export const getMicrophoneDeviceList = async () => {
const defaultMicrophoneId = JSON.parse((await getDefaultMicrophone()).msg).ret.deviceId;
let microphoneListArr = JSON.parse((await getAudioDeviceList(0)).msg).ret;
let microphoneList = [];
for (let i = 0, len = microphoneListArr.length; i < len; i++) {
microphoneList.push({
deviceId: microphoneListArr[i].szDeviceId,
deviceName: microphoneListArr[i].szDeviceName,
isDefault: defaultMicrophoneId === microphoneListArr[i].szDeviceId
});
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;
return microphoneList;
};
/**
* @function 指定麦克风
* @param deviceId:String 麦克风 id,必选
* @param operationType:String 操作类型,可选
* 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
* 不传即是普通的设置麦克风设备的行为
* @return Promise | void
*/
export const setMicrophoneDevice = async (args) => {
defaultApi.writeLog(`${JSON.stringify(args)}----setMicrophoneDevice-zego----`);
let { deviceId, operationType, deviceState, code, changedDeviceId } = args;
let deviceName = '';
let microPhoneData;
if (!deviceId) {
microPhoneData = await getMicrophoneDeviceList();
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) { };
usingAudioDeviceId.microphone = deviceId;
await setAudioDevice(0, deviceId);
await setAudioVolumeNotify(0, deviceId);
if (operationType == 'hotPlug' || operationType == 'deviceError') {
if (!microPhoneData) {
microPhoneData = await getMicrophoneDeviceList();
deviceName = deviceListReport.micList[deviceId];
}
NOTICE[operationType]({
deviceType: 'microphone',
useDeviceId: deviceId,
useDeviceName: deviceName,
deviceList: microPhoneData,
message: code,
deviceState,
changedDeviceId
});
}
};
/**
* @function 获取麦克风音量
* @return Promise | void
*/
export const getMicDeviceVolume = () => {
return callMethod('GetMicDeviceVolume', {
deviceId: usingAudioDeviceId.microphone
});
};
/**
* @function 设置麦克风音量
* @param volume:Number 麦克风音量,必选
* @return Promise | void
*/
export const setMicDeviceVolume = (volume) => {
return callMethod('SetMicDeviceVolume', {
deviceId: usingAudioDeviceId.microphone,
volume
});
};
/**
* @function 设置麦克风静音
* @param mute:Boolean 必选
* @return Promise | void
*/
export const setMicDeviceMute = (mute) => {
return callMethod('SetMicDeviceMute', {
deviceId: usingAudioDeviceId.microphone,
mute
});
};
//设置采集扬声器声音
export const setAudioSpeakerCapture = async (bEnable) => {
return callMethod('EnableMixSystemPlayout', {
bEnable,
});
};
// //采集扬声器
// export const setAudioSpeakerCapture2 = async (bEnable) => {
// return callMethod('MixEnginePlayout', {
// bEnable,
// });
// };
/**
* @function 获取当前应用程序音量
* @return Promise | void
*/
export const getSpeakerSimpleVolume = () => {
return callMethod('GetSpeakerSimpleVolume', {
deviceId: usingAudioDeviceId.speaker
});
};
/**
* @function 设置视频预览载体 view
* @param idx:Number 直播通道,可选,默认为 0
* @return Promise | void
*/
export const setPreviewView = (idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('SetPreviewView', {
idx
});
};
/**
* @function 设置本地预览视频视图的模式
* @param mode:Number 模式,必选,0 | 1 | 2
* 0:等比缩放,可能有黑边
* 1:等比缩放填充整View,可能有部分被裁减
* 2:填充整个View
* @param idx:Number 直播通道,可选,默认为 0
* @return Promise | void
*/
export const setPreviewViewMode = (mode, idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('SetPreviewViewMode', {
mode,
idx
});
};
/**
* @function 开启预览
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const startPreview = (idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('StartPreview', {
idx
});
};
/**
* @function 停止预览
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const stopPreview = (idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('StopPreview', {
idx
});
};
/**
* @function 设置当前应用程序音量
* @param volume:Number 应用程序音量,必选
* @return Promise | void
*/
export const setSpeakerSimpleVolume = (volume) => {
return callMethod('SetSpeakerSimpleVolume', {
deviceId: usingAudioDeviceId.speaker,
volume
});
};
/**
* @function 设置当前应用程序静音
* @param mute:Boolean 是否静音,必选
* @return Promise | void
*/
export const setSpeakerSimpleMute = (mute) => {
return callMethod('SetSpeakerSimpleMute', {
deviceId: usingAudioDeviceId.speaker,
mute
});
};
/**
* @function 开启本地或者远程的视频视图
* @param isLocal:Boolean 是否是本地的视频预览,必选
* @param streamId:String 要拉取的视频流的 id,可选,只有拉取远程的视频流的时候才是必选的
* @param domId:String <video> 标签的 id,可选
* 如果传了 domId,就把视频绑定到对应的 <video> 标签上
* @return Promise 可从 Promise 中获取 src,Promise.then((src) => {})
*/
export const startLocalOrRemotePreview = async (isLocal, streamId, domId, isTeacherNewWindowPreview) => {
defaultApi.writeLog(`${streamId}----streamId-zego----`);
let playChannel = null;
let isUpdateChromeVersion = await util.getChromeVersion();
// zego 的本地视频预览必须使用 -1 通道,为了与远程的视频预览兼容使用,此处需要判断是否是本地预览
if (isLocal) {
playChannel = -1;
} else {
playChannel = streamIdToPreviewId[streamId];
}
const externalConstraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'external',
chromeMediaSourceId: `ems://zego/${isLocal || isTeacherNewWindowPreview ? -1 : streamId}`,
maxWidth: 3840,
maxHeight: 2160,
minWidth: 240,
minHeight: 180,
}
}
};
defaultApi.writeLog(`avsdk ZEGO::startLocalOrRemotePreview streamId: ${streamId} uiChnIndex: ${playChannel}`);
return new Promise((resolve, reject) => {
const handleExternalSuccess = (stream) => {
defaultApi.writeLog(`${stream}----stream-zego----`);
stream.oninactive = () => {
defaultApi.writeLog('Stream inactive');
};
const src = isUpdateChromeVersion ? stream : window.URL.createObjectURL(stream);
if (domId && document.querySelector(domId)) {
if (isUpdateChromeVersion) {
document.querySelector(domId).srcObject = src;
} else {
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 nFps:Number 帧率,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const setVideoFPS = (nFps, idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('SetVideoFPS', {
nFps,
idx
});
};
/**
* @function 设置本地预览以及推流的视频码率
* @param nBitrate:Number 码率,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const setVideoBitrate = (nBitrate, idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('SetVideoBitrate', {
nBitrate,
idx
});
};
/**
* @function 设置本地预览以及推流的音频码率
* @param bitrate:Number 码率,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const setAudioBitrate = (bitrate, idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('SetAudioBitrate', {
bitrate,
idx
});
};
/**
* @function 设置本地预览的视频分辨率
* @param nWidth:Number 分辨率宽,必选
* @param nHeight:Numnber 分辨率高,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const setCameraParam = async (nWidth, nHeight, idx) => {
defaultApi.writeLog(`avsdk ZEGO::setCameraParam ${nWidth}--${nHeight}`);
await enableCamera(false);
await setVideoCaptureResolution(nWidth, nHeight, idx);
return enableCamera(true);
};
/**
* @function 设置本地预览的视频分辨率
* @param nWidth:Number 分辨率宽,必选
* @param nHeight:Numnber 分辨率高,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const setVideoCaptureResolution = async (nWidth, nHeight, idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
await setCapturePipelineScaleMode();
return callMethod('SetVideoCaptureResolution', {
nWidth,
nHeight,
idx
});
};
export const setCapturePipelineScaleMode = () => {
return callMethod('SetCapturePipelineScaleMode', {
scaleMode: 1
});
};
/**
* @function 设置推流的视频分辨率
* @param nWidth:Number 分辨率宽,必选
* @param nHeight:Numnber 分辨率高,必选
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const setVideoEncodeResolution = (nWidth, nHeight, idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('SetVideoEncodeResolution', {
nWidth,
nHeight,
idx
});
};
/**
* @function 初始化
* @param args:Object
* {
* extensionVersion, // String,扩展版本号,必选
* appId, // String,Zego 秘钥,必选
* appSignature, // String,签名,必选
* businessType, // Number,业务类型,必选
* userId, // Number,用户 id,必选
* userName, // String,用户名,必选
* roomId, // String,频道(房间) id,必选
* nNetType // Number,网络类型,可选,默认为 1
* }
* classMode 0:小组课及其他 1:小班课 2:组合小班课
* @return Promise
*/
export const init = async (args) => {
defaultApi.writeLog(`avsdk ZEGO::init ${JSON.stringify(args)}`);
currentRole = args.role;
currentClassMode = args.classMode;
currentGroupSdkType = args.sdkTypeGroup;
localStreamId = args.streamId;
teacherId = args.teacherId;
lecturerStreamId = typeof args.lecturerStreamId === 'string' ? args.lecturerStreamId.toUpperCase() : null;
lecturerUid = typeof lecturerStreamId === 'string' ? lecturerStreamId.split('_')[2] : null;
dataReport.setRealSDKType('zego');
await loadZego(args.extensionVersion);
if (args.role === 'teacher' && args.mode === 1) {
//坐立模式开启外部采集器,站立关闭外部采集器,只有小组课坐立模式才会调用,通过这个把rtmp数据给到zego或者rtc(头像),开启了外部采集,zego不会再本地采集数据
await setVideoCaptureFactory(true);
}
// if (args.classMode === 1 || args.classMode === 2){
// //如果是小班课保证关闭外部采集器,小组课上一步打开
// await setVideoCaptureFactory(false);
// };
// 注册回调
addListener(args.userId, args.userName, args.confId, args.nNetType, args.devices, args.role);
//rtc同屏以及高光时刻,都开启音频外部采集,默认传1
if (args.role === 'teacher') {
defaultApi.writeLog('avsdk ZEGO::init_ open setAudioAuxSource');
if (args.classMode === 1 || args.classMode === 2) {
await setAudioAuxSource(1);
}
teacherId = null;
}
if (args.mode === 1 && ((isZegoGroupClass() && (args.role === 'teacher' || args.role === 'assistant')) || (isZegoCombinationClass() && args.role === 'assistant'))) {
//zego推拉RTMP小组课,授课端坐立模式、辅导端坐立模式 或 zego推拉RTMP组合小班辅导端坐立模式,需要将摄像头渲染模式设置为 内部渲染+外部渲染
await startEngine(args.appId, args.appSignature, args.businessType, 5); //5为摄像头内部渲染+外部渲染
} else {
await startEngine(args.appId, args.appSignature, args.businessType); //默认为摄像头外部渲染
}
//小班课 需要加载采集插件的输入数据,为了拼接zego头像
if ((args.classMode === 1 || args.classMode === 2) && args.role === 'teacher') {
screenStreamId = `${window.zby_sdk_init_params.institutionId}_${args.roomId}_${args.userId}_${args.live_id}_1`;
loadCollectionInputEntry();
};
if (args.noiseSuppressMode == -2) {
defaultApi.writeLog(`noiseSuppressMode 关闭噪声消除 ${args.noiseSuppressMod}`);
await setEnableNoiseSuppress(false);
} else if (args.noiseSuppressMode == -1) {
defaultApi.writeLog(`noiseSuppressMode 由底层进行配置,js层不进行配置 ${args.noiseSuppressMode}`);
} else {
defaultApi.writeLog(`noiseSuppressMode 启动噪音消除,模式为: ${args.noiseSuppressMode}`);
await setEnableNoiseSuppress(true);
await setNoiseSuppressMode(+args.noiseSuppressMode);
await enableTransientNoiseSuppress(true);
};
//aecMode为-1是关闭回音消除,云控下传
if (args.aecMode == -2) {
defaultApi.writeLog(`aecMode 关闭回音消除 ${args.aecMode}`);
await setEnableAEC(false);
} else if (args.aecMode == -1) {
defaultApi.writeLog(`aecMode 由底层进行配置,js层不进行配置 ${args.aecMode}`);
} else {
defaultApi.writeLog(`aecMode 启动回音消除,模式为: ${args.aecMode}`);
await setEnableAEC(true);
await setAECMode(+args.aecMode);
};
await setVideoBitrate(args.encodeCaptureBitrate);
// await setAudioBitrate(args.encodeCaptureBitrate);
await setVideoFPS(args.encodeCaptureFps);
await setVideoCaptureResolution(args.previewResolutionWidth, args.previewResolutionHeight);
await setVideoEncodeResolution(args.encodedResolutionWidth, args.encodedResolutionHeight);
if ((isZegoGroupClass() && (args.role === 'teacher' || args.role === 'assistant')) || (isZegoCombinationClass() && args.role === 'assistant')) {
defaultApi.writeLog('zego小组课或辅导端组合小班课,用第二路推RTMP流,在pyzx初始化采集参数');
} else {
await setVideoBitrate(args.screenSameBitrate, 1);
await setVideoFPS(args.screenSameFps, 1);
await setVideoCaptureResolution(args.screenSameResolutionWidth, args.screenSameResolutionHeight, 1);
await setVideoEncodeResolution(args.screenSameResolutionWidth, args.screenSameResolutionHeight, 1);
}
// await setVideoBitrate(500000);
// await setAudioBitrate(500000);
// await setVideoFPS(15);
// await setVideoCaptureResolution(1280, 720);
// await setVideoEncodeResolution(1280, 720);
await setSoundLevelMonitorCycle(500);
startPullingStreamObserver();
if (isInitFinished) {
defaultApi.writeLog(`avsdk ZEGO::init_isInitFinished ${isInitFinished}`);
// await afterInitSDK(args.userId, args.userName, args.roomId, args.nNetType, JSON.parse(JSON.stringify(args.devices)));
isInitFinished = 0;
window.current_sdk_type = 'zego';
defaultApi.writeLog('zego init finished current_sdk_type : zego');
} else {
await new Promise((resolve, reject) => {
let timer = null;
timer = setInterval(() => {
if (isInitFinished) {
isInitFinished = 0;
clearInterval(timer);
resolve();
}
}, 30);
});
window.current_sdk_type = 'zego';
defaultApi.writeLog('zego init finished current_sdk_type : zego');
}
};
/**
* @function 在OnInitSDK事件之后调用的初始化方法
* @param userId:Number 用户 id,必选
* @param userName:String 用户名,必选
* @param roomId:String 频道(房间) id,必选
* @param nNetType:Number 网络类型,可选,默认为 1
* @return Promise
*/
const afterInitSDK = async (userId, userName, confId, nNetType, devices, role, classMode) => {
// 指定默认设备
const usedDevices = window.zbyAVSDK_device_checker_init;
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) || ''
};
defaultApi.writeLog('avsdk ZEGO::afterInitSDK');
await setDefaultDevice(_devices, 'default');
await loginChannel(userId, userName, confId, nNetType);
//设置拉流质量监控周期
await setPlayQualityMoniterCycle(5000);
await setPublishQualityMoniterCycle(5000);
await setMediaSideFlags();
defaultApi.writeLog(`test: isZegoGroupClass, ${isZegoGroupClass()}`);
if ((isZegoGroupClass() && (role === 'teacher' || role === 'assistant')) || (isZegoCombinationClass() && role === 'assistant')) {
defaultApi.writeLog(`avsdk ZEGO::afterInitSDK zegoRtmp, classMode: ${classMode} role: ${role}`);
} else {
// 初始化的时候不打开摄像头
await enableCamera(false);
// 只有调用了这个方法之后才开启关闭摄像头才有效
await startPreview();
}
// 置为 1 表示初始化完成
isInitFinished = 1;
};
const deviceCheckerInitSDK = async () => {
defaultApi.writeLog('avsdk ZEGO::deviceCheckerInitSDK');
await enableCamera(false);
await startPreview();
isDeviceCheckInitFinished = 1;
};
const getDeviceCheckInitStatus = () => {
return isDeviceCheckInitFinished;
};
const setDeviceCheckInitStatus = () => {
isDeviceCheckInitFinished = 0;
};
//设置是否发送sei
const setMediaSideFlags = (bStart = true, bOnlyAudioPublish = false, mediaInfoType = 2, seiSendType = 1, idx = 0) => {
return callMethod('SetMediaSideFlags', { bStart, bOnlyAudioPublish, mediaInfoType, seiSendType, idx });
};
//设置拉流质量监控周期
const setPlayQualityMoniterCycle = (timeInMS) => {
return callMethod('SetPlayQualityMoniterCycle', { timeInMS });
};
//设置推流质量监控周期
const setPublishQualityMoniterCycle = (timeInMS) => {
return callMethod('SetPublishQualityMoniterCycle', { timeInMS });
};
/**
* @function 设置默认的硬件设备,包括摄像头、麦克风以及扬声器
* @return Promise
*/
export const setDefaultDevice = async (devices, operationType) => {
// 设置默认的摄像头
if (devices && devices.camera) {
if (zbyAVSDK_device_checker_init.camera.list.length == 0) {
await getVideoDeviceList();
}
await setVideoDevice({ deviceId: devices.camera, operationType: `${operationType}_1` });
} else {
const cameraData = await getVideoDeviceList();
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 getMicrophoneDeviceList();
}
await setMicrophoneDevice({ deviceId: devices.microphone, operationType: `${operationType}_1` });
} else {
const microPhoneData = await getMicrophoneDeviceList();
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` });
}
}
}
// 设置默认的扬声器
if (devices && devices.speaker) {
await setSpeakerDevice({ deviceId: devices.speaker, operationType: `${operationType}_1` });
} else {
const speakerData = await getSpeakerDeviceList();
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 title:String 直播的名称,可选,默认为 ''
* @param flag:Number 直播属性,可选,默认为 0
* @param seq:Number 请求序号,可选,默认为 0
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const startPublish2 = async (streamid, idx, title, flag, seq) => {
const streamId = streamid || window.zbyAVSDK_init_params.zego.streamId;
if (typeof title === 'undefined') {
title = 'none';
}
if (typeof flag === 'undefined') {
flag = 0;
}
if (typeof seq === 'undefined') {
seq = 0;
}
if (typeof idx === 'undefined') {
idx = 0;
}
await new Promise((resolve, reject) => {
let timer = setInterval(() => {
if (hasLoginChannel) {
clearInterval(timer);
resolve();
}
}, 30);
});
let res = await callMethod('StartPublish2', {
pszStreamID: streamId,
pszTitle: title,
seq,
idx,
flag
});
await startSoundLevelMonitor();
await new Promise((resolve, reject) => {
let timer = setInterval(() => {
if (hasPushFlow) {
clearInterval(timer);
resolve();
}
}, 300);
});
// 心跳开始
if (!isFirstHeartBeatReport) {
isFirstHeartBeatReport = true;
heartBeatDataReport('start');
}
return res;
};
/**
* @function 结束推流
* @param msg:String 自定义信息,server 对接流结束回调包含此字段内容,可选,默认为 'none'
* @param flag:Number 直播属性,可选,默认为 0
* @param idx:Number 推流通道,可选,默认为 0
* @return Promise | void
*/
export const stopPublish = (idx, msg, flag) => {
if (typeof msg === 'undefined') {
msg = 'none';
}
if (typeof flag === 'undefined') {
flag = 0;
}
if (typeof idx === 'undefined') {
idx = 0;
}
// try {
// dataReport.stopPublish({publish_streamid:window.zbyAVSDK_init_params.zego.streamId});
// } catch (error) {}
return callMethod('StopPublish', {
pszMsg: msg,
flag,
idx
});
};
/**
* @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(`ZEGO:: 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(`ZEGO:: The streamId queue does not contain this streamId ${uiChnIndexs}`);
const arr = streamId.split('_');
const [,,userId] = arr;
if (String(userId).toUpperCase() === String(lecturerUid)) {
playChannel = arr.length === 4 ? 0 : 1; // 主流使用0,辅流使用1
} else {
if (uiChnIndexs.length === 0) {
defaultApi.writeLog('error', `ZEGO:: all uiChnIndexs are used ${uiChnIndexs}`);
delete streamIdRtcPlayerInfo[streamId];
NOTICE.overMaxConnect({streamId, message: 'reach max connect'});
return {
videoSrc: '',
playerId: -2
};
}
playChannel = uiChnIndexs.pop();
}
streamIdToPreviewId[streamId] = playChannel;
defaultApi.writeLog(`streamIdIsNoExited ${playChannel} ${uiChnIndexs}`);
} else {
playChannel = streamIdToPreviewId[streamId];
defaultApi.writeLog(`ZEGO:: The streamId queue contains this streamId ${playChannel} ${uiChnIndexs}`);
await stopPlayStream(streamId);
resetStreamIdRtcPlayerInfo(streamId);
}
// 判断是否创建或者获取播放通道成功,不成功就删掉重试
if (playChannel == undefined) {
delete streamIdToPreviewId[streamId];
defaultApi.writeLog('error', 'ZEGO:: uiChnIndex is not exist');
// delete streamIdToBlobUrl[streamId];
return initPullFlow(streamId, domId, mute, pInfo, notAutoPlay, audioOnly);
}
previewIdToStreamId[playChannel] = streamId;
//是否拉取声音
activateAudioPlayStream(streamId, !!!mute);
//是否拉取画面
activateVideoPlayStream(streamId, !!!audioOnly);
if (!notAutoPlay) {
await playStream(streamId, playChannel, pInfo);
}
const videoSrc = await startLocalOrRemotePreview(false, streamId, domId);
return {
videoSrc,
playerId: playChannel
};
};
/**
* @function 恢复拉指定的流
* @param streamId:String 被拉取的流的 id,必选
* @param uiChnIndex:Number 播放通道,可选
* @param pInfo:String 多媒体流附加信息,可选,默认为 'none'
* @return Promise | void
*/
export const playStream = (streamId, uiChnIndex, pInfo) => {
if (typeof uiChnIndex === 'undefined') {
uiChnIndex = streamIdToPreviewId[streamId];
}
if (typeof pInfo === 'undefined') {
pInfo = 'none';
}
defaultApi.writeLog(`avsdk ZEGO::PlayStream streamId: ${streamId} uiChnIndex: ${uiChnIndex}`);
pullingStore.setData(streamId, { info: pInfo });
return callMethod('PlayStream', {
pszStreamID: streamId,
uiChnIndex,
pInfo
});
};
/**
* 判断是否回收通道
* @param {number} id
*/
const isCollectChannel = (id) => {
return typeof id === 'number' &&
!uiChnIndexs.includes(id) &&
id !== 0 && // 主讲主流不回收
id !== 1; // 主讲辅流不回收
};
/**
* @function 停止拉指定的流
* @param streamId:String 被拉取的流的 id,必选
* @param recovery:Boolean 是否回收播放器, 可选
* @return Promise | void
*/
export const stopPlayStream = async (streamId, recovery) => {
const id = streamIdToPreviewId[streamId];
delete streamIdRtcPlayerInfo[streamId];
if (recovery && isCollectChannel(id)) {
uiChnIndexs.push(id);
defaultApi.writeLog(`avsdk ZEGO::stopPlayStream streamId: ${streamId} id: ${id} uiChnIndexs: ${uiChnIndexs}`);
delete streamIdToPreviewId[streamId];
// delete streamIdToBlobUrl[streamId];
}
pullingStore.delete(streamId);
return callMethod('StopPlayStream', {
pszStreamID: streamId
});
};
/**
* @function 停止并回收所有拉流
* @return Promise | void
*/
export const stopAllPlayStream = () => {
const toRecoveryArr = [];
for (let streamId in streamIdToPreviewId) {
if (streamIdToPreviewId.hasOwnProperty(streamId)) {
const id = streamIdToPreviewId[streamId];
if (isCollectChannel(id)) {
uiChnIndexs.push(id);
}
pullingStore.delete(streamId);
toRecoveryArr.push(callMethod('StopPlayStream', {
pszStreamID: streamId
}));
}
}
defaultApi.writeLog(`avsdk ZEGO::stopAllPlayStream uiChnIndexs: ${uiChnIndexs}`);
streamIdToPreviewId = {};
return Promise.all(toRecoveryArr);
};
/**
* @function 切换播放流
* @param streamId:String 需要停止流的 id,必选
* @param toStreamId:String 被拉取的流的 id,必选
* @param toDomId:String <video> 标签的 id,可选
* @return Promise | void
*/
export const changePullFlow = async (streamId, toStreamId, toDomId) => {
await stopPlayStream(streamId, true);
return initPullFlow(toStreamId, toDomId);
};
/**
* @function 转推,录课
* @param type:String 必选,'start' | 'stop'
* @param seq:Number 可选,默认为 1
* @return Promise | void
*/
export const mixStream = (type, args, seq) => {
if (!args) {
const zego = window.zbyAVSDK_init_params.zego;
const streamId = zego.streamId;
const sid = zego.sid;
let pInputStreamList;
let nInputStreamCount;
if (type === 'start') {
pInputStreamList = [{
szStreamID: streamId,
layout: {
top: 0,
right: zego.previewResolutionWidth,
bottom: zego.previewResolutionHeight,
left: 0
},
uSoundLevelID: 0,
nContentControl: 0
}];
nInputStreamCount = 1;
}
if (type === 'stop') {
pInputStreamList = [];
nInputStreamCount = 0;
}
if (typeof seq === 'undefined') {
seq = 1;
}
return callMethod('MixStream', {
config: {
szOutputStream: zego.recordUrl + '/' + sid,
bOutputIsUrl: true,
nOutputFps: zego.previewCaptureFps,
nOutputBitrate: zego.previewCaptureBitrate,
nOutputAudioBitrate: zego.previewCaptureBitrate,
nOutputWidth: zego.previewResolutionWidth,
nOutputHeight: zego.previewResolutionHeight,
nOutputAudioConfig: 1,
pInputStreamList,
nInputStreamCount,
pUserData: '',
nLenOfUserData: 0,
nChannels: 0,
nOutputBackgroundColor: 0,
pOutputBackgroundImage: '',
bWithSoundLevel: false
},
seq
});
} else {
return callMethod('MixStream', args);
}
};
/**
* @function 是否拉取音频流--通过流id控制
* @param streamId:String 被拉取的流的 id,必选
* @param bActive:Boolean true -> 拉取,false -> 不拉取,必选
* @return Promise | void
*/
export const activateAudioPlayStream = (streamId, bActive) => {
streamIdRtcPlayerInfo1[streamId].audio_type = bActive;
return callMethod('ActivateAudioPlayStream', {
nChannelIdx: streamIdToPreviewId[streamId],
bActive
});
};
/**
* @function 是否拉取音频流--通过播放器id控制
* @param operation:Boolean,必选,true -> 拉取,false -> 不拉取,必选
* @param playerId:Number,必选,播放器id
* @return Promise | void
*/
export const pullAudioFlow = (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]
defaultApi.writeLog(`pullAudioFlow-playerId ${playerId}`);
}
}
let streamId = previewIdToStreamId[playerId];
try {
streamIdRtcPlayerInfo1[streamId].audio_type = operation;
defaultApi.writeLog(`pullAudioFlow ${streamId}`);
} catch (e) {
defaultApi.writeLog(`pullAudioFlow ${streamId} --error ${JSON.stringify(e)}`, null, 'error');
}
try {
dataReport.setPullVoice({
code: +operation,
pull_uid: util.getUidByStreamId(streamId),
pull_streamid: streamId,
playerId
// operator:'client'
});
} catch (e) { };
return callMethod('ActivateAudioPlayStream', {
bActive: operation,
nChannelIdx: playerId
});
};
/**
* @function 是否拉取画面流
* @param streamId:String 被拉取的流的 id,必选
* @param bActive:Boolean true -> 拉取,false -> 不拉取,必选
* @return Promise | void
*/
export const activateVideoPlayStream = (streamId, bActive) => {
try {
streamIdRtcPlayerInfo1[streamId].video_type = bActive;
defaultApi.writeLog(`activateVideoPlayStream ${streamId}`);
} catch (e) {
defaultApi.writeLog(`activateVideoPlayStream ${streamId} --error ${JSON.stringify(e)}`, null, 'error');
}
return callMethod('ActivateVideoPlayStream', {
nChannelIdx: streamIdToPreviewId[streamId],
bActive
});
};
/**
* @function 开启/关闭麦克风音量回调
* @param open:Boolean true -> 开启,false -> 关闭,必选
* @return void
*/
export const openMicVolumeCb = (open) => {
// if (open) {
// runMicVolumeCb = true;
// } else {
// runMicVolumeCb = false;
// }
};
/**
* @function 监听设备的音量变化
* @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
* @param deviceId:String 音频设备 id,必选
* @return Promise | void
*/
export const setAudioVolumeNotify = (deviceType, deviceId) => {
return callMethod('SetAudioVolumeNotify', {
deviceType,
deviceId
});
};
/**
* @function 关闭监听设备的音量变化
* @param deviceType:Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
* @param deviceId:String 音频设备 id,必选
* @return Promise | void
*/
export const stopAudioVolumeNotify = (deviceType, deviceId) => {
return callMethod('StopAudioVolumeNotify', {
deviceType,
deviceId
});
};
/**
* @function 启动 soundLevel 监听
* StartSoundLevelMonitor 后一定要 StopSoundLevelMonitor
* @return Promise | void
*/
export const startSoundLevelMonitor = () => {
return callMethod('StartSoundLevelMonitor', {});
};
/**
* @function 停止 soundLevel 监听
* @return Promise | void
*/
export const stopSoundLevelMonitor = () => {
return callMethod('StopSoundLevelMonitor', {});
};
/**
* @function 设置 soundLevel 的监控周期
* @param timeInMS:Number 时间周期,单位为毫秒,取值范围 [100, 3000],必选
* @return Promise | void
*/
export const setSoundLevelMonitorCycle = (timeInMS) => {
return callMethod('SetSoundLevelMonitorCycle', {
timeInMS
});
};
/**
* @function 单路录制
* @param type:String start/stop,必选
* @param szOutputStream:转推地址(支持rtmp/avertp),必选
* @param szStreamID:推流ID,必选
* @return Promise | void
*/
/*export const singleRecord = async (type, szOutputStream, szStreamID, seq = 1) => {
defaultApi.writeLog(`avsdk ZEGO::singleRecord szStreamID: ${szStreamID} szOutputStream: ${szOutputStream} type: ${type}`);
const zego = window.zbyAVSDK_init_params.zego;
let pInputStreamList = [];
let nInputStreamCount = 0;
if (type === 'start') {
pInputStreamList = [{
szStreamID,
layout: {
top: 0,
right: zego.previewResolutionWidth,
bottom: zego.previewResolutionHeight,
left: 0
},
uSoundLevelID: 0,
nContentControl: 0
}];
nInputStreamCount = 1;
}
return callMethod('MixStream', {
config: {
szOutputStream,
bOutputIsUrl: true,
nOutputFps: zego.previewCaptureFps,
nOutputBitrate: zego.previewCaptureBitrate,
nOutputAudioBitrate: zego.previewCaptureBitrate,
nOutputWidth: zego.previewResolutionWidth,
nOutputHeight: zego.previewResolutionHeight,
nOutputAudioConfig: 1,
pInputStreamList,
nInputStreamCount,
pUserData: '',
nLenOfUserData: 0,
nChannels: 0,
nOutputBackgroundColor: 0,
pOutputBackgroundImage: '',
bWithSoundLevel: false
},
seq
});
};
*/
export const hasStream = streamId => {
return Object.keys(streamIdToPreviewId).includes(streamId);
};
/**
* @function 获取当前视频流的通道
* @param streamId 流 id
* @return channelIndex
*/
export const getChannelIndex = (streamId) => {
defaultApi.writeLog(`ZEGO : getChannelIndex streamId: ${streamId} ${JSON.stringify(streamIdToPreviewId)}`);
return streamIdToPreviewId[streamId];
};
/**
* @function 获取当前视频流所有的通道
* @param streamId 流 id
* @return channelIndex
*/
export const getAllChannelIndex = () => {
defaultApi.writeLog(`ZEGO : getAllChannelIndex ${JSON.stringify(streamIdToPreviewId)}`);
return streamIdToPreviewId;
};
/**
* @function 录制转推对外接口
* @param type:String start/stop,必选
* @param szOutputStream:转推地址(支持rtmp/avertp),必选
* @param szStreamID:推流ID,必选
* @return Promise | void
*/
export const singleRecord = async (type, szOutputStream, szStreamID) => {
defaultApi.writeLog(`avsdk ZEGO::recordPublishTarget szStreamID: ${szStreamID} szOutputStream: ${szOutputStream} type: ${type}`);
const zego = window.zbyAVSDK_init_params.zego;
if (!szOutputStream) {
const sid = zego.sid;
// 拼接转推地址
szOutputStream = zego.recordUrl + '/' + sid;
}
if (!szStreamID) {
szStreamID = window.zbyAVSDK_init_params.zego.streamId;
}
if (type === 'start') {
return addPublishTarget(szOutputStream, szStreamID);
}
if (type === 'stop') {
return deletePublishTarget(szOutputStream, szStreamID);
}
};
/**
* @function 开始转推
* @param szTarget:转推地址(支持rtmp/avertp),必选
* @param szStreamID:推流ID,必选
* @return Promise | void
*/
export const addPublishTarget = async (szTarget, szStreamID) => {
defaultApi.writeLog(`avsdk ZEGO::addPublishTarget szStreamID: ${szStreamID} szTarget: ${szTarget}`);
let res = await callMethod('AddPublishTarget', {
strTarget: szTarget,
pszStreamID: szStreamID,
});
try {
dataReport.turnPushResult({
errorcode: res == 0 ? 0 : 1,
case: 'turn push',
streamid: szStreamID
});
} catch (e) { };
return res;
};
/**
* @function 结束转推
* @param szTarget:转推地址(支持rtmp/avertp),必选
* @param szStreamID:推流ID,必选
* @param seqNum
* @return Promise | void
*/
export const deletePublishTarget = async (szTarget, szStreamID) => {
defaultApi.writeLog(`avsdk ZEGO::deletePublishTarget szStreamID: ${szStreamID} szTarget: ${szTarget}`);
return callMethod('DeletePublishTarget', {
strTarget: szTarget,
pszStreamID: szStreamID
});
};
/**
* @function 开始本地视频录制
* @param enableRecord 开始/结束
* @param storagePath 录制路径
* @return Promise | void
*/
export const localRecordVideo = async (enableRecord, storagePath) => {
defaultApi.writeLog(`avsdk ZEGO::localRecordVideo enableRecord: ${enableRecord}, storagePath: ${storagePath}`);
if (enableRecord) {
return callMethod('StartRecord', {
channelIndex: 0,
recordType: 3,
enableStatusCallback: true,
interval: 3000,
recordFormat: 2,
isFragment: false,
storagePath
});
} else {
return callMethod('StopRecord', {
channelIndex: 0
});
}
};
/**
* @function 本地音频录制
* @param enableRecord 开始/结束
* @param storagePath 录制路径
* @return Promise | void
*/
export const localRecordAudio = async (enableRecord, storagePath) => {
defaultApi.writeLog(`avsdk ZEGO::localRecordAudio enableRecord: ${enableRecord}, storagePath: ${storagePath}`);
return callMethod('EnableSelectedAudioRecord', {
uMask: 1,
nSampleRate: 16000,
nChannels: 1,
enableRecord,
storagePath,
});
};
export const setRecordPath = async (idx = 0, path) => {
defaultApi.writeLog(`avsdk ZEGO::setRecordPath idx: ${idx}, path: ${path}`);
return callMethod('SetRecordPath', { idx, path });
};
export const getRecordPath = async (idx = 0) => {
defaultApi.writeLog(`avsdk ZEGO::getRecordPath idx: ${idx}`);
return callMethod('GetRecordPath', { idx });
};
export const getDesktopPath = () => {
defaultApi.writeLog('avsdk ZEGO::getDesktopPath');
return callMethod('GetDesktopPath', {});
};
/**
* @function 本地录制
* @param {string | undefined} fileName
* @param {number} fileType 1:flv 2:mp4
* @param {number} idx 0:主流 1:辅流 2:RTMP流
* @returns {Promise<any>}
*/
export const startCaptureRecord = async (fileName, fileType, idx, path) => {
defaultApi.writeLog(`avsdk ZEGO::startCaptureRecord fileName: ${fileName}, fileType: ${fileType}, idx: ${idx}, path: ${path}`);
await setRecordPath(idx, path);
await startRecord(fileName, fileType, idx);
await startCapture(idx);
};
export const stopCaptureRecord = async (idx) => {
defaultApi.writeLog(`avsdk ZEGO::stopCaptureRecord idx: ${idx}`);
await stopCapture(idx);
await stopRecord(idx);
};
/**
* @function 本地录制
* @param {string | undefined} fileName
* @param {number} fileType 1:flv 2:mp4
* @param {number} idx 0:主流 1:辅流 2:RTMP流
* @returns {Promise<any>}
*/
const startRecord = async (fileName, fileType, idx) => {
defaultApi.writeLog(`avsdk ZEGO::startRecord fileName: ${fileName}, fileType: ${fileType}, idx: ${idx}`);
if (idx === 1 && !screenStreamId) {
defaultApi.writeLog('avsdk ZEGO::startRecord screenStreamId is null', null, 'error');
return;
}
if (!fileName) {
const fileNameMap = [localStreamId, screenStreamId, 'rtmp'];
fileName = `${fileNameMap[idx]}-${util.currentTimeString().replace(/:/g, '-')}`;
}
dataReport.startRecord({ fileName, fileType, idx });
return callMethod('StartRecord2', {
idx,
recordType: 3, // 1:音频 2:视频 3:音视频
fileName,
recordFormat: fileType,
});
};
/**
* 结束音视频录制 channelIndex(number) 录制通道 0主推流通道/ 1辅助推流通道/ 2辅助推流通道
* @param {number} idx 0 | 1 | 2
* @returns
*/
const stopRecord = async (idx) => {
defaultApi.writeLog(`avsdk ZEGO::stopRecord idx: ${idx}`);
dataReport.stopRecord({ idx });
return callMethod('StopRecord', { idx });
};
/**
* 开启外部音频采集(通知sdk)推流前,调用此接口,publish_channel,number类型,传2代表第三路流
* @param {number} idx 0 | 1 | 2
* @returns
*/
const startCapture = async (idx) => {
defaultApi.writeLog(`avsdk ZEGO::startCapture idx: ${idx}`);
return callMethod('StartCapture', { publish_channel: idx });
};
/**
* 结束外部音频采集(通知sdk)
* @param {number} idx 0 | 1 | 2
* @returns
*/
const stopCapture = async (idx) => {
defaultApi.writeLog(`avsdk ZEGO::stopCapture idx: ${idx}`);
return callMethod('StopCapture', { publish_channel: idx });
};
/**
* @function 设置外部采集设备模块
* @param bUseVideoCapture 是否开启外部采集设备模块
* @attention 必须在 InitSDK 前调用,置空必须在UninitSDK之后
* @return Promise | void
*/
const setVideoCaptureFactory = (bUseVideoCapture = false, streamChannel) => {
defaultApi.writeLog('info', 'avsdk ZEGO::setVideoCaptureFactory');
return callMethod('SetVideoCaptureFactory', {
bUseVideoCapture,
streamChannel
});
};
//设置音频数据来源
const setAudioAuxSource = (source) => {
defaultApi.writeLog('info', 'avsdk ZEGO::setAudioAuxSource');
return callMethod('SetAudioAuxSource', {
source
});
};
const setVideoSource = (idx, srcType) => {
return callMethod('SetVideoSource', { idx, srcType });
};
//RTC同屏 辅助通道音频类型 0:静音 / 1:麦克风 / 2:拉流的声音 / 3:1+2 / 4:麦克风+扬声器 / 5:扬声器(不含拉流)
const setAUXAudioType = (type) => {
defaultApi.writeLog('info', 'avsdk ZEGO::SetAUXAudioType');
return callMethod('SetAUXAudioType', {
type
});
};
//录制通道音频类型 0:静音 / 1:麦克风 / 2:拉流的声音 / 4:1+2 / 8:麦克风+扬声器(不含拉流)
const setThirdAudioType = (type) => {
defaultApi.writeLog('info', 'avsdk ZEGO::SetThirdAudioType');
return callMethod('SetThirdAudioType', {
type
});
};
//开启音频外部采集 参数:channel 参数类型:int 通道(传1使用辅通道)
const startAudioExCapture = (channel) => {
defaultApi.writeLog('info', 'avsdk ZEGO::startAudioExCapture');
return callMethod('StartAudioExCapture', {
channel
});
};
/**
* 第四路流类型设置
* @param {number} type 6: 视频:主流 音频:麦克风+扬声器 7: 外部音频源,外部视频源,数据来源player
* @returns
*/
const setFourthStreamType = (type) => {
return callMethod('SetFourthStreamType', {
type
});
};
//加载截屏插件
const LoadCollectionOutputEntry2 = () => {
defaultApi.writeLog('info', 'avsdk ZEGO::LoadCollectionOutputEntry2');
return callMethod('LoadCollectionOutputEntry2', {});
};
//开启同屏
const startMultiScreen = async () => {
defaultApi.writeLog('info', 'avsdk ZEGO::startMultiScreen');
// await setVideoCaptureFactory(true,1);
await LoadCollectionOutputEntry2();
};
/**
* @function 设置推流时是否发送视频数据
* @param mute true不发送(仅预览),false 发送
* @param idx 推流通道索引 0表示主推流通道,默认;1表示第二路流推流通道,无法推出声音
* @return Promise | void (0代表设置成功,否则设置失败)
*/
const muteVideoPublish = (mute = true, idx = 0) => {
return callMethod('MuteVideoPublish', {
mute,
idx
});
};
/**
* @function 开始连麦
* @param mode:number 采集器默认, 默认0=站立 1=坐立
* @return Promise | void
*/
export const teacherStartLinkMic = async (mode = 0) => {
if (mode === 0) {
await muteVideoPublish(true, 0);
await setMicrophoneDevice({});
await enableMic(true);
} else {
//开始连麦后,需要打开摄像头(开启外部采集器后,不会争抢摄像头),否则学生拉到的小班流会没有画面
await enableCamera(true);
await enableMic(true);
}
};
/**
* @function 结束连麦
* @param mode:number 采集器默认, 默认0=站立 1=坐立
* @return Promise | void
*/
export const teacherStopLinkMic = async (mode = 0) => {
// if (mode === 0) {
// await muteVideoPublish(false,0);
// } else {
// await setVideoCaptureFactory(false);
// }
};
/**
* @function 设置镜像状态(预览)
* @param { boolean } type true=开启 false=关闭
* @return: Promise
*/
export const setMirrorStatus = (isMirror) => {
// return callMethod('SetPreViewMirror', { isMirror });
return setVideoMirrorMode(isMirror >> 0);
};
/**
* @function 设置拉流镜像状态
* @param { boolean } type true=开启 false=关闭
* @return: Promise
*/
export const setPlayViewMirror = (isMirror) => {
return callMethod('SetPlayViewMirror', { isMirror });
};
/**
* @function 设置推流镜像状态
* @param { boolean } id 采集器Id
* @param { 0 | 1 | 2 | 3 } mode 0: 预览启用镜像,推流不启用镜像; 1: 预览启用镜像,推流启用镜像; 2: 预览不启用镜像,推流不启用镜像; 3: 预览不启用镜像,推流启用镜像
* @return: Promise
*/
export const setVideoMirrorMode = (mode, idx = 0) => {
let realId = mode == 0 ? 2 : 1;
defaultApi.writeLog(`ZEGO: setVideoMirrorMode, mode: ${mode}, idx:${idx}, realId:${realId}`);
return callMethod('SetVideoMirrorMode', { idx, mode: realId });
};
//美颜相关
/**
* @deprecated
* @function 开启美颜
* @return: Promise
*/
export const startSenseMe = async () => {
return callMethod('StartSenseMe', {});
};
/**
* @deprecated
* @function 设置美颜参数
* * @param { number } type //美颜类型,1、红润强度,3、磨皮强度,4、美白强度,5、大眼,6、瘦脸,7、小脸,8、对比度强度,9、饱和度强度
* * @param { number } value //美颜数值,有效值范围0~100
* @return: Promise
*/
export const setBeautifyParam = async (type, value) => {
return callMethod('SetBeautifyParam', { type, value });
};
/**
* @deprecated
* @function 关闭美颜
* @return: Promise
*/
export const endSenseMe = async () => {
return callMethod('EndSenseMe', {});
};
export const enableBeautify = (enable, streamId = localStreamId, idx = 0) => {
defaultApi.writeLog(`avsdk ZEGO::enableBeautify enable: ${enable} idx: ${idx}`);
return callMethod('EnableBeautify', { enable, streamId, idx });
};
/**
*
* @param {number} mode 美颜类型 101:美白、103:磨皮、201:瘦脸、605:背景虚化、901:自定义背景、202:大眼、603:锐化
* @param {number} subMode 美白模式【0,2,3】,当前选项:0,
磨皮:【1,2】,当前选项:1
瘦脸:【0】,
背景虚化:【0,1】、当前选项:1
自定义背景:【0】、
大眼:【0】
锐化:【0】
* @param {number} value 美白: 默认30 ,0表示不美白;
磨皮:默认 74, 0表示不磨皮;
瘦脸:默认 11, 0表示不瘦脸
背景虚化:默认值 0, 0表示不虚化
自定义背景:默认值0;非0表示设置背景(当前设计不支持用户自定义背景,)
大眼:默认值13,0表示不做大眼
锐化:默认值0,0表示不锐化
* @param {string} streamId 流id
* @param {string | undefined} image 自定义背景图片地址
* @returns
*/
export const setBeautifyParam2 = (mode, subMode, value, streamId = localStreamId, idx = 0, image) => {
defaultApi.writeLog(`avsdk ZEGO::setBeautifyParam param: ${JSON.stringify({mode, subMode, value, streamId, idx, image})}`);
return callMethod('SetBeautifyParam', { mode, subMode, value, streamId, idx, image });
};
/**
*
* @param {'UseLoopbackDefault' | 'UseDefaultDevice'} key
* @param {'enable'} value
* @returns
*/
export const setEngineConfig = (key, value) => {
defaultApi.writeLog(`avsdk ZEGO::setEngineConfig key: ${key}, value: ${value}`);
return callMethod('SetEngineConfig', { key, value });
};
/**
* @function zego40路拉流混音接口
* * @param { number } mixMode //混流模式 0=关闭混流 1=开启混流
* * @param { array } streamIdArr //表示要突出声音的channel
* * @param { number } mixNum //表示要突出channel的个数, 需要与参数'channels'的数组大小相同
* @return: Promise
*/
export const setAudioMixMode = async (mixMode, streamIdArr) => {
let channelsArr = [];
if (streamIdArr.length > 0) {
streamIdArr.forEach(item => {
channelsArr.push(getChannelIndex(item));
});
}
return callMethod('SetAudioMixMode', { mixMode: mixMode, channels: channelsArr, num: channelsArr.length });
};
// 获取TalMedia.dll版本号
export const getSDKVersion = async () => {
return callMethod('GetSDKVersion', {}).then((e) => {
return JSON.parse(e.msg).ret;
});
};
/**
* @function 获取摄像头所支持的分辨率
* @param deviceId:String 设备id
* @return Promise | void
*/
export const getCameraSupportedRes = (deviceId) => {
return callMethod('GetCameraSupportedResolution', {
deviceId
});
};
/**
* @function 发送SEI
* @param inData: String 需要传输的音视频次要信息数据,外部输入。
* @param dataLen: Number 数据长度,不能超过 4096 bytes
* @param bPacket: Bool 是否采用外部打包好的包头,填写 false。
* @param idx:Number 推流通道 index
* 调用频率不能超过帧率,假设推流采用默认帧率15 fps,即调用频率不能超过 1000/15=66.7 ms/次。
*/
export const sendMediaSideInfo = (info) => {
return callMethod('SendMediaSideInfo', {
inData: info,
dataLen: info.length,
bPacket: false,
idx: 0
});
};
//判断当前课型是否为zego推拉RTMP小组课
export const isZegoGroupClass = () => {
defaultApi.writeLog(`test: isZegoGroupClass--currentClassMode--currentGroupSdkType', ${currentClassMode}, ${currentGroupSdkType}`);
return currentClassMode == 0 && currentGroupSdkType == 'zego'/*rtmp流为zego*/;
};
//判断当前课型是否为zego推拉RTMP组合小班课
export const isZegoCombinationClass = () => {
console.log('test: isZegoCombinationClass--currentClassMode--currentGroupSdkType', currentClassMode, currentGroupSdkType);
return currentClassMode == 2 && currentGroupSdkType == 'zego'/*rtmp流为zego*/;
};
//判断推流类型是否为音频流
export const isStreamAudioMuted = () => {
return callMethod('IsStreamAudioMuted', {});
};
//判断推流类型是否为视频流
export const isStreamVideoMuted = () => {
return callMethod('IsStreamVideoMuted', {});
};
/**
* 获取麦克风响度
* @returns {Promise<number>}
*/
export const getMicrophoneLoudness = () => {
return callMethod('GetMicrophonLoudness', {});
};
/**
* 获取默认扬声器id
* @returns {Promise<string | null>}
*/
export const getCurrentSpeakerId = () => {
return callMethod('GetCurrentSpeakerId', {})
.then((e) => {
if (e.code === 0) {
return JSON.parse(e.msg).speakerCurId;
} else {
return null;
}
});
};
/**
* 获取默认麦克风id
* @returns {Promise<string | null>}
*/
export const getCurrentMicrophoneId = () => {
return callMethod('GetCurrentMicrophoneId', {})
.then((e) => {
if (e.code === 0) {
return JSON.parse(e.msg).microphoneCurId;
} else {
return null;
}
});
};
/**
* 获取默认摄像头id
* @returns {Promise<string | null>}
*/
export const getCurrentCameraId = () => {
return callMethod('GetCurrentCameraId', {})
.then((e) => {
if (e.code === 0) {
return JSON.parse(e.msg).cameraCurId;
} else {
return null;
}
});
};
export const takeLocalSnapShot = (streamId = localStreamId, idx = 0) => {
return callMethod('takeLocalSnapShot', {streamId, idx});
};
export const takeRemoteSnapShot = (streamId) => {
return callMethod('takeRemoteSnapShot', {streamId});
};
export const getStreamType = async () => {
try {
const pushAudio = JSON.parse((await isStreamAudioMuted()).msg).ret;
const pushVideo = JSON.parse((await isStreamVideoMuted()).msg).ret;
let stream_type = 'both';
if (pushAudio && !pushVideo) {
stream_type = 'video';
}
if (!pushAudio && pushVideo) {
stream_type = 'audio';
}
if (pushAudio && pushVideo) {
stream_type = 'none';
}
return stream_type;
} catch (e) {
let stream_type = 'none';
return stream_type;
}
};
const heartBeatRealKeys = ['video_fps', 'video_bitrate', 'audio_fps', 'audio_bitrate'];
const _heartBeatDataReport = () => {
const pullInfo = [];
Object.keys(streamIdRtcPlayerInfo).forEach(streamid => {
heartBeatRealKeys.forEach(realKey => {
if (!streamIdRtcPlayerInfo[streamid].hasOwnProperty(realKey)) {
streamIdRtcPlayerInfo[streamid][realKey] = [];
}
});
//获取拉流类型,后期可写为函数提出去
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';
}
pullInfo.push({
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 {
//获取推流类型,后期可写为函数提出去
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';
};
defaultApi.writeLog(`push_type_zego,camera: ${zbysdk.deviceStatus.camera},microphone: ${zbysdk.deviceStatus.microphone},type: ${zbysdk.deviceStatus.stream_type},a_fps: ${dataZegoCapture.afps}, a_bit: ${dataZegoCapture.akbps}, v_fps: ${dataZegoCapture.fps}, v_bit: ${dataZegoCapture.kbps}`);
dataReport.heartbeat({
...{ ...heartBeatDataReportObj, push_type: zbysdk.deviceStatus.stream_type, volume: heartBeatDataReportObj.volume.slice(0, heartBeatDataReportObj.volume.length - 1) },
pull_info: pullInfo,
});
} catch (e) {
console.log(e);
}
}
resetHeartBeatDataReportObj();
};
let pointer = 0;
const runHeartBeatCalc = () => {
Object.keys(streamIdToPreviewId).forEach((streamId) => {
const data = statsStore.getData(streamId) || {};
heartBeatDataReportCalc('OnPlayQulityUpdate', data, streamId);
});
const data = statsStore.getData(localStreamId) || {};
heartBeatDataReportCalc('OnPublishQualityUpdate', data, localStreamId);
++pointer;
if (pointer === heartBeatItemCount) {
pointer = 0;
_heartBeatDataReport();
}
};
const heartBeatDataReport = (type) => {
try {
if (type === 'start' && !heartBeatDataReportTimer) {
runHeartBeatCalc();
heartBeatDataReportTimer = setInterval(() => {
runHeartBeatCalc();
}, 2 * 1000);
}
if (type === 'stop') {
statsStore.clear();
clearInterval(heartBeatDataReportTimer);
heartBeatDataReportTimer = null;
}
} catch (error) {
console.error(error);
}
};
const heartBeatDataReportCalc = (name, _d, streamid) => {
const ctime = Math.round((new Date().getTime()+dataReport.timestamp)/1000);
// 拉流
const pullKeys = ['fps', 'kbps', 'afps', 'akbps'];
if (name === 'OnPlayQulityUpdate') {
if (streamIdRtcPlayerInfo && streamIdRtcPlayerInfo.hasOwnProperty(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(_d[pullKeys[index]])));
}
});
if (isReport) {
let audio_fps_zego_pull = 0;
let audio_bitrate_zego_pull = 0;
let video_fps_zego_pull = 0;
let video_bitrate_zego_pull = 0;
let pull_type = 'none';
if (streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
audio_fps_zego_pull = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_zego_pull = parseFloat(parseInt(_d.akbps || 0));
video_fps_zego_pull = parseFloat(parseInt(_d.fps || 0));
video_bitrate_zego_pull = parseFloat(parseInt(_d.kbps || 0));
pull_type = 'both';
} else if (!streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
video_fps_zego_pull = parseFloat(parseInt(_d.fps || 0));
video_bitrate_zego_pull = parseFloat(parseInt(_d.kbps || 0));
pull_type = 'video';
} else if (streamIdRtcPlayerInfo1[streamid].audio_type && !streamIdRtcPlayerInfo1[streamid].video_type) {
audio_fps_zego_pull = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_zego_pull = parseFloat(parseInt(_d.akbps || 0));
pull_type = 'audio';
}
streamIdRtcPlayerInfo[streamid].audio_fps[pointer] = audio_fps_zego_pull;
streamIdRtcPlayerInfo[streamid].audio_bitrate[pointer] = audio_bitrate_zego_pull;
streamIdRtcPlayerInfo[streamid].video_fps[pointer] = video_fps_zego_pull;
streamIdRtcPlayerInfo[streamid].video_bitrate[pointer] = video_bitrate_zego_pull;
streamIdRtcPlayerInfo[streamid].ctime[pointer] = ctime;
streamIdRtcPlayerInfo[streamid].avtimestampdiff[pointer] = _d.avTimestampDiff || 0;
streamIdRtcPlayerInfo[streamid].pull_loss[pointer] = _d.pktLostRate || 0;
streamIdRtcPlayerInfo[streamid].pull_delay[pointer] = _d.rtt || 0;
streamIdRtcPlayerInfo[streamid].pull_type_ex[pointer] = pull_type;
}
}
}
// 推流
const pushKeys = ['fps', 'kbps', 'afps', 'akbps'];
if (name === 'OnPublishQualityUpdate') {
// 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_zego_push = 0;
let audio_bitrate_zego_push = 0;
let video_fps_zego_push = 0;
let video_bitrate_zego_push = 0;
let push_type = 'none';
dataZegoCapture = _d;
if (userAndDeviceStatusInfo.isPushingFlow) {
if (zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
audio_fps_zego_push = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_zego_push = parseFloat(parseInt(_d.akbps || 0));
video_fps_zego_push = parseFloat(parseInt(_d.fps || 0));
video_bitrate_zego_push = parseFloat(parseInt(_d.kbps || 0));
push_type = 'both';
} else if (!zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
audio_fps_zego_push = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_zego_push = parseFloat(parseInt(_d.akbps || 0));
push_type = 'audio';
} else if (zbysdk.deviceStatus.camera && !zbysdk.deviceStatus.microphone) {
video_fps_zego_push = parseFloat(parseInt(_d.fps || 0));
video_bitrate_zego_push = parseFloat(parseInt(_d.kbps || 0));
push_type = 'video';
}
}
heartBeatDataReportObj.audio_fps[pointer] = audio_fps_zego_push;
heartBeatDataReportObj.audio_bitrate[pointer] = audio_bitrate_zego_push;
heartBeatDataReportObj.video_fps[pointer] = video_fps_zego_push;
heartBeatDataReportObj.video_bitrate[pointer] = video_bitrate_zego_push;
heartBeatDataReportObj.push_loss[pointer] = _d.pktLostRate || 0;
heartBeatDataReportObj.push_delay[pointer] = _d.rtt || 0;
heartBeatDataReportObj.push_type_ex[pointer] = push_type;
heartBeatDataReportObj.type_backend[pointer] = getBackgroundStatus();
heartBeatDataReportObj.ctime[pointer] = ctime;
}
};
//推流字段
const resetHeartBeatDataReportObj = () => {
heartBeatDataReportObj = {
ctime: new Array(heartBeatItemCount).fill(0),
push_type: 'none',
video_fps: new Array(heartBeatItemCount).fill(0),
video_bitrate: new Array(heartBeatItemCount).fill(0),
audio_fps: new Array(heartBeatItemCount).fill(0),
audio_bitrate: new Array(heartBeatItemCount).fill(0),
push_loss: new Array(heartBeatItemCount).fill(0),
push_delay: new Array(heartBeatItemCount).fill(0),
push_type_ex: new Array(heartBeatItemCount).fill('none'),
type_backend: new Array(heartBeatItemCount).fill(0),
volume: ''
};
};
//记录拉流类型,用作数据上报
const resetStreamIdRtcPlayerInfo1 = (streamId) => {
streamIdRtcPlayerInfo1[streamId] = {
audio_type: false,
video_type: false,
stream_type: 'none'
};
};
resetHeartBeatDataReportObj();
//拉流字段
const resetStreamIdRtcPlayerInfo = (streamId) => {
streamIdRtcPlayerInfo[streamId] = {
pull_uid: util.getUidByStreamId(streamId),
streamid: streamId,
ctime: new Array(heartBeatItemCount).fill(0),
pull_type: streamIdRtcPlayerInfo1[streamId].stream_type,
volume: '',
video_fps: new Array(heartBeatItemCount).fill(0),
video_bitrate: new Array(heartBeatItemCount).fill(0),
audio_fps: new Array(heartBeatItemCount).fill(0),
audio_bitrate: new Array(heartBeatItemCount).fill(0),
pull_loss: new Array(heartBeatItemCount).fill(0),
pull_delay: new Array(heartBeatItemCount).fill(0),
pull_type_ex: new Array(heartBeatItemCount).fill('none'),
//音画不同步字段
avtimestampdiff: new Array(heartBeatItemCount).fill(0),
// 累加
audio_ifg: 0,
video_ifg: 0
};
};
export default {
loadZego,
unloadZego,
startEngine,
destroyEngine,
loginChannel,
logoutChannel,
setDefaultDevice,
getVideoDeviceList,
setVideoDevice,
enableCamera,
enableMic,
setAudioSpeakerCapture,
getAudioDeviceList,
setAudioDevice,
getSpeakerDeviceList,
setSpeakerDevice,
getCurrentSpeakerDevice,
getSpeakerDeviceVolume,
setSpeakerDeviceVolume,
setSpeakerDeviceMute,
getMicrophoneDeviceList,
setMicrophoneDevice,
getMicDeviceVolume,
setMicDeviceVolume,
setMicDeviceMute,
getSpeakerSimpleVolume,
setSpeakerSimpleVolume,
setSpeakerSimpleMute,
setPreviewView,
startPreview,
startLocalOrRemotePreview,
setVideoFPS,
setVideoBitrate,
setAudioBitrate,
setVideoCaptureResolution,
setCameraParam,
setVideoEncodeResolution,
init,
startPublish2,
stopPublish,
initPullFlow,
playStream,
stopPlayStream,
stopAllPlayStream,
changePullFlow,
mixStream,
leaveRoomWithChangeSDK,
leaveRoom,
isZegoGroupClass,
isZegoCombinationClass,
activateAudioPlayStream,
pullAudioFlow,
activateVideoPlayStream,
setAudioVolumeNotify,
stopAudioVolumeNotify,
openMicVolumeCb,
startSoundLevelMonitor,
stopSoundLevelMonitor,
setSoundLevelMonitorCycle,
singleRecord,
hasStream,
getChannelIndex,
addPublishTarget,
deletePublishTarget,
localRecordVideo,
localRecordAudio,
startCaptureRecord,
stopCaptureRecord,
setMirrorStatus,
getStreamType,
startSenseMe,
setBeautifyParam,
endSenseMe,
enableBeautify,
setBeautifyParam2,
setEngineConfig,
setPlayViewMirror,
setMuteStreamAudio,
setMuteStreamVideo,
teacherStartLinkMic,
teacherStopLinkMic,
getSDKVersion,
getCameraSupportedRes,
setAudioMixMode,
setVideoMirrorMode,
startMultiScreen,
getAllChannelIndex,
startAudioExCapture,
setFourthStreamType,
sendMediaSideInfo,
setVideoSource,
setAUXAudioType,
setThirdAudioType,
loadCollectionInputEntry2,
stopCheckDevice,
addListener,
getDeviceCheckInitStatus,
setDeviceCheckInitStatus,
setRecordPath,
getRecordPath,
getDesktopPath,
getMicrophoneLoudness,
getCurrentSpeakerId,
getCurrentMicrophoneId,
getCurrentCameraId,
takeLocalSnapShot,
takeRemoteSnapShot,
};