06 — One API, many containers (AV1 + WebM + Opus)

Every tutorial so far played MPEG-1. This one plays AV1 — the royalty-free codec — with no change to your code. video_open sniffs the file’s magic bytes and picks the backend: pl_mpeg for an MPEG-1 .mpg, dav1d for a raw AV1 .ivf, and nestegg + dav1d + libopus for a WebM container carrying AV1 video alongside Opus audio. Open, decode, borrow — the surface never names the codec.

(That clip is Emma explaining WebM — and it plays as a WebM: the demo below decodes the very thing she’s describing, AV1 video and Opus sound, through the same calls.)

The library

probe_media opens a path, decodes the whole stream, and reports what came out. It is the MPEG-1 decode loop from tutorials 02 and 05 verbatimvideo_decode for the AV1 frames, video_enable_audio + video_decode_audio + the get_audio_data borrow for the Opus audio. The only thing that changed is the file you hand it.

require video
require daslib/defer
require math

struct MediaProbe {
    width, height  : int
    has_audio      : bool
    samplerate     : int
    audio_channels : int
    video_frames   : int
    audio_batches  : int
    audio_peak     : float
}

// Open `path` (format auto-detected), decode the whole stream, and report what came
// out — proving the WebM / AV1 / Opus path runs through the exact same surface as
// the MPEG-1 tutorials. Audio is opt-in, so enable it when the file has a track.
def public probe_media(path : string) : MediaProbe {
    var p = video_open(path)
    if (p == null) return MediaProbe()
    defer() { video_close(p) }
    var m = MediaProbe(width = video_width(p), height = video_height(p),
        has_audio = video_has_audio(p))
    if (m.has_audio && video_enable_audio(p)) {
        m.samplerate = video_samplerate(p)
        m.audio_channels = video_audio_channels(p)
    }
    // Decode the AV1 video frames. dav1d does the work; `get_data` would borrow the
    // pixels exactly as in tutorial 02 — here we just count them.
    while (video_decode(p)) {
        m.video_frames++
    }
    // Decode the Opus audio. libopus hands back interleaved float PCM through the
    // same `get_audio_data` borrow as the MP2 audio in tutorial 05.
    while (video_decode_audio(p)) {
        m.audio_batches++
        p |> get_audio_data() $(s : array<float>#) {
            for (v in s) {
                m.audio_peak = max(m.audio_peak, abs(v))
            }
        }
    }
    return m
}

Two notes. Opus always decodes at 48 kHz, so video_samplerate reports 48000 for any WebM regardless of the source rate; video_audio_channels reports the real channel count (1 or 2). And the AV1 path is 8-bit 4:2:0 only — 10-bit / HDR / 4:2:2 / 4:4:4 fail closed with a clear message rather than decoding wrong, by design.

Self-verifying

The test decodes the bundled sample.webm (Emma’s WebM, 640×640 AV1 + stereo Opus) and checks that both tracks came through — AV1 frames from dav1d, Opus batches from libopus, at 48 kHz with real amplitude.

require webm_tut

[export]
def main() {
    let m = probe_media("tutorials/06_webm/sample.webm")
    verify(m.width == 640 && m.height == 640, "the WebM reports its 640x640 size")
    verify(m.video_frames > 0, "AV1 video decodes through dav1d")
    verify(m.has_audio, "the WebM carries an Opus audio track")
    verify(m.samplerate == 48000, "Opus decodes at 48000 Hz")
    verify(m.audio_channels == 2, "the demo's Opus audio is stereo")
    verify(m.audio_batches > 0, "Opus audio decodes through libopus")
    verify(m.audio_peak > 0.01, "decoded audio is real (Emma's voice, not silence)")
    print("tutorial 06 ok: {m.video_frames} AV1 frames, {m.audio_batches} Opus batches at {m.samplerate}Hz\n")
}

Running it

Run it from the repo root — the test opens its sample with a repo-root-relative path:

cd <dasVideo>
daslang -load_module . tutorials/06_webm/test_webm_decode.das

prints:

tutorial 06 ok: 467 AV1 frames, 933 Opus batches at 48000Hz

The same examples/play_audio_gl.das from tutorial 05 plays this WebM on screen, with sound, A/V-synced — point it at tutorials/06_webm/sample.webm and watch. Because the API is format-agnostic, the windowed player needed no change to gain AV1 + WebM + Opus.

That closes the ladder. Anything not in the royalty-free set (H.264, VP9, Theora, …) you decode yourself and feed the same borrow API — dasVideo ships the codecs whose patents and licenses are both clear, and stays out of your way for the rest.