diff --git a/utils/stream/rtcstream.js b/utils/stream/rtcstream.js new file mode 100644 index 00000000..bfe529ce --- /dev/null +++ b/utils/stream/rtcstream.js @@ -0,0 +1,187 @@ +'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'); + 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'); + } + +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_chat.js b/utils/stream/ws_chat.js new file mode 100644 index 00000000..e58c1353 --- /dev/null +++ b/utils/stream/ws_chat.js @@ -0,0 +1,80 @@ +'use strict'; + +// Create websocket for communication +const websocket = new WebSocket( + 'ws://' + + window.location.host + + '/ws/chat/' + + room_name + + '/' +); +console.log('Websocket created for room: ', room_name); + +let token = new Date().getTime() + Math.random(); +console.log('token: ',token); + +websocket.onmessage = function(event){ + let message_data = JSON.parse(event.data); + console.log('WebSocket received: ', message_data); + + // If the content type from the websocket is chat_message, + // the text field is appended to the chat box + if(message_data['type'] == 'chat_message'){ + if(Number(token) !== Number(message_data['token'])){ + console.log('Escribir mensaje'); + add_message('receive', message_data['message']); + } + }else if(message_data['type'] == 'candidate'){ + console.log('Candidate received'); + addIceCandidate(message_data['candidate']); + }else if(message_data['type'] == 'offer'){ + console.log('Offer received'); + startRemoteStream(message_data['offer']); + }else if(message_data['type'] == 'answer'){ + console.log('Answer received'); + setAnswerDescription(message_data['answer']); + }else if(message_data['type'] == 'denied'){ + console.log('Denied connection'); + window.location.pathname = '/'; + }else if(message_data['type'] == 'checkusers'){ + if(message_data['users']){ + startLocalStream() + }else{ + console.log('No peer connected') + } + } +}; + +function add_message(direction, message){ + let chat_log = document.querySelector('#chat-log'); + + if(direction === 'send'){ + chat_log.innerHTML += ('

[You] ' + message + '

'); + }else if(direction === 'receive'){ + chat_log.innerHTML += ('

[Sender] ' + message + '

'); + } +} + +// Focus al input text +document.querySelector('#chat-message-input').focus(); + +// On Enter pressed, the WebSocket is sent +document.querySelector('#chat-message-input').onkeyup = function(e) { + if (e.keyCode === 13) { // enter, return + document.querySelector('#chat-message-submit').click(); + } +}; + +// Al hacer click en el botón de enviar el websocket se envía +document.querySelector('#chat-message-submit').onclick = function(e) { + const messageInputDom = document.querySelector('#chat-message-input'); + const message = messageInputDom.value; + websocket.send(JSON.stringify({ + 'type':'chat_message', + 'token':token, + 'message': message, + })); + console.log('WebSocket sent!'); + add_message('send', message); + messageInputDom.value = ''; +}; \ No newline at end of file