Start

Video playback

Source VIDEO_HOWTO.md Synced 2026-10-07 · b38e70a

A public YouTube video now plays to completion in the QEMU development runtime, with decoded frames and non-silent PCM captured from the virtual audio device. Local WebM and WebAudio tests also pass. Physical speakers and higher resolutions remain unverified. Repeated navigation later exhausted the guest memory pool and caused a browser page fault; this is a working short-playback milestone, not a claim of stable long-session browsing.

Latest stability pass#

The later October 3 run used kernel 8583de8f5609, QEMU WHPX with four CPUs and 8 GiB RAM, and a fresh disposable FAT16 browser disk. All four local VP8/Vorbis and VP9/Opus HTML5/MSE cases passed clock, pause, seek, and ending checks again. Their run IDs are 1791052762093-d6a6aae53178f, 1791052860858-8b7b2be08d929, 1791052915035-90795f3b5b8f7, and 1791052955655-1b4c69ae67ac2 in the fixture telemetry.

The mapping hint table now coalesces overlapping records and preserves live edges when an unmap split cannot allocate metadata. This corrects reproduced bookkeeping failures; it does not establish stable browser memory use. The same guest grew to approximately 3,683 MiB committed with 18 address spaces after initial browsing, the four local cases, and a YouTube navigation. The YouTube document had no video element at the sampled point and retained the old displayed frame. Creating another tab raised committed memory above 4,100 MiB. The 30-minute responsive browsing/playback target was not met.

Evidence is under ignored build/browser-stability-qemu/: kernel/source hashes, serial log, individual navigation reports, memory samples, and screenshots. The automatic sampler stopped when a diagnostic operation held the QMP connection; subsequent manual samples are separate. Do not interpret that sampling gap as a constant-memory interval. Diagnostic pauses and production tracing also make this unsuitable as an FPS benchmark.

The memory audit separates implemented metadata fixes from remaining retention and lifetime problems. This pass did not retest physical speakers or the Beelink, change the browser sandbox flags, or establish YouTube controls and longer playback.

Earlier playback evidence#

The October 3, 2026 runs used the staged Chrome/Chromium 152 runtime, QEMU WHPX, a disposable guest disk, and the native XerisOS kernel. That build has SHA-256 prefix c1a29d; the four video combinations below passed on the preceding 741cd537 build. Clips are short and 320x240, so these results do not establish sustained FPS or high-resolution playback quality.

Check Observed result
VP8/Vorbis and VP9/Opus, HTML5 Both passed clock-rate, pause, seek, and ending checks
VP8/Vorbis and VP9/Opus, Media Source Extensions (MSE) Both passed clock-rate, pause, seek, and ending checks
Local-fixture fullscreen Enter and exit worked in QEMU; YouTube fullscreen remains unverified
Fully downloaded three-second WAV, played from a Blob URL Clock advanced 0.918373 s in 1.0017 s; reached its end; captured 440 Hz PCM
WebAudio oscillator Clock advanced 2.0053 s in 2.002 s; captured 440 Hz PCM
ALSA device discovery Before: zero hints. After: one real playback hint, hw:CARD=XerisHDA,DEV=0, which opened successfully
Guest libasound device setup All 16 rate/channel/device configurations passed after negotiation fixes
Guest PCM handle lifetime Passed: closing an older stream preserves the replacement's prepared state; snd_pcm_drop succeeds
YouTube website/player Public 19.021 s clip completed at 320x240, unmuted at volume 1; 288 total frames, 13 dropped, and captured PCM

Evidence is local and ignored by Git:

  • build/media-playback/telemetry.jsonl contains the four video runs and Blob WAV run 1791009088381-fde2edd70689b8, including clock measurements.
  • build/youtube-qemu/blob-tone-pcm.json records 264,550 nonzero sample values across both channels, spanning 3.179274 s from first to last active sample.
  • build/youtube-qemu/hints-webaudio-pcm.json records 171,216 nonzero sample values across both channels, spanning 1.965 s. Both captures are stereo, 44.1 kHz, with a measured tone near 440 Hz.
  • build/youtube-qemu/alsa-hints-before.log and alsa-hints-after.log preserve the discovery comparison and guest probe results.
  • build/youtube-qemu/youtube-hints-pcm.json records 1,667,826 nonzero sample values and 19.1814 s from first to last active sample. The actual website test used https://www.youtube.com/watch?v=jNQXAC9IVRw; bounded one-second samples showed approximately normal clock progress through ended: true, with 251,930 decoded audio bytes. This was website playback, not a downloaded test asset. The separate YouTube controls attempt did not complete because a later page load stalled.
  • build/youtube-qemu/youtube-success-and-oom.log preserves that successful playback and the later fault after further navigation. The kernel reported committed=4862MB dry=1 before the failure in the 8 GiB guest. The broader out-of-memory/page-table rollback repair was not applied. A separate earlier loading stall remained in a Blink hash-table insertion loop; its cause has not been established.
  • build/youtube-qemu/youtube-controls-loading-stall.log records a fresh-run loading stall without physical pool exhaustion (dry=0). A bounded 9.74 s observation found no recurrence of the earlier Blink loop. The cause remains unverified; it must not be attributed solely to the out-of-memory failure.

