282 lines
No EOL
9.8 KiB
JavaScript
282 lines
No EOL
9.8 KiB
JavaScript
import io from 'socket.io-client';
|
|
import kurentoUtils from 'kurento-utils';
|
|
import { EventEmitter } from 'events';
|
|
|
|
(async () => {
|
|
|
|
// RTCPeerConnection
|
|
let pc;
|
|
|
|
// Send permission dialog to client
|
|
const openMediaDevices = async (constraints) => {
|
|
return await navigator.mediaDevices.getUserMedia(constraints);
|
|
}
|
|
|
|
try {
|
|
const stream = await openMediaDevices({ 'video': true, 'audio': true });
|
|
console.log('Got MediaStream:', stream);
|
|
|
|
function updateList(elements, type) {
|
|
const listElement = document.querySelector(type == 'video' ? 'select#availableCameras' : 'select#availableMicrophones');
|
|
listElement.innerHTML = '';
|
|
elements.forEach(camera => {
|
|
const cameraOption = document.createElement('option');
|
|
cameraOption.label = camera.label;
|
|
cameraOption.value = camera.deviceId;
|
|
listElement.add(cameraOption);
|
|
});
|
|
}
|
|
|
|
// Fetch an array of devices of a certain type
|
|
async function getConnectedDevices(type) {
|
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
return devices.filter(device => device.kind === type)
|
|
}
|
|
|
|
// Listen for changes to media devices and update the list accordingly
|
|
navigator.mediaDevices.addEventListener('devicechange', async _ => {
|
|
updateList(await getConnectedDevices('videoinput'), 'video');
|
|
updateList(await getConnectedDevices('audioinput'), 'audio');
|
|
});
|
|
|
|
updateList(await getConnectedDevices('videoinput'), 'video');
|
|
updateList(await getConnectedDevices('audioinput'), 'audio');
|
|
|
|
|
|
async function playVideoFromCamera() {
|
|
try {
|
|
const videoSelect = document.querySelector('select#availableCameras');
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
video: {
|
|
deviceId: videoSelect.options[videoSelect.selectedIndex].value,
|
|
// width: 1920,
|
|
// height: 1080,
|
|
frameRate: 30,
|
|
}
|
|
});
|
|
const videoElement = document.querySelector('video#localVideo');
|
|
videoElement.srcObject = stream;
|
|
} catch (error) {
|
|
console.error('Error opening video camera.', error);
|
|
}
|
|
}
|
|
|
|
// document.querySelector('button#startVideo').addEventListener('click', () => playVideoFromCamera());
|
|
|
|
} catch (error) {
|
|
console.error('Error accessing media devices.', error);
|
|
}
|
|
|
|
|
|
// console.log('Make Call');
|
|
const socket = io();
|
|
socket.on('connect', function () {
|
|
socket.on('startResponse', startResponse);
|
|
});
|
|
|
|
// socket.emit('events', { test: 'test' });
|
|
// socket.emit('start', {});
|
|
// });
|
|
|
|
// socket.on('exception', function (data) { // Not working?
|
|
// console.log('event', data);
|
|
// });
|
|
|
|
// socket.on('disconnect', function () {
|
|
// console.log('Disconnected');
|
|
// socket.close(); // Good idea?
|
|
// });
|
|
|
|
var webRtcPeer;
|
|
|
|
document.querySelector('button#startCall').addEventListener('click', () => start());
|
|
document.querySelector('button#stopCall').addEventListener('click', () => stop());
|
|
|
|
async function start() {
|
|
console.log('Starting video call ...')
|
|
|
|
console.log('Creating WebRtcPeer and generating local sdp offer ...');
|
|
|
|
pc = new RTCPeerConnection({
|
|
iceServers: [
|
|
{
|
|
urls: 'stun:stun.cliffbreak.de',
|
|
credential: '8dd2a7643a840ff4bb10e82069b6feceaf6d344a19a590ffc062fa10ea34687b',
|
|
},
|
|
]
|
|
});
|
|
|
|
const dataChannel = pc.createDataChannel('test'); //Unneeded?
|
|
let videoStream;
|
|
|
|
try {
|
|
const videoSelect = document.querySelector('select#availableCameras');
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
video: {
|
|
deviceId: videoSelect.options[videoSelect.selectedIndex].value,
|
|
// width: 1920,
|
|
// height: 1080,
|
|
frameRate: 30,
|
|
}
|
|
});
|
|
const videoElement = document.querySelector('video#localVideo');
|
|
console.log(stream);
|
|
videoElement.srcObject = stream;
|
|
videoStream = stream;
|
|
} catch (error) {
|
|
console.error('Error opening video camera.', error);
|
|
}
|
|
|
|
let candidategatheringdone = false;
|
|
const candidatesQueueOut = [];
|
|
pc.onicecandidate = (event) => {
|
|
const candidate = event.candidate;
|
|
if (EventEmitter.listenerCount('icecandidate') || EventEmitter
|
|
.listenerCount('candidategatheringdone')) {
|
|
candidategatheringdone = false;
|
|
} else if (!candidategatheringdone) {
|
|
candidatesQueueOut.push(candidate);
|
|
if (!candidate) {
|
|
candidategatheringdone = true;
|
|
console.log(candidatesQueueOut);
|
|
while (candidatesQueueOut.length) {
|
|
const candidate = candidatesQueueOut.shift();
|
|
if (!!candidate)
|
|
onIceCandidate(candidate);
|
|
}
|
|
for (const track of videoStream.getVideoTracks()) {
|
|
pc.addTrack(track, videoStream);
|
|
}
|
|
|
|
// pc.addStream(videoStream);
|
|
}
|
|
}
|
|
};
|
|
|
|
pc.createOffer().then((offer) => {
|
|
console.log('Created SDP offer');
|
|
console.log(offer);
|
|
const newOffer = new RTCSessionDescription({
|
|
type: offer.type,
|
|
sdp: removeFIDFromOffer(offer.sdp) + getSimulcastInfo(videoStream),
|
|
});
|
|
return pc.setLocalDescription(newOffer);
|
|
}).then(() => {
|
|
const localDescription = pc.localDescription;
|
|
console.log('Local description set: ' + localDescription.sdp);
|
|
onOffer(null, localDescription.sdp);
|
|
}).catch(onOffer);
|
|
|
|
|
|
function removeFIDFromOffer(sdp) {
|
|
var n = sdp.indexOf("a=ssrc-group:FID");
|
|
|
|
if (n > 0) {
|
|
return sdp.slice(0, n);
|
|
} else {
|
|
return sdp;
|
|
}
|
|
}
|
|
|
|
function getSimulcastInfo(videoStream) {
|
|
var videoTracks = videoStream.getVideoTracks();
|
|
if (!videoTracks.length) {
|
|
logger.warn('No video tracks available in the video stream')
|
|
return ''
|
|
}
|
|
var lines = [
|
|
'a=x-google-flag:conference',
|
|
'a=ssrc-group:SIM 1 2 3',
|
|
'a=ssrc:1 cname:localVideo',
|
|
'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,
|
|
'a=ssrc:1 mslabel:' + videoStream.id,
|
|
'a=ssrc:1 label:' + videoTracks[0].id,
|
|
'a=ssrc:2 cname:localVideo',
|
|
'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,
|
|
'a=ssrc:2 mslabel:' + videoStream.id,
|
|
'a=ssrc:2 label:' + videoTracks[0].id,
|
|
'a=ssrc:3 cname:localVideo',
|
|
'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,
|
|
'a=ssrc:3 mslabel:' + videoStream.id,
|
|
'a=ssrc:3 label:' + videoTracks[0].id
|
|
];
|
|
|
|
lines.push('');
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// var options = {
|
|
// localVideo: document.querySelector('video#localVideo'),
|
|
// remoteVideo: document.querySelector('video#remoteVideo'),
|
|
// onicecandidate: onIceCandidate
|
|
// }
|
|
|
|
// webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, function (error) {
|
|
// if (error) return onError(error);
|
|
// this.generateOffer(onOffer);
|
|
// });
|
|
|
|
}
|
|
|
|
function onIceCandidate(candidate) {
|
|
console.log('Local candidate' + JSON.stringify(candidate));
|
|
socket.emit('onIceCandidate', JSON.stringify({ candidate }));
|
|
}
|
|
|
|
function onOffer(error, offerSdp) {
|
|
if (error) return onError(error);
|
|
|
|
console.info('Invoking SDP offer callback function ' + location.host);
|
|
socket.emit('start', JSON.stringify({ sdpOffer: offerSdp }));
|
|
}
|
|
|
|
function onError(error) {
|
|
console.error(error);
|
|
}
|
|
|
|
function startResponse(message) {
|
|
console.log('SDP answer received from server. Processing ...');
|
|
const sdpAnswer = JSON.parse(message).sdpAnswer;
|
|
// webRtcPeer.processAnswer(sdpAnswer);
|
|
let answer = new RTCSessionDescription({
|
|
type: 'answer',
|
|
sdp: sdpAnswer,
|
|
});
|
|
|
|
|
|
pc.setRemoteDescription(answer, () => {
|
|
const remoteVideo = document.querySelector('video#remoteVideo');
|
|
// remoteVideo.pause();
|
|
|
|
|
|
// pc.getRemoteStreams = function () {
|
|
// var stream = new MediaStream();
|
|
// pc.getReceivers().forEach(function (sender) {
|
|
// stream.addTrack(sender.track);
|
|
// });
|
|
// return [stream];
|
|
// };
|
|
|
|
// console.table(pc.getRemoteStreams());
|
|
// const stream = pc.getRemoteStreams()[0];
|
|
// console.log(stream);
|
|
pc.ontrack = function (event) {
|
|
console.log('ontrack');
|
|
console.log(event);
|
|
remoteVideo.srcObject = event.streams[0];
|
|
}
|
|
// remoteVideo.load();
|
|
});
|
|
}
|
|
|
|
function stop() {
|
|
console.log('Stopping video call ...');
|
|
socket.emit('stop');
|
|
// TODO:::
|
|
if (webRtcPeer) {
|
|
webRtcPeer.dispose();
|
|
webRtcPeer = null;
|
|
}
|
|
}
|
|
})(); |