medooze/sfu

<dialog class = "ready-dialog mdl-dialog"> html code works only on chrome browser

Opened this issue · 0 comments

The session cannot be attended because other browsers do not support the dialog feature.

I replaced this with $('#modalinfo').modal

www/index.html

<html>
	<head>
	<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
	content="width=device-width, initial-scale=1, shrink-to-fit=no">
	<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
	<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
	<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
	<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
	<link href="css/getmdl-select.min.css" rel="stylesheet" type="text/css"/>
	<link href="css/flag-icon.min.css" rel="stylesheet" type="text/css"/>
	<link href="css/bootstrap.min.css" rel="stylesheet">
	<script src="js/jquery-2.2.4.min.js"></script>
	<script src="js/common_scripts.js"></script>
	<script src="js/jquery-ui-1.8.22.min.js"></script>

	<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
	<script src="js/transaction-manager.js" type="text/javascript"></script>
	<script src="js/getmdl-select.js" type="text/javascript"></script>
	<script src="js/soundmeter.js" type="text/javascript"></script>
	<script src="js/sfu.js" type="text/javascript"></script>
	<style>
		body {
			background: #e2e1e0;
			text-align: center;
			margin: 0px;
			padding: 0px;
		}

		.scroll {
			overflow: auto;
			overflow: overlay;
			z-index: 9;
			scroll-snap-type: proximity;
		}
		.scroll::-webkit-scrollbar {
			position: absolute;
			width: 12px;
			height: 12px;
		}
		.scroll::-webkit-scrollbar-button {
			width: 0px;
			height: 0px;
		}
		.scroll::-webkit-scrollbar-corner {
			background-color: transparent;
		}
		.scroll::-webkit-scrollbar-track {
			border: 4px solid transparent;
			border-radius: 50px;
			background-clip: content-box;
			background-color: transparent;
		}
		.scroll::-webkit-scrollbar-thumb {
			border: 4px solid transparent;
			border-radius: 50px;
			background-clip: content-box;
			background-color: rgba(0, 0, 0, 0.2);
			min-height: 40px;
		}
		.scroll.dark::-webkit-scrollbar-thumb {
			background-color: rgba(255, 255, 255, 0.4);
			min-height: 40px;
		}
		.scroll:hover::-webkit-scrollbar-track {
			background-color: rgba(0, 0, 0, 0.1);
		}
		.scroll.dark:hover::-webkit-scrollbar-track {
			background-color: rgba(255, 255, 255, 0.2);
		}
		.scroll:hover::-webkit-scrollbar-thumb {
			background-color: rgba(0, 0, 0, 0.3);
		}
		.scroll.dark:hover::-webkit-scrollbar-thumb {
			background-color: rgba(255, 255, 255, 0.6);
		}
		.scroll:hover::-webkit-scrollbar-track:hover {
			background-color: rgba(0, 0, 0, 0.2);
		}
		.scroll.dark:hover::-webkit-scrollbar-track:hover {
			background-color: rgba(255, 255, 255, 0.3);
		}
		.scroll:hover::-webkit-scrollbar-thumb:hover {
			background-color: rgba(0, 0, 0, 0.5);
		}
		.scroll.dark:hover::-webkit-scrollbar-thumb:hover {
			background-color: rgba(255, 255, 255, 0.8);
		}
		.scroll:hover::-webkit-scrollbar-thumb:active {
			background-color: rgba(0, 0, 0, 0.8);
		}
		.scroll.dark:hover::-webkit-scrollbar-thumb:active {
			background-color: rgba(255, 255, 255, 0.8);
		}
		.scroll.scroll-big::-webkit-scrollbar {
			width: 24px;
			height: 24px;
		}
		.scroll.scroll-big::-webkit-scrollbar-button {
			width: 0px;
			height: 0px;
		}
		.scroll.scroll-big::-webkit-scrollbar-corner {
			background-color: transparent;
		}
		.scroll.scroll-big::-webkit-scrollbar-track {
			border: 9px solid transparent;
			border-radius: 50px;
			background-clip: content-box;
			background-color: transparent;
		}
		.scroll.scroll-big::-webkit-scrollbar-thumb {
			border: 9px solid transparent;
			border-radius: 50px;
			background-clip: content-box;
			background-color: rgba(0, 0, 0, 0.2);
			min-height: 40px;
		}
		.scroll.scroll-big.dark::-webkit-scrollbar-thumb {
			background-color: rgba(255, 255, 255, 0.4);
			min-height: 40px;
		}
		.scroll.scroll-big:hover::-webkit-scrollbar-track {
			background-color: rgba(0, 0, 0, 0.1);
		}
		.scroll.scroll-big.dark:hover::-webkit-scrollbar-track {
			background-color: rgba(255, 255, 255, 0.2);
		}
		.scroll.scroll-big:hover::-webkit-scrollbar-thumb {
			background-color: rgba(0, 0, 0, 0.3);
		}
		.scroll.scroll-big.dark:hover::-webkit-scrollbar-thumb {
			background-color: rgba(255, 255, 255, 0.6);
		}
		.scroll.scroll-big:hover::-webkit-scrollbar-track:hover {
			background-color: rgba(0, 0, 0, 0.2);
		}
		.scroll.scroll-big.dark:hover::-webkit-scrollbar-track:hover {
			background-color: rgba(255, 255, 255, 0.3);
		}
		.scroll.scroll-big:hover::-webkit-scrollbar-thumb:hover {
			background-color: rgba(0, 0, 0, 0.5);
		}
		.scroll.scroll-big.dark:hover::-webkit-scrollbar-thumb:hover {
			background-color: rgba(255, 255, 255, 0.8);
		}
		.scroll.scroll-big:hover::-webkit-scrollbar-thumb:active {
			background-color: rgba(0, 0, 0, 0.8);
		}
		.scroll.scroll-big.dark:hover::-webkit-scrollbar-thumb:active {
			background-color: rgba(255, 255, 255, 0.8);
		}

		video {
			object-fit: cover;
			float: left;
			background: #fff;
			border-radius: 2px;
			display: inline-block;
			margin: 1rem;
			position: relative;
			height: 150px;
			box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
			transition: all 0.5s cubic-bezier(.25,.8,.25,1);
			padding:1px;
			bottom: 0px;
			max-width: 200px;
			opacity: 1;
		}
		
		.disabled {
			height: 0px;
			opacity: 0;
		}
		
		video:hover {
			box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
			bottom: 125px;
			height: 300px;
			max-width: 400px;
		}
		video::before {
			content: "hola";
		}
		video::after {
			content: "hola";
		}

		#outher-container {
			margin: 0px;
			padding: 0px;
			width: 100%;
			position: fixed;
			bottom: 5px;
			height: 100%;
			display: flex;
			overflow-x: auto;
			overflow-y: hidden;
		}
		#container {
			position: absolute;
			bottom: 60px;
			margin: 0px;
			padding: 0px;
			height: 150px;
			display: flex;
		}
		
		.ready-dialog 
		{
			width: 640px;
			text-align: left;

		}
		.ready-dialog p 
		{
			color: black;
			font-size: 12pt;

		}
		.ready-dialog code
		{
			font-size: 12pt;

		}
			
		/***********************************/
		.bar-audio{
			display: block;
			margin: 0 auto;
			padding-left: 40px;
			padding-bottom: 5px;
		}
		bar-audio p.text-1{
			font-size: 8px;
			margin: 5px;
		}

		bar-audio p.text-1 span{
			font-size: 8px;
			padding-left: 155px;
		}

		.bg-bar-outher {
			width: 300px;
			text-align: left;
		}
		.bg-bar{
			margin: 0 auto;
			display:block;
			height:20px;
			width:202px;
			background: url("images/bar_audio.png");


		}
		.bar-inside{
			display:block;
			height:20px;
			width:0%;
			background: url("images/bar_audio_on.png");
		}
		#random {
			top: -8px;
			left: 15px;
		}
		
		.room-header {
			position: relative;
			top: 10px;
			margin: 0 auto;
		}
		
		.room-info  {
			background: #111;
			border-radius: 2px;
			width: 200px;
			height: 40px;
			box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
			transition: all 0.5s cubic-bezier(.25,.8,.25,1);
			opacity: 0;
			padding-top: 15px;
			font-size: 14pt;
			color: white;
		}
		a {
			text-decoration: none !important;
		}
		
	</style>
	</head>
	<body>
	<div id="outher-container" class="scroll">
		<div class="room-header">
			<div class="room-info"> Room: <a href="" id="room-id"></a></div>
		</div>
		<div id="container">
			
				
		
		</div>
		
	</div>
	
	
	<div aria-hidden="true" aria-labelledby="myModalLabel" role="dialog"
		tabindex="-1" id="modalinfo" class="modal fade"
		style="z-index: 1200; top: 95px;">
		<div class="modal-dialog">
			<div class="modal-content">
