gstreamer/webrtc/sendrecv/js/webrtc.js
Nirbheek Chauhan e9b0656bad Add sendrecv implementation in js and gst webrtc
JS code runs on the browser and uses the browser's webrtc
implementation.

C code uses gstreamer's webrtc implementation, for which you need the
following repositories:

https://github.com/ystreet/gstreamer/tree/promise
https://github.com/ystreet/gst-plugins-bad/tree/webrtc

You can build these with either Autotools gst-uninstalled:

https://arunraghavan.net/2014/07/quick-start-guide-to-gst-uninstalled-1-x/

Or with Meson gst-build:

https://cgit.freedesktop.org/gstreamer/gst-build/
2017-10-21 20:02:19 +05:30

203 lines
6.3 KiB
JavaScript

/* vim: set sts=4 sw=4 et :
*
* Demo Javascript app for negotiating and streaming a sendrecv webrtc stream
* with a GStreamer app. Runs only in passive mode, i.e., responds to offers
* with answers, exchanges ICE candidates, and streams.
*
* Author: Nirbheek Chauhan <nirbheek@centricular.com>
*/
var connect_attempts = 0;
var peer_connection = null;
var rtc_configuration = {iceServers: [{urls: "stun:stun.services.mozilla.com"},
{urls: "stun:stun.l.google.com:19302"}]};
var ws_conn;
var local_stream;
var peer_id;
function getOurId() {
return Math.floor(Math.random() * (9000 - 10) + 10).toString();
}
function resetState() {
// This will call onServerClose()
ws_conn.close();
}
function handleIncomingError(error) {
setStatus("ERROR: " + error);
resetState();
}
function getVideoElement() {
return document.getElementById("stream");
}
function setStatus(text) {
console.log(text);
document.getElementById("status").textContent = text;
}
function resetVideoElement() {
var videoElement = getVideoElement();
videoElement.pause();
videoElement.src = "";
videoElement.load();
}
// SDP offer received from peer, set remote description and create an answer
function onIncomingSDP(sdp) {
console.log("Incoming SDP is "+ JSON.stringify(sdp));
peer_connection.setRemoteDescription(sdp).then(() => {
setStatus("Remote SDP set");
if (sdp.type != "offer")
return;
setStatus("Got SDP offer, creating answer");
peer_connection.createAnswer().then(onLocalDescription).catch(setStatus);
}).catch(setStatus);
}
// Local description was set, send it to peer
function onLocalDescription(desc) {
console.log("Got local description: " + JSON.stringify(desc));
peer_connection.setLocalDescription(desc).then(function() {
setStatus("Sending SDP answer");
ws_conn.send(JSON.stringify(peer_connection.localDescription));
});
}
// ICE candidate received from peer, add it to the peer connection
function onIncomingICE(ice) {
console.log("Incoming ICE: " + JSON.stringify(ice));
var candidate = new RTCIceCandidate(ice);
peer_connection.addIceCandidate(candidate).catch(setStatus);
}
function onServerMessage(event) {
console.log("Received " + event.data);
switch (event.data) {
case "HELLO":
setStatus("Registered with server, waiting for call");
return;
default:
if (event.data.startsWith("ERROR")) {
handleIncomingError(event.data);
return;
}
// Handle incoming JSON SDP and ICE messages
try {
msg = JSON.parse(event.data);
} catch (e) {
if (e instanceof SyntaxError) {
handleIncomingError("Error parsing incoming JSON: " + event.data);
} else {
handleIncomingError("Unknown error parsing response: " + event.data);
}
return;
}
// Incoming JSON signals the beginning of a call
if (peer_connection == null)
createCall(msg);
if (msg.sdp != null) {
onIncomingSDP(msg.sdp);
} else if (msg.ice != null) {
onIncomingICE(msg.ice);
} else {
handleIncomingError("Unknown incoming JSON: " + msg);
}
}
}
function onServerClose(event) {
resetVideoElement();
if (peer_connection != null) {
peer_connection.close();
peer_connection = null;
}
// Reset after a second
window.setTimeout(websocketServerConnect, 1000);
}
function onServerError(event) {
setStatus("Unable to connect to server, did you add an exception for the certificate?")
// Retry after 3 seconds
window.setTimeout(websocketServerConnect, 3000);
}
function websocketServerConnect() {
connect_attempts++;
if (connect_attempts > 3) {
setStatus("Too many connection attempts, aborting. Refresh page to try again");
return;
}
peer_id = getOurId();
setStatus("Connecting to server");
ws_conn = new WebSocket('wss://' + window.location.hostname + ':8443');
/* When connected, immediately register with the server */
ws_conn.addEventListener('open', (event) => {
document.getElementById("peer-id").textContent = peer_id;
ws_conn.send('HELLO ' + peer_id);
setStatus("Registering with server");
});
ws_conn.addEventListener('error', onServerError);
ws_conn.addEventListener('message', onServerMessage);
ws_conn.addEventListener('close', onServerClose);
var constraints = {video: true, audio: true};
// Add local stream
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => { local_stream = stream })
.catch(errorUserMediaHandler);
} else {
errorUserMediaHandler();
}
}
function onRemoteStreamAdded(event) {
videoTracks = event.stream.getVideoTracks();
audioTracks = event.stream.getAudioTracks();
if (videoTracks.length > 0) {
console.log('Incoming stream: ' + videoTracks.length + ' video tracks and ' + audioTracks.length + ' audio tracks');
getVideoElement().srcObject = event.stream;
} else {
handleIncomingError('Stream with unknown tracks added, resetting');
}
}
function errorUserMediaHandler() {
setStatus("Browser doesn't support getUserMedia!");
}
function createCall(msg) {
// Reset connection attempts because we connected successfully
connect_attempts = 0;
peer_connection = new RTCPeerConnection(rtc_configuration);
peer_connection.onaddstream = onRemoteStreamAdded;
/* Send our video/audio to the other peer */
peer_connection.addStream(local_stream);
if (!msg.sdp) {
console.log("WARNING: First message wasn't an SDP message!?");
}
peer_connection.onicecandidate = (event) => {
// We have a candidate, send it to the remote party with the
// same uuid
if (event.candidate == null) {
console.log("ICE Candidate was null, done");
return;
}
ws_conn.send(JSON.stringify({'ice': event.candidate}));
};
setStatus("Created peer connection for call, waiting for SDP");
}