screentinker/docs/player-parity.md
ScreenTinker c4ee7d008f web player: declare capabilities at runtime, persist them, and audit all four players
The dashboard offered every control to every display, so a browser tab showed a
reboot button that could never work. server/lib/player-capabilities.js defines the
vocabulary; this makes the web player actually speak it.

The declaration is computed, not constant, because the same index.html is BOTH the
browser player and the BrightSign player. system.reboot / display.power /
display.resolution / system.self_update are claimed only when BS.hasHost() answers —
deliberately hasHost() and not isBrightSign(), since the UA check is also true for a
widget built without node integration, which can reach none of them. Screenshots,
offline cache, transitions and native sync are each probed the same way.

Capabilities were never persisted: the column and the handler did not exist, so a
declaration would have been sent and silently dropped. Added the migration and
applyCapabilities(). An ABSENT declaration leaves the column NULL so the baseline
still applies — several hundred fielded displays declare nothing and would otherwise
lose every control at once — while an EMPTY declaration is stored as '[]' and honoured.

docs/player-parity.md records every capability against all four players with a reason
for each "no", and flags three Tizen baseline errors found while verifying it.

Tests: 1109/1109. Both inline <script> blocks in index.html parse clean.
2026-08-05 14:17:40 -05:00

6.9 KiB

Player parity matrix

What each player can actually do, verified against the code rather than assumed. This is the document that says where the remaining work is, so a wrong "yes" here is worse than a missing row: it puts a control on the dashboard that cannot work.

Capability names come from server/lib/player-capabilities.js. Players declare their own set at registration; a player that declares nothing falls back to the per-platform baseline in that file.

Legend supported · ⚠️ partial/conditional (reason given) · not supported (reason given)

BrightSign runs the same server/player/index.html as the browser, so it differs only where the autorun.brs host bridge adds something the browser cannot reach.

Playback

capability Android Web Tizen BrightSign
playback.video ExoPlayer <video> AVPlay hardware plane
playback.image
playback.widget WebView iframe iframe iframe
playback.youtube WebView embed IFrame API iframe embed IFrame API
playback.zones
playback.transitions GL wipes (#204) ⚠️ declared only when the bundle loads — a failed load hard-cuts rather than breaking playback ⚠️ as web
playback.pip PipOverlay #pipContainer

Audio

capability Android Web Tizen BrightSign
audio.mute incl. YouTube via IFrame bridge incl. YouTube via postMessage as web
audio.volume set_volume set_volume no set_volume handler exists — the dashboard slider does nothing today as web

Display

capability Android Web Tizen BrightSign
display.rotation native rootView.rotation CSS transform CSS + AVPlay for video ⚠️ host rotates the output via roVideoMode; CSS alone cannot turn the hardware video plane
display.power screen_off / lock_now a browser tab cannot power a panel — the overlay only paints black screen_off draws a black overlay, deliberately, "so the command still does something visible" ⚠️ media teardown always works; CEC is best-effort and absent on some units
display.resolution no video-mode control in the app not addressable from a browser roVideoMode via the host

Remote view and control

capability Android Web Tizen BrightSign
remote.screenshot ⚠️ view capture always; full-screen only with accessibility or MediaProjection ⚠️ canvas only — same-origin content, and the alpha probe rejects frames where no pixels arrived captureAndSend ⚠️ host framebuffer capture requires primary storage; falls back to canvas, which cannot read the video plane
remote.stream 1fps ⚠️ as web
remote.input

Lifecycle

capability Android Web Tizen BrightSign
system.restart_player location.reload() host rebuilds the widget — a page reload does not reliably return
system.reboot device owner a browser tab cannot reboot its host no Tizen API exposed to the app RebootSystem() via the host
system.self_update APK OTA (UpdateChecker) the server deploys the player; there is nothing for it to update .wgt updates go through Tizen's own store/CLI autorun.zip package update

Device management

Android device-owner territory. Everything here is elsewhere for the same reason — no equivalent privilege model exists on those platforms — so the column is collapsed.

capability Android Web / Tizen / BrightSign
system.kiosk lock-task, now persisted across reboot no device-owner concept
system.brightness Tier 0/1
system.screen_timeout Tier 1
system.install_apk Tier 2 not an APK platform
system.shell Tier 2, handled in WebSocketService
system.time Tier 2

Synchronisation and resilience

capability Android Web Tizen BrightSign
sync.clock
sync.native no native protocol ⚠️ SyncManager, BOS 8.2.10+; multicast so all members must share one L2 network
offline.cache content downloaded to disk service worker ⚠️ playlist payload onlyst_payload_cache replays the last renderable payload, but there is no service worker, so media still needs the network service worker + 1GB storage quota

Real gaps worth closing

Ordered by how visible the failure is to an operator.

  1. Tizen audio.volume — dead control. set_volume has no handler in tizen/js/app.js; the only volume path is the on-device KEYCODE_VOLUME_* keys. The dashboard slider silently does nothing. Either implement the handler or let the capability hide the control.
  2. Tizen offline.cache is partial. The playlist survives a reboot; the media does not. A Tizen panel that loses its uplink keeps its schedule and cannot play it. This is the largest functional gap in the table.
  3. BrightSign remote.screenshot needs primary storage. Reachable today only via the canvas fallback, which cannot read the video plane, so screenshots show everything except the video. Resolves itself when a card or SSD is fitted.
  4. display.resolution is BrightSign-only. Fine, but the dashboard should not offer it elsewhere.

Correctly impossible — do not "fix" these

  • system.reboot on web/Tizen. No API exists. A browser tab rebooting its host would be a browser vulnerability.
  • display.power on web. The overlay is the honest maximum; the panel stays lit.
  • All of device management off Android. No equivalent privilege model exists on Tizen or BrightSign, and a web player has no device to manage.
  • system.self_update on web. The player is the deployment; there is nothing to update.
  • sync.native off BrightSign. It is BrightSign's own protocol, and the clock-derived one is the cross-platform answer that already works everywhere.

⚠️ Corrections needed in player-capabilities.js

Found while verifying this table. The baselines only apply to displays that declare nothing, so these are wrong for the existing fleet until each player ships its declaration:

  • tizen claims audio.volume — no handler exists (gap 1 above). Should be removed.
  • tizen omits remote.screenshot and remote.stream — both are implemented (captureAndSend, startStreaming). Should be added.
  • tizen claims offline.cache — true only for the playlist payload, not media. Either keep it with the partial meaning documented, or split the capability.