feat(video): decode VP8 and VP9 through libvpx - #4095
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3424ebad8d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
MERGE Positive improvement: yes. VP8/VP9 tracks were failing with Worth the complexity: yes. One focused backend (~575 lines) plus nix/CI/CLI wiring and quest bookkeeping. No frame threading keeps timestamps ordered with nothing to drain — appropriate for a first software path. Docs and media-feature scripts update with the capability. Different approach: waiting on hardware VP8/VP9 paths leaves OBS and software-only hosts broken. Shipping 10-bit / 4:2:2 / 4:4:4 now would widen the surface without a stated consumer; refusing them cleanly is better than silent wrong output. A pure-Rust decoder would avoid the native dep but is not ready; pinning a maintained sys crate with pregenerated bindings is the practical choice. This is an automated review, not the maintainer's decision |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bbae30b29d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b049acfc9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| VideoCodec::AV1(av1) if is_supported_av1(av1) => (Codec::Av1, Conversion::Passthrough), | ||
| VideoCodec::VP8 => (Codec::Vp8, Conversion::Passthrough), | ||
| VideoCodec::VP9(vp9) if is_supported_vp9(vp9) => (Codec::Vp9, Conversion::Passthrough), |
There was a problem hiding this comment.
Handle the publisher's generic VP9 codec string
When browser capability probing falls back to "vp09", which js/publish/src/video/encoder.ts:568-569 explicitly supports and then publishes unchanged, Rust parses it as VideoCodec::Unknown because rs/hang/src/catalog/video/codec.rs:68 recognizes only strings beginning with "vp09.". This new VP9 arm is therefore never reached for those valid browser-produced tracks, and playback still fails with UnsupportedCodec; normalize the publisher's selected codec or resolve the generic form from its first keyframe before applying the profile checks. (Written by GPT-5.6 Sol)
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Disagree. Bare vp09 is the browser probe hint, not an RFC 6381 codec string, and VideoCodec only accepts vp09. so the profile is known before subscribe. Decoder::new uses that profile (is_supported_vp9) and refuses anything else. Treating vp09 as profile 0, or reading the profile out of the first keyframe, would weaken that gate. The same generic fallback exists for avc1, av01, and hev1; it is a publisher/catalog question, not this decoder.
(Written by Grok 4.7)
|
Squash-merged onto (Written by Grok 4.7) |
Problem
moq-video had no VP8 or VP9 decoder, so those tracks failed with
UnsupportedCodec. The OBS decoder replacement put VP8/VP9 playback off until this quest landed (quest/m1/obs-moq-video/vpx.md).Approach
vpxdecode backend in moq-video (decode/backend/vpx.rs). It uses libvpx throughlibvpx-native-sys5.0.17 (kornelski/rust-vpx, updated 2026-08, ships pregenerated bindings for libvpx 1.5 through 1.17, so no libclang).Kind::AutoandKind::Software, and can be picked withKind::Named("vpx"). It decodes one coded frame per call (a VP9 superframe stays one unit) to CPU I420 and passes the input timestamp through. libvpx runs without frame threading, so nothing is reordered and there is no tail to drain.Decoder::new, using the catalog.UnsupportedCodec, even if the catalog claimed profile 0.CORRUPT_FRAME), the backend drops every delta frame until the next keyframe, so nothing is decoded against broken references. Any other libvpx error is fatal, includingUNSUP_BITSTREAM, which libvpx documents as a stream it cannot parse.Bt601Limited, the only color space RFC 6386 defines. VP9 takes its color from the bitstream'scolor_spaceand range. Other spaces are left asNone.pipewireandvaapi, because libvpx comes from the build host.libvpx(nixpkgs 1.16.0) and setsVPX_STATIC=1, so the archive is linked in. The test binary has noNEEDED libvpx.--all-features, not built under Nix) now runsbrew install libvpx pkgconf.rs/scripts/media-features.shcompiles thevpxshape, checks its dependency graph, and runs its tests.vpxpassthrough feature, and with itplayaccepts--video-codec vp8|vp9, and the docs are updated (doc/bin/cli.md,doc/lib/rs/moq-video.md, the README).Impact
moq_video::decode::CodecgainsVp8andVp9. This is additive, because the enum is#[non_exhaustive].moq_video::decode::NAMESgains"vpx".moq-video/vpxandmoq-cli/vpx.Decoder::newnow acceptsVideoCodec::VP8andVideoCodec::VP9(profile 0, 8-bit 4:2:0) where it used to returnUnsupportedCodec.Alternatives
The plan asked for vendored libvpx through a maintained binding. No maintained crate ships the libvpx source, so this was a maintainer decision; I picked an option and recorded the others here:
libvpx-native-syswith libvpx from the build host, linked statically. Nix pins the version. The binding is small and maintained, and needs no libclang or network access at build time.shiguredo_libvpx2026.1.0 (libvpx 1.16):source-buildfeature runsgit cloneat build time and needs bindgen. It builds Windows with mingw and cannot cross-compile.ccbuild with pregeneratedvpx_config.hand rtcd headers, with SIMD added later. This is the only option that also covers Windows MSVC and crates.io users with no libvpx installed. It is also a bespoke build of about 250 C files to maintain. Deferred to the follow-up quest, to be chosen only if the Windows OBS build needs it.rusty_vp9/vp9dec: too new, and VP9 only.libvpx 1.17.0 is the newest upstream release, but nixpkgs pins 1.16.0. The binding supports both.
Follow-ups
quest/m1/obs-moq-video/vpx-obs.md: turn onvpxfor the OBS moq-ffi/libmoq builds once the source replacement lands, pick a static libvpx for Windows MSVC (vcpkg or the in-tree build), and check decoded pixels and the imports in the plugin. The OBS-source half of this quest's verification moves there because the OBS source does not decode through moq-video yet.quest/m2/video-codec-coverage.mdnow owns hardware VP8/VP9) and odd-dimension pictures (I420 needs even sizes, so these fail loudly).🤖 Generated with Claude Code
(Written by Claude Opus 5.5)