<form action="#" id="ready-form">
				<div class="modal-header">
					<h4 class="modal-title">SFU Test154 Konfium</h4>
				</div>
				<div class="modal-body">
					
						<div class="alert alert-warning">Lütfen Tüm alanları Doldurunuz!</div>
					
					
					
					
						<div class="form-group">
							<label  for="roomId">Room Id</label>
							<input class="form-control" type="text" id="roomId" name="roomId" required> 
							
    					</div>
    					
    					<div class="form-group">
    						<label for="name">Adınız</label>
							<input class="form-control" type="text" id="name" name="name"  required> 
							
    					</div>
    					
					   <div class="form-group">
					   		<label for="name">Anahtar Şifreniz</label>
							<input class="form-control" type="text"  id="key" name="key" required> 
							
    				</div>
						
						
						
						<div class="mdl-selectfield">
							<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label getmdl-select getmdl-select__fix-height">
								<input class="mdl-textfield__input" type="text" id="audio_devices" readonly tabIndex="-1" value="Default">
								<label for="audio_devices">
									<i class="mdl-icon-toggle__label material-icons">keyboard_arrow_down</i>
								</label>
								<label for="audio_devices" class="mdl-textfield__label">Audio Device</label>
								<ul id="audio_devices_menu" for="audio_devices" class="mdl-menu mdl-menu--bottom-left mdl-js-menu" style="width:300px">
								</ul>
							</div>
						</div>
						
						<div class="bg-bar-outher">
							<div class="bg-bar">
								<div class="voometer bar-inside" style="width: 5%;"></div>
							</div>
						</div>
					
				</div>
				<div class="modal-footer">
					
					<button type="button" class="btn btn-default" id="random">Rastgele Ata</button>
					<button type="submit" class="btn btn-danger"  id="connect" >Katıl</button>
				</div>
