/**
* 引擎模块, rtc系统的入口。
* @module AVDEngine
* @author roymond.wang
* @since 2015-09-01
*
* @example
* var AVDEngine = ModuleBase.use(ModulesEnum.avdEngine);
*/
var log = log4javascript.getLogger();
var avdEngineHandle;
ModuleBase.define("AVDEngine", ["Room", "RestServer", "Error", "BrowserDetect", "StreamExporterManager","ElectronManager","RtmManager","InviteManager"], function(Room, RestServer, Error, BrowserDetect, StreamExporterManager,ElectronManager,RtmManager,InviteManager) {
/**
* AVDEngine构造函数,实例化avdEngine 对象,用于获取摄像头列表、麦克风列表、检查浏览器兼容性、控制日志等级及日志保存等功能。
* @constructor
* @alias AVDEngine
*
* @example
* var avdEngine = new AVDEngine();
*/
var AVDEngine = function() {
this.avdVersion = "4.0.8.6";
this.accessToken;
this.clientIsUseCascade = false; //是否级联加入会议
// this.errMsgi18n = 'en'; err默认语言英文
this.restServerInfo = {
mcuServerUrl: null,
clusterRestAddr: null,
roomServerUrl: null,
iceServers: null,
stunUrl: null,
trunUrl: null,
iceusername: null,
iceCredential: null,
isCluster: false,
protocolStr: document.location.protocol,
notQueryGetMcu:false,
masterVersion: null,
masterPackageVersion: null,
boxVersion: null,
boxPackageVersion: null,
signalData: null,
iv: '0000000000000000'
}
if(this.restServerInfo.protocolStr == "httpsionic:"){
this.restServerInfo.protocolStr = "https:";
}
if(this.restServerInfo.protocolStr != "http:" && this.restServerInfo.protocolStr != "https:" && this.restServerInfo.protocolStr != "httpsionic:"){
this.restServerInfo.protocolStr = "http:";
}
this.loggerReportConfig = null; //终端日志上报设置
this.loggerReport = null; //终端日志上报指示器
this.logReportWait = []; //终端日志上报前缓存的内容
this.connectionStatisticsConfig = null; //媒体统计设置
this.browserDetect;
this.levelValue = log4javascript.Level.INFO.level;
this.appSign = null;
this.appFunName = null;
this.bugout = null;
this.supperStrongWeakNetWork = false; //当前服务器版本是否支持强弱网抗丢包
this.videoCoding = VideoCodingType.H264; //视频编码格式,默认值为H264
this.audioCoding = AudioCodingType.opus; //音频编码格式,默认值为opus
this.audioAutoSub = true; //音频自动订阅,默认值为true
this.audioBatchEnabled = false; //音频自动订阅时,对多路音频是否开启批量订阅处理,可以只进行一次SDP交互以提高性能,默认值为false
this.enableEchoCancellation = true; //是否开启回声消除,默认值为true
this.enableNoiseSuppression = true; //是否开启降噪,默认值为true
this.enableAutoGainControl = true; //是否开启自动增益控制,默认值为true
this.simulcastEnabled = false; //是否开启多流,默认值为false
this.dataCrypto = DataCryptoType.AES128; //数据加密类型,默认值为AES128
this.mediaConnectType = 0; //0 udp优先; 1 tcp优先
this.customerTokenEnabled = false; //是否开启客户自己的认证token,默认值为false
this.restfulRequestType = 0; //RestFul API请求类型. 0: GET; 1: POST
this.cameraMap = {};
this.microphoneMap = {};
this.speakerMap = {}; //扬声器
this.hasCamera = false;
this.hasMicrophone = false;
this.hasSpeaker = false;
this.checkVideoStream = null;
this.checkAudioStream = null;
this.checkAudioLocalStatsCollector = null;
this.checkMicrophoneId = null;
this.checkMicrophoneName = null;
this.mcuClusterRouteParams = null; //分布式集群部署中,加入房间时mcu服务器的分配路由参数,填写格式如:{ip_tag":"local","idc_code":"idc_code"}
this.defaultVideoParamsWidth = 640;
this.defaultVideoParamsHeight = 480;
this.defaultVideoParamsResolutionSetType = ResolutionSetType.ideal;
this.defaultVideoParamsFrameRate = 20;
this.defaultScreenParamsWidth = 1920;
this.defaultScreenParamsHeight = 1080;
this.defaultScreenParamsFrameRate = 10;
this.defaultMediaPublishKeepAliveTime = 15000; //媒体流发布的保活时长(单位为毫秒),默认为15000毫秒即30秒
this.detect = this.getBrowserDetect();
this.browserName = this.detect.browser.name;
this.fullVersion = this.detect.browser.fullVersion;
this.majorVersion = this.fullVersion.split(".")[0];
this.browserOsName = this.detect.osName;
this.isChrome = this.detect.isChrome;
this.isOpera = this.detect.isOpera;
this.isIE = this.detect.isIE;
this.isFirefox = this.detect.isFirefox;
this.isSafari = this.detect.isSafari;
this.isMicroMessenger = this.detect.isMicroMessenger;
this.isUCBrowser = this.detect.isUCBrowser;
//当前客户端的资源
this.clientResource = new Object();
this.restFullProtocol = null;
this.traceId = null;
//Electron应用参数设置
this.applyToElectron = false;
this.electronProtocol = "http:";
this.electronManager = null;
this.appender = new log4javascript.BrowserConsoleAppender();
this.appender.setThreshold(log4javascript.Level.INFO);
appenderLayout(this.appender);
this.loggerReport = new LoggerReport();
this.eventEmitter = new EventEmitter();
this.deviceChangeListener();
this.rooms = [];
this.eraserCustomIconPath = '';
this.ignoreModifyResourceReq = false;
this.enableHardwareEncoding = false;
//Safari 15.1.X使用H264 编码时会导致页面崩溃, 默认值调整了为VP8
if(this.isSafari && this.fullVersion && this.fullVersion.split(".")[0] =='15' && this.fullVersion.split(".")[1]=='1'){
this.videoCoding = VideoCodingType.VP8;
}
// Android Chrome/Android微信支持vp8、不支持h264解码,默认值调整为VP8
if((this.isChrome && this.detect.osName == 'Android') || (this.isMicroMessenger && this.detect.osName == 'Android')){
log.debug("===avdEngine, Android Chrome or Android MM detected, set default video codec to VP8")
this.videoCoding = VideoCodingType.VP8;
}
log.info(`===avdEngine, browserName:${this.browserName}, fullVersion:${this.fullVersion}, checkBrowserSupport:${this.checkBrowserSupport()}, GetUserMedia:${this.detect.getUserMediaSupport}, RTCPeerConnection:${this.detect.RTCPeerConnectionSupport}, WebSocket:${this.detect.WebSocketSupport}`);
avdEngineHandle = this;
};
/**
* 获取当前rtc web SDK的版本号
* @returns {String} - 版本号
*
* @example
* var Version = avdEngine.getVersion();
*/
AVDEngine.prototype.getVersion = function() {
var version = "rtc-" + this.avdVersion;
return version;
};
/**
* 引擎级别的回调
* @param {EngineCallback} type - 回调枚举标识, 包括EngineCallback.device_microphone_changet 和 device_camera_change.
* @param {Object} callback - 回调方法名,可以自定义,建议保持默认值
* @example
* avdEngine.addCallback(EngineCallback.device_microphone_change, onMicrophoneDeviceChangeHandle)
* avdEngine.addCallback(EngineCallback.device_camera_change, onCameraDeviceChangeHandle)
*
* //param {String} changeType - 设备变化类型,可以为"add"、"remove"
* //param {Array} deviceIdList - 变动的麦克风设备ID列表
* function onMicrophoneDeviceChangeHandle(changeType, deviceIdList) {}
*
* //param {String} changeType - 设备变化类型,可以为"add"、"remove"
* //param {Array} deviceIdList - 变动的摄像头设备ID列表
* function onCameraDeviceChangeHandle(changeType, deviceIdList) {}
*/
AVDEngine.prototype.addCallback = function(type, callback) {
this.eventEmitter.addListener(type, callback);
};
/**
* @desc 获取avdEngine的err语言
* @returns {String} - 语言
*
* @example
* var Version = avdEngine.getLanguage();
*/
// AVDEngine.prototype.getErrLanguage = function() {
// var language = "rtc-errLanguage-" + this.errMsgi18n;
// return language;
// };
/**
* @desc 设置avdEngine的err语言
* @returns {String} - 语言
*
* @example
* var Version = avdEngine.getLanguage();
*/
// AVDEngine.prototype.setErrLanguage = function(lang) {
// this.errMsgi18n = lang
// var language = "rtc-errLanguage-" + this.errMsgi18n;
// return language;
// };
/**
* @desc Electron开发应用时,设置应用的协议
* @param {Boolean} - islectron true为应用于Electron
* @param {String} - protocolStr Electron应用时的协议,可不填,默认为http:
*/
AVDEngine.prototype.setApplyToElectron = function(islectron,protocolStr){
log.info(`===avdEngine.setApplyToElectron(), islectron:${islectron}, protocolStr:${protocolStr}`);
this.logReportWait.push(`avdEngine.setApplyToElectron(), islectron:${islectron}, protocolStr:${protocolStr}`);
if(islectron){
this.applyToElectron = islectron;
}
if(protocolStr){
this.electronProtocol = protocolStr;
this.restServerInfo.protocolStr = protocolStr;
}
};
/**
* @desc 设置RestFull访问时应用的协议。 不设置默认取document.location.protocol
* @param {String} - protocolStr RestFull访问应用时的协议,可填写如https:
*/
AVDEngine.prototype.setRestFullProtocol = function(protocolStr){
log.info(`===avdEngine.setRestFullProtocol(),protocolStr:${protocolStr}`);
if(protocolStr){
this.restFullProtocol = protocolStr;
}
}
/**
* @desc 获取ElectronManager对象
*
* @example
* var electronManager = avdEngine.getElectronManager();
*
*/
AVDEngine.prototype.getElectronManager= function() {
this.electronManager = new ElectronManager();
return this.electronManager;
}
/**
* @desc 设置视频是否开启多流,默认值为false
* @param {Boolean} - isSimulcast false为不开启,true为开启
*
* @example
* avdEngine.setSimulcastEnabled(true);
*/
AVDEngine.prototype.setSimulcastEnabled = function(isSimulcast) {
if(this.isChrome && this.majorVersion<73){
this.simulcastEnabled = false;
log.info("===avdEngine.setSimulcastEnabled(),当前Chromer版本小于73,不支持视频多流功能");
this.logReportWait.push("avdEngine.setSimulcastEnabled(),当前Chromer版本小于73,不支持视频多流功能");
}else if(this.isFirefox){ //firefox多流时,订阅端设置为中高视频等级时会看不到视频。目前没有找到原因,暂时firefox禁用多流。
this.simulcastEnabled = false;
log.info("===avdEngine.setSimulcastEnabled(),当前为firefox浏览器,不支持视频多流功能");
}else {
this.simulcastEnabled = isSimulcast;
log.info(`===avdEngine.setSimulcastEnabled(),设置视频是否开启多流:${isSimulcast}`);
this.logReportWait.push(`avdEngine.setSimulcastEnabled(),设置视频是否开启多流:${isSimulcast}`);
}
}
/**
* 获取视频是否开启多流的状态值
*/
AVDEngine.prototype.getSimulcastEnabled = function() {
return this.simulcastEnabled;
}
/**
* @desc 音频自动订阅时,设置对多路音频批量订阅处理,这样可以只进行一次SDP交互以提高性能,默认值为false,
* @param {Boolean} - isAudioBatch false为不开启,true为开启
*
* @example
* avdEngine.setAudioBatchEnabled(true);
*/
AVDEngine.prototype.setAudioBatchEnabled = function(isAudioBatch) {
this.audioBatchEnabled = isAudioBatch;
log.info(`===avdEngine.setAudioBatchEnabled(),音频自动订阅时,多路音频是否开启批量订阅处理:${isAudioBatch}`);
this.logReportWait.push(`avdEngine.setAudioBatchEnabled(),音频自动订阅时,多路音频是否开启批量订阅处理:${isAudioBatch}`);
}
/**
* 获取音频自动订阅时,对多路音频批量订阅处理的状态值
*/
AVDEngine.prototype.getAudioBatchEnabled = function() {
return this.audioBatchEnabled;
}
/**
* @desc 设置是否开启客户自己的认证token,不调接口设置时,默认值为不开启
* @param {Boolean} - customerTokenEnabled false为不开启,true为开启
*
* @example
* avdEngine.setCustomerTokenEnabled(true);
*/
AVDEngine.prototype.setCustomerTokenEnabled = function(customerTokenEnabled) {
this.customerTokenEnabled = customerTokenEnabled;
log.info(`===avdEngine.setCustomerTokenEnabled(), customerTokenEnabled:${customerTokenEnabled}`);
this.logReportWait.push(`avdEngine.setCustomerTokenEnabled(), customerTokenEnabled:${customerTokenEnabled}`);
}
/**
* 获取当前是否开启客户自己的认证token状态值
*/
AVDEngine.prototype.getCustomerTokenEnabled = function() {
return this.customerTokenEnabled;
}
/**
* @desc 设置RESTful API 请求类型
* @param {int} requestType, 0 表示GET; 1表示 POST,默认为0
* @example
* avdEngine.setRestfulRequestType(1);
*/
AVDEngine.prototype.setRestfulRequestType = function(requestType) {
this.restfulRequestType = requestType;
log.info(`===avdEngine.setRestfulRequestType(), 设置RESTful API 请求类型: ${this.restfulRequestType}`);
this.logReportWait.push(`avdEngine.setRestfulRequestType(), 设置RESTful API 请求类型: ${this.restfulRequestType}`);
}
/**
* @desc 获取RESTful API 请求类型,0 表示GET; 1表示 POST,默认为0
* @example
* avdEngine.getRestfulRequestType();
*/
AVDEngine.prototype.getRestfulRequestType = function() {
return this.restfulRequestType;
}
/**
* @desc 获取浏览器检测结果
* @returns {Object} - 浏览器检测结果对象
*
* @example
* var detect = avdEngine.getBrowserDetect();
*
* detect.browser.name: 浏览器内核名称
* detect.browser.fullVersion: 内核版本
* detect.browser.isCustomized: 是否是定制外壳
* detect.browser.shellFullVersion:外壳版本
* detect.osName: 操作系统名称
* detect.getUserMediaSupport:是否支持webrtc的GetUserMedia
* detect.RTCPeerConnectionSupport:是否支持webrtc的RTCPeerConnection
* detect.dataChannelSupport: 是否支持webrtc的DataChannel
* detect.WebSocketSupport: 是否支持WebSocket
* detect.screenSharingSupport: 是否支持屏幕共享(chrome42以上及https访问)
* detect.h264Support: 是否支持H264(chrome52及以上)
*/
AVDEngine.prototype.getBrowserDetect = function() {
if(!this.browserDetect){
this.browserDetect = new BrowserDetect();
}
return this.browserDetect.detect;
}
/**
* @desc 检测当前浏览器是否支持rtc能力,目前仅支持Chrome内核的浏览器、firefox和safair.
* @returns {boolean} - 是否支持, false:不支持; true:支持
* @example
* var browserSupport = avdEngine.checkBrowserSupport();
*/
AVDEngine.prototype.checkBrowserSupport = function() {
if(!this.browserDetect){
this.browserDetect = new BrowserDetect();
}
return this.browserDetect.checkBrowserSupport();
};
/**
* @desc 检测当前浏览器是否通过插件支持rtc能力
* @returns {boolean} - 是否支持, false:不支持; true:支持
*
* @example
* var browserPluginSupport = avdEngine.checkBrowserPluginSupport();
*/
AVDEngine.prototype.checkBrowserPluginSupport = function() {
if(!this.browserDetect){
this.browserDetect = new BrowserDetect();
}
return this.browserDetect.checkBrowserPluginSupport();
};
/**
* @desc 设置日志显示方式和日志级别
* @param {Appender} appenderModel - 日志显示方式(枚举型), 默认值为Appender.browserConsole
* @param {LogLevel} logLevel - 日志级别(枚举型),默认值为LogLevel.info
*
* @example
* avdEngine.setLog(Appender.browserConsole, LogLevel.debug);
*/
AVDEngine.prototype.setLog = function(appenderModel, logLevel) {
//var appender;
if(appenderModel == Appender.alert) {
this.appender = new log4javascript.AlertAppender();
} else if(appenderModel == Appender.inpage) {
this.appender = new log4javascript.InPageAppender();
} else if(appenderModel == Appender.popup) {
this.appender = new log4javascript.PopUpAppender();
} else if(appenderModel == Appender.browserConsole) {
//this.appender = new log4javascript.BrowserConsoleAppender();
}
if(logLevel == LogLevel.all) {
this.levelValue = log4javascript.Level.ALL.level;
this.appender.setThreshold(log4javascript.Level.ALL);
} else if(logLevel == LogLevel.trace) {
this.levelValue = log4javascript.Level.TRACE.level;
this.appender.setThreshold(log4javascript.Level.TRACE);
} else if(logLevel == LogLevel.debug) {
this.levelValue = log4javascript.Level.DEBUG.level;
this.appender.setThreshold(log4javascript.Level.DEBUG);
} else if(logLevel == LogLevel.info) {
this.levelValue = log4javascript.Level.INFO.level;
this.appender.setThreshold(log4javascript.Level.INFO);
} else if(logLevel == LogLevel.warn) {
this.levelValue = log4javascript.Level.WARN.level;
this.appender.setThreshold(log4javascript.Level.WARN);
} else if(logLevel == LogLevel.error) {
this.levelValue = log4javascript.Level.ERROR.level;
this.appender.setThreshold(log4javascript.Level.ERROR);
} else if(logLevel == LogLevel.fatal) {
this.levelValue = log4javascript.Level.FATAL.level;
this.appender.setThreshold(log4javascript.Level.FATAL);
} else if(logLevel == LogLevel.off) {
this.levelValue = log4javascript.Level.OFF.level;
this.appender.setThreshold(log4javascript.Level.OFF);
}
appenderLayout(this.appender);
};
/**
* @desc 设置日志的可输出下载地址
* @param {String} logFilename - 输出下载地址
*
* @example
* avdEngine.setBugout(avdlog.txt);
*/
AVDEngine.prototype.setBugout = function(logFilename) {
this.bugout = new debugout;
this.bugout.realTimeLoggingOn = false;
this.bugout.useTimestamps = true;
if(logFilename){
this.bugout.logFilename = logFilename;
log.info(`===avdEngine.setBugout(), logFilename: ${logFilename}`);
this.logReportWait.push(`avdEngine.setBugout(), logFilename: ${logFilename}`);
}
};
/**
* 日志内容通过应用层提供的函数进行上报
* @param {string} appSign -应用层设置的标识,用于区别某用户某一次加入会议所产生的所有日志
* @param {Object} appFunName -应用层设置的方法函数,SDK调用该函数上报日志内容
*/
AVDEngine.prototype.setReportLogger = function(appSign, appFunName) {
if(typeof appSign !== 'string'){
log.error("===avdEngine.setReportLogger(),appSign setting invalid");
return;
}
if(typeof appFunName !== 'function'){
log.error("===avdEngine.setReportLogger(),appFunName setting invalid");
return;
}
this.appSign = appSign;
this.appFunName = appFunName;
}
/**
* @desc 设置视频编码格式
* @async
* @param {VideoCodingType} videoCodingType - 视频编码格式(枚举型),默认值为videoCodingType.H264
*
* @example
* avdEngine.setVideoCoding(VideoCodingType.VP8).then(function(){}).otherwise(function(error){});
*/
AVDEngine.prototype.setVideoCoding = function(videoCodingType) {
var deferred = when.defer();
if(typeof(videoCodingType) == "undefined"){
log.error(`===avdEngine.setVideoCoding(), 设置视频编解码失败。videoCodingType: ${videoCodingType}`);
var err = new Error(ErrorConstant.set_videCoding_failed);
deferred.reject(err);
}else{
var checkErr = false;
var showMsg = "";
//Safari 15.1.X使用H264 编码时会导致页面崩溃
if(this.isSafari && this.fullVersion && this.fullVersion.split(".")[0] =='15' && this.fullVersion.split(".")[1]=='1'){
if(videoCodingType == VideoCodingType.H264){
checkErr = true;
// showMsg = "Safari 15.1.X使用H264 编码时会导致页面崩溃, 建议设置为VP8";
showMsg = "In Safari 15.1.X, H264 encoding may cause page crash. Therefore, you are advised to set it to VP8";
}
}
if(videoCodingType == VideoCodingType.H265){
if(this.isChrome && this.majorVersion<136){
checkErr = true;
showMsg = "Chrome versions below 136 do not support H265";
}else{
log.warn(`===avdEngine.setVideoCoding(),Chrome 136+supports H265 video encoding, provided that your local computer CPU or discrete graphics card supports H.265 hard decoding and the driver is installed correctly, Chrome can play H.265 videos.`);
}
}
if(checkErr){
log.error(`===avdEngine.setVideoCoding(), 设置视频编解码失败. ${showMsg}`);
var err = new Error(ErrorConstant.set_videCoding_failed);
err.message = err.message+"," + showMsg;
deferred.reject(err);
}else{
this.videoCoding = videoCodingType;
log.info(`===avdEngine.setVideoCoding(), 设置视频编解码: ${this.videoCoding}`);
this.logReportWait.push(`avdEngine.setVideoCoding(), 设置视频编解码: ${this.videoCoding}`);
deferred.resolve();
}
}
return deferred.promise;
}
/**
* @desc 获取当前的视频编码格式
*
* @example
* avdEngine.getVideoCoding();
*/
AVDEngine.prototype.getVideoCoding = function() {
return this.videoCoding;
}
/**
* @desc 设置音频编码格式
* @async
* @param {AudioCodingType} audioCodingType - 音频编码格式(枚举型),默认值为AudioCodingType.opus
*
* @example
* avdEngine.setAudioCoding(AudioCodingType.opus).then(function(){}).otherwise(function(error){});
*/
AVDEngine.prototype.setAudioCoding = function(audioCodingType) {
var deferred = when.defer();
if(typeof(audioCodingType) == "undefined"){
log.error(`===avdEngine.setAudioCoding(), 设置音频编解码失败。audioCodingType: ${audioCodingType}`);
var err = new Error(ErrorConstant.set_audioCoding_failed);
deferred.reject(err);
}else{
var checkErr = false;
var showMsg = "";
//Chrome 110版本开始不再支持ISAC
if(this.isChrome && this.majorVersion >= 110){
if(audioCodingType == AudioCodingType.ISAC_16k || audioCodingType == AudioCodingType.ISAC_32k){
checkErr = true;
showMsg = "Chrome version 110 no longer supports ISAC";
}
}
//Firefox不支持ISAC
if(this.isFirefox){
if(audioCodingType == AudioCodingType.ISAC_16k || audioCodingType == AudioCodingType.ISAC_32k){
checkErr = true;
showMsg = "Firefox no longer supports ISAC";
}
}
//Safari不支持ISAC_32K
if(this.isSafari){
if(audioCodingType == AudioCodingType.ISAC_32k){
checkErr = true;
showMsg = "Safari no longer supports ISAC_32K";
}
}
if(checkErr){
log.error(`===avdEngine.setAudioCoding(), 设置音频编解码失败. ${showMsg}`);
var err = new Error(ErrorConstant.set_audioCoding_failed);
err.message = err.message+"," + showMsg;
deferred.reject(err);
}else{
this.audioCoding = audioCodingType;
log.info(`===avdEngine.setAudioCoding(), 设置音频编解码: ${this.audioCoding}`);
this.logReportWait.push(`avdEngine.setAudioCoding(), 设置音频编解码: ${this.audioCoding}`);
deferred.resolve();
}
}
return deferred.promise;
}
/**
* @desc 获取当前的音频编码格式
*
* @example
* avdEngine.getAudioCoding();
*/
AVDEngine.prototype.getAudioCoding = function() {
return this.audioCoding;
}
/**
* @desc 设置音频订阅方式是否为自动订阅,默认值为true
* @param {Boolean} isAudioAutodSub true为自动订阅,flase为手动订阅
*
* @example
* avdEngine.setAudioAutoSub(false);
*/
AVDEngine.prototype.setAudioAutoSub = function(isAudioAutoSub) {
this.audioAutoSub = isAudioAutoSub;
log.info(`===avdEngine.setAudioAutoSub(), 设置音频自动订阅: ${isAudioAutoSub}`);
this.logReportWait.push(`avdEngine.setAudioAutoSub(), 设置音频自动订阅: ${isAudioAutoSub}`);
}
/**
* @desc 获取当前的音频的订阅方式,true为自动订阅,flase为手动订阅
*
* @example
* avdEngine.getAudioAutoSub();
*/
AVDEngine.prototype.getAudioAutoSub = function() {
return this.audioAutoSub;
}
/**
* @desc 设置媒体通道协议
* @param {int} mediaType, 0 表示udp优先; 1表示 tcp优先,默认为0
*
* @example
* avdEngine.setMediaConnectType(1);
*/
AVDEngine.prototype.setMediaConnectType = function(mediaType) {
this.mediaConnectType = mediaType;
log.info(`===avdEngine.setMediaConnectType(), 设置媒体通道协议: ${this.mediaConnectType}`);
this.logReportWait.push(`avdEngine.setMediaConnectType(), 设置媒体通道协议: ${this.mediaConnectType}`);
}
/**
* @desc 获取媒体通道协议,0 表示udp优先; 1表示 tcp优先,默认为0
* @example
* avdEngine.getMediaConnectType();
*/
AVDEngine.prototype.getMediaConnectType = function() {
return this.mediaConnectType;
}
/**
* @desc 设置数据加密类型
* @param {DataCryptoType} dataCryptoType - 数据加密类型(枚举型),默认值为DataCryptoType.AES128
*
* @example
* avdEngine.setDataCryptoType(DataCryptoType.AES256);
*/
AVDEngine.prototype.setDataCryptoType = function(dataCryptoType){
if(typeof(dataCryptoType) == "undefined"){
log.error(`===avdEngine.setDataCryptoType(), 设置数据加密类型失败。dataCryptoType: ${dataCryptoType}`);
}else{
this.dataCrypto = dataCryptoType;
log.info(`===avdEngine.setDataCryptoType(), 设置数据加密类型: ${this.dataCrypto}`);
this.logReportWait.push(`avdEngine.setDataCryptoType(), 设置数据加密类型: ${this.dataCrypto}`);
}
}
/**
* @desc 获取当前的数据加密类型
*
* @example
* avdEngine.getDataCryptoType();
*/
AVDEngine.prototype.getDataCryptoType = function() {
return this.dataCrypto;
}
/**
* @desc 获取摄像头分辨率枚举,该枚举值只是罗列了主流的分辨率,而非真实的当前摄像头支持的分辨率集。
*
* @example
* avdEngine.getResolutionEnum().then(fillResolutionElement);
*
* function fillResolutionElement(resolutionEnum){
* for(var key in resolutionEnum){
* var resolutionObject = resolutionEnum[key];
* var value = resolutionObject.width +" X " + resolutionObject.height;
* }
* }
*/
AVDEngine.prototype.getResolutionEnum = function() {
var deferred = when.defer();
deferred.resolve(Resolution);
return deferred.promise;
}
/**
* @desc 在分布式集群部署中,设置加入房间时mcu服务器的路由参数分配
* @param {Object} mcuClusterRouteParams
* 参数值格式参考如:{ip_tag":"local","idc_code":"idc_code"}
* ip_tag:对应于 rtc_node_addr 中的tag标志,用于区分同一台服务器的多网卡地址,可以自定义,然后在参数中传入。
* 举例的话,比如 'local','internal','dianxin','liantong'
* idc_code:对应于 rtc_node 中的 idc_code标志,用于区分不同的服务器,唯一,可以自定义。
* 比如北京服务器设置为'beijing',杭州的设置为'hangzhou',然后在参数中传入,用于定位到服务器。
*/
AVDEngine.prototype.setMcuClusterRouteParams = function(mcuClusterRouteParams) {
this.mcuClusterRouteParams = mcuClusterRouteParams;
}
/**
* @desc 设置视频相关的默认参数值
* @param {int} width 分辨率宽,默认值为640
* @param {int} height 分辨率高,默认值为480
* @param {Object} resolutionSetType 分辨率设置类型枚举,默认值为强制即ResolutionSetType.ideal
* @param {int} frameRate 视频帧率,默认值为20
*
* @example
* avdEngine.setDefaultVideoParams(1080,720,ResolutionSetType.ideal,25);
*/
AVDEngine.prototype.setDefaultVideoParams = function(width,height,resolutionSetType,frameRate) {
if(width){
this.defaultVideoParamsWidth = width;
}
if(height){
this.defaultVideoParamsHeight = height;
}
if(resolutionSetType){
this.defaultVideoParamsResolutionSetType = resolutionSetType;
}
if(frameRate){
this.defaultVideoParamsFrameRate = frameRate;
}
log.info(`===avdEngine.setDefaultVideoParams(), 设置视频相关的默认参数值, 分辨率宽: ${width}, 分辨率高: ${height}, 分辨率设置类型: ${resolutionSetType}, 视频帧率: ${frameRate}`);
this.logReportWait.push(`avdEngine.setDefaultVideoParams(), 设置视频相关的默认参数值, 分辨率宽: ${width}, 分辨率高: ${height}, 分辨率设置类型: ${resolutionSetType}, 视频帧率: ${frameRate}`);
}
/**
* @desc 设置桌面共享相关的默认参数值
* @param {int} maxWidth 分辨率最大宽度,默认值为1920
* @param {int} maxHeight 分辨率最大高度,默认值为1080
* @param {int} minFrameRate 最小帧率, 默认值为5
* @param {int} maxFrameRate 最大帧率, 默认值为10
*
* @example
* avdEngine.setDefaultScreenParams(1080,720,5,10);
*/
AVDEngine.prototype.setDefaultScreenParams = function(maxWidth,maxHeight,minFrameRate,maxFrameRate) {
if(maxWidth){
this.defaultScreenParamsWidth = maxWidth;
}
if(maxHeight){
this.defaultScreenParamsHeight = maxHeight;
}
if(maxFrameRate){
this.defaultScreenParamsFrameRate = maxFrameRate;
}
log.info(`===avdEngine.setDefaultScreenParams(), 设置桌面共享相关的默认参数值, 分辨率最大宽度: ${maxWidth}, 分辨率最大高度: ${maxHeight}, 最小帧率: ${minFrameRate}, 最大帧率: ${maxFrameRate}`);
this.logReportWait.push(`avdEngine.setDefaultScreenParams(), 设置桌面共享相关的默认参数值, 分辨率最大宽度: ${maxWidth}, 分辨率最大高度: ${maxHeight}, 最小帧率: ${minFrameRate}, 最大帧率: ${maxFrameRate}`);
}
/**
* @desc 设置媒体流推送的保活时长(单位为毫秒),不设置默认为15000毫秒
* @param {int} keepAliveTime -保活时长
*/
AVDEngine.prototype.setDefaultMediaPublishKeepAliveTime = function(keepAliveTime) {
if(keepAliveTime){
this.defaultMediaPublishKeepAliveTime = keepAliveTime;
}
log.info(`===avdEngine.setDefaultMediaPublishKeepAliveTime(), 设置媒体流发布的保活时长(单位为毫秒), keepAliveTime: ${keepAliveTime}`);
}
/**
* @desc 引擎初始化
* @async
* @param {String} serverURI - MCU服务器地址
* @param {String} accessToken - 访问令牌
* @param {boolean} isUseCascade - 是否级联加入会议,可以不传,默认为false
*
* @example
* avdEngine.init('avd.nice2meet.cn:9610','ZDgyNGU1MGU0ZGY2MTJlZWY3NjkwMjQ5NDY0YWE3MDkxZGJjNmRiZg==').then(function(){}).otherwise(function(error){});
*/
AVDEngine.prototype.init = function(serverURI, accessToken, isUseCascade) {
var self = this;
var deferred = when.defer();
if(typeof(isUseCascade) == "undefined"){
isUseCascade = false;
}
log.info(`===avdEngine.init(), 引擎初始化开始, serverURI: ${serverURI}, accessToken: ${accessToken}, isUseCascade: ${isUseCascade}`);
this.logReportWait.push(`avdEngine.init(), 引擎初始化开始, serverURI: ${serverURI}, accessToken: ${accessToken}, isUseCascade: ${isUseCascade}`);
if(!accessToken || !serverURI){
var error = new Error(ErrorConstant.avdEngine_init_failed);
log.error(`===avdEngine.init(), error, code: ${error.code}, message: ${error.message}`);
deferred.reject(error);
return deferred.promise;
}
this.restServerInfo.mcuServerUrl = serverURI;
this.clientIsUseCascade = isUseCascade;
this.accessToken = accessToken.replace(/%3D%3D/g, "==");
this.accessToken = this.accessToken.replace(/%3d%3d/g, "==");
/**
* 操作系统版本号及手机型号等信息上报给服务器,规则如下,没有时可以不用上报该属性。
* user_resources:{
* os: 'ac OS_10.13.6',
* endpoint: {
userAgent: 'abc',
* endpointType:'',
* browser:'chrome_103.0.0.0',
* browserShell:'browser_2.1',
* wxVersion:''
* }
* }
*/
var endpoint = {};
var nAgt = navigator.userAgent;
//var nAgt = "Mozilla/5.0 (Linux; U; Android 12; zh-CN; ANA-AN00 Build/HUAWEIANA-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UWS/5.12.8.0 Mobile Safari/537.36 AliApp(DingTalk/7.6.50) com.alibaba.android.rimet/43925710 Channel/227200 language/zh-CN abi/64 Hmos/4.2.0 xpn/huawei UT4Aplus/0.2.25 colorScheme/light";
endpoint.userAgent = nAgt;
var uaparser = new UAParser(nAgt);
if(uaparser){
var os = uaparser.getOS();
if(os){
self.clientResource.os = os.name + "_" + os.version;
}
var browser = uaparser.getBrowser();
if(browser){
endpoint.browser = browser.name + "_" + browser.version;
}
}
if(self.detect.browser.isCustomized){
endpoint.browserShell = self.detect.browser.shellName + "_" + self.detect.browser.shellFullVersion;
}
self.clientResource.endpoint = endpoint;
//获取本地客户端IP
if(typeof(navigator.Promise) != "undefined"){
getLocalClientIP().then(function(localClientIP){
log.debug(`===avdEngine.init(), localClientIP: ${localClientIP}`);
self.clientResource.ips = localClientIP;
});
}
if(typeof(navigator.connection) != "undefined"){
var effectiveType = getEffectiveType()
log.debug(`===avdEngine.init(), effectiveType: ${effectiveType}`);
self.clientResource.net = effectiveType;
}
log.debug("===avdEngine.init(),clientResource:" + JSON.stringify(self.clientResource));
log.info("===avdEngine.init(),引擎初始化成功");
this.logReportWait.push("引擎初始化成功!");
deferred.resolve();
return deferred.promise;
};
/**
* @desc avdEngine OEM初始化
* @async
* @param {String} serverURI - MCU服务器地址
* @param {String} oemName - oem名称
*/
AVDEngine.prototype.initWithOEM = function(serverURI, oemName) {
var deferred = when.defer();
log.info("===avdEngine.initWithOEM(), OEM init begin");
if(oemName !='qiniu.com'){
var err = new Error(ErrorConstant.oemname_notfound);
deferred.reject(err);
}else{
this.restServerInfo.mcuServerUrl = serverURI;
this.restServerInfo.roomServerUrl = serverURI;
this.restServerInfo.isCluster = true;
deferred.resolve();
}
//TODO 生成一个唯一id 记录每一次访问 用于日志
return deferred.promise;
};
/**
* @desc 获取房间。如房间存在,直接返回,不存在时创建房间后返回
* @param {String} roomId - 房间ID
* @returns {Object} room - 房间对象
*
* @example
* var roomId = '13124323454';
* room = avdEngine.obtainRoom(roomId);
*/
AVDEngine.prototype.obtainRoom = function(roomId) {
if (roomId == null || roomId == '') {
var error = new Error(ErrorConstant.roomId_required);
log.error(`===avdEngine.obtainRoom() error, code: ${error.code}, message: ${error.message}`);
return;
}
if (typeof roomId !== 'string') {
var error = new Error(ErrorConstant.exporter_parameter_type_error);
log.error(`===avdEngine.obtainRoom() error, code: ${error.code}, message: ${error.message} [roomId需字符串类型]`);
return;
}
var room = getRoomById(roomId, this.rooms);
if(room == null) {
room = new Room(roomId);
//平板电脑chrome浏览器不支持H264,在有小程序加会的场景中,需要通知到服务器进行转码
// if(this.browserOsName == "Android"){
// room.userAgent = USER_AGENT_AVD_VIDEO_TRANSCODING;
// }
// else if(this.browserOsName == "iOS"){
// room.userAgent = USER_AGENT_AVD_IOS;
// }
this.rooms.push(room);
}
return room;
};
/**
* @desc 获取房间对象
* @param {String} roomId - 房间ID
* @returns {Object} room - 房间对象
*/
AVDEngine.prototype.getRoom = function(roomId) {
if (roomId == null || roomId == '') {
var error = new Error(ErrorConstant.roomId_required);
log.error(`===avdEngine.getRoom() error, code: ${error.code}, message: ${error.message}`);
return;
}
if (typeof roomId !== 'string') {
var error = new Error(ErrorConstant.exporter_parameter_type_error);
log.error(`===avdEngine.getRoom() error, code: ${error.code}, message: ${error.message} [roomId需字符串类型]`);
return;
}
var room = getRoomById(roomId, this.rooms);
return room;
}
/**
* @desc 获取当前所有设备的原始对象,需要应用层自己去疏理摄像头,麦克风等。
* @async
* @returns {Object} - devices - 所有设备的原始对象
*
* @example
* avdEngine.getAllDevices().then(onAllDevices).otherwise(showError);
*
* function onAllDevices(devices) {
* devices.forEach(function(device){
* if (device.kind === 'videoinput' || device.kind === 'video') {
* console.log("camera:",device);
* }else if(device.kind === 'audioinput' || device.kind === 'audio'){
* console.log("microphone:",device);
* }else if(device.kind === 'audiooutput'){
* console.log("speaker:",device);
* }
* }
* }
*/
AVDEngine.prototype.getAllDevices = function() {
var deferred = when.defer();
// console.log("aaaaaaaaaaaaaaaaaaaaa");
if(typeof navigator.mediaDevices !== 'undefined') {
log.debug("===avdEngine.getAllDevices(), by navigator.mediaDevices.enumerateDevices()");
navigator.mediaDevices.enumerateDevices().then(function(devices) {
// console.log("===avdEngine.getAllDevices(), by navigator.mediaDevices.enumerateDevices(),devices:",devices);
deferred.resolve(devices);
});
} else if(typeof MediaStreamTrack !== 'undefined') {
if(typeof(MediaStreamTrack.getSources) != "undefined"){
log.debug("===avdEngine.getAllDevices(),by MediaStreamTrack.getSources");
MediaStreamTrack.getSources(function(sourceInfos) {
deferred.resolve(sourceInfos);
});
}else{
var error = new Error(ErrorConstant.navigatorUserMediaError_notSupportedError);
deferred.reject(error);
}
} else {
var error = new Error(ErrorConstant.not_support_mediaStreamTrack);
deferred.reject(error);
}
return deferred.promise;
};
/**
* @desc 初始化摄像头、麦克风设备
* <p>逻辑:摄像头设备会全部初始化到服务器,麦克风设备只会初始化默认的一个到服务器</p>
* @async
* @return {int} - 返回初始化完成状态。1:代表完成
*
* @example
* avdEngine.initDevice().then(function(result){
* if(result == 1){
* console.log('设备初始化完成');
* }
* }).otherwise(function(error){});
*/
AVDEngine.prototype.initDevice = function() {
var deferred = when.defer();
var self = this;
log.info("===avdEngine.initDevice():Camera and Microphone begin...");
if(self.isChrome && self.majorVersion <81){
self.initDeviceHandler().then(function(state){
deferred.resolve(state);
}).otherwise(function(error){
deferred.reject(error);
});
}else{
if(self.isFirefox){
var constraints = {
audio: {deviceId: 'undefined'},
video: {deviceId: 'undefined'}
};
log.debug("===avdEngine.initDevice(),firefox constraints:"+JSON.stringify(constraints));
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
self.initDeviceHandler().then(function(state){
if (stream) {
stream.getTracks().forEach(function(track) {
track.stop();
});
stream = null;
}
deferred.resolve(state);
}).otherwise(function(error){
deferred.reject(error);
});
}).catch(function(error){
log.error('===avdEngine.initDevice(),navigator.MediaDevices.getUserMedia of firfox error: ', error.message, error.name);
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj;
if(error.name == 'NotSupportedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notSupportedError);
}else if(error.name == 'OverconstrainedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_overconstrainedError);
}else if(error.name == 'TrackStartError' || error.name == 'NotReadableError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_trackStartError);
}else if(error.name == 'NotAllowedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notAllowedError);
}else if(error.name == 'TypeError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_typeError);
}else if(error.name == 'NotFoundError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notFoundError);
}else{
errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
}
deferred.reject(errorObj);
});
}else{
self.checkDevice().then(function(result) {
if(!result.deviceIdIsUndefined){
log.debug("===AVDEngine.initDevice(),chrome Version >= 81 or Other browsers,deviceId is not Undefined");
self.initDeviceHandler().then(function(state){
deferred.resolve(state);
}).otherwise(function(error){
deferred.reject(error);
});
}else{
log.debug("===AVDEngine.initDevice(),chrome Version >= 81 or Other browsers ,deviceId is Undefined");
var constraints = {};
if(result.video){
constraints.video = {deviceId: 'undefined'};
}
if(result.audio){
constraints.audio = {deviceId: 'undefined'};
}
log.debug("===avdEngine.initDevice(),chrome version>=81 or Other browsers,constraints:"+JSON.stringify(constraints));
if(!result.video && ! result.audio){
deferred.resolve(1);
return deferred.promise;
}else{
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
if (stream) {
stream.getTracks().forEach(function(track) {
track.stop();
});
stream = null;
}
self.initDeviceHandler().then(function(state){
deferred.resolve(state);
}).otherwise(function(error){
deferred.reject(error);
});
}).catch(function(error){
log.error('===avdEngine.initDevice(),navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj;
if(error.name == 'NotSupportedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notSupportedError);
}else if(error.name == 'OverconstrainedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_overconstrainedError);
}else if(error.name == 'TrackStartError' || error.name == 'NotReadableError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_trackStartError);
}else if(error.name == 'NotAllowedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notAllowedError);
}else if(error.name == 'TypeError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_typeError);
}else if(error.name == 'NotFoundError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notFoundError);
}else{
errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
}
deferred.reject(errorObj);
});
}
}
}).otherwise(function(error){
deferred.reject(error);
});
}
}
return deferred.promise;
}
/**
* @ignore
*/
AVDEngine.prototype.initDeviceHandler = function() {
var deferred = when.defer();
var self = this;
this.getAllDevices().then(
function(devices) {
var cameraMap = {};
var cameraLen = 0;
var microphoneMap = {};
var microphoneLen = 0;
var defaultMicrophoneLable = null;
var microphoneGroupId = null;
var speakerMap = {};
var speakerLen = 0;
var defaultSpeakerLable = null;
var speakerGroupId = null;
for(var i = 0; i != devices.length; ++i) {
var device = devices[i];
var deviceId = device.deviceId || device.id;
if(device.kind === 'videoinput' || device.kind === 'video') {
self.hasCamera = true;
cameraLen += 1;
cameraMap[deviceId] = device.label || 'camera' + cameraLen;
} else if(device.kind === 'audioinput' || device.kind === 'audio') {
self.hasMicrophone = true;
if(device.deviceId == 'default'){
var lableLines = device.label.split(' - ');
defaultMicrophoneLable = lableLines[1];
microphoneGroupId = device.groupId;
}else{
if(device.groupId == microphoneGroupId && device.label == defaultMicrophoneLable){
microphoneLen += 1;
microphoneMap[deviceId] = device.label || 'microphone' + microphoneLen;
}
if(device.groupId != microphoneGroupId){
microphoneLen += 1;
microphoneMap[deviceId] = device.label || 'microphone' + microphoneLen;
}
}
} else if(device.kind === 'audiooutput') {
self.hasSpeaker = true;
if(device.deviceId == 'default'){
var lableLines = device.label.split(' - ');
defaultSpeakerLable = lableLines[1];
speakerGroupId = device.groupId;
}else {
if(device.groupId == speakerGroupId && device.label == defaultSpeakerLable){
speakerLen += 1;
speakerMap[deviceId] = device.label || 'speaker' + speakerLen;
}
if(device.groupId != speakerGroupId){
speakerLen += 1;
speakerMap[deviceId] = device.label || 'speaker' + speakerLen;
}
}
}
}
self.cameraMap = cameraMap;
log.info("===avdEngine.initDevice(),Camera finish,cameraMap:",JSON.stringify(cameraMap));
//客户端资源上报用
self.clientResource.cameras = [];
for(var key in cameraMap) {
var val = cameraMap[key];
var clientResourceCamera = new Object();
clientResourceCamera.id = key;
clientResourceCamera.name = val;
self.clientResource.cameras.push(clientResourceCamera);
}
self.microphoneMap = microphoneMap;
for(var key in microphoneMap) {
var val = microphoneMap[key];
if(!self.checkMicrophoneId){
self.checkMicrophoneId = key;
self.checkMicrophoneName = val;
}
break;
}
log.info("===avdEngine.initDevice(),Microphone finish,microphoneMap:",JSON.stringify(microphoneMap));
//客户端资源上报用
self.clientResource.microphones = [];
for(var key in microphoneMap) {
var val = microphoneMap[key];
var clientResourceMicrophone = new Object();
clientResourceMicrophone.id = key;
clientResourceMicrophone.name = val;
self.clientResource.microphones.push(clientResourceMicrophone);
}
self.speakerMap = speakerMap;
log.info("===avdEngine.initDevice(),Speaker finish,speakerMap:",JSON.stringify(speakerMap));
//客户端资源上报用
self.clientResource.speakers = [];
for(var key in speakerMap) {
var val = speakerMap[key];
var clientResourceSpeaker = new Object();
clientResourceSpeaker.id = key;
clientResourceSpeaker.name = val;
self.clientResource.speakers.push(clientResourceSpeaker);
}
deferred.resolve(1);
}
).otherwise(
function(error) {
log.error("===avdEngine.initDevice() error, code:" + error.code + "; message:" + error.message);
deferred.reject(error);
}
)
return deferred.promise;
}
/**
* @desc 设备热插拔时,通过该方法可以更新设备资源。
* <p>逻辑:摄像头设备会全部初始化到服务器,麦克风设备只会初始化默认的一个到服务器</p>
* @ignore
*/
AVDEngine.prototype.refreshDevice = function() {
var deferred = when.defer();
var self = this;
var oldMicrophoneIdArray = [];
for(key in self.microphoneMap) {
oldMicrophoneIdArray.push(key);
}
var oldCameraIdArray = [];
for(key in self.cameraMap) {
oldCameraIdArray.push(key);
}
this.getAllDevices().then(
function(devices) {
var cameraMap = {};
var cameraLen = 0;
var microphoneMap = {};
var microphoneLen = 0;
var defaultMicrophoneLable = null;
var microphoneGroupId = null;
var speakerMap = {};
var speakerLen = 0;
var defaultSpeakerLable = null;
var speakerGroupId = null;
for(var i = 0; i != devices.length; ++i) {
var device = devices[i];
var deviceId = device.deviceId || device.id;
if(device.kind === 'videoinput' || device.kind === 'video') {
cameraLen += 1;
cameraMap[deviceId] = device.label || 'camera' + cameraLen;
} else if(device.kind === 'audioinput' || device.kind === 'audio') {
if(device.deviceId == 'default'){
var lableLines = device.label.split(' - ');
defaultMicrophoneLable = lableLines[1];
microphoneGroupId = device.groupId;
}else{
if(device.groupId == microphoneGroupId && device.label == defaultMicrophoneLable){
microphoneLen += 1;
microphoneMap[deviceId] = device.label || 'microphone' + microphoneLen;
}
if(device.groupId != microphoneGroupId){
microphoneLen += 1;
microphoneMap[deviceId] = device.label || 'microphone' + microphoneLen;
}
}
} else if(device.kind === 'audiooutput') {
if(device.deviceId == 'default'){
var lableLines = device.label.split(' - ');
defaultSpeakerLable = lableLines[1];
speakerGroupId = device.groupId;
}else {
if(device.groupId == speakerGroupId && device.label == defaultSpeakerLable){
speakerLen += 1;
speakerMap[deviceId] = device.label || 'speaker' + speakerLen;
}
if(device.groupId != speakerGroupId){
speakerLen += 1;
speakerMap[deviceId] = device.label || 'speaker' + speakerLen;
}
}
}
}
refreshVideoDeviceHandle(oldCameraIdArray, cameraMap, self.rooms, self.cameraMap);
for(var key in microphoneMap) {
var val = microphoneMap[key];
self.checkMicrophoneId = key;
self.checkMicrophoneName = val;
break;
}
refreshAudioDeviceHandle(oldMicrophoneIdArray, microphoneMap, self.rooms, self.microphoneMap);
self.speakerMap = speakerMap;
}
).otherwise(
function(error) {
log.error("===avdEngine.refreshDevice(),get device error!error code:" + error.code + "; error message:" + error.message);
}
)
setTimeout(function() {
deferred.resolve();
}, 2000);
return deferred.promise;
}
/**
* 实现逻辑:
* 1. 现麦克风设备不存在,新麦克风设备需要做init处理
* 2. 新麦克风设备与现麦克风设备ID一样,不做处理
* 3. 现麦克风设备状态为init,则新麦克风设备也需要做init处理
* 4. 现麦克风设备状态为publish或muted,则新麦克风设备也需要做publish处理
*/
/**
* @desc 设置成当前使用的麦克风
* @param {String} microphoneId - 麦克风设备Id
*/
AVDEngine.prototype.setRecordingMicrophone = function(microphoneId) {
log.info(`===avdEngine.setRecordingMicrophone(), microphoneId: ${microphoneId}`);
var self = this;
for(var i = 0; i != self.rooms.length; ++i) {
var room = self.rooms[i];
if(room && room.selfUser){
var audio = room.selfUser.audio;
if(!audio) {
var name = self.microphoneMap[microphoneId];
room.selfUser.initAudio(microphoneId, name);
} else {
if(microphoneId != audio.id) {
if(audio.status == StreamStatus.init) {
room.selfUser.deleteAudio(audio.id);
var name = self.microphoneMap[microphoneId];
room.selfUser.initAudio(microphoneId, name);
self.checkMicrophoneId = microphoneId;
self.checkMicrophoneName = name;
log.info(`===avdEngine.setRecordingMicrophone(), microphoneId: ${microphoneId}, microphoneName: ${name} success!`);
} else if(audio.status == StreamStatus.published || audio.status == StreamStatus.muted) {
var element = audio.element;
audio.closeMicrophone().then(function(){
//设置延时,以防远端订阅音频时,原来的设备id已失效引发错误。
setTimeout(function(){
room.selfUser.deleteAudio(audio.id);
var name = self.microphoneMap[microphoneId];
room.selfUser.initAudio(microphoneId, name);
room.selfUser.audio.openMicrophone(element);
self.checkMicrophoneId = microphoneId;
self.checkMicrophoneName = name;
log.info(`===avdEngine.setRecordingMicrophone(), microphoneId: ${microphoneId}, microphoneName: ${name} success!`);
},600);
});
}
}
}
}
}
}
// /**
// * @desc 获取所有设备对象
// * @returns {Object} - deviceObject = {video: video,audio: audio,speaker: speaker};
// *
// * @ignore
// *
// *
// * @example
// * avdEngine.getDeviceObject().then(showDevices).otherwise(alertError);
// *
// * function showDevices(deviceObject) {
// * var video = deviceObject.video;
// * var audio = deviceObject.audio;
// * var speaker = deviceObject.speaker;
// * }
// */
// AVDEngine.prototype.getDeviceObject = function() {
// var deferred = when.defer();
// var self = this;
// log.info("===avdEngine.getDeviceObject() begin");
// this.getAllDevices().then(
// function(devices) {
// console.log("===avdEngine.getDeviceObject(),devices:",devices);
// var deviceObject = {
// video: null,
// audio: null,
// speaker: null
// };
// var cameraMap = {};
// var cameraLen = 0;
// var microphoneMap = {};
// var microphoneLen = 0;
// var microphoneGroupId =null;
// var speakerMap = {};
// var speakerLen = 0;
// var speakerGroupId =null;
// for(var i = 0; i != devices.length; ++i) {
// var device = devices[i];
// var deviceId = device.deviceId || device.id;
// if(device.kind === 'videoinput' || device.kind === 'video') {
// cameraLen += 1;
// cameraMap[deviceId] = device.label || 'camera' + cameraLen;
// } else if(device.kind === 'audioinput' || device.kind === 'audio') {
// if(device.deviceId == 'default'){
// microphoneLen += 1;
// microphoneMap[deviceId] = device.label || 'microphone' + microphoneLen;
// microphoneGroupId = device.groupId;
// }else{
// if(device.groupId != microphoneGroupId){
// microphoneLen += 1;
// microphoneMap[deviceId] = device.label || 'microphone' + microphoneLen;
// }
// }
// } else if(device.kind === 'audiooutput') {
// if(device.deviceId == 'default'){
// speakerLen += 1;
// speakerMap[deviceId] = device.label || 'speaker' + speakerLen;
// speakerGroupId = device.groupId;
// }else {
// if(device.groupId != speakerGroupId){
// speakerLen += 1;
// speakerMap[deviceId] = device.label || 'speaker' + speakerLen;
// }
// }
// }
// }
// deviceObject.video = cameraMap;
// deviceObject.audio = microphoneMap;
// deviceObject.speaker = speakerMap;
// deferred.resolve(deviceObject);
// }
// ).otherwise(
// function(error) {
// deferred.reject(error);
// }
// )
// return deferred.promise;
// }
/**
* @desc 检查设备是否存在
* @returns {Object} - deviceResult = {video: false,audio: false,speaker: false};
*
* @ignore
*
* @example
* avdEngine.checkDevice().then(checkResult).otherwise(checkError);
*
* function checkResult(result) {
* console.log("Does the Video Device Exist?" + result.video);
* console.log("Does the Audio Device Exist?" + result.audio);
* console.log("Does the Speaker Device Exist?" + result.speaker);
* }
*/
AVDEngine.prototype.checkDevice = function() {
var deferred = when.defer();
var self = this;
log.debug('===checkDevice(),start...')
this.getAllDevices().then(
function(devices) {
var deviceResult = {
video: false,
audio: false,
speaker: false,
deviceIdIsUndefined:false,
};
for(var i = 0; i != devices.length; ++i) {
var device = devices[i];
var deviceId = device.deviceId || device.id;
if(!deviceId){
deviceResult.deviceIdIsUndefined = true;
}
if(device.kind === 'videoinput' || device.kind === 'video') {
deviceResult.video = true;
} else if(device.kind === 'audioinput' || device.kind === 'audio') {
deviceResult.audio = true;
} else if(device.kind === 'audiooutput') {
deviceResult.speaker = true;
}
}
log.debug('===checkDevice(),sucess end...')
deferred.resolve(deviceResult);
}
).otherwise(
function(error) {
log.debug('===checkDevice(),error end...')
deferred.reject(error);
}
)
return deferred.promise;
}
/**
* @desc 设备检测,打开摄像头
* @async
* @param {String} cameraId - 摄像头ID,空字符串时则默认选择打开一个摄像头
* @param {Int} resolution - 视频分辨率
* @param {Int} framerate - 视频帧率
* @returns {Object} - 视频流对象
*
* @example
* avdEngine.checkOpenVideo(cameraId, resolution, framerate).then(showVideo).otherwise(alertError);
*
* function showVideo(stream) {
* checkVideo.srcObject = stream;
* }
*/
AVDEngine.prototype.checkOpenVideo = function(cameraId, resolution, framerate) {
var deferred = when.defer();
var self = this;
self.checkCloseVideo();
var constraints = {
video: {},
audio: false
};
if(cameraId && cameraId!="undefined") {
constraints.video.deviceId = {
exact: cameraId
}
}
if (resolution) {
var realResolution = Resolution[resolution];
constraints.video.width = {
min: realResolution.width,
max: realResolution.width
};
constraints.video.height = {
min: realResolution.height,
max: realResolution.height
};
}
if (framerate && (self.isChrome || self.isOpera)) {
if(typeof(framerate) == 'string') {
framerate = Number(framerate);
}
constraints.video.frameRate = {
min: framerate,
max: framerate
};
}
try {
//console.log("aaaa00000000000,constraints:",constraints);
log.info("===avdEngine.checkOpenVideo(),constraints:"+ JSON.stringify(constraints));
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
self.checkVideoStream = stream;
deferred.resolve(stream);
}).catch(function(error){
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj;
if(error.name == 'NotSupportedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notSupportedError);
}else if(error.name == 'OverconstrainedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_overconstrainedError);
}else if(error.name == 'TrackStartError' || error.name == 'NotReadableError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_trackStartError);
}else if(error.name == 'NotAllowedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notAllowedError);
}else if(error.name == 'TypeError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_typeError);
}else if(error.name == 'NotFoundError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notFoundError);
}else{
errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
}
log.error("===avdEngine.checkOpenVideo(),errorName:"+ error.name);
deferred.reject(errorObj);
});
} catch(e) {
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
deferred.reject(errorObj)
}
return deferred.promise;
}
/**
* @desc 设备检测,打开麦克风
* @async
* @param {String} microphoneId - 麦克风ID,空字符串时则默认选择打开一个麦克风
* @returns {Object} - 音频流对象
*
* @example
* avdEngine.checkOpenAudio().then(showAudio).otherwise(alertError);
*
* function showAudio(stream) {
* checkAudio.srcObject = stream;
* }
*/
AVDEngine.prototype.checkOpenAudio = function(microphoneId) {
var deferred = when.defer();
var self = this;
self.checkCloseAudio();
var constraints = {
video: false,
audio: {}
};
if(microphoneId && microphoneId!="undefined") {
constraints.audio.deviceId = {
exact:microphoneId
}
}
try {
log.info("===avdEngine.checkOpenAudio(),constraints:"+ JSON.stringify(constraints));
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
self.checkAudioStream = stream;
deferred.resolve(stream);
}).catch(function(error) {
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj;
if(error.name == 'NotSupportedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notSupportedError);
}else if(error.name == 'OverconstrainedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_overconstrainedError);
}else if(error.name == 'TrackStartError' || error.name == 'NotReadableError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_trackStartError);
}else if(error.name == 'NotAllowedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notAllowedError);
}else if(error.name == 'TypeError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_typeError);
}else if(error.name == 'NotFoundError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notFoundError);
}else{
errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
}
deferred.reject(errorObj)
});
} catch(e) {
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
deferred.reject(errorObj)
}
return deferred.promise;
}
/**
* @desc 设备检测,打开摄像头和麦克风
* @async
* @param {String} cameraId - 摄像头ID,空字符串时则默认选择打开一个摄像头
* @param {String} microphoneId - 麦克风ID,空字符串时则默认选择打开一个麦克风
* @param {Int} resolution - 视频分辨率
* @param {Int} framerate - 视频帧率
* @returns {Object} - 音视频流对象
*
* @example
* avdEngine.checkOpenVideoAndAudio().then(showVideoAndAudio).otherwise(alertError);
*
* function showVideoAndAudio(stream) {
* checkVideo.srcObject = stream.videoStream;
* checkAudio.srcObject = stream.audioStream;
* }
*/
AVDEngine.prototype.checkOpenVideoAndAudio = function(cameraId,microphoneId,resolution,framerate) {
var deferred = when.defer();
var self = this;
self.checkCloseVideo();
self.checkCloseAudio();
var constraints = {
video: {},
audio: {}
};
if(cameraId && cameraId!="undefined") {
constraints.video.deviceId = {
exact:cameraId
};
}
if(microphoneId && microphoneId!="undefined") {
constraints.audio.deviceId = {
exact:microphoneId
}
}
if (resolution) {
var realResolution = Resolution[resolution];
constraints.video.width = {
min: realResolution.width,
max: realResolution.width
};
constraints.video.height = {
min: realResolution.height,
max: realResolution.height
};
}
if (framerate && (self.isChrome || self.isOpera)) {
if(typeof(framerate) == 'string') {
framerate = Number(framerate);
}
constraints.video.frameRate = {
min: framerate,
max: framerate
};
}
try {
log.info("===avdEngine.checkOpenVideoAndAudio(),constraints:"+ JSON.stringify(constraints));
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
var audioStream = new MediaStream();
var videoStream = new MediaStream();
if (stream) {
var audioTracks = stream.getAudioTracks();
for (var i = 0; i < audioTracks.length; i++) {
audioStream.addTrack(audioTracks[i]);
}
var videoTracks = stream.getVideoTracks();
for (i = 0; i < videoTracks.length; i++) {
videoStream.addTrack(videoTracks[i]);
}
}
var newStream = {
audioStream:audioStream,
videoStream:videoStream
}
self.checkVideoStream = newStream.videoStream;
self.checkAudioStream = newStream.audioStream;
deferred.resolve(newStream);
}).catch(function(error){
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj;
if(error.name == 'NotSupportedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notSupportedError);
}else if(error.name == 'OverconstrainedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_overconstrainedError);
}else if(error.name == 'TrackStartError' || error.name == 'NotReadableError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_trackStartError);
}else if(error.name == 'NotAllowedError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notAllowedError);
}else if(error.name == 'TypeError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_typeError);
}else if(error.name == 'NotFoundError'){
errorObj = new Error(ErrorConstant.navigatorUserMediaError_notFoundError);
}else{
errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
}
deferred.reject(errorObj);
});
} catch(e) {
var Error = ModuleBase.use(ModulesEnum.error);
var errorObj = new Error(ErrorConstant.navigatorUserMediaError_unknown);
deferred.reject(errorObj)
}
return deferred.promise;
}
/**
* @desc 设备检测,麦克风音量的检测回调
* @param {int} intervalMilis - 音量采购频率,单位为毫秒
* @param {Object} showAudioLevel - 音量值回调方法
*
* @example
* avdEngine.checkAudioLevel(1000,showAudioLevel);
* function showAudioLevelm(audioLevel) {}
*/
AVDEngine.prototype.checkAudioLevel = function(intervalMilis,showAudioLevel) {
if(this.checkAudioStream){
checkAudioLocalStatsCollector = new LocalStatsCollector(this.checkAudioStream, intervalMilis, showAudioLevel);
checkAudioLocalStatsCollector.start();
}
}
/**
* @desc 设备检测,关闭摄像头
*
* @example
* avdEngine.checkCloseVideo();
*/
AVDEngine.prototype.checkCloseVideo = function() {
if(this.checkVideoStream){
this.checkVideoStream.getTracks().forEach(function(track) {
track.stop();
});
log.debug('===avdEngine.checkCloseVideo finished')
}
this.checkVideoStream = null;
}
/**
* @desc 设备检测,关闭麦克风
*
* @example
* avdEngine.checkCloseAudio();
*/
AVDEngine.prototype.checkCloseAudio = function() {
if(this.checkAudioLocalStatsCollector){
this.checkAudioLocalStatsCollector.stop();
}
this.checkAudioLocalStatsCollector = null;
if(this.checkAudioStream){
this.checkAudioStream.getTracks().forEach(function(track) {
track.stop();
});
log.debug('===avdEngine.checkCloseAudio() finished')
}
this.checkAudioStream = null;
}
function getRoomById(roomId, rooms) {
var room = null;
for(var i = 0; i < rooms.length; i++) {
var srcRoom = rooms[i];
if(srcRoom.id == roomId) {
room = srcRoom;
break;
}
}
return room;
};
function appenderLayout(appender) {
var popUpLayout = new log4javascript.PatternLayout("%d{yyyy-MM-dd HH:mm:ss SSS} %-5p - %m{1}%n");
appender.setLayout(popUpLayout);
log.addAppender(appender);
};
function refreshAudioDeviceHandle(oldMicrophoneIdArray, microphoneMap, rooms, selfMicrophoneMap) {
var newMicrophoneIdArray = [];
for(var key in microphoneMap) {
newMicrophoneIdArray.push(key);
}
//alert("oldMicrophoneIdArray:" + oldMicrophoneIdArray);
//alert("newMicrophoneIdArray:" + newMicrophoneIdArray);
var addMicrophoneIdArray = arrayUtil.inANotInB(newMicrophoneIdArray, oldMicrophoneIdArray);
var deleMicrophoneIdArray = arrayUtil.inANotInB(oldMicrophoneIdArray, newMicrophoneIdArray);
if(deleMicrophoneIdArray.length == 0 && addMicrophoneIdArray.length == 0) {
//alert("audio无变化,不做处理");
return false;
}
for(var i = 0; i < this.rooms.length; i++) {
var srcRoom = rooms[i];
if(srcRoom.selfUser) {
if(addMicrophoneIdArray.length > 0) {
//alert("有新增,addMicrophoneIdArray:"+ addMicrophoneIdArray);
for(var j = 0; j < addMicrophoneIdArray.length; j++) {
var name = microphoneMap[addMicrophoneIdArray[j]];
selfMicrophoneMap[addMicrophoneIdArray[j]] = name;
}
}
if(deleMicrophoneIdArray.length > 0) {
//alert("有删除,deleMicrophoneIdArray:"+deleMicrophoneIdArray);
for(var j = 0; j < deleMicrophoneIdArray.length; j++) {
var name = selfMicrophoneMap[deleMicrophoneIdArray[j]];
if(deleMicrophoneIdArray[j] == self.checkMicrophoneId) {
srcRoom.selfUser.deleteAudio(deleMicrophoneIdArray[j]);
}
delete selfMicrophoneMap[deleMicrophoneIdArray[j]];
}
}
}
}
};
function refreshVideoDeviceHandle(oldCameraIdArray, cameraMap, rooms, selfCameraMap) {
var self = this;
var newCameraIdArray = [];
for(var key in cameraMap) {
newCameraIdArray.push(key);
}
// alert("oldCameraIdArray:" + oldCameraIdArray);
// alert("newCameraIdArray:" + newCameraIdArray);
var addCameraIdArray = arrayUtil.inANotInB(newCameraIdArray, oldCameraIdArray);
var deleCameraIdArray = arrayUtil.inANotInB(oldCameraIdArray, newCameraIdArray);
if(deleCameraIdArray.length == 0 && addCameraIdArray.length == 0) {
//alert("video无变化,不做处理");
//console.log("===========wq999999999999");
return false;
}
if(self.isSafari){
//TODO safari 暂时保留这个模块,先不做任何操作 弄懂了在回来写
return false;
//EndTODO
}else{
for(var i = 0; i < rooms.length; i++) {
var srcRoom = rooms[i];
if(srcRoom.selfUser) {
if(addCameraIdArray.length > 0) {
// alert("有新增,addCameraIdArray:"+ addCameraIdArray);
for(var j = 0; j < addCameraIdArray.length; j++) {
var name = cameraMap[addCameraIdArray[j]];
selfCameraMap[addCameraIdArray[j]] = name;
srcRoom.selfUser.initVideo(addCameraIdArray[j], name);
}
}
if(deleCameraIdArray.length > 0) {
// alert("有删除,deleCameraIdArray:"+deleCameraIdArray);
for(var j = 0; j < deleCameraIdArray.length; j++) {
delete selfCameraMap[deleCameraIdArray[j]];
srcRoom.selfUser.deleteVideo(deleCameraIdArray[j]);
}
}
}
}
}
};
// /**
// * @desc 获取录制对象,走Restful API实现。
// * @param {String} restServerURI - 录制服务REST API的服务器地址
// * @returns {Object} record - 录制对象
// */
// AVDEngine.prototype.obtainRecord = function(restServerURI) {
// var record = new Record(restServerURI);
// return record;
// };
// /**
// * @desc 获取直播对象,走Restful API实现。
// * @param {String} restServerURI - 直播服务REST API 的服务器地址
// * @returns {Object} live - 直播对象
// */
// AVDEngine.prototype.obtainLive = function(restServerURI) {
// var live = new Live(restServerURI);
// return live;
// };
// /**
// * @desc 获取外部设备对象,包括rtsp和h323。走Restful API实现。
// * @param {String} restServerURI - 外部设备服务REST API 的服务器地址
// * @returns {Object} outgoing - 外部设备对象
// */
// AVDEngine.prototype.obtainOutgoing = function(restServerURI) {
// var outgoing = new Outgoing(restServerURI);
// return outgoing;
// };
/**
* @desc 获取流导出对象
* @ignore
* @returns {Object} streamExporterManager - 流导出对象
*/
AVDEngine.prototype.getStreamExporterManager = function() {
var streamExporterManager = new StreamExporterManager();
return streamExporterManager;
};
AVDEngine.prototype.deviceChangeListenerHandler = function() {
var self = this;
log.debug('===avdEngine.deviceChangeListenerHandler(), device change detected.')
// 添加时间戳的比较防止音频设备短时间内多次触发devicechange
var oldMicrphoneIdArr = Object.keys(self.microphoneMap);
var oldCameraIdArr = Object.keys(self.cameraMap);
var newMicrophoneIdArr = null;
var newCameraIdArr = null;
self.initDeviceHandler().then(function(state){
if(state == 1){
newMicrophoneIdArr = Object.keys(self.microphoneMap);
newCameraIdArr = Object.keys(self.cameraMap);
if(newMicrophoneIdArr.length > oldMicrphoneIdArr.length){
//有麦克风新增的情况
var tempAddMicrophoneIdArr = newMicrophoneIdArr.filter(function(newMicrophoneId){
return oldMicrphoneIdArr.findIndex(function(oldMicrophoneId){
return newMicrophoneId === oldMicrophoneId;
}) < 0;
})
log.debug('===avdEngine.deviceChangeListenerHandler(), Microphone device change detected. type:add, deviceIds: ' + tempAddMicrophoneIdArr)
self.eventEmitter.emit(EngineCallback.device_microphone_change, changeTypeEnum.add, tempAddMicrophoneIdArr);
}else if(newMicrophoneIdArr.length < oldMicrphoneIdArr.length){
//有麦克风移除的情况
var removedMicrophoneIdArr = oldMicrphoneIdArr.filter(function(oldMicrophoneId){
return newMicrophoneIdArr.findIndex(function(newMicrophoneId){
return newMicrophoneId === oldMicrophoneId;
}) < 0;
})
log.debug('===avdEngine.deviceChangeListenerHandler(), Microphone device change detected. type:remove, deviceIds: ' + removedMicrophoneIdArr)
self.eventEmitter.emit(EngineCallback.device_microphone_change, changeTypeEnum.remove, removedMicrophoneIdArr);
//被拔掉的麦克风如果正好是已上报到服务器的,做删除处理
if(removedMicrophoneIdArr.length > 0) {
for(var i = 0; i< removedMicrophoneIdArr.length; i++) {
if(removedMicrophoneIdArr[i] == self.checkMicrophoneId) {
for(var j = 0; j < self.rooms.length; j++) {
var srcRoom = self.rooms[j];
if(srcRoom.selfUser) {
srcRoom.selfUser.deleteAudio(removedMicrophoneIdArr[i]);
}
}
}
}
}
}
if(newCameraIdArr.length > oldCameraIdArr.length){
//有摄像头新增的情况
var newCameraIdArr = newCameraIdArr.filter(function(newCameraId){
return oldCameraIdArr.findIndex(function(oldCameraId){
return newCameraId === oldCameraId;
}) < 0;
})
log.debug('===avdEngine.deviceChangeListenerHandler(), Camera device change detected. type:add, deviceIds: ' + newCameraIdArr)
self.eventEmitter.emit(EngineCallback.device_camera_change, changeTypeEnum.add, newCameraIdArr);
//新插入的摄像头上报到服务器
for(var i = 0; i < newCameraIdArr.length; i++) {
var deviceId = newCameraIdArr[i];
var deviceName = self.cameraMap[deviceId];
for(var j = 0; j < self.rooms.length; j++) {
var srcRoom = self.rooms[j];
if(srcRoom.selfUser) {
srcRoom.selfUser.initVideo(deviceId, deviceName);
}
}
}
}else if(newCameraIdArr.length < oldCameraIdArr.length){
//有摄像头移除的情况
var removedCameraIdArr = oldCameraIdArr.filter(function(oldCameraId){
return newCameraIdArr.findIndex(function(newCameraId){
return newCameraId === oldCameraId;
}) < 0;
})
log.debug('===avdEngine.deviceChangeListenerHandler(), Camera device change detected. type: remove, deviceIds: ' + removedCameraIdArr)
self.eventEmitter.emit(EngineCallback.device_camera_change, changeTypeEnum.remove, removedCameraIdArr);
//被拔掉的摄像头上报到服务器
for(var i = 0; i < removedCameraIdArr.length; i++) {
var deviceId = removedCameraIdArr[i];
for(var j = 0; j < self.rooms.length; j++) {
var srcRoom = self.rooms[j];
if(srcRoom.selfUser) {
srcRoom.selfUser.deleteVideo(deviceId);
}
}
}
}
}
})
}
/**
* @desc 设备检测变动逻辑做防抖处理,默认2秒防抖间隔。
* @ignore
*/
AVDEngine.prototype.deviceChangeListener = function() {
var self = this;
if(!navigator || !navigator.mediaDevices){
return;
}
navigator.mediaDevices.ondevicechange = debounce(function(){
self.deviceChangeListenerHandler();
}, 2000);
};
/**
* @description 设置回声消除
* @param {Boolean} enable - 是否开启回声消除,默认为true
* @example
* avdEngine.setEchoCancellation(false);
*/
AVDEngine.prototype.setEchoCancellation = function(enable) {
if(typeof enable !== 'boolean'){
log.error("===avdEngine.setEchoCancellation() faild, true or false is needed");
return;
}
log.info("===avdEngine.setEchoCancellation(), enable: " + enable);
this.enableEchoCancellation = enable;
};
/**
* @description 设置降噪
* @param {Boolean} enable
*/
AVDEngine.prototype.setNoiseSuppression = function(enable) {
if(typeof enable !== 'boolean'){
log.error("===avdEngine.setNoiseSuppression() faild, true or false is needed");
return;
}
log.info("===avdEngine.setNoiseSuppression(), enable: " + enable);
this.enableNoiseSuppression = enable;
};
/**
* @description 设置自动增益
* @param {Boolean} enable
*/
AVDEngine.prototype.setAutoGainControl = function(enable) {
if(typeof enable !== 'boolean'){
log.error("===avdEngine.setAutoGainControl() faild, true or false is needed");
return;
}
log.info("===avdEngine.setAutoGainControl(), enable: " + enable);
this.enableAutoGainControl = enable;
};
/**
* @desc 是否忽略服务器下发的资源调整信令,true则不会响应服务器调整资源的通知,例如没有人订阅时发布的视频不会自动降低分辨率码率等
* @param {Boolean} isIgnore 是否忽略修改资源请求
*/
AVDEngine.prototype.setIgnoreModifyResourceReq = function(isIgnore) {
if(typeof isIgnore !== 'boolean'){
log.error("===avdEngine.setIgnoreModifyResourceReq() faild, true or false is needed");
return;
}
log.info("===avdEngine.setIgnoreModifyResourceReq(), isIgnore: " + isIgnore);
this.ignoreModifyResourceReq = isIgnore;
};
/**
* @desc 在加入会议之前设置是否启用硬件编码,默认为false,推荐有独立显卡的电脑设备开启,如性能足够,可实现高分辨率、高码率和高帧率
* @param {Boolean} isEnable 是否开启硬件编码
*/
AVDEngine.prototype.setEnableHardwareEncoding = function(isEnable) {
var gpuInfoStr = getGPUInfo();
log.info("===avdEngine.setEnableHardwareEncoding(), isEnable: ", isEnable, ', current gpu info is: ', gpuInfoStr);
if(typeof isEnable !== 'boolean'){
log.error("===avdEngine.setEnableHardwareEncoding() faild, true or false is needed");
return;
}
// 排除Intel的核显GPU
if(isEnable === true && gpuInfoStr.indexOf('Intel(R) HD Graphics') > -1){
log.error("===avdEngine.setEnableHardwareEncoding() faild, Detected Intel graphics card, ignored");
return;
}
this.enableHardwareEncoding = isEnable;
}
/**
* @desc 预加载美颜模型(人脸检测),可以在用户点击美颜按钮之前提前调用,显著减少首次启用美颜的等待时间
* @returns {Promise} - 预加载结果
* @example
* avdEngine.preloadBeautyModel().then(function() { console.log('预加载完成'); }).catch(function(e) { console.error('预加载失败:', e); });
*/
AVDEngine.prototype.preloadBeautyModel = function() {
var BeautyMediaPipe = null;
try {
BeautyMediaPipe = ModuleBase.use("BeautyMediaPipe");
} catch (e) {
log.warn('=== preloadBeautyModel: BeautyMediaPipe not available:', e);
return Promise.reject(new Error('BeautyMediaPipe not available'));
}
if (BeautyMediaPipe && BeautyMediaPipe.preload) {
log.info('=== preloadBeautyModel: starting preload');
return BeautyMediaPipe.preload().then(function(detector) {
log.info('=== preloadBeautyModel: preload completed, detector ready');
return detector;
})["catch"](function(e) {
log.error('=== preloadBeautyModel: preload failed:', e);
return Promise.reject(e);
});
}
return Promise.reject(new Error('preload method not available'));
};
/**
* @desc 设置traceId,用于监控埋点实现
* @param {String} traceId
*/
AVDEngine.prototype.setTraceId = function(traceId) {
log.info("===avdEngine.setTraceId(), traceId: " + traceId);
this.traceId = traceId;
};
/**
* @desc 获取traceId
* @returns {String} traceId
*/
AVDEngine.prototype.getTraceId = function() {
return this.traceId;
};
return AVDEngine;
});