Git Product home page Git Product logo

sfu's Introduction

Medooze SFU

A future proof, experimental WebRTC VP9 SVC SFU.

Motivation

There are already several good production ready alternatives for implementing multiconferencing on webrtc, like Jitsi, Janus or SwitchRTC SFUs and even if you need more legacy support you can try our MCU. Our goal is to experiment and provide an early access to the functionalities that will be available in the near future that will improve drastically the performance and quality of multiconferencing services on WebRTC.

Due to the experimental nature of this functionalities we will only officially support Chrome Canary to be able to access the very latest functionalities available (sometimes even running behind a flag). We don't care about interporeability with other browsers (they will eventually catch up) nor SDP legacy support.

Goal

It is our goal to implement only the We intent to implement support the following features:

This is a moving target as new functionalities will be available on Chrome and some others will be removed, we will update our targets appropiatelly.

To enable VP9 SVC on Chrome Canary you must use the following command line:

chrome.exe --force-fieldtrials=WebRTC-SupportVP9SVC/EnabledByFlag2SL3TL/

End to end encrytpion

A full version of SFrame end to end encryption is under works via insertable streams. Current implementation just uses frame counter as IV which is then inserted in the AES-GCM encrypted frame payload for emoing all required capabilities.

Install

You just need to install all the depencencies and generate the ssl certificates:

npm install 
openssl req -sha256 -days 3650 -newkey rsa:1024 -nodes -new -x509 -keyout server.key -out server.cert

If you get an error like this

gyp verb build dir attempting to create "build" dir: /usr/local/src/medooze/sfu/node_modules/medooze-media-server/build
gyp ERR! configure error
gyp ERR! stack Error: EACCES: permission denied, mkdir '/usr/local/src/medooze/sfu/node_modules/medooze-media-server/build'

You may try instead with:

npm install --unsafe-perm

Usage

In order to run the sfu just:

node index.js [ip]

where the ip is the ICE candidate ip address used for RTP media. To test a simple web client just browse to https://[ip]:8000/.

License

MIT

sfu's People

Contributors

fengjing95 avatar murillo128 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sfu's Issues

Error when run npm install

When I execute the script, Ireport the following error; pls help me.

2282 verbose stack Error: [email protected] install: test -f build/Release/medooze-media-server.node || (node-gyp configure && node-gyp rebuild --jobs=max)
2282 verbose stack Exit status 1
2282 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:332:16)
2282 verbose stack at EventEmitter.emit (events.js:315:20)
2282 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
2282 verbose stack at ChildProcess.emit (events.js:315:20)
2282 verbose stack at maybeClose (internal/child_process.js:1051:16)
2282 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:287:5)
2283 verbose pkgid [email protected]
2284 verbose cwd /usr/local/webrtc/sfu
2285 verbose Linux 4.15.0-96-generic
2286 verbose argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "--unsafe-perm"
2287 verbose node v14.0.0
2288 verbose npm v6.14.4
2289 error code ELIFECYCLE
2290 error errno 1
2291 error [email protected] install: test -f build/Release/medooze-media-server.node || (node-gyp configure && node-gyp rebuild --jobs=max)
2291 error Exit status 1
2292 error Failed at the [email protected] install script.
2292 error This is probably not a problem with npm. There is likely additional logging output above.
2293 verbose exit [ 1, true ]

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

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


Identify remote streams

From what I understand, Medooze SFU adds a new stream whenever a new person gets added. But this doesn't give us a way to label each user against a stream on the client end. Is there any way to resolve?

TypeError: this.remoteInfo.getStreamByMediaId is not a function

Hello

