zby-live-sdk
Version:
This is a live SDK for weclassroom.
3,094 lines • 105 kB
JavaScript
import dataReport from '../network/dataReport.js';
import defaultApi from '../default';
import NOTICE from '../notice';
import zbysdk from '../zby-live-sdk.js';
import { deviceListReport } from './device.js';
import util from '../util/util';
import globalConfig, { getAppIdByInstitutionId, talrtcExt } from '../config/config.js';
import { DataStore, userAndDeviceStatusInfo, getBackgroundStatus } from '../util/statsStore.js';
import { getRTCToken } from '../network/api.js';
// 扩展标识
const extensionId = 'talrtc_ext';
// 端提供的 API 入口
const EM = window.EM;
// 是否执行过 TALRTC 的 AddListener 的标志
let hasAddListener = false;
// 监听id
let EMListenerId = 0;
// rtc小班课=0;rtc大班课=1
const classType = 0;
// 0: 小组课 1: 小班课 2: 新小班课
let classMode = 0;
// 心跳
let heartBeatDataReportObj = {};
let isFirstHeartBeatReport = false;
// 维护的一份拉流的 streamId 与本地预览通道的映射表
let streamIdToPreviewId = {};
let previewIdToStreamId = {};
let heartBeatDataReportTimer = null;
let streamIdRtcPlayerInfo = {};
let streamIdRtcPlayerInfo1 = {};
// 拉流的播放通道初始化从 0 开始依次递增至44(共45路流),-1 为推流的本地视频预览
const chnTotal = 50;
const uiChnIndexs = new Array(chnTotal).fill(0).map((a, b) => b).reverse();
// 维护的一份当前正在使用的音频设备的 deviceId 的列表
let usingAudioDeviceId = {
speaker: '',
microphone: ''
};
let localStreamId = null;
let screenStreamId = null;
let fourthStreamId = null;
let fourthStreamUsingCamera = true;
let isScreenSharing = false;
let isSpeakerCapturing = false;
let isMicphoneClosed = true;
let sdkVersion = null;
let sdkCoreVersion = null;
const streamMuteStatus = {};
const localStreamMuteStatus = {
video: false,
audio: false,
};
//记录底层推流回调的值
let dataTalrtcCapture = {};
let qualityLocalArr = [];
let qualityRemoteArr = [];
let localRoomId = null;
let userId = null;
let isNoticeMicVolume = false;
let enableLocalCapture = true; // 本地摄像头采集,默认允许
let teacherId = null;
let qualityLocal = 0;
let qualityRemote = 0;
let lecturerStreamId = null;
let lecturerUid = null;
const heartBeatItemCount = 15;
const statsStore = new DataStore(1500);
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 audioPullingStore = new DataStore(/*一小时过期时间*/60 * 60 * 1000);
const videoPullingStore = new DataStore(/*一小时过期时间*/60 * 60 * 1000);
let pullingStreamInterval = -1;
const BUSI_USER_ROLE = {
student: 0,
teacher: 1,
assistant: 2,
};
const ENGINE_TYPE = {
talrtc: 7,
nertc: 8,
volcrtc: 9,
'zego-express': 10,
};
const ENGINE_TYPE_NAME = {
7: 'talrtc',
8: 'nertc',
9: 'volcrtc',
10: 'zego-express',
};
/**
* @typedef {{
* audio: boolean,
* video: boolean,
* }} NoticedStream
*
* @description 已经通知过remote_video_ready的流
* @type {Map<string, NoticedStream>}
*/
const userRTCStatus = new Map();
const localStreamIdMap = [];
const getStreamMuteStatus = (streamId, type) => {
const streamStatus = streamMuteStatus[streamId] || {};
return streamStatus[type] || false;
};
const setStreamMuteStatus = (streamId, type, status) => {
if (!streamMuteStatus[streamId]) {
streamMuteStatus[streamId] = {
audio: false,
video: false,
};
}
streamMuteStatus[streamId][type] = status;
// 是否上报心跳标记,只有在有流的情况下才上报心跳
if (!streamIdRtcPlayerInfo1[streamId]) {
resetStreamIdRtcPlayerInfo1(streamId);
}
streamIdRtcPlayerInfo1[streamId][`${type}_type`] = !status;
};
/**
* 判断主流还是辅流
* @param {string} streamId
* @returns {boolean} true: 主流 false: 辅流
*/
const isMainStream = (streamId) => {
return streamId.split('_').length === 4;
};
const callMethod = (name, args) => {
// EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
if (EM) {
return new Promise((resolve, reject) => {
// let noneCamera = (name === 'SetCameraDevice' && !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
});
}
);
});
}
};
const loadTalrtc = (extensionVersion) => {
// EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
if (EM) {
return new Promise((resolve, reject) => {
removerListener();
defaultApi.writeLog(`-> load tal rtc sdk ${extensionId}, ${extensionVersion}`);
EM.Load(
extensionId,
extensionVersion,
false,
(code, msg) => {
defaultApi.writeLog(`loadTalrtc Code: ${code}\nMessage: ${msg}`);
addListener();
resolve();
}
);
});
}
};
//卸载监听
const removerListener = () => {
hasAddListener = false;
defaultApi.writeLog(`TALRTC::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 (zbysdk.openListenerLog) {
console.log(`avsdk TALRTC::Listener:: start event: ${event}, data: ${JSON.stringify(data)}`);
}
if (data && data.indexOf(extensionId) > -1) {
try {
EMListenerId = JSON.parse(data)[extensionId];
defaultApi.writeLog(`TALRTC::addListener-- EMListenerId: ${EMListenerId}`);
} catch (error) {
}
}
if (!event || !data) {
return;
}
let _data = JSON.parse(data);
// defaultApi.writeLog(`TALRTC: event:: ${event} : ${data}`);
switch (event) {
// 推流器出现错误
case 'onLocalError':
defaultApi.writeLog(`TALRTC::addListener-- onLocalError: ${data}`);
NOTICE.pushStreamError({
errorStreamType:_data.streamType,
code: _data.code,
errorMsg: _data
});
dataReport.pushStreamError({
errorStreamType:_data.streamType,
code: _data.code
});
// defaultApi.writeLog(`TALRTC: event:: onLocalError: ${_data}`);
break;
// 直播推流器警告通知
case 'onLocalWarning':
defaultApi.writeLog(`TALRTC::addListener-- onLocalWarning: ${data}`);
NOTICE.pushStreamWarning({
warnStreamType:_data.streamType,
code: _data.code
});
dataReport.pushStreamWarning({
warnStreamType:_data.streamType,
code: _data.code
});
break;
// 首帧音频推送完成的回调通知
case 'onSendLocalFirstAudioFrame':
defaultApi.writeLog(`TALRTC: event:: onSendLocalFirstAudioFrame: ${data}`);
break;
// 采集首帧事件
case 'OnCaptureVideoFirstFrame':
defaultApi.writeLog(`TALRTC: event:: OnCaptureVideoFirstFrame: ${data}`);
// NOTICE.pushFlowSuccess({code:0,publish_streamid: _data.pszStreamID});
break;
// 本地音量
case 'onLocalMicrophoneVolumeUpdate':
// defaultApi.writeLog(`TALRTC: event:: onLocalMicrophoneVolumeUpdate: ${data}`);
if (isNoticeMicVolume) {
NOTICE.captureMicVolumeChanged({
volume: Math.round(_data.volume)
});
}
heartBeatDataReportObj.volume = heartBeatDataReportObj.volume + Math.round(_data.volume) + ',';
break;
// 推流器连接状态回调通知。推流器连接状态 1与服务器断开连接/2 正在连接服务器/3 连接服务器成功/4 重连服务器中 5
case 'onLocalConnectStatusUpdate':
defaultApi.writeLog(`TALRTC: event:: onLocalConnectStatusUpdate: ${data}`);
dataReport.localConnectStatus({
connectStatuStreamType:_data.streamType,
state: _data.state
});
if (_data.state === 2 || _data.state === 1) {
NOTICE.networkError();
} else if (_data.state === 3) {
NOTICE.networkRecovery();
}
break;
// 网络质量的实时统计回调
case 'onLocalNetworkQuality':
// defaultApi.writeLog(`TALRTC: event:: onLocalNetworkQuality: ${data}`);
if(_data.quality == 4 || _data.quality == 5 || _data.quality == 6) {
NOTICE.localNetworkQuality({code: _data.quality});
// dataReport.localNetworkQuality({
// code: _data.quality,
// });
}
qualityLocal = _data.quality;
// qualityLocalArr.push(_data.quality);
break;
// 远端流错误通知,拉流出现错误时,会回调该通知
case 'onRemoteError':
defaultApi.writeLog(`TALRTC: event:: onRemoteError: ${data}`);
NOTICE.pullStreamError({
strErrorStreamId: _data.streamId,
code:_data.code
});
break;
// 远端流警告通知,拉流出现警告时,会回调该通知
case 'onRemoteWarning':
defaultApi.writeLog(`TALRTC: event:: onRemoteWarning: ${data}`);
NOTICE.pullStreamWarning({
strWarnStreamId: _data.streamId,
code:_data.code
});
break;
// 卡顿事件,用于事件上报
case 'stuckevent':
defaultApi.writeLog(`TALRTC: event:: stuckevent: ${data}`);
break;
// 远端音频首帧
case 'onRecvRemoteAudioFirstFrame':
try{
NOTICE.firstAudioSize({
streamId: _data.streamId,
userId: util.getUidByStreamId(_data.streamId)
});
dataReport.firstAudioSize({
pull_streamid: _data.streamId,
pull_uid: util.getUidByStreamIdDr(_data.streamId),
code:'0'
});
} catch (e) { };
defaultApi.writeLog(`TALRTC: event:: OnRecvRemoteAudioFirstFrame: ${data}`);
break;
// 远端流音量大小
case 'onRemoteAudioVolume':
// defaultApi.writeLog(`TALRTC: event:: onRemoteAudioVolume: ${data}}`);
let cbData = {
streamid: _data.streamId,
streamId: _data.streamId,
userId: util.getUidByStreamId(_data.streamId),
volume: _data.volume
};
NOTICE.playerVolumeChanged(cbData);
NOTICE.volumeChange(cbData);
if(streamIdRtcPlayerInfo[_data.streamId]) {
streamIdRtcPlayerInfo[_data.streamId].volume = streamIdRtcPlayerInfo[_data.streamId].volume + Math.round(_data.volume)+ ',';
}
break;
// case 'onUserEnableAudio':
// try {
// if (
// teacherId &&
// _data.streamId.match(teacherId) &&
// isMainStream(_data.streamId) &&
// _data.inuse
// ) {
// setChiefAudioStream(_data.streamId, util.getUidByStreamIdDr(_data.streamId));
// }
// } catch (e) {
// defaultApi.writeLog(`TALRTC: event:: onUserEnableAudio: ${e}`);
// }
// defaultApi.writeLog(`TALRTC: event:: onUserEnableAudio: ${data}`);
// break; // 由于后续还有处理逻辑,所以不能直接 break
// 远端视频首帧
case 'onRenderRemoteVideoFirstFrame':
try{
// Test Code: 临时增加
setTimeout(() => {
NOTICE.pullFlowResult({code:0,pull_streamid: _data.streamId});
NOTICE.firstVideoSize({
streamId: _data.streamId,
userId: util.getUidByStreamId(_data.streamId)
});
dataReport.firstVideoSize({
pull_streamid: _data.streamId,
pull_uid: util.getUidByStreamIdDr(_data.streamId),
code:'0'
});
}, 100);
} catch (e) { };
defaultApi.writeLog(`TALRTC: event:: OnRenderRemoteVideoFirstFrame: ${data}`);
break;
// SEI消息
case 'onRecvSEIMsg':
defaultApi.writeLog(`TALRTC: event:: onRecvSEIMsg: ${data}`);
break;
// 网络质量的实时统计回调
case 'onRemoteNetworkQuality':
// defaultApi.writeLog(`TALRTC: event:: onRemoteNetworkQuality: ${data}`);
if(_data.quality == 4 || _data.quality == 5 || _data.quality == 6) {
NOTICE.remoteNetworkQuality({code: _data.quality});
// dataReport.remoteNetworkQuality({
// code: _data.quality,
// });
}
qualityRemote = _data.quality;
// qualityRemoteArr.push(_data.quality);
break;
// 转推 CDN 上发布音视频流的事件回调
case 'onRtmpStreamingStateChanged':
defaultApi.writeLog(`TALRTC: event:: onRtmpStreamingStateChanged: ${data}`);
break;
// 设备异常
case 'onDeviceError':
defaultApi.writeLog(`TALRTC: event:: OnDeviceError: ${data}`);
let codemsg = {
'-2': '无效设备ID',
'-3': '没有权限',
'-4': '采集帧率为0',
'-5': '设备被占用',
'-6': '设备未插入',
'-7': '需要重启系统',
'-8': '媒体服务无法恢复',
'-9': '设备被SIRI占用'
};
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});
break;
// 拉流的结果通知
case 'onPlayStateUpdate':
defaultApi.writeLog(`TALRTC: event:: onPlayStateUpdate: ${data}`);
break;
// 推流的结果通知
case 'onPushStateUpdate':
defaultApi.writeLog(`TALRTC: event:: onPushStateUpdate: ${data}`);
if (_data.state === 3) {
NOTICE.pushFlowSuccess({code:0,publish_streamid: _data.streamId});
}
dataReport.publishResult({
code: '0',
publish_rtc_streamid: _data.streamId.split('_').length === 5 ? _data.streamId : undefined,
});
break;
case 'onAudioSubscribeStateChange':
defaultApi.writeLog(`TALRTC: event:: onAudioSubscribeStateChange: ${data} ${audioPullingStore.has(_data.streamId)}`);
if (!audioPullingStore.has(_data.streamId)) {
break;
}
dataReport.pullResult({
code: _data.code,
media_type: 'audio',
pull_uid: _data.userId,
pull_streamid: _data.streamId,
});
audioPullingStore.delete(_data.streamId);
break;
case 'onVideoSubscribeStateChange':
defaultApi.writeLog(`TALRTC: event:: onVideoSubscribeStateChange: ${data} ${videoPullingStore.has(_data.streamId)}`);
if (!videoPullingStore.has(_data.streamId)) {
break;
}
dataReport.pullResult({
code: _data.code,
media_type: 'video',
pull_uid: _data.userId,
pull_streamid: _data.streamId,
});
videoPullingStore.delete(_data.streamId);
break;
// 推、拉流的统计数据。
case 'onLocalStatistics':
try {
if (!localStreamId) {
return;
}
const _d = JSON.parse(_data.stats);
NOTICE.pushQuality({
video: {
streamId: localStreamId,
userId,
bitrate: _d.videoBitrate,
fps: _d.fps,
}
});
NOTICE.playLossAndDelay({
userId,
delay:_d.rtt,
lostrate:_d.packetLoss
});
}catch(e){}
// heartBeatDataReportCalc('onLocalStatistics', _data);
statsStore.setData(localStreamId, _data);
// defaultApi.writeLog(`TALRTC: event:: onStatisticsUpdate: ${_data}`);
break;
// 本地摄像头设备的通断状态发生变化
case 'onRemoteStatistics':
try {
const _d = JSON.parse(_data.stats);
NOTICE.pullQuality({
video: {
streamId: _d.streamId,
userId: util.getUidByStreamId(_d.streamId),
bitrate: _d.videoBitrate,
fps: _d.fps,
}
});
NOTICE.playLossAndDelay({
userId: util.getUidByStreamId(_d.streamId),
delay:_d.rtt,
lostrate:_d.packetLoss
});
statsStore.setData(_d.streamId, _data);
}catch(e){}
// heartBeatDataReportCalc('onRemoteStatistics', _data);
// defaultApi.writeLog(`TALRTC: event:: onStatisticsUpdate: ${_data}`);
break;
// 本地摄像头设备的通断状态发生变化
case 'onVideoDeviceChanged':
defaultApi.writeLog(`TALRTC: event:: onVideoDeviceChanged: ${data}`);
setCameraDevice({deviceId: _data.state == 1 ? _data.deviceId : '', operationType: 'hotPlug', deviceState: _data.state == 1 ? 0 : 1, changedDeviceId: _data.deviceId});
break;
/* 本地麦克风设备的通断状态发生变化
* state:1:使用中 2:被禁用 4:一开始就没有?(我也不懂) 8:设备移除
* deviceType:-1:未知 0:播放器 1:麦克风 2:视频显示设备 3:摄像头
*/
case 'onAudioDeviceChanged':
defaultApi.writeLog(`TALRTC: event:: onAudioDeviceChanged: ${data}`);
if (_data.deviceType === 0) {
setSpeakerDevice({deviceId: _data.state == 1 ? _data.deviceId : '', operationType: 'hotPlug', deviceState: _data.state == 1 ? 0 : 1, changedDeviceId: _data.deviceId});
}
if (_data.deviceType === 1) {
setMicrophoneDevice({deviceId: _data.state == 1 ? _data.deviceId : '', operationType: 'hotPlug', deviceState: _data.state == 1 ? 0 : 1, changedDeviceId: _data.deviceId});
}
break;
// mute 视频状态,true:远端开始推流,并拉流收到首帧;false:远端停止推流。
case 'onRemoteVideoStatus':
defaultApi.writeLog(`TALRTC: event:: onRemoteVideoStatus: ${data}`);
break;
// mute 音频状态,true:远端开始推流,并拉流收到首帧;false:远端停止推流。
case 'onRemoteAudioStatus':
defaultApi.writeLog(`TALRTC: event:: onRemoteAudioStatus: ${data}`);
break;
// 首帧视频推送完成的回调通知
case 'onSendLocalFirstVideoFrame':
defaultApi.writeLog(`TALRTC: event:: onSendLocalFirstVideoFrame: ${data}`);
break;
case 'updateRet':
defaultApi.writeLog(`TALRTC: event:: updateRet: ${data}`);
break;
case 'onBeautifyEvent':
if (_data.code === 0) {
defaultApi.writeLog(`TALRTC: event:: onBeautifyEvent success ${JSON.stringify(_data)}`);
} else {
const message = [
'',
'License错误',
'渲染错误',
'参数错误',
];
NOTICE.beautifyError({
code: _data.code,
message: message[_data.code],
});
}
break;
case 'onEngineType':
defaultApi.writeLog(`TALRTC: event:: onEngineType: ${data}`);
dataReport.setRealSDKType(_data.engineType);
if (_data.engineType === 'volcrtc') {
statsStore.updateValidityPeriod(2500);
} else if (_data.engineType === 'talrtc') {
statsStore.updateValidityPeriod(1500);
}
break;
case 'onJoinchannelError':
defaultApi.writeLog(`TALRTC: event:: onJoinchannelError: ${data}`);
dataReport.joinRtcChannel(_data);
break;
case 'onUserOnline':
if (globalConfig.usePomelo) {
break;
}
defaultApi.writeLog(`TALRTC: event:: onUserOnline: ${data}`);
userRTCStatus.set(_data.userId, { audio: false, video: false });
NOTICE.userJoin({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
});
NOTICE.updateStreamDeviceStatus({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
deviceType: 'audio',
deviceStatus: 'closed',
});
NOTICE.updateStreamDeviceStatus({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
deviceType: 'video',
deviceStatus: 'closed',
});
break;
case 'onUserMuteAudio': {
if (globalConfig.usePomelo) {
break;
}
defaultApi.writeLog(`TALRTC: event:: ${event}: ${data}`);
if (_data.streamId.split('_').length === 5) {
break;
}
let streamInfo = userRTCStatus.get(_data.userId);
if (!streamInfo) {
// 如果没有通知过,说明是第一次收到mute通知
streamInfo = { audio: false, video: false };
userRTCStatus.set(_data.userId, streamInfo);
}
if (streamInfo.audio === !_data.mute) {
// 无变更
defaultApi.writeLog(`TALRTC: event:: ${event}: no change for userId: ${_data.userId}, streamId: ${_data.streamId}, mute: ${_data.mute}`);
break;
}
streamInfo.audio = !_data.mute;
if (streamInfo.audio && !streamInfo.video) {
// 如果音频开启了,视频没有开启,说明是首次上行,通知add
NOTICE.addStream({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
streamId: _data.streamId,
});
break;
}
if (!streamInfo.video) {
// 如果音视频都没有,通知remove
userRTCStatus.delete(_data.userId);
NOTICE.removeStream({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
streamId: _data.streamId,
});
}
break;
}
case 'onUserMuteVideo': {
if (globalConfig.usePomelo) {
break;
}
defaultApi.writeLog(`TALRTC: event:: ${event}: ${data}`);
if (_data.streamId.split('_').length === 5) {
break;
}
let streamInfo = userRTCStatus.get(_data.userId);
if (!streamInfo) {
// 如果没有通知过,说明是第一次收到mute通知
streamInfo = { audio: false, video: false };
userRTCStatus.set(_data.userId, streamInfo);
}
if (streamInfo.video === !_data.mute) {
// 无变更
defaultApi.writeLog(`TALRTC: event:: ${event}: no change for userId: ${_data.userId}, streamId: ${_data.streamId}, mute: ${_data.mute}`);
break;
}
streamInfo.video = !_data.mute;
if (streamInfo.video && !streamInfo.audio) {
// 如果视频开启了,音频没有开启,说明是首次上行,通知add
NOTICE.addStream({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
streamId: _data.streamId,
});
break;
}
if (!streamInfo.audio) {
// 如果音视频都没有,通知remove
userRTCStatus.delete(_data.userId);
NOTICE.removeStream({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
streamId: _data.streamId,
});
}
break;
}
case 'onUserEnableAudio':
try {
if (
teacherId &&
_data.streamId.match(teacherId) &&
isMainStream(_data.streamId) &&
_data.inuse
) {
setChiefAudioStream(_data.streamId, util.getUidByStreamIdDr(_data.streamId));
}
} catch (e) {
defaultApi.writeLog(`TALRTC: event:: onUserEnableAudio error: ${e}`);
}
if (globalConfig.usePomelo) {
break;
}
defaultApi.writeLog(`TALRTC: event:: onUserEnableAudio: ${data}`);
if (_data.streamId.split('_').length === 5) {
break;
}
NOTICE.updateStreamDeviceStatus({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
deviceType: 'audio',
deviceStatus: _data.deviceStatus,
});
break;
case 'onUserEnableVideo':
if (globalConfig.usePomelo) {
break;
}
defaultApi.writeLog(`TALRTC: event:: onUserEnableVideo: ${data}`);
if (_data.streamId.split('_').length === 5) {
break;
}
NOTICE.updateStreamDeviceStatus({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
deviceType: 'video',
deviceStatus: _data.deviceStatus,
});
break;
case 'onUserOffline':
if (globalConfig.usePomelo) {
break;
}
userRTCStatus.delete(_data.userId);
defaultApi.writeLog(`TALRTC: event:: onUserOffline: ${data}`);
NOTICE.leave({
userId: _data.userId,
userName: _data.userName,
role: _data.role,
});
break;
case 'onEngineChangeNotify':
if (globalConfig.usePomelo) {
break;
}
defaultApi.writeLog(`TALRTC: event:: onEngineChangeNotify: ${data}`);
zbysdk.changeSDK();
break;
default:
console.warn('warning: uncaught listener:', event);
}
});
}
};
const checkUpdate = async (institutionId, userid, role) => {
defaultApi.writeLog(`avsdk TALRTC::checkUpdate ${institutionId}, ${userid}, ${role}`);
await loadTalrtc(talrtcExt.version);
const appid = getAppIdByInstitutionId(String(institutionId));
defaultApi.writeLog(`avsdk TALRTC::checkUpdate appid ${appid}`);
userId += '';
if (!appid) {
return;
}
const data = {
appid,
userid,
role: BUSI_USER_ROLE[role] || 0,
};
return callMethod('CheckUpdate', {
...data,
extraInfo: JSON.stringify(data)
});
};
const startPullingStreamObserver = () => {
audioPullingStore.restore();
videoPullingStore.restore();
if (pullingStreamInterval !== -1) {
clearInterval(pullingStreamInterval);
}
pullingStreamInterval = setInterval(() => {
audioPullingStore.forEach((value, key) => {
try {
if (!audioPullingStore.checkData(value)) {
audioPullingStore.delete(key);
dataReport.pullResult({
code: PullResult.PULL_STREAM_CODE_TIMEOUT,
media_type: 'audio',
pull_uid: util.getUidByStreamIdDr(key),
pull_streamid: key,
});
}
} catch (e) {
defaultApi.writeLog(`pullingStreamInterval error ${e}`, undefined, 'error');
}
});
videoPullingStore.forEach((value, key) => {
try {
if (!videoPullingStore.checkData(value)) {
videoPullingStore.delete(key);
dataReport.pullResult({
code: PullResult.PULL_STREAM_CODE_TIMEOUT,
media_type: 'video',
pull_uid: util.getUidByStreamIdDr(key),
pull_streamid: key,
});
}
} catch (e) {
defaultApi.writeLog(`pullingStreamInterval error ${e}`, undefined, 'error');
}
});
}, 500);
};
/**
*
* @param {string | number} userId
* @returns {string}
*/
const formatUserId = (userId) => {
userId = String(userId);
const userNumber = userId.match(/\d+/);
const baseNumber = 1e9;
if (!userNumber || !userNumber.length) {
return baseNumber;
}
const pureNumber = parseInt(userNumber[0]) || 0;
if (pureNumber === Number(userId)) {
return String(pureNumber);
} else {
return String(pureNumber + baseNumber);
}
};
/**
* 处理token
* @param {string} token
*/
const processToken = (token) => {
const tokenArr = token.split('.');
if (tokenArr.length === 3) {
const payloadStr = window.atob(tokenArr[1]);
const payload = JSON.parse(payloadStr);
globalConfig.usePomelo = payload.signal >> 0 === 0;
defaultApi.writeLog(`avsdk TALRTC::usePomelo ${globalConfig.usePomelo}`);
dataReport.setRealSDKType(ENGINE_TYPE_NAME[payload.type] || payload.type);
return payload;
} else {
defaultApi.writeLog('avsdk TALRTC::processToken error ' + token, undefined, 'error');
return token;
}
};
// 初始化相关
const init = async (args) => {
defaultApi.writeLog(`avsdk TALRTC::init ${JSON.stringify(args)}`);
const usedDevices = window.zbyAVSDK_device_checker_init;
const { devices } = args;
const sdkType = window.zbyAVSDK_init_sdk_type || 'talrtc';
localRoomId = args.roomId;
userId = args.userId;
localStreamId = args.streamId;
localStreamIdMap[0] = localStreamId;
teacherId = args.teacherId;
lecturerStreamId = typeof args.lecturerStreamId === 'string' ? args.lecturerStreamId.toUpperCase() : null;
lecturerUid = typeof lecturerStreamId === 'string' ? lecturerStreamId.split('_')[2] : null;
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) || ''
};
const [, token] = await Promise.all([
loadTalrtc(args.extensionVersion),
getRTCToken({ appid: args.appId, planid: parseInt(args.live_id), user: formatUserId(args.userId), room: args.roomId, ignoreVolc: 1 }),
]);
defaultApi.writeLog(`-> get token success ${token}`);
processToken(token);
args.usersign = token;
await startEngine(args.appId, localRoomId, args.userId, args.usersign || '67890', ENGINE_TYPE[sdkType] || 7, args.live_id, BUSI_USER_ROLE[args.role] || 0);
defaultApi.writeLog('-> start talrtc sdk success');
userRTCStatus.clear();
classMode = args.classMode >> 0;
// 0: 小组课 1: 小班课 2: 新小班课
if (classMode === 1 || classMode === 2){
if(args.role === 'teacher') {
//小班课 需要加载采集插件的输入数据,为了拼接talrtc头像
await loadCollectionInputEntry();
//屏幕流通道
screenStreamId = `${window.zby_sdk_init_params.institutionId}_${localRoomId}_${userId}_${args.live_id}_1`;
localStreamIdMap[1] = screenStreamId;
await enableExternalVideoSource(true, screenStreamId);
await setCameraEncodeFps(args.screenSameFps, screenStreamId);
await setCameraCaptureResolution(args.screenSameResolutionWidth, args.screenSameResolutionHeight, screenStreamId);
await setCameraEncodeResolution(args.screenSameResolutionWidth, args.screenSameResolutionHeight, screenStreamId);
await setCameraEncodeBitrate(args.screenSameBitrate, screenStreamId);
//rtc同屏以及高光时刻,都开启音频外部采集,默认传1
await setAudioAuxSource(1);
teacherId = null;
}
if(args.noiseSuppressMode == -2) {
defaultApi.writeLog(`noiseSuppressMode 关闭噪声消除 ${args.noiseSuppressMode}`);
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 >> 0);
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 >> 0);
};
};
if((args.role === 'teacher' || args.role === 'assistant') && classMode === 0){
//坐立模式开启外部采集器,站立关闭外部采集器,只有小组课坐立模式才会调用,通过这个把rtmp数据给到talrtc(头像),开启了外部采集,talrtc不会再本地采集数据
// V2 不区分坐立站立,老师和辅导老师都启动外部视频源
await enableExternalVideoSource(true, localStreamId);
enableLocalCapture = false; // 禁用本地摄像头采集,防止与其他进程抢占摄像头
} else {
await setDefaultDevice(_devices, 'default');
}
// 主通道
await setCameraEncodeFps(args.encodeCaptureFps, localStreamId);
await setCameraCaptureResolution(args.previewResolutionWidth, args.previewResolutionHeight, localStreamId);
await setCameraEncodeResolution(args.encodedResolutionWidth, args.encodedResolutionHeight, localStreamId);
await setCameraEncodeBitrate(args.encodeCaptureBitrate, localStreamId);
await setMicphoneVolumInterval(500);
await setMediaSideFlags();
startPullingStreamObserver();
window.current_sdk_type = sdkType;
defaultApi.writeLog('avsdk TALRTC::init finished current_sdk_type: ' + sdkType);
};
/**
* @function 初始化引擎
* @param {string} appid TalRTC 秘钥,必选
* @param {string} userid
* @param {string} usersign
* @param {1 | 7} type 直播中台SDK底层引擎类型 7: 自研引擎
* @param {string} liveID
* @param {0 | 1 | 2} businessRole 0: student 1: teacher 2: tutor
* @return Promise | void
*/
const startEngine = (appid, roomId, userid, usersign, type, liveID, businessRole) => {
defaultApi.writeLog(`avsdk TALRTC::StartEngine appid: ${appid} roomId:${roomId} userid: ${userid} usersign: ${usersign}, liveID: ${liveID}, businessRole: ${businessRole}`);
return callMethod('StartEngine', {
appid,
roomId,
userid,
usersign,
type,
liveID: '' + liveID,
businessRole,
institutionId: '' + window.zby_sdk_init_params.institutionId,
streamId: localStreamId,
lecturerStreamId,
});
};
//开启消除回声
export const setEnableAEC = (enable) => {
return callMethod('EnableAEC', {enable});
};
//设置回声消除模式 mode :激进模式 =0/中等模式 =1/轻度模式 =2
export const setAECMode = (mode) => {
return callMethod('SetAECMode', {mode});
};
//开启噪音消除
export const setEnableNoiseSuppress = (enable) => {
return callMethod('EnableNoiseSuppress', {enable});
};
//设置噪音消除模式 mode :0:低 1:中 2高
export const setNoiseSuppressMode = (mode) => {
return callMethod('SetNoiseSuppressMode', {mode});
};
//新增瞬时降噪接口 瞬时降噪 可以处理掉 敲键盘、敲桌子、敲门等瞬态噪声。非瞬态噪声,此接口无法处理,比如划桌子等摩擦噪音。
export const enableTransientNoiseSuppress = (enable) => {
return callMethod('EnableTransientNoiseSuppress',{enable});
};
/**
* 加载采集插件的输入数据 //调用后,talrtc 可以向采集插件传送摄像头数据,用于拼接
* @return {Promise}
*/
export const loadCollectionInputEntry = () => {
return callMethod('LoadCollectionInputEntry', {});
};
/**
* 1、设置需要拼接的学生头像流id,按数组顺序拼接
* 2、加载截屏插件,获取截屏插件设置拉流头像回调函数
* @param {Array} stream_ids 拉流id数组,最大有效数量7个
* @returns {Promise}
*/
export const loadCollectionInputEntry2 = (stream_ids) => {
return callMethod('LoadCollectionInputEntry2', {stream_ids});
};
//设置是否发送sei
const setMediaSideFlags = (start = true, onlyAudioPublish = false, mediaInfoType = 2, seiSendType = 1, idx = 0) => {
return callMethod('SetMediaSideFlags',{start,onlyAudioPublish, mediaInfoType, seiSendType, idx});
};
/**
* @function 销毁引擎
* @return Promise | void
*/
const destroyEngine = () => {
return callMethod('DestroyEngine', {});
};
// 设置采集分辨率
const setCameraCaptureResolution = (width, height, streamId = localStreamId) => {
return callMethod('SetCameraCaptureResolution', { width, height, streamId });
};
// 设置编码分辨率
const setCameraEncodeResolution = (width, height, streamId = localStreamId) => {
return callMethod('SetCameraEncodeResolution', { width, height, streamId });
};
// 设置编码帧率
const setCameraEncodeFps = (fps, streamId = localStreamId) => {
return callMethod('SetCameraEncodeFps', { fps, streamId });
};
// 设置编码码率
const setCameraEncodeBitrate = (bitrate, streamId = localStreamId) => {
return callMethod('SetCameraEncodeBitrate', { bitrate, streamId });
};
// 获取指定摄像头的支持的分辨率
const getCameraResolution = async (deviceId) => {
const resp = await callMethod('GetCameraResolution', { deviceId });
const cameraArr = JSON.parse(resp.msg || '{}').SupportedResolution;
return {
code: resp.code,
msg: JSON.stringify(cameraArr)
};
};
// 获取 rtc_sdk.dll 版本号
const getSDKVersion = async () => {
if (sdkVersion) {
return sdkVersion;
}
return callMethod('GetSDKVersion', {}).then((e) => {
return sdkVersion = JSON.parse(e.msg).version;
}).catch((e) => {
return '';
});
};
// 获取 rtc_sdk.dll 内核版本号
const getSDKCoreVersion = async () => {
if (sdkCoreVersion) {
return sdkCoreVersion;
}
return callMethod('GetSDKCoreVersion', {}).then((e) => {
return sdkCoreVersion = JSON.parse(e.msg).version;
}).catch((e) => {
return '';
});
};
/**
* @function 设置默认的硬件设备,包括摄像头、麦克风以及扬声器
* @return Promise
*/
export const setDefaultDevice = async (devices, operationType) => {
// 设置默认的摄像头
if (devices && devices.camera) {
if (zbyAVSDK_device_checker_init.camera.list.length == 0) {
await getCameraDeviceList();
}
await setCameraDevice({ deviceId: devices.camera, operationType: `${operationType}_1` });
} else {
const cameraData = await getCameraDeviceList();
if (cameraData.length) {
let hasSetCamera = false;
for (let item of cameraData) {
if (item.isDefault) {
await setCameraDevice({ deviceId: item.deviceId, operationType: `${operationType}_2` });
hasSetCamera = true;
break;
}
}
if (!hasSetCamera) {
await setCameraDevice({ 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();
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 getSpeakerDeviceListInternal();
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 = async (intervalMs = 500) => {
isNoticeMicVolume = true;
isMicphoneClosed = false;
return callMethod('StartMicrophone', { intervalMs });
};
/**
* @function 关闭麦克风
* @return Promise | void
*/
const stopMicrophone = () => {
isNoticeMicVolume = false;
isMicphoneClosed = true;
return callMethod('StopMicrophone', {});
};
const openOrCloseMicrophone = async (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 getMicrophoneDeviceListInternal = () => {
return callMethod('GetMicrophoneDeviceList', {});
};
/**
* @function 获取麦克风列表
* @return Promise | void
*/
const getMicrophoneDeviceList = async () => {
const defaultMicrophoneId = JSON.parse((await getDefaultMicrophone()).msg).deviceId;
const resp = await getMicrophoneDeviceListInternal();
let microphoneListArr = JSON.parse(JSON.parse(resp.msg).DeviceList) || [];
let microphoneList = [];
for (let i = 0, len = microphoneListArr.length; i < len; i++) {
microphoneList.push({
deviceId: microphoneListArr[i].id,
deviceName: microphoneListArr[i].name,
isDefault: defaultMicrophoneId === microphoneListArr[i].id
});
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;
defaultApi.writeLog(`getMicrophoneDeviceList ${JSON.stringify(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) => {
defaultApi.writeLog('-> getMicrophoneDeviceList_microphoneListArr_tal_rtc_i');
let { deviceId, operationType, deviceState, 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) {
};
await setCurrentMicrophoneDevice(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,
deviceState,
changedDeviceId
});
}
};
/**
* @function 获取当前麦克风的音量
* @return Promise | void
*/
const getCurrentMicrophoneVolume = () => {
return callMethod('GetCurrentMicrophoneVolume', {}).then(ret => {
let volume = 0;
try {
volume = Math.round(JSON.parse(ret.msg).microphoneVolume / 255 * 100);
} 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: Math.round(volume / 100 * 255)
});
};
// 摄像头相关
/**
* @function 打开摄像头
* @param width: Number
* @param height: Number
* @return Promise | void
*/
const startCamera = (width, height) => {
return callMethod('StartCamera', { width, height, streamId: localStreamId });
};
/**
* @function 关闭摄像头
* @return Promise | void
*/
const stopCamera = () => {
return callMethod('StopCamera', { streamId: localStreamId });
};
const openOrCloseCamera = async (operation) => {
if (operation) {
await startCamera();
} else {
await stopCamera();
}
};
/**
* @function 获取摄像头列表
* @return Promise | void
*/
const getCameraDeviceListInternal = () => {
return callMethod('GetCameraDeviceList', {});
};
/**
* @function 获取摄像头列表
* @return Promise | void
*/
export const getCameraDeviceList = async () => {
const resp = await getCameraDeviceListInternal();
let videoListArr = JSON.parse(JSON.parse(resp.msg).DeviceList) || [];
let videoList = [];
for (let i = 0, len = videoListArr.length; i < len; i++) {
let nameForChecking = videoListArr[i].name.toLowerCase();
let checkIfIsDefaultFromName = (nameForChecking.indexOf('built-in') >= 0) ||
(nameForChecking.indexOf('builtin') >= 0) ||
(nameForChecking.indexOf('default') >= 0) ||
(nameForChecking.indexOf('默认') >= 0) ||
(nameForChecking.indexOf('默認') >= 0);
videoList.push({
deviceId: videoListArr[i].id,
deviceName: videoListArr[i].name,
isDefault: videoListArr[i].default || checkIfIsDefaultFromName
});
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,必选
* @return Promise | void
*/
const setCurrentCameraDeviceInternal = (deviceId, streamId = localStreamId) => {
window.zbyAVSDK_device_checker_init.camera.use = deviceId;
return callMethod('SetCurrentCameraDevice', {
streamId,
deviceId
});
};
/**
* @function 指定使用的摄像头
* @param deviceId: String 摄像头 id,必选
* @param operationType: String 操作类型,可选
* 'device_error' -> 设备出错处理,'plug_and_unplug' -> 热插拔处理,
* 不传即是普通的设置摄像头设备的行为
* @return Promise | void
*/
const setCameraDevice = async (args) => {
defaultApi.writeLog(`setCameraDevice ${JSON.stringify(args)} enableLocalCapture: ${enableLocalCapture}`);
let { deviceId, operationType, deviceState, code, changedDeviceId, streamId } = args;
if (!enableLocalCapture) {
window.zbyAVSDK_device_checker_init.camera.use = deviceId;
window.zbyAVSDK_device_checker_init.camera.name = deviceListReport.cameraList[deviceId];
return;
}
let deviceName = '';
let cameraData = await getCameraDeviceList();
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];
try {
dataReport.setDevice({
device_type: 1,
device_id: deviceId,
device_name: deviceListReport.cameraList[deviceId],
operationType,
fore_state: operationType == 'hotPlug' ? deviceState + 1 : '-'
});
} catch (e) {
};
await setCurrentCameraDeviceInternal(deviceId, streamId);
if (fourthStreamUsingCamera && fourthStreamId && !streamId) {
await setCurrentCameraDeviceInternal(deviceId, fourthStreamId);
}
if (operationType == 'hotPlug' || operationType == 'deviceError') {
if (!cameraData) {
cameraData = await getCameraDeviceList();
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 getAudioDeviceList = () => {
return callMethod('GetAudioDeviceList', {});
};
/**
* @function 获取系统默认的音频设备
* @param deviceType: Number 音频设备类型,0 -> 麦克风,1 -> 扬声器,必选
* @return Promise | void
*/
const getDefaultAudioDeviceId = (deviceType) => {
return callMethod('GetDefaultAudioDeviceId', {
deviceType,
// 以下两个参数仅仅是为了向下向后兼容,使用 hardcore 即可
deviceId: '',
deviceIdLength: 0
});
};
/**
* @function 获取系统默认的麦克风设备 id
* @return Promise
*/
const getDefaultMicrophone = () => {
return getDefaultAudioDeviceId(0);
};
/**
* @function 获取系统默认的扬声器设备 id
* @return Promise
*/
const getDefaultSpeaker = () => {
return getDefaultAudioDeviceId(1);
};
/**
* @function 获取扬声器设备列表
* @return Promise | void
*/
const getSpeakerDeviceListInternal = () => {
return callMethod('GetSpeakerDeviceList', {});
};
/**
* @function 获取扬声器列表
* @return Promise | void
*/
const getSpeakerDeviceList = async () => {
const defaultSpeakerId = JSON.parse((await getDefaultSpeaker()).msg).deviceId;
const resp = await getSpeakerDeviceListInternal();
let speakerListArr = JSON.parse(JSON.parse(resp.msg).DeviceList) || [];
let speakerList = [];
for (let i = 0, len = speakerListArr.length; i < len; i++) {
speakerList.push({
deviceId: speakerListArr[i].id,
deviceName: speakerListArr[i].name,
isDefault: defaultSpeakerId === speakerListArr[i].id
});
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,必选
* @return Promise | void
*/
const setCurrentSpeakerDevice = (deviceId) => {
window.zbyAVSDK_device_checker_init.speaker.use = deviceId;
return callMethod('SetCurrentSpeakerDevice', {
deviceId
}).then((result) => {
if (result.code === 0) {
NOTICE.speakerChanged({
deviceId
});
}
return result;
});
};
/**
* 获取当前扬声器设备id
* @return {String} 当前扬声器设备id
*/
const getCurrentSpeakerDevice = () => {
defaultApi.writeLog(`avsdk TALSDK::getSpeakerDevice ${usingAudioDeviceId.speaker}`);
return usingAudioDeviceId.speaker;
};
/**
* @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-talrtc----`);
let { deviceId, operationType, deviceState, code, changedDeviceId } = args;
let deviceName = '';
let speakerData;
if (!deviceId) {
speakerData = await getSpeakerDeviceList();
if (speakerData.length) {
let hasGetSpeaker = false;
for (let item of speakerData) {
if (item.isDefault) {
deviceId = item.deviceId;
deviceName = item.deviceName;
hasGetSpeaker = true;
break;
}
}
if (!hasGetSpeaker) {
deviceId = speakerData[0].deviceId;
deviceName = speakerData[0].deviceName;
}
} else {
deviceId = '';
}
if (deviceId === '') {
NOTICE.noDevice({
deviceType: 'speaker'
});
}
}
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);
};
usingAudioDeviceId.speaker = deviceId;
await setCurrentSpeakerDevice(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 获取当前扬声器的音量
* @returns {Promise<number>}
*/
const getCurrentSpeakerVolume = () => {
return callMethod('GetCurrentSpeakerVolume', {}).then(ret => {
let volume = 0;
try {
volume = Math.round(JSON.parse(ret.msg).speakerVolume / 255 * 100);
} catch (e) {
console.error(`zby-live-sdk: getCurrentSpeakerVolume ret: ${ret}. error: ${e}`);
}
return volume;
});
};
/**
* @function 是否存在媒体流
* @param streamId
* @returns {boolean}
*/
export const hasStream = streamId => {
return Object.keys(streamIdToPreviewId).includes(streamId);
};
/**
* @function 获取当前视频流的通道
* @param streamId 流 id
* @return channelIndex
*/
export const getChannelIndex = (streamId) => {
defaultApi.writeLog(`avsdk TALSDK::getChannelIndex streamId: ${streamId} ${JSON.stringify(streamIdToPreviewId)}`);
return streamIdToPreviewId[streamId];
};
/**
* @function 获取当前视频流所有的通道
* @param streamId 流 id
* @return channelIndex
*/
export const getAllChannelIndex = () => {
defaultApi.writeLog(`avsdk TALSDK::getAllChannelIndex ${JSON.stringify(streamIdToPreviewId)}`);
return streamIdToPreviewId;
};
/**
* @function 设置当前扬声器音量
* @param volume: number 音量值
* @return Promise | void
*/
const setCurrentSpeakerVolume = (volume) => {
return callMethod('SetCurrentSpeakerVolume', {
volume: Math.round(volume / 100 * 255)
});
};
/**
* @function 设置扬声器静音
* @param mute: Boolean 必选
* @return Promise | void
*/
const setSpeakerDeviceMute = (mute) => {
return callMethod('SetSpeakerDeviceMute', {
deviceId: usingAudioDeviceId.speaker,
mute
});
};
/**
* @function 获取当前应用程序音量
* @return Promise | void
*/
const getSpeakerSimpleVolume = () => {
return callMethod('GetSpeakerSimpleVolume', {
deviceId: usingAudioDeviceId.speaker
}).then(ret => {
let volume = 0;
try {
volume = Math.round(JSON.parse(ret.msg).speakerVolume / 255 * 100);
} catch (e) {
console.error(`zby-live-sdk: getSpeakerSimpleVolume ret: ${ret}. error: ${e}`);
}
return volume;
});
};
/**
* @function 设置当前应用程序音量
* @param volume: Number 应用程序音量,必选
* @return Promise | void
*/
const setSpeakerSimpleVolume = (volume) => {
return callMethod('SetSpeakerSimpleVolume', {
deviceId: usingAudioDeviceId.speaker,
volume: Math.round(volume / 100 * 255)
});
};
/**
* @function 设置当前应用程序静音
* @param mute: Boolean 是否静音,必选
* @return Promise | void
*/
const setSpeakerSimpleMute = (mute) => {
return callMethod('SetSpeakerSimpleMute', {
deviceId: usingAudioDeviceId.speaker,
mute
});
};
const enableAudioSpeakerCapture = (capture) => {
setSystemAudioLoopback(capture);
};
/**
* @function 打开系统声音采集
* @enable true|false
* @return Promise | void
*/
const setSystemAudioLoopback = (enable) => {
isSpeakerCapturing = enable;
if (enable) {
return setAudioType(6, localStreamId);
} else {
return setAudioType(1, localStreamId);
}
// return callMethod('SetAudioSpeakerCapture', {
// enable,
// });
};
/**
* @function 录制转推对外接口
* @param type: String start/stop 必选
* @param targetUrl: String 转推地址(支持 RTMP) 必选
* @return Promise | void
*/
const controlCdnStreaming = async (type) => {
const targetUrl = 'rtmp://media-push-ali-test.livecloud.eaydu.com/testlive/210926-11?key=75c868672113df0063430e9e47fb2a60&appId=zt10001&bizId=zt10001AI&cdn=1';
defaultApi.writeLog(`avsdk TALRTC::controlCdnStreaming targetUrl: ${targetUrl} type: ${type}`);
if (type === 'start') {
return addPublishRtmpStreamUrl(targetUrl);
}
if (type === 'stop') {
return removePublishStreamUrl(targetUrl);
}
};
/**
* @function SEI 消息
* @param data: String 数据
* @param dataSize: Number 数据长度
* @param repeatCount: Number 重试次数
* @return Promise | void
*/
const sendSEIMsg = (data) => {
return callMethod('SendSEIMsg', { data: data, dataSize: data.length, repeatCount: 3 });
};
/**
* @function 开始屏幕采集
* @return Promise | void
*/
const startScreenCapture = () => {
return callMethod('StartScreenCapture', {});
};
/**
* @function 停止屏幕采集
* @return Promise | void
*/
const stopScreenCapture = () => {
return callMethod('StopScreenCapture', {});
};
const setPreviewStreamId = (streamId) => {
return callMethod('SetPreviewStreamId', { streamId });
};
// 推拉流相关
/**
* @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(`avsdk TALSDK::startLocalOrRemotePreview streamId: ${streamId}, isLocal: ${isLocal}`);
let isUpdateChromeVersion = await util.getChromeVersion();
const externalConstraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'external',
chromeMediaSourceId: `ems://talrtc_ext/${isLocal || isTeacherNewWindowPreview ? -2 : streamId}`,
maxWidth: 3840,
maxHeight: 2160,
minWidth: 240,
minHeight: 180,
}
}
};
defaultApi.writeLog(`externalConstraints ${JSON.stringify(externalConstraints)}`);
if (isLocal) {
await setPreviewStreamId(streamId || localStreamId);
}
return new Promise((resolve, reject) => {
const handleExternalSuccess = (stream) => {
defaultApi.writeLog(`${stream}----stream-talrtc----`);
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 = stream;
console.log('内核升级1', domId, document.querySelector(domId), document.querySelector(domId).srcObject, src);
} else {
console.log('内核升级2');
document.querySelector(domId).src = window.URL.createObjectURL(stream);
}
}
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 = async (streamId = localStreamId, idx = 0) => {
defaultApi.writeLog(`avsdk TALSDK::startPush streamId: ${streamId}, idx: ${idx}`);
localStreamIdMap[idx] = streamId;
if (idx === 3 && fourthStreamUsingCamera) {
setFourthStreamType(6, streamId);
}
try {
if (!isFirstHeartBeatReport) {
isFirstHeartBeatReport = true;
heartBeatDataReport('start');
}
} catch (error) {}
const muteAudio = localStreamMuteStatus.audio;
const muteVideo = localStreamMuteStatus.video;
return callMethod('StartPush', {
streamId,
muteAudio,
muteVideo,
muteDefaultAudio: classMode === 1 || classMode === 2,
muteDefaultVideo: classMode === 1 || classMode === 2
});
};
/**
* @function 停止推流
* @param streamId: String 流 id
* @return Promise | void
*/
const stopPush = (streamId) => {
if (!streamId) {
streamId = localStreamId;
}
if (streamId === screenStreamId) {
isScreenSharing = false;
}
defaultApi.writeLog(`avsdk TALSDK::stopPush streamId: ${streamId}`);
const idx = localStreamIdMap.indexOf(streamId);
if (idx > -1) {
delete localStreamIdMap[idx];
}
return callMethod('StopPush', { streamId });
};
/**
* @function 不推/推 音频
* @param mute: bool
* @return Promise | void
*/
const muteLocalAudio = (mute) => {
defaultApi.writeLog(`avsdk TALSDK::muteLocalAudio mute: ${mute}`);
localStreamMuteStatus.audio = mute;
return callMethod('MuteLocalAudio', { mute });
};
/**
* @function 不推/推 视频
* @param mute: bool
* @return Promise | void
*/
const muteLocalVideo = (mute, streamId = localStreamId) => {
defaultApi.writeLog(`avsdk TALSDK::muteLocalVideo mute: ${mute}`);
localStreamMuteStatus.video = mute;
return callMethod('MuteLocalVideo', { mute, streamId });
};
/**
* @function 开始转推本地摄像头 TALRTC 流至 RTMP。目前只支持转推 1 路
* @return Promise | void
*/
const addPublishRtmpStreamUrl = (url) => {
defaultApi.writeLog(`avsdk TALSDK::addPublishRtmpStreamUrl url: ${url}`);
return callMethod('AddPublishRtmpStreamUrl', { url });
};
/**
* @function 停止转推 RTMP
* @return Promise | void
*/
const removePublishStreamUrl = (url) => {
defaultApi.writeLog(`avsdk TALSDK::removePublishStreamUrl url: ${url}`);
return callMethod('RemovePublishStreamUrl', { url });
};
/**
* @function 开始拉流
* @param {string} streamId 流 id
* @return {Promise} Promise | void
*/
const startPlay = (streamId, muteAudio, muteVideo) => {
if (typeof muteAudio === 'undefined') {
muteAudio = getStreamMuteStatus(streamId, 'audio');
}
if (typeof muteVideo === 'undefined') {
muteVideo = getStreamMuteStatus(streamId, 'video');
}
defaultApi.writeLog(`avsdk TALSDK::startPlay streamId: ${streamId}, muteAudio: ${muteAudio}, muteVideo: ${muteVideo}`);
audioPullingStore.setData(streamId, {muted: muteAudio});
videoPullingStore.setData(streamId, {muted: muteVideo});
return callMethod('StartPlay', {
myStreamId: localStreamId,
streamId,
muteAudio,
muteVideo,
muteDefaultAudio: classMode === 1 || classMode === 2,
muteDefaultVideo: classMode === 1 || classMode === 2
}).then((...args) => {
setStreamMuteStatus(streamId, 'audio', muteAudio);
setStreamMuteStatus(streamId, 'video', muteVideo);
return args;
});
};
/**
* @function 初始化拉流
* @param {string} streamId:String 从传来的信道消息中获取,必选
* @param {string} domId:String <video> 标签的 id,可选
* 如果传了就把视频绑定到对应的 <video> 标签上
* @param pInfo:String 多媒体流附加信息,可选,默认为 'none'
* @return src:String 视频预览地址
*/
export const initPullFlow = async (streamId, domId, mute, pInfo, notAutoPlay, audioOnly) => {
defaultApi.writeLog(`TALRTC::initPullFlow_start , streamId :${streamId}, notAutoPlay: ${notAutoPlay}, audioOnly: ${audioOnly}`);
let playChannel;
resetStreamIdRtcPlayerInfo1(streamId);
resetStreamIdRtcPlayerInfo(streamId);
try {
if (!isFirstHeartBeatReport) {
isFirstHeartBeatReport = true;
heartBeatDataReport('start');
}
} catch (error) {}
if (streamIdToPreviewId[streamId] == undefined) {
defaultApi.writeLog(`TALRTC:: 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 ? chnTotal : chnTotal + 1; // 主流使用50,辅流使用51
} else {
if (uiChnIndexs.length === 0) {
defaultApi.writeLog('TALRTC:: all channel are used', null, 'error');
delete streamIdRtcPlayerInfo[streamId];
NOTICE.overMaxConnect({streamId, message: 'reach max connect'});
return {
videoSrc: '',
playerId: -2,
};
}
playChannel = uiChnIndexs.pop();
}
streamIdToPreviewId[streamId] = playChannel;
} else {
defaultApi.writeLog(`TALRTC::The streamId queue contains this streamId: ${streamId}`);
playChannel = streamIdToPreviewId[streamId];
// console.log('streamIdIsExited',playChannel,streamIdToPreviewId[streamId],uiChnIndexs);
await stopPlay(streamId);
resetStreamIdRtcPlayerInfo(streamId);
}
// 判断是否创建或者获取播放通道成功,不成功就删掉重试
if (playChannel == undefined) {
delete streamIdToPreviewId[streamId];
defaultApi.writeLog('TALRTC:: uiChnIndex is not exist', null, 'error');
return initPullFlow(streamId, domId, mute, pInfo, notAutoPlay, audioOnly);
}
previewIdToStreamId[playChannel] = streamId;
/**
* 将 muteRemote 参数耦合进startPlay,减少异步调用,降低黑屏概率
*/
// 是否拉取声音
// await muteRemoteAudio(streamId, !!mute);
// // 是否拉取画面
// await muteRemoteVideo(streamId, !!audioOnly);
const videoSrc = await startLocalOrRemotePreview(false, streamId, domId);
if (!notAutoPlay) {
await startPlay(streamId, !!mute, !!audioOnly);
}
return {
videoSrc,
playerId: playChannel
};
};
/**
* @function 开始连麦
* @param mode: number 采集器默认, 默认0=站立 1=坐立
* @return Promise | void
*/
export const teacherStartLinkMic = async (mode = 0) => {
await startMicrophone();
if (mode === 0) {
await muteLocalVideo(true, localStreamId);
await setMicrophoneDevice({});
// await muteStreamAudio(false);
await muteLocalAudio(false);
} else {
// await muteLocalVideo(false, localStreamId);
await muteLocalAudio(false);
}
};
/**
* @function 结束连麦
* @param mode:number 采集器默认, 默认0=站立 1=坐立
* @return Promise | void
*/
export const teacherStopLinkMic = async (mode = 0) => {
await stopMicrophone();
if (mode === 0) {
await muteLocalAudio(true);
} else {
await setSystemAudioLoopback(false);
}
};
/**
* @function 停止拉流
* @param streamId: String 流 id
* @return Promise | void
*/
const stopPlay = (streamId, recovery) => {
const id = streamIdToPreviewId[streamId];
delete streamIdRtcPlayerInfo[streamId];
if (recovery && (id || id === 0) && !uiChnIndexs.includes(id)) {
uiChnIndexs.push(id);
defaultApi.writeLog(`avsdk TALRTC::stopPlayStream streamId: ${streamId} id: ${id} uiChnIndexs: ${uiChnIndexs}`);
delete streamIdToPreviewId[streamId];
delete previewIdToStreamId[id];
}
audioPullingStore.delete(streamId);
videoPullingStore.delete(streamId);
return callMethod('StopPlay', { streamId });
};
/**
* @function 停止拉所有的流
* @return Promise | void
*/
const stopPlayAll = () => {
streamIdRtcPlayerInfo = {};
streamIdToPreviewId = {};
previewIdToStreamId = {};
uiChnIndexs.length = chnTotal;
uiChnIndexs.fill(0).forEach((value, index, array) => {
array[index] = array.length - (index + 1);
});
audioPullingStore.restore();
videoPullingStore.restore();
return callMethod('StopPlayAll', {});
};
/**
* @function 切换播放流
* @param streamId: String 需要停止流的 id,必选
* @param toStreamId: String 被拉取的流的 id,必选
* @param toDomId: String <video> 标签的 id,可选
* @return Promise | void
*/
const changePullFlow = async (streamId, toStreamId, toDomId) => {
await stopPlay(streamId);
return initPullFlow(toStreamId, toDomId);
};
/**
* @function 是否拉取音频流--通过播放器id控制
* @param mute: Boolean,必选,true -> 不拉取,false -> 拉取,必选
* @param playerId: Number,必选,播放器 id
* @return Promise | void
*/
const pullAudioFlow = (playerId, mute, streamid) => {
defaultApi.writeLog(`pullAudioFlow -- playerId ${playerId} operation ${mute} streamId ${streamid}`);
if(streamid){
if(streamIdToPreviewId[streamid] == undefined) {
NOTICE.pullAudioFlowError({streamid});
return;
}else{
playerId = streamIdToPreviewId[streamid];
defaultApi.writeLog(`pullAudioFlow-playerId ${playerId}, ${JSON.stringify(streamIdToPreviewId)}`);
}
}
let streamId = previewIdToStreamId[playerId];
try {
streamIdRtcPlayerInfo1[streamId].audio_type = !mute;
defaultApi.writeLog(
`pullAudioFlow, ${streamId} ::previewIdToStreamId', ${JSON.stringify(previewIdToStreamId)} ::streamIdRtcPlayerInfo1 ${JSON.stringify(streamIdRtcPlayerInfo1)}`);
} catch (e) {
defaultApi.writeLog(`pullAudioFlow ::streamIdRtcPlayerInfo1--error ${JSON.stringify(e)}`, null, 'error');
}
try {
dataReport.setPullVoice({
code: +!mute,
pull_uid: util.getUidByStreamId(streamId),
pull_streamid: streamId,
playerId
// operator:'client'
});
} catch (e) { };
return muteRemoteAudio(streamId, mute);
};
/**
* @function 设置镜像状态(预览)
* @param { 0 | 1 | 2 | 3 } mode 0: 预览启用镜像,推流不启用镜像; 1: 预览启用镜像,推流启用镜像; 2: 预览不启用镜像,推流不启用镜像; 3: 预览不启用镜像,推流启用镜像
* @return {Promise}
*/
const setMirrorStatus = (mode) => {
const realId = mode == 0 ? 2 : 1;
defaultApi.writeLog(`avsdk TALRTC::setMirrorStatus, mode: ${mode}, streamId:${localStreamId}, realId:${realId}`);
return callMethod('SetMirrorStatus', { mode: realId, streamId: localStreamId });
};
/**
* @function 设置拉流镜像状态
* @param { boolean } type true=开启 false=关闭
* @return: Promise
*/
const setPlayViewMirror = (isMirror, id) => {
return callMethod('SetPlayViewMirror', { isMirror, streamId: id });
};
/**
* @function 开启预览
* @param idx: Number 推流通道,可选,默认为 0
* @return Promise | void
*/
const startPreview = (idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('StartPreview', {
idx
});
};
/**
* @function 停止预览
* @param idx: Number 推流通道,可选,默认为 0
* @return Promise | void
*/
const stopPreview = (idx) => {
if (typeof idx === 'undefined') {
idx = 0;
}
return callMethod('StopPreview', {
idx
});
};
/**
* @function 不拉/拉 音频
* @param streamId: string
* @param mute: bool
* @return Promise | void
*/
const muteRemoteAudio = (streamId, mute) => {
return callMethod('MuteRemoteAudio', { streamId, mute }).then((...args) => {
const audio = audioPullingStore.getData(streamId);
if (audio) {
audio.muted = mute;
}
setStreamMuteStatus(streamId, 'audio', mute);
return args;
});
};
/**
* @function 不拉/拉 视频
* @param streamId: string
* @param mute: bool
* @return Promise | void
*/
const muteRemoteVideo = (streamId, mute) => {
return callMethod('MuteRemoteVideo', { streamId, mute }).then((...args) => {
const video = videoPullingStore.getData(streamId);
if (video) {
video.muted = mute;
}
setStreamMuteStatus(streamId, 'video', mute);
return args;
});
};
/**
* @function 设置外部采集设备模块
* @param {boolean} enable 是否开启外部采集设备模块
* @param {string} streamId
* @param {number} video_source 0:屏幕采集 1:player采集
* @attention
* @return Promise | void
*/
export const enableExternalVideoSource = (enable = false, streamId = localStreamId, video_source = 0) => {
defaultApi.writeLog('avsdk TALRTC::EnableExternalVideoSource');
return callMethod('EnableExternalVideoSource', {enable, streamId, video_source});
};
export const setFourthStreamType = (type, streamId) => {
defaultApi.writeLog(`avsdk TALRTC::SetFourthStreamType type:${type} streamId:${streamId}`);
if (!streamId) {
defaultApi.writeLog('avsdk TALRTC::SetFourthStreamType - streamId is null', null, 'error');
throw new Error('streamId is null');
}
fourthStreamId = streamId;
if (type === 6) {
fourthStreamUsingCamera = true;
setAudioType(6, streamId);
enableExternalVideoSource(false, streamId, 1);
setCurrentCameraDeviceInternal(window.zbyAVSDK_device_checker_init.camera.use, streamId);
} else if (type === 7) {
fourthStreamUsingCamera = false;
setAudioType(8, streamId);
enableExternalVideoSource(true, streamId, 1);
} else {
defaultApi.writeLog('avsdk TALRTC::SetFourthStreamType - type error', null, 'error');
throw new Error('type error');
}
};
//设置音频数据来源
const setAudioAuxSource = (source) => {
defaultApi.writeLog('avsdk TALRTC::setAudioAuxSource');
return callMethod('SetAudioAuxSource', {
source
});
};
/**
* RTC流音频类型
* @param {number} type 音频类型 0:静音 / 1:麦克风 / 2:拉流的声音 / 3:1+2 / 4:麦克风+扬声器+2 5: 扬声器 6: 麦克风+扬声器 8: player插件音频
* @param {string} streamId 流
* @returns {Promise<void>}
*/
const setAudioType = (type, streamId) => {
defaultApi.writeLog('avsdk TALRTC::SetAudioType');
if (!streamId) {
streamId = screenStreamId;
}
return callMethod('SetAudioType', {
streamId,
type
});
};
/**
*
* @param {boolean} enable
* @param {number} type
* @returns
*/
export const enableRTCAudioMixMode = (enable, type) => {
return callMethod('EnableRTCAudioMixMode', { enable, type });
};
//开启音频外部采集 参数:channel 参数类型:int 通道(传1使用辅通道)
const startAudioExCapture = (channel) => {
defaultApi.writeLog('avsdk TALRTC::startAudioExCapture');
return callMethod('StartAudioExCapture', {
channel
});
};
/**
* @function 加载采集插件的输出数据 //采集屏幕并且拼接了连麦学生头像的数据
* @returns {Promise}
*/
export const loadCollectionOutputEntry = () => {
return callMethod('LoadCollectionOutputEntry', {
streamId: localStreamId
});
};
/**
* @function 加载采集插件的输出数据 //只采集屏幕数据
* @returns {Promise}
*/
const LoadCollectionOutputEntry2 = () => {
defaultApi.writeLog('avsdk TALRTC::LoadCollectionOutputEntry2');
return callMethod('LoadCollectionOutputEntry2', {
streamId: screenStreamId
});
};
//开启同屏
const startMultiScreen = async () => {
defaultApi.writeLog('avsdk TALRTC::startMultiScreen');
// await enableExternalVideoSource(true,1);
await LoadCollectionOutputEntry2();
};
/**
* 音频增强调到最高优先级
* @param {string} streamId
* @param {string} uid
* @param {boolean} add
* @returns
*/
const setChiefAudioStream = async (streamId, uid, add = true) => {
defaultApi.writeLog('avsdk TALRTC::SetChiefAudioStream');
return callMethod('SetChiefAudioStream', {
streamId,
uid,
add
});
};
/**
* talrtc40路拉流混音接口
* @param {number} mixMode //混流模式 0=关闭混流 1=开启混流
* @param {Array<string>} streamIdArr //表示要突出声音的channel
* @returns {Promise}
*/
export const setAudioMixMode = async (mixMode, streamIdArr) => {
// let channelsArr = [];
// if(streamIdArr.length > 0){
// streamIdArr.forEach( item => {
// channelsArr.push(getChannelIndex(item));
// });
// }
if (!Array.isArray(streamIdArr)) return;
// if (!streamIdArr.includes(teacherId)) {
// streamIdArr.push(teacherId);
// }
return callMethod('SetAudioMixMode', {mixMode: mixMode, streamIdArr, num: streamIdArr.length});
};
/**
* @function 离开教室
* @return Promise | void
*/
const leaveRoom = async () => {
for (let key in streamIdToPreviewId) {
stopPlay(key, true);//huishou
}
if (screenStreamId) {
screenStreamId = null;
}
stopPush(localStreamId);
if (isSpeakerCapturing) {
setSystemAudioLoopback(false);
}
if(window.zby_sdk_init_params.role === 'teacher' && window.zby_sdk_init_params.mode === 1){
//坐立模式关闭外部采集器,站立关闭外部采集器
enableExternalVideoSource(false);
}
localStreamId = null;
screenStreamId = null;
fourthStreamId = null;
removerListener();
destroyEngine();
heartBeatDataReport('stop');
// isFirstHeartBeatReport = false;
userRTCStatus.clear();
streamIdToPreviewId = {};
previewIdToStreamId = {};
streamIdRtcPlayerInfo = {};
streamIdRtcPlayerInfo1 = {};
localStreamIdMap.length = 0;
uiChnIndexs.length = chnTotal;
uiChnIndexs.fill(0).forEach((value, index, array) => {
array[index] = array.length - (index + 1);
});
clearInterval(pullingStreamInterval);
pullingStreamInterval = -1;
};
/**
* @function 卸载 Talrtc 扩展
* @return Promise | void
*/
export const unloadTalrtc = () => {
// EM 是寄宿于端的,浏览器中并不存在,为防止报错需要先进行能力检测
if (EM) {
return new Promise((resolve, reject) => {
EM.UnLoad(
extensionId,
(code, msg) => {
defaultApi.writeLog(`unloadTalrtc Code: ${code}\nMessage: ${msg}`);
resolve();
}
);
});
}
};
/**
* @function 本地录制
* @param {string | undefined} fileName
* @param {number} fileType 1:flv 2:mp4
* @param {number} idx 0:主流 1:辅流 2:RTMP流
* @param {string} path unicode路径
* @returns {Promise<any>}
*/
export const startRecord = async (fileName, fileType, idx, path) => {
const streamId = localStreamIdMap[idx];
defaultApi.writeLog(`avsdk TALRTC::startRecord fileName: ${fileName}, fileType: ${fileType}, idx: ${idx}, streamId: ${streamId}`);
if (!streamId) {
defaultApi.writeLog(`avsdk TALRTC::startRecord - Invalid idx ${idx}`, null, 'error');
throw new Error(`Invalid idx ${idx}`);
}
if (!fileName) {
fileName = `${streamId}-${util.currentTimeString().replace(/:/g, '-')}`;
}
if (!path) {
defaultApi.writeLog(`avsdk TALRTC::startRecord path is not set, idx:${idx}`, null, 'error');
throw new Error('setRecordPath first');
}
dataReport.startRecord({fileName, fileType, idx, streamId});
return callMethod('StartRecord', {
streamId,
recordType: 3, // 1:音频 2:视频 3:音视频
fileName,
recordFormat: fileType,
path,
});
};
/**
* 结束音视频录制 channelIndex(number) 录制通道 0主推流通道/ 1辅助推流通道/ 2辅助推流通道
* @param {number} idx 0 | 1 | 2
* @returns
*/
export const stopRecord = async (idx) => {
const streamId = localStreamIdMap[idx];
defaultApi.writeLog(`avsdk TALRTC::stopRecord idx: ${idx}, streamId: ${streamId}`);
if (!streamId) {
defaultApi.writeLog(`avsdk TALRTC::stopRecord - Invalid idx ${idx}`, null, 'error');
throw new Error(`Invalid idx ${idx}`);
}
dataReport.stopRecord({idx, streamId});
return callMethod('StopRecord',{streamId});
};
export const enableBeautify = (enable, streamId = localStreamId) => {
defaultApi.writeLog(`avsdk TALRTC::enableBeautify enable: ${enable} streamId: ${streamId}`);
return callMethod('EnableBeautify', { enable, streamId });
};
/**
*
* @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 setBeautifyParam = (mode, subMode, value, streamId = localStreamId, image) => {
defaultApi.writeLog(`avsdk TALRTC::setBeautifyParam param: ${JSON.stringify({mode, subMode, value, streamId, image})}`);
return callMethod('SetBeautifyParam', { mode, subMode, value, streamId, image });
};
/**
*
* @param {'UseLoopbackDefault' | 'UseDefaultDevice'} key
* @param {'enable'} value
* @returns
*/
export const setEngineConfig = (key, value) => {
defaultApi.writeLog(`avsdk TALRTC::setEngineConfig key: ${key}, value: ${value}`);
return callMethod('SetEngineConfig', { key, value });
};
/**
* 获取麦克风响度
* @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});
};
const heartBeatRealKeys = ['video_fps', 'video_bitrate', 'audio_fps', 'audio_bitrate'];
const _heartBeatDataReport = () => {
const pullInfo = [];
console.log('拉流的类型1',streamIdRtcPlayerInfo);
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_talrtc,camera: ${zbysdk.deviceStatus.camera},microphone: ${zbysdk.deviceStatus.microphone},type: ${zbysdk.deviceStatus.stream_type},a_fps: ${dataTalrtcCapture.fps}, a_bit: ${dataTalrtcCapture.audioBitrate}, v_fps: ${dataTalrtcCapture.fps}, v_bit: ${dataTalrtcCapture.videoBitrate}`);
dataReport.heartbeat({
...{...heartBeatDataReportObj, push_type: zbysdk.deviceStatus.stream_type, volume: heartBeatDataReportObj.volume.slice(0,heartBeatDataReportObj.volume.length-1)},
pull_info: pullInfo,
});
dataReport.localNetworkQuality({
code: qualityLocal,
});
dataReport.remoteNetworkQuality({
code: qualityRemote,
});
} catch (e) {
console.log(e);
}
}
resetHeartBeatDataReportObj();
};
let pointer = 0;
const runHeartBeatCalc = () => {
Object.keys(streamIdToPreviewId).forEach((streamId) => {
const data = statsStore.getData(streamId) || { stats: JSON.stringify({ streamId })};
try {
heartBeatDataReportCalc('onRemoteStatistics', data);
} catch (error) {
defaultApi.writeLog(`avsdk TALRTC::runHeartBeatCalc error: ${error}`);
}
});
const data = statsStore.getData(localStreamId) || { stats: JSON.stringify({}) };
try {
heartBeatDataReportCalc('onLocalStatistics', data);
} catch (error) {
defaultApi.writeLog(`avsdk TALRTC::runHeartBeatCalc error: ${error}`);
}
++pointer;
if (pointer === heartBeatItemCount) {
pointer = 0;
_heartBeatDataReport();
}
};
const heartBeatDataReport = (type) => {
try {
if (type === 'start' && !heartBeatDataReportTimer) {
isFirstHeartBeatReport = true;
runHeartBeatCalc();
heartBeatDataReportTimer = setInterval(() => {
runHeartBeatCalc();
}, 2 * 1000);
}
if (type === 'stop') {
isFirstHeartBeatReport = false;
statsStore.clear();
clearInterval(heartBeatDataReportTimer);
heartBeatDataReportTimer = null;
}
} catch (error) {
console.error(error);
}
};
const lastStreamReportTimestamps = {};
const heartBeatDataReportCalc = (name, _data) => {
let _d = JSON.parse(_data.stats);
// if (lastStreamReportTimestamps[_d.streamId] && Date.now() - lastStreamReportTimestamps[_d.streamId] < 4999) {
// return;
// } else {
// lastStreamReportTimestamps[_d.streamId] = Date.now();
// }
const ctime = Math.round((new Date().getTime()+dataReport.timestamp)/1000);
// 拉流
const pullKeys = ['fps', 'videoBitrate', 'afps', 'audioBitrate'];
if (name === 'onRemoteStatistics') {
if (streamIdRtcPlayerInfo && streamIdRtcPlayerInfo.hasOwnProperty(_d.streamId)) {
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]])));
}
});
if (isReport) {
let audio_fps_talrtc_pull = 0;
let audio_bitrate_talrtc_pull = 0;
let video_fps_talrtc_pull = 0;
let video_bitrate_talrtc_pull = 0;
let pull_type = 'none';
if(streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
audio_fps_talrtc_pull = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_talrtc_pull = parseFloat(parseInt(_d.audioBitrate || 0));
video_fps_talrtc_pull = parseFloat(parseInt(_d.fps || 0));
video_bitrate_talrtc_pull = parseFloat(parseInt(_d.videoBitrate || 0));
pull_type = 'both';
} else if(!streamIdRtcPlayerInfo1[streamid].audio_type && streamIdRtcPlayerInfo1[streamid].video_type) {
video_fps_talrtc_pull = parseFloat(parseInt(_d.fps || 0));
video_bitrate_talrtc_pull = parseFloat(parseInt(_d.videoBitrate || 0));
pull_type = 'video';
} else if(streamIdRtcPlayerInfo1[streamid].audio_type && !streamIdRtcPlayerInfo1[streamid].video_type) {
audio_fps_talrtc_pull = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_talrtc_pull = parseFloat(parseInt(_d.audioBitrate || 0));
pull_type = 'audio';
}
streamIdRtcPlayerInfo[streamid].audio_fps[pointer] = audio_fps_talrtc_pull;
streamIdRtcPlayerInfo[streamid].audio_bitrate[pointer] = audio_bitrate_talrtc_pull;
streamIdRtcPlayerInfo[streamid].video_fps[pointer] = video_fps_talrtc_pull;
streamIdRtcPlayerInfo[streamid].video_bitrate[pointer] = video_bitrate_talrtc_pull;
streamIdRtcPlayerInfo[streamid].pull_loss[pointer] = _d.packetLoss || 0;
streamIdRtcPlayerInfo[streamid].pull_delay[pointer] = _d.rtt || 0;
streamIdRtcPlayerInfo[streamid].pull_type_ex[pointer] = pull_type;
streamIdRtcPlayerInfo[streamid].ctime[pointer] = ctime;
streamIdRtcPlayerInfo[streamid].video_ifg += _d.videoBlockRate || 0;
streamIdRtcPlayerInfo[streamid].audio_ifg += _d.audioBlockRate || 0;
// console.log('hsg_tpull_keys',JSON.parse(JSON.stringify(streamIdRtcPlayerInfo[streamid])),_d.avTimestampDiff);
if (lecturerStreamId === String(streamid).toUpperCase()) {
if (audio_fps_talrtc_pull + audio_bitrate_talrtc_pull > 0 && // 任意一个有大于0的值
(audio_fps_talrtc_pull === 0 || audio_bitrate_talrtc_pull === 0) // 有一个为0
) {
defaultApi.writeLog(`avsdk TALRTC::AUDIO_STATS_WARNING ${_data.stats}`, undefined, 'error');
}
if (video_fps_talrtc_pull + video_bitrate_talrtc_pull > 0 && // 任意一个有大于0的值
(video_fps_talrtc_pull === 0 || video_bitrate_talrtc_pull === 0) // 有一个为0
) {
defaultApi.writeLog(`avsdk TALRTC::VIDEO_STATS_WARNING ${_data.stats}`, undefined, 'error');
}
}
}
}
}
// 推流
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_talrtc_push = 0;
let audio_bitrate_talrtc_push = 0;
let video_fps_talrtc_push = 0;
let video_bitrate_talrtc_push = 0;
dataTalrtcCapture =_d;
let push_type = 'none';
if(userAndDeviceStatusInfo.isPushingFlow) {
if(zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
audio_fps_talrtc_push = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_talrtc_push = parseFloat(parseInt(_d.audioBitrate || 0));
video_fps_talrtc_push = parseFloat(parseInt(_d.fps || 0));
video_bitrate_talrtc_push= parseFloat(parseInt(_d.videoBitrate || 0));
push_type = 'both';
} else if(!zbysdk.deviceStatus.camera && zbysdk.deviceStatus.microphone) {
audio_fps_talrtc_push = parseFloat(parseInt(_d.afps || 0));
audio_bitrate_talrtc_push = parseFloat(parseInt(_d.audioBitrate || 0));
push_type = 'audio';
} else if(zbysdk.deviceStatus.camera && !zbysdk.deviceStatus.microphone) {
video_fps_talrtc_push = parseFloat(parseInt(_d.fps || 0));
video_bitrate_talrtc_push = parseFloat(parseInt(_d.videoBitrate || 0));
push_type = 'video';
}
if (zbysdk.deviceStatus.microphone && audio_fps_talrtc_push === 0 && audio_bitrate_talrtc_push === 0) {
defaultApi.writeLog(`avsdk TALRTC::AUDIO_PUSH_STATS_WARNING ${_data.stats}`, undefined, 'error');
}
if (zbysdk.deviceStatus.camera && video_fps_talrtc_push === 0 && video_bitrate_talrtc_push === 0) {
defaultApi.writeLog(`avsdk TALRTC::VIDEO_PUSH_STATS_WARNING ${_data.stats}`, undefined, 'error');
}
}
heartBeatDataReportObj.audio_fps[pointer] = audio_fps_talrtc_push;
heartBeatDataReportObj.audio_bitrate[pointer] = audio_bitrate_talrtc_push;
heartBeatDataReportObj.video_fps[pointer] = video_fps_talrtc_push;
heartBeatDataReportObj.video_bitrate[pointer] = video_bitrate_talrtc_push;
heartBeatDataReportObj.push_loss[pointer] = _d.packetLoss || 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 = () => {
console.log('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: '',
};
};
resetHeartBeatDataReportObj();
//记录拉流类型,用作数据上报
const resetStreamIdRtcPlayerInfo1 = (streamId) => {
streamIdRtcPlayerInfo1[streamId] = {
audio_type: false,
video_type: false,
stream_type: 'none'
};
// console.log('hsgshgs_heartbeat',streamIdRtcPlayerInfo1);
};
//拉流字段
const resetStreamIdRtcPlayerInfo = (streamId) => {
console.log('reset stream info ----------', streamId);
streamIdRtcPlayerInfo[streamId] = {
pull_uid: util.getUidByStreamId(streamId),
streamid: streamId,
ctime: new Array(heartBeatItemCount).fill(0),
pull_type: streamIdRtcPlayerInfo1[streamId].stream_type,
volume: '',
// 平均值
// count: 0,
// video_fps: 0,
// video_bitrate: 0,
// audio_fps: 0,
// audio_bitrate: 0,
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 {
checkUpdate,
init,
enableExternalVideoSource,
setFourthStreamType,
setCameraCaptureResolution,
setCameraEncodeResolution,
setCameraEncodeFps,
setCameraEncodeBitrate,
getCameraResolution,
getMicrophoneDeviceList,
setMicrophoneDevice,
openOrCloseMicrophone,
getCurrentMicrophoneVolume,
setCurrentMicrophoneVolume,
getCameraDeviceList,
setCameraDevice,
openOrCloseCamera,
getSpeakerDeviceList,
setSpeakerDevice,
getCurrentSpeakerDevice,
getCurrentSpeakerVolume,
setCurrentSpeakerVolume,
setSpeakerDeviceMute,
muteRemoteAudio,
muteRemoteVideo,
getSpeakerSimpleVolume,
setSpeakerSimpleVolume,
setSpeakerSimpleMute,
startLocalOrRemotePreview,
enableAudioSpeakerCapture,
startPush,
stopPush,
startPlay,
stopPlay,
initPullFlow,
stopPlayAll,
changePullFlow,
startPreview,
stopPreview,
sendSEIMsg,
loadCollectionInputEntry2,
controlCdnStreaming, // addPublishRtmpStreamUrl or removePublishStreamUrl
leaveRoom,
destroyEngine,
unloadTalrtc,
muteLocalVideo,
muteLocalAudio,
pullAudioFlow,
hasStream,
teacherStartLinkMic,
teacherStopLinkMic,
setMirrorStatus,
setPlayViewMirror,
setAudioAuxSource,
setAudioType,
enableRTCAudioMixMode,
startAudioExCapture,
startMultiScreen,
setAudioMixMode,
startRecord,
stopRecord,
enableBeautify,
setBeautifyParam,
setEngineConfig,
getChannelIndex,
getSDKVersion,
getSDKCoreVersion,
getMicrophoneLoudness,
getCurrentSpeakerId,
getCurrentMicrophoneId,
getCurrentCameraId,
takeLocalSnapShot,
takeRemoteSnapShot,
};