</form>
			</div>
		</div>
	</div>
	
	<script>
  $( function() {
	  $('#modalinfo').modal({backdrop: 'static', keyboard: false});
	  $('#modalinfo').modal('show');
  } );
  </script>
	</body>
		
</html>	

Change
www/js/sfu.js


let participants;
let audioDeviceId;
let videoResolution = true;

//Get our url
const href = new URL(window.location.href);
//Get id
const roomId = href.searchParams.get("roomId");
//Get name
const name = href.searchParams.get("name");
//Get key
const key = href.searchParams.get("key");
//Get video
const nopublish = href.searchParams.has("nopublish");
//Get ws url from navigaro url
const url = "wss://"+href.host;
//Check support for insertabe media streams
const supportsInsertableStreams = !!RTCRtpSender.prototype.createEncodedVideoStreams;

if (href.searchParams.has ("video"))
	switch (href.searchParams.get ("video").toLowerCase ())
	{
		case "1080p":
			videoResolution = {
				width: {min: 1920, max: 1920},
				height: {min: 1080, max: 1080},
			};
			break;
		case "720p":
			videoResolution = {
				width: {min: 1280, max: 1280},
				height: {min: 720, max: 720},
			};
			break;
		case "576p":
			videoResolution = {
				width: {min: 720, max: 720},
				height: {min: 576, max: 576},
			};
			break;
		case "480p":
			videoResolution = {
				width: {min: 640, max: 640},
				height: {min: 480, max: 480},
			};
			break;
		case "320p":
			videoResolution = {
				width: {min: 320, max: 320},
				height: {min: 240, max: 240},
			};
			break;
		case "no":
			videoResolution = false;
			break;
	}


