Flutter app — DVR/HLS player for iPad tablets, with multi-slot support and real-time WebSocket. Based on VLCKit (MobileVLCKit) with a custom native VLC player (MethodChannel + direct VLCKit).
Video Player
| Feature | What it does |
|---|---|
| 3 streaming slots | MAIN, FEED, ALT — 3 simultaneous HLS video players in a vertical layout |
| Fullscreen | Tap a video to expand it to full screen |
| DVR | Timeline bar to seek back in time, return to live with a tap |
| Auto-advance live | Automatic return to live after N seconds of inactivity on the DVR |
| LIVE/DVR badge | Colored badge: red for live, blue for DVR |
| Video controls | Play/pause, seek, volume (headphone-only audio), fullscreen toggle |
| Headphone audio | Volume at 0.0 by default, activates automatically only with headphones connected |
| BoxFit cover | Videos fill the screen while keeping proportions, no side black bars |
| Live/Go Live | Blue button for DVR, red for live streaming |
Connection
| Feature | What it does |
|---|---|
| Persistent WebSocket | Continuous connection to the backend to receive real-time updates |
| Exponential backoff | Automatic reconnection with progressive delay (1s, 2s, 4s, 8s, max 30s) |
| Configurable | Backend URL passed via --dart-define=BACKEND_URL=..., nothing hardcoded |
| Remote reset | Receives the device:reset command to restart all streams without restarting the app |
| Remote slot control | Receives the device:slot:control command to stop/start/restart a single slot |
Stream switching
| Feature | What it does |
|---|---|
| Automatic switch | When the Control Room changes a link, all iPads switch to the new stream in real time |
| Free slot | When a slot is freed, the video stops and waits for the next stream |
| Track last URLs | On reset, the app remembers the last valid URLs and restores them after 500ms |
Requirements
| Tool | Version |
|---|---|
| Flutter | 3.44.4+ |
| Dart | 3.12.2+ |
| Xcode | 16+ (for iOS build) |
| CocoaPods | (for iOS dependencies) |
Player Dependencies
| Package | Role |
|---|---|
MobileVLCKit (pod) | VLC playback engine (declared in ios/Podfile) |
web_socket_channel | Real-time WebSocket to the backend (ws_link_service.dart) |
audio_session | Audio session configuration |
shared_preferences | Settings persistence |
Architecture
lib/
├── main.dart # Flutter entry point → VlcCustomTestScreen
├── vlc_custom_test_screen.dart # Main custom VLC player screen
├── vlc/
│ ├── vlc_controller.dart # VLC controller (playback, stall watchdog, retry)
│ ├── vlc_controls.dart # Video controls overlay
│ ├── vlc_timeline_widget.dart # Interactive DVR timeline
│ ├── dvr_support_probe.dart # Server DVR capability detection
│ └── dvr_url_strategy.dart # DVR URL strategies (Flussonic/Wowza)
├── vlc_native_player.dart # Native VLC bridge (MethodChannel)
├── switch_timing.dart # Slot transition timing
├── dvr_range.dart # DVR range data model
├── link_service.dart # REST service (legacy, unused)
└── ws_link_service.dart # WebSocket service (active)Test
bash
# Run all tests
flutter test
# Run only the VLC controller tests
flutter test test/vlc_controller_test.dart test/vlc_controller_state_test.dartApp Store Notes
The app is designed as a LAN kiosk: it connects via HTTP to a private IP (e.g. 192.168.1.127:3002). For App Store submission you need the App Review Notes and the privacy policy (see Mac Setup and Complete Guide). ATS allows traffic to private IPs; NSAllowsLocalNetworking is configured in ios/Runner/Info.plist for the local network.
→ See Build Flutter App for setup, running and building.