Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
fcwu
GitHub Repository: fcwu/docker-ubuntu-vnc-desktop
Path: blob/develop/rootfs/usr/local/lib/web/backend/vnc/app.py
387 views
1
from __future__ import (
2
absolute_import, division, print_function, with_statement
3
)
4
import re
5
import os
6
from flask import (
7
Flask,
8
request,
9
Response,
10
jsonify,
11
abort,
12
)
13
from gevent import subprocess as gsp, spawn, sleep
14
from geventwebsocket.exceptions import WebSocketError
15
from .response import httperror
16
from .util import ignored
17
from .state import state
18
from .log import log
19
20
21
# Flask app
22
app = Flask('novnc2')
23
app.config.from_object('config.Default')
24
app.config.from_object(os.environ.get('CONFIG') or 'config.Development')
25
26
27
@app.route('/api/state')
28
@httperror
29
def apistate():
30
state.wait(int(request.args.get('id', -1)), 30)
31
state.switch_video(request.args.get('video', 'false') == 'true')
32
mystate = state.to_dict()
33
return jsonify({
34
'code': 200,
35
'data': mystate,
36
})
37
38
39
@app.route('/api/health')
40
def apihealth():
41
if state.health:
42
return 'success'
43
abort(503, 'unhealthy')
44
45
46
@app.route('/api/reset')
47
def reset():
48
if 'w' in request.args and 'h' in request.args:
49
args = {
50
'w': int(request.args.get('w')),
51
'h': int(request.args.get('h')),
52
}
53
state.set_size(args['w'], args['h'])
54
55
state.apply_and_restart()
56
57
# check all running
58
for i in range(40):
59
if state.health:
60
break
61
sleep(1)
62
log.info('wait services is ready...')
63
else:
64
return jsonify({
65
'code': 500,
66
'errorMessage': 'service is not ready, please restart container'
67
})
68
return jsonify({'code': 200})
69
70
71
@app.route('/resize')
72
@httperror
73
def apiresize():
74
state.reset_size()
75
return '<html><head><script type = "text/javascript">var h=window.location.href;window.location.href=h.substring(0,h.length-6);</script></head></html>'
76
77
78
@app.route('/api/live.flv')
79
@httperror
80
def liveflv():
81
def generate():
82
xenvs = {
83
'DISPLAY': ':1',
84
}
85
bufsize = 1024 * 1
86
framerate = 20
87
88
# sound
89
sound_cmd_input = []
90
sound_cmd_parameters = []
91
zero_latency_make_sound_not_good = [
92
'-tune', 'zerolatency',
93
]
94
95
xenvs['X_WIDTH'] = state.w
96
xenvs['X_HEIGHT'] = state.h
97
xenvs['X_WIDTH'] -= state.w % 2
98
xenvs['X_HEIGHT'] -= state.h % 2
99
100
pixels_count = xenvs['X_WIDTH'] * xenvs['X_HEIGHT']
101
# factor (720p)
102
# 383: 2400k
103
# 300: 3000k
104
# 230: 4000k
105
factor = 265
106
maxbitrate_cmd = [
107
'-maxrate', str(int(pixels_count / factor)) + 'k',
108
'-bufsize', str(int(pixels_count / factor / 3)) + 'k'
109
]
110
111
# TODO move to global
112
# get default source
113
sound_cmd_input = [
114
'-f', 'alsa',
115
'-i', 'hw:2,1',
116
]
117
sound_cmd_parameters = [
118
'-ar', '44100',
119
'-c:a', 'mp3',
120
]
121
# flv.js report error if enabling hw acceleration
122
# hwaccel_dev = ['-vaapi_device', '/dev/dri/renderD128']
123
# hwaccel_if = ['-vf', 'format=nv12,hwupload']
124
# vcodec = 'h264_vaapi'
125
hwaccel_dev = []
126
hwaccel_if = []
127
vcodec = 'libx264'
128
# zero_latency_make_sound_not_good = []
129
# sound_cmd_parameters = []
130
# sound_cmd_input = []
131
cmd = ['/usr/local/ffmpeg/ffmpeg'] + sound_cmd_input + hwaccel_dev + [
132
'-video_size', '{X_WIDTH}x{X_HEIGHT}'.format(**xenvs),
133
'-framerate', '{}'.format(framerate),
134
'-f', 'x11grab', '-draw_mouse', '1',
135
'-i', '{DISPLAY}'.format(**xenvs),
136
] + hwaccel_if + [
137
'-r', '{}'.format(framerate),
138
'-g', '{}'.format(framerate),
139
'-flags:v', '+global_header',
140
'-vcodec', vcodec,
141
'-preset', 'ultrafast',
142
'-b_strategy', '0',
143
'-pix_fmt', 'yuv420p',
144
'-bsf:v', 'dump_extra=freq=e',
145
] + maxbitrate_cmd \
146
+ sound_cmd_parameters + zero_latency_make_sound_not_good + [
147
'-f', 'flv', 'pipe:1',
148
]
149
log.info('command: ' + ' '.join(cmd))
150
pobj = gsp.Popen(
151
cmd,
152
stdout=gsp.PIPE,
153
stderr=gsp.PIPE,
154
env={k: str(v) for k, v in xenvs.items()},
155
)
156
157
def readerr(f):
158
reobj = re.compile(r'bitrate=(\S+)')
159
global av_bitrate
160
try:
161
while True:
162
buf = f.read(bufsize)
163
if len(buf) == 0:
164
break
165
patterns = reobj.findall(buf.decode('utf-8', 'ignore'))
166
if len(patterns) > 0:
167
av_bitrate = patterns[-1]
168
# log.info(str(buf))
169
except Exception as e:
170
log.exception(e)
171
172
preaderr = None
173
try:
174
preaderr = spawn(readerr, pobj.stderr)
175
try:
176
while True:
177
buf = pobj.stdout.read(bufsize)
178
if len(buf) == 0:
179
break
180
# ws.send(buf)
181
yield buf
182
except WebSocketError:
183
pass
184
except Exception as e:
185
log.exception(e)
186
finally:
187
with ignored(Exception):
188
pobj.kill()
189
preaderr.join()
190
except Exception as e:
191
log.exception(e)
192
finally:
193
log.info('exiting')
194
with ignored(Exception):
195
pobj.kill()
196
with ignored(Exception):
197
preaderr.kill()
198
log.info('exited')
199
return Response(generate(), mimetype='video/x-flv')
200
201
202
if __name__ == '__main__':
203
app.run(host=app.config['ADDRESS'], port=app.config['PORT'])
204
205