Skip to content

feat: add H.264 hardware decoding#26

Draft
DatCaptainHorse wants to merge 1 commit into
hgaiser:mainfrom
DatCaptainHorse:feat/decoding-support
Draft

feat: add H.264 hardware decoding#26
DatCaptainHorse wants to merge 1 commit into
hgaiser:mainfrom
DatCaptainHorse:feat/decoding-support

Conversation

@DatCaptainHorse

Copy link
Copy Markdown
Contributor

PR changes

Adds decoding support, scoped to H.264 for now so there's not too much to review at once.. though I feel it already is 😅

LLM's summary:

Adds a stream-driven H.264 decoder alongside the existing encoder, plus a
shared Vulkan Video layer (src/video.rs, src/decoder/codec.rs) used by both
directions.

- Full reference management: IDR, sliding-window, and MMCO (B-pyramid).
- Display-order output by default via an internal reorder buffer, with a
  decode-order opt-out for lowest latency; DecodeConfig gains OutputOrder
  and Decoder gains flush().
- Readback and reorder copies run on the transfer queue; fixes transfer
  queue-family selection to avoid the video engine.
- Encoder B-frame guards return errors instead of panicking.
- Rejects unsupported streams (interlaced, scaling matrices) with clear errors.

Verified byte-identical to ffmpeg across AMD (RADV) and NVIDIA, including
B-pyramid, mid-stream resolution changes, and encode->decode round trips.

Comment

Whew.. this has been atleast a few weeks worth of work, I ended up getting a claude subscription since I was losing my mind over H.264 codec parsing and dealing with all the reference frame management, it's just not fun and goes over my brain's capacity to handle 😓

This is very much a draft, meaning WIP, RFC and all. API could be better IMO, and I'd like some feedback and guidance on what you wish for @hgaiser !

Currently codec parsing and actual decode are done separately for API user:

for au in access_units(&stream) {
    let decoded = match decoder.decode(au, frame_count as u64) {
...

incase someone wants to do their own parsing or do some magic by intercepting parsed data. When adding AV1 decoding in future, the parsing API needs changing to be more codec-agnostic though.

I've verified it works though in a real use-case, here's a picture showing pixelforge decoding integrated into a native app for playing Nestri streams, H.264 is decoded and shown perfectly fine
image

It's a lot to unpack, despite trying to limit the scope to H.264 decoding for now, there's a lot of boilerplate and logic needed for even just that, I apologize for the size of this PR 😅

Certain code parts are somewhat oriented towards H.26X codecs, so when adding AV1 decoding in future, it will need adjusting to be more clean and sane.

Aside from decoder changes, the encoder pub fn new.. was poked a bit to change the "b-frames unsupported" assertion into an Err return instead, allowing to gracefully handle that. Though I can revert that one since it's overreaching a bit here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hgaiser

hgaiser commented Jul 24, 2026

Copy link
Copy Markdown
Owner

I laughed when I read ... scoped to H.264 for now so there's not too much to review at once.. and looked at the amount of diff lines :p

Thanks for submitting it though, cool stuff. I had a rough implementation of decoding working a long time ago but decided to keep the scope relatively smaller by focusing only on encoding. That said, I'm completely open to adding decoding support. It will probably take some time to go over it.

For full transparency: I'll probably review the public API and use LLMs to review the internals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants