screentinker/tizen/index.html
screentinker c2115c88e3
fix(tizen): portrait/flipped VIDEO via AVPlay (hardware-plane rotation) — no more black screen (#170) (#174)
Bold field report: portrait video on Tizen = black screen. Root cause: our orientation
support rotates #stage with a CSS transform, but on Tizen the HTML5 <video> is composited
on a HARDWARE video plane that ignores CSS rotate — so portrait/flipped video blacks out
(images/widgets/text rotate fine). It's a Tizen platform limitation, not a regression.

Fix: route portrait / portrait-flipped VIDEO through Tizen AVPlay, whose setDisplayRotation
rotates the hardware plane itself. Scoped to portrait/flipped ONLY — landscape keeps the
proven HTML5 <video> path (double-buffer + group-sync drift), so the working case is untouched.

- index.html: <object id="avPlayer" type="application/avplayer"> hole-punch surface (hidden
  until a portrait video plays; off-hardware it's inert).
- player.js: setOrientation()/avAvailable(); renderVideoAv() (open/setDisplayRect/
  setDisplayRotation 90|270/setDisplayMethod LETTER_BOX/prepareAsync/play; onstreamcompleted
  -> loop|advance); avStop() torn down in clearStage() before every render; renderVideo()
  branches to AVPlay only for portrait/flipped when webapis.avplay exists.
- Graceful: any AVPlay error/absence -> honest note (portrait_video_unsupported, 5 locales),
  NEVER a silent black screen. currentVideoEl stays null for AV (portrait is solo-first;
  schedule engine still drives index/position).
- app.js: applyOrientation() now tells the player the orientation so renderVideo can choose
  the path.

Exit-signal marker slice unaffected (test green). Landscape + off-hardware paths unchanged.
NEEDS ON-DEVICE VALIDATION on a real Tizen TV (AVPlay can't be exercised off-hardware) —
signed test .wgt built for Bold.

Refs #170.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:26:27 -05:00

64 lines
2.8 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ScreenTinker</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- Setup: enter server URL -->
<div id="setup" class="screen">
<div class="card">
<h1>ScreenTinker</h1>
<p class="sub">Digital Signage Player</p>
<label for="serverUrl">Server URL</label>
<input id="serverUrl" type="url" value="https://screentinker.com" autocomplete="off"
autocorrect="off" autocapitalize="off" spellcheck="false">
<button id="connectBtn">Connect</button>
<p id="setupStatus" class="status"></p>
</div>
</div>
<!-- Pairing: show the code -->
<div id="pairing" class="screen hidden">
<div class="card">
<h1>ScreenTinker</h1>
<p class="sub">Pair this display</p>
<div id="pairCode" class="code">------</div>
<p class="hint">Enter this code in your ScreenTinker dashboard<br>(Devices &rarr; Pair a display)</p>
<p id="pairStatus" class="status">Waiting to be paired&hellip;</p>
<button id="resetBtn" class="ghost">Change server</button>
</div>
</div>
<!-- #170: Tizen AVPlay video surface. HTML5 <video> lives on a hardware plane that ignores
CSS rotate, so portrait/flipped VIDEO blacks out; AVPlay's setDisplayRotation rotates the
hardware plane. Hidden until a portrait/flipped video plays; off-hardware (no webapis) it
stays unused. Sits above the (empty-during-AV) stage so the hole-punched plane is visible. -->
<object id="avPlayer" type="application/avplayer" style="position:fixed;inset:0;width:100%;height:100%;z-index:5;display:none"></object>
<!-- Playback stage -->
<div id="stage" class="screen stage hidden"></div>
<!-- #109: PiP overlay layer — a sibling ABOVE #stage that the playlist never
touches. app.js mirrors the orientation transform onto it. -->
<div id="pip"></div>
<!-- Tiny on-screen status (offline / errors), auto-hides -->
<div id="toast" class="toast hidden"></div>
<!-- #125: Samsung device-API bridges. $WEBAPIS / $B2BAPIS are resolved by the
Tizen platform at runtime; off-hardware (browser / URL Launcher) these 404
harmlessly and the surfaces are simply absent. -->
<script src="$WEBAPIS/webapis/webapis.js"></script>
<script src="$B2BAPIS/b2bapis/b2bapis.js"></script>
<script src="js/socket.io.min.js"></script>
<script src="js/schedule-eval.js"></script>
<script src="js/player.js"></script>
<script src="js/device-control.js"></script>
<script src="js/pip-overlay.js"></script>
<script src="js/app.js"></script>
</body>
</html>