Skip to content

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

FeatureWhat it does
3 streaming slotsMAIN, FEED, ALT — 3 simultaneous HLS video players in a vertical layout
FullscreenTap a video to expand it to full screen
DVRTimeline bar to seek back in time, return to live with a tap
Auto-advance liveAutomatic return to live after N seconds of inactivity on the DVR
LIVE/DVR badgeColored badge: red for live, blue for DVR
Video controlsPlay/pause, seek, volume (headphone-only audio), fullscreen toggle
Headphone audioVolume at 0.0 by default, activates automatically only with headphones connected
BoxFit coverVideos fill the screen while keeping proportions, no side black bars
Live/Go LiveBlue button for DVR, red for live streaming

Connection

FeatureWhat it does
Persistent WebSocketContinuous connection to the backend to receive real-time updates
Exponential backoffAutomatic reconnection with progressive delay (1s, 2s, 4s, 8s, max 30s)
ConfigurableBackend URL passed via --dart-define=BACKEND_URL=..., nothing hardcoded
Remote resetReceives the device:reset command to restart all streams without restarting the app
Remote slot controlReceives the device:slot:control command to stop/start/restart a single slot

Stream switching

FeatureWhat it does
Automatic switchWhen the Control Room changes a link, all iPads switch to the new stream in real time
Free slotWhen a slot is freed, the video stops and waits for the next stream
Track last URLsOn reset, the app remembers the last valid URLs and restores them after 500ms

Requirements

ToolVersion
Flutter3.44.4+
Dart3.12.2+
Xcode16+ (for iOS build)
CocoaPods(for iOS dependencies)

Player Dependencies

PackageRole
MobileVLCKit (pod)VLC playback engine (declared in ios/Podfile)
web_socket_channelReal-time WebSocket to the backend (ws_link_service.dart)
audio_sessionAudio session configuration
shared_preferencesSettings 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.dart

App 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.

© 2026 Zerolatency — All Rights Reserved