ModuleBase.define("User", ["Video", "Audio", "Screen", "ScreenAudio", "Board", "Error"], function(Video, Audio, Screen, ScreenAudio, Board, Error) {
/**
* @desc 用户User构造函数。
* @constructor
* @alias User
* @param {String} id - 用户id
* @param {String} name - 用户名称
* @param {String} data - 用户扩展字段
*/
var User = function(id, name, data, roomHandle) {
this.roomHandle = roomHandle;
this.id = id;
this.name = name;
this.data = data;
this.eventEmitter = new EventEmitter();
this.subVideoTrack2ResourceInfo = {}; //用于定位pc.onaddTrack,pc.onremoveTrack时的视频track与所属用户的绑定,map(trackId:resourceInfo)集 //TODO
this.subAudioTrack2ResourceInfo = {}; //用于定位pc.onaddTrack,pc.onremoveTrack时的音频track与所属用户的绑定,map(trackId:resourceInfo)集 //TODO
this.subScreenTrack2ResourceInfo = {}; //用于定位pc.onaddTrack,pc.onremoveTrack时的桌面共享track与所属用户的绑定,map(trackId:resourceInfo)集 //TODO
this.reJoinSubVideoIds = [];
this.reJoinSubAudioIds = [];
this.reJoinSubScreenIds = [];
this.reJoinVideoId2CustomSet = {}; //重新加会时,原应用层设置的视频相关参数信息(width、height、setType、frameRate、aspectRatio、description)依然有效,用于定位videoId与CustomSet对象的绑定,map(videoId:CustomSet)集
this.reJoinScreenDescription = null; //重新加会时,原应用层设置的桌面共享流名称依然有效,不需要应用层再次设置
this.boards = [];
this.screenStream = null; //TODO 用于唇音同步,当前用户所有打开的桌面共享、桌面共享音频等track都加入到这个stream中
roomHandle.cleanupBoardByReJoinConnected = updateBoardMsgRepCheck(avdEngineHandle.restServerInfo.boxVersion);
this.init();
};
/**
* @des user对象初始化
* @ignore
*/
User.prototype.init = function() {
this.nodeId = null;
this.role = null;
this.userAgent = null;
this.stream = null; //TODO 用于唇音同步,当前用户所有打开的视频、音频都加入到这个stream中
this.customStream = null;//TODO 用于唇音同步,当前用户所有导入的第三方视频、音频都加入到这个stream中
this.waitingDelete = false;
this.videos = [];
this.audio;
this.screen;
this.customAudios = []; //用于存放所有导入的第三方音频。
this.toMCUPC; //与MCU服务器间的PeerConnection
this.mulLanding = false; //user id 多地登陆时,后加会者会踢掉前加会者
this.deviceId2VideoSdpSsrc = {};
this.audioSdpSsrc;
this.screenSdpSsrc;
this.subVideoIds = []; //我订阅视频的videoId数组
this.subAudioIds = []; //我订阅音频的audioId数组
this.subScreenIds = []; //我订阅桌面共享的screenId数组
// this.subVideoStream2ResourceInfo = {}; //用于定位pc.onaddstream返回视频stream与所属用户的绑定,map(stream.id:resourceInfo)集
// this.subAudioStream2ResourceInfo = {}; //用于定位pc.onaddstream返回音频stream与所属用户的绑定,map(stream.id:resourceInfo)集
// this.subScreenStream2ResourceInfo = {}; //用于定位pc.onaddstream返回桌面共享stream与所属用户的绑定,map(stream.id:resourceInfo)集
//与服务器重连相关
// this.reconnectionSubVideoIds= null;
// this.reconnectionSubAudioIds= null;
// this.reconnectionSubScreenIds= null;
// this.reconnectionUsers= [];
//下面都是P2P会议模式下的定义
this.toP2PPCs = {}; //自己与其他用户间的peerConnection链接集,map(nodeId:peerConnection)集
this.sdpP2PMap = {}; //map(nodeId:Sdp)集
this.videoSdpSsrcMapP2PMap = {}; //map(nodeId:videoSdpSsr)集
this.audioSdpSsrcP2PMap = {}; //map(nodeId:audioSdpSsr)集
this.screenSdpSsrcP2PMap = {}; //map(nodeId:screenSdpSsr)集
this.subMyVideosP2P = []; //订阅我视频的nodeId集
this.subMyAudiosP2P = []; //订阅我音频的nodeId集
this.subMyScreensP2P = []; //订阅我桌面共享的nodeId集
}
/**
* @desc 创建video对象
* @param {String} id - video id
* @param {String} name - video name
* @ignore
*/
User.prototype.createVideo = function(id, name) {
var video = new Video(id, name, this.roomHandle);
video.ownerId = this.id;
this.videos.push(video);
return video;
};
/**
* @desc 创建audio对象
* @param {String} id - audio id
* @param {String} name - audio name
* @ignore
*/
User.prototype.createAudio = function(id, name) {
var audio = new Audio(id, name, this.roomHandle);
audio.ownerId = this.id;
this.audio = audio;
return audio;
};
/**
* @desc 创建screen对象
* @param {String} id - screen id
* @param {String} name - screen name
* @ignore
*/
User.prototype.createScreen = function(id, name) {
var screen = new Screen(id, name, this.roomHandle);
screen.ownerId = this.id;
this.screen = screen;
return screen;
};
/**
* @desc 创建screenaudio对象
* @param {String} id - screen audio id
* @param {String} name - screen audio name
* @ignore
*/
User.prototype.createScreenAudio = function(id, name) {
var screenAudio = new ScreenAudio(id, name, this.roomHandle);
screenAudio.ownerId = this.id;
this.screen.screenAudio = screenAudio;
return screenAudio;
};
/**
* @desc 初始化桌面共享
* 逻辑:
* id: UUID;
* name: user.name+"_screen";
* @ignore
*/
User.prototype.initScreen = function() {
var id = Math.uuid();
var name = this.name+"-screen";
var screenAudioId = id + '_screen_audio'
var screenAudioName = this.name + '-screen-audio';
var screen = this.createScreen(id, name);
var screenAudio = this.createScreenAudio(screenAudioId, screenAudioName);
screen.level = 0;
screen.description = '';
screen.screenAudio = screenAudio;
//重新加会时,原应用层设置的视频/桌面共享流名称依然有效,不需要应用层再次设置.
if(this.reJoinScreenDescription){
screen.description = this.reJoinScreenDescription;
}
this.eventEmitter.emit(UserCallback.screen_status_notify, screen.status, screen.id, screen.name, this.id);
this.roomHandle.masterServer.addScreenMsg(id, screen);
this.roomHandle.masterServer.addSpeakerMsg(screenAudioId, screenAudio);
};
/**
* @desc 初始化视频设备
* @param {String} id - 视频设备ID
* @param {String} name - 视频设备名称
* @ignore
*/
User.prototype.initVideo = function(id, name) {
var video = this.createVideo(id, name);
video.cameraType = CameraType.unknown;
video.level = 0;
video.description = '';
var videoCustomSet = this.reJoinVideoId2CustomSet[id];
if(videoCustomSet){
if(videoCustomSet.aspectRatio){
video.aspectRatio = videoCustomSet.aspectRatio
}
if(videoCustomSet.description){
video.description = videoCustomSet.description
}
if(videoCustomSet.width){
video.resolution.width = videoCustomSet.width
}
if(videoCustomSet.height){
video.resolution.height = videoCustomSet.height
}
if(videoCustomSet.setType){
video.setType = videoCustomSet.setType
}
if(videoCustomSet.frameRate){
video.frameRate = videoCustomSet.frameRate
}
}
this.eventEmitter.emit(UserCallback.camera_status_notify, video.status, video.id, video.name, this.id);
var cameraUUID = this.roomHandle.setDeviceIdByUUID(0, id);
this.roomHandle.masterServer.addWebcamMsg(cameraUUID, video);
};
/**
* @desc 删除视频设备。如果已经打开,就先关闭; 如果已经pub,就先unpub;
* @param {String} id - 视频设备ID
* @ignore
*/
User.prototype.deleteVideo = function(id) {
var self = this;
var video = self.getVideo(id);
if (video) {
if(video.status == StreamStatus.published){
video.unpublish().then(function(){
arrayUtil.objectSplice(self.videos, id);
self.eventEmitter.emit(UserCallback.camera_status_notify, StreamStatus.none, video.id, video.name, self.id);
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, id);
self.roomHandle.masterServer.removeWebcamMsg(cameraUUID);
});
}else{
if (video.status == StreamStatus.opened) {
video.unpreview();
}
arrayUtil.objectSplice(self.videos, id);
self.eventEmitter.emit(UserCallback.camera_status_notify, StreamStatus.none, video.id, video.name, self.id);
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, id);
self.roomHandle.masterServer.removeWebcamMsg(cameraUUID);
}
}
}
/**
* @desc 初始化音频设备
* @param {String} id - 音频设备Id
* @param {String} name - 音频设备名称
* @ignore
*/
User.prototype.initAudio = function(id, name) {
var audio = this.createAudio(id, name);
avdEngineHandle.checkAudioId = id;
avdEngineHandle.checkAudioName = name;
this.eventEmitter.emit(UserCallback.microphone_status_notify, audio.status, audio.id, audio.name, this.id);
var microphoneUUID = this.roomHandle.setDeviceIdByUUID(2, id);
this.roomHandle.masterServer.addSpeakerMsg(microphoneUUID, audio);
};
/**
* @desc 删除音频设备。如果已经打开,就先关闭; 如果已经pub,就先unpub;
* @param {String} id - 音频设备ID
* @ignore
*/
User.prototype.deleteAudio = function(id) {
var self = this;
var audio;
if(id.indexOf('custom_audio') !== -1 ){
audio = self.getAudio(id);
}else{
audio = self.getCustomAudio(id);
}
if (audio) {
if (audio.status == StreamStatus.published) {
audio.closeMicrophone().then(function(){
var microphoneUUID = self.roomHandle.setDeviceIdByUUID(2, id);
self.roomHandle.masterServer.removeSpeakerMsg(microphoneUUID);
if(id.indexOf('custom_audio') !== -1 ){
self.audio = null;
}else{
arrayUtil.objectSplice(self.customAudios, id);
}
self.eventEmitter.emit(UserCallback.microphone_status_notify, StreamStatus.none, audio.id, audio.name, self.id);
});
}else{
var microphoneUUID = self.roomHandle.setDeviceIdByUUID(2, id);
self.roomHandle.masterServer.removeSpeakerMsg(microphoneUUID);
if(id.indexOf('custom_audio') !== -1 ){
self.audio = null;
}else{
arrayUtil.objectSplice(self.customAudios, id);
}
self.eventEmitter.emit(UserCallback.microphone_status_notify, StreamStatus.none, audio.id, audio.name, self.id);
}
}
}
/**
* @desc 获取Video对象
* @param {String} cameraId - 摄像头设备Id
* @returns {Object} - video Video对象
*/
User.prototype.getVideo = function(cameraId) {
var video = null;
for (var i = 0; i < this.videos.length; i++) {
var srcVideo = this.videos[i];
if (srcVideo.id == cameraId) {
video = srcVideo;
break;
}
}
return video;
}
/**
* @desc 获取Audio对象
* @param {String} customAudioId - 第三方音频设备Id
* @returns {Object} - audio Aduio对象
*/
User.prototype.getCustomAudio = function(customAudioId) {
var audio = null;
for (var i = 0; i < this.customAudios.length; i++) {
var srcAudio = this.customAudios[i];
if (srcAudio.id == customAudioId) {
audio = srcAudio;
break;
}
}
return audio;
}
/**
* @desc 获取Screen对象
* @returns {Object} - screen Screen对象
*/
User.prototype.getScreen = function() {
return this.screen;
}
/**
* @desc 获取ScreenAudio对象
* @returns {Object} - screenAudio ScreenAudio对象
*/
User.prototype.getScreenAudio = function() {
return this.screen.screenAudio;
}
/**
* @desc 获取Audio对象
* @param {String} microphoneId - 麦克风设备ID
* @returns {Object} - audio Audio对象
*/
User.prototype.getAudio = function(microphoneId) {
var srcAudio = null;
if (this.audio && this.audio.id == microphoneId) {
srcAudio = this.audio;
}
return srcAudio;
}
/**
* @desc 打开摄像头和麦克风
* @async
* @param {Obejct} video - video对象
* @param {Obejct} audio - audio对象
* @param {Object} videoElement - 视频控件对象
* @param {Object} audioElement - 音频控件对象
*/
User.prototype.openCameraAndMicrophone = function(video, audio, videoElement, audioElement) {
log.info("===user.openCameraAndMicrophone(),预览摄像头及发布流和打开麦克风");
avdEngineHandle.loggerReport.info("预览摄像头及发布流和打开麦克风");
var deferred = when.defer();
var self = this;
if (video && audio == null) {
video.publish();
} else if (video == null && audio) {
audio.openMicrophone();
} else if (video && audio) {
obtainUserMedia(audio, video,
function(stream) {
//about video
var orderId = getRandomNum(1, 3000); //用于PubRoomResourceMsgRep中的ID
video.status = StreamStatus.opened;
video.track = stream.videoStream.getVideoTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream.videoStream;
} else {
self.roomHandle.selfUser.stream.addTrack(video.track);
}
var cameraUUID = self.roomHandle.setDeviceIdByUUID(0, video.id);
var videoStream = self.getNewStreamByTrack(video.track);
self.roomHandle.masterServer.videoPublishHandle(cameraUUID, videoStream, orderId);
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success, function(id) {
if (id == orderId) {
video.element = videoElement;
videoElement.srcObject = videoStream;
video.status = StreamStatus.published;
if (!self.roomHandle.hasObject(self.roomHandle.pubVideos, video)) {
self.roomHandle.pubVideos.push(video);
}
self.eventEmitter.emit(UserCallback.camera_status_notify, video.status, video.id, video.name, self.id);
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error, function(error, id) {
if (id == orderId) {
deferred.reject(error);
}
});
//about audio
var audioOrderId = getRandomNum(30001, 6000); //用于PubRoomResourceMsgRep中的ID
audio.state = StreamStatus.opened;
audio.track = stream.audioStream.getAudioTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream.audioStream;
} else {
self.roomHandle.selfUser.stream.addTrack(audio.track);
}
var microphoneUUID = self.roomHandle.setDeviceIdByUUID(2, audio.id);
var audioStream = self.getNewStreamByTrack(audio.track);
self.roomHandle.masterServer.audioPublishHandle(microphoneUUID, audioStream, audioOrderId);
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_success, function(id) {
if (id == audioOrderId) {
if(audioElement){
audio.element = audioElement;
audioElement.srcObject = audioStream;
}
audio.status = StreamStatus.published;
if (!self.roomHandle.hasObject(self.roomHandle.pubAudios, audio)) {
self.roomHandle.pubAudios.push(audio);
}
self.eventEmitter.emit(UserCallback.microphone_status_notify, audio.status, audio.id, audio.name, self.id);
}
});
self.roomHandle.addCallback(EngineCallback.pub_roomresourcemsg_rep_error, function(error, id) {
if (id == audioOrderId) {
deferred.reject(error);
}
});
},
function(e) {
var error = new Error(ErrorConstant.failed_access_localMedia);
deferred.reject(error);
});
}
return deferred.promise;
}
/**
* @desc 命令远端用户发布某一摄像头视频
* @param {Obejct} video - 视频对象
*/
User.prototype.remotecmdPublishCamera = function(video) {
log.info("===user.remotecmdPublishCamera(),userId:"+video.ownerId+",设备Id:"+ video.id);
avdEngineHandle.loggerReport.info("命令远端用户发布某一摄像头视频,userId:"+video.ownerId+",设备Id:"+ video.id);
var nodeId = this.roomHandle.getNodeId(video.ownerId);
this.roomHandle.masterServer.videoIndiction(true, video.id, nodeId);
}
/**
* @desc 命令远端用户取消发布摄像头视频
* @param {String} userId - 用户Id
* @param {String} cameraId - 摄像头设备Id
*/
User.prototype.remotecmdUnpublishCamera = function(userId,cameraId) {
log.info("===user.remotecmdUnpublishCamera(),userId:" + userId +",cameraId:"+cameraId);
avdEngineHandle.loggerReport.info("命令远端用户取消发布摄像头视频, userId:" + userId +",cameraId:"+ cameraId);
var nodeId = this.roomHandle.getNodeId(userId);
this.roomHandle.masterServer.videoIndiction(false, cameraId, nodeId);
}
/**
* @desc 命令远端用户打开麦克风
* @param {String} userId - 远端用户Id
*/
User.prototype.remotecmdOpenMicrophone = function(userId){
log.info("===user.remotecmdOpenMicrophone(),userId:"+userId);
avdEngineHandle.loggerReport.info("命令远端用户打开麦克风, userId:"+ userId);
var user = this.roomHandle.getUser(userId);
var nodeId = this.roomHandle.getNodeId(userId);
this.roomHandle.masterServer.audioIndiction("pub",user.audio.id,nodeId);
}
/**
* @desc 命令远端用户关闭麦克风
* @param {String} userId - 远端用户Id
*/
User.prototype.remotecmdCloseMicrophone = function(userId){
log.info("===user.remotecmdCloseMicrophone(),userId:"+userId);
avdEngineHandle.loggerReport.info("命令远端用户关闭麦克风, userId:"+ userId);
var user = this.roomHandle.getUser(userId);
var nodeId = this.roomHandle.getNodeId(userId);
this.roomHandle.masterServer.audioIndiction("unpub",user.audio.id,nodeId);
}
/**
* @desc 命令远端用户麦克风静音
* @param {String} userId - 远端用户Id
*/
User.prototype.remotecmdMuteMicrophone = function(userId){
log.info("===user.remotecmdMuteMicrophone(),userId:"+userId);
avdEngineHandle.loggerReport.info("命令远端用户麦克风静音, userId:"+ userId);
var user = this.roomHandle.getUser(userId);
var nodeId = this.roomHandle.getNodeId(userId);
this.roomHandle.masterServer.audioIndiction("mute",user.audio.id,nodeId);
}
/**
* @desc 命令远端用户麦克风取消静音
* @param {String} userId - 远端用户Id
*/
User.prototype.remotecmdUnmuteMicrophone = function(userId){
log.info("===user.remotecmdUnmuteMicrophone(),userId:"+userId);
avdEngineHandle.loggerReport.info("命令远端用户麦克风取消静音, userId:"+ userId);
var user = this.roomHandle.getUser(userId);
var nodeId = this.roomHandle.getNodeId(userId);
this.roomHandle.masterServer.audioIndiction("unmute",user.audio.id,nodeId);
}
/**
* @desc 更新用户名称
* @param {String} userName - 用户名称
*/
User.prototype.updateUserName = function(userName) {
log.info("===user.updateUserName(),userName:"+userName);
avdEngineHandle.loggerReport.info("===user.updateUserName(), userName:" + userName);
var self = this;
this.roomHandle.participants.forEach(function(user){
if (user.id == self.id) {
user.name = userName;
}
})
this.roomHandle.masterServer.updateUserName(this.nodeId, userName);
};
/**
* @desc 更新用户扩展内容
* @param {String} userData - 用户扩展内容
*/
User.prototype.updateUserData = function(userData) {
this.data = userData;
this.roomHandle.masterServer.updateUserDataMsg(userData);
};
/**
* @desc 获得用户的扩展信息
* @param {String} userId - 用户id不填,表示自己
*/
User.prototype.getUserData = function(userId) {
if (typeof(userId) == 'undefined' || userId == null || userId == '') {
return this.data;
} else {
var user = this.roomHandle.getUser(userId);
if (user) {
return user.data;
}
}
};
/**
* @desc 视频流渲染进视频控件
* @param {Object} element - 视频控件对象
* @param {Object} stream - 视频流
*/
User.prototype.attachVideoElementMediaStream = function(element, stream) {
if (stream) {
var track = stream.getTracks()[0];
var resourceInfo = this.subVideoTrack2ResourceInfo[track.id];
var user = this.getUserByVideoTrackId(track.id);
if (user) {
var video = user.getVideo(resourceInfo.resource_id);
if (video) {
video.element = element;
}
}
}
if (element) {
log.debug("===attachVideoElementMediaStream(),userId:" + this.id + ",userName:" + this.name + ",elementId:" +element.id);
element.srcObject = stream;
}
};
/**
* @desc 桌面共享流渲染进视频控件
* @param {Object} element - 视频控件对象
* @param {Object} stream - 桌面共享流
*/
User.prototype.attachScreenElementMediaStream = function(element, stream) {
if (stream) {
var track = stream.getTracks()[0];
var resourceInfo = this.subScreenTrack2ResourceInfo[track.id];
var user = this.getUserByScreenTrackId(track.id);
if (user) {
var screen = user.getScreen(resourceInfo.resource_id);
if (screen) {
screen.element = element;
}
}
}
if (element) {
element.srcObject = stream;
}
};
/**
* @desc 桌面共享音频流渲染进音频控件
* @param {Object} element -音频控件对象
* @param {Object} stream - 桌面共享音频流
*/
User.prototype.attachScreenAudioElementMediaStream = function(element, stream) {
if (element) {
element.srcObject = stream;
}
};
/**
* @desc 音频流渲染进音频控件
* @param {Object} element - 音频控件对象
* @param {Object} stream - 音频流
*/
User.prototype.attachAudioElementMediaStream = function(element, stream) {
if (stream) {
var track = stream.getTracks()[0];
var resourceInfo = this.subAudioTrack2ResourceInfo[track.id];
var user = this.getUserByAudioTrackId(track.id);
if (user) {
var audio = user.getAudio(resourceInfo.resource_id);
if (audio) {
audio.element = element;
}
}
}
if (element) {
element.srcObject = stream;
}
};
/**
* @desc 用户级别的回调
* @param {UserCallback} type - 回调枚举标识
* @param {Object} callback -回调方法名,可以自定义
*
* @example
*
*
* user.addCallback(UserCallback.microphone_status_notify, onMicrophoneStatusNotify);
* user.addCallback(UserCallback.camera_status_notify, onCameraStatusNotify);
* user.addCallback(UserCallback.screen_status_notify, onScreenStatusNotify);
*
*
* user.addCallback(UserCallback.camera_data_notify, onCameraDataNotify);
* user.addCallback(UserCallback.screen_data_notify, onScreenDataNotify);
*
* user.addCallback(UserCallback.publish_camera_notify, onPublishCameraNotify);
* user.addCallback(UserCallback.unpublish_camera_notify, onUnpublishCameraNotify);
* user.addCallback(UserCallback.subscrible_camera_result, onSubscribleCameraResult);
* user.addCallback(UserCallback.unsubscrible_camera_result, onUnsubscribleCameraResult);
*
* user.addCallback(UserCallback.publish_screen_notify, onPublishScreenNotify);
* user.addCallback(UserCallback.unpublish_screen_notify, onUnpublishScreenNotify);
* user.addCallback(UserCallback.subscrible_screen_result, onSubscribleScreenResult);
* user.addCallback(UserCallback.unsubscrible_screen_result, onUnsubscribleScreenResult);
*
* user.addCallback(UserCallback.publish_microphone_notify, onPublishMicrophoneNotify);
* user.addCallback(UserCallback.unpublish_microphone_notify, onUnpublishMicrophoneNotify);
* user.addCallback(UserCallback.subscrible_microphone_result, onSubscribleMicrophoneResult);
* user.addCallback(UserCallback.unsubscrible_microphone_result, onUnsubscribleMicrophoneResult);
*
*
* user.addCallback(UserCallback.subscrible_screen_audio_result, onSubscribleScreenAudioResult);
* user.addCallback(UserCallback.unsubscrible_screen_audio_result, onUnsubscribleScreenAudioResult);
*
* user.addCallback(UserCallback.board_add_result, onBoardAddResult);
* user.addCallback(UserCallback.board_update_result, onBoardUpateResult);
* user.addCallback(UserCallback.board_remove_result, onBoardremoveResult);
*
*
* 麦克风状态更新
* param:status - 状态
* param:microphoneId - 麦克风设备Id
* param:microphoneName - 麦克风设备名称
* param:userId - 麦克风设备所属者ID
*
* function onMicrophoneStatusNotify(status, microphoneId, microphoneName, userId) {
*
* }
*
*
*摄像头状态更新
* param : status - 状态
* param : cameraId - 摄像头设备Id
* param : cameraName- 摄像头设备名称
* param : userId- 摄像头设备所属者ID
*
* function onCameraStatusNotify(status, cameraId, cameraName, userId) {
*
* }
*
*桌面共享状态更新
* param : status - 状态
* param : cameraId - 桌面共享设备Id
* param : cameraName- 桌面共享设备名称
* param : userId- 桌面共享设置所属者ID
*
* function onScreenStatusNotify(status, screenId, screenName, userId) {
*
* }
*
*
*
* 摄像头数据更新
* param : level - 级别
* param : description - 描述
* param : cameraId - 摄像头设备Id
* param : cameraName- 摄像头设备名称
* param : userId- 摄像头设备所属者ID
* function onCameraDataNotify(level, description, cameraId, cameraName, userId) {
*
* }
*
*
*桌面共享数据更新
* param : level - 级别
* param : description - 描述
* param : screenId - 桌面共享设备Id
* param : screenName- 桌面共享设备名称
* param : userId- 桌面共享设置所属者ID
*
* function onScreenDataNotify(level, description, screenId, screenName, userId) {
*
* }
*
*
*
*发布视频的回调
* param : videos - 发布的视频数组
*function onPublishCameraNotify(videos) {
*
* }
*
* 取消发布视频的回调
* param : video - 取消发布的视频
*
* function onUnpublishCameraNotify(video) {
*
* }
*
*
* 订阅远端视频流反馈
* param: stream - 远端视频流
* param :userId - 所属用户ID
* param :userName- 所属用户名称
* param :cameraId- 摄像头设备ID
*
*function onSubscribleCameraResult(stream, userId, userName,cameraId) {
*
* }
*
*
* 取消订阅远端视频流反馈
* param :userId- 所属用户ID
* param :userName-所属用户名称
* param : cameraId-摄像头设备ID
*
*function onUnsubscribleCameraResult(userId, userName,cameraId){
*
* }
*
*
* 发布桌面共享的回调
* param : screens - 发布的桌面共享数组
*
* function onPublishScreenNotify(screens){
*
* }
*
* 取消发布桌面共享的回调
* param : screen - 取消发布的桌面共享
*
* function onUnpublishScreenNotify(screen) {
*
* }
*
*
* 订阅远端桌面共享流反馈
* param: stream - 远端桌面共享流
* param :userId - 所属用户ID
* param :userName- 所属用户名称
* param :screenId- 桌面共享设备ID
* param :width- 分辨率宽度
* param :height- 分辨率高度
* param :framerate- 帧率
*
* function onSubscribleScreenResult(stream, userId, userName,screenId,width,height,framerate) {
*
* }
*
*
* 取消订阅远端桌面共享流反馈
* param :userId- 所属用户ID
* param :userName-所属用户名称
* param : cameraId-桌面共享设备ID
*
* function onUnsubscribleScreenResult(userId, userName,screenId){
*
* }
*
*
*
*发布音频的回调
* param : audios - 发布的音频数组
*function onPublishMicrophoneNotify(audios) {
*
* }
*
* 取消发布音频的回调
* param : audio - 取消发布的音频
*
* function onUnpublishMicrophoneNotify(audio) {
*
* }
*
*
*
* 订阅远端音频流反馈
* param:stream- 远端音频流
* param:userId- 所属用户ID
* param:userName-所属用户名称
*
*function onSubscribleMicrophoneResult(stream, userId, userName){
*
*}
*
*
*
*取消订阅远端音频流反馈
*param :userId- 所属用户ID
*param : userName-所属用户名称
*
*function onUnsubscribleMicrophoneResult(userId, userName){
*
*}
*
*
* * 订阅远端屏幕共享音频流反馈
* param:stream- 远端屏幕共享音频流
* param:userId- 所属用户ID
* param:userName-所属用户名称
*
*function onSubscribleMicrophoneResult(stream, userId, userName){
*
*}
*
*
*
*取消订阅远端屏幕共享音频流反馈
*param :userId- 所属用户ID
*param : userName-所属用户名称
*
*function onUnsubscribleMicrophoneResult(userId, userName){
*
*}
*
*
*
*白板新增操作反馈
*param :board-白板对象
*
*function onBoardAddResult(board){
*
*}
*
*白板删除操作反馈
*param :boardId-白板对象Id
*
*function onBoardRemoveResult(boardId){
*
*}
*/
User.prototype.addCallback = function(type, callback) {
this.eventEmitter.addListener(type, callback);
}
/**
* @desc 用于定位pc.onaddtrack或pc.onremovetrack对应的视频track与所属用户的绑定,map(trackId:resourceInfo)集
* @param {String} trackId -track id
* @ignore
*/
User.prototype.getUserByVideoTrackId = function(trackId) {
var resourceInfo = this.subVideoTrack2ResourceInfo[trackId];
if (resourceInfo) {
var nodeId = resourceInfo.owner_id;
return this.roomHandle.getUserByNodeId(nodeId);
} else {
return null;
}
}
/**
* @desc 用于定位pc.onaddtrack或pc.onremovetrack对应音频track与所属用户的绑定,map(trackId:resourceInfo)集
* @param {String} trackId -track id
* @ignore
*/
User.prototype.getUserByAudioTrackId = function(trackId) {
var resourceInfo = this.subAudioTrack2ResourceInfo[trackId];
if (resourceInfo) {
var nodeId = resourceInfo.owner_id;
return this.roomHandle.getUserByNodeId(nodeId);
} else {
return null;
}
}
/**
* @desc 用于定位pc.onaddtrack或pc.onremovetrack对应桌面共享track与所属用户的绑定,map(trackId:resourceInfo)集
* @param {String} trackId -track id
* @ignore
*/
User.prototype.getUserByScreenTrackId = function(trackId) {
var resourceInfo = this.subScreenTrack2ResourceInfo[trackId];
if (resourceInfo) {
var nodeId = resourceInfo.owner_id;
return this.roomHandle.getUserByNodeId(nodeId);
} else {
return null;
}
}
/**
* @desc 由track产生一个新的stream,且该stream中仅包含当前track
* @param {String} currTrack -track
* @ignore
*/
User.prototype.getNewStreamByTrack = function(currTrack) {
var streamTemp = null;
if (currTrack && this.stream && this.stream.getTracks().length > 0) {
var streamTemp = this.stream.clone();
streamTemp.getTracks().forEach(function(track) {
streamTemp.removeTrack(track);
});
streamTemp.addTrack(currTrack);
}
return streamTemp;
}
/**
* @desc 仅针对有微信小程序端加入的情况下使用,且当前web端有多个摄像头。一个摄像头原则上不用调该接口。
* @param {String} audioId 音频设备ID
* @param {String} videoId 视频设备ID
*/
User.prototype.bindAudioToVideo = function(audioId, videoId) {
var deferred = when.defer();
var orderId = 109;
this.roomHandle.masterServer.bindAudioToVideo(orderId, audioId, videoId);
this.roomHandle.addCallback(EngineCallback.bind_audio2video_rep_success, function(id) {
if (id == orderId) {
deferred.resolve();
}
});
this.roomHandle.addCallback(EngineCallback.bind_audio2video_rep_error, function(id) {
if (id == orderId) {
deferred.reject();
}
});
return deferred.promise;
}
/**
* @desc 创建白板
* @async
* @param {int} width - 白板的渲染宽度
* @param {int} height - 白板的渲染高度
* @param {String} backgroundColor - 白板的背景色,格式为RGBA 例如 透明:"0,0,0,0" 红色不透明: "255,0,0,1"
* @param {String} backgroundImage - 白板的背景图访问路径
* @param {int} outputWidth - 白板的输出宽度
* @param {int} outputHeight - 白板的输出高度
* @param {String} title - 白板标题
*/
User.prototype.createBoard = function(width, height, backgroundColor, backgroundImage, title, outputWidth,
outputHeight, description, extendData) {
var deferred = when.defer();
var self = this;
var board = new Board(this.roomHandle, this.id, width, height, backgroundColor, backgroundImage, title, outputWidth, outputHeight, description, extendData);
// 上面new的时候设置的inputWidth和inputHeight会被置为null,暂不清楚为何,注释掉
// board.inputWidth = null;
// board.inputHeight = null;
var supperBoardAuth = supperBoardAuthCheck(avdEngineHandle.restServerInfo.boxVersion);
if(!supperBoardAuth){
this.boards.push(board);
this.roomHandle.boardId2userId[board.id] = this.id;
this.roomHandle.masterServer.addBoardMsg(OperType.add, board);
//二个方法都是同步的,可能会导致订阅端收到 add或 share的PDU时序不确认,固在方法之间设置延时处理
setTimeout(function(){
self.shareBoardById(board.id);
deferred.resolve(board);
},600);
return deferred.promise;
}else {
this.roomHandle.boardId2userId[board.id] = this.id;
this.roomHandle.masterServer.addBoardMsg(OperType.add, board);
var curTimeout = setTimeout(function(){
var timeoutError = new Error(10000,'user.createBoard(), addBoardMsg send time out');
delete self.roomHandle.boardId2userId[board.id];
deferred.reject(timeoutError);
},5000);
this.addCallback(EngineCallback.updateboardmsg_add_rep_success, function(boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
var isExit = arrayUtil.objectSplice(self.boards,boardId);
if(!isExit){
self.boards.push(board);
self.shareBoardById(board.id);
}
deferred.resolve(board);
}
});
this.addCallback(EngineCallback.updateboardmsg_add_rep_error, function(error,boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
delete self.roomHandle.boardId2userId[board.id];
deferred.reject(error);
}
});
return deferred.promise;
}
}
/**
* @description 更新白板
* @async
* @param {Object} board 对象
*/
User.prototype.updateBoard = function(board) {
var deferred = when.defer();
var self = this;
var isSupper = updateBoardMsgRepCheck(avdEngineHandle.restServerInfo.boxVersion);
if(!isSupper){
this.roomHandle.masterServer.addBoardMsg(OperType.update, board);
deferred.resolve();
return deferred.promise;
}else{
this.roomHandle.masterServer.addBoardMsg(OperType.update, board);
var curTimeout = setTimeout(function(){
var timeoutError = new Error(10000,'user.updateBoard(), addBoardMsg send time out');
deferred.reject(timeoutError);
},5000);
this.addCallback(EngineCallback.updateboardmsg_update_rep_success, function(boardId) {
if(board.id == boardId) {
deferred.resolve();
}
});
this.addCallback(EngineCallback.updateboardmsg_update_rep_error, function(error,boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
deferred.reject(error);
}
});
return deferred.promise;
}
}
/**
* @desc 通过白板Id获取白板
* @param {String} boardId - 白板Id
*/
User.prototype.getBoard = function(boardId) {
var retBoard;
for (var i = 0; i < this.boards.length; i++) {
var board = this.boards[i];
if (board.id == boardId) {
retBoard = board;
break;
}
}
return retBoard;
}
/**
* @desc 获取所有的白板
*/
User.prototype.getAllBoards = function() {
return this.boards;
}
/**
* @desc 删除指定的白板
* @async
* @param {String} boardId - 白板Id
* @param {String} userId - 白板所属的用户id,允许不传,不传时,白板必现是当前自己的白板。填写是userid所属的白板
*/
User.prototype.removeBoardById = function(boardId, userId) {
var deferred = when.defer();
var self = this;
var isSupper = updateBoardMsgRepCheck(avdEngineHandle.restServerInfo.boxVersion);
if(!isSupper){
this.roomHandle.masterServer.removeBoardMsg(OperType.remove, boardId, userId);
setTimeout(function(){
arrayUtil.objectSplice(self.boards, boardId);
delete self.roomHandle.boardId2userId[boardId];
delete self.roomHandle.annotation2boardId[boardId];
deferred.resolve();
},200);
return deferred.promise;
}else{
this.roomHandle.masterServer.removeBoardMsg(OperType.remove, boardId, userId);
if(userId){
//删除别人的白板,收不到updateboardmsg_remove_rep_success或updateboardmsg_remove_rep_error的回调,所以这里直接操作。by roymond 2024/11/21
delete self.roomHandle.userid2boardsBy812[userId];
delete self.roomHandle.boardId2userId[boardId];
delete self.roomHandle.annotation2boardId[boardId];
deferred.resolve();
}else{
var curTimeout = setTimeout(function(){
var timeoutError = new Error(10000,'user.removeBoardById(), removeBoardMsg send time out');
deferred.reject(timeoutError);
},5000);
this.addCallback(EngineCallback.updateboardmsg_remove_rep_success, function(retBoardId) {
if(boardId == retBoardId) {
clearTimeout(curTimeout);
arrayUtil.objectSplice(self.boards, boardId);
delete self.roomHandle.boardId2userId[boardId];
delete self.roomHandle.annotation2boardId[boardId];
deferred.resolve();
}
});
this.addCallback(EngineCallback.updateboardmsg_remove_rep_error, function(error,retBoardId) {
if(boardId == retBoardId) {
clearTimeout(curTimeout);
deferred.reject(error);
}
});
}
return deferred.promise;
}
}
/**
* @desc 关闭指定的共享白板
* @async
* @param {String} boardId - 白板Id
*/
User.prototype.closeBoardById = function(boardId) {
var deferred = when.defer();
var self = this;
var board = this.getBoard(boardId);
if (board.status == boardStatusEnum.close) {
deferred.resolve();
return deferred.promise;
}
var isSupper = updateBoardMsgRepCheck(avdEngineHandle.restServerInfo.boxVersion);
if(!isSupper){
this.roomHandle.masterServer.closeBoardMsg(OperType.close, boardId);
setTimeout(function(){
if (self.roomHandle.annotation2boardId[boardId]) {
self.roomHandle.annotation2boardId[boardId].queueArr = [];
for (var k in self.roomHandle.annotation2boardId[boardId].storageArr) {
self.roomHandle.annotation2boardId[boardId].queueArr.push(self.roomHandle.annotation2boardId[boardId].storageArr[k]);
}
}
board.status = boardStatusEnum.close;
board.annotation = null;
deferred.resolve();
},200);
return deferred.promise;
}else{
this.roomHandle.masterServer.closeBoardMsg(OperType.close, boardId);
var curTimeout = setTimeout(function(){
var timeoutError = new Error(10000,'user.closeBoardById(), closeBoardMsg send time out');
deferred.reject(timeoutError);
},5000);
this.addCallback(EngineCallback.updateboardmsg_close_rep_success, function(boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
if (self.roomHandle.annotation2boardId[boardId]) {
self.roomHandle.annotation2boardId[boardId].queueArr = [];
for (var k in self.roomHandle.annotation2boardId[boardId].storageArr) {
self.roomHandle.annotation2boardId[boardId].queueArr.push(self.roomHandle.annotation2boardId[boardId].storageArr[k]);
}
}
board.status = boardStatusEnum.close;
board.annotation = null;
deferred.resolve();
}
});
this.addCallback(EngineCallback.updateboardmsg_close_rep_error, function(error,boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
deferred.reject(error);
}
});
return deferred.promise;
}
}
/**
* @desc 共享指定白板
* @async
* @param {String} boardId - 白板Id
*/
User.prototype.shareBoardById = function(boardId) {
var deferred = when.defer();
var self = this;
var board = this.getBoard(boardId);
if (board.status == boardStatusEnum.open) {
deferred.resolve();
return deferred.promise;
}
var isSupper = updateBoardMsgRepCheck(avdEngineHandle.restServerInfo.boxVersion);
if(!isSupper){
self.roomHandle.masterServer.shareBoardMsg(OperType.share, boardId, board);
setTimeout(function(){
board.status = boardStatusEnum.open;
var annotation = self.roomHandle.annotation2boardId[boardId];
if (annotation) {
/**
* desc: 防止有注释时重绘但此时annotation init还没完成zr对象为空导致zr.add报错的问题,让重绘在init完成之后有zr对象了再触发
* by: 王博
* date: 2021/6/24
*/
setTimeout(function(){
log.debug("==KKKKKKKKKKK222222222,annotation.queueArr.length:"+annotation.queueArr.length);
self.roomHandle.updateAnnotationDrow(annotation.queueArr);
deferred.resolve();
}, 50);
}
},200);
return deferred.promise;
}else{
self.roomHandle.masterServer.shareBoardMsg(OperType.share, boardId, board);
var curTimeout = setTimeout(function(){
var timeoutError = new Error(10000,'user.shareBoardById(), shareBoardMsg send time out');
deferred.reject(timeoutError);
},5000);
this.addCallback(EngineCallback.updateboardmsg_share_rep_success, function(boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
board.status = boardStatusEnum.open;
var annotation = self.roomHandle.annotation2boardId[boardId];
if (annotation) {
/**
* desc: 防止有注释时重绘但此时annotation init还没完成zr对象为空导致zr.add报错的问题,让重绘在init完成之后有zr对象了再触发
* by: 王博
* date: 2021/6/24
*/
setTimeout(function(){
log.debug("==KKKKKKKKKKK33333333333,annotation.queueArr.length:"+annotation.queueArr.length);
self.roomHandle.updateAnnotationDrow(annotation.queueArr);
deferred.resolve();
}, 50);
}
}
});
this.addCallback(EngineCallback.updateboardmsg_share_rep_error, function(error,boardId) {
if(board.id == boardId) {
clearTimeout(curTimeout);
deferred.reject(error);
}
});
return deferred.promise;
}
}
/**
* @desc 删除自己所属的全部白板
* @param {String} userId - 白板创建者Id
*/
User.prototype.removeBoard = function() {
var self = this;
self.boards.forEach(function(board){
delete self.roomHandle.boardId2userId[board.id];
delete self.roomHandle.annotation2boardId[board.id];
arrayUtil.objectSplice(self.boards, board.id);
})
}
/**
* @desc 针对移动端H5打开前置摄像头
* @async
* @param {Object} element - 视频控件对象
*/
User.prototype.openMobileFrontVideo = function(element) {
log.info("===video openMobileFrontVideo...")
var deferred = when.defer();
var self = this;
//部分Adnroid手机(oppo realmeX2 Android11 微信8.0.31 ,红米K30i Android10 微信8.0.11 等)切换前后置摄像头无效
// var constraints = {
// audio: false,
// video:{ 'facingMode': "user" }
// };
var video = self.videos[0];
var constraints = {
audio: false,
video:{
facingMode: {
exact: "user"
}
}
};
if (video.setType == ResolutionSetType.ideal) {
constraints.video.width = {
ideal: video.resolution.width
};
constraints.video.height = {
ideal: video.resolution.height
};
}else{
constraints.video.width = {
exact: video.resolution.width
};
constraints.video.height = {
exact: video.resolution.height
};
}
constraints.video.frameRate = {
ideal: video.frameRate
};
log.info("===video openMobileFrontVideo(),constraints:" + JSON.stringify(constraints));
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
log.debug("===video openMobileFrontVideo(),stream:",stream);
currentVideoParameter.videoWidth= video.resolution.width;
currentVideoParameter.videoHeight= video.resolution.height;
currentVideoParameter.frameRate = video.frameRate;
video.track = stream.getVideoTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream;
} else {
self.roomHandle.selfUser.stream.addTrack(video.track);
}
video.isPubState = true;
video.publishHandle(element).then(function() {
log.info("===video openMobileFrontVideo(),预览前置摄像头及发布流成功,videoId:"+ video.id+",videoName:"+video.name+",userId:"+video.ownerId);
deferred.resolve();
}).otherwise(function(error) {
video.isPubState = false;
log.error("===video openMobileFrontVideo(),预览前置摄像头及发布视频流失败,videoId:"+ video.id+",videoName:"+video.name+",userId:"+video.ownerId+",error:"+JSON.stringify(error));
deferred.reject(error);
});
}).catch(function(error) {
log.error("===video openMobileFrontVideo() failed,error: ", error);
video.isPubState = false;
deferred.reject(error);
});
return deferred.promise;
}
/**
* @desc 针对移动端H5关闭前置摄像头
* @async
*/
User.prototype.closeMobileFrontVideo = function() {
log.info("===video closeMobileFrontVideo...")
var deferred = when.defer();
var video = this.videos[0];
video.unpreview();
video.unpublish().then(function(){
log.info("===video closeMobileFrontVideo success")
deferred.resolve();
}).otherwise(function(error){
log.info("===video closeMobileFrontVideo error")
deferred.reject(error);
});
return deferred.promise;
}
/**
* @desc 针对移动端H5关闭后置摄像头
* @async
*/
User.prototype.closeMobileBackVideo = function() {
log.info("===video closeMobileBackVideo...")
var deferred = when.defer();
var video = this.videos[1];
video.unpreview();
video.unpublish().then(function(){
log.info("===video closeMobileBackVideo success")
deferred.resolve();
}).otherwise(function(error){
log.info("===video closeMobileBackVideo error")
deferred.reject(error);
});
return deferred.promise;
}
/**
* @desc 针对移动端H5打开后置摄像头
* @async
* @param {Object} element - 视频控件对象
*/
User.prototype.openMobileBackVideo = function(element) {
log.info("===video openMobileBackVideo()...")
var deferred = when.defer();
var self = this;
/**
* 手机H5打开后置摄像头,部分机型(华为mate60,p40,p30等)打开的是放大的长焦视频。本地做兼容处理逻辑如下:
* 当前操作系统为Android,摄像头个数>2,取最后一个摄像头作为后置。
* 存在的隐患或不确定:
* 1)小米\vivo目前测试都是返回2个摄像头,未来新款手机会不会返回超过2个摄像头不确定
* 2)摄像头列表的最后一个现作为后置摄像头,目前测试没有问题。未来新款手机会不会出现另类不确定
*/
var isCustomise = false;
var cameraLen = 0;
var customiseCameraId;
var customiseCameraName;
if(avdEngineHandle.detect.osName == "Android"){
for(var key in avdEngineHandle.cameraMap) {
var val = avdEngineHandle.cameraMap[key];
cameraLen++;
customiseCameraId = key;
customiseCameraName = val;
}
if(cameraLen > 2){
isCustomise = true;
log.debug("===video openMobileBackVideo(), detect.osName:Android, cameraLen:"+ cameraLen +", customiseCameraId:" + customiseCameraId+", customiseCameraName:"+customiseCameraName);
}
}
//部分Adnroid手机(oppo realmeX2 Android11 微信8.0.31 ,红米K30i Android10 微信8.0.11 等)切换前后置摄像头无效
// var constraints = {
// audio: false,
// video:{ 'facingMode': "environment" }
// };
var video = self.videos[1];
var constraints = {
audio: false,
video:{
facingMode: {
exact: "environment"
}
}
};
if(isCustomise){
constraints = {
audio: false,
video:{
deviceId:{
exact: customiseCameraId
}
}
};
}
if (video.setType == ResolutionSetType.ideal) {
constraints.video.width = {
ideal: video.resolution.width
};
constraints.video.height = {
ideal: video.resolution.height
};
}else{
constraints.video.width = {
exact: video.resolution.width
};
constraints.video.height = {
exact: video.resolution.height
};
}
constraints.video.frameRate = {
ideal: video.frameRate
};
log.info("===video openMobileBackVideo(),constraints:" + JSON.stringify(constraints));
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
log.debug("===video openMobileBackVideo(),stream:",stream);
currentVideoParameter.videoWidth= video.resolution.width;
currentVideoParameter.videoHeight= video.resolution.height;
currentVideoParameter.frameRate = video.frameRate;
video.track = stream.getVideoTracks()[0];
if (self.roomHandle.selfUser.stream == null) {
self.roomHandle.selfUser.stream = stream;
} else {
self.roomHandle.selfUser.stream.addTrack(video.track);
}
video.isPubState = true;
video.publishHandle(element).then(function() {
log.info("===video openMobileBackVideo(),预览后置摄像头及发布流成功,videoId:"+ video.id+",videoName:"+video.name+",userId:"+video.ownerId);
deferred.resolve();
}).otherwise(function(error) {
video.isPubState = false;
log.error("===video openMobileBackVideo(),预览后置摄像头及发布视频流失败,videoId:"+ video.id+",videoName:"+video.name+",userId:"+video.ownerId+",error:"+JSON.stringify(error));
deferred.reject(error);
});
}).catch(function(error) {
log.error("===video openMobileBackVideo() failed,error: ", error);
video.isPubState = false;
deferred.reject(error);
});
return deferred.promise;
}
/**
* @desc 创建一个自定义的视频轨道
* @async
* @param {MediaStreamTrack} mediaStreamTrack - 视频轨道
* @param {String} trackName - 视频轨道名称,非必填项,不填时默认为userName + "_custom_video"
*
* @example
* 以从Canvas中获取流为例:
* var stream = customCanvas.captureStream();
* var track = stream.getVideoTracks()[0];
* user.createCustomVideoTrack(track).then(function(video){
* video.publish().then(function(){});
* }).otherwise(function(error){});
*/
User.prototype.createCustomVideoTrack = function(mediaStreamTrack, trackName) {
log.info("===video createCustomVideoTrack...");
var deferred = when.defer();
var self = this;
if(!mediaStreamTrack){
log.error('===video createCustomVideoTrack(),mediaStreamTrack params are mandatory and cannot be empty');
var error = new Error(ErrorConstant.params_require);
deferred.reject(error);
}
if(mediaStreamTrack instanceof MediaStreamTrack){
var id = Math.uuid() + "_custom_video";
var name = this.name + "_custom_video";
if(trackName){
name = trackName;
}
try{
this.initVideo(id, name);
setTimeout(function(){
var video = self.getVideo(id);
video.track = mediaStreamTrack;
if (self.customStream == null) {
self.customStream = new MediaStream();
}
self.customStream.addTrack(mediaStreamTrack);
deferred.resolve(video);
},500);
}catch(e){
deferred.reject(e);
}
}else{
log.error('===video createCustomVideoTrack(),mediaStreamTrack parameter type mismatch');
var error = new Error(ErrorConstant.params_type_mismatch);
deferred.reject(error);
}
return deferred.promise;
}
/**
* @desc 删除一个自定义的视频轨道
* @async
* @param {String} videoId 视频设备ID
*/
User.prototype.deleteCustomVideoTrack = function(videoId) {
log.info("===video deleteCustomVideoTrack...");
var deferred = when.defer();
var self = this;
var video = self.getVideo(videoId);
if(video){
if(video.status == StreamStatus.published){
video.unpreview();
video.unpublish().then(function(){
log.info("===video deleteCustomVideoTrack, unpublish success");
try{
var customStream = self.customStream;
if(customStream){
customStream.getTracks().forEach(function(track) {
if(video.track && track.id == video.track.id){
track.stop();
}
});
}
if(video.track){
video.track.stop();
video.track = null;
}
self.deleteVideo(videoId);
setTimeout(function(){
deferred.resolve();
},500);
}catch(e){
deferred.reject(e);
}
}).otherwise(function(error){
log.info("===video deleteCustomVideoTrack, unpublish error");
deferred.reject(error);
});
}else{
try{
self.deleteVideo(videoId);
setTimeout(function(){
deferred.resolve();
},500);
}catch(e){
deferred.reject(e);
}
}
}else{
log.error("===video deleteCustomVideoTrack, video is null");
deferred.reject();
}
return deferred.promise;
}
/**
* @desc 创建一个自定义的音频轨道
* @async
* @param {MediaStreamTrack} mediaStreamTrack - 音频轨道
* @param {String} trackName - 音频轨道名称,非必填项,不填时默认为userName + "_custom_audio"
*/
User.prototype.createCustomAudioTrack = function(mediaStreamTrack, trackName) {
log.info("===audio createCustomAudioTrack...");
var deferred = when.defer();
var self = this;
if(!mediaStreamTrack){
log.error('===audio createCustomAudioTrack(),mediaStreamTrack params are mandatory and cannot be empty');
var error = new Error(ErrorConstant.params_require);
deferred.reject(error);
}
if(mediaStreamTrack instanceof MediaStreamTrack){
var id = Math.uuid() + "_custom_audio";
var name = this.name + "_custom_audio";
if(trackName){
name = trackName;
}
try{
var audio = new Audio(id, name, this.roomHandle);
audio.ownerId = this.id;
this.customAudios.push(audio);
this.eventEmitter.emit(UserCallback.microphone_status_notify, audio.status, audio.id, audio.name, this.id);
var microphoneUUID = this.roomHandle.setDeviceIdByUUID(2, id);
this.roomHandle.masterServer.addSpeakerMsg(microphoneUUID, audio);
setTimeout(function(){
audio.track = mediaStreamTrack;
if (self.customStream == null) {
self.customStream = new MediaStream();
}
self.customStream.addTrack(mediaStreamTrack);
deferred.resolve(audio);
},500);
}catch(e){
deferred.reject(e);
}
}else{
log.error('===aduio createCustomAudioTrack(),mediaStreamTrack parameter type mismatch');
var error = new Error(ErrorConstant.params_type_mismatch);
deferred.reject(error);
}
return deferred.promise;
}
/**
* @desc 删除一个自定义的音频轨道
* @async
* @param {String} audioId 音频设备ID
*/
User.prototype.deleteCustomAudioTrack = function(audioId) {
log.info("===audio deleteCustomAudioTrack...");
var deferred = when.defer();
var self = this;
var audio = self.getCustomAudio(audioId);
if(audio){
if(audio.status == StreamStatus.published){
audio.closeMicrophone().then(function(){
log.info("===audio deleteCustomAudioTrack, closeMicrophone success");
try{
var customStream = self.customStream;
if(customStream){
customStream.getTracks().forEach(function(track) {
if(audio.track && track.id == audio.track.id){
track.stop();
}
});
}
if(audio.track){
audio.track.stop();
audio.track = null;
}
self.deleteAudio(audioId);
setTimeout(function(){
deferred.resolve();
},500);
}catch(e){
deferred.reject(e);
}
}).otherwise(function(error){
log.info("===audio deleteCustomAudioTrack, closeMicrophone error");
deferred.reject(error);
});
}else{
try{
self.deleteAudio(audioId);
setTimeout(function(){
deferred.resolve();
},500);
}catch(e){
deferred.reject(e);
}
}
}else{
log.error("===audio deleteCustomAudioTrack, audio is null");
deferred.reject();
}
return deferred.promise;
}
//兼容老服务器版本不校验白板功能授权(config/serverinfo接口返回的box_version值,如果>=3.1.7则开启支持白板功能授权校验)
function supperBoardAuthCheck(boxVersion){
var subjectVersion = "3.1.7";
var isSupper = false;
if(boxVersion){
var ret = versionStringCompare(boxVersion, subjectVersion);
if(ret >= 0){
isSupper = true;
}
}
if(!isSupper){
log.info("===user 当前服务器版本不支持白板功能授权校验,客户端也将不启用校验。");
}
return isSupper;
}
//兼容老服务器版本不下发operType=update/share/close/remove的UPDATE_BOARD_MSG_REP,(config/serverinfo接口返回的box_version值,如果>=3.1.8则开始下发相关PDU)
function updateBoardMsgRepCheck(boxVersion){
var subjectVersion = "3.1.8";
var isSupper = false;
if(boxVersion){
var ret = versionStringCompare(boxVersion, subjectVersion);
if(ret >= 0){
isSupper = true;
}
}
if(!isSupper){
log.info("===user 当前服务器版本不下发operType=update/share/close/remove的UPDATE_BOARD_MSG_REP PDU.");
}
return isSupper;
}
return User;
});