diff --git a/utils/stream/rtc_stream.js b/utils/stream/rtc_stream.js new file mode 100644 index 00000000..dd6bcdb5 --- /dev/null +++ b/utils/stream/rtc_stream.js @@ -0,0 +1,189 @@ +'use strict'; + +let stream; +let localStream; +let localPeer; +let remotePeer; + +const constraints = { + video: { + mediaSource: "screen", // whole screen sharing + //mediaSource: "window", // choose a window to share + //mediaSource: "application", // choose a window to share + width: {max: '1920'}, + height: {max: '1080'}, + frameRate: {max: '10'} + } +}; + +const offerOptions = { + OfferToReceiveAudio: 1, + OfferToReceiveVideo: 1 +}; + +var configuration = { + "iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] +}; + +let iceCandidates = []; + + +function checkUsers() { + streamws.send( + JSON.stringify({ + type: "check-users", + }) + ); +} + +// Empezar el streaming local: +// 1. Desactivar el botón de start, localVideo.play(), +// obtener el media stream, establecer localVideo.srcObject = stream y +// dar valor a la variable localStream con el stream que hemos obtenido. +// 2. Crear la instancia de la RTCPeerConnection +// 3. Crear el handler del evento icecandidate +// 4. Añadir los media tracks del localStream para que sean enviadas +// al remote peer +// 5. Crear la oferta de candidato en el extremo local +// 6. Crear la descripción local de localPeer +async function startLocalStream(){ + console.log('[NOVNC] empezando stream local'); + // Obtener el stream del usuario + if(!canvas){ + console.log('[NOVNC] Error: no CANVAS'); + users = false; + return + } + stream = canvas.captureStream(); + + // Mostrar el video + localVideo.srcObject = stream; + + // Establecer cual es el localStream + localStream = stream; + + // Crear instancia de RTCPeerConnection + localPeer = new RTCPeerConnection(configuration); + + // Send candidate to the remote peer + localPeer.addEventListener('icecandidate', event => onIceCandidate(localPeer, event)); + + // Añadir los media tracks que genera el localStream a + // las tracks de localPeer para enviarlas al remote peer + localStream.getTracks().forEach(track => localPeer.addTrack(track, localStream)); + console.log('[NOVNC] Adding tracks'); + + // Crear oferta + localPeer.createOffer(offerOptions); + + // Establecer la descripcion de localPeer + localPeer.addEventListener('negotiationneeded', addLocalDescription(localPeer)); +} + +// Cuando recibo un socket del tipo offer: +// 1. Creo la instancia de la conexión RTC del extemo remoto +// 2. Creo el hadler para el evento icecandidate +// 3. Handler para recibir los mediatracks +// 4. Notifico si el estado del remotePeer cambia +// 5. Establezco la descripcion remota del extremo remoto +// 6. Crear respuesta y establecer la descripcion local del extremo remoto +// 7. Enviar un websocket con la respuesta al otro peer +async function startRemoteStream(offer){ + // Creo la instancia de la conexión remota + remotePeer = new RTCPeerConnection(configuration); + console.log('[NOVNC] RTC Remote Peer conection created'); + + // Handler para el evento icecandidate + // Send candidate to the remote peer + remotePeer.addEventListener('icecandidate', event => onIceCandidate(remotePeer, event)); + + // Recibir los media tracks del extremo local + remotePeer.ontrack = gotRemoteStream; + + // Notificar si el estado remoto cambia + remotePeer.oniceconnectionstatechange = () => console.log('Remote ice state ' + remotePeer.iceConnectionState); + + // Set remote description + try { + remotePeer.setRemoteDescription(offer); + console.log('[NOVNC] Remote description set'); + } catch (error) { + console.log(`[NOVNC] Set remote description error: ${error}`); + } + + try { + // Create answer + const answer = await remotePeer.createAnswer(); + // Set local description of remote peer + remotePeer.setLocalDescription(answer); + // Signal localStream peer with the answer + signalRemotePeer(JSON.stringify({ + 'type':'answer', + 'answer':answer + })); + }catch(error){ + console.log(`[NOVNC] Failed to create local session description ${error}`); + } + + +} + +function addIceCandidate(candidate) { + //var candidate = new RTCIceCandidate(cand) + if (localPeer === undefined) { + remotePeer.addIceCandidate(candidate); + } else { + localPeer.addIceCandidate(candidate); + } + console.log('[NOVNC] Candidate Added'); + // Crear set + } + +async function setAnswerDescription(answer){ + try { + localPeer.setRemoteDescription(answer); + console.log("[NOVNC] Local Peer remote description set"); + }catch(error){ + console.log(`[NOVNC] Failed setting local peer remote description ${error}`); + } +} + +// Añadir los candidatos a la lista de candidatos +function onIceCandidate(peer, event){ + if(event.candidate != null && event.candidate != undefined){ + console.log(`[NOVNC] onicecandidate event on ${peer}`); + // Send the candidate to the other peer via WebSockets + signalRemotePeer(JSON.stringify({ + 'type':'candidate', + 'candidate':event.candidate + })); + } +} + +async function addLocalDescription(peer) { + try { + const offer = await peer.createOffer(offerOptions); + peer.setLocalDescription(offer); + console.log(`[NOVNC] ${peer} local description set`); + signalRemotePeer(JSON.stringify({ + 'type':'offer', + 'offer':offer + })); + } catch(err) { + console.log(`[NOVNC] ${peer} error setting local description`); + } +} + +function signalRemotePeer(data){ + streamws.send(data); + console.log('[NOVNC] Signaling remote peer...'); +} + +function gotRemoteStream(e){ + console.log('[NOVNC] Got remote video: ', e.streams[0]); + if (remoteVideo.srcObject !== e.streams[0]) { + //rightVideo.play(); + remoteVideo.srcObject = e.streams[0]; + console.log('[NOVNC] pc2 received remote stream'); + } +} \ No newline at end of file diff --git a/utils/stream/ws_stream.js b/utils/stream/ws_stream.js new file mode 100644 index 00000000..d88cb83f --- /dev/null +++ b/utils/stream/ws_stream.js @@ -0,0 +1,40 @@ +'use strict'; + +document.addEventListener('oncreateroom', function(){ + // Creo WS de stream + streamws = new WebSocket( + 'ws://' + + '127.0.0.1:8000' + + '/ws/stream/' + + room_name + + '/' + ); + console.log('[NOVNC] WebSocket streamws creado'); + // Handler streamws + streamws.onmessage = function(e){ + let message_data = JSON.parse(event.data); + console.log('[NOVNC] streamws: ', message_data); + if(message_data['type'] == 'checkusers'){ + if(message_data['users']){ + console.log('[NOVNC] usuarios correctos'); + users = true; + startLocalStream(); + }else{ + console.log('[NOVNC] usuarios incorrectos'); + users = false; + } + }else if(message_data['type'] == 'candidate'){ + console.log('[NOVNC] Candidate received'); + addIceCandidate(message_data['candidate']); + }else if(message_data['type'] == 'offer'){ + console.log('[NOVNC] Offer received'); + startRemoteStream(message_data['offer']); + }else if(message_data['type'] == 'answer'){ + console.log('[NOVNC] Answer received'); + setAnswerDescription(message_data['answer']); + }else if(message_data['type'] == 'denied'){ + console.log('[NOVNC] Denied connection'); + window.location.pathname = '/'; + } + } +}); \ No newline at end of file