Update app.py
Browse files
app.py
CHANGED
|
@@ -10,23 +10,13 @@ import threading
|
|
| 10 |
import numpy as np
|
| 11 |
import psutil
|
| 12 |
import ctypes
|
| 13 |
-
import traceback
|
| 14 |
from ctypes import c_int, c_void_p, c_char_p, POINTER, c_ubyte, c_bool
|
| 15 |
from aiohttp import web
|
| 16 |
-
|
| 17 |
-
# CORRECT IMPORTS FOR AIORTC
|
| 18 |
-
from aiortc import (
|
| 19 |
-
RTCPeerConnection,
|
| 20 |
-
RTCSessionDescription,
|
| 21 |
-
VideoStreamTrack,
|
| 22 |
-
RTCIceServer,
|
| 23 |
-
RTCConfiguration
|
| 24 |
-
)
|
| 25 |
-
from aiortc.rtcconfiguration import RTCIceTransportPolicy, RTCBundlePolicy
|
| 26 |
from av import VideoFrame
|
| 27 |
|
| 28 |
# ==========================================
|
| 29 |
-
# C++ X11 CAPTURE + FAULT TOLERANCE
|
| 30 |
# ==========================================
|
| 31 |
CPP_SOURCE = r"""
|
| 32 |
#include <X11/Xlib.h>
|
|
@@ -135,6 +125,8 @@ int init_grabber(int w, int h, const char* display_name) {
|
|
| 135 |
XShmAttach(cap.display, &cap.shminfo);
|
| 136 |
XSync(cap.display, False);
|
| 137 |
|
|
|
|
|
|
|
| 138 |
cap.sws_ctx = sws_getContext(w, h, AV_PIX_FMT_BGRA,
|
| 139 |
w, h, AV_PIX_FMT_YUV420P,
|
| 140 |
SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
|
@@ -151,12 +143,14 @@ int init_grabber(int w, int h, const char* display_name) {
|
|
| 151 |
int capture_frame() {
|
| 152 |
if (cap.is_init && cap.display && cap.image) {
|
| 153 |
last_x_error_code = 0;
|
|
|
|
| 154 |
XShmGetImage(cap.display, cap.root, cap.image, 0, 0, AllPlanes);
|
| 155 |
return (last_x_error_code == 0);
|
| 156 |
}
|
| 157 |
return 0;
|
| 158 |
}
|
| 159 |
|
|
|
|
| 160 |
void convert_to_yuv(void* y, int y_stride, void* u, int u_stride, void* v, int v_stride) {
|
| 161 |
if (!cap.is_init || !cap.sws_ctx || !cap.image) return;
|
| 162 |
|
|
@@ -168,10 +162,11 @@ void convert_to_yuv(void* y, int y_stride, void* u, int u_stride, void* v, int v
|
|
| 168 |
sws_scale(cap.sws_ctx, srcSlice, srcStride, 0, cap.height, dst, dstStride);
|
| 169 |
}
|
| 170 |
|
|
|
|
| 171 |
void move_mouse(int x, int y) {
|
| 172 |
if (!cap.is_init || !cap.input_display) return;
|
| 173 |
XTestFakeMotionEvent(cap.input_display, -1, x, y, CurrentTime);
|
| 174 |
-
XFlush(cap.input_display);
|
| 175 |
}
|
| 176 |
|
| 177 |
void mouse_button(int button, int is_down) {
|
|
@@ -205,6 +200,7 @@ def compile_cpp():
|
|
| 205 |
with open("xcapture.cpp", "w") as f:
|
| 206 |
f.write(CPP_SOURCE)
|
| 207 |
|
|
|
|
| 208 |
cmd = [
|
| 209 |
"g++", "-O3", "-march=native", "-ffast-math", "-flto", "-shared", "-fPIC",
|
| 210 |
"-o", LIB_PATH, "xcapture.cpp",
|
|
@@ -221,6 +217,7 @@ compile_cpp()
|
|
| 221 |
# Load C++ Library
|
| 222 |
try:
|
| 223 |
xlib = ctypes.CDLL(LIB_PATH)
|
|
|
|
| 224 |
xlib.init_grabber.argtypes = [c_int, c_int, c_char_p]
|
| 225 |
xlib.init_grabber.restype = c_int
|
| 226 |
xlib.capture_frame.argtypes = []
|
|
@@ -235,6 +232,7 @@ try:
|
|
| 235 |
xlib.mouse_button.restype = None
|
| 236 |
xlib.key_send.argtypes = [c_char_p, c_int]
|
| 237 |
xlib.key_send.restype = None
|
|
|
|
| 238 |
USE_CSHM = True
|
| 239 |
except Exception as e:
|
| 240 |
print(f"Library load failed: {e}")
|
|
@@ -249,8 +247,14 @@ DEFAULT_WIDTH = 1280
|
|
| 249 |
DEFAULT_HEIGHT = 720
|
| 250 |
|
| 251 |
logging.basicConfig(level=logging.WARNING)
|
|
|
|
|
|
|
| 252 |
video_executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
|
|
|
|
|
|
|
|
|
| 253 |
video_lock = threading.Lock()
|
|
|
|
| 254 |
|
| 255 |
config = {
|
| 256 |
"width": DEFAULT_WIDTH,
|
|
@@ -261,6 +265,7 @@ class InputManager:
|
|
| 261 |
def __init__(self):
|
| 262 |
self.scroll_accum = 0
|
| 263 |
|
|
|
|
| 264 |
def mouse_move(self, x, y):
|
| 265 |
if USE_CSHM: xlib.move_mouse(x, y)
|
| 266 |
|
|
@@ -300,7 +305,7 @@ def start_system():
|
|
| 300 |
"-ac", "-noreset", "-nolisten", "tcp"
|
| 301 |
], stderr=subprocess.DEVNULL)
|
| 302 |
|
| 303 |
-
time.sleep(1)
|
| 304 |
set_resolution(DEFAULT_WIDTH, DEFAULT_HEIGHT)
|
| 305 |
|
| 306 |
if shutil.which("matchbox-window-manager"):
|
|
@@ -311,6 +316,7 @@ def start_system():
|
|
| 311 |
def maintain_antigravity():
|
| 312 |
while True:
|
| 313 |
try:
|
|
|
|
| 314 |
running = False
|
| 315 |
for p in psutil.process_iter(['name']):
|
| 316 |
if p.info['name'] == "antigravity":
|
|
@@ -319,12 +325,13 @@ def maintain_antigravity():
|
|
| 319 |
if not running:
|
| 320 |
subprocess.Popen(["antigravity"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 321 |
except: pass
|
| 322 |
-
time.sleep(5)
|
| 323 |
|
| 324 |
def set_resolution(w, h):
|
| 325 |
with video_lock:
|
| 326 |
if w == config["width"] and h == config["height"]:
|
| 327 |
return
|
|
|
|
| 328 |
try:
|
| 329 |
if w % 2 != 0: w += 1
|
| 330 |
if h % 2 != 0: h += 1
|
|
@@ -332,10 +339,14 @@ def set_resolution(w, h):
|
|
| 332 |
if h > MAX_HEIGHT: h = MAX_HEIGHT
|
| 333 |
|
| 334 |
mode_name = f"M_{w}_{h}"
|
|
|
|
|
|
|
|
|
|
| 335 |
subprocess.call(["xrandr", "--newmode", mode_name, f"{60*w*h/1000000:.2f}",
|
| 336 |
str(w), str(w+40), str(w+80), str(w+160),
|
| 337 |
str(h), str(h+3), str(h+10), str(h+16),
|
| 338 |
"-hsync", "+vsync"], stderr=subprocess.DEVNULL)
|
|
|
|
| 339 |
subprocess.call(["xrandr", "--addmode", "screen", mode_name], stderr=subprocess.DEVNULL)
|
| 340 |
subprocess.call(["xrandr", "--output", "screen", "--mode", mode_name], stderr=subprocess.DEVNULL)
|
| 341 |
|
|
@@ -355,6 +366,8 @@ class VirtualScreenTrack(VideoStreamTrack):
|
|
| 355 |
|
| 356 |
def _produce_frame(self, w, h):
|
| 357 |
if not USE_CSHM: return None
|
|
|
|
|
|
|
| 358 |
with video_lock:
|
| 359 |
try:
|
| 360 |
if w != self.last_w or h != self.last_h:
|
|
@@ -363,25 +376,39 @@ class VirtualScreenTrack(VideoStreamTrack):
|
|
| 363 |
self.last_w = w
|
| 364 |
self.last_h = h
|
| 365 |
|
| 366 |
-
|
|
|
|
|
|
|
| 367 |
|
|
|
|
| 368 |
frame = VideoFrame(width=w, height=h, format="yuv420p")
|
|
|
|
|
|
|
|
|
|
| 369 |
xlib.convert_to_yuv(
|
| 370 |
c_void_p(int(frame.planes[0].buffer_ptr)), frame.planes[0].line_size,
|
| 371 |
c_void_p(int(frame.planes[1].buffer_ptr)), frame.planes[1].line_size,
|
| 372 |
c_void_p(int(frame.planes[2].buffer_ptr)), frame.planes[2].line_size
|
| 373 |
)
|
| 374 |
return frame
|
| 375 |
-
except:
|
|
|
|
| 376 |
|
| 377 |
async def recv(self):
|
| 378 |
pts, time_base = await self.next_timestamp()
|
|
|
|
| 379 |
w, h = config["width"], config["height"]
|
| 380 |
|
| 381 |
frame = None
|
| 382 |
if USE_CSHM:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
try:
|
| 384 |
-
frame = await asyncio.get_event_loop().run_in_executor(
|
|
|
|
|
|
|
| 385 |
except: pass
|
| 386 |
|
| 387 |
if frame is None:
|
|
@@ -395,63 +422,49 @@ class VirtualScreenTrack(VideoStreamTrack):
|
|
| 395 |
|
| 396 |
async def offer(request):
|
| 397 |
try:
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
)
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
new_sdp.append("b=AS:4000")
|
| 442 |
-
new_sdp.append("b=TIAS:4000000")
|
| 443 |
-
answer.sdp = "\r\n".join(new_sdp)
|
| 444 |
-
|
| 445 |
-
await pc.setLocalDescription(answer)
|
| 446 |
-
|
| 447 |
-
return web.Response(content_type="application/json", text=json.dumps({
|
| 448 |
-
"sdp": pc.localDescription.sdp,
|
| 449 |
-
"type": pc.localDescription.type
|
| 450 |
-
}), headers={"Access-Control-Allow-Origin": "*"})
|
| 451 |
-
|
| 452 |
-
except Exception as e:
|
| 453 |
-
traceback.print_exc()
|
| 454 |
-
return web.Response(status=500, text=f"Server Error: {str(e)}")
|
| 455 |
|
| 456 |
def map_key(key):
|
| 457 |
if not key: return None
|
|
@@ -465,6 +478,7 @@ async def handle_debounced_resize(w, h):
|
|
| 465 |
if resize_timer: resize_timer.cancel()
|
| 466 |
|
| 467 |
async def task():
|
|
|
|
| 468 |
await asyncio.sleep(0.1)
|
| 469 |
await asyncio.get_event_loop().run_in_executor(video_executor, set_resolution, w, h)
|
| 470 |
|
|
@@ -472,8 +486,11 @@ async def handle_debounced_resize(w, h):
|
|
| 472 |
|
| 473 |
async def process_input(data):
|
| 474 |
try:
|
|
|
|
| 475 |
msg = json.loads(data)
|
| 476 |
t = msg.get("type")
|
|
|
|
|
|
|
| 477 |
if t == "mousemove":
|
| 478 |
w, h = config["width"], config["height"]
|
| 479 |
input_manager.mouse_move(int(msg["x"] * w), int(msg["y"] * h))
|
|
|
|
| 10 |
import numpy as np
|
| 11 |
import psutil
|
| 12 |
import ctypes
|
|
|
|
| 13 |
from ctypes import c_int, c_void_p, c_char_p, POINTER, c_ubyte, c_bool
|
| 14 |
from aiohttp import web
|
| 15 |
+
from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack, RTCIceServer, RTCConfiguration
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
from av import VideoFrame
|
| 17 |
|
| 18 |
# ==========================================
|
| 19 |
+
# C++ X11 CAPTURE + FAULT TOLERANCE + DUAL CHANNEL
|
| 20 |
# ==========================================
|
| 21 |
CPP_SOURCE = r"""
|
| 22 |
#include <X11/Xlib.h>
|
|
|
|
| 125 |
XShmAttach(cap.display, &cap.shminfo);
|
| 126 |
XSync(cap.display, False);
|
| 127 |
|
| 128 |
+
// SWS_FAST_BILINEAR is good, SWS_POINT is faster but blocky.
|
| 129 |
+
// Using BILINEAR for balance.
|
| 130 |
cap.sws_ctx = sws_getContext(w, h, AV_PIX_FMT_BGRA,
|
| 131 |
w, h, AV_PIX_FMT_YUV420P,
|
| 132 |
SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
|
|
|
| 143 |
int capture_frame() {
|
| 144 |
if (cap.is_init && cap.display && cap.image) {
|
| 145 |
last_x_error_code = 0;
|
| 146 |
+
// This blocks only the video thread
|
| 147 |
XShmGetImage(cap.display, cap.root, cap.image, 0, 0, AllPlanes);
|
| 148 |
return (last_x_error_code == 0);
|
| 149 |
}
|
| 150 |
return 0;
|
| 151 |
}
|
| 152 |
|
| 153 |
+
// Optimized pointer math happens inside sws_scale
|
| 154 |
void convert_to_yuv(void* y, int y_stride, void* u, int u_stride, void* v, int v_stride) {
|
| 155 |
if (!cap.is_init || !cap.sws_ctx || !cap.image) return;
|
| 156 |
|
|
|
|
| 162 |
sws_scale(cap.sws_ctx, srcSlice, srcStride, 0, cap.height, dst, dstStride);
|
| 163 |
}
|
| 164 |
|
| 165 |
+
// INPUT FUNCTIONS USE SEPARATE DISPLAY CONNECTION
|
| 166 |
void move_mouse(int x, int y) {
|
| 167 |
if (!cap.is_init || !cap.input_display) return;
|
| 168 |
XTestFakeMotionEvent(cap.input_display, -1, x, y, CurrentTime);
|
| 169 |
+
XFlush(cap.input_display); // Flush only input stream
|
| 170 |
}
|
| 171 |
|
| 172 |
void mouse_button(int button, int is_down) {
|
|
|
|
| 200 |
with open("xcapture.cpp", "w") as f:
|
| 201 |
f.write(CPP_SOURCE)
|
| 202 |
|
| 203 |
+
# ADDED: -march=native -ffast-math -flto for CPU optimization
|
| 204 |
cmd = [
|
| 205 |
"g++", "-O3", "-march=native", "-ffast-math", "-flto", "-shared", "-fPIC",
|
| 206 |
"-o", LIB_PATH, "xcapture.cpp",
|
|
|
|
| 217 |
# Load C++ Library
|
| 218 |
try:
|
| 219 |
xlib = ctypes.CDLL(LIB_PATH)
|
| 220 |
+
|
| 221 |
xlib.init_grabber.argtypes = [c_int, c_int, c_char_p]
|
| 222 |
xlib.init_grabber.restype = c_int
|
| 223 |
xlib.capture_frame.argtypes = []
|
|
|
|
| 232 |
xlib.mouse_button.restype = None
|
| 233 |
xlib.key_send.argtypes = [c_char_p, c_int]
|
| 234 |
xlib.key_send.restype = None
|
| 235 |
+
|
| 236 |
USE_CSHM = True
|
| 237 |
except Exception as e:
|
| 238 |
print(f"Library load failed: {e}")
|
|
|
|
| 247 |
DEFAULT_HEIGHT = 720
|
| 248 |
|
| 249 |
logging.basicConfig(level=logging.WARNING)
|
| 250 |
+
|
| 251 |
+
# Dedicated thread for video capture
|
| 252 |
video_executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
| 253 |
+
|
| 254 |
+
# LOCKS
|
| 255 |
+
# video_lock: Protects XShm, resizing, and video display connection
|
| 256 |
video_lock = threading.Lock()
|
| 257 |
+
# Input does NOT use a lock anymore because it uses a separate X11 connection in C++
|
| 258 |
|
| 259 |
config = {
|
| 260 |
"width": DEFAULT_WIDTH,
|
|
|
|
| 265 |
def __init__(self):
|
| 266 |
self.scroll_accum = 0
|
| 267 |
|
| 268 |
+
# No locking needed here, C++ handles separate connection
|
| 269 |
def mouse_move(self, x, y):
|
| 270 |
if USE_CSHM: xlib.move_mouse(x, y)
|
| 271 |
|
|
|
|
| 305 |
"-ac", "-noreset", "-nolisten", "tcp"
|
| 306 |
], stderr=subprocess.DEVNULL)
|
| 307 |
|
| 308 |
+
time.sleep(1) # Reduced sleep
|
| 309 |
set_resolution(DEFAULT_WIDTH, DEFAULT_HEIGHT)
|
| 310 |
|
| 311 |
if shutil.which("matchbox-window-manager"):
|
|
|
|
| 316 |
def maintain_antigravity():
|
| 317 |
while True:
|
| 318 |
try:
|
| 319 |
+
# Check optimization: Avoid list parsing overhead
|
| 320 |
running = False
|
| 321 |
for p in psutil.process_iter(['name']):
|
| 322 |
if p.info['name'] == "antigravity":
|
|
|
|
| 325 |
if not running:
|
| 326 |
subprocess.Popen(["antigravity"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 327 |
except: pass
|
| 328 |
+
time.sleep(5) # Increased sleep to save CPU
|
| 329 |
|
| 330 |
def set_resolution(w, h):
|
| 331 |
with video_lock:
|
| 332 |
if w == config["width"] and h == config["height"]:
|
| 333 |
return
|
| 334 |
+
|
| 335 |
try:
|
| 336 |
if w % 2 != 0: w += 1
|
| 337 |
if h % 2 != 0: h += 1
|
|
|
|
| 339 |
if h > MAX_HEIGHT: h = MAX_HEIGHT
|
| 340 |
|
| 341 |
mode_name = f"M_{w}_{h}"
|
| 342 |
+
|
| 343 |
+
# Combine xrandr calls to reduce process fork overhead?
|
| 344 |
+
# Xrandr is CLI, safer to keep separate but fast.
|
| 345 |
subprocess.call(["xrandr", "--newmode", mode_name, f"{60*w*h/1000000:.2f}",
|
| 346 |
str(w), str(w+40), str(w+80), str(w+160),
|
| 347 |
str(h), str(h+3), str(h+10), str(h+16),
|
| 348 |
"-hsync", "+vsync"], stderr=subprocess.DEVNULL)
|
| 349 |
+
|
| 350 |
subprocess.call(["xrandr", "--addmode", "screen", mode_name], stderr=subprocess.DEVNULL)
|
| 351 |
subprocess.call(["xrandr", "--output", "screen", "--mode", mode_name], stderr=subprocess.DEVNULL)
|
| 352 |
|
|
|
|
| 366 |
|
| 367 |
def _produce_frame(self, w, h):
|
| 368 |
if not USE_CSHM: return None
|
| 369 |
+
|
| 370 |
+
# Only lock the video part. Input continues in parallel.
|
| 371 |
with video_lock:
|
| 372 |
try:
|
| 373 |
if w != self.last_w or h != self.last_h:
|
|
|
|
| 376 |
self.last_w = w
|
| 377 |
self.last_h = h
|
| 378 |
|
| 379 |
+
# 1. Capture (Copy X11 -> Shared Memory)
|
| 380 |
+
if xlib.capture_frame() == 0:
|
| 381 |
+
return None
|
| 382 |
|
| 383 |
+
# 2. Allocate Frame (Python overhead, but required for aiortc)
|
| 384 |
frame = VideoFrame(width=w, height=h, format="yuv420p")
|
| 385 |
+
|
| 386 |
+
# 3. Convert (Shared Memory -> Frame Buffer)
|
| 387 |
+
# Using direct address integer for speed
|
| 388 |
xlib.convert_to_yuv(
|
| 389 |
c_void_p(int(frame.planes[0].buffer_ptr)), frame.planes[0].line_size,
|
| 390 |
c_void_p(int(frame.planes[1].buffer_ptr)), frame.planes[1].line_size,
|
| 391 |
c_void_p(int(frame.planes[2].buffer_ptr)), frame.planes[2].line_size
|
| 392 |
)
|
| 393 |
return frame
|
| 394 |
+
except:
|
| 395 |
+
return None
|
| 396 |
|
| 397 |
async def recv(self):
|
| 398 |
pts, time_base = await self.next_timestamp()
|
| 399 |
+
|
| 400 |
w, h = config["width"], config["height"]
|
| 401 |
|
| 402 |
frame = None
|
| 403 |
if USE_CSHM:
|
| 404 |
+
# Offload heavy C++ work to thread.
|
| 405 |
+
# While this runs, the Main Thread can process "process_input" (Mouse/Keys)
|
| 406 |
+
# because we are not holding the Global Interpreter Lock (ctypes releases it)
|
| 407 |
+
# and we are not holding a global "input lock".
|
| 408 |
try:
|
| 409 |
+
frame = await asyncio.get_event_loop().run_in_executor(
|
| 410 |
+
video_executor, self._produce_frame, w, h
|
| 411 |
+
)
|
| 412 |
except: pass
|
| 413 |
|
| 414 |
if frame is None:
|
|
|
|
| 422 |
|
| 423 |
async def offer(request):
|
| 424 |
try:
|
| 425 |
+
params = await request.json()
|
| 426 |
+
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
|
| 427 |
+
except: return web.Response(status=400)
|
| 428 |
+
|
| 429 |
+
ice_servers = [RTCIceServer(urls=["stun:stun.l.google.com:19302"])]
|
| 430 |
+
pc = RTCPeerConnection(RTCConfiguration(iceServers=ice_servers))
|
| 431 |
+
pcs.add(pc)
|
| 432 |
+
|
| 433 |
+
@pc.on("connectionstatechange")
|
| 434 |
+
async def on_state():
|
| 435 |
+
if pc.connectionState in ["failed", "closed"]:
|
| 436 |
+
await pc.close()
|
| 437 |
+
pcs.discard(pc)
|
| 438 |
+
|
| 439 |
+
@pc.on("datachannel")
|
| 440 |
+
def on_dc(channel):
|
| 441 |
+
@channel.on("message")
|
| 442 |
+
async def on_message(message):
|
| 443 |
+
# Direct call to input processing
|
| 444 |
+
await process_input(message)
|
| 445 |
+
|
| 446 |
+
pc.addTrack(VirtualScreenTrack())
|
| 447 |
+
|
| 448 |
+
await pc.setRemoteDescription(offer)
|
| 449 |
+
answer = await pc.createAnswer()
|
| 450 |
+
|
| 451 |
+
# FORCE HIGHER BITRATE (4Mbps)
|
| 452 |
+
# Injecting bandwidth info into SDP before setting local description
|
| 453 |
+
sdp_lines = answer.sdp.splitlines()
|
| 454 |
+
new_sdp = []
|
| 455 |
+
for line in sdp_lines:
|
| 456 |
+
new_sdp.append(line)
|
| 457 |
+
if line.startswith("m=video"):
|
| 458 |
+
new_sdp.append("b=AS:4000")
|
| 459 |
+
new_sdp.append("b=TIAS:4000000")
|
| 460 |
+
answer.sdp = "\r\n".join(new_sdp)
|
| 461 |
+
|
| 462 |
+
await pc.setLocalDescription(answer)
|
| 463 |
+
|
| 464 |
+
return web.Response(content_type="application/json", text=json.dumps({
|
| 465 |
+
"sdp": pc.localDescription.sdp,
|
| 466 |
+
"type": pc.localDescription.type
|
| 467 |
+
}), headers={"Access-Control-Allow-Origin": "*"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
|
| 469 |
def map_key(key):
|
| 470 |
if not key: return None
|
|
|
|
| 478 |
if resize_timer: resize_timer.cancel()
|
| 479 |
|
| 480 |
async def task():
|
| 481 |
+
# REDUCED DELAY FROM 0.5 to 0.1 FOR FASTER RESIZING
|
| 482 |
await asyncio.sleep(0.1)
|
| 483 |
await asyncio.get_event_loop().run_in_executor(video_executor, set_resolution, w, h)
|
| 484 |
|
|
|
|
| 486 |
|
| 487 |
async def process_input(data):
|
| 488 |
try:
|
| 489 |
+
# Optimized parsing
|
| 490 |
msg = json.loads(data)
|
| 491 |
t = msg.get("type")
|
| 492 |
+
|
| 493 |
+
# Immediate dispatch
|
| 494 |
if t == "mousemove":
|
| 495 |
w, h = config["width"], config["height"]
|
| 496 |
input_manager.mouse_move(int(msg["x"] * w), int(msg["y"] * h))
|