I am currently working on webrtc project with multiple clients (5 - 4 with input video streams and 1 for showing them) and signaling server. Main goal is to connect clients on this way:
Every client with input video stream should be connected to client that will show all of video streams. It should be connected like that because I want to use webrtc so I can have low latency. I would also like to use web sockets for signaling server. Server is written in Python and clients in Javascript. Does anyone know how to accomplish that?
Server:
from aiohttp import web
import socketio
ROOM = 'room'
sessionIDs = []
sio = socketio.AsyncServer(cors_allowed_origins='*', ping_timeout=35)
app = web.Application()
sio.attach(app)
#sio.event
async def connect(sid, environ):
print('Connected', sid)
sessionIDs.append(sid)
await sio.emit('ready', room=ROOM, skip_sid=sid)
sio.enter_room(sid, ROOM)
#sio.event
def disconnect(sid):
sio.leave_room(sid, ROOM)
print('Disconnected', sid)
#sio.event
async def data(sid, data):
print('Message from {}: {}'.format(sid, data))
await sio.emit('data', data, room=ROOM, skip_sid=sid)
if __name__ == '__main__':
web.run_app(app, port=9999)
Client:
const SIGNALING_SERVER_URL = 'http://localhost:9999';
const TURN_SERVER_URL = 'localhost:3478';
const TURN_SERVER_USERNAME = 'username';
const TURN_SERVER_CREDENTIAL = 'credential';
const PC_CONFIG = {
iceServers: [
{
urls: 'turn:' + TURN_SERVER_URL + '?transport=tcp',
username: TURN_SERVER_USERNAME,
credential: TURN_SERVER_CREDENTIAL
},
{
urls: 'turn:' + TURN_SERVER_URL + '?transport=udp',
username: TURN_SERVER_USERNAME,
credential: TURN_SERVER_CREDENTIAL
}
]
};
// Signaling methods
let socket = io(SIGNALING_SERVER_URL, { autoConnect: false }, /*{query: 'loggeduser=finalClient'}*/ /*{auth: {token: 'my-token'}}*/);
socket.on('data', (data) => {
console.log('Data received: ', data);
handleSignalingData(data);
});
socket.on('ready', () => {
console.log('Ready');
// Connection with signaling server is ready, and so is local stream
createPeerConnection();
sendOffer();
});
let sendData = (data) => {
socket.emit('data', data);
};
// WebRTC methods
let pc;
let localStream;
let remoteStreamElement = document.querySelector('#remoteStream');
let getLocalStream = () => {
socket.connect();
}
let createPeerConnection = () => {
try {
pc = new RTCPeerConnection(PC_CONFIG);
pc.onicecandidate = onIceCandidate;
pc.ontrack = onTrack;
pc.addStream(localStream);
console.log('PeerConnection created');
} catch (error) {
console.error('PeerConnection failed: ', error);
}
};
let sendOffer = () => {
console.log('Send offer');
pc.createOffer().then(
setAndSendLocalDescription,
(error) => { console.error('Send offer failed: ', error); }
);
};
let sendAnswer = () => {
console.log('Send answer');
pc.createAnswer().then(
setAndSendLocalDescription,
(error) => { console.error('Send answer failed: ', error); }
);
};
let setAndSendLocalDescription = (sessionDescription) => {
pc.setLocalDescription(sessionDescription);
console.log('Local description set');
sendData(sessionDescription);
};
let onIceCandidate = (event) => {
if (event.candidate) {
console.log('ICE candidate');
sendData({
type: 'candidate',
candidate: event.candidate
});
}
};
let onTrack = (event) => {
console.log('Add track');
remoteStreamElement.srcObject = event.streams[0];
};
let handleSignalingData = (data) => {
switch (data.type) {
case 'offer':
createPeerConnection();
pc.setRemoteDescription(new RTCSessionDescription(data));
sendAnswer();
break;
case 'answer':
pc.setRemoteDescription(new RTCSessionDescription(data));
break;
case 'candidate':
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
break;
}
};
// Start connection
getLocalStream();
Currently main functionality is peer to peer connection between them, but I want to make new peer to peer connection (for every client - with input stream - connected to server) with specific client that will show all of them.
Related
I am trying to establish a webRTC connection using socket.io and this documentation: https://webrtc.org/getting-started/peer-connections. It looks like I have been able to establish a connection and set the LocalDescription and Remote Description but the 'icecandidate' event listener never triggers to add the candidate to one another. Any help would be greatly appreciated!
Client
const peerConnection = new RTCPeerConnection(config);
const config = {
iceServers: [{ "urls": "stun:stun.l.google.com:19302", }]
};
async function makeCall() {
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
socket.emit('offer', (offer));
socket.on('answer', async (answer) => {
if(answer) {
console.log("answer successful");
const remoteDesc = new RTCSessionDescription(answer);
await peerConnection.setRemoteDescription(remoteDesc);
console.log(peerConnection);
}
});
}
makeCall();
socket.on('offer', async (offer) => {
if(offer) {
console.log("now sending offer: " + offer);
peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
socket.emit('answer', (answer));
//signalingChannel.send({'answer': answer});
}
});
// !!! THIS CODE NEVER TRIGGERS AND 'peerConnection.iceCandidate' returns null
peerConnection.addEventListener('icecandidate', event => {
console.log('triggered outside scope'); // trigger check
if (event.candidate) {
console.log('triggered inside scope'); // trigger check
socket.emit('candidate', event.candidate);
}
});
// BELOW WILL NOT TRIGGER WITHOUT 'icecandidate' LISTENER
// Listen for remote ICE candidates and add them to the local RTCPeerConnection ()
socket.on('candidate', async (iceCandidate) => {
if (iceCandidate) {
try {
await peerConnection.addIceCandidate(iceCandidate);
} catch (e) {
console.error('Error adding received ice candidate', e);
}
}
})
// Confirm they are connected
peerConnection.addEventListener('connectionstatechange', event => {
if (peerConnection.connectionState === 'connected') {
// Peers connected!
console.log('success')
}
});
Server
io.on("connection", (socket) => {
socket.on('offer', (offer, answer) => {
console.log(offer);
socket.broadcast.emit('offer', (offer));
})
socket.on('answer', (answer) => {
console.log(answer);
socket.broadcast.emit('answer', (answer));
})
socket.on('candidate', (candidate) => {
console.log(candidate);
socket.broadcast.emit('candidate', (candidate));
})
});
The connection you are trying to establish does not have any media tracks (added via addTrack or addTransceiver) nor datachannels. Therefore you offer will not have any SDP m= lines and since the ice candidates are associated with those you won't get any and no connection will be established.
I'm working on app which send message via websockets (managed by django channels) and in return it receives json from django db as a message and renders frontend based on that json.
I have Invalid State Error when I try to send message by websocket, why? Messages send are usually Json. I works properly all the time but commented part doesn't and I don't know why please explain me.
function main() {
configGame();
}
function configGame() {
const socket = "ws://" + window.location.host + window.location.pathname;
const websocket = new WebSocket(socket);
const playerName = document.querySelector(".playerName_header").textContent;
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "ready",
});
sendMess(mess);
});
start_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "start",
});
sendMess(mess);
});
}
function openWebsocket() {
console.log("Establishing Websocket Connection...");
websocket.onopen = () => {
console.log("Websocket Connection Established!");
};
}
function setWebsocket() {
websocket.onmessage = (mess) => {
console.log(`Message: ${mess.data}`);
dataJson = JSON.parse(mess.data);
dataJson = JSON.parse(dataJson.message);
//Player Ready (jeszcze z max_players zrobic kontrolke)
if (dataJson.action === "player_ready") {
const playersReadyText = document.querySelector(".players_ready_text");
playersReadyText.textContent = `Players ready: ${dataJson.players_ready}`;
}
};
websocket.onclose = () => {
console.log("Websocket Connection Terminated!");
};
}
/*
function checkState() {
let mess = JSON.stringify({
player: playerName,
action: "game state",
});
sendMess(mess);
}
*/
function sendMess(messText) {
websocket.send(messText);
}
openWebsocket();
checkState(); //This one doesn't work
asignEvents();
setWebsocket();
}
// Asigning Event Listneres to DOM ELEMENTS
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
console.log("Ready");
});
start_btn.addEventListener("click", () => {
console.log("Start");
});
}
main();
Error:
Console (Safari) returns InvalidState error and points to
method checkState and sendMess.
InvalidStateError: The object is in an invalid state.
Is the websocket connected?
sendMess(messText) {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(messText);
} else {
console.warn("websocket is not connected");
}
}
Hi can someone check is there something wrong with my code cause it not displayig the remote video. The log always return 'Start web RTC as 'offerer'' for both local and remote video. And I think mostly the sample code almost similar with my code so I dont know. Much love appreciate if you guys can help me. Thanks!
if (!location.hash) {
location.hash = Math.floor(Math.random() * 0xFFFFFF).toString(16);
}
const roomHash = location.hash.substring(1);
//const roomHash = '5dfd9b';
console.log("ROOM ID: >> " +roomHash);
// TODO: Replace with your own channel ID
const drone = new ScaleDrone('AUpfMxm16E9bfdgA');
// Room name needs to be prefixed with 'observable-'
//const roomName = 'observable-' + roomHash;
const roomName = 'observable-testPHR';
const configuration = {
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
};
let room;
let pc;
function onSuccess() {
console.log("Connection sucess");
};
function onError(error) {
console.log("Connection failed!");
//console.error(error);
};
drone.on('open', error => {
if (error) {
console.log( " Error open drone >>");
return console.error(error);
}
console.log(" Drone open >>")
room = drone.subscribe(roomName);
room.on('open', error => {
if (error) {
onError(error);
}
});
// We're connected to the room and received an array of 'members'
// connected to the room (including us). Signaling server is ready.
room.on('members', members => {
console.log('MEMBERS', members);
// If we are the second user to connect to the room we will be creating the offer
const isOfferer = members.length === 2;
startWebRTC(isOfferer);
});
});
// Send signaling data via Scaledrone
function sendMessage(message) {
console.log("Sending signal via scaledrone >>");
drone.publish({
room: roomName,
message
});
}
function startWebRTC(isOfferer) {
console.log('Starting WebRTC in as', isOfferer ? 'offerer' : 'waiter');
pc = new RTCPeerConnection(configuration);
console.log(" Test A ");
// 'onicecandidate' notifies us whenever an ICE agent needs to deliver a
// message to the other peer through the signaling server
pc.onicecandidate = event => {
console.log("Send Message to Candidate");
if (event.candidate) {
sendMessage({'candidate': event.candidate});
}
};
console.log(" Test B ");
// If user is offerer let the 'negotiationneeded' event create the offer
if (isOfferer) {
console.log(" Create Offer ");
pc.onnegotiationneeded = () => {
pc.createOffer().then(localDescCreated).catch(onError);
}
}
console.log(" Test C ");
// When a remote stream arrives display it in the #remoteVideo element
pc.ontrack = event => {
console.log("Display remote video >>>")
const stream = event.streams[0];
console.log(" Stream : >>" +stream);
if (!remoteVideo.srcObject || remoteVideo.srcObject.id !== stream.id) {
remoteVideo.srcObject = stream;
}
};
console.log(" Test D ");
navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
}).then(stream => {
console.log(" Display Local Video >> ");
// Display your local video in #localVideo element
localVideo.srcObject = stream;
// Add your stream to be sent to the conneting peer
stream.getTracks().forEach(track => pc.addTrack(track, stream));
}, onError);
console.log(" Test E ");
// Listen to signaling data from Scaledrone
room.on('data', (message, client) => {
// Message was sent by us
if (client.id === drone.clientId) {
return;
}
console.log(" Test F ");
if (message.sdp) {
// This is called after receiving an offer or answer from another peer
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), () => {
// When receiving an offer lets answer it
if (pc.remoteDescription.type === 'offer') {
console.log(" Answer call ");
pc.createAnswer().then(localDescCreated).catch(onError);
}
}, onError);
} else if (message.candidate) {
// Add the new ICE candidate to our connections remote description
pc.addIceCandidate(
new RTCIceCandidate(message.candidate), onSuccess, onError
);
}
});
}
function localDescCreated(desc) {
pc.setLocalDescription(
desc,
() => sendMessage({'sdp': pc.localDescription}),
onError
);
}
I am working on solutions using which i can send desktop push notification to subscribed clients.
I have created basic solution in where whenever user click on button i ask user for whether they want to allow notifications for my app or not!
I am getting an error of "Registration failed - permission denied" whenever i click on button for first time.
So that i am not able to get required endpoints to save at backend
Here is my code
index.html
<html>
<head>
<title>PUSH NOT</title>
<script src="index.js"></script>
</head>
<body>
<button onclick="main()">Ask Permission</button>
</body>
</html>
index.js
const check = () => {
if (!("serviceWorker" in navigator)) {
throw new Error("No Service Worker support!");
} else {
console.log("service worker supported")
}
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
} else {
console.log("PushManager worker supported")
}
};
const registerServiceWorker = async () => {
const swRegistration = await navigator.serviceWorker.register("/service.js?"+Math.random());
return swRegistration;
};
const requestNotificationPermission = async () => {
const permission = await window.Notification.requestPermission();
// value of permission can be 'granted', 'default', 'denied'
// granted: user has accepted the request
// default: user has dismissed the notification permission popup by clicking on x
// denied: user has denied the request.
if (permission !== "granted") {
throw new Error("Permission not granted for Notification");
}
};
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const permission = await requestNotificationPermission();
};
// main(); we will not call main in the beginning.
service.js
// urlB64ToUint8Array is a magic function that will encode the base64 public key
// to Array buffer which is needed by the subscription option
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const saveSubscription = async subscription => {
console.log("Save Sub")
const SERVER_URL = "http://localhost:4000/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
};
self.addEventListener("activate", async () => {
try {
const applicationServerKey = urlB64ToUint8Array(
"BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM"
);
const options = { applicationServerKey, userVisibleOnly: true };
const subscription = await self.registration.pushManager.subscribe(options);
console.log(JSON.stringify(subscription))
const response = await saveSubscription(subscription);
} catch (err) {
console.log(err.code)
console.log(err.message)
console.log(err.name)
console.log('Error', err)
}
});
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!! ", event.data.text());
} else {
console.log("Push event but no data");
}
});
Also i have created a bit of backend as well
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const webpush = require('web-push')
const app = express();
app.use(cors());
app.use(bodyParser.json());
const port = 4000;
app.get("/", (req, res) => res.send("Hello World!"));
const dummyDb = { subscription: null }; //dummy in memory store
const saveToDatabase = async subscription => {
// Since this is a demo app, I am going to save this in a dummy in memory store. Do not do this in your apps.
// Here you should be writing your db logic to save it.
dummyDb.subscription = subscription;
};
// The new /save-subscription endpoint
app.post("/save-subscription", async (req, res) => {
const subscription = req.body;
await saveToDatabase(subscription); //Method to save the subscription to Database
res.json({ message: "success" });
});
const vapidKeys = {
publicKey:
'BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM',
privateKey: 'mHSKS-uwqAiaiOgt4NMbzYUb7bseXydmKObi4v4bN6U',
}
webpush.setVapidDetails(
'mailto:janakprajapati90#email.com',
vapidKeys.publicKey,
vapidKeys.privateKey
)
const sendNotification = (subscription, dataToSend='') => {
webpush.sendNotification(subscription, dataToSend)
}
app.get('/send-notification', (req, res) => {
const subscription = {endpoint:"https://fcm.googleapis.com/fcm/send/dLjyDYvI8yo:APA91bErM4sn_wRIW6xCievhRZeJcIxTiH4r_oa58JG9PHUaHwX7hQlhMqp32xEKUrMFJpBTi14DeOlECrTsYduvHTTnb8lHVUv3DkS1FOT41hMK6zwMvlRvgWU_QDDS_GBYIMRbzjhg",expirationTime:null,keys:{"p256dh":"BE6kUQ4WTx6v8H-wtChgKAxh3hTiZhpfi4DqACBgNRoJHt44XymOWFkQTvRPnS_S9kmcOoDSgOVD4Wo8qDQzsS0",auth:"CfO4rOsisyA6axdxeFgI_g"}} //get subscription from your databse here.
const message = 'Hello World'
sendNotification(subscription, message)
res.json({ message: 'message sent' })
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Please help me
Try the following code:
index.js
const check = () => {
if (!("serviceWorker" in navigator)) {
throw new Error("No Service Worker support!");
} else {
console.log("service worker supported")
}
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
} else {
console.log("PushManager worker supported")
}
};
const saveSubscription = async subscription => {
console.log("Save Sub")
const SERVER_URL = "http://localhost:4000/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
};
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const registerServiceWorker = async () => {
return navigator.serviceWorker.register("service.js?"+Math.random()).then((swRegistration) => {
console.log(swRegistration);
return swRegistration;
});
};
const requestNotificationPermission = async (swRegistration) => {
return window.Notification.requestPermission().then(() => {
const applicationServerKey = urlB64ToUint8Array(
"BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM"
);
const options = { applicationServerKey, userVisibleOnly: true };
return swRegistration.pushManager.subscribe(options).then((pushSubscription) => {
console.log(pushSubscription);
return pushSubscription;
});
});
};
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const subscription = await requestNotificationPermission(swRegistration);
// saveSubscription(subscription);
};
service.js
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!! ", event.data.text());
} else {
console.log("Push event but no data");
}
});
I can think of three reasons that the permission is denied
1) your site is not on https (including localhost that is not on https), the default behaviour from chrome as far as i know is to block notifications on http sites. If that's the case, click on the info icon near the url, then click on site settings, then change notifications to ask
2) if you are on Safari, then safari is using the deprecated interface of the Request permission, that is to say the value is not returned through the promise but through a callback so instead of
Notification.requestPermission().then(res => console.log(res))
it is
Notification.requestPermission(res => console.log(res))
3) Your browser settings are blocking the notifications request globally, to ensure that this is not your problem run the following code in the console (on a secured https site)
Notification.requestPermission().then(res => console.log(res))
if you receive the alert box then the problem is something else, if you don't then make sure that the browser is not blocking notifications requests
So I was following this tutorial to learn how to implement a WebRTC server-client setup. Once I got that working I wanted to split the client into two parts, one sender and one receiver. Now they can establish a connection with each other but the receiver never gets the stream from the sender.
I managed to determine that the code flow between the original code and split versions remains the same, except that neither peer executes the onicecandidate event.
According to this I need to explicitly include OfferToReceiveAudio: true and OfferToReceiveVideo: true since I'm using Chrome, which I did but it didn't seem to make any difference.
Currently, they both receive SDP from each other, there is a local and remote description in the peerConnection, and iceGatheringState is "new" but iceConnectionState is "checking" (unlike the second link where he states it should also be "new")
How come they aren't exchanging ICE candidates when it's split in two like this?
Sender.js
const HTTPSPort = 3434;
const domain = '127.0.0.1';
const wssHost = 'wss://' + domain + ':' + HTTPSPort + '/websocket/';
// Feed settings
const video = true;
const audio = true;
const constraints = { "audio": audio, "video": video };
var videoContainer = null, feed = null,
pC = null, wsc = new WebSocket(wssHost),
pCConfig = [
{ 'url': 'stun:stun.services.mozilla.com' },
{ 'url': 'stun:stun.l.google.com:19302' }
];
function pageReady() {
// Check browser WebRTC availability
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
videoContainer = document.getElementById('videoFeed');
// Get the feed and show it in the local video element
feed = stream;
videoContainer.srcObject = feed;
}).catch(function () {
alert("Sorry, your browser does not support WebRTC!");
});
}
wsc.onmessage = function (evt) {
if (!pC) {
// Initiate peerConnection
pC = new RTCPeerConnection(pCConfig);
// Send any ice candidates to the other peer
pC.onicecandidate = onIceCandidateHandler;
pC.addStream(feed);
}
// Read the message
var signal = JSON.parse(evt.data);
if (signal.sdp) {
log('Received SDP from remote peer.');
pC.setRemoteDescription(new RTCSessionDescription(signal.sdp));
answerCall();
} else if (signal.candidate) {
log('Received ICECandidate from remote peer.');
pC.addIceCandidate(new RTCIceCandidate(signal.candidate));
}
};
function answerCall() {
pC.createAnswer().then(function (answer) {
var ans = new RTCSessionDescription(answer);
pC.setLocalDescription(ans).then(function () {
wsc.send(JSON.stringify({ 'sdp': ans }));
}).catch(errorHandler);
}).catch(errorHandler);
}
function onIceCandidateHandler(evt) {
if (!evt || !evt.candidate) return;
wsc.send(JSON.stringify({ 'candidate': evt.candidate }));
};
Receiver.js
const HTTPSPort = 3434;
const domain = '127.0.0.1';
const wssHost = 'wss://' + domain + ':' + HTTPSPort + '/websocket/';
var remoteVideo = null,
pC = null, wsc = new WebSocket(wssHost),
pCConfig = [
{ 'url': 'stun:stun.services.mozilla.com' },
{ 'url': 'stun:stun.l.google.com:19302' }
],
mediaConstraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
function pageReady() {
remoteVideo = document.getElementById('remoteVideo');
icebutton = document.getElementById('checkICE');
icebutton.addEventListener('click', function (evt) {
console.log(pC);
})
};
wsc.onopen = function () {
// Initiates peerConnection
pC = new RTCPeerConnection(pCConfig);
// Send any ICE candidates to the other peer
pC.onicecandidate = onIceCandidateHandler;
// Once remote stream arrives, show it in the remote video element
pC.onaddstream = onAddStreamHandler;
// Offer a connection to the server
createAndSendOffer();
};
function createAndSendOffer() {
pC.createOffer(mediaConstraints).then(function (offer) {
var off = new RTCSessionDescription(offer);
pC.setLocalDescription(off).then(function () {
wsc.send(JSON.stringify({ 'sdp': off }));
}).catch(errorHandler);
}).catch(errorHandler);
}
wsc.onmessage = function (evt) {
// Read the message
var signal = JSON.parse(evt.data);
if (signal.sdp) {
console.log('Received SDP from remote peer.');
pC.setRemoteDescription(new RTCSessionDescription(signal.sdp));
} else if (signal.candidate) {
console.log('Received ICECandidate from remote peer.');
pC.addIceCandidate(new RTCIceCandidate(signal.candidate));
}
};
function onIceCandidateHandler(evt) {
if (!evt || !evt.candidate) return;
wsc.send(JSON.stringify({ 'candidate': evt.candidate }));
};
function onAddStreamHandler(evt) {
// Set remote video stream as source for remote video HTML element
remoteVideo.srcObject = evt.stream;
};
You forgot iceServers. Change
pCConfig = [
{ 'url': 'stun:stun.services.mozilla.com' },
{ 'url': 'stun:stun.l.google.com:19302' }
];
to
pCConfig = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
};
Additionally:
url has been deprecated, use urls.
The mozilla stun server has been decommissioned, so save yourself some time and exclude it.
mandatory and OfferToReceiveAudio have been deprecated. Use offerToReceiveAudio.
mandatory and OfferToReceiveVideo have been deprecated. Use offerToReceiveVideo.
Tips
Kudos for using promises (unlike the tutorial)! Note you can return them to flatten things:
function createAndSendOffer() {
return pC.createOffer(mediaConstraints).then(function (offer) {
return pC.setLocalDescription(offer);
})
.then(function () {
wsc.send(JSON.stringify({ sdp: pC.localDescription }));
})
.catch(errorHandler);
}