Compare commits
3 Commits
perfectnog
...
stable
Author | SHA1 | Date |
---|---|---|
|
212e0151fa | |
|
3d8fb16d70 | |
|
41b7a052ab |
|
@ -5,6 +5,7 @@ html,body{
|
||||||
body{
|
body{
|
||||||
background-color: #141414;
|
background-color: #141414;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font-family: system-ui;
|
||||||
}
|
}
|
||||||
*{
|
*{
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -73,6 +74,7 @@ video{
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.tool-container > .tools{
|
.tool-container > .tools{
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -98,3 +100,43 @@ video{
|
||||||
line-height: 2;
|
line-height: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tool-container > .namer{
|
||||||
|
flex: 0 0 auto;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 10px;
|
||||||
|
line-height: 2;
|
||||||
|
margin: 10px;
|
||||||
|
width: max-content;
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
display: flex;
|
||||||
|
& > *{
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-container > .progress{
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 250px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: white;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
& .progress-box {
|
||||||
|
height: 5px;
|
||||||
|
width: 50%;
|
||||||
|
background-color: red;
|
||||||
|
transition: width 0.05s linear;
|
||||||
|
}
|
||||||
|
}
|
162
public/index.js
162
public/index.js
|
@ -1,3 +1,4 @@
|
||||||
|
const url = new URL(window.location);
|
||||||
/**
|
/**
|
||||||
* @type {import("./MWSE/index").default}
|
* @type {import("./MWSE/index").default}
|
||||||
*/
|
*/
|
||||||
|
@ -6,6 +7,14 @@ let mwse;
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
let mySocketId;
|
let mySocketId;
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let myIPAddress;
|
||||||
|
/**
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let myNumber;
|
||||||
/**
|
/**
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
|
@ -33,19 +42,11 @@ function connect()
|
||||||
mwse = new MWSE({
|
mwse = new MWSE({
|
||||||
endpoint: "wss://ws.saqut.com"
|
endpoint: "wss://ws.saqut.com"
|
||||||
});
|
});
|
||||||
|
|
||||||
MWSE.rtc.defaultICEServers = [{
|
|
||||||
urls: "turn:20.166.82.187:3478",
|
|
||||||
username: "turnserver",
|
|
||||||
credential: "turnserver"
|
|
||||||
},{
|
|
||||||
urls: "stun:stun.l.google.com:19302"
|
|
||||||
}];
|
|
||||||
|
|
||||||
mwse.scope(beginEngine);
|
mwse.scope(beginEngine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let peername = url.searchParams.get("user");
|
||||||
|
|
||||||
let interact = false;
|
let interact = false;
|
||||||
|
|
||||||
|
@ -64,23 +65,31 @@ document.addEventListener("click",()=>{
|
||||||
*/
|
*/
|
||||||
let activeVideo;
|
let activeVideo;
|
||||||
|
|
||||||
function templateVideo(name, stream,infinitedMute)
|
function templateVideo(name, stream,infinitedMute, peername)
|
||||||
{
|
{
|
||||||
let t = new DOMParser().parseFromString(`
|
let t = new DOMParser().parseFromString(`
|
||||||
<div class="frame">
|
<div class="frame" data-name="${name}">
|
||||||
<video autoplay playsinline muted data-name="${name}">
|
<video autoplay playsinline muted>
|
||||||
|
|
||||||
</video>
|
</video>
|
||||||
<!--div class="tool-container">
|
<div class="tool-container">
|
||||||
<div class="tools">
|
<div class="namer">
|
||||||
<button>
|
<div class="avatar">
|
||||||
<i class="material-icons">home</i>
|
<i class="material-icons">account_circle</i>
|
||||||
</button>
|
</div>
|
||||||
<button>
|
<div class="name">
|
||||||
<i class="material-icons">close</i>
|
${peername ?? "User"}
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div--->
|
<div class="tools">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-box">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`,"text/html");
|
`,"text/html");
|
||||||
|
|
||||||
|
@ -93,14 +102,17 @@ function templateVideo(name, stream,infinitedMute)
|
||||||
i.muted = 1;
|
i.muted = 1;
|
||||||
i.classList.add("soundon");
|
i.classList.add("soundon");
|
||||||
}
|
}
|
||||||
if(stream) i.srcObject = stream;
|
if(stream){
|
||||||
|
i.srcObject = stream;
|
||||||
|
bindAudioLevel(stream, t.querySelector(".progress-box"));
|
||||||
|
}
|
||||||
return t.querySelector("div");
|
return t.querySelector("div");
|
||||||
}
|
}
|
||||||
function addVideoList(name, stream, peer, infinitedMute)
|
function addVideoList(name, stream, peer, infinitedMute, name)
|
||||||
{
|
{
|
||||||
if(!videoContainer.querySelector(`[name="${name}"]`))
|
if(!videoContainer.querySelector(`[name="${name}"]`))
|
||||||
{
|
{
|
||||||
let video = templateVideo(name, stream, infinitedMute);
|
let video = templateVideo(name, stream, infinitedMute, name);
|
||||||
video.dataset.user = peer.socketId;
|
video.dataset.user = peer.socketId;
|
||||||
videoContainer.appendChild(video);
|
videoContainer.appendChild(video);
|
||||||
}
|
}
|
||||||
|
@ -111,10 +123,6 @@ function removeVideoList(name)
|
||||||
if(videoContainer.querySelector(`[data-user="${name}"]`))
|
if(videoContainer.querySelector(`[data-user="${name}"]`))
|
||||||
{
|
{
|
||||||
let k = videoContainer.querySelector(`[data-user="${name}"]`);
|
let k = videoContainer.querySelector(`[data-user="${name}"]`);
|
||||||
if(k.dataset.user == activeVideo?.dataset.user || !activeVideo)
|
|
||||||
{
|
|
||||||
activePeer = null;
|
|
||||||
}
|
|
||||||
k.remove();
|
k.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,8 +132,8 @@ async function beginEngine()
|
||||||
let me = mwse.peer("me");
|
let me = mwse.peer("me");
|
||||||
me.disablePairAuth();
|
me.disablePairAuth();
|
||||||
mySocketId = me.socketId;
|
mySocketId = me.socketId;
|
||||||
|
myIPAddress = await mwse.virtualPressure.allocAPIPAddress();
|
||||||
let url = new URL(window.location);
|
myNumber = await mwse.virtualPressure.allocAPNumber();
|
||||||
roomid = url.searchParams.get("room");
|
roomid = url.searchParams.get("room");
|
||||||
|
|
||||||
if(!!roomid == 0)
|
if(!!roomid == 0)
|
||||||
|
@ -178,7 +186,8 @@ async function startOutgoingWebcam()
|
||||||
{ width: { exact: 240 } }
|
{ width: { exact: 240 } }
|
||||||
],
|
],
|
||||||
facingMode: "user"
|
facingMode: "user"
|
||||||
}
|
},
|
||||||
|
audio: true
|
||||||
});
|
});
|
||||||
outgoingStream = mediaStream;
|
outgoingStream = mediaStream;
|
||||||
}
|
}
|
||||||
|
@ -199,47 +208,62 @@ async function connectRoom()
|
||||||
notifyActionInvite: false,
|
notifyActionInvite: false,
|
||||||
notifyActionJoined: true
|
notifyActionJoined: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await mwse.peer('me').info.set("name", peername);
|
||||||
|
|
||||||
await room.createRoom();
|
await room.createRoom();
|
||||||
|
|
||||||
room.on("join", peer => IncomingPeer(peer,true));
|
room.on("join", peer => IncomingPeer(peer, true));
|
||||||
room.on("eject", peer => OutgoingPeer(peer));
|
room.on("eject", peer => OutgoingPeer(peer));
|
||||||
|
|
||||||
for (const peer of await room.fetchPeers()) {
|
for (const peer of await room.fetchPeers()) {
|
||||||
if(peer.socketId != mwse.peer('me').socketId){
|
IncomingPeer(peer)
|
||||||
IncomingPeer(peer,false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addVideoList("My Webcam",outgoingStream, mwse.peer("me"), true)
|
addVideoList("My Webcam",outgoingStream, mwse.peer("me"), true, peername);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("./MWSE/Peer").default} peer
|
* @param {import("./MWSE/Peer").default} peer
|
||||||
*/
|
*/
|
||||||
function IncomingPeer(peer,activeConnect)
|
async function IncomingPeer(peer,activeConnect)
|
||||||
{
|
{
|
||||||
|
await peer.info.fetch();
|
||||||
|
|
||||||
|
peer.createRTC({
|
||||||
|
iceCandidatePoolSize: 0
|
||||||
|
},[{
|
||||||
|
urls: "turn:20.166.82.187:3478",
|
||||||
|
username: "turnserver",
|
||||||
|
credential: "turnserver"
|
||||||
|
},{
|
||||||
|
urls: "stun:stun.l.google.com:19302"
|
||||||
|
}]);
|
||||||
|
|
||||||
if(activeConnect)
|
if(activeConnect)
|
||||||
{
|
{
|
||||||
peer.rtc.connect();
|
peer.rtc.connect();
|
||||||
}
|
}
|
||||||
peer.rtc.rtc.turboBitrate = 0;
|
peer.rtc.rtc.turboBitrate = 0;
|
||||||
peer.rtc.on('connected',() => {
|
peer.rtc.on('connected',() => {
|
||||||
console.log("Connected");
|
|
||||||
if(!activeConnect)
|
if(!activeConnect)
|
||||||
{
|
{
|
||||||
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
||||||
activePeers[peer.socketId] = peer.rtc.rtc;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
peer.rtc.on('disconnected',() => {
|
peer.rtc.on('disconnected',() => {
|
||||||
console.log("Disconnected");
|
removeVideoList(peer.socketId, peer);
|
||||||
removeVideoList(peer.streamY, peer);
|
|
||||||
delete activePeers[peer.socketId];
|
delete activePeers[peer.socketId];
|
||||||
});
|
});
|
||||||
peer.rtc.on("stream:added", ({stream,name}) => {
|
peer.rtc.on("stream:added", ({stream,name}) => {
|
||||||
peer.streamY = peer.socketId + " | " + name + " - " + stream.id;
|
addVideoList(
|
||||||
addVideoList(peer.socketId + " | " + name + " - " + stream.id,stream, peer);
|
peer.socketId,
|
||||||
|
stream,
|
||||||
|
peer,
|
||||||
|
void 0,
|
||||||
|
peer.info.get("name")
|
||||||
|
);
|
||||||
if(activeConnect)
|
if(activeConnect)
|
||||||
{
|
{
|
||||||
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
||||||
|
@ -252,12 +276,11 @@ function IncomingPeer(peer,activeConnect)
|
||||||
function OutgoingPeer(peer)
|
function OutgoingPeer(peer)
|
||||||
{
|
{
|
||||||
removeVideoList(peer.socketId, peer);
|
removeVideoList(peer.socketId, peer);
|
||||||
|
delete activePeers[peer.socketId];
|
||||||
}
|
}
|
||||||
|
|
||||||
let relative;
|
let relative;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
for(const [,peerRtc] of Object.entries(activePeers))
|
for(const [,peerRtc] of Object.entries(activePeers))
|
||||||
{
|
{
|
||||||
|
@ -275,4 +298,51 @@ setInterval(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},1000);
|
},1000);
|
||||||
|
|
||||||
|
function bindAudioLevel(stream, element, options = {}) {
|
||||||
|
const {
|
||||||
|
minPercent = 5,
|
||||||
|
maxPercent = 100,
|
||||||
|
decayRate = 0.97, // 0.9–0.99 arasında önerilir
|
||||||
|
volumeScale = 2.0 // gelen rms değerini büyütmek için (normalize daha geniş aralık)
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
const source = audioCtx.createMediaStreamSource(stream);
|
||||||
|
const analyser = audioCtx.createAnalyser();
|
||||||
|
|
||||||
|
analyser.fftSize = 512;
|
||||||
|
analyser.smoothingTimeConstant = 0.1;
|
||||||
|
|
||||||
|
const dataArray = new Uint8Array(analyser.frequencyBinCount);
|
||||||
|
source.connect(analyser);
|
||||||
|
|
||||||
|
const track = stream.getAudioTracks()[0];
|
||||||
|
const bufferLength = analyser.frequencyBinCount;
|
||||||
|
|
||||||
|
function update()
|
||||||
|
{
|
||||||
|
if (track.readyState === 'ended' || !track.enabled) {
|
||||||
|
source.disconnect();
|
||||||
|
analyser.disconnect();
|
||||||
|
audioCtx.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
analyser.getByteFrequencyData(dataArray);
|
||||||
|
|
||||||
|
// Ortalama ses seviyesi hesapla
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < bufferLength; i++) {
|
||||||
|
sum += dataArray[i];
|
||||||
|
}
|
||||||
|
const avg = sum / bufferLength;
|
||||||
|
|
||||||
|
// Ortalama değeri % olarak hesapla (0-255 arası değer)
|
||||||
|
const volumePercent = Math.min(100, (avg / 255) * 100);
|
||||||
|
element.style.width = `${volumePercent * 2}%`;
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue