Skip to content
This repository was archived by the owner on Nov 26, 2024. It is now read-only.

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

@burakguder

Description

@burakguder

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();
		
	});
});


Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions