123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- import mqtt from 'mqtt'
- import uuid from 'uuid/v4'
- const ID = uuid()
- const separator = '( ͡° ͜ʖ ͡°)'
- const RTC = {}
- const client = mqtt.connect('wss://broker.peerjs.com')
- let localStream
- client.on('message', async (topic, message) => {
- const msg = message.toString()
- let cmd
- let payload
- if (msg.includes(separator)) {
- cmd = msg.split(separator)[0]
- payload = msg.split(separator)[1]
- } else {
- cmd = msg
- }
- switch (cmd) {
- case 'joinroom': {
- if (payload !== ID) client.publish(payload, `roomuser${separator}${ID}/${topic}`)
- break
- }
- case 'roomuser':
- console.log('roomuser payload', payload)
- let p = payload.split('/')
- const id = p.shift()
- call(id, p.join('/'))
- break
- case 'ice': {
- const ice = JSON.parse(payload)
- const caller = ice.id
- const room = ice.room
- try {
- console.log('ICE', ice)
- const candidate = new global.RTCIceCandidate(ice.candidate)
- console.log('CANDIDATE', candidate)
- if (candidate) {
- RTC[room][caller].addIceCandidate(candidate)
- }
- } catch (e) {
- console.log(e.message)
- }
- break
- }
- case 'answer': {
- console.log('ANSWER')
- const a = JSON.parse(payload)
- const caller = a.id
- const room = a.room
- const answer = a.answer
- try {
- await RTC[room][caller].setRemoteDescription(answer)
- } catch (e) {
- console.log(e.message)
- }
- break
- }
- case 'offer': {
- console.log('OFFER')
- const p = JSON.parse(payload)
- const caller = p.id
- const room = p.room
- const offer = p.offer
- const peerconn = new global.RTCPeerConnection({
- iceServers: [
- { urls: 'stun:0.peerjs.com:3478' },
- { urls: 'turn:0.peerjs.com:3478', username: 'peerjs', credential: 'peerjsp' }
- ]
- })
- RTC[room][caller] = peerconn
- peerconn.onicecandidate = event => {
- console.log('local ice', event)
- if (event.candidate) {
- client.publish(caller, `ice${separator}${JSON.stringify({
- id: ID,
- room,
- candidate: event.candidate
- })}`)
- }
- }
- peerconn.onsignalingstatechange = event => {
- console.log('STATE', peerconn.signalingState)
- }
- peerconn.oniceconnectionstatechange = event => {
- console.log('ICE STATE', peerconn.iceConnectionState)
- }
- peerconn.ontrack = event => {
- RTC[room].onRemoteStream(event.streams[0])
- }
- peerconn.onremovetrack = event => {
- console.log('ON REMOVE TRACK', event)
- }
- peerconn.ondatachannel = dataChannel => {
- RTC[room].onDataStream(dataChannel.channel)
- }
- try {
- const desc = new global.RTCSessionDescription(offer)
- await peerconn.setRemoteDescription(desc)
- // dataChannel comes from the offer, must not create one
- if (room.split('/')[0] !== 'data') {
- await processMedia(caller, room.split('/')[0], room, peerconn)
- }
- const answer = await peerconn.createAnswer()
- await peerconn.setLocalDescription(answer)
- client.publish(caller, `answer${separator}${JSON.stringify({
- id: ID,
- room: room,
- answer: peerconn.localDescription
- })}`)
- } catch (e) {
- console.log('setRemoteDescription', e.message)
- }
- }
- }
- })
- client.on('connect', () => {
- client.subscribe(ID, err => {
- if (err) console.error(err)
- })
- })
- function Call (room, onRemoteStream, onLocalStream, onDataStream) {
- RTC[room] = {
- onRemoteStream,
- onLocalStream,
- onDataStream
- }
- client.subscribe(room, err => {
- if (err) console.error(err)
- })
- console.log('publish to', room, `joinroom${separator}${ID}`)
- client.publish(room, `joinroom${separator}${ID}`)
- }
- export default class Peer {
- constructor (room, { mode, secret, onRemoteStream, onLocalStream, onDataStream }) {
- const _room = `${mode || 'video'}/${room}${secret}`
- this.room = _room
- Call(_room, onRemoteStream, onLocalStream, onDataStream)
- }
- video (yes) {
- const videoTrack = localStream.getVideoTracks()
- videoTrack[0].enabled = yes
- }
- close () {
- for (let k of Object.keys(RTC[this.room])) {
- close(RTC[this.room][k])
- }
- delete RTC[this.room]
- }
- }
- window.Peer = Peer
- async function call (id, room) {
- const peerconn = new global.RTCPeerConnection({
- iceServers: [
- { urls: 'stun:0.peerjs.com:3478' },
- { urls: 'turn:0.peerjs.com:3478', username: 'peerjs', credential: 'peerjsp' }
- ]
- })
- console.log('ROOM', room)
- RTC[room][id] = peerconn
- peerconn.onnegotiationneeded = async () => {
- const offer = await peerconn.createOffer()
- try {
- await peerconn.setLocalDescription(offer)
- } catch (e) {
- console.log('setLocalDescription', e.message)
- }
- client.publish(id, `offer${separator}${JSON.stringify({
- id: ID,
- room,
- offer: peerconn.localDescription
- })}`)
- }
- peerconn.oniceconnectionstatechange = event => {
- if (peerconn.iceConnectionState === 'disconnected') {
- console.error('iceConnectionState disconnected')
- }
- }
- peerconn.ontrack = event => {
- console.log('new track', event.streams[0])
- RTC[room].onRemoteStream(event.streams[0])
- }
- peerconn.onremovetrack = event => {
- console.log('ON REMOVE TRACK', event)
- }
- peerconn.onicecandidate = (event) => {
- if (event.candidate) {
- client.publish(id, `ice${separator}${JSON.stringify({
- id: ID,
- room: room,
- candidate: event.candidate
- })}`)
- }
- }
- const mode = room.split('/')[0]
- processMedia(id, mode, room, peerconn)
- }
- async function processMedia (id, mode, room, peerconn) {
- switch (mode) {
- case 'audio': {
- if (!localStream) {
- localStream = await navigator.mediaDevices.getUserMedia({
- video: false,
- audio: true
- })
- RTC[room].onLocalStream(localStream)
- }
- const tracks = localStream.getTracks()
- for (let t of tracks) {
- peerconn.addTrack(t, localStream)
- }
- break
- }
- case 'video': {
- if (!localStream) {
- localStream = await navigator.mediaDevices.getUserMedia({
- video: true,
- audio: true
- })
- RTC[room].onLocalStream(localStream)
- }
- const videoTracks = localStream.getVideoTracks()
- RTC[room].videoSender = peerconn.addTrack(videoTracks[0], localStream)
- const audioTracks = localStream.getAudioTracks()
- RTC[room].audioSender = peerconn.addTrack(audioTracks[0], localStream)
- break
- }
- case 'data':
- let dataChannel = peerconn.createDataChannel(id)
- dataChannel.addEventListener('open', () => {
- RTC[room].onDataStream(dataChannel)
- })
- }
- }
- function close (mediaConnection) {
- try {
- mediaConnection.ontrack = null
- mediaConnection.onremovetrack = null
- mediaConnection.onremovestream = null
- mediaConnection.onicecandidate = null
- mediaConnection.oniceconnectionstatechange = null
- mediaConnection.onsignalingstatechange = null
- mediaConnection.onicegatheringstatechange = null
- mediaConnection.onnegotiationneeded = null
- mediaConnection.close()
- mediaConnection = null
- } catch (e) {
- console.log('Trying to close', mediaConnection)
- }
- }
|