Example streaming
This commit is contained in:
		
							parent
							
								
									356b5341b2
								
							
						
					
					
						commit
						70cf3a03e4
					
				| 
						 | 
				
			
			@ -30,12 +30,11 @@ app.get("/test",(request, response)=>{
 | 
			
		|||
app.get("/index.js.map",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/index.js.map"))
 | 
			
		||||
});
 | 
			
		||||
app.get("/webrtc.js",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/webrtc.js"))
 | 
			
		||||
});
 | 
			
		||||
app.get("/webrtc.adapter.js",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/webrtc.adapter.js"))
 | 
			
		||||
app.get("/stream",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./public/index.html"))
 | 
			
		||||
});
 | 
			
		||||
app.use("/stream",express.static(resolve("./public")));
 | 
			
		||||
 | 
			
		||||
app.get("/",(request, response)=>{
 | 
			
		||||
    response.sendFile(resolve("./script/index.html"))
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,93 @@
 | 
			
		|||
html,body{
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
body{
 | 
			
		||||
    background-color: #141414;
 | 
			
		||||
}
 | 
			
		||||
.root{
 | 
			
		||||
    gap: 10px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-wrap: nowrap;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
.dialoque{
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-wrap: nowrap;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
.videolist{
 | 
			
		||||
    flex: 0 0 200px;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-wrap: nowrap;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    gap: 10px;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
    padding-right: 10px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
.dialoque #primaryVideo{
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    object-fit: contain;
 | 
			
		||||
    background-color: transparent/*#282828*/;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    z-index: 2;
 | 
			
		||||
}
 | 
			
		||||
.dialoque #primaryVideoShadow{
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    object-fit: cover;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    top: 0;
 | 
			
		||||
}
 | 
			
		||||
.dialoque #secondaryVideo{
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    height: 200px;
 | 
			
		||||
    min-width: 300px;
 | 
			
		||||
    max-width: 30%;
 | 
			
		||||
    min-height: 200px;
 | 
			
		||||
    max-height: 35%;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 10px;
 | 
			
		||||
    bottom: 10px;
 | 
			
		||||
    background-color: #383838;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    z-index: 3;
 | 
			
		||||
    outline-offset: 0px;
 | 
			
		||||
    outline-style: solid;
 | 
			
		||||
    outline-width: 5px;
 | 
			
		||||
    outline-color: rgba(0,0,0,.5);
 | 
			
		||||
}
 | 
			
		||||
.videolist .frame{
 | 
			
		||||
    max-width: 200px;
 | 
			
		||||
    object-fit: cover;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    border: solid 3px transparent;
 | 
			
		||||
}
 | 
			
		||||
.videolist .frame.active{
 | 
			
		||||
    border: solid 3px green;
 | 
			
		||||
    box-shadow: 0px 0px 20px -10px green;
 | 
			
		||||
}
 | 
			
		||||
video{
 | 
			
		||||
    image-rendering: pixelated;
 | 
			
		||||
}
 | 
			
		||||
::-webkit-scrollbar {
 | 
			
		||||
    width: 6px;
 | 
			
		||||
}
 | 
			
		||||
::-webkit-scrollbar-thumb {
 | 
			
		||||
    background: white;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
}
 | 
			
		||||
::-webkit-scrollbar-track {
 | 
			
		||||
    background-color: #ffffff1f;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
<html lang="tr">
 | 
			
		||||
<head>
 | 
			
		||||
    <base href="/stream/">
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>saQüt Video Streaming</title>
 | 
			
		||||
    <script src="https://ws.saqut.com/script"></script>
 | 
			
		||||
    <link rel="stylesheet" href="./index.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div class="root">
 | 
			
		||||
    <div class="dialoque">
 | 
			
		||||
      <video
 | 
			
		||||
        id="primaryVideo"
 | 
			
		||||
        muted
 | 
			
		||||
        playsinline
 | 
			
		||||
        autoplay
 | 
			
		||||
      >
 | 
			
		||||
      </video>
 | 
			
		||||
      <video
 | 
			
		||||
        id="primaryVideoShadow"
 | 
			
		||||
        muted
 | 
			
		||||
        playsinline
 | 
			
		||||
        autoplay
 | 
			
		||||
      >
 | 
			
		||||
      </video>
 | 
			
		||||
      <video
 | 
			
		||||
        id="secondaryVideo"
 | 
			
		||||
        muted
 | 
			
		||||
        playsinline
 | 
			
		||||
        autoplay
 | 
			
		||||
      >
 | 
			
		||||
      </video>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="videolist">
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <script src="index.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,259 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @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");
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
            setPrimaryVideo(stream, true);
 | 
			
		||||
        };
 | 
			
		||||
        videoContainer.appendChild(video);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeVideoList(name)
 | 
			
		||||
{
 | 
			
		||||
    if(videoContainer.querySelector(`[data-name="${name}"]`))
 | 
			
		||||
    {
 | 
			
		||||
        let k = videoContainer.querySelector(`[data-name="${name}"]`);
 | 
			
		||||
        let user = k.dataset.user;
 | 
			
		||||
        if(k.dataset.user == activeVideo.dataset.user)
 | 
			
		||||
        {
 | 
			
		||||
            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(!!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, audio: true});
 | 
			
		||||
    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({
 | 
			
		||||
        iceServers:[{
 | 
			
		||||
            urls: "turn:161.97.136.175:3478",
 | 
			
		||||
            username: "argist-eu-east-25",
 | 
			
		||||
            credential: "ee7df17eed35f4cf5a207777f3c0cd7d3b1901a5de7aff52ea55742289d7fee2"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun1.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun2.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun3.l.google.com:19302"
 | 
			
		||||
        },{
 | 
			
		||||
            urls: "stun:stun4.l.google.com:19302"
 | 
			
		||||
        }],
 | 
			
		||||
        bundlePolicy: "max-compat",
 | 
			
		||||
        iceCandidatePoolSize: 0,
 | 
			
		||||
        iceTransportPolicy: "all"
 | 
			
		||||
    });
 | 
			
		||||
    if(activeConnect)
 | 
			
		||||
    {
 | 
			
		||||
        peer.rtc.connect();
 | 
			
		||||
    }
 | 
			
		||||
    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)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue