ModuleBase.define("Video", ["Base", "Error"], function (Base, Error) {
/**
* @desc 视频Video构造函数。
* @constructor
* @alias Video
* @param {String} id - 设备Id
* @param {String} name - 设备名称
*/
class Video extends Base {
constructor(id, name, roomHandle) {
super(id, name);
this.roomHandle = roomHandle;
this.level;
this.description;
this.resolutionSelect;
this.resolution = {
width: avdEngineHandle.defaultVideoParamsWidth,
height: avdEngineHandle.defaultVideoParamsHeight,
};
this.setType = avdEngineHandle.defaultVideoParamsResolutionSetType;
this.frameRate = avdEngineHandle.defaultVideoParamsFrameRate;
this.aspectRatio = 0;
this.cameraType;
//通过分辨率宽高计算得出的最大带宽值
this.maxBandwidth = null;
//应用层客户调用接口自行设置的最大带宽值
this.clientSetMaxBandwidth = null;
this.streamName; //导出时视频流名称
this.streamNickName; //导出时视频流昵称
this.isPubState = false; //防止重复发布,在发布过程中该状态为true
this.rtcRtpSenderIntervalId = null;
this.spatialLayerActiveList = [];
this.spatialLayerActiveDoing = false;
this.currentVideoQuality = null; //当前视频质量值。实现逻辑:一秒钟内取最后一次质量值进行分辨率码及流调整
this.changePublishVideoQualityInterval = null; //码流分辨率调整定时器,时间1秒
this.videoStreamControlTimer = null;
this.publishTimer = null;
this.unpublishTimer = null;
this.subscribleTimer = null;
this.unsubscribleTimer = null;
// 虚拟背景相关属性
this._virtualBgEnabled = false; //虚拟背景是否已启用
this._virtualBgImage = null; //虚拟背景图片 URL 或颜色值
this._virtualBgOptions = {}; //虚拟背景配置选项
this._virtualBgProcessor = null; //虚拟背景处理器实例
this._virtualBgTrack = null; //虚拟背景处理后的视频轨道
this._virtualBgOriginalStream = null; //虚拟背景处理前的原始视频流
this._virtualBgWasEnabled = false; //虚拟背景之前是否启用过
this._virtualBgSelfieMode = false; //虚拟背景自拍模式(镜像)
this._virtualBgModelSelection = 0; //虚拟背景模型选择(0:通用, 1:自拍)
};
/**
* @desc 通过track生成新的MediaStream,虚拟背景启用时返回处理后的track
* @returns {MediaStream | null}
*/
getNewStreamByTrack = function () {
var trackToUse = this._virtualBgEnabled && this._virtualBgTrack ? this._virtualBgTrack : this.track;
if (trackToUse && trackToUse instanceof MediaStreamTrack) {
try {
var newStream = new MediaStream();
newStream.addTrack(trackToUse);
return newStream;
} catch (error) {
log.error("===Video.getNewStreamByTrack(),Failed to create MediaStream. Error:", error);
return null;
}
} else {
log.error("===Video.getNewStreamByTrack(), Invalid or missing track:", trackToUse);
return null;
}
};
/**
* @desc 设置摄像头的优先级别
* @param {int} level -优先级别
*/
setLevel = function (level) {
if (typeof level !== 'number' || !Number.isInteger(level)) {
log.error(`===video.setLevel(), level must be an integer。level: ${level}`);
}else{
log.info(`===video.setLevel(), videoId: ${this.id}, level: ${level}`);
this.level = level;
}
};
/**
* @desc 设置备注
* @param {String} description - 备注
*/
setDescription = function (description) {
log.info(`===video.setDescription(), videoId: ${this.id}, description: ${description}`);
this.description = description;
this.saveVideoCustomConfig('description', description)
};
/**
* @desc 设置导出时视频流名称
* @param {String} streamName - 视频流名称
*/
setStreamName = function (streamName) {
log.info(`===video.setStreamName(), videoId: ${this.id}, streamName: ${streamName}`);
this.streamName = streamName;
};
/**
* @desc 设置导出时视频流昵称
* @param {String} streamNickName - 视频流昵称
*/
setStreamNickName = function (streamNickName) {
log.info(`===video.setStreamNickName(), videoId: ${this.id}, streamNickName: ${streamNickName}`);
this.streamNickName = streamNickName;
};
/**
* @desc 设置分辨率
* @param {String} resolution- 分辨率标识
* @param {Object} resolutionSetType- 分辨率设置类型,可不填写,null时默认为期望即ResolutionSetType.ideal,也可填写强制,即ResolutionSetType.exact;
*/
setResolution = function (resolution, resolutionSetType) {
log.info(`===video.setResolution(), videoId: ${this.id}, resolution: ${resolution}, resolutionSetType: ${resolutionSetType}`);
this.resolutionSelect = resolution;
this.resolution = Resolution[resolution];
if (resolutionSetType) {
this.setType = resolutionSetType;
}
};
/**
* @desc 设置分辨率
* @param {int} width- 宽度
* @param {int} height- 高度
* @param {string} resolutionSetType- 分辨率设置类型,可不填写,null时默认为期望即ResolutionSetType.ideal,也可填写强制,即ResolutionSetType.exact;
*/
setResolutionWH = function (width, height, resolutionSetType, isInnerCall) {
log.info(`===video.setResolutionWH(), videoId: ${this.id}, width: ${width}, height: ${height}, resolutionSetType: ${resolutionSetType}`);
this.resolutionSelect = width;
var resolutionObject = {};
resolutionObject.order = 1;
resolutionObject.width = width;
resolutionObject.height = height;
if (!isInnerCall) {
this.resolution = resolutionObject;
this.saveVideoCustomConfig('width', width)
this.saveVideoCustomConfig('height', height)
if (resolutionSetType != undefined) {
this.setType = resolutionSetType;
this.saveVideoCustomConfig('setType', resolutionSetType)
}
}
}
/**
* @desc 设置帧率
* @param {String} frameRate- 帧率
*/
setFrameRate = function (frameRate) {
log.info(`===video.setFrameRate(), videoId: ${this.id}, frameRate: ${frameRate}`);
if (typeof (frameRate) == 'string') {
frameRate = Number(frameRate);
}
this.frameRate = frameRate;
this.saveVideoCustomConfig('frameRate', frameRate)
};
/**
* @desc 设置分辨率宽高比
* @param {Object} aspectRatio- 分辨率宽高比(1.7777777778, 1.3333333333),16:9或4:3时会开启强制
*/
setAspectRatio = function (aspectRatio) {
log.info(`===video.setAspectRatio(), videoId: ${this.id}, aspectRatio: ${aspectRatio}`);
if (typeof (aspectRatio) == 'string') {
aspectRatio = Number(aspectRatio);
}
this.aspectRatio = aspectRatio;
this.saveVideoCustomConfig('aspectRatio', aspectRatio)
}
/**
* @desc 应用生效更新后的视频约束(分辨率、帧率等)
*/
applyConstraints = function () {
if (this.track) {
var constraints = getApplyConstraints(this);
this.track.applyConstraints(constraints);
}
}
/**
* 视频打开时,分辨率即时更新生效,且通知给服务器
* @param {int} width
* @param {int} height
* @param {string} resolutionSetType- 分辨率设置类型,可不填写,null时默认为期望即ResolutionSetType.ideal,也可填写强制,即ResolutionSetType.exact;
*/
applyConstraintsWH = function (width, height, resolutionSetType) {
if (this.track) {
var tempResolutionSetType = resolutionSetType == ResolutionSetType.ideal ? ResolutionSetType
.ideal : ResolutionSetType.exact;
this.setResolutionWH(width, height, tempResolutionSetType);
var constraints = getApplyConstraints(this);
this.track.applyConstraints(constraints);
var maxBandwidth = this.maxBandwidth;
if (this.clientSetMaxBandwidth) {
maxBandwidth = this.clientSetMaxBandwidth;
}
this.sendVideoStreamControl(maxBandwidth, this.resolution.width, this.resolution.height);
}
}
/**
* @desc 设置带宽
* @param {int} maxBandwidth- 最大带宽
*/
setBandwidth = function (maxBandwidth) {
log.debug("===video.setBandwidth(),maxBandwidth:" + maxBandwidth + "Kpbs, videoId:" + this.id +
", userId:" + this.ownerId);
var self = this;
if ('RTCRtpSender' in window && 'setParameters' in window.RTCRtpSender.prototype) {
var sender = self.RTCRtpSender;
if (sender) {
var parameters = sender.getParameters();
//console.log("===video.setBandwidth(),parameters:",parameters);
if (!parameters.encodings) {
parameters.encodings = [{}];
} else if (parameters.encodings == []) {
parameters.encodings[0] = {};
}
////如果设置为unlimited
if (!maxBandwidth || maxBandwidth == 0) {
for (var i = 0; i < parameters.encodings.length; i++) {
delete parameters.encodings[i].maxBitrate;
}
} else {
if (!avdEngineHandle.isFirefox) {
var len = parameters.encodings.length;
if (len == 1) {
parameters.encodings[0].maxBitrate = maxBandwidth * 1000;
} else if (len == 2) {
parameters.encodings[0].maxBitrate = maxBandwidth * 500;
parameters.encodings[1].maxBitrate = maxBandwidth * 1000;
} else if (len == 3) {
parameters.encodings[0].maxBitrate = maxBandwidth * 250;
parameters.encodings[1].maxBitrate = maxBandwidth * 500;
parameters.encodings[2].maxBitrate = maxBandwidth * 1000;
}
}
if (avdEngineHandle.isFirefox) {
var len = parameters.encodings.length;
if (len == 1) {
parameters.encodings[0].maxBitrate = maxBandwidth * 1000;
} else if (len == 2) {
parameters.encodings[0].maxBitrate = maxBandwidth * 1000;
parameters.encodings[1].maxBitrate = maxBandwidth * 500;
} else if (len == 3) {
parameters.encodings[0].maxBitrate = maxBandwidth * 1000;
parameters.encodings[1].maxBitrate = maxBandwidth * 500;
parameters.encodings[2].maxBitrate = maxBandwidth * 250;
}
}
}
sender.setParameters(parameters).then(function () {
self.clientSetMaxBandwidth = maxBandwidth;
log.info("===video.setBandwidth(),带宽设置成功,clientSetMaxBandwidth:" +
maxBandwidth + "Kpbs, videoId:" + self.id + ", userId:" + self
.ownerId);
self.sendVideoStreamControl(maxBandwidth, self.resolution.width, self
.resolution.height);
}).catch(function (e) {
log.error("===video.setBandwidth(),带宽设置失败,clientSetMaxBandwidth:" +
maxBandwidth + "Kpbs, videoId:" + self.id + ", userId:" + self
.ownerId + ",error:", e);
});
} else {
//设备打开前,设置带宽的处理。
self.clientSetMaxBandwidth = maxBandwidth;
log.debug("===video.setBandwidth(),RTCRtpSender is null,clientSetMaxBandwidth:" +
maxBandwidth + "Kpbs, videoId:" + self.id + ", userId:" + self.ownerId);
}
}
};
/**
* @description 发PDU消息给服务器,如可以实现1080P的码流达到10M
* @async
* @param {Object} maxBandwidth 最大带宽
* @param {int} width 宽
* @param {int} height 高
*/
sendVideoStreamControl = function (maxBandwidth, width, height) {
log.info("===video.sendVideoStreamControl(),maxBandwidth:" + maxBandwidth + "Kpbs, width:" +
width + ", height:" + height + ", videoId:" + this.id + ", nodeId:" + this.roomHandle
.selfUser.nodeId);
var deferred = when.defer();
var self = this;
var orderId = getRandomNum(50000, 51000); //用于VideoStreamControlReq中的ID
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, self.id);
self.roomHandle.masterServer.sendVideoStreamControl(cameraUUID, maxBandwidth, width, height,
orderId);
clearTimeout(self.videoStreamControlTimer);
self.videoStreamControlTimer = setTimeout(function () {
log.error("===video.sendVideoStreamControl(),带宽设置发送服务器端超时. maxBandwidth:" +
maxBandwidth + "Kpbs, width:" + width + ", height:" + height +
", resoucceId:" + cameraUUID + ", nodeId:" + self.roomHandle.selfUser.nodeId
);
deferred.reject(new Error(ErrorConstant.operate_timeout.code,
'VIDEO_STREAM_CONTROL_REQ' + ErrorConstant.operate_timeout.message))
}, 3000)
self.roomHandle.addCallback(UserCallback.video_stream_control_rep_success, function (id) {
if (id == orderId) {
clearTimeout(self.videoStreamControlTimer);
log.info("===video.sendVideoStreamControl(), 带宽设置发送服务器端成功. maxBandwidth:" +
maxBandwidth + "Kpbs,width:" + width + ", height:" + height +
", resoucceId:" + cameraUUID + ", nodeId:" + self.roomHandle.selfUser
.nodeId);
deferred.resolve();
}
});
self.roomHandle.addCallback(UserCallback.video_stream_control_rep_error, function (error, id) {
if (id == orderId) {
clearTimeout(self.videoStreamControlTimer);
log.error("===video.sendVideoStreamControl(),带宽设置发送服务器端失败. maxBandwidth:" +
maxBandwidth + "Kpbs, width:" + width + ", height:" + height +
", resoucceId:" + cameraUUID + ", nodeId:" + self.roomHandle.selfUser
.nodeId);
deferred.reject(error);
}
});
}
/**
* @desc 预览摄像头。
* @async
* @param {Object} element - 视频控件对象
*/
preview = function (element) {
log.info("===video.preview()");
var deferred = when.defer();
var self = this;
// var error = doFsCheck(FTConstant.video, Error)
// if(error) {
// deferred.reject(error);
// return deferred.promise;
// }
// var resInt = parseInt(self.resolutionSelect);
// if(resInt > 640) {
// var error = doFsCheck(FTConstant.video_hd, Error)
// if(error) {
// deferred.reject(error);
// return deferred.promise;
// }
// }
if (self.track) {
if (self.status != StreamStatus.published) {
self.status = StreamStatus.opened;
}
self.element = element;
element.srcObject = self.getNewStreamByTrack();
deferred.resolve();
} else {
obtainUserMedia(null, self).then(function (stream) {
self.track = stream.videoStream.getVideoTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream.videoStream;
} else {
self.roomHandle.selfUser.stream.addTrack(self.track);
}
self.status = StreamStatus.opened;
self.element = element;
element.srcObject = self.getNewStreamByTrack();
deferred.resolve();
}).otherwise(function (e) {
deferred.reject(e);
});
}
return deferred.promise;
};
/**
* @desc 取消预览摄像头。
*/
unpreview = function () {
log.info("===video.unpreview()");
var self = this;
if (self._virtualBgProcessor) {
self._virtualBgProcessor.close();
self._virtualBgProcessor = null;
self._virtualBgEnabled = false;
self._virtualBgTrack = null;
}
if (self.element) {
self.element.srcObject = null;
self.element = null;
}
self._virtualBgOriginalStream = null;
self.status = StreamStatus.init;
};
/**
* @desc 发布视频流。
* @async
* @param {boolean} isRejoinCall 是否是重新加入会议。
*/
publish = function (isRejoinCall) {
log.info("===video.publish()");
var deferred = when.defer();
var self = this;
if (self.roomHandle.roomJoinState != RoomStateEnum.CONNECTED) {
var err = new Error(ErrorConstant.room_state_invalid);
log.debug("===video publish() 发布视频流异常, room state: ", self.roomHandle.roomJoinState);
deferred.reject(err);
return deferred.promise;
}
if (self.isPubState) {
log.debug("===video publish(),isPubState =true to return");
deferred.resolve();
return deferred.promise;
}
self.isPubState = true;
this.publishHandle(null, isRejoinCall).then(function () {
log.info("===video publish(),发布视频流成功,Id:" + self.id + ",name:" + self.name +
",ownerId:" + self.ownerId);
avdEngineHandle.loggerReport.info("video publish(),发布视频流成功,Id:" + self.id +
",设备名称:" + self.name + ",ownerId:" + self.ownerId);
deferred.resolve();
}).otherwise(function (error) {
self.isPubState = false;
log.error("===video publish(),发布视频流异常,Id:" + self.id + ",name:" + self.name +
",ownerId:" + self.ownerId + ",Error:" + JSON.stringify(error));
avdEngineHandle.loggerReport.error(
"video publish(),The published video stream is abnormal , Id:" + self.id +
",name:" + self.name + ",ownerId:" + self.ownerId + ",Error:" + JSON
.stringify(error));
deferred.reject(error);
});
return deferred.promise;
};
/**
* @desc 预览摄像头及发布流
* @async
* @param {Object} element - 视频控件对象
* @param {Object} isRejoinCall - 是否为重新加入会议
*/
previewAndPublish = function (element, isRejoinCall) {
log.info("===video.previewAndPublish()");
var deferred = when.defer();
var self = this;
if (self.roomHandle.roomJoinState != RoomStateEnum.CONNECTED) {
var errObj = ErrorConstant.room_state_invalid
errObj.message += ("(State: " + self.roomHandle.roomJoinState + ")")
var err = new Error(errObj);
log.debug("===video publish() 发布视频流异常, room state: " + self.roomHandle.roomJoinState);
deferred.reject(err);
return;
}
avdEngineHandle.loggerReport.debug("previewAndPublish()触发...");
// var error = doFsCheck(FTConstant.video, Error)
// if(error) {
// log.error(error);
// deferred.reject(error);
// return deferred.promise;
// }
// var resInt = parseInt(this.resolutionSelect);
// if(resInt > 640) {
// var error = doFsCheck(FTConstant.video_hd, Error)
// if(error) {
// log.error("===video.previewAndPublish(),error:",error);
// deferred.reject(error);
// return deferred.promise;
// }
// }
if (self.isPubState) {
log.debug("===video previewAndPublish(),isPubState =true to return");
deferred.resolve();
return deferred.promise;
}
self.isPubState = true;
this.publishHandle(element, isRejoinCall).then(function () {
log.info("===video previewAndPublish(),预览摄像头及发布流成功,videoId:" + self.id +
",videoName:" + self.name + ",userId:" + self.ownerId);
avdEngineHandle.loggerReport.info("video previewAndPublish(),预览摄像头及发布流成功,videoId:" +
self.id + ",videoName:" + self.name + ",userId:" + self.ownerId);
deferred.resolve();
}).otherwise(function (error) {
self.isPubState = false;
log.error("===video previewAndPublish(),预览摄像头及发布视频流异常,videoId:" + self.id +
",videoName:" + self.name + ",userId:" + self.ownerId + ",error:" + JSON
.stringify(error));
avdEngineHandle.loggerReport.error(
"video previewAndPublish(), Previewing cameras and publishing video streams are abnormal, videoId:" +
self.id + ",videoName:" + self.name + ",userId:" + self.ownerId +
",error:" + JSON.stringify(error));
deferred.reject(error);
});
return deferred.promise;
};
/**
* publish() and previewAndPublish() 的实现
* @param {Object} element video控件
* @param {Boolean} isRejoinCall 是否是重新加会(重连)的时候调用
* @ignore
*/
publishHandle = function (element, isRejoinCall) {
var deferred = when.defer();
var orderId = getRandomNum(10000, 11000); //用于PubRoomResourceMsgRep中的ID
var self = this;
var bit = self.roomHandle.calInitVideoBitrate(self.resolution.width, self.resolution.height);
self.maxBandwidth = bit.maxBit;
if (self.status == StreamStatus.init) {
if (self.track) {
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, self.id);
var videoStream = self.getNewStreamByTrack();
self.roomHandle.masterServer.videoPublishHandle(cameraUUID, videoStream, orderId);
clearTimeout(self.publishTimer);
self.publishTimer = setTimeout(function () {
self.isPubState = false;
log.debug(
"===video publishHandle(status == StreamStatus.init,track!=null),pub_roomresourcemsg_rep timeout"
);
deferred.reject(new Error(ErrorConstant.operate_timeout.code,
'Publish video' + ErrorConstant.operate_timeout.message))
}, 3000)
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success, function (
id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
if (element != undefined) {
self.status = StreamStatus.opened;
self.element = element;
element.srcObject = videoStream;
}
self.status = StreamStatus.published;
if (!self.roomHandle.hasObject(self.roomHandle.pubVideos, self)) {
self.roomHandle.pubVideos.push(self);
}
log.info(
"===video previewAndPublish(),callback camera_status_notify,self.track != null, status:" +
self.status + ",videoId:" + self.id + ",videoName:" + self
.name + ",userId:" + self.ownerId);
self.roomHandle.selfUser.eventEmitter.emit(UserCallback
.camera_status_notify, self.status, self.id, self.name, self
.roomHandle.selfUser.id);
deferred.resolve();
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error, function (
error, id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
deferred.reject(error);
}
});
} else {
obtainUserMedia(null, self).then(function (stream) {
self.track = stream.videoStream.getVideoTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream.videoStream;
} else {
self.roomHandle.selfUser.stream.addTrack(self.track);
}
self.status = StreamStatus.opened;
var videoStream = self.getNewStreamByTrack();
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, self.id);
self.roomHandle.masterServer.videoPublishHandle(cameraUUID, videoStream,
orderId);
clearTimeout(self.publishTimer);
self.publishTimer = setTimeout(function () {
self.isPubState = false;
log.debug(
"===video publishHandle(status == StreamStatus.init,track==null),pub_roomresourcemsg_rep timeout"
);
deferred.reject(new Error(ErrorConstant.operate_timeout.code,
'Publish video' + ErrorConstant.operate_timeout
.message))
}, 3000)
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success,
function (id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
if (element != undefined) {
self.status = StreamStatus.opened;
self.element = element;
element.srcObject = videoStream;
}
self.status = StreamStatus.published;
if (!self.roomHandle.hasObject(self.roomHandle.pubVideos,
self)) {
self.roomHandle.pubVideos.push(self);
}
log.info(
"===video previewAndPublish(),callback camera_status_notify,self.track= null, status:" +
self.status + ",videoId:" + self.id +
",videoName:" + self.name + ",userId:" + self
.ownerId);
self.roomHandle.selfUser.eventEmitter.emit(UserCallback
.camera_status_notify, self.status, self.id, self
.name, self.roomHandle.selfUser.id);
deferred.resolve();
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error,
function (error, id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
deferred.reject(error);
}
});
}).otherwise(function (e) {
deferred.reject(e);
});
}
} else if (self.status == StreamStatus.opened) {
if (self.track) {
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, self.id);
self.roomHandle.masterServer.videoPublishHandle(cameraUUID, self.getNewStreamByTrack(),
orderId);
clearTimeout(self.publishTimer);
self.publishTimer = setTimeout(function () {
self.isPubState = false;
log.debug(
"===video publishHandle(status == StreamStatus.opened,track!=null),pub_roomresourcemsg_rep timeout"
);
deferred.reject(new Error(ErrorConstant.operate_timeout.code,
'Publish video' + ErrorConstant.operate_timeout.message))
}, 3000)
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success, function (
id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
self.status = StreamStatus.published;
if (!self.roomHandle.hasObject(self.roomHandle.pubVideos, self)) {
self.roomHandle.pubVideos.push(self);
}
self.roomHandle.selfUser.eventEmitter.emit(UserCallback
.camera_status_notify, self.status, self.id, self.name, self
.roomHandle.selfUser.id);
deferred.resolve();
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error, function (
error, id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
deferred.reject(error);
}
});
} else {
obtainUserMedia(null, self).then(function (stream) {
self.track = stream.videoStream.getVideoTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream.videoStream;
} else {
self.roomHandle.selfUser.stream.addTrack(self.track);
}
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, self.id);
self.roomHandle.masterServer.videoPublishHandle(cameraUUID, self
.getNewStreamByTrack(), orderId);
clearTimeout(self.publishTimer);
self.publishTimer = setTimeout(function () {
self.isPubState = false;
log.debug(
"===video publishHandle(status == StreamStatus.opened,track==null),pub_roomresourcemsg_rep timeout"
);
deferred.reject(new Error(ErrorConstant.operate_timeout.code,
'Publish video' + ErrorConstant.operate_timeout
.message))
}, 3000);
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success,
function (id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
self.status = StreamStatus.published;
if (!self.roomHandle.hasObject(self.roomHandle.pubVideos,
self)) {
self.roomHandle.pubVideos.push(self);
}
self.roomHandle.selfUser.eventEmitter.emit(UserCallback
.camera_status_notify, self.status, self.id, self
.name, self.roomHandle.selfUser.id);
deferred.resolve();
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error,
function (error, id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
deferred.reject(error);
}
});
}).otherwise(function (e) {
deferred.reject(e);
});
}
} else if (self.status == StreamStatus.published && isRejoinCall) {
log.debug("===TTTTTTTTT00000000000000");
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, self.id);
self.roomHandle.masterServer.videoPublishHandle(cameraUUID, self.getNewStreamByTrack(),
orderId);
clearTimeout(self.publishTimer);
self.publishTimer = setTimeout(function () {
self.isPubState = false;
log.debug(
"===video publishHandle(status == StreamStatus.opened,&& room rejoin or reconnecting),pub_roomresourcemsg_rep timeout"
);
deferred.reject(new Error(ErrorConstant.operate_timeout.code, 'Publish video' +
ErrorConstant.operate_timeout.message))
}, 3000)
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success, function (id) {
if (id == orderId) {
clearTimeout(self.publishTimer);
self.roomHandle.selfUser.eventEmitter.emit(UserCallback
.camera_status_notify, self.status, self.id, self.name, self
.roomHandle.selfUser.id);
deferred.resolve();
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error, function (error,
id) {
if (id == orderId) {
log.debug(
"===video publishHandle(status == StreamStatus.opened,&& && room rejoin or reconnecting),pub_roomresourcemsg_rep_error"
);
clearTimeout(self.publishTimer);
deferred.reject(error);
}
});
} else {
log.debug("===video publishHandle() status: " + self.status);
}
return deferred.promise;
}
/**
* @desc 取消发布视频流。
* @async
*/
unpublish = function () {
log.info("===video.unpublish()");
var deferred = when.defer();
var orderId = getRandomNum(11001, 12000);
var self = this;
self.maxBandwidth = null;
if (this.status == StreamStatus.published || (this.status == StreamStatus.init && (this.track ||
this.track != undefined))) {
self.isPubState = false;
arrayUtil.objectSplice(self.roomHandle.pubVideos, this.id);
this.status = StreamStatus.init;
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, this.id);
self.roomHandle.masterServer.videoUnpublishHandle(cameraUUID, this.getNewStreamByTrack(),
orderId);
clearTimeout(self.unpublishTimer);
self.unpublishTimer = setTimeout(function () {
self.isPubState = false;
log.debug("===video unpublish, unpub_roomresourcemsg_rep timeout");
deferred.reject(new Error(ErrorConstant.operate_timeout.code,
'Unpublish video' + ErrorConstant.operate_timeout.message))
}, 3000);
self.roomHandle.addCallback(EngineCallback.unpub_roomresourcemsg_rep_success, function (id) {
if (id == orderId) {
clearTimeout(self.unpublishTimer);
//第三方导入流unpub时,不对本地track清场,在user.deleteCustomVideoTrack()时统一清场。
if (self.id.indexOf('custom_video') !== -1) {
var virtualBgWasActive = self._virtualBgWasEnabled;
if (!virtualBgWasActive) {
var stream = self.roomHandle.selfUser.stream;
if (stream) {
stream.getTracks().forEach(function (track) {
if (track.id == self.track.id) {
track.stop();
}
});
}
}
if (self.element) {
if (!virtualBgWasActive) {
var elementStream = self.element.srcObject;
if (elementStream) {
elementStream.getTracks().forEach(function (track) {
track.stop();
});
}
self.element.srcObject = null;
self.element = null;
}
}
if (!virtualBgWasActive && self.track) {
self.track.stop();
self.track = null;
}
self._virtualBgWasEnabled = false;
self.roomHandle.currBandwidth = null;
}
log.info("===video unpublish(),取消发布视频流成功,videoId:" + self.id +
",videoName:" + self.name + ",userId:" + self.ownerId);
avdEngineHandle.loggerReport.info("video unpublish(),取消发布视频流成功,videoId:" +
self.id + ",videoName:" + self.name + ",userId:" + self.ownerId);
self.roomHandle.selfUser.eventEmitter.emit(UserCallback
.camera_status_notify, self.status, self.id, self.name, self
.roomHandle.selfUser.id);
setTimeout(function () {
deferred.resolve();
}, 50);
}
});
self.roomHandle.addCallback(EngineCallback.unpub_roomresourcemsg_rep_error, function (error,
id) {
if (id == orderId) {
clearTimeout(self.unpublishTimer);
log.error("===video unpublish, unpub_roomresourcemsg_rep_error,videoId:" +
self.id + ",videoName:" + self.name + ",userId:" + self.ownerId);
deferred.reject(error);
}
});
}
return deferred.promise;
};
/**
* @desc 订阅视频流
* @async
* @param {int} videoQuality- 视频质量举值,可以不填写,默认为VideoQualityType.high
*/
subscrible = function (videoQuality) {
log.info("===video subscrible(),订阅视频流,videoId:" + this.id + ",userId:" + this.ownerId +
",videoQuality:" + videoQuality);
avdEngineHandle.loggerReport.info("video subscrible(),订阅视频流,videoId:" + this.id + ",userId:" +
this.ownerId + ",videoQuality:" + videoQuality);
var deferred = when.defer();
var orderId = getRandomNum(12001, 13000); //用于subRoomResourceMsgRep中的ID
var self = this;
if (videoQuality) {
self.roomHandle.masterServer.videoSubscribleHandle(self.resourceInfo, videoQuality,
orderId);
} else {
self.roomHandle.masterServer.videoSubscribleHandle(self.resourceInfo, VideoQualityType.high,
orderId);
}
clearTimeout(self.subscribleTimer);
self.subscribleTimer = setTimeout(function () {
log.debug("===video subscrible, sub_roomresourcemsg_rep_success timeout");
deferred.reject(new Error(ErrorConstant.operate_timeout.code, 'Subscribe video' +
ErrorConstant.operate_timeout.message))
}, 3000);
self.roomHandle.addCallback(EngineCallback.sub_roomresourcemsg_rep_success, function (id) {
if (id == orderId) {
clearTimeout(self.subscribleTimer);
setTimeout(function () {
log.info("===video subscrible(),订阅视频流成功,videoId:" + self.id +
",userId:" + self.ownerId + ",videoQuality:" + videoQuality);
deferred.resolve();
}, 50);
}
});
self.roomHandle.addCallback(EngineCallback.sub_roomresourcemsg_rep_error, function (error, id) {
if (id == orderId) {
clearTimeout(self.subscribleTimer);
deferred.reject(error);
}
});
return deferred.promise;
};
/**
* @desc 取消订阅视频流。
* @async
*/
unsubscrible = function () {
log.info("===video unsubscrible(),取消订阅视频流,videoId:" + this.id + ",userId:" + this.ownerId);
avdEngineHandle.loggerReport.info("video unsubscrible(),取消订阅视频流,videoId:" + this.id +
",userId:" + this.ownerId);
var deferred = when.defer();
var orderId = getRandomNum(13001, 14000);
var self = this;
self.roomHandle.masterServer.videoUnsubscribleHandle(self.resourceInfo, orderId);
clearTimeout(self.unsubscribleTimer);
self.unsubscribleTimer = setTimeout(function () {
log.debug("===video unsubscrible, unsub_roomresourcemsg_rep_success timeout");
deferred.reject(new Error(ErrorConstant.operate_timeout.code, 'Unsubscribe video' +
ErrorConstant.operate_timeout.message))
}, 3000);
self.roomHandle.addCallback(EngineCallback.unsub_roomresourcemsg_rep_success, function (id) {
if (id == orderId) {
clearTimeout(self.unsubscribleTimer);
setTimeout(function () {
log.info("===video unsubscrible(),取消订阅视频流成功,videoId:" + self.id +
",userId:" + self.ownerId);
deferred.resolve();
}, 50);
}
});
self.roomHandle.addCallback(EngineCallback.unsub_roomresourcemsg_rep_error, function (error,
id) {
if (id == orderId) {
clearTimeout(self.unsubscribleTimer);
deferred.reject(error);
}
});
return deferred.promise;
};
/**
* @desc 禁视频
*/
muteCamera = function () {
log.info("===video.muteCamera()");
var self = this;
if (this.status == StreamStatus.published) {
this.status = StreamStatus.muted;
self.roomHandle.selfUser.eventEmitter.emit(UserCallback.camera_status_notify, this.status,
this.id, this.name, self.roomHandle.selfUser.id);
var stream = self.roomHandle.selfUser.stream;
stream.getTracks().forEach(function (track) {
if (track.id == self.track.id) {
track.enabled = false;
}
});
if (self.element) {
var elementStream = self.element.srcObject;
if (elementStream) {
elementStream.getTracks().forEach(function (track) {
if (track.id == self.track.id) {
track.enabled = false;
}
});
}
}
if (self.track) {
self.track.enabled = false;
}
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, this.id);
self.roomHandle.masterServer.updateWebcamMsg(self.roomHandle.selfUser.nodeId, cameraUUID,
this);
log.info("===video muteCamera(),禁视频. Id:" + self.id + ",ownerId:" + self.ownerId +
",status:" + self.status);
avdEngineHandle.loggerReport.info("video muteCamera(),禁视频. Id:" + self.id + ",ownerId:" +
self.ownerId + ",status:" + self.status);
}
}
/**
* @desc 取消禁视频
*/
unmuteCamera = function () {
log.info("===video.unmuteCamera()");
var self = this;
if (this.status == StreamStatus.muted) {
this.status = StreamStatus.published;
self.roomHandle.selfUser.eventEmitter.emit(UserCallback.camera_status_notify, this.status,
this.id, this.name, self.roomHandle.selfUser.id);
var stream = self.roomHandle.selfUser.stream;
stream.getTracks().forEach(function (track) {
if (track.id == self.track.id) {
track.enabled = true;
}
});
if (self.element) {
var elementStream = self.element.srcObject;
if (elementStream) {
elementStream.getTracks().forEach(function (track) {
if (track.id == self.track.id) {
track.enabled = true;
}
});
}
}
if (self.track) {
self.track.enabled = true;
}
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, this.id);
self.roomHandle.masterServer.updateWebcamMsg(self.roomHandle.selfUser.nodeId, cameraUUID,
this);
log.info("===videe unmuteCamera(),取消禁视频. Id:" + self.id + ",ownerId:" + self.ownerId +
",status:" + self.status);
avdEngineHandle.loggerReport.info("video unmuteCamera(),取消禁视频. Id:" + self.id +
",ownerId:" + self.ownerId + ",status:" + self.status);
}
};
/**
* @desc 获取禁视频状态
*/
ismuteCamera = function () {
var ismute = false;
if (this.status == StreamStatus.muted) {
ismute = true;
}
return ismute;
};
/**
* @desc 更改订阅视频的视频质量
* @param {int} quality- 视频质量枚举值,如可传VideoQualityType.high
*/
changeSubscribedVideoQuality = function (quality) {
var self = this;
if (self.roomHandle.selfUser.id == this.ownerId) {
log.debug("===video changeSubscribedVideoQuality(),自己发布的视频不能更改。id:" + this.id +
",ownerId:" + this.ownerId + ",quality:" + quality);
} else {
log.debug("===video changeSubscribedVideoQuality(),更改订阅视频的视频质量,id:" + this.id +
",ownerId:" + this.ownerId + ",quality:" + quality);
avdEngineHandle.loggerReport.debug("video changeSubscribedVideoQuality(),更改订阅视频的视频质量,id:" +
this.id + ",ownerId:" + this.ownerId + ",quality:" + quality);
self.roomHandle.masterServer.changeSubscribedVideoQuality(this.resourceInfo, this
.quality2Level(quality));
}
}
/**
* @desc 更改发布视频源端的视频质量
* @param level 级别
*/
changePublishVideoQuality = function (level) {
log.info("===video.changePublishVideoQuality(),videoId:" + this.id + ",userId:" + this.ownerId +
",level:" + level);
var self = this;
//log.debug("+++DS1111111111,videoId:"+self.id+",level:"+level);
self.currentVideoQuality = level;
if (self.changePublishVideoQualityInterval) {
clearInterval(self.changePublishVideoQualityInterval);
self.changePublishVideoQualityInterval = null;
}
self.changePublishVideoQualityInterval = setInterval(function () {
if (self.currentVideoQuality) {
if (self.roomHandle) {
//log.debug("+++DS222222222,videoId:"+self.id+",level:"+level);
self.changePublishVideoQualityHandle(self.currentVideoQuality);
self.currentVideoQuality = null;
clearInterval(self.changePublishVideoQualityInterval);
self.changePublishVideoQualityInterval = null;
}
}
}, 1000);
}
changePublishVideoQualityHandle = function (level) {
var self = this;
var quality = this.level2Quality(level);
log.debug("===video.changePublishVideoQualityHandle(),userName:" + self.roomHandle.selfUser
.name + ",videoId:" + this.id + ",level:" + level + ",resolution.width:" + this
.resolution.width + ",resolution.height:" + this.resolution.height);
var maxBandwidth = null;
if (this.clientSetMaxBandwidth) {
maxBandwidth = this.clientSetMaxBandwidth;
} else {
maxBandwidth = this.maxBandwidth;
}
if (maxBandwidth) {
var currMaxBandwidth = 0;
if (quality == VideoQualityType.high) {
currMaxBandwidth = maxBandwidth;
} else if (quality == VideoQualityType.normal) {
currMaxBandwidth = parseInt(maxBandwidth / 2);
} else if (quality == VideoQualityType.low) {
currMaxBandwidth = parseInt(maxBandwidth / 4);
}
if ('RTCRtpSender' in window && 'setParameters' in window.RTCRtpSender.prototype) {
var sender = this.RTCRtpSender;
if (sender) {
var parameters = sender.getParameters();
if (!parameters.encodings) {
parameters.encodings = [{}];
} else if (parameters.encodings == []) {
parameters.encodings[0] = {};
}
if (!maxBandwidth || maxBandwidth == 0) {
if (parameters.encodings[0]) {
delete parameters.encodings[0].maxBitrate;
}
} else {
if (parameters.encodings[0]) {
parameters.encodings[0].maxBitrate = currMaxBandwidth * 1000;
}
}
sender.setParameters(parameters).then(function () {
log.info("===video.changePublishVideoQualityHandle(),userName:" + self
.roomHandle.selfUser.name + ",videoId:" + self.id + ",level:" +
level + ",带宽修改成功,maxBandwidth:" + currMaxBandwidth);
}).catch(function (e) {
log.error("===video.changePublishVideoQualityHandle(),userName:" + self
.roomHandle.selfUser.name + ",videoId:" + self.id + ",level:" +
level + ",带宽修改失败,maxBandwidth:" + currMaxBandwidth + ",error:",
e);
});
}
}
}
var detect = avdEngineHandle.browserDetect.detect ? avdEngineHandle.browserDetect.detect :
avdEngineHandle.getBrowserDetect();
// iphone手机,ios版本<17.0时,因服务器下发视频等级调整分辨率时,会引发 iphone手机打开前置,视频看着是很大的横屏。所以这里做了相应的处理。
if (detect.osName == 'iOS' && versionStringCompare(detect.browser.fullVersion, '17') < 0) {
log.debug("===video.changePublishVideoQualityHandle(), current os: " + detect.osName + ' ' +
detect.osVersion + ' to ignore');
} else {
var resolutionModified = {
width: self.resolution.width,
height: self.resolution.height
}
if (quality == VideoQualityType.high) {
resolutionModified.width = self.resolution.width;
resolutionModified.height = self.resolution.height;
} else if (quality == VideoQualityType.normal) {
if ((self.resolution.width / 2) >= 320 || (self.resolution.height / 2) >= 180) {
resolutionModified.width = self.resolution.width / 2;
resolutionModified.height = self.resolution.height / 2;
} else {
resolutionModified.width = 320;
resolutionModified.height = 180;
}
} else if (quality == VideoQualityType.low) {
if ((self.resolution.width / 4) >= 320 || (self.resolution.height / 4) >= 180) {
resolutionModified.width = self.resolution.width / 4;
resolutionModified.height = self.resolution.height / 4;
} else {
resolutionModified.width = 320;
resolutionModified.height = 180;
}
}
// self.setResolutionWH(resolutionModified.width, resolutionModified.height, ResolutionSetType.ideal, true)
// self.applyConstraints();
self.adjustVideoResoutionRealTime(resolutionModified.width, resolutionModified.height);
log.debug("===video.changePublishVideoQualityHandle(),userName:" + self.roomHandle.selfUser
.name + ",videoId:" + self.id + ",level:" + level + ",修改视频分辨率, 分辨率宽:" +
resolutionModified.width + ",分辨率高:" + resolutionModified.height);
}
}
quality2Level = function (quality) {
var level =
3; // 1 terrible, 2 worse, 3 bad, 4 normal. video_level is Temporal level, 1-4, bigger is better
if (quality == VideoQualityType.high) {
level = 4;
} else if (quality == VideoQualityType.normal) {
level = 3;
} else if (quality == VideoQualityType.low) {
level = 2;
}
return level;
}
level2Quality = function (level) {
var quality = VideoQualityType.normal;
if (level >= 4) {
quality = VideoQualityType.high;
} else if (level == 3) {
quality = VideoQualityType.normal;
} else {
quality = VideoQualityType.low;
}
return quality;
}
/**
* @desc 摄像头数据更改
* @param {int} level
* @param {String} description
*/
updateCameraData = function (level, description) {
var self = this;
log.debug("===video.updateCameraData(),level:" + level + ",description:" + description);
this.level = level;
this.description = description;
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, this.id);
if (self.roomHandle && self.roomHandle.selfUser) {
this.saveVideoCustomConfig('description', description)
self.roomHandle.masterServer.updateWebcamMsg(self.roomHandle.selfUser.nodeId, cameraUUID,
this);
}
}
/**
* @desc 更新导出时视频流的名称及昵称
* @async
* @param {String} streamName 视频流名称
* @param {String} streamNickName 视频流昵称
*/
updateExporterStreamName = function (streamName, streamNickName) {
var deferred = when.defer();
var self = this;
self.streamName = streamName;
self.streamNickName = streamNickName;
self.roomHandle.masterServer.updateExporterStreamName(self).then(function () {
deferred.resolve();
}).otherwise(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
/**
* @desc 视频多流时,设置最大空域层数
* @param {int} spatialLayer 最大空域层数
*/
setMaxSpatialLayer = function (spatialLayer) {
if (avdEngineHandle.simulcastEnabled) {
if (this.RTCRtpSender) {
var parameters = this.RTCRtpSender.getParameters();
parameters.encodings.forEach(function (encoding, idx) {
log.debug("===video.setMaxSpatialLayer(),spatialLayer:" + spatialLayer +
",idx:" + idx);
if (idx < spatialLayer) {
encoding.active = true;
} else {
encoding.active = false;
}
});
this.RTCRtpSender.setParameters(parameters);
}
} else {
log.debug("===video.setMaxSpatialLayer(),设置无效因为simulcastEnabled=false");
}
}
/**
* @description 视频多流时,设置空域每层开启的状态。状态值定义: 0-维持原状, 1-unactive, 2-active
* @param {int} baseActive - 第一层状态
* @param {int} ass1Active - 第二层状态
* @param {int} ass2Active - 第三层状态
*/
setSpatialLayerActive = function (baseActive, ass1Active, ass2Active) {
if (avdEngineHandle.isChrome && avdEngineHandle.majorVersion >= 73 && avdEngineHandle
.majorVersion <= 76) {
log.debug(
"===video.setSpatialLayerActive(),chrome版本为73-76,支持多流但不支持服务器下发的各路流active/unactive设置"
);
return;
}
var self = this;
log.debug("===video.setSpatialLayerActive(),baseActive:" + baseActive + ",ass1Active:" +
ass1Active + ",ass2Active:" + ass2Active);
if (avdEngineHandle.simulcastEnabled) {
var spatialLayerActiveObj = {};
spatialLayerActiveObj.baseActive = baseActive;
spatialLayerActiveObj.ass1Active = ass1Active;
spatialLayerActiveObj.ass2Active = ass2Active;
self.spatialLayerActiveList.push(spatialLayerActiveObj);
setTimeout(function () {
if (!self.spatialLayerActiveDoing) {
self.spatialLayerActiveDoing = true;
var baseActiveNew = 0;
var ass1ActiveNew = 0;
var ass2ActiveNew = 0;
self.spatialLayerActiveList.forEach(function (spatialLayerActive) {
if (spatialLayerActive.baseActive != 0) {
baseActiveNew = spatialLayerActive.baseActive;
}
if (spatialLayerActive.ass1Active != 0) {
ass1ActiveNew = spatialLayerActive.ass1Active;
}
if (spatialLayerActive.ass2Active != 0) {
ass2ActiveNew = spatialLayerActive.ass2Active;
}
});
log.debug("===video.setSpatialLayerActive(),baseActiveNew:" +
baseActiveNew + ",ass1ActiveNew:" + ass1ActiveNew +
",ass2ActiveNew:" + ass2ActiveNew);
if (self.RTCRtpSender) {
log.debug("===video.setSpatialLayerActive(),RTCRtpSender != null");
self.rtcRtpSenderIntervalId = setInterval(
function () {
var parameters = self.RTCRtpSender.getParameters();
if (parameters.encodings.length > 0) {
log.debug(
"===video.setSpatialLayerActive(),parameters.encodings:",
JSON.stringify(parameters.encodings));
clearInterval(self.rtcRtpSenderIntervalId);
self.rtcRtpSenderIntervalId = null;
parameters.encodings.forEach(function (encoding, idx) {
if (idx == 0) {
if (baseActiveNew == 2) {
log.debug(
"===video.setSpatialLayerActive(),baseActive active to true"
);
encoding.active = true;
} else if (baseActiveNew == 1) {
log.debug(
"===video.setSpatialLayerActive(),baseActive active to false"
);
encoding.active = false;
}
}
if (idx == 1) {
if (ass1ActiveNew == 2) {
log.debug(
"===video.setSpatialLayerActive(),ass1Active active to true"
);
encoding.active = true;
} else if (ass1ActiveNew == 1) {
log.debug(
"===video.setSpatialLayerActive(),ass1Active active to false"
);
encoding.active = false;
}
}
if (idx == 2) {
if (ass2ActiveNew == 2) {
log.debug(
"===video.setSpatialLayerActive(),ass2Active active to true"
);
encoding.active = true;
} else if (ass2ActiveNew == 1) {
log.debug(
"===video.setSpatialLayerActive(),ass2Active active to false"
);
encoding.active = false;
}
}
});
self.RTCRtpSender.setParameters(parameters);
self.spatialLayerActiveDoing = false;
}
}, 200);
}
}
}, 1000);
} else {
log.debug("===video.setSpatialLayerActive(),设置无效因为simulcastEnabled=false");
}
}
/**
* @desc 视频流渲染进视频控件
* @param {Object} element - 视频控件对象
* @param {Object} stream - 视频流
*/
attachVideoElementMediaStream = function (element, stream) {
if (stream != null) {
this.element = element;
}
if (element) {
element.srcObject = stream;
}
};
saveVideoCustomConfig = function (property, value) {
var self = this;
if (self.roomHandle && self.roomHandle.selfUser) {
var videoCustomSet = self.roomHandle.selfUser.reJoinVideoId2CustomSet[this.id];
if (!videoCustomSet) {
self.roomHandle.selfUser.reJoinVideoId2CustomSet[this.id] = {};
}
self.roomHandle.selfUser.reJoinVideoId2CustomSet[this.id][property] = value;
}
}
/**
* @desc 用于无人订阅当前视频自动调整码率时,实时调整当前视频track的分辨率宽高,不影响视频原设置的分辨率
* @param {*} width
* @param {*} height
* @ignore
*/
adjustVideoResoutionRealTime = function (width, height) {
if (isNaN(width) || isNaN(height)) {
return;
}
log.info('===video.adjustVideoResoutionRealTime, width:' + width, ',height:' + height);
var self = this;
if (self.track) {
var constraints = {
width: {
ideal: parseInt(width)
},
height: {
ideal: parseInt(height)
},
frameRate: {
ideal: self.frameRate
},
aspectRatio: {
ideal: self.aspectRatio
}
};
self.track.applyConstraints(constraints);
}
}
/**
* @desc 检查虚拟背景是否可用
* @returns {Promise<boolean>}
*/
canUseVirtualBackground = function() {
var Factory = ModuleBase.use("VirtualBgFactory");
var processorType = this._virtualBgOptions && this._virtualBgOptions.processorType;
var processor = Factory.createProcessor(processorType);
return processor.canUse();
};
/**
* @desc 启用虚拟背景
* @param {string} background - 背景图片 URL、颜色值或 'blur'
* @param {Object} options - 可选配置 { processorType: 'mediaPipe', quality: 'high'|'medium'|'low' }
* @returns {Promise}
*/
enableVirtualBackground = function(background, options) {
var self = this;
var defaultOptions = {
processorType: 'mediaPipe',
quality: 'high'
};
try {
self._virtualBgEnabled = true;
self._virtualBgImage = background;
self._virtualBgOptions = Object.assign(defaultOptions, options || {});
if (self.track) {
return self._applyVirtualBackground();
}
return Promise.resolve();
} catch (e) {
log.error('=== enableVirtualBackground EXCEPTION:', e);
return Promise.reject(e);
}
};
/**
* @desc 禁用虚拟背景
*/
disableVirtualBackground = function() {
var self = this;
log.info('=== disableVirtualBackground: START');
if (!self._virtualBgEnabled) {
log.info('=== disableVirtualBackground: not enabled');
return null;
}
self._virtualBgWasEnabled = true;
self._virtualBgEnabled = false;
if (self._virtualBgProcessor) {
self._virtualBgProcessor.close();
self._virtualBgProcessor = null;
}
self._virtualBgTrack = null;
self._virtualBgImage = null;
var resultStream = self._virtualBgOriginalStream;
self._virtualBgOriginalStream = null;
log.info('=== disableVirtualBackground: resultStream=' + (resultStream ? 'exists' : 'null'));
return resultStream;
};
/**
* @desc 启用美颜
* @param {Object} beautyConfig - 美颜配置 { smoothingLevel, whiteningLevel, cheekThinningLevel, eyeEnlargeLevel, lipEnlargeLevel, filterType, filterIntensity }
* @param {Object} options - 可选配置 { processorType: 'mediaPipe', quality: 'high'|'medium'|'low' }
* @returns {Promise}
*/
enableBeauty = function(beautyConfig, options) {
var self = this;
var defaultOptions = {
processorType: 'mediaPipe',
quality: 'high'
};
try {
self._beautyEnabled = true;
self._beautyConfig = beautyConfig || {};
self._beautyOptions = Object.assign(defaultOptions, options || {});
if (self.track) {
return self._applyBeauty();
}
return Promise.resolve();
} catch (e) {
log.error('=== enableBeauty EXCEPTION:', e);
return Promise.reject(e);
}
};
/**
* @desc 禁用美颜
* @returns {MediaStream|null} 禁用前的原始视频流
*/
disableBeauty = function() {
var self = this;
log.info('=== disableBeauty: START');
if (!self._beautyEnabled) {
log.info('=== disableBeauty: not enabled');
return null;
}
self._beautyWasEnabled = true;
self._beautyEnabled = false;
if (self._beautyProcessor) {
self._beautyProcessor.close();
self._beautyProcessor = null;
}
self._beautyTrack = null;
self._beautyConfig = null;
var resultStream = self._beautyOriginalStream;
self._beautyOriginalStream = null;
log.info('=== disableBeauty: resultStream=' + (resultStream ? 'exists' : 'null'));
return resultStream;
};
/**
* @desc 获取美颜处理器的 FPS
* @returns {number} 美颜 FPS,如果未启用美颜返回 0
*/
getBeautyFps = function() {
if (this._beautyProcessor && this._beautyEnabled) {
return this._beautyProcessor.getFps() || 0;
}
return 0;
};
/**
* @desc 检查美颜是否已启用
* @returns {boolean}
*/
isBeautyEnabled = function() {
return !!this._beautyEnabled;
};
/**
* @desc 获取当前美颜配置
* @returns {Object|null} 当前美颜配置,如果未启用返回 null
*/
getBeautyConfig = function() {
if (this._beautyEnabled && this._beautyConfig) {
return this._beautyConfig;
}
return null;
};
/**
* @desc 实时更新美颜参数(无需重新启用美颜)
* @param {Object} beautyConfig - 美颜配置 { smoothingLevel, whiteningLevel, cheekThinningLevel, eyeEnlargeLevel, lipEnlargeLevel, filterType, filterIntensity }
*/
setBeautyConfig = function(beautyConfig) {
if (!beautyConfig) {
return;
}
this._beautyConfig = Object.assign(this._beautyConfig || {}, beautyConfig);
if (this._beautyProcessor) {
this._beautyProcessor.setBeautyConfig(this._beautyConfig);
}
};
/**
* @desc 内部方法:应用美颜处理
* @returns {Promise}
* @ignore
*/
_applyBeauty = function() {
var self = this;
if (!self._beautyEnabled) {
return Promise.resolve();
}
if (!self._beautyProcessor) {
var Factory = ModuleBase.use("BeautyFactory");
var processorType = self._beautyOptions.processorType;
var processor = Factory.createProcessor(processorType);
if (self._beautyOptions.quality === 'low') {
processor.setBeautyConfig(Object.assign(processor.getBeautyConfig() || {}, {
processWidth: 256,
processHeight: 144,
targetFps: 15
}));
} else if (self._beautyOptions.quality === 'medium') {
processor.setBeautyConfig(Object.assign(processor.getBeautyConfig() || {}, {
processWidth: 384,
processHeight: 216,
targetFps: 24
}));
}
if (self._beautyConfig) {
processor.setBeautyConfig(self._beautyConfig);
}
self._beautyProcessor = processor;
return processor.canUse().then(function() {
return self._processBeauty();
}).catch(function(e) {
log.error('=== _applyBeauty canUse ERROR:', e);
throw e;
});
} else {
if (self._beautyConfig) {
self._beautyProcessor.setBeautyConfig(self._beautyConfig);
}
return self._processBeauty();
}
};
/**
* @desc 内部方法:处理美颜
* @returns {Promise}
* @ignore
*/
_processBeauty = function() {
var self = this;
var processor = self._beautyProcessor;
return new Promise(function(resolve, reject) {
var originalStream = new MediaStream([self.track]);
if (!originalStream) {
reject(new Error('No stream available'));
return;
}
self._beautyOriginalStream = originalStream;
log.info('=== _processBeauty: self.element=' + (self.element ? 'exists' : 'null'));
processor.onStreamUpdate = function(processedStream) {
log.info('=== _processBeauty: stream updated');
};
processor.pipeMediaStream(originalStream).then(function(processedStream) {
var newTrack = processedStream.getVideoTracks()[0];
self._beautyTrack = newTrack;
self.track = newTrack;
return processedStream;
}).then(resolve)["catch"](function(e) {
log.error('=== _processBeauty ERROR:', e);
reject(e);
});
});
};
/**
* @desc 内部方法:应用虚拟背景处理
* @returns {Promise}
* @ignore
*/
_applyVirtualBackground = function() {
var self = this;
if (!self._virtualBgEnabled) {
return Promise.resolve();
}
if (!self._virtualBgProcessor) {
var Factory = ModuleBase.use("VirtualBgFactory");
var processorType = self._virtualBgOptions.processorType;
var processor = Factory.createProcessor(processorType);
if (self._virtualBgOptions.quality === 'low') {
processor.setConfig({
processWidth: 256,
processHeight: 144,
targetFps: 15
});
} else if (self._virtualBgOptions.quality === 'medium') {
processor.setConfig({
processWidth: 384,
processHeight: 216,
targetFps: 24
});
}
self._virtualBgProcessor = processor;
return processor.canUse().then(function() {
return self._processVirtualBg();
}).catch(function(e) {
log.error('=== _applyVirtualBackground canUse ERROR:', e);
throw e;
});
}
return self._processVirtualBg();
};
/**
* @desc 内部方法:处理虚拟背景
* @returns {Promise}
* @ignore
*/
_processVirtualBg = function() {
var self = this;
var originalStream = new MediaStream([self.track]);
log.info('=== _processVirtualBg: self.element=' + (self.element ? 'exists' : 'null'));
if (self.element) {
self._virtualBgOriginalStream = self.element.srcObject;
log.info('=== _processVirtualBg: saved _virtualBgOriginalStream');
self.element.srcObject = originalStream;
log.info('=== _processVirtualBg: set element to originalStream');
}
return self._virtualBgProcessor.pipeMediaStream(
originalStream,
self._virtualBgImage
).then(function(processedStream) {
var newTrack = processedStream.getVideoTracks()[0];
self._virtualBgTrack = newTrack;
return processedStream;
}).catch(function(e) {
log.error('===_processVirtualBg error:', e);
throw e;
});
};
}
return Video;
});