function addRemoteTrack(event)
{
	console.log(event);
	
	const track	= event.track;
	const stream	= event.streams[0];
	
	if (!stream)
		return console.log("addRemoteTrack() no stream")
	
	//Check if video is already present
	let video = container.querySelector("video[id='"+stream.id+"']");
	
	//Check if already present
	if (video)
		//Ignore
		return console.log("addRemoteTrack() video already present for "+stream.id);
	
	//Listen for end event
	track.onended=(event)=>{
		console.log(event);
	
		//Check if video is already present
		let video = container.querySelector("video[id='"+stream.id+"']");

		//Check if already present
		if (!video)
			//Ignore
			return console.log("removeRemoteTrack() video not present for "+stream.id);

		container.removeChild(video);
	}
	
	//Create new video element
	video = document.createElement("video");
	//Set same id
	video.id = stream.id;
	//Set src stream
	video.srcObject = stream;
	//Set other properties
	video.autoplay = true;
	video.play();
	//Append it
	container.appendChild(video);
}
	
function addLocalVideoForStream(stream,muted)
{
	//Create new video element
	const video = document.createElement("video");
	//Set same id
	video.id = stream.id;
	//Set src stream
	video.srcObject = stream;
	//Set other properties
	video.autoplay = true;
	video.muted = muted;
	video.play();
	//Append it
	container.appendChild(video);
}

/*
 Get some key material to use as input to the deriveKey method.
 The key material is a secret key supplied by the user.
 */
async function getRoomKey(roomId,secret) 
{
	const enc = new TextEncoder();
	const keyMaterial = await window.crypto.subtle.importKey(
		"raw",
		enc.encode(secret),
		{name: "PBKDF2"},
		false,
		["deriveBits", "deriveKey"]
	);
	return window.crypto.subtle.deriveKey(
		{
			name: "PBKDF2",
			salt: enc.encode(roomId),
			iterations: 100000,
			hash: "SHA-256"
		},
		keyMaterial,
		{"name": "AES-GCM", "length": 256},
		true,
		["encrypt", "decrypt"]
	);
}

  /*
   * 
   */
async function connect(url,roomId,name,secret) 
{
	let counter = 0;
	const roomKey = await getRoomKey(roomId,secret);
	async function encrypt(chunk, controller) {
		try {
			//Get iv
			const iv = new ArrayBuffer(4);
			//Create view, inc counter and set it
			new DataView(iv).setUint32(0,counter <65535 ? counter++ : counter=0);
			//Encrypt
			const ciphertext = await window.crypto.subtle.encrypt(
				{
					name: "AES-GCM",
					iv: iv
				},
				roomKey,
				chunk.data
			);
			//Set chunk data
			chunk.data = new ArrayBuffer(ciphertext.byteLength + 4);
			//Crate new encoded data and allocate size for iv
			const data = new Uint8Array(chunk.data);
			//Copy iv
			data.set(new Uint8Array(iv),0);
			//Copy cipher
			data.set(new Uint8Array(ciphertext),4);
			//Write
			controller.enqueue(chunk);
		} catch(e) {
		}
	}

	async function decrypt(chunk, controller) {
		try {
			//decrypt
			chunk.data =  await window.crypto.subtle.decrypt(
				{
				  name: "AES-GCM",
				  iv: new Uint8Array(chunk.data,0,4)
				},
				roomKey,
				new Uint8Array(chunk.data,4,chunk.data.byteLength - 4)
			);
			//Write
			controller.enqueue(chunk);
		} catch(e) {
		}
	}
	
	const isCryptoEnabled = !!secret && supportsInsertableStreams;

	var pc = new RTCPeerConnection({
		bundlePolicy				: "max-bundle",
		rtcpMuxPolicy				: "require",
		forceEncodedVideoInsertableStreams	: isCryptoEnabled
	});
	
	//Create room url
	const roomUrl = url +"?id="+roomId;
		
	var ws = new WebSocket(roomUrl);
	var tm = new TransactionManager(ws);
	
	pc.ontrack = (event) => {
		//If encrypting/decrypting
		if (isCryptoEnabled) 
		{
			//Create transfor strem fro decrypting
			const transform = new TransformStream({
				start() {},
				flush() {},
				transform: decrypt
			});
			//Get the receiver streams for track
			let receiverStreams = event.receiver.createEncodedVideoStreams();
			//Decrytp
			receiverStreams.readableStream
				.pipeThrough(transform)
				.pipeTo(receiverStreams.writableStream);
		}
		addRemoteTrack(event);
	};
	
	ws.onopen = async function()
	{
	        console.log("ws:opened");
		
		try
		{
			if (!nopublish)
			{
				const stream = await navigator.mediaDevices.getUserMedia({
					audio: {
						deviceId: audioDeviceId
					},
					video: videoResolution
				});

				console.debug("md::getUserMedia sucess",stream);

				//Play it
				addLocalVideoForStream(stream,true);
				//Add stream to peer connection
				for (const track of stream.getTracks())
				{
					//Add track
					const sender = pc.addTrack(track,stream);
					//If encrypting/decrypting
					if (isCryptoEnabled) 
					{
						//Get insertable streams
						const senderStreams = sender.createEncodedVideoStreams();
						//Create transform stream for encryption
						let senderTransformStream = new TransformStream({
							start() {},
							flush() {},
							transform: encrypt
						});
						//Encrypt
						senderStreams.readableStream
						    .pipeThrough(senderTransformStream)
						    .pipeTo(senderStreams.writableStream);
					}
  				}
			 }
			
			//Create new offer
			const offer = await pc.createOffer({
				offerToReceiveAudio: true,
				offerToReceiveVideo: true
			});

			console.debug("pc::createOffer sucess",offer);

			//Set it
			pc.setLocalDescription(offer);

			console.log("pc::setLocalDescription succes",offer.sdp);
			
			//Join room
			const joined = await tm.cmd("join",{
				name	: name,
				sdp	: offer.sdp
			});
			
			console.log("cmd::join success",joined);
			
			//Create answer
			const answer = new RTCSessionDescription({
				type	:'answer',
				sdp	: joined.sdp
			});
			
			//Set it
			await pc.setRemoteDescription(answer);
			
			console.log("pc::setRemoteDescription succes",answer.sdp);
			
			console.log("JOINED");
		} catch (error) {
			console.error("Error",error);
			ws.close();
		}
	};
	
	tm.on("cmd",async function(cmd) {
		console.log("ts::cmd",cmd);
		
		switch (cmd.name)
		{
			case "update" :
				try
				{
					console.log(cmd.data.sdp);
					
					//Create new offer
					const offer = new RTCSessionDescription({
						type : 'offer',
						sdp  : cmd.data.sdp
					});
					
					//Set offer
					await pc.setRemoteDescription(offer);
					
					console.log("pc::setRemoteDescription succes",offer.sdp);
					
					//Create answer
					const answer = await pc.createAnswer();
					
					console.log("pc::createAnswer succes",answer.sdp);
					
					//Only set it locally
					await pc.setLocalDescription(answer);
					
					console.log("pc::setLocalDescription succes",answer.sdp);
					
					//accept
					cmd.accept({sdp:answer.sdp});
					
				} catch (error) {
					console.error("Error",error);
					ws.close();
				}
				break;
		}
	});
	
	tm.on("event",async function(event) {
		console.log("ts::event",event);
		
		switch (event.name)
		{
			case "participants" :
				//update participant list
				participants = event.participants;
				break;	
		}
	});
}

navigator.mediaDevices.getUserMedia({
	audio: true,
	video: false
})
.then(function(stream){	

	//Set the input value
	audio_devices.value = stream.getAudioTracks()[0].label;
	
	//Get the select
	var menu = document.getElementById("audio_devices_menu");
	
	//Populate the device lists
	navigator.mediaDevices.enumerateDevices()
		.then(function(devices) {
			//For each one
			devices.forEach(function(device) 
			{
				//It is a mic?
				if (device.kind==="audioinput")
				{
					//Create menu item
					var li = document.createElement("li");
					//Populate
					li.dataset["val"] = device.deviceId;	
					li.innerText = device.label;
					li.className = "mdl-menu__item";
					
					//Add listener
					li.addEventListener('click', function() {
						console.log(device.deviceId);
						//Close previous
						stream.getAudioTracks()[0].stop();
						//Store device id
						audioDeviceId = device.deviceId
						//Get stream for the device
						navigator.mediaDevices.getUserMedia({
							audio: {
								deviceId: device.deviceId
							},
							video: false
						})
						.then(function(stream){	
							//Store it
							soundMeter.connectToSource(stream).then(draw);
						});
	
					});
					//Append
					menu.appendChild (li);
				}
			});
			//Upgrade
			getmdlSelect.init('.getmdl-select');
		        componentHandler.upgradeDom();
		})
		.catch(function(error){
			console.log(error);
		});
	
	var fps = 20;
	var now;
	var then = Date.now();
	var interval = 1000/fps;
	var delta;
	var drawTimer;
	var soundMeter = new SoundMeter(window);
	//Stop
	cancelAnimationFrame(drawTimer);

	function draw() {
		drawTimer = requestAnimationFrame(draw);

		now = Date.now();
		delta = now - then;

		if (delta > interval) {
			then = now ;
			var tot = Math.min(100,(soundMeter.instant*200));
			//Get all 
			const voometers = document.querySelectorAll (".voometer");
			//Set new size
			for (let i=0;i<voometers.length;++i)
				voometers[i].style.width = (Math.floor(tot/5)*5) + "%";
		}
	
	}
	soundMeter.connectToSource(stream).then(draw);
	
	var dialog = document.querySelector('dialog');
	//dialog.showModal();
	if (!supportsInsertableStreams)
		$('#key').html("<red>Your browser does not support insertable streams<red>");
	if (roomId)
	{
		$('#roomId').val(roomId);
		supportsInsertableStreams && $('#key').val(key);
		//$('#name').focus();
	}
	
	$( "#random" ).click(function() {
		$('#roomId').val(Math.random().toString(36).substring(7));
		$('#name').val(Math.random().toString(36).substring(7));
		$('#key').val(Math.random().toString(36).substring(7));
	});
	
	
	$( "form" ).submit(function( event ) {  
		$('#modalinfo').modal('hide');
		var a = document.querySelector(".room-info a");
		a.target = "_blank";
		a.href = "?roomId="+this.roomId.value;
		if (this.key.value)
			a.href += "&key="+encodeURI(this.key.value);
		a.innerText = this.roomId.value;
		a.parentElement.style.opacity = 1;
		connect(url, this.roomId.value, this.name.value,this.key.value);
		event.preventDefault();
		
	});
});