# SyncPlayer (LAN Synchronized Video Playback) Flask + Flask-SocketIO system for near frame-level synchronized playback across many browser-capable SoC displays on a local LAN. ## Key synchronization mechanism ### 1) Custom NTP-style time sync (client does **not** use its wall clock) Client keeps a high-resolution monotonic clock using: ```js client_ms = performance.timeOrigin + performance.now() ``` Every 10s the client sends `t1_ms` and the server replies with `{t1_ms, t2_ms}` where `t2_ms` is server time in ms. Client receives the reply at `t3_ms` and calculates: ```text rtt = t3 - t1 offset = t2 - (t1 + t3)/2 server_time ~= client_time + offset ``` Offset is smoothed to reduce jitter. ### 2) Scheduled absolute start When an event triggers, the server schedules a future start time: ```text start_time_ms = server_time_ms() + EVENT_LEAD_TIME_MS (default 5000) ``` Server sends each participating display: - `video_url` - `start_time_ms` - `event_id` Client preloads, pauses at 0, then uses a `setTimeout` + `requestAnimationFrame` loop to call `video.play()` *as close as possible* to `start_time_ms` using the corrected server clock. ### 3) Drift correction during playback Every 2s client computes: ```text expected = (serverNowMs - start_time_ms)/1000 drift = video.currentTime - expected ``` - if `|drift| > 15ms`: adjust `playbackRate` slightly (0.99 / 1.01) - if `|drift| > 100ms`: hard seek to expected This keeps the group synchronized on low-latency LANs. ## Project structure ``` /app /templates /static /media models.py routes.py sockets.py udp_listener.py sync.py config.py run.py requirements.txt ``` ## Setup ### 1) Create venv + install deps Windows (cmd): ```bat python -m venv .venv .venv\Scripts\activate pip install -r requirements.txt ``` ### 2) Initialize database ```bat python run.py init-db ``` ### 3) Run server ```bat python run.py --host 0.0.0.0 --port 5000 ``` Notes: - On Windows / Python 3.14+, this runs in `threading` async mode by default (good enough for LAN testing). For highest scale/throughput, run on Linux and install `eventlet` or `gevent`. - You can force an async mode with: `set ASYNC_MODE=threading` (Windows) or `export ASYNC_MODE=eventlet|gevent|threading` (Linux). Open admin UI: - `http://:5000/admin/` Open display pages: - `http://:5000/display/` ## UDP trigger Configure UDP Port + UDP Payload on an Event in the admin UI. The UDP listener binds ports automatically (refreshes config every 5s). Send a UDP packet (example PowerShell): ```powershell $udp = new-Object System.Net.Sockets.UdpClient $bytes = [Text.Encoding]::UTF8.GetBytes("SHOW_START") $udp.Send($bytes, $bytes.Length, "192.168.1.10", 7777) | Out-Null $udp.Close() ``` ### Firewall notes - Allow TCP port (default 5000) for HTTP/WebSocket. - Allow the configured UDP ports for triggers. ## HTTP URL trigger (LAN) You can also trigger an event by opening a URL (useful for simple controllers without UDP): - By id: `http://:5000/trigger/` - By name: `http://:5000/trigger_by_name/` Cooldown: - Cooldown is enforced by default. - To bypass cooldown (LAN only), add `?force=1`. Example: ``` http://192.168.1.10:5000/trigger/1?force=1 ``` ## Idle image (no active event) You can upload an **Idle Image** in the admin UI: - `http://:5000/admin/idle-image` When no triggered event is active, displays show this image fullscreen. ## Debug overlay On display page: - Press **F2** or **Ctrl+Shift+D**, or - **Double click** anywhere to toggle a small overlay showing Socket.IO connection state, offset/RTT, event id, drift and playbackRate. ## Display video cache warming When a display page loads, it will fetch `/api/videos` and perform **background cache warming**: - Default strategy: request the first **1MB** of each video via HTTP `Range` (`bytes=0-1048575`). - Purpose: get the container + initial moov atoms into cache to reduce startup stutter. - It is **best-effort**: browsers may evict cache under pressure, and some platforms ignore caching for large media. - Cache warming automatically pauses once an event starts to avoid competing bandwidth during synchronized start.