Physical speaker output has not been verified. Frame counters include seeking and are not a frame-pacing benchmark. Earlier PCM completion records used the video element's state by mistake; use the newer element-specific clock records and separate PCM captures. An empty capture segment is not evidence of silence being submitted to the device.

These results follow fixes for expired scheduler waits, ALSA negotiation and status/control ioctls, per-handle playback ownership, and HDA consumption across ring wrap and underruns. ALSA readiness now honors the negotiated buffer and avail_min; protected BSP maintenance clears consumed samples. Adding a hint for the real hardware PCM also corrected WebAudio's device discovery: its earlier clock advanced through Chromium's fake output path without producing any captured PCM.

Prepare a disposable runtime#

Use a separate test disk/profile and a kernel/symbol pair from the same build. The browser and its runtime libraries are separate inputs; they are not included by these test tools. Keep the tested browser version and launch arguments with the run's evidence.

Before creating the guest data image, ensure its staged payload includes:

  • FONTS/UI.TTF, copied from the repository's Inter-Regular.ttf, with its font license retained in any distributed package.
  • XKBMAP.TXT, copied from assets/XKBMAP.TXT, with assets/xkb-data.copyright retained when distributing it.
  • The complete existing browser/runtime payload, including its loader and required shared libraries.

Missing fonts can make the browser render incorrectly. Missing XKBMAP.TXT prevents the Wayland keyboard map from being supplied. Check the serial log for font activation and for keymap: no /XKBMAP.TXT before debugging codecs. Use QEMU user-mode networking for the host fixture address below, and an HDA device plus an audio backend when testing sound. Record audio to a disposable WAV if useful; check its samples for actual non-silent output, not merely that a file exists.

Run the local baseline#

From the repository root with Python 3; Node.js is needed only for the JavaScript regression checks:

python tools/media-playback/server.py --download --prepare
python tools/media-playback/test_server.py
node tools/media-playback/test_clock.js
node tools/media-playback/test_blob_tone.js
python tools/media-playback/server.py --port 8765

The downloader uses pinned official Chromium test clips and generates a 440 Hz WAV. Downloads, their license/provenance, and telemetry stay under ignored build/media-playback/; no YouTube content is downloaded. The server binds only host loopback and serves an explicit list of test resources.

Open these in the guest browser:

URL Test
http://10.0.2.2:8765/?auto=1 VP8/Vorbis HTML5
http://10.0.2.2:8765/?auto=1&clip=vp9 VP9/Opus HTML5
http://10.0.2.2:8765/?auto=mse VP8/Vorbis MSE
http://10.0.2.2:8765/?auto=mse&clip=vp9 VP9/Opus MSE

Run all four when validating a change. For audio without network delivery during playback, click Download WAV baseline, wait for a successful download, then click Play downloaded WAV. The URL tone remains a separate network-path check; a full buffered range alone does not prove all bytes remain cached. Use the sound, tone, and fullscreen buttons with a real click. Tone results have scope: progress-only and audibleOutput: unverified: a moving media clock does not prove audio reached the device. Listen or validate captured PCM separately. Stop the server with Ctrl+C when finished. See the fixture README for telemetry details and limitations.

Remaining work and acceptance criteria#

  1. Resolve intermittent page-loading stalls and memory-pressure failures. Retain the passing playback baseline while diagnosing each failure; neither the earlier hash-table loop nor hint-table pressure explains every stall.
  2. Complete YouTube pause/resume, seeking, fullscreen enter/exit, and quality change checks. Preserve player state, selected codecs, browser diagnostics, and captured PCM alongside the completed 19-second website playback run.
  3. Test more videos and longer playback. Investigate network buffering separately from the passing Blob WAV baseline; the earlier URL tone still had delivery stalls. Do not substitute downloaded/converted YouTube content for website tests.
  4. Repeat longer clips, browser navigation, and simultaneous activity while measuring frame pacing, dropped frames, and audio/video sync. The IPC, keyboard-locking, timer, and audio regressions do not replace runtime tests.
  5. Repeat on the Beelink at realistic resolutions and verify physical speaker output. Keep production tracing disabled during timing measurements.

Relevant host regressions (a host C compiler is required; pass --cc if needed):

python tools/test_abi_audio.py
python tools/test_abi_fionread.py
python tools/test_hda_stream_ring.py
python tools/test_bsp_audio.py
python tools/test_scheduler_deadlines.py
python tools/test_scm_send.py
python tools/test_socket_shutdown.py
python tools/test_wayland_fullscreen.py
python tools/test_wayland_keys.py

For the real-libasound regression, build tools/build_alsa_guest_probe.ps1 against the staged runtime, stage its ALSAPROBE.ELF beside the loader/libraries, and run /ALSAPROBE.ELF in the disposable guest. Its default run checks device discovery and opening, handle lifetime, and all 16 format configurations.

The native Video app remains a separate, silent MJPEG player with an 8 MiB file cap and 512x512 frame limit. Its VIDEO.MJP path is not the browser/YouTube playback path.

This page mirrors the file in the XerisOS repository. Changes go there. Edit on GitHub