Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for portal / server abstraction #39

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ RUN apt-get update && \
fonts-noto \
fonts-nanum \
pulseaudio \
alsa-utils \
x11-session-utils \
ffmpeg \
sudo \
socat \
grep \
procps \
chromium
Expand Down
46 changes: 36 additions & 10 deletions src/browser/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ChildProcess } from 'child_process'

import { convertKeyCode } from '../utils/keyboard.utils'
import { xvfb, pulseaudio, openbox, chromium, ffmpeg, ffmpegaudio, xdotool } from './utils'

import { signToken } from '../utils/generate.utils'
import { fetchPortalId } from '../utils/helpers.utils'

export default class VirtualBrowser {
width: number
Expand All @@ -17,6 +18,10 @@ export default class VirtualBrowser {
xdoin: any
input: object

ffmpeg: ChildProcess
ffmpegAudio: ChildProcess
isStreaming: boolean

constructor(width: number, height: number, videoBitrate: string, videoFps: string, audioBitrate: string, startupUrl: string, bitDepth: number) {
this.width = width
this.height = height
Expand Down Expand Up @@ -44,11 +49,6 @@ export default class VirtualBrowser {
console.log('Setting up chromium...')
this.setupChromium()

console.log('Setting up ffmpeg...')
this.setupFfmpeg()
if (process.env.AUDIO_ENABLED !== 'false')
this.setupFfmpegAudio()

console.log('Setting up xdotool...')
const { stdin: xdoin } = xdotool(env)
this.xdoin = xdoin
Expand All @@ -59,21 +59,47 @@ export default class VirtualBrowser {
}
})

private setupFfmpeg = () => {
ffmpeg(this.env, signToken({ id: fetchPortalId() }, process.env.STREAMING_KEY || process.env.APERTURE_KEY),
beginStreams = (id: string) => {
this.isStreaming = true

console.log('Setting up ffmpeg...')
this.setupFfmpeg(id)

if (process.env.AUDIO_ENABLED !== 'false')
this.setupFfmpegAudio(id)
}

private setupFfmpeg = (id: string) => {
// TODO: Add server id
this.ffmpeg = ffmpeg(this.env, signToken({ id }, process.env.STREAMING_KEY || process.env.APERTURE_KEY),
this.width, this.height, this.videoFps, this.videoBitrate).on('close', () => {
if(!this.isStreaming) return

console.log('ffmpeg has suddenly stopped - attempting a restart')
setTimeout(this.setupFfmpeg, 1000)
})
}
private setupFfmpegAudio = () => {
ffmpegaudio(this.env, signToken({ id: fetchPortalId() }, process.env.STREAMING_KEY || process.env.APERTURE_KEY),
private setupFfmpegAudio = (id: string) => {
// TODO: Add server id
this.ffmpegAudio = ffmpegaudio(this.env, signToken({ id }, process.env.STREAMING_KEY || process.env.APERTURE_KEY),
this.audioBitrate).on('close', () => {
if(!this.isStreaming) return

console.log('ffmpeg audio has suddenly stopped - attempting a restart')
setTimeout(this.setupFfmpegAudio, 1000)
})
}

endStreams = () => {
this.isStreaming = false

if(this.ffmpeg)
this.ffmpeg.kill()

if(this.ffmpegAudio)
this.ffmpegAudio.kill()
}

// ToDo: Add a communication to the portals WS that the portal is stopping (closed the browser),
// then stop it after Chromium is closed for a normal shutdown.
private setupChromium = () => chromium(this.env, this.startupUrl).on('close', this.setupChromium)
Expand Down
2 changes: 1 addition & 1 deletion src/browser/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const ffmpeg = (env: NodeJS.ProcessEnv, token: string, width: number, hei
})

export const ffmpegaudio = (env: NodeJS.ProcessEnv, token: string, bitrate: string) => spawn('ffmpeg', [
'-f', 'alsa',
'-f', 'pulse',
'-ac', '2',
'-ar', '44100',
'-i', 'default',
Expand Down
42 changes: 33 additions & 9 deletions src/clients/portals.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import WebSocket from 'ws'
import VirtualBrowser from '../browser'

import { signToken } from '../utils/generate.utils'
import { fetchPortalId } from '../utils/helpers.utils'
import { fetchHostname } from '../utils/helpers.utils'
import createWebSocket, { WSEvent } from '../config/websocket.config'

const CONTROLLER_EVENT_TYPES = ['KEY_DOWN', 'KEY_UP', 'PASTE_TEXT', 'MOUSE_MOVE', 'MOUSE_SCROLL', 'MOUSE_DOWN', 'MOUSE_UP']

export default class WRTCClient {
export default class PortalsClient {
id: string

peers: Map<string, any>
browser: VirtualBrowser
websocket: WebSocket
Expand All @@ -23,9 +25,7 @@ export default class WRTCClient {
const websocket = createWebSocket()
this.websocket = websocket

websocket.addEventListener('open', () => {
this.emitBeacon()
})
websocket.addEventListener('open', this.emitBeacon)

websocket.addEventListener('message', ({ data }) => {
let json: any
Expand All @@ -41,25 +41,49 @@ export default class WRTCClient {

websocket.addEventListener('close', () => {
this.websocket = null
this.browser.endStreams()

console.log('Attempting reconnect to @cryb/portals via WS')
setTimeout(this.setupWebSocket, 2500)
})
}

emitBeacon = () => {
console.log('emitting beacon to portals server')
console.log('emitting beacon to portals server', this.id)

const token = signToken(this.fetchBeaconMetadata(), process.env.PORTALS_KEY)
this.send({ op: 2, d: { token, type: 'server' } })
}

private fetchBeaconMetadata = () => {
let metadata = {}

const id = fetchPortalId(), token = signToken({ id }, process.env.PORTALS_KEY)
this.send({ op: 2, d: { token, type: 'portal' } })
if(this.id) metadata = {...metadata, id: this.id}
if(fetchHostname()) metadata = {...metadata, hostname: fetchHostname()}

return metadata
}

reset = () => {
// TODO: Add reset method to clear Chromium history
}

handleMessage = (message: WSEvent) => {
const { op, d, t } = message

if(op === 0)
if(op === 0) {
if(CONTROLLER_EVENT_TYPES.indexOf(t) > -1)
this.browser.handleControllerEvent(d, t)
else if(t === 'RESET')
this.reset()
} else if(op === 10) {
console.log(d)

const { id } = d
this.id = id

this.browser.beginStreams(id)
}
}

send = (object: WSEvent) => this.websocket.send(JSON.stringify(object))
Expand Down
2 changes: 1 addition & 1 deletion src/config/websocket.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default () => {

ws.addEventListener('open', () => console.log('Connected to @cryb/portals via WS'))
ws.addEventListener('close', () => console.log('Disconnected from @cryb/portals WS'))
ws.addEventListener('error', error => console.error('Error during WS connection to @cryb/portals: ', error))
ws.addEventListener('error', () => console.error('Error during WS connection to @cryb/portals WS'))

return ws
}
16 changes: 8 additions & 8 deletions src/utils/helpers.utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import os from 'os'
import { argv } from 'yargs'
import { hostname as _hostname } from 'os'

export const fetchPortalId = () => {
if(argv.portalId) return argv.portalId as string
export const fetchHostname = () => {
const hostname = _hostname()
if(!hostname) return null

const machineId = os.hostname().split('-')[1]
if(!machineId) return os.hostname() as string
const parts = hostname.split('-')
if(parts[0] !== 'cryb_portal') return null

return machineId as string
}
return hostname
}