DispatchLib_add_scripts("adapter.js"); DispatchLib_add_scripts("tinySDP/tsdp_api.js"); DispatchLib_add_scripts("ffmpeg/RecordRTC.js"); //————————————————————————————————————— 配置 —————————————————————————————————————————— var DispatchRtc_DebugVideo = false; var DispatchRtc_RecordMp4 = true; //————————————————————————————————————— DispatchRtcLib —————————————————————————————————————————— function DispatchRtcLib() { this.index = 0; this.CallID = 0; this.CallType = -1; this.peerconnection = null; this.LocalVideoCandidates = new Array(); this.LocalAudioCandidates = new Array(); this.LocalSdp = null; //本地sdp this.LocalAudioStream = null; //本地音频媒体stream this.LocalVideoStream = null; //本地视频媒体stream this.LocalVideoLib = null; //本地视频videolib this.RemoteStream = null; //对端媒体stream this.RemoteSdp = null; //对端sdp this.RemoteVideoLib = null; //对端视频 this.RemoteAudioLabel = null; //audio标签 双向语音用 this.AudioRTCRtpSender = null; this.VideoRTCRtpSender = null; this.CurrentCaptureDev = "none"; this.bytesReceived = 0; this.framesDecoded = 0; this.frameWidth = 0; this.frameHeight = 0; this.CandidateTimeout = null; this.CurrentMicphone = null; } var DispatchRtcLib_Array = new Array; var DispatchRtcLib_MoniterVideo = 0; var DispatchRtcLib_VoipAudio = 1; var DispatchRtcLib_VoipVideo = 2; var DispatchRtcLib_ConfAudio = 3; var DispatchRtcLib_ConfVideoPush = 4; var DispatchRtcLib_ConfVideoPull = 5; //————————————————————————————————————— DispatchDevices —————————————————————————————————————————— var DispatchDevices = { DevicesList: null, //全部设备列表 SpeakerList: new Array, //扬声器列表 MicphoneList: new Array, //麦克风列表 CameraList: new Array, //摄像头 UsingSpeaker: null, UsingMicphone: null, UsingCamera: null, CameraWidth: 1280, CameraHeight: 720, CameraFrameRate: 25, }; //选择本地设备 DispatchDevices.setLocalDevice = function(Type, DevID) { if (Type == 0) { //扬声器 if (DispatchDevices.UsingSpeaker.deviceId == DevID) { return 0; } for (i = 0; i < DispatchDevices.SpeakerList.length; i++) { var speaker = DispatchDevices.SpeakerList[i]; if (speaker.deviceId == DevID) { //扬声器 DispatchDevices.UsingSpeaker = speaker; console.log("* 改变当前扬声器为" + DispatchDevices.UsingSpeaker.label); return 0; } } return -1; } else if (Type == 1) { //麦克风 if (DispatchDevices.UsingMicphone.deviceId == DevID) { return 0; } for (i = 0; i < DispatchDevices.MicphoneList.length; i++) { var micphone = DispatchDevices.MicphoneList[i]; if (micphone.deviceId == DevID) { //扬声器 DispatchDevices.UsingMicphone = micphone; console.log("* 改变当前麦克风为" + DispatchDevices.UsingMicphone.label); DispatchPocLib.MicphoneChanged(); return 0; } } return -1; } else if (Type == 2) { //摄像头 if (DispatchDevices.UsingCamera.deviceId == DevID) { return 0; } for (var i = 0; i < DispatchDevices.CameraList.length; i++) { var camera = DispatchDevices.CameraList[i]; if (camera.deviceId == DevID) { //扬声器 DispatchDevices.UsingCamera = camera; console.log("* 改变当前摄像头为" + DispatchDevices.UsingCamera.label); return 0; } } return -1; } return -2; } //检测本地设备 DispatchDevices.checkLocalDevice = function(devicelist) { DispatchDevices.DevicesList = new Array; DispatchDevices.SpeakerList = new Array; DispatchDevices.MicphoneList = new Array; DispatchDevices.CameraList = new Array; var getDevicesDone = function(devices) { DispatchDevices.DevicesList = devices; console.log("本地设备列表 开始 ——————————————————"); var UsingSpeakerChanged = true; var UsingMicphoneChanged = true; var UsingCameraChanged = true; for (var i = 0; i < devices.length; i++) { var thisdev = devices[i]; var device = { deviceId: thisdev.deviceId, label: thisdev.label, groupId: thisdev.groupId, kind: thisdev.kind, }; if (device.label.indexOf(" - ") == 0) { //客户端没有显示默认、通讯 if (device.deviceId == "default") { device.label = "默认" + device.label; } else if (device.deviceId == "communications") { device.label = "通讯" + device.label; } } if (device.label.length > 11 && device.label[device.label.length-1] == ")" && device.label[device.label.length-6] == ":" && device.label[device.label.length-11] == "(" && device.label[device.label.length-12] == " ") { device.label = device.label.substring(0, device.label.length - 12); } if (device.kind == "audiooutput") { //扬声器 if (device.deviceId != "communications") { DispatchDevices.SpeakerList.push(device); } console.log("* 扬声器:" + device.label + " ID:" + device.deviceId); if (DispatchDevices.UsingSpeaker && device.label == DispatchDevices.UsingSpeaker.label) { UsingSpeakerChanged = false; } } else if (device.kind == "audioinput") { //麦克风 if (device.deviceId != "communications") { DispatchDevices.MicphoneList.push(device); } console.log("* 麦克风:" + device.label + " ID:" + device.deviceId); if (DispatchDevices.UsingMicphone && device.label == DispatchDevices.UsingMicphone.label) { UsingMicphoneChanged = false; } } else if (device.kind == "videoinput") { //摄像头 DispatchDevices.CameraList.push(device); console.log("* 摄像头:" + device.label + " ID:" + device.deviceId); if (DispatchDevices.UsingCamera && device.label == DispatchDevices.UsingCamera.label) { UsingCameraChanged = false; } } } if (UsingSpeakerChanged) //扬声器 { for (i = 0; i < DispatchDevices.SpeakerList.length; i++) { var speaker = DispatchDevices.SpeakerList[i]; if (speaker.deviceId == "default") { //设备变更 DispatchDevices.UsingSpeaker = speaker; console.log("! 当前扬声器为" + DispatchDevices.UsingSpeaker.label); break; } } if (DispatchDevices.SpeakerList.length == 0) { DispatchDevices.UsingSpeaker = null; } if (DispatchDevices.UsingSpeaker == null) { console.log("! 无可用扬声器"); } } if (UsingMicphoneChanged) //麦克风 { for (i = 0; i < DispatchDevices.MicphoneList.length; i++) { var micro = DispatchDevices.MicphoneList[i]; if (micro.deviceId == "default") { //设备变更 DispatchDevices.UsingMicphone = micro; console.log("! 当前麦克风为" + DispatchDevices.UsingMicphone.label); break; } } if (DispatchDevices.MicphoneList.length == 0) { DispatchDevices.UsingMicphone = null; } if (DispatchDevices.UsingMicphone == null) { console.log("! 无可用麦克风"); } } if (UsingCameraChanged) { DispatchDevices.UsingCamera = null; if (DispatchDevices.CameraList.length == 0) { console.log("! 无可用摄像头"); } else { DispatchDevices.UsingCamera = DispatchDevices.CameraList[0]; console.log("! 当前摄像头为" + DispatchDevices.UsingCamera.label); } } console.log("本地设备列表 结束 ——————————————————"); if (UsingMicphoneChanged) { DispatchPocLib.MicphoneChanged(); } if (DispatchDevices.SpeakerList.length > 0 && DispatchDevices.UsingSpeaker == null) { //获取不到权限的情况,尝试打开设备再关闭 var localmedia = { "audio": true, "video": true }; if (DispatchDevices.MicphoneList.length == 0) { localmedia.audio = false; } if (DispatchDevices.CameraList.length == 0) { localmedia.video = false; } if (localmedia.audio || localmedia.video) { alert("初次加载,测试本地媒体设备,请赋予权限."); function success(stream) { for (var i = 0; i < stream.getTracks().length; i++) { stream.getTracks()[i].stop(); } setTimeout(DispatchDevices.checkLocalDevice(), 1000); } function error(error) { console.log("加载本地媒体设备失败,会影响使用."); } navigator.getUserMedia(localmedia, success, error); } } } if (devicelist) { getDevicesDone(devicelist); } else { navigator.mediaDevices.enumerateDevices() .then(getDevicesDone) .catch(function(error) { console.log(error); }); } } //————————————————————————————————————— DispatchRtcLib —————————————————————————————————————————— DispatchRtcLib.Init = function() { // 获取谷歌浏览器版本 console.log("DispatchRtcLib.Init"); function getChromeVersion() { var arr = navigator.userAgent.split(' '); var chromeVersion = ''; for(var i=0;i < arr.length;i++){ if(/chrome/i.test(arr[i])) chromeVersion = arr[i] } if(chromeVersion){ return Number(chromeVersion.split('/')[1].split('.')[0]); } else { return false; } } if(getChromeVersion()) { var version = getChromeVersion(); console.log("Chrome " + version); } function InitLocalMedia() { //生成本地虚拟头像 DispatchRtcLib.LocalPhotoStream = null; var LocalPhoto_canvas = document.createElement('canvas'); if (LocalPhoto_canvas.captureStream) { LocalPhoto_canvas.width = 320; LocalPhoto_canvas.height = 240; LocalPhoto_canvas.style.display = "none"; document.body.appendChild(LocalPhoto_canvas); var ctx = LocalPhoto_canvas.getContext('2d'); var img = new Image(); img.onload = function() { ctx.drawImage(img, 0, 0); DispatchRtcLib.LocalPhotoStream = LocalPhoto_canvas.captureStream(); DispatchRtcLib.LocalPhotoStream.bLocalPhoto = true; DispatchRtcLib.LocalPhotoStream.getVideoTracks()[0].bLocalPhoto = true; window.setInterval(function() { if (DispatchRtcLib.LocalPhotoStream) { ctx.drawImage(img, 0, 0); } }, 500); } img.src = DispatchLibPath+"image/LocalPhoto.jpg"; } //生成静音采集 DispatchRtcLib.LocalMuteAudio = document.createElement('audio'); if (DispatchRtcLib.LocalMuteAudio) { DispatchRtcLib.LocalMuteAudio.autoplay = true; DispatchRtcLib.LocalMuteAudio.loop = true; DispatchRtcLib.LocalMuteAudio.src = DispatchLibPath+"image/mute.mp3"; DispatchRtcLib.LocalMuteAudio.onplay=function(){ console.log("DispatchRtcLib.LocalMuteAudio.onplay"); }; //DispatchRtcLib.LocalMuteAudio.currentTime; } //媒体流信息处理 window.setInterval(function() { for (var i = 0; i < DispatchRtcLib_Array.length; i++) { if (DispatchRtcLib_Array[i] != null && DispatchRtcLib_Array[i].peerconnection && DispatchRtcLib_Array[i].RemoteVideoLib && DispatchRtcLib_Array[i].RemoteStream) { if (DispatchRtcLib_Array[i].CallType == DispatchRtcLib_MoniterVideo || DispatchRtc_DebugVideo) { let rtcLib = DispatchRtcLib_Array[i]; rtcLib.peerconnection.getStats(null).then( (stats) => { var bytes_sec = 0; var frames_sec = 0; var packetsLost = 0; var packetsReceived = 0; stats.forEach(report => { if (report.type == "inbound-rtp" && report.mediaType == "video") { bytes_sec = report.bytesReceived - rtcLib.bytesReceived; frames_sec = report.framesDecoded - rtcLib.framesDecoded; packetsLost = report.packetsLost; packetsReceived = report.packetsReceived; rtcLib.bytesReceived = report.bytesReceived; rtcLib.framesDecoded = report.framesDecoded; } else if (report.type == "track" && report.remoteSource && report.kind == "video") { rtcLib.frameWidth = report.frameWidth; rtcLib.frameHeight = report.frameHeight; } }); if (rtcLib.frameWidth && rtcLib.frameHeight) { rtcLib.RemoteVideoLib.setVideoText("H264", frames_sec, 8*bytes_sec/1024, rtcLib.frameWidth, rtcLib.frameHeight); } }); } } } }, 1000); } //初始化媒体 if (navigator.mediaDevices) { //尝试获取本地媒体 navigator.mediaDevices.enumerateDevices() .then(function(devices) { console.log("enumerateDevices"); DispatchDevices.checkLocalDevice(devices); InitLocalMedia(); var msgData = new Object; msgData.Msg = "MediaInitNotify"; msgData.Type = 1; msgData.Reason = "MediaInit Sucess"; DispatchCtrlLib.FuncCallback(msgData); //监听本地设备更改事件 navigator.mediaDevices.ondevicechange = function(event) { console.log("本地设备更新"); DispatchDevices.checkLocalDevice(); } }) .catch(function(error) { console.log(error); var failmsg = new Object; failmsg.Msg = "MediaInitNotify"; failmsg.Type = 0; failmsg.Reason = error; DispatchCtrlLib.FuncCallback(failmsg); }); } else { console.log("no navigator.mediaDevices"); var failmsg = new Object; failmsg.Msg = "MediaInitNotify"; failmsg.Type = 0; failmsg.Reason = "browser no authority"; DispatchCtrlLib.FuncCallback(failmsg); return; } return 0; } //用于会议接听来电 DispatchRtcLib.VoipLastIncomingCallInfo = null; DispatchRtcLib.VoipLastIncomingCallSdp = null; DispatchRtcLib.OnMsgCallback = function(Msg) { var ret = true; switch (Msg.Msg) { case "WebrtcSdpNotify": { DispatchRtcLib.SetRemoteSdp(Msg.CallID, Msg.WebrtcSdp, Msg.CallType); ret = false; DispatchRtcLib.VoipLastIncomingCallSdp = Msg; break; } case "VoipCallStatusNotify": { if (CurrentConference.CallID == Msg.CallID) { DispatchConference.RecvVoipCallStatus(Msg); ret = false; break; } if (Msg.Status > 2) { DispatchRtcLib.Stop(Msg.CallID); } DispatchVideoLib.func_call_onStatus(Msg.CallID, Msg.Status); break; } case "VoipDispatchCallRsp": { if (Msg.Result == 0 && Msg.CallID!= 0) { DispatchCtrlLib.VoipHandleIncomingCall(Msg.CallID, 1); } break; } case "VoipIncomingCallNotify": { DispatchRtcLib.VoipLastIncomingCallInfo = Msg; if (Msg.ConferenceInvite) //通用版本暂不升级到会议接口 { // if (CurrentConference.ConfID) //当前已在会议中 // { // ret = false; // DispatchCtrlLib.VoipHandleIncomingCall(Msg.CallID, 0); //直接拒接 // } // else // { ret = false; CurrentConference.CallID = Msg.CallID; CurrentConference.ConfID = Msg.ExtNum; CurrentConference.Type = Msg.CallType; var msg = new Object; msg.Msg = "ConferenceInviteNotify"; msg.ConfID = Msg.ExtNum; msg.Type = Msg.CallType; DispatchCtrlLib.FuncCallback(msg); // } } break; } case "MonitorVideoStatusNotify": { if (Msg.Status > 2) { DispatchRtcLib.Stop(Msg.CallID); } break; } case "ConferenceWsInviteNotify": { ret = false; DispatchConference.RecvWsInviteNotify(Msg); break; } default: { break; } } return ret; } DispatchRtcLib.getRtcLib = function(CallID, ForceNew) { var rtcLib = null; for (var i = 0; i < DispatchRtcLib_Array.length; i++) { if (DispatchRtcLib_Array[i] != null && DispatchRtcLib_Array[i].CallID == CallID) { rtcLib = DispatchRtcLib_Array[i]; console.log("Get DispatchRtcLib_" + rtcLib.index + ", CallID:" + rtcLib.CallID); break; } } if (rtcLib == null && ForceNew) { rtcLib = new DispatchRtcLib; rtcLib.CallID = CallID; var index = -1; for (var i = 0; i < DispatchRtcLib_Array.length; i++) { if (!DispatchRtcLib_Array[i] == null) { index = i; break; } } if (index != -1) { rtcLib.index = index; DispatchRtcLib_Array[index] = rtcLib; } else { rtcLib.index = DispatchRtcLib_Array.length; DispatchRtcLib_Array.push(rtcLib); } console.log("New DispatchRtcLib_" + rtcLib.index + ", CallID:" + rtcLib.CallID); } return rtcLib; } var OnAddStream = function(event) { var rtcLib = event.currentTarget.rtcLib; rtcLib.RemoteStream = event.stream; if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //单向视频 if (rtcLib.RemoteVideoLib) { rtcLib.RemoteVideoLib.video.srcObject = rtcLib.RemoteStream; if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //默认静音 //rtcLib.RemoteVideoLib.video.muted = true; rtcLib.RemoteVideoLib.video.volume = 0; } } } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_ConfAudio) { //双向语音 rtcLib.RemoteAudioLabel = document.createElement('audio'); rtcLib.RemoteAudioLabel.id="DispatchAudio_" + rtcLib.index; rtcLib.RemoteAudioLabel.autoplay = true; document.body.appendChild(rtcLib.RemoteAudioLabel); rtcLib.RemoteAudioLabel.srcObject = rtcLib.RemoteStream; } else if (rtcLib.CallType == DispatchRtcLib_VoipVideo) { //双向视频 if (rtcLib.RemoteVideoLib) { rtcLib.RemoteVideoLib.video.srcObject = rtcLib.RemoteStream; } } else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPull) { //双向视频 if (rtcLib.RemoteVideoLib) { rtcLib.RemoteVideoLib.video.srcObject = rtcLib.RemoteStream; } } } function isIPv4(ip){ var exp=/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; var flag = ip.match(exp); if(flag != undefined && flag!=""){ return true; } else { return false; } } DispatchRtcLib.CreateOffer = function(CallID, Type, OnCreateOfferSucess, OnCreateOfferFail, target) { var rtcLib = DispatchRtcLib.getRtcLib(CallID, true); rtcLib.CallType = Type; if (target) { rtcLib.target = target; } var localmedia = { audio: false, video: false, autoGainControl: true, echoCancellation: true, noiseSuppression: true, }; var OnCandidateDone = function() { if (rtcLib.CandidateTimeout) { clearTimeout(rtcLib.CandidateTimeout); rtcLib.CandidateTimeout = null; } console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Done"); if (rtcLib.localsdp.indexOf("m=video") != -1 && rtcLib.LocalVideoCandidates.length == 0) { var port = Math.floor((Math.random() * 20000) + 40000); var candidate = "a=candidate:1 1 udp 1 169.254.28.42 " + port + " typ host"; rtcLib.LocalVideoCandidates.push(candidate); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Done Add Video Candidates: " + candidate); } if (rtcLib.localsdp.indexOf("m=audio") != -1 && rtcLib.LocalAudioCandidates.length == 0) { var port = Math.floor((Math.random() * 20000) + 40000); var candidate = "a=candidate:1 1 udp 1 169.254.28.42 " + port + " typ host"; rtcLib.LocalAudioCandidates.push(candidate); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Done Add Audio Candidates: " + candidate); } if (Type == DispatchRtcLib_MoniterVideo) { //console.log("compressLocalSdp "); //console.log(rtcLib.localsdp); //console.log(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates)); //OnCreateOfferSucess(compressLocalSdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates)); OnCreateOfferSucess(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates)); } else { OnCreateOfferSucess(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates)); } } var OnIceCandidate = function(event) { if (event.candidate && event.candidate.protocol == "udp" && isIPv4(event.candidate.address)) { var v_m = event.currentTarget.localDescription.sdp.indexOf("m=video"); var a_m = event.currentTarget.localDescription.sdp.indexOf("m=audio"); if (v_m == -1) { a_m = 0; } else if (a_m == -1) { v_m = 0; } else { if (v_m < a_m) { v_m = 0; a_m = 1; } else { v_m = 1; a_m = 0; } } if (event.candidate.sdpMid == a_m) { var can = event.candidate.candidate.split(" "); var candidate_str = ""; for (var i = 0; i < can.length; i++) { var line = can[i]; if (line) { if (i == 0) { line = "candidate:1"; } if (i == 1) { line = "1"; } if (i == 3) { line = "1"; } if (line == "generation") { can[i+1] = null; continue; } if (line == "ufrag") { can[i+1] = null; continue; } if (line == "network-id") { can[i+1] = null; continue; } if (line == "network-cost") { can[i+1] = null; continue; } candidate_str += line; candidate_str += " "; } } candidate_str = candidate_str.substr(0, candidate_str.length-1) var a = "a=" + candidate_str; rtcLib.LocalAudioCandidates.push(a); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Audio " + a); } else if (event.candidate.sdpMid == v_m) { var can = event.candidate.candidate.split(" "); var candidate_str = ""; for (var i = 0; i < can.length; i++) { var line = can[i]; if (line) { if (i == 0) { line = "candidate:1"; } if (i == 1) { line = "1"; } if (i == 3) { line = "1"; } if (line == "generation") { can[i+1] = null; continue; } if (line == "ufrag") { can[i+1] = null; continue; } if (line == "network-id") { can[i+1] = null; continue; } if (line == "network-cost") { can[i+1] = null; continue; } candidate_str += line; candidate_str += " "; } } candidate_str = candidate_str.substr(0, candidate_str.length-1) var a = "a=" + candidate_str; rtcLib.LocalVideoCandidates.push(a); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video " + a); } } else if (event.candidate && event.candidate.protocol == undefined && event.candidate.address == undefined) { if (event.candidate.sdpMid == "audio") { var a = "a=" + event.candidate.candidate; rtcLib.LocalAudioCandidates.push(a); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Audio " + a); } else if (event.candidate.sdpMid == "video") { var a = "a=" + event.candidate.candidate; rtcLib.LocalVideoCandidates.push(a); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video " + a); } console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video " + a); } else if (event.candidate == null) { if (rtcLib.CandidateTimeout) { OnCandidateDone(); } } } var OnConnectionStatechange = function(event) { console.log("DispatchRtcLib " + rtcLib.CallID + " OnConnectionStatechange " + rtcLib.peerconnection.connectionState); } rtcLib.peerconnection = new RTCPeerConnection(); rtcLib.peerconnection.rtcLib = rtcLib; rtcLib.peerconnection.onicecandidate = OnIceCandidate; rtcLib.peerconnection.onaddstream = OnAddStream; rtcLib.peerconnection.onconnectionstatechange = OnConnectionStatechange; var OnGetUserMediaSucess = function(stream) { var constraints = null; if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //单向视频 rtcLib.LocalVideoStream = null; rtcLib.LocalAudioStream = null; rtcLib.peerconnection.addTransceiver("audio", {direction:"recvonly"}); rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"}); } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_ConfAudio) { //双向语音 rtcLib.LocalVideoStream = null; rtcLib.LocalAudioStream = stream; if (rtcLib.peerconnection.addTrack) { for (var i = 0; i < stream.getTracks().length; i++) { var Track = stream.getTracks()[i]; if (Track.kind == "audio") { rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalAudioStream = stream; rtcLib.CurrentMicphone = Track.label; console.log("AudioDev Using " + Track.label); } } } else { rtcLib.peerconnection.addStream(stream); } } else if (rtcLib.CallType == DispatchRtcLib_VoipVideo) { //双向视频 if (rtcLib.peerconnection.addTrack) { for (var i = 0; i < stream.getTracks().length; i++) { var Track = stream.getTracks()[i]; if (Track.kind == "audio") { rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalAudioStream = stream; rtcLib.CurrentMicphone = Track.label; console.log("AudioDev Using " + Track.label); } else if (Track.kind == "video") { rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalVideoStream = stream; rtcLib.CurrentCaptureDev = Track.label; console.log("VideoDev Using " + Track.label); } } if (!localmedia.video) { //无本地摄像头,使用头像代替 var Track = DispatchRtcLib.LocalPhotoStream.getVideoTracks()[0]; console.log("peerconnection.addTrack " + Track.kind); rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, DispatchRtcLib.LocalPhotoStream); if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = DispatchRtcLib.LocalPhotoStream; } } else { if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream; rtcLib.LocalVideoLib.video.muted = true; } } } else { rtcLib.LocalVideoStream = stream; rtcLib.peerconnection.addStream(stream); if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream; rtcLib.LocalVideoLib.video.muted = true; } } } else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush) { //会议视频推送 for (var i = 0; i < stream.getTracks().length; i++) { var Track = stream.getTracks()[i]; if (Track.kind == "video") { rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalVideoStream = stream; rtcLib.CurrentCaptureDev = Track.label; console.log("VideoDev Using " + Track.label); } } if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream; rtcLib.LocalVideoLib.video.muted = true; } constraints = { offerToReceiveVideo:false, offerToReceiveAudio:false }; } else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPull) { rtcLib.LocalVideoStream = null; rtcLib.LocalAudioStream = null; //rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"}); rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"}); } var OnPcCreateOfferSucess = function(offer) { rtcLib.localsdp = offer.sdp; var OnSetLocalDescriptionSucess = function() { console.log("OnSetLocalDescriptionSucess"); } var OnSetLocalDescriptionFail = function(exception) { console.log("OnSetLocalDescriptionFail " + exception); OnCreateOfferFail("初始化本地媒体失败"); } rtcLib.peerconnection.setLocalDescription(offer, OnSetLocalDescriptionSucess, OnSetLocalDescriptionFail); rtcLib.CandidateTimeout = setTimeout(OnCandidateDone, 2000); } var OnPcCreateOfferFail = function(error) { console.log("OnCreateOfferFail " + error); OnCreateOfferFail("初始化本地媒体失败"); } rtcLib.peerconnection.createOffer(OnPcCreateOfferSucess, OnPcCreateOfferFail, constraints); } var OnGetUserMediaFail = function(error) { console.log("打开本地设备失败 " + error.code + " : "+ error.message); OnCreateOfferFail("打开本地设备失败"); } if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //单向视频 OnGetUserMediaSucess(null); return CallID; } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_ConfAudio) { //双向语音 if (navigator.mediaDevices) { if (DispatchDevices.SpeakerList.length == 0) { //无麦克风或扬声器 console.log("本地无可用扬声器"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -1; } if (DispatchDevices.MicphoneList.length == 0) { //无麦克风或扬声器 console.log("本地无可用麦克风"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -2; } } localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId}; } else if (rtcLib.CallType == DispatchRtcLib_VoipVideo) { //双向视频 if (navigator.mediaDevices) { if (DispatchDevices.SpeakerList.length == 0) { //无麦克风或扬声器 console.log("本地无可用扬声器"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -1; } if (DispatchDevices.MicphoneList.length == 0) { //无麦克风或扬声器 console.log("本地无可用麦克风"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -2; } localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId}; if (DispatchDevices.CameraList.length > 0) { //localmedia.video = true; localmedia.video = { width: DispatchDevices.CameraWidth, height: DispatchDevices.CameraHeight, frameRate: DispatchDevices.CameraFrameRate, deviceId: DispatchDevices.UsingCamera.deviceId }; } } else { localmedia.audio = true; localmedia.video = true; } } else if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush) { //视频推送 if (navigator.mediaDevices) { if (DispatchDevices.CameraList.length > 0) { //localmedia.video = true; localmedia.video = { width: DispatchDevices.CameraWidth, height: DispatchDevices.CameraHeight, frameRate: DispatchDevices.CameraFrameRate, deviceId: DispatchDevices.UsingCamera.deviceId }; } else { OnGetUserMediaSucess(DispatchRtcLib.LocalPhotoStream); return CallID; } } else { localmedia.audio = false; localmedia.video = true; } } if (rtcLib.CallType == DispatchRtcLib_ConfVideoPull) { //单向视频 OnGetUserMediaSucess(null); return CallID; } getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail); console.log("尝试打开本地设备 " + localmedia.toString()); return CallID; } DispatchRtcLib.ModifySdp = function(sdp, remoteip) { if (remoteip.length == 0) { return sdp; } console.log("DispatchRtcLib.ModifySdp " + remoteip); var lineArray = sdp.split("\r\n"); for (var i = 0; i < lineArray.length; i++) { var line = lineArray[i]; if (line.indexOf("a=candidate") == 0) { // 0 1 2 3 4 5 6 7 //a=candidate:1 1 UDP 2015363327 192.168.0.209 15149 typ host //a=candidate:3 1 TCP 1010827519 192.168.0.209 15025 typ host tcptype passive var candidateArray = line.split(" "); candidateArray[4] = remoteip; lineArray[i] = candidateArray.join(" "); } } var sdp_after = lineArray.join("\r\n"); return sdp_after; } DispatchRtcLib.SetRemoteSdp = function(CallID, RemoteSdp, CallType) { var rtcLib = DispatchRtcLib.getRtcLib(CallID, true); rtcLib.RemoteSdp = RemoteSdp; rtcLib.CallType = CallType; if (rtcLib.CallType == DispatchRtcLib_MoniterVideo || rtcLib.CallType == DispatchRtcLib_ConfVideoPush || rtcLib.CallType == DispatchRtcLib_ConfVideoPull) { rtcLib.RemoteSdp = DispatchRtcLib.ModifySdp(rtcLib.RemoteSdp, DispatchCtrlLib.msip); } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_VoipVideo || rtcLib.CallType == DispatchRtcLib_ConfAudio) { rtcLib.RemoteSdp = DispatchRtcLib.ModifySdp(rtcLib.RemoteSdp, DispatchCtrlLib.fsip); } if (rtcLib.peerconnection) { var remotesdp = new RTCSessionDescription(); remotesdp.type = "answer"; remotesdp.sdp = rtcLib.RemoteSdp; var OnSetRemoteDescriptionSucess = function() { console.log("OnSetRemoteDescriptionSucess"); } var OnSetRemoteDescriptionFail = function(exception) { console.log("OnSetRemoteDescriptionFail " + exception); if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //单向视频 var failmsg = new Object; failmsg.Msg = "MonitorVideoStatusNotify"; failmsg.CallID = CallID; failmsg.Status = 7; failmsg.Reason = exception.toString(); DispatchCtrlLib.FuncCallback(failmsg); DispatchCtrlLib.MonitorVideoHangup(CallID); DispatchRtcLib.Stop(CallID); } else { //双向语音 双向视频 var failmsg = new Object; failmsg.Msg = "VoipCallStatusNotify"; failmsg.CallID = CallID; failmsg.Status = 7; failmsg.Reason = exception.toString(); DispatchCtrlLib.FuncCallback(failmsg); DispatchCtrlLib.VoipHangupCall(CallID); DispatchRtcLib.Stop(CallID); } } rtcLib.peerconnection.setRemoteDescription(remotesdp, OnSetRemoteDescriptionSucess, OnSetRemoteDescriptionFail); } } DispatchRtcLib.CreateAnswer = function(CallID, OnCreateAnswerSucess, OnCreateAnswerFail) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null || rtcLib.RemoteSdp == null) { console.log("初始化本地媒体失败"); return -3; } var OnCandidateDone = function() { if (rtcLib.CandidateTimeout) { clearTimeout(rtcLib.CandidateTimeout); rtcLib.CandidateTimeout = null; } console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Done"); OnCreateAnswerSucess(changesdp(rtcLib.localsdp, rtcLib.LocalVideoCandidates, rtcLib.LocalAudioCandidates)); } var OnIceCandidate = function(event) { if (event.candidate && event.candidate.protocol == "udp" && isIPv4(event.candidate.address)) { var v_m = event.currentTarget.localDescription.sdp.indexOf("m=video"); var a_m = event.currentTarget.localDescription.sdp.indexOf("m=audio"); if (v_m == -1) { a_m = 0; } else if (a_m == -1) { v_m = 0; } else { if (v_m < a_m) { v_m = 0; a_m = 1; } else { v_m = 1; a_m = 0; } } if (event.candidate.sdpMid == a_m) { var a = "a=" + event.candidate.candidate; rtcLib.LocalAudioCandidates.push(a); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Audio " + a); } else if (event.candidate.sdpMid == v_m) { var a = "a=" + event.candidate.candidate; rtcLib.LocalVideoCandidates.push(a); console.log("DispatchRtcLib " + rtcLib.CallID + "OnIceCandidate Video " + a); } } else if (event.candidate == null) { if (rtcLib.CandidateTimeout) { OnCandidateDone(); } } } rtcLib.peerconnection = new RTCPeerConnection(); rtcLib.peerconnection.rtcLib = rtcLib; rtcLib.peerconnection.onicecandidate = OnIceCandidate; rtcLib.peerconnection.onaddstream = OnAddStream; var localmedia = { audio: false, video: false, autoGainControl: true, echoCancellation: true, noiseSuppression: true, }; var OnGetUserMediaSucess = function(stream) { if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //单向视频 rtcLib.LocalVideoStream = null; rtcLib.LocalAudioStream = null; rtcLib.peerconnection.addTransceiver("audio", {direction:"recvonly"}); rtcLib.peerconnection.addTransceiver("video", {direction:"recvonly"}); } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_ConfAudio) { //双向语音 rtcLib.LocalVideoStream = null; rtcLib.LocalAudioStream = stream; for (const Track of stream.getTracks()) { if (Track.kind == "audio") { rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalAudioStream = stream; rtcLib.CurrentMicphone = Track.label; console.log("AudioDev Using " + Track.label); } } } else if (rtcLib.CallType == DispatchRtcLib_VoipVideo) { //双向视频 for (const Track of stream.getTracks()) { if (Track.kind == "audio") { rtcLib.AudioRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalAudioStream = stream; rtcLib.CurrentMicphone = Track.label; console.log("AudioDev Using " + Track.label); } else if (Track.kind == "video") { rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, stream); rtcLib.LocalVideoStream = stream; rtcLib.CurrentCaptureDev = Track.label; console.log("VideoDev Using " + Track.label); } } if (!localmedia.video) { //无本地摄像头,使用头像代替 var Track = DispatchRtcLib.LocalPhotoStream.getVideoTracks()[0]; console.log("peerconnection.addTrack " + Track.kind); rtcLib.VideoRTCRtpSender = rtcLib.peerconnection.addTrack(Track, DispatchRtcLib.LocalPhotoStream); if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = DispatchRtcLib.LocalPhotoStream; } } else { if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = rtcLib.LocalVideoStream; rtcLib.LocalVideoLib.video.muted = true; } } } var OnSetRemoteDescriptionSucess = function(offer) { var OnPcCreateAnswerSucess = function(answer) { console.log("OnPcCreateAnswerSucess"); rtcLib.localsdp = answer.sdp; var OnSetLocalDescriptionSucess = function() { console.log("OnSetLocalDescriptionSucess"); } var OnSetLocalDescriptionFail = function(exception) { console.log("OnSetLocalDescriptionFail " + exception.toString()); OnCreateAnswerFail(exception.toString()); } console.log(answer.sdp); rtcLib.peerconnection.setLocalDescription(answer, OnSetLocalDescriptionSucess, OnSetLocalDescriptionFail); rtcLib.CandidateTimeout = setTimeout(OnCandidateDone, 2000); } var OnPcCreateAnswerFail = function(exception) { console.log("OnPcCreateAnswerFail " + exception.toString()); OnCreateAnswerFail(exception.toString()); } rtcLib.peerconnection.createAnswer(OnPcCreateAnswerSucess, OnPcCreateAnswerFail); } var OnSetRemoteDescriptionFail = function(error) { console.log("OnSetRemoteDescriptionFail " + error.toString()); OnCreateAnswerFail(error.toString()); } rtcLib.RemoteSdp = changesdp_local(rtcLib.RemoteSdp); var remotesdp = new RTCSessionDescription(); remotesdp.type = "offer"; remotesdp.sdp = rtcLib.RemoteSdp; rtcLib.peerconnection.setRemoteDescription(remotesdp, OnSetRemoteDescriptionSucess, OnSetRemoteDescriptionFail); } var OnGetUserMediaFail = function(error) { console.log("打开本地设备失败 " + error.code + " : "+ error.message); OnCreateAnswerFail("打开本地设备失败"); } if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //单向视频 OnGetUserMediaSucess(null); return 0; } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_ConfAudio) { //双向语音 if (DispatchDevices.SpeakerList.length == 0) { //无麦克风或扬声器 console.log("本地无可用扬声器"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -1; } if (DispatchDevices.MicphoneList.length == 0) { //无麦克风或扬声器 console.log("本地无可用麦克风"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -2; } localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId}; } else if (rtcLib.CallType == DispatchRtcLib_VoipVideo) { //双向视频 if (DispatchDevices.SpeakerList.length == 0) { //无麦克风或扬声器 console.log("本地无可用扬声器"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -1; } if (DispatchDevices.MicphoneList.length == 0) { //无麦克风或扬声器 console.log("本地无可用麦克风"); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; return -2; } localmedia.audio = {deviceId: DispatchDevices.UsingMicphone.deviceId}; if (DispatchDevices.CameraList.length > 0) { //localmedia.video = true; localmedia.video = { width: DispatchDevices.CameraWidth, height: DispatchDevices.CameraHeight, frameRate: DispatchDevices.CameraFrameRate, deviceId: DispatchDevices.UsingCamera.deviceId }; } } getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail); } DispatchRtcLib.DisplayVideo = function(CallID, VideoLib) { if (CallID == -1) { for (var i = 0; i < DispatchRtcLib_Array.length; i++) { if (DispatchRtcLib_Array[i] != null && (DispatchRtcLib_Array[i].CallType == DispatchRtcLib_VoipVideo || DispatchRtcLib_Array[i].CallType == DispatchRtcLib_ConfVideoPush)) { DispatchRtcLib_Array[i].LocalVideoLib = VideoLib; if (DispatchRtcLib_Array[i].LocalVideoStream) { VideoLib.video.srcObject = DispatchRtcLib_Array[i].LocalVideoStream; VideoLib.video.muted = true; } else { VideoLib.video.srcObject = DispatchRtcLib.LocalPhotoStream; } if (DispatchRtcLib_Array[i].CallType == DispatchRtcLib_MoniterVideo) { VideoLib.flag_bMoniterVideo = true; } VideoLib.rtclib = DispatchRtcLib_Array[i]; break; } } } else { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib) { rtcLib.RemoteVideoLib = VideoLib; if (rtcLib.RemoteStream) { VideoLib.video.srcObject = rtcLib.RemoteStream; if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { //默认静音 //VideoLib.video.muted = true; VideoLib.video.volume = 0; VideoLib.flag_bMoniterVideo = true; } } if (rtcLib.CallType == DispatchRtcLib_MoniterVideo) { VideoLib.flag_bMoniterVideo = true; } VideoLib.rtclib = rtcLib; } } } DispatchRtcLib.Stop = function(CallID) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null) { return; } if (rtcLib.RemoteStream) { for (var i = 0; i < rtcLib.RemoteStream.getTracks().length; i++) { rtcLib.RemoteStream.getTracks()[i].stop(); } rtcLib.RemoteStream = null; } if (rtcLib.LocalVideoStream && !rtcLib.LocalVideoStream.bLocalPhoto) { for (var i = 0; i < rtcLib.LocalVideoStream.getTracks().length; i++) { rtcLib.LocalVideoStream.getTracks()[i].stop(); } rtcLib.LocalVideoStream = null; } if (rtcLib.LocalAudioStream) { for (var i = 0; i < rtcLib.LocalAudioStream.getTracks().length; i++) { rtcLib.LocalAudioStream.getTracks()[i].stop(); } rtcLib.LocalAudioStream = null; } if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.StopDisplay(); rtcLib.LocalVideoLib.video.muted = false; rtcLib.LocalVideoLib = null; } if (rtcLib.RemoteVideoLib) { rtcLib.RemoteVideoLib.StopDisplay(); rtcLib.RemoteVideoLib.video.muted = false; rtcLib.RemoteVideoLib = null; } if (rtcLib.RemoteAudioLabel) { document.body.removeChild(rtcLib.RemoteAudioLabel); rtcLib.RemoteAudioLabel = null; } if (rtcLib.peerconnection) { rtcLib.peerconnection.close(); rtcLib.peerconnection = null; } console.log("DispatchRtcLib.Stop " + rtcLib.index + " CallID: " + CallID); DispatchRtcLib_Array[rtcLib.index] = null; rtcLib = null; } DispatchRtcLib.VoipSetCamera = function(CallID, VideoSrc) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null || (rtcLib.CallType != DispatchRtcLib_VoipVideo && rtcLib.CallType != DispatchRtcLib_ConfVideoPush)) { return -1; } if (rtcLib.CurrentCaptureDev == VideoSrc) { return 0; } var old_track = rtcLib.VideoRTCRtpSender.track; old_track.enabled = false; var Constraints = old_track.getConstraints(); console.log(Constraints); var replaceVideoTrack = function(stream) { stream.getVideoTracks()[0].enabled = false; rtcLib.VideoRTCRtpSender.replaceTrack(stream.getVideoTracks()[0]) .then(() => { console.log("replaceTrack Sucess to " + VideoSrc); setTimeout(function(){ stream.getVideoTracks()[0].enabled = true; }, 1000); if (rtcLib.CurrentCaptureDev == "screen") { if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush) { var xml = "<VOIP>" + "<Command-Name>ConferenceScreenShareEnd</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "</Command-Info>" + "</VOIP>\r\n"; DispatchConference.WsSend(xml); } //防止OnDisplayMediaInactive rtcLib.LocalVideoStream.ForbiddenInactive = true; } else if (VideoSrc == "screen") { if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush) { var xml = "<VOIP>" + "<Command-Name>ConferenceScreenShareStart</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "</Command-Info>" + "</VOIP>\r\n"; DispatchConference.WsSend(xml); } } rtcLib.CurrentCaptureDev = VideoSrc; if (rtcLib.LocalVideoLib) { rtcLib.LocalVideoLib.video.srcObject = stream; rtcLib.LocalVideoLib.video.muted = true; } rtcLib.LocalVideoStream = stream; if (!old_track.bLocalPhoto) { old_track.stop(); } }) .catch(error => { console.log(error); }); } if (VideoSrc == "screen") { if (rtcLib.CallType == DispatchRtcLib_ConfVideoPush) { //屏幕共享时,判断会议中是否有人屏幕共享中 if (CurrentConference.ScreenShare.length != 0) { console.log("会议中有其他人正在桌面共享 " + CurrentConference.ScreenShare); return -5; } } var OnGetDisplayMediaSucess = function(stream) { var OnDisplayMediaInactive = function(event) { console.log("停止桌面共享"); if (rtcLib.LocalVideoStream.ForbiddenInactive) { return; } if (DispatchDevices.CameraList.length == 0) { setTimeout(DispatchRtcLib.VoipSetCamera(CallID, "none"), 1000); } else { setTimeout(DispatchRtcLib.VoipSetCamera(CallID, DispatchDevices.CameraList[0].label), 1000); } } stream.addEventListener("inactive", OnDisplayMediaInactive); replaceVideoTrack(stream); } navigator.mediaDevices.getDisplayMedia({video:true}) .then(stream => { OnGetDisplayMediaSucess(stream); }) .catch(error => { console.log(error); }); } else if (VideoSrc == "none") { replaceVideoTrack(DispatchRtcLib.LocalPhotoStream); } else { var device = null; for (var i = 0; i < DispatchDevices.CameraList.length; i++) { var camera = DispatchDevices.CameraList[i]; if (camera.label == VideoSrc) { device = camera.deviceId; break; } } if (device) { var localmedia = { audio: false, video: false, autoGainControl: true, echoCancellation: true, noiseSuppression: true, }; localmedia.video = { width: DispatchDevices.CameraWidth, height: DispatchDevices.CameraHeight, frameRate: DispatchDevices.CameraFrameRate, deviceId: device }; var OnGetUserMediaSucess = function(stream) { replaceVideoTrack(stream); } var OnGetUserMediaFail = function(error) { console.log("打开本地设备失败 " + error.code + " : "+ error.message); } getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail); } else { return -3; } } return 0; } DispatchRtcLib.VoipSetMicphone = function(CallID, Micphone) { var device = null; var groupId = null; var old_groupId = null; var rtcLib = DispatchRtcLib.getRtcLib(CallID); for (var i = 0; i < DispatchDevices.MicphoneList.length; i++) { if (DispatchDevices.MicphoneList[i].label == Micphone) { console.log("VoipSetMicphone to " + DispatchDevices.MicphoneList[i].label); device = DispatchDevices.MicphoneList[i].deviceId; groupId = DispatchDevices.MicphoneList[i].groupId; } if (DispatchDevices.MicphoneList[i].label == rtcLib.CurrentMicphone) { old_groupId = DispatchDevices.MicphoneList[i].groupId; } } if (!device) { return -1; } if (rtcLib.CurrentMicphone == Micphone || groupId == old_groupId) { console.log("VoipSetMicphone nochange "); return 0; } var old_track = rtcLib.AudioRTCRtpSender.track; var localmedia = { audio: {deviceId: device}, video: false, autoGainControl: true, echoCancellation: true, noiseSuppression: true, }; var OnGetUserMediaSucess = function(stream) { rtcLib.AudioRTCRtpSender.replaceTrack(stream.getAudioTracks()[0]) .then(() => { console.log("replaceTrack Sucess to " + Micphone); rtcLib.CurrentMicphone = Micphone; old_track.stop(); }) .catch(error => { console.log(error); }); } var OnGetUserMediaFail = function(error) { console.log("打开本地设备失败 " + error.code + " : "+ error.message); } getUserMedia(localmedia, OnGetUserMediaSucess, OnGetUserMediaFail); } DispatchRtcLib.VoipSetCallVolume = function(CallID, Volume) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null) { return -1; } if (Volume < 0) { Volume = 0; } else if (Volume > 100) { Volume = 100; } Volume = Volume / 100.0; if (rtcLib.RemoteAudioLabel) { rtcLib.RemoteAudioLabel.volume = Volume; } if (rtcLib.RemoteVideoLib) { rtcLib.RemoteVideoLib.video.volume = Volume; } console.log("DispatchRtcLib.VoipSetCallVolume " + Volume); return 0; } DispatchRtcLib.VoipSetCallMicMute = function(CallID, Mute) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null) { return -1; } var bEnabled = true; if (Mute) { bEnabled = false; } if (rtcLib.LocalAudioStream) { var AudioTracks = rtcLib.LocalAudioStream.getAudioTracks(); for (var i = 0; i < AudioTracks.length; i++) { AudioTracks[i].enabled = bEnabled; } } return 0; } DispatchRtcLib.VoipSetSpeaker = function(CallID, Speaker) { var deviceId = null; for (var i = 0; i < DispatchDevices.SpeakerList.length; i++) { if (DispatchDevices.SpeakerList[i].label == Speaker) { console.log("VoipSetSpeaker to " + DispatchDevices.SpeakerList[i].label); deviceId = DispatchDevices.SpeakerList[i].deviceId; break; } } if (!deviceId) { return -3; } var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null) { return -1; } if (rtcLib.CallType == DispatchRtcLib_MoniterVideo || rtcLib.CallType == DispatchRtcLib_VoipVideo) { //视频 rtcLib rtcLib.RemoteVideoLib.video.setSinkId(deviceId) .then(function() { console.log("VoipSetSpeaker Sucess"); }) .catch(function(error) { console.log("VoipSetSpeaker Fail"); console.log(error) }); } else if (rtcLib.CallType == DispatchRtcLib_VoipAudio || rtcLib.CallType == DispatchRtcLib_ConfAudio) { //语音 rtcLib.RemoteAudioLabel.setSinkId(deviceId) .then(function() { console.log("VoipSetSpeaker Sucess"); }) .catch(function(error) { console.log("VoipSetSpeaker Fail"); console.log(error) }); } return 0; } DispatchRtcLib.ConvertRecordWorker = null; DispatchRtcLib.ConvertRecordWorkerLoaded = false; DispatchRtcLib.ConvertRecordList = []; DispatchRtcLib.ConvertingRecord = null; //{name: , file: } DispatchRtcLib.NewConvertRecordWorker = function() { if (DispatchRtcLib.ConvertRecordWorker == null) { var blob = URL.createObjectURL(new Blob(['importScripts("' + DispatchLibPath + 'ffmpeg/ffmpeg_asm.js");var now = Date.now;function print(text) {postMessage({"type" : "stdout","data" : text});};onmessage = function(event) {var message = event.data;if (message.type === "command") {var Module = {print: print,printErr: print,files: message.files || [],arguments: message.arguments || [],TOTAL_MEMORY: message.TOTAL_MEMORY || false};postMessage({"type" : "start","data" : Module.arguments.join(" ")});postMessage({"type" : "stdout","data" : "Received command: " +Module.arguments.join(" ") +((Module.TOTAL_MEMORY) ? ". Processing with " + Module.TOTAL_MEMORY + " bits." : "")});var time = now();var result = ffmpeg_run(Module);var totalTime = now() - time;postMessage({"type" : "stdout","data" : "Finished processing (took " + totalTime + "ms)"});postMessage({"type" : "done","data" : result,"time" : totalTime});}};postMessage({"type" : "ready"});'], { type: 'application/javascript' })); DispatchRtcLib.ConvertRecordWorker = new Worker(blob); URL.revokeObjectURL(blob); DispatchRtcLib.ConvertRecordWorker.onmessage = function(event) { var message = event.data; if (message.type == "ready") { console.log("ffmpeg loaded"); DispatchRtcLib.ConvertRecordWorkerLoaded = true; DispatchRtcLib.pushConvertRecordList(null); } else if (message.type == "stdout") { console.log("ffmpeg log:", message.data); } else if (message.type == "start") { console.log("ffmpeg start process"); } else if (message.type == "done") { console.log("ffmpeg done process"); var result = message.data[0]; console.log(JSON.stringify(result)); var filename = DispatchRtcLib.ConvertingRecord.name; var fullBlob = new File([result.data], filename, { type: 'video/mp4' }); var url = window.URL.createObjectURL(fullBlob); var a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = filename; document.body.appendChild(a); a.click(); setTimeout(function () { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 1000); DispatchRtcLib.ConvertingRecord = null; DispatchRtcLib.pushConvertRecordList(null); } }; } } DispatchRtcLib.pushConvertRecordList = function(record) { if (record) { DispatchRtcLib.ConvertRecordList.splice(DispatchRtcLib.ConvertRecordList.length, 0, record) } if (DispatchRtcLib.ConvertRecordWorkerLoaded) { //Worker加载完 if (DispatchRtcLib.ConvertRecordList.length > 0 && DispatchRtcLib.ConvertingRecord == null) { DispatchRtcLib.ConvertingRecord = DispatchRtcLib.ConvertRecordList[0]; DispatchRtcLib.ConvertRecordList.splice(0, 1); DispatchRtcLib.ConvertRecordWorker.postMessage({ type: 'command', arguments: '-i video.webm -c:v copy -an -strict experimental output.mp4'.split(' '), //arguments: '-i video.webm -c:v copy -c:a copy -strict experimental output.mp4'.split(' '), files: [ { data: new Uint8Array(DispatchRtcLib.ConvertingRecord.file), name: 'video.webm' } ] }); } } } DispatchRtcLib.StartRecordStream = function(CallID) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null) { return -1; } if (!rtcLib.RemoteStream) { return -2; } DispatchRtcLib.NewConvertRecordWorker(); rtcLib.RecorderStartTime = DispatchVideoLib.currentTime(); rtcLib.recordVideo = RecordRTC(rtcLib.RemoteStream, { type: 'video', mimeType: 'video/webm;codecs=h264' }); rtcLib.recordVideo.startRecording(); } DispatchRtcLib.StopRecordStream = function(CallID) { var rtcLib = DispatchRtcLib.getRtcLib(CallID); if (rtcLib == null) { return -1; } if (!rtcLib.recordVideo) { return -2; } rtcLib.recordVideo.stopRecording(function(url) { var videoBlob = rtcLib.recordVideo.getBlob(); var fileReader = new FileReader(); fileReader.onload = function() { DispatchRtcLib.pushConvertRecordList({ name: rtcLib.target.Number + "-[" + rtcLib.RecorderStartTime + "]-[" + DispatchVideoLib.currentTime() + "].mp4", file: this.result, }); }; fileReader.readAsArrayBuffer(videoBlob); rtcLib.recordVideo = null; }); } DispatchRtcLib.VoipSetCameraParms = function(Width, Height, FrameRate) { if (DispatchDevices.UsingCamera) { return -1; } DispatchDevices.CameraWidth = Width; DispatchDevices.CameraHeight = Height; DispatchDevices.CameraFrameRate = FrameRate; return 0; } //————————————————————————————————————— DispatchConference —————————————————————————————————————————— function DispatchConferenceMember() { this.Status = 0; //状态 this.ExtNum = ""; //分机号 this.MemName = ""; //名称 this.MemType = ""; //类型 1-调度台 2-手机 3-SIP话机 4-未知分机 this.MemMute = 0; //是否禁言 0-讲话中 1-禁言中 this.MemVideo = 0; //视频是否就绪 0-未就绪 1-就绪 }; function DispatchConference() { this.ConfID = null; this.MemberList = new Array; this.CallID = 0; //对应呼叫ID this.CallStatus = 0; //呼叫状态 this.Type = 0; //0-语音会议 1-视频会议 this.websocket = null; this.VideoPush_CallID = 0; }; var CurrentConference = new DispatchConference; //当前会议信息 DispatchConference.CurrentConferenceInfo = function() { if (CurrentConference.ConfID) { return CurrentConference; } else { return null; } } DispatchConference.ConferenceEnter = function(ConfID, Type, ConfPwd) { if (CurrentConference.ConfID) { return -2; } var msg = new Object; msg.Msg = "VoipMakeAudioCallReq"; msg.ExtNum = ConfID; if (typeof ConfPwd == "string") { msg.ExtNum = 'c' + ConfID + '_p' + ConfPwd; } msg.CallID = VoipCallID++; var OnMakeCallSucess = function(sdp) { msg.OfferSdp = sdp; console.log("DispatchConference.ConferenceEnter " + msg.CallID + " local_sdp:"); console.log(sdp); DispatchCtrlLib.SendWorkerMsg(msg); CurrentConference.ConfID = ConfID; CurrentConference.CallID = msg.CallID; CurrentConference.CallStatus = 1; CurrentConference.Type = Type; } var OnMakeCallFail = function(error) { console.log("DispatchConference.ConferenceEnter Fail:" + error); var failmsg = new Object; failmsg.Msg = "ConferenceEnterNotify"; failmsg.ConfID = ConfID; failmsg.Status = 7; failmsg.Reason = error; DispatchCtrlLib.FuncCallback(failmsg); } return DispatchRtcLib.CreateOffer(msg.CallID, DispatchRtcLib_ConfAudio, OnMakeCallSucess, OnMakeCallFail); } DispatchConference.RecvVoipCallStatus = function(msg) { var failmsg = new Object; failmsg.Msg = "ConferenceEnterNotify"; failmsg.ConfID = CurrentConference.ConfID; failmsg.Status = msg.Status; if (msg.Status > 2) { //会议停止 DispatchConference.Stop(); } DispatchCtrlLib.FuncCallback(failmsg); } DispatchConference.ConferenceExit = function() { if (!CurrentConference.ConfID) { return -2; } var msg = new Object; msg.Msg = "VoipHangupCallReq"; msg.CallID = CurrentConference.ConfID; DispatchCtrlLib.SendWorkerMsg(msg); DispatchConference.Stop(); } DispatchConference.WsSend = function(Msg) { if (CurrentConference.websocket) { console.log("DispatchConference.WsSend: " + Msg); CurrentConference.websocket.send(Msg); } } DispatchConference.RecvWsInviteNotify = function(msg) { if (!CurrentConference.ConfID) { return -2; } //"ws://127.0.0.1:3701" if (DispatchCtrlLib.msip.length > 0) { console.log("WebSocket Url: " + msg.Url + " before"); if (window.location.protocol == 'https:') { //wss时使用nginx转发 var port = DispatchCtrlLib.LoginInfo.NginxPort + 10; msg.Url = "wss://" + DispatchCtrlLib.LoginInfo.NginxIP + ":" + port + "/ms" } else { var urlArray = msg.Url.split(":"); msg.Url = "ws://" + DispatchCtrlLib.msip + ":" + urlArray[2]; } console.log("WebSocket Url: " + msg.Url + " after"); } console.log("new WebSocket " + msg.Url); var websocket = new WebSocket(msg.Url); websocket.binaryType = 'arraybuffer'; websocket.onopen = function(evt) { console.log("websocket.onopen"); var xml = "<VOIP>" + "<Command-Name>ConferenceWsInit</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNum>"+DispatchCtrlLib.extnumber+"</ExtNum>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); } websocket.onclose = function(evt) { console.log("websocket.onclose"); CurrentConference.websocket = null; } websocket.onerror = function(evt) { console.log("websocket.onerror"); } websocket.onmessage = DispatchConference.WebsocketOnMessage; window.setInterval(function () { var xml = "<VOIP>" + "<Command-Name>ConferenceHeartbeat</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); }, 30*1000); CurrentConference.websocket = websocket; } DispatchConference.WebsocketOnMessage = function(evt) { var getNodeValue = function(xml, name) { for (var i = 0; i < xml.all.length; i++) { if (xml.all[i].tagName == name) { return xml.all[i].textContent; } } return null; } var getNodeList = function(xml, name) { var list = new Array; for (var i = 0; i < xml.all.length; i++) { if (xml.all[i].tagName == name) { var obj = { }; for (var j = 0; j < xml.all[i].childNodes.length; j++) { var tagName = xml.all[i].childNodes[j].tagName; var textContent = xml.all[i].childNodes[j].textContent; obj[tagName] = textContent; } list.push(obj); } } return list; } if (typeof(evt.data) == "string") { console.log("DispatchConference.WebsocketOnMessage: " + evt.data); var xml_end = evt.data.indexOf("</VOIP>") + "</VOIP>".length; var xml = evt.data.substring(0, xml_end); var doc = (new DOMParser()).parseFromString(xml, "text/xml"); var ConfID = getNodeValue(doc, "Command-Arg"); if (ConfID != CurrentConference.ConfID) { console.log("Command-Arg " + ConfID + " != CurrentConference.ConfID " + CurrentConference.ConfID); return; } var CommandName = getNodeValue(doc, "Command-Name"); switch(CommandName) { case "ConferenceInfo": { CurrentConference.HostExtNum = getNodeValue(doc, "HostExtNum"); CurrentConference.ScreenShare = getNodeValue(doc, "ScreenShare"); var MemList = getNodeList(doc, "Member"); for (var i = 0; i < MemList.length; i++) { var Member = { ExtNum: MemList[i].ExtNum, MemName: MemList[i].MemName, MemType: MemList[i].MemType, Status: MemList[i].MemStatus, MemMute: MemList[i].Mute, MemVideo: MemList[i].Video, PullCallID: 0 }; CurrentConference.MemberList.push(Member); } var msg = new Object; msg.Msg = "ConferenceStatusNotify"; msg.ConfID = CurrentConference.ConfID; msg.Status = -1; DispatchCtrlLib.FuncCallback(msg); if (CurrentConference.Type) { //视频会议 开始推送视频 DispatchConference.VideoPushReq(); } break; } case "ConferenceStatus": { var Status = parseInt(getNodeValue(doc, "Status")); var ExtNum = getNodeValue(doc, "ExtNum"); var MemName = getNodeValue(doc, "MemName"); var MemType = parseInt(getNodeValue(doc, "MemType")); var MemStatus = parseInt(getNodeValue(doc, "MemStatus")); var Mute = parseInt(getNodeValue(doc, "Mute")); var Video = parseInt(getNodeValue(doc, "Video")); var Index = -1; for (var i = 0 ; i < CurrentConference.MemberList.length; i++) { if (CurrentConference.MemberList[i].ExtNum == ExtNum) { Index = i; break; } } if (ExtNum && Index >= 0) { CurrentConference.MemberList[Index].ExtNum = ExtNum; CurrentConference.MemberList[Index].MemName = MemName; CurrentConference.MemberList[Index].MemType = parseInt(MemType); CurrentConference.MemberList[Index].Status = parseInt(MemStatus); CurrentConference.MemberList[Index].Mute = parseInt(Mute); CurrentConference.MemberList[Index].Video = parseInt(Video); } /* 0-成员移除 1-成员已接入 2-成员未接入 3-成员连接中 4-会议结束 5-成员禁言 6-成员禁言取消 7-成员开始讲话 8-成员结束讲话 9-主持人变更 10-视频就绪 11-视频结束 12-屏幕共享开始 13-屏幕共享结束 */ switch(Status) { case 0: { //0-成员移除 if (Index != -1) { CurrentConference.MemberList.slice(Index); } break; } case 1: { //1-成员已接入 if (Index == -1) { var Member = { ExtNum: ExtNum, MemName: MemName, MemType: MemType, Status: MemStatus, MemMute: Mute, MemVideo: Video, PullCallID: 0 }; CurrentConference.MemberList.push(Member); } break; } case 2: { //2-成员未接入 if (Index == -1) { var Member = { ExtNum: ExtNum, MemName: MemName, MemType: MemType, Status: MemStatus, MemMute: Mute, MemVideo: Video, PullCallID: 0 }; CurrentConference.MemberList.push(Member); } else { CurrentConference.MemberList[Index].Status = 2; CurrentConference.MemberList[Index].PullCallID = 0; } break; } case 3: { //3-成员连接中 if (Index == -1) { var Member = { ExtNum: ExtNum, MemName: MemName, MemType: MemType, Status: MemStatus, MemMute: Mute, MemVideo: Video, PullCallID: 0 }; CurrentConference.MemberList.push(Member); } else { CurrentConference.MemberList[Index].Status = 3; } break; } case 4: { //4-会议结束 DispatchConference.Stop(); break; } case 9: { //9-主持人变更 CurrentConference.HostExtNum = ExtNum; break; } case 12: { //12-屏幕共享开始 CurrentConference.ScreenShare = ExtNum; break; } case 13: { //13-屏幕共享结束 CurrentConference.ScreenShare = ""; break; } default: break; } var cb_msg = { Msg: "ConferenceStatusNotify", ConfID: ConfID, ExtNum: ExtNum, MemName: MemName, MemType: MemType, Status: parseInt(Status), MemMute: Mute, MemVideo: Video }; DispatchCtrlLib.FuncCallback(cb_msg); break; } case "ConferenceVideoPushRsp": { var CommandID = getNodeValue(doc, "Command-ID"); var Result = getNodeValue(doc, "Result"); if (Result == 0) { var sdp_start = evt.data.indexOf("v=0"); var sdp = evt.data.substring(sdp_start); if (evt.data.indexOf("test666") == -1) { //测试 DispatchConference.VideoPushRsp(sdp); } var xml = "<VOIP>" + "<Command-Name>ConferenceVideoPushAck</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID>"+CurrentConference.ConfID+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "</Command-Info>" + "</VOIP>\r\n"; DispatchConference.WsSend(xml); } else { console.log("ConferenceVideoPushRsp Result: " + Result); DispatchRtcLib.Stop(CurrentConference.VideoPush_CallID); CurrentConference.VideoPush_CallID = 0; } break; } case "ConferenceVideoPushEnd": { if (CurrentConference.VideoPush_CallID) { DispatchRtcLib.Stop(CurrentConference.VideoPush_CallID); CurrentConference.VideoPush_CallID = 0; } break; } case "ConferenceVideoPullRsp": { var CommandID = getNodeValue(doc, "Command-ID"); var Result = getNodeValue(doc, "Result"); var Target = getNodeValue(doc, "Target"); if (Result == 0) { var sdp_start = evt.data.indexOf("v=0"); var sdp = evt.data.substring(sdp_start); if (evt.data.indexOf("test666") == -1) { //测试 for (var i = 0; i < CurrentConference.MemberList.length; i++) { if (CurrentConference.MemberList[i].ExtNum == Target) { DispatchConference.VideoPullRsp(CurrentConference.MemberList[i].PullCallID, sdp); break; } } } var xml = "<VOIP>" + "<Command-Name>ConferenceVideoPullAck</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID>"+CurrentConference.ConfID+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "</Command-Info>" + "</VOIP>\r\n"; DispatchConference.WsSend(xml); } else { console.log("ConferenceVideoPushRsp Result: " + Result); DispatchRtcLib.Stop(CurrentConference.VideoPush_CallID); CurrentConference.VideoPush_CallID = 0; } break; } case "ConferenceVideoPullEnd": { var CommandID = getNodeValue(doc, "Command-ID"); var Target = getNodeValue(doc, "Target"); for (var i = 0; i < CurrentConference.MemberList.length; i++) { if (CurrentConference.MemberList[i].ExtNum == Target) { DispatchRtcLib.Stop(CurrentConference.MemberList[i].PullCallID); CurrentConference.MemberList[i].PullCallID = 0; break; } } break; } default: break; } } } //处理会议邀请 DispatchConference.ConferenceHandleInvite = function(Handle) { if (!CurrentConference.ConfID) { return -2; } if (Handle) { //接听 DispatchCtrlLib.VoipHandleIncomingCall(CurrentConference.CallID, 1); } else { DispatchCtrlLib.VoipHandleIncomingCall(CurrentConference.CallID, 0); CurrentConference={}; } } //会议停止 DispatchConference.Stop = function() { DispatchRtcLib.Stop(CurrentConference.CallID); if (CurrentConference.Type == 1) { //视频会议 } if (CurrentConference.websocket) { CurrentConference.websocket.close(); CurrentConference.websocket = null; } CurrentConference.CallStatus = 0; CurrentConference.ConfID = null; CurrentConference.CallID = 0; CurrentConference.MemberList = new Array; } //离开会议 DispatchConference.ConferenceExit = function() { if (!CurrentConference.ConfID) { return -2; } var msg = new Object; msg.Msg = "VoipHangupCallReq"; msg.CallID = CurrentConference.CallID; DispatchCtrlLib.SendWorkerMsg(msg); DispatchConference.Stop(); return 0; } //会议邀请 DispatchConference.ConferenceInvite = function(ExtNumList) { if (!CurrentConference.ConfID) { return -2; } var xml = "<VOIP>" + "<Command-Name>ConferenceInvite</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>"; for (var i = 0; i < ExtNumList.length; i++) { xml = xml + "<ExtNumber>"+ExtNumList[i].ExtNum+"</ExtNumber>"; } xml = xml + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); return 0; } //会议结束 DispatchConference.ConferenceEnd = function() { if (!CurrentConference.ConfID) { return -2; } if (CurrentConference.HostExtNum != DispatchCtrlLib.extnumber) { console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!"); return -3; } var xml = "<VOIP>" + "<Command-Name>ConferenceKick</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNumber>all</ExtNumber>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); DispatchConference.Stop(); return 0; } //会议踢人 DispatchConference.ConferenceKick = function(ExtNum) { if (!CurrentConference.ConfID) { return -2; } if (CurrentConference.HostExtNum != DispatchCtrlLib.extnumber) { console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!"); return -3; } var xml = "<VOIP>" + "<Command-Name>ConferenceKick</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNumber>" + ExtNum + "</ExtNumber>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); return 0; } //会议成员禁言 DispatchConference.ConferenceMute = function(ExtNum, Mute) { if (!CurrentConference.ConfID) { return -2; } if (ExtNum != DispatchCtrlLib.extnumber && CurrentConference.HostExtNum != DispatchCtrlLib.extnumber) { console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!"); return -3; } var xml = "<VOIP>" + "<Command-Name>ConferenceMute</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNumber>" + ExtNum + "</ExtNumber>" + "<Mute>" + Mute + "</Mute>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); return 0; } //会议设置音量 DispatchConference.ConferenceSetVolume = function(Volume) { if (!CurrentConference.ConfID) { return -2; } if (typeof Volume == "number") { return DispatchRtcLib.VoipSetCallVolume(CurrentConference.CallID, Volume); } else { return -1; } } //会议切换本地扬声器 DispatchConference.ConferenceSetSpeaker = function(DevLabel) { if (!CurrentConference.ConfID) { return -2; } return DispatchRtcLib.VoipSetSpeaker(CurrentConference.CallID, DevLabel); } //会议切换本地麦克风 DispatchConference.ConferenceSetMicphone = function(DevLabel) { if (!CurrentConference.ConfID) { return -2; } return DispatchRtcLib.VoipSetMicphone(CurrentConference.CallID, DevLabel); } //会议切换本地摄像头 DispatchConference.ConferenceSetCamera = function(DevLabel) { if (!CurrentConference.ConfID) { return -2; } return DispatchRtcLib.VoipSetCamera(CurrentConference.VideoPush_CallID, DevLabel) } //本地禁言 DispatchConference.ConferenceLocalMicMute = function(Mute) { if (!CurrentConference.ConfID) { return -2; } var xml = "<VOIP>" + "<Command-Name>ConferenceMute</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNumber>" + DispatchCtrlLib.extnumber + "</ExtNumber>" + "<Mute>" + Mute + "</Mute>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); return 0; } //会议视频观看 DispatchConference.ConferenceWatch = function(ExtNum, VideoLib) { if (!CurrentConference.ConfID) { return -2; } for (var i = 0; i < CurrentConference.MemberList.length; i++) { if (CurrentConference.MemberList[i].ExtNum == ExtNum) { if (CurrentConference.MemberList[i].PullCallID == 0) { var CallID = VoipCallID++; var OnMakeCallSucess = function(sdp) { CurrentConference.MemberList[i].PullCallID = CallID; var xml = "<VOIP>" + "<Command-Name>ConferenceVideoPullReq</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID>"+CurrentConference.ConfID+"-"+ExtNum+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "<Target>" + ExtNum + "</Target>" + "</Command-Info>" + "</VOIP>\r\n"; xml = xml + sdp; //CurrentConference.MemberList[i].VideoLib = VideoLib; DispatchRtcLib.DisplayVideo(CallID, VideoLib); DispatchConference.WsSend(xml); } var OnMakeCallFail = function(error) { console.log("DispatchConference.PullVideo Fail:" + error); } return DispatchRtcLib.CreateOffer(CallID, DispatchRtcLib_ConfVideoPull, OnMakeCallSucess, OnMakeCallFail); } return -1; } } return -1; } //会议视频聚焦 DispatchConference.ConferenceFocus = function(ExtNum) { if (!CurrentConference.ConfID) { return -2; } var xml = "<VOIP>" + "<Command-Name>ConferenceFocus</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNumber>" + DispatchCtrlLib.extnumber + "</ExtNumber>" + "</Command-Info>" + "</VOIP>"; DispatchConference.WsSend(xml); } //会议开始推送 DispatchConference.VideoPushReq = function() { var CallID = VoipCallID++; var OnMakeCallSucess = function(sdp) { CurrentConference.VideoPush_CallID = CallID; var xml = "<VOIP>" + "<Command-Name>ConferenceVideoPushReq</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID>"+CurrentConference.ConfID+"-"+DispatchCtrlLib.extnumber+"</Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "</Command-Info>" + "</VOIP>\r\n"; xml = xml + sdp; DispatchConference.WsSend(xml); } var OnMakeCallFail = function(error) { console.log("DispatchConference.PushLocalVideo Fail:" + error); } return DispatchRtcLib.CreateOffer(CallID, DispatchRtcLib_ConfVideoPush, OnMakeCallSucess, OnMakeCallFail); } //视频推送应答 DispatchConference.VideoPushRsp = function(answer) { DispatchRtcLib.SetRemoteSdp(CurrentConference.VideoPush_CallID, answer, DispatchRtcLib_ConfVideoPush); } //视频拉取应答 DispatchConference.VideoPullRsp = function(CallID, answer) { DispatchRtcLib.SetRemoteSdp(CallID, answer, DispatchRtcLib_ConfVideoPull); } //会议移交主持人 DispatchConference.ChangeHost = function(ExtNum) { if (CurrentConference.HostExtNum != DispatchCtrlLib.extnumber) { console.log("CurrentConference HostExtNum " + CurrentConference.HostExtNum + " is not you!"); return -2; } var xml = "<VOIP>" + "<Command-Name>ConferenceHostChange</Command-Name>" + "<Command-Arg>"+CurrentConference.ConfID+"</Command-Arg>" + "<Command-ID></Command-ID>" + "<Command-Info>" + "<ExtNum>" + DispatchCtrlLib.extnumber + "</ExtNum>" + "<Host>" + ExtNum + "</Host>" + "</Command-Info>" + "</VOIP>\r\n"; DispatchConference.WsSend(xml); return 0; } //————————————————————————————————————— DispatchPocLib —————————————————————————————————————————— function DispatchPocLib() {}; DispatchPocLib.audioContext = null; DispatchPocLib.AudioWorkletNode = null; DispatchPocLib.AudioWorkletNodeSrc = null; DispatchPocLib.AudioMicphoneStream = null; //共享内存模式相关 const Shared_BufferLen_640size = 8; //80ms DispatchPocLib.Shared_BufferLen = 640*Shared_BufferLen_640size; DispatchPocLib.Shared_Play_Left = null; DispatchPocLib.Shared_Play_Right = null; DispatchPocLib.Shared_Play_Write_Index = null; DispatchPocLib.Shared_Play_Read_Index = null; DispatchPocLib.Shared_Play_Length = null; DispatchPocLib.Shared_Capture = null; DispatchPocLib.Shared_Capture_Flag = null; DispatchPocLib.Shared_Capture_Write_Index = null; DispatchPocLib.Shared_Capture_Read_Index = null; DispatchPocLib.Shared_Capture_Length = null; //消息转发模式相关 const Message_BufferLen_640size = 2; //80ms*2 DispatchPocLib.Message_BufferLen = 640*Message_BufferLen_640size; DispatchPocLib.OnAudioWorkerMessage = function(event) { if (event && event.data && event.data.Msg) { if (event.data.Msg == "AudioWorkletInit") { if (typeof SharedArrayBuffer === "undefined") { msg = { Msg: "AudioBufferInit", Mode: "Int16Array", BufferLen: DispatchPocLib.Shared_BufferLen, Message_BufferLen: DispatchPocLib.Message_BufferLen, }; DispatchPocLib.AudioWorkletNode.port.postMessage(msg) msg.Msg = "AudioBufferInit" DispatchCtrlLib.SendWorkerMsg(msg); console.log("------ Audio Message Mode ------"); } else { msg = { Msg: "AudioBufferInit", Mode: "SharedArrayBuffer", BufferLen: DispatchPocLib.Shared_BufferLen, Message_BufferLen: DispatchPocLib.Message_BufferLen, Shared_Play_Left: DispatchPocLib.Shared_Play_Left, Shared_Play_Right: DispatchPocLib.Shared_Play_Right, Shared_Play_Write_Index: DispatchPocLib.Shared_Play_Write_Index, Shared_Play_Read_Index: DispatchPocLib.Shared_Play_Read_Index, Shared_Play_Length: DispatchPocLib.Shared_Play_Length, Shared_Capture: DispatchPocLib.Shared_Capture, Shared_Capture_Flag: DispatchPocLib.Shared_Capture_Flag, Shared_Capture_Write_Index: DispatchPocLib.Shared_Capture_Write_Index, Shared_Capture_Read_Index: DispatchPocLib.Shared_Capture_Read_Index, Shared_Capture_Length: DispatchPocLib.Shared_Capture_Length, }; DispatchPocLib.AudioWorkletNode.port.postMessage(msg) msg.Msg = "AudioBufferInit" DispatchCtrlLib.SendWorkerMsg(msg); console.log("------ Audio Shared Mode ------"); } } else if (event.data.Msg == "AudioCapBuffer") { msg = { Msg: "AudioCapBuffer", Buffer: event.data.Buffer, } if (DispatchCtrlLib.WsWorker) { DispatchCtrlLib.WsWorker.postMessage(msg); } } } } DispatchPocLib.Start = function() { if (DispatchPocLib.audioContext) { return; } if (DispatchPocLib.Shared_Play_Left == null && typeof SharedArrayBuffer != "undefined") { //640*Float32Array(80ms) = 160*Float32Array(20ms)*4 = 128*Float32Array*5 //Float32Array = 4bytes //buffer_ms = Shared_BufferLen*80ms DispatchPocLib.Shared_Play_Left = new SharedArrayBuffer(Int16Array.BYTES_PER_ELEMENT*DispatchPocLib.Shared_BufferLen); DispatchPocLib.Shared_Play_Right = new SharedArrayBuffer(Int16Array.BYTES_PER_ELEMENT*DispatchPocLib.Shared_BufferLen); DispatchPocLib.Shared_Play_Write_Index = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Audio_Play_Write_Index = new Uint32Array(DispatchPocLib.Shared_Play_Write_Index); DispatchPocLib.Shared_Play_Read_Index = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Audio_Play_Read_Index = new Uint32Array(DispatchPocLib.Shared_Play_Read_Index); DispatchPocLib.Shared_Play_Length = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Shared_Play_Length = new Uint32Array(DispatchPocLib.Shared_Play_Length); DispatchPocLib.Shared_Capture = new SharedArrayBuffer(Int16Array.BYTES_PER_ELEMENT*DispatchPocLib.Shared_BufferLen); DispatchPocLib.Shared_Capture_Flag = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Audio_Capture_Flag = new Uint32Array(DispatchPocLib.Shared_Capture_Flag); DispatchPocLib.Shared_Capture_Write_Index = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Audio_Capture_Write_Index = new Uint32Array(DispatchPocLib.Shared_Capture_Write_Index); DispatchPocLib.Shared_Capture_Read_Index = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Audio_Capture_Read_Index = new Uint32Array(DispatchPocLib.Shared_Capture_Read_Index); DispatchPocLib.Shared_Capture_Length = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT); const Shared_Capture_Length = new Uint32Array(DispatchPocLib.Shared_Capture_Length); Audio_Play_Write_Index[0] = 640; Audio_Play_Read_Index[0] = 0; Audio_Capture_Write_Index[0] = 640; Audio_Capture_Read_Index[0] = 0; Audio_Capture_Flag[0] = 0; Shared_Play_Length[0] = 0; Shared_Capture_Length[0] = 0; } var StartAudioWorklet = async function(stream) { var audioContext = new AudioContext({sampleRate: 8000}); await audioContext.audioWorklet.addModule(DispatchLibPath+'DispatchAudioWorklet.js'); let microphone = null; if (stream) { microphone = audioContext.createMediaStreamSource(stream); DispatchPocLib.AudioMicphoneStream = stream; } else { microphone = audioContext.createMediaElementSource(DispatchRtcLib.LocalMuteAudio); } DispatchPocLib.AudioWorkletNode = new AudioWorkletNode(audioContext, 'DispatchAudioWorklet') DispatchPocLib.AudioWorkletNode.port.onmessage = DispatchPocLib.OnAudioWorkerMessage; var dest = null; if (DispatchDevices.SpeakerList.length == 0) { console.log("本地无可用扬声器"); } dest = audioContext.destination; microphone.connect(DispatchPocLib.AudioWorkletNode).connect(dest) DispatchPocLib.AudioWorkletNodeSrc = microphone; console.log("Starting AudioWorkletNode Mute"); DispatchPocLib.audioContext = audioContext; } DispatchRtcLib.LocalMuteAudio.play(); if (DispatchDevices.MicphoneList.length == 0) { console.log("本地无可用麦克风"); StartAudioWorklet(); return; } var OnGetUserMediaSucess = function(stream) { StartAudioWorklet(stream); } var OnGetUserMediaFail = function(stream) { console.log("GetUserMedia Fail"); } console.log("打开麦克风" + DispatchDevices.UsingMicphone.label); navigator.getUserMedia( { audio: { deviceId: DispatchDevices.UsingMicphone.deviceId }, video: false, autoGainControl: true, echoCancellation: true, noiseSuppression: true, }, OnGetUserMediaSucess, OnGetUserMediaFail ); } DispatchPocLib.MicphoneChanged = function() { if (DispatchPocLib.audioContext == null) { return; } var audioContext = DispatchPocLib.audioContext; var StartAudioWorklet = async function(stream) { DispatchPocLib.AudioMicphoneStream.getAudioTracks()[0].stop(); DispatchPocLib.AudioWorkletNodeSrc.disconnect(); let microphone = null; if (stream) { microphone = audioContext.createMediaStreamSource(stream); DispatchPocLib.AudioMicphoneStream = stream; } else { microphone = audioContext.createMediaElementSource(DispatchRtcLib.LocalMuteAudio); } microphone.connect(DispatchPocLib.AudioWorkletNode); DispatchPocLib.AudioWorkletNodeSrc = microphone; console.log("Starting AudioWorkletNode Mute"); } if (DispatchDevices.MicphoneList.length == 0) { console.log("本地无可用麦克风"); StartAudioWorklet(); return; } var OnGetUserMediaSucess = function(stream) { StartAudioWorklet(stream); } var OnGetUserMediaFail = function(stream) { console.log("GetUserMedia Fail"); } console.log("打开麦克风" + DispatchDevices.UsingMicphone.label); navigator.getUserMedia( { audio: { deviceId: DispatchDevices.UsingMicphone.deviceId }, video: false, autoGainControl: true, echoCancellation: true, noiseSuppression: true, }, OnGetUserMediaSucess, OnGetUserMediaFail ); }