I am trying to install the SFU demo, but I have a few issues;

  1. The file www/js/sfu.js connect() method refers to this.key, which is undefined. Easily fixable, although I have no clue what the key is supposed to do.

  2. Which led me to being able to pseudo connect, albeit an error in the server console:
    2020-04-06T15:34:08.055Z [INFO ] rooms[fff]::participants[0] init
    TypeError: this.remoteInfo.getStreamByMediaId is not a function
    at SDPManagerUnified.processRemoteDescription (/Users/rud/Desktop/medooze/sfu/node_modules/medooze-media-server/lib/SDPManagerUnified.js:319:39)
    at Participant.init (/Users/rud/Desktop/medooze/sfu/lib/Participant.js:124:16)
    at TransactionManager. (/Users/rud/Desktop/medooze/sfu/index.js:167:33)
    at TransactionManager.emit (events.js:198:13)
    at WebSocketConnection.TransactionManager.listener (/Users/rud/Desktop/medooze/sfu/node_modules/transaction-manager/index.js:85:12)
    at WebSocketConnection.emit (events.js:198:13)
    at WebSocketConnection.processFrame (/Users/rud/Desktop/medooze/sfu/node_modules/websocket/lib/WebSocketConnection.js:552:26)
    at /Users/rud/Desktop/medooze/sfu/node_modules/websocket/lib/WebSocketConnection.js:321:40
    at process._tickCallback (internal/process/next_tick.js:61:11)
    connection:onclose
    2020-04-06T15:34:08.074Z [INFO ] rooms[fff]::participants[0] stop
    2020-04-06T15:34:08.074Z [INFO ] rooms[fff]::participants[0] onstopped

Thus my humble question, is this demo running with latest medooze-media-server ..?

Thank you :)

Is this repository still active?

Hello,
I am looking to set up an SFU using media-server and have a few inquiries.

Unlike 'media-server-node', it appears that this repository has not been updated for over 18 months. Is it still active?
Is it appropriate to execute this repository to establish the SFU? Is it accurate that the SFU repository utilizes files from both 'media-server-node' and 'media-server'? (This is a question asking whether this repository represents an example of media-server-node's SFU version.)
I've noticed that the MCU version appears to be implemented using CPP files from media-server, whereas the SFU version seems to utilize js files. Could you kindly clarify the rationale behind this implementation choice?

Thank you for taking the time to read through this somewhat lengthy inquiry.

Safari compatibility

Seems like the current SFU demo doesn't work with Safari.

The first participant uses Chrome, the second one uses Safari. Safari's participant doesn't get stream and neither onaddstream nor onaddtrack events are invoked. But the streams from Safari are visible in Chrome tab. So basically I can hear and see person who uses Safari, but he or she can't see or hear me.

If both participants use Chrome or Firefox - everything works fine

I've tried with vp8 and h264;packetization-mode=1 codecs - the same result for both.
The websocket signaling works fine.

Server info:
Ubuntu 18.04.2 LTS
Nodejs v8.10.0

Client info
MacOS Mojave 10.14.6 (18G95)
Safari version 12.1.2 (14607.3.9)

What might be the reason? Is this example compatible with Safari?

Thanks for your help in advance.

Unable to share camera on ios screen

I tested the sample code on linux, Windows, Android and ios.
It works smoothly on other devices and browsers other than iOS. but the camera screen turns black on the ios screen and does not connect. I think some features related to the latest version of IOS need to be updated.

Have support unified-plan ?

Hi @murillo128

Sorry for my problem. I'm a newbie about webrtc.

I'm working on webrtc SFU project using medooze. I want to use Unified-plan on this project but having some bugs.

answer error:  DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Media section has more than one track specified with a=ssrc lines which is not supported with Unified Plan.

↑ It's seem SDP server support for plan B

When i tried to use sdp.unify(), having another bug.

answer error:  DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: The order of m-lines in answer doesn't match order in offer. Rejecting answer.

↑ After searching, I have known unify method support only offer sdp

I took a look about SDPManager but I don't know how to use it in SFU
https://github.com/medooze/media-server-node/blob/master/lib/SDPManagerUnified.js

Can you give me a solution? Thank you so much.

Issue when I add 2 outgoings streams

Hello,
I change SFU for select ougoings streams because for my use case I need that.
I remove auto attachTo and I added event for add outgoing streams.
For the first add, that working good every time, for add the 3, 4, 5nd ... that working good every time but the 2nd has very often an issue, no audio or not linked with video player ..., without image sometime, when i add controls to the player, I can stop the player but audio continue ... I added stats and I found that I don't have information about stream,

normally, for the 1st stream, I have this stats :
image

for the 2nd one, I have :
image

googCodecName is "" and not "opus"

When I don't have video, I have the same with the video stream:
image

I log than I wait stable connexion for add a new outgoing stream, not better, sometime that not working.

I try with different version of google chrome and with android, windows, linux, same issue :(

My code is here :
https://github.com/Nic01as/sfu

for create my issue, I start 3 participants and add participants 1 and 2 into the 3.
1:
image

2:
image

3:
image

add 1 into 3
image

wait 10s
add 2 into 3
image

no video, sometime no audio, sometime all good.

in this exemple, googCodecName: "" for video stream 3.

Do you have an idea ?

Fix SDP error

Hey, I've updated my media server and based on this project I get this error

Error DOMException: Failed to set local answer sdp: Called in wrong state: kStable

Error happens when triggered is Update event, I've got it same as in this project, happens on Chrome, related to SDP update probably.

ts::event {name: "update", data: {…}}data: {sdp: "v=0
↵o=- 1521165548828 1 IN IP4 127.0.0.1
↵s=seman…f-fa3328b46eb7 audio1
↵a=rtcp-mux
↵a=rtcp-rsize
↵"}name: "update"__proto__: Object
sfu.js:292 pc::setRemoteDescription succes v=0
o=- 1521165548828 1 IN IP4 127.0.0.1
s=semantic-sdp
c=IN IP4 0.0.0.0
t=0 0
a=msid-semantic: WMS *
a=group:BUNDLE audio
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=rtpmap:111 opus/48000/2
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=setup:passive
a=mid:audio
a=sendrecv
a=ice-ufrag:b1758ff55ecc6e71
a=ice-pwd:9d46e9811c689727d5103452a408af47c90819d6bf3e6c8b
a=fingerprint:sha-256 7E:E8:C3:1C:9F:91:E1:0D:72:35:90:9D:12:99:F7:9F:B0:2E:E5:4E:22:78:18:89:E9:54:CB:CF:62:99:F1:20
a=candidate:1 1 UDP 33554431 145.239.82.81 51810 typ host
a=ssrc:279434614 cname:fbca4e47-e571-4655-8c6f-fa3328b46eb7
a=ssrc:279434614 msid:fbca4e47-e571-4655-8c6f-fa3328b46eb7 audio1
a=rtcp-mux
a=rtcp-rsize

sfu.js:292 pc::setRemoteDescription succes v=0
o=- 1521165548828 1 IN IP4 127.0.0.1
s=semantic-sdp
c=IN IP4 0.0.0.0
t=0 0
a=msid-semantic: WMS *
a=group:BUNDLE audio
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=rtpmap:111 opus/48000/2
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=setup:passive
a=mid:audio
a=sendrecv
a=ice-ufrag:b1758ff55ecc6e71
a=ice-pwd:9d46e9811c689727d5103452a408af47c90819d6bf3e6c8b
a=fingerprint:sha-256 7E:E8:C3:1C:9F:91:E1:0D:72:35:90:9D:12:99:F7:9F:B0:2E:E5:4E:22:78:18:89:E9:54:CB:CF:62:99:F1:20
a=candidate:1 1 UDP 33554431 145.239.82.81 51810 typ host
a=ssrc:279434614 cname:fbca4e47-e571-4655-8c6f-fa3328b46eb7
a=ssrc:279434614 msid:fbca4e47-e571-4655-8c6f-fa3328b46eb7 audio1
a=rtcp-mux
a=rtcp-rsize

sfu.js:297 pc::createAnswer succes v=0
o=- 748647842640918674 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio
a=msid-semantic: WMS 1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2
m=audio 53346 UDP/TLS/RTP/SAVPF 111
c=IN IP4 192.168.1.105
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:478433003 1 udp 2122265343 fd20:4689:5094::7d51:ccd9:3ec1:8c3d 53343 typ host generation 0 network-id 2
a=candidate:690563730 1 udp 2122199807 fd20:4689:5094::11ec:fa88:bcd:6142 53344 typ host generation 0 network-id 3
a=candidate:511557519 1 udp 2122134271 fd20:4689:5094::4c0 53345 typ host generation 0 network-id 4
a=candidate:2222700650 1 udp 2122063615 192.168.1.105 53346 typ host generation 0 network-id 1
a=ice-ufrag:VhiF
a=ice-pwd:ExdKeEEV5HOhwapjvOQVHGMz
a=ice-options:trickle
a=fingerprint:sha-256 EA:62:45:F4:22:1D:68:4D:78:F7:45:8A:81:55:F4:65:48:B4:84:27:14:3E:32:71:5B:97:0E:61:D6:3C:AF:5D
a=setup:active
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:3817187234 cname:uCPVfjUoG/sOhAWL
a=ssrc:3817187234 msid:1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2 5e43af39-25e2-4bad-ae86-e6cf040ad719
a=ssrc:3817187234 mslabel:1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2
a=ssrc:3817187234 label:5e43af39-25e2-4bad-ae86-e6cf040ad719

sfu.js:297 pc::createAnswer succes v=0
o=- 748647842640918674 4 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio
a=msid-semantic: WMS 1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2
m=audio 53346 UDP/TLS/RTP/SAVPF 111
c=IN IP4 192.168.1.105
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:478433003 1 udp 2122265343 fd20:4689:5094::7d51:ccd9:3ec1:8c3d 53343 typ host generation 0 network-id 2
a=candidate:690563730 1 udp 2122199807 fd20:4689:5094::11ec:fa88:bcd:6142 53344 typ host generation 0 network-id 3
a=candidate:511557519 1 udp 2122134271 fd20:4689:5094::4c0 53345 typ host generation 0 network-id 4
a=candidate:2222700650 1 udp 2122063615 192.168.1.105 53346 typ host generation 0 network-id 1
a=ice-ufrag:VhiF
a=ice-pwd:ExdKeEEV5HOhwapjvOQVHGMz
a=ice-options:trickle
a=fingerprint:sha-256 EA:62:45:F4:22:1D:68:4D:78:F7:45:8A:81:55:F4:65:48:B4:84:27:14:3E:32:71:5B:97:0E:61:D6:3C:AF:5D
a=setup:active
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:3817187234 cname:uCPVfjUoG/sOhAWL
a=ssrc:3817187234 msid:1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2 5e43af39-25e2-4bad-ae86-e6cf040ad719
a=ssrc:3817187234 mslabel:1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2
a=ssrc:3817187234 label:5e43af39-25e2-4bad-ae86-e6cf040ad719

sfu.js:302 pc::setLocalDescription succes v=0
o=- 748647842640918674 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio
a=msid-semantic: WMS 1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2
m=audio 53346 UDP/TLS/RTP/SAVPF 111
c=IN IP4 192.168.1.105
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:478433003 1 udp 2122265343 fd20:4689:5094::7d51:ccd9:3ec1:8c3d 53343 typ host generation 0 network-id 2
a=candidate:690563730 1 udp 2122199807 fd20:4689:5094::11ec:fa88:bcd:6142 53344 typ host generation 0 network-id 3
a=candidate:511557519 1 udp 2122134271 fd20:4689:5094::4c0 53345 typ host generation 0 network-id 4
a=candidate:2222700650 1 udp 2122063615 192.168.1.105 53346 typ host generation 0 network-id 1
a=ice-ufrag:VhiF
a=ice-pwd:ExdKeEEV5HOhwapjvOQVHGMz
a=ice-options:trickle
a=fingerprint:sha-256 EA:62:45:F4:22:1D:68:4D:78:F7:45:8A:81:55:F4:65:48:B4:84:27:14:3E:32:71:5B:97:0E:61:D6:3C:AF:5D
a=setup:active
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=ssrc:3817187234 cname:uCPVfjUoG/sOhAWL
a=ssrc:3817187234 msid:1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2 5e43af39-25e2-4bad-ae86-e6cf040ad719
a=ssrc:3817187234 mslabel:1DPgd4rvjERIhtypgpgNYYlaEsWdcvH5Fhy2
a=ssrc:3817187234 label:5e43af39-25e2-4bad-ae86-e6cf040ad719

sfu.js:305 Error DOMException: Failed to set local answer sdp: Called in wrong state: kStable

Update event on sfu.js

	case "update" :
				try
				{

					//Create new offer
					const offer = new RTCSessionDescription({
						type : 'offer',
						sdp  : event.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);

				} catch (error) {
					console.error("Error",error);
					
					mp.trigger('voiceChat_browserError', error);
					
					ws.close();
				}

Update event on server

						tm.event('update',{
							sdp	: sdp.toString()
						});

On connection close event

Could you please advice best practice to catch event when remote peer is closed connection?
I'm implementing simple SFU server and need to know when client peer is leaving the room. How do I catch it in proper way?

Thanks in advance.

Is this project still alive?

I also had this "your browser does not support insertable streams". Commented it and got one step ahead (otherwise nothing happened)

The default port is 8084, whereas the readme says 8000

The latency of just 4 participants is higher than what I have seen with other solutions so far.

I would really like to join you, but not to go again with some dead horse

MacOS build error

System version : macOS 10.13.4

the log :

CXX(target) Release/obj.target/medooze-media-server/media-server/src/remoteratecontrol.o
  CXX(target) Release/obj.target/medooze-media-server/media-server/src/remoterateestimator.o
1 warning generated.
../media-server/src/remoteratecontrol.cpp:113:65: error: call to 'abs' is ambiguous
        if (hypothesis!=OverUsing && (fmin(fpsCalc.GetAcumulated(),60)*std::abs(offset)<threshold))
                                                                       ^~~~~~~~
/usr/include/stdlib.h:137:6: note: candidate function
int      abs(int) __pure2;
         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:111:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long      abs(     long __x) _NOEXCEPT {return  labs(__x);}
                                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:113:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
                                           ^
../media-server/src/remoteratecontrol.cpp:119:7: error: call to 'abs' is ambiguous
                if (std::abs(residual)<3*sqrt(varNoise))
                    ^~~~~~~~
/usr/include/stdlib.h:137:6: note: candidate function
int      abs(int) __pure2;
         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:111:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long      abs(     long __x) _NOEXCEPT {return  labs(__x);}
                                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:113:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
                                           ^
../media-server/src/remoteratecontrol.cpp:162:6: error: call to 'abs' is ambiguous
        if (std::abs(T)>threshold)
            ^~~~~~~~
/usr/include/stdlib.h:137:6: note: candidate function
int      abs(int) __pure2;
         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:111:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long      abs(     long __x) _NOEXCEPT {return  labs(__x);}
                                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:113:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
                                           ^
../media-server/src/remoteratecontrol.cpp:173:167: error: call to 'abs' is ambiguous
  ...UltraDebug("BWE: Overusing bitrate:%.0llf max:%.0llf min:%.0llf T:%f,threshold:%f\n",bitrateCalc.GetInstantAvg(),bitrateCalc.GetMaxAvg(),bitrateCalc.GetMinAvg(),std::abs(T),threshold);
                                                                                                                                                                      ^~~~~~~~
/usr/include/stdlib.h:137:6: note: candidate function
int      abs(int) __pure2;
         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:111:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long      abs(     long __x) _NOEXCEPT {return  labs(__x);}
                                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:113:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
                                           ^
../media-server/src/remoteratecontrol.cpp:179:180: error: call to 'abs' is ambiguous
  ...UltraDebug("BWE: Overusing bitrate:%.0llf max:%.0llf min:%.0llf T:%f,threshold:%f\n",overUseCount,bitrateCalc.GetInstantAvg(),bitrateCalc.GetMaxAvg(),bitrateCalc.GetMinAvg(),std::abs(T),threshold);
                                                                                                                                                                                   ^~~~~~~~
/usr/include/stdlib.h:137:6: note: candidate function
int      abs(int) __pure2;
         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:111:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long      abs(     long __x) _NOEXCEPT {return  labs(__x);}
                                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:113:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
                                           ^
../media-server/src/remoteratecontrol.cpp:188:155: error: call to 'abs' is ambiguous
                                UltraDebug("BWE:  UnderUsing bitrate:%.0llf max:%.0llf min:%.0llf T:%d\n",bitrateCalc.GetInstantAvg(),bitrateCalc.GetMaxAvg(),bitrateCalc.GetMinAvg(),std::abs(T));
                                                                                                                                                                                      ^~~~~~~~
/usr/include/stdlib.h:137:6: note: candidate function
int      abs(int) __pure2;
         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:111:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long      abs(     long __x) _NOEXCEPT {return  labs(__x);}
                                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:113:44: note: candidate function
inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
                                           ^
6 errors generated.
make: *** [Release/obj.target/medooze-media-server/media-server/src/remoteratecontrol.o] Error 1
make: *** Waiting for unfinished jobs....
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node_modules/node-gyp/lib/build.js:258:23)
gyp ERR! stack     at ChildProcess.emit (events.js:160:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:209:12)
gyp ERR! System Darwin 17.5.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--jobs=max"
gyp ERR! cwd /Users/congchen/projects/js/sfu/node_modules/medooze-media-server
gyp ERR! node -v v9.4.0
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] install: `test -f build/Release/medooze-media-server.node || (node-gyp configure && node-gyp rebuild --jobs=max)`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/congchen/.npm/_logs/2018-04-11T08_33_04_093Z-debug.log

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.