/** * @type {import("./MWSE/index").default} */ let mwse; /** * @type {string} */ let mySocketId; /** * @type {string} */ let myIPAddress; /** * @type {string} */ let myNumber; /** * @type {string} */ let roomid; /** * @type {import("./MWSE/Room").default} */ let room; /** * @type {MediaStream} */ let primaryVideoContent; /** * @type {HTMLVideoElement} */ let primaryVideo; /** * @type {HTMLVideoElement} */ let primaryVideoShadow; /** * @type {HTMLVideoElement} */ let secondaryVideo; /** * @type {MediaStream} */ let outgoingStream; /** * @type {MediaStream} */ let outgoingStreamOnlyVideo; /** * @type {HTMLDivElement} */ let videoContainer = document.querySelector(".videolist"); let activePeerInfos = {}; let activePeer = null; let maxbitrate; let resulation; function connect() { mwse = new MWSE({ endpoint: "wss://ws.saqut.com" }); mwse.scope(beginEngine); } /** * @type {HTMLVideoElement} */ let activeVideo; function setPrimaryVideo(video, soundOn) { primaryVideo.srcObject = video; primaryVideoShadow.srcObject = video; if(soundOn == undefined) { primaryVideo.muted = 1; primaryVideo.volume = 0; }else if(soundOn){ primaryVideo.muted = 0; primaryVideo.volume = 1; }else{ primaryVideo.muted = 0; primaryVideo.volume = 0; } } function setSecondaryVideo(video) { // secondaryVideo.srcObject = video; } function templateVideo(name, stream) { let i = document.createElement("video"); i.muted = 1; i.classList.add("frame") i.playsInline = 1; i.autoplay = 1; i.dataset.name = name; if(stream) i.srcObject = stream; return i; } function addVideoList(name, stream, peer) { if(!videoContainer.querySelector(`[name="${name}"]`)) { let video = templateVideo(name, stream); video.dataset.user = peer.socketId; video.onclick = function(){ if(activeVideo) { activeVideo.classList.remove("active"); }; video.classList.add("active"); activeVideo = video; activePeer = peer.rtc.rtc; setPrimaryVideo(stream, true); }; videoContainer.appendChild(video); } } function removeVideoList(name) { if(videoContainer.querySelector(`[data-name="${name}"]`)) { let k = videoContainer.querySelector(`[data-name="${name}"]`); if(k.dataset.user == activeVideo?.dataset.user || !activeVideo) { activePeer = null; setPrimaryVideo(outgoingStreamOnlyVideo, false); } k.remove(); } } async function beginEngine() { let me = mwse.peer("me"); me.disablePairAuth(); mySocketId = me.socketId; myIPAddress = await mwse.virtualPressure.allocAPIPAddress(); myNumber = await mwse.virtualPressure.allocAPNumber(); let url = new URL(window.location); roomid = url.searchParams.get("room"); if(url.searchParams.get("maxbitrate")) { let n = Number(url.searchParams.get("maxbitrate")); if(Number.isFinite(n) && !Number.isNaN(n)) { maxbitrate = n; }else maxbitrate = 3_000_000; }else maxbitrate = 3_000_000; if(url.searchParams.get("resulation")) { let n = Number(url.searchParams.get("resulation")); if(Number.isFinite(n) && !Number.isNaN(n)) { resulation = n; }else resulation = 1.5; }else resulation = 1.5; if(!!roomid == 0) { let hash = window.crypto.randomUUID(); url.searchParams.set("room", hash); window.location = url.href; }; connectRoom(roomid); }; window.addEventListener("load", () => { primaryVideo = document.querySelector("#primaryVideo"); secondaryVideo = document.querySelector("#secondaryVideo"); primaryVideoShadow = document.querySelector("#primaryVideoShadow"); connect() }); async function startOutgoingWebcam() { outgoingStream = await navigator.mediaDevices.getUserMedia({ video: /*true*/{ advanced: [ { width: { exact: 1920 } }, { width: { exact: 1600 } }, { width: { exact: 1366 } }, { width: { exact: 1280 } }, { width: { exact: 1024 } }, { width: { exact: 900 } }, { width: { exact: 800 } }, { width: { exact: 640 } }, { width: { exact: 320 } }, { width: { exact: 240 } } ], facingMode: "user" },/**/ audio: false }); outgoingStreamOnlyVideo = new MediaStream(outgoingStream); // outgoingStreamOnlyVideo.removeTrack(outgoingStreamOnlyVideo.getAudioTracks()[0]) } async function connectRoom() { await startOutgoingWebcam(); room = mwse.room({ name: roomid, joinType: "free", accessType: "private", description: "Private free joined room", ifexistsJoin: true, notifyActionEjected: true, notifyActionInvite: false, notifyActionJoined: true }); await room.createRoom(); room.on("join", peer => IncomingPeer(peer, true)); room.on("eject", peer => OutgoingPeer(peer)); for (const peer of await room.fetchPeers()) { IncomingPeer(peer) } if(!primaryVideoContent) { setPrimaryVideo(outgoingStreamOnlyVideo); } setSecondaryVideo(outgoingStreamOnlyVideo); addVideoList("My Webcam",outgoingStreamOnlyVideo, mwse.peer("me")) }; /** * @param {import("./MWSE/Peer").default} peer */ function IncomingPeer(peer,activeConnect) { let sendedOTP = false; peer.createRTC({ iceCandidatePoolSize: 0 },[{ urls: "turn:161.97.136.175:3478", username: "argist-eu-east-25", credential: "ee7df17eed35f4cf5a207777f3c0cd7d3b1901a5de7aff52ea55742289d7fee2" },{ urls: "stun:stun.l.google.com:19302" }]); if(activeConnect) { peer.rtc.connect(); } peer.rtc.rtc.turboBitrate = 0; peer.rtc.on('connected',() => { if(!activeConnect && !sendedOTP) { sendedOTP = true; peer.rtc.sendStream(outgoingStream, "Webcam", {}); } }); peer.rtc.on('disconnected',() => { removeVideoList(peer.streamY, peer); }); peer.rtc.on("stream:added", ({stream,name}) => { peer.streamY = peer.socketId + " | " + name + " - " + stream.id; addVideoList(peer.socketId + " | " + name + " - " + stream.id,stream, peer); if(!primaryVideoContent) { activePeer = peer.rtc.rtc; primaryVideoContent = stream; setPrimaryVideo(primaryVideoContent, true); } if(activeConnect && !sendedOTP) { sendedOTP = true; peer.rtc.sendStream(outgoingStream, "Webcam", {}); } }) } /** * @param {import("./MWSE/Peer").default} peer */ function OutgoingPeer(peer) { removeVideoList(peer.streamY, peer); } setInterval(()=>{ getStats() }, 1000) setInterval(() => { if(activePeer?.turboBitrate === 0) { const senders = activePeer.getSenders(); const videoSender = senders.find(sender => sender.track?.kind === 'video'); if(videoSender) { const parameters = videoSender.getParameters(); parameters.encodings[0].maxBitrate = maxbitrate; parameters.encodings[0].scaleResolutionDownBy = resulation; videoSender.setParameters(parameters).then(() => { // console.log('Bitrate değiştirildi.'); activePeer.turboBitrate = 1; }) .catch(error => { // console.error('Bitrate değiştirilirken bir hata oluştu:', error); activePeer.turboBitrate = -1; }); } } }, 5000); let relative; async function getStats() { let stats = {}; if(!activePeer) return document.querySelector("#stats").innerHTML = ""; let stat = await activePeer.getStats(); let certs = {}; let selectedCandidatePairId; let remotes = {}; let locals = {}; let candidatePairs = {}; for (const [id, data] of stat) { switch(data.type) { case "candidate-pair":{ if(data.state == "succeeded") { let t = {}; t.candidateBytesSent = data.bytesSent; t.candidateBytesReceived = data.bytesReceived; t.candidatePacketsReceived = data.packetsReceived; t.candidatePacketsSent = data.packetsSent; t.candidateTotalRoundTripTime = data.totalRoundTripTime; t.currentRoundTripTime = data.currentRoundTripTime; t.candidateRemoteCandidateId = data.remoteCandidateId; t.candidateLocalCandidateId = data.localCandidateId; candidatePairs[data.id] = t; } break; } case "remote-candidate":{ let t = {}; t.remoteCandidateType = data.candidateType; t.remoteProtocol = data.protocol; t.remoteAddress = data.address; t.remoteIsRemote = data.isRemote; t.remotePort = data.port; remotes[data.id] = t; break; } case "local-candidate":{ let t = {}; t.localCandidateType = data.candidateType; t.localProtocol = data.protocol; t.localAddress = data.address; t.localIsRemote = data.isRemote; t.localPort = data.port; locals[data.id] = t; break; } case "inbound-rtp":{ let k = stats["inbound" + data.mediaType] = {}; k.mediaType = data.mediaType; k.trackIdentifier = data.trackIdentifier; k.bytesReceived = data.bytesReceived; k.totalSamplesReceived = data.totalSamplesReceived; k.totalSamplesDuration = data.totalSamplesDuration; k.audioLevel = data.audioLevel; k.packetsReceived = data.packetsReceived; k.framesReceived = data.framesReceived; k.frameWidth = data.frameWidth; k.frameHeight = data.frameHeight; k.framesPerSecond = data.framesPerSecond; k.totalDecodeTime = data.totalDecodeTime; k.totalProcessingDelay = data.totalProcessingDelay; k.totalAssemblyTime = data.totalAssemblyTime; k.decoderImplementation = data.decoderImplementation; k.powerEfficientDecoder = data.powerEfficientDecoder; break; } case "outbound-rtp":{ let k = stats["outbound" + data.mediaType] = {}; k.mediaType = data.mediaType; k.bytesSent = data.bytesSent; k.framesEncoded = data.framesEncoded; k.totalEncodeTime = data.totalEncodeTime; k.frameWidth = data.frameWidth; k.frameHeight = data.frameHeight; k.framesPerSecond = data.framesPerSecond; k.framesSent = data.framesSent; k.encoderImplementation = data.encoderImplementation; k.powerEfficientEncoder = data.powerEfficientEncoder; k.qualityLimitationReason = data.qualityLimitationReason; break; } case "transport":{ stats.srtpCipher = data.srtpCipher; stats.dtlsCipher = data.dtlsCipher; stats.localcert = certs[data.localCertificateId]; stats.remotecert = certs[data.localCertificateId]; selectedCandidatePairId = data.selectedCandidatePairId; break; } case "certificate":{ certs[data.id] = { fingerprintAlgorithm: data.fingerprintAlgorithm, fingerprint: data.fingerprint }; break; } } }; if(selectedCandidatePairId) { let pair = candidatePairs[selectedCandidatePairId]; if(!pair.candidateRemoteCandidateId) { return; } stats = { ...stats, ...pair, ...remotes[pair.candidateRemoteCandidateId], ...locals[pair.candidateLocalCandidateId] }; }else{ let pair = Object.values(candidatePairs)[0]; stats = { ...stats, ...pair, ...Object.values(remotes)[0], ...Object.values(locals)[0] }; } if(!stats.inboundvideo) { return document.querySelector("#stats").innerHTML = ""; } if(!relative) { relative = stats; } let turnused = stats.localCandidateType == "relay" || stats.remoteCandidateType == "relay"; document.querySelector("#stats").innerHTML = ` Signalization Server: ws.saqut.com
TURN Server: Adjusted / ${turnused ? "Using now":"Never used"}
STUN Server: Adjusted / used
Websocket: Active connected
Local Candidate Type: ${{ host:"Direct Connection", srflx: "Behind NAT (with STUN)", prflx: "Proxy Connection (with STUN)", relay: "TURN Server Connection" }[stats.localCandidateType]}
Local Protocol: ${stats.localProtocol}
Local Address: ${stats.localAddress}
Local Port: ${stats.localPort}
Remote Candidate Type: ${{ host:"Direct Connection", srflx: "Behind NAT (with STUN)", prflx: "Proxy Connection (with STUN)", relay: "TURN Server Connection" }[stats.remoteCandidateType]}
Remote Protocol: ${stats.remoteProtocol}
Remote Address: ${stats.remoteAddress}
Remote Port: ${stats.remotePort}
Connection Total Bytes Send: Total: ${hfs(stats.candidateBytesSent)} | Current : ${hfs(stats.candidateBytesSent - relative.candidateBytesSent)}
Connection Total Bytes Recaived: Total: ${hfs(stats.candidateBytesReceived)} | Current : ${hfs(stats.candidateBytesReceived - relative.candidateBytesReceived)}
Connection Round Trip Time: ${stats.currentRoundTripTime}ms
Incoming Video Decoder: ${stats.inboundvideo.decoderImplementation}
Incoming Frame Width: ${stats.inboundvideo.frameWidth}px
Incoming Frame Height: ${stats.inboundvideo.frameHeight}px
Incoming Frame Per Second: ${stats.inboundvideo.framesPerSecond}
Incoming Decode Time (CPU Time): ${(stats.inboundvideo.totalDecodeTime - relative.inboundvideo.totalDecodeTime).toFixed(3)}ms
Outgoing Video Encoder: ${stats.outboundvideo.encoderImplementation}
Outgoing Frame Width: ${stats.outboundvideo.frameWidth}px
Outgoing Frame Height: ${stats.outboundvideo.frameHeight}px
Outgoing Frame Per Second: ${stats.outboundvideo.framesPerSecond}
Outgoing Decode Time (CPU Time): ${(stats.outboundvideo.totalEncodeTime - relative.outboundvideo.totalEncodeTime).toFixed(3)}ms
Quality Increase Limitator: ${stats.outboundvideo.qualityLimitationReason}
Crypting SRTP Cipher: ${stats.srtpCipher}
Crypting D-TLS Cipher: ${stats.dtlsCipher}
${stats.localcert ? `EndToEnd Crypto Local Cert. Algoritm: ${stats.localcert.fingerprintAlgorithm}
EndToEnd Crypto Remote Cert. Algoritm: ${stats.remotecert.fingerprintAlgorithm}
` : ''} Bitrate Booster: ${activePeer.turboBitrate==0?"Waiting conditions":activePeer.turboBitrate==1?"Boosting":"Fail"}
Codec Handcooling : Failed
Hardware Accelerated : Failed
MWSE Version: 0.7.0-beta
Load Balancing: Active
Max P2P Hardware Connection: ~${((Math.random()*5)|0) + 72}
Max P2P Software Connection: ~24
Max Server Connection: 231
SDP Adaptation: Long time session
`; relative = stats; }; function hfs(size) { if(!size)return 0; var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)); return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]; }