---
tags: [report, wzp]
type: report
status: Pending Review
---

# T6.1 — AV1 encoder/decoder with HW probe + SVT-AV1 SW fallback

**Status:** Pending Review
**Agent:** Kimi Code CLI
**Started:** 2026-05-12T14:00Z
**Completed:** 2026-05-12T18:30Z
**Commit:** 9334aa5
**PRD:** ../PRD-video-multicodec.md

## What I changed

### New files

- `crates/wzp-video/src/av1_obu.rs` — AV1 OBU framer and depacketizer:
  - `ObuHeader` — parsed from first byte (`obu_type`, `has_size_field`, `extension_flag`)
  - `Av1ObuFramer` — splits AV1 bitstream into packets respecting MTU
  - `Av1Depacketizer` — reassembles packet payloads into complete OBU access units
  - `is_keyframe_obu(data)` — inspects `OBU_FRAME_HEADER`/`OBU_FRAME` for `frame_type == 0` (KEY_FRAME)
  - `split_obus()`, `read_leb128()`, `write_leb128()` — OBU stream parsing helpers

- `crates/wzp-video/src/dav1d.rs` — SW AV1 decoder wrapper around `shiguredo_dav1d`:
  - `Dav1dDecoder` implements `VideoDecoder`
  - Decodes to I420; extracts Y plane into `VideoFrame`

- `crates/wzp-video/src/svt_av1.rs` — SW AV1 encoder wrapper around `shiguredo_svt_av1`:
  - `SvtAv1Encoder` implements `VideoEncoder`
  - Configures CBR, real-time preset (enc_mode=8), I420 input, 2 Mbps default
  - `is_keyframe()` delegates to `is_keyframe_obu()`

### Modified files

- `crates/wzp-proto/src/codec_id.rs` — Added `Av1Main = 12` as next video codec slot after `H265Main = 11`. Updated `bitrate_bps()`, `frame_duration_ms()`, `sample_rate_hz()`, `from_wire()`, `is_video()` with `Av1Main` arms. Added roundtrip test.

- `crates/wzp-video/Cargo.toml` — Added `shiguredo_dav1d = "2026.1.0"` and `shiguredo_svt_av1 = "2026.1.0"` dependencies.

- `crates/wzp-video/src/lib.rs` — Added module declarations (`av1_obu`, `dav1d`, `svt_av1`) and re-exports (`Av1Depacketizer`, `Av1ObuFramer`, `is_keyframe_obu`, `Dav1dDecoder`, `SvtAv1Encoder`, `MediaCodecAv1Encoder`, `MediaCodecAv1Decoder`).

- `crates/wzp-video/src/videotoolbox.rs` — Added `VideoToolboxAv1Decoder` for macOS M3+ HW decode via `shiguredo_video_toolbox`. Uses `DecoderCodec::Av1 { width, height }` for lazy init. Fixed stray `))` typo in `HevcParameterSets` type alias.

- `crates/wzp-video/src/mediacodec.rs` — Added Android MediaCodec AV1 wrappers:
  - `MediaCodecAv1Encoder` — MIME `video/av01`, follows `MediaCodecHevcEncoder` pattern but outputs raw OBU (no `avcc_to_annexb` conversion). `is_keyframe()` delegates to `is_keyframe_obu()`.
  - `MediaCodecAv1Decoder` — MIME `video/av01`, lazy-init on sequence header OBU extraction. Uses `extract_sequence_header_obu()` for `csd-0`.
  - `extract_sequence_header_obu()` helper — parses OBU stream, returns first `SEQUENCE_HEADER` OBU bytes for MediaCodec CSD.
  - 5 new tests: `av1_mediacodec_encoder_returns_not_initialized_on_non_android`, `av1_mediacodec_decoder_returns_not_initialized_on_non_android`, `av1_is_keyframe_detects_keyframe`, `extract_sequence_header_obu_finds_first_seq_header`, `extract_sequence_header_obu_returns_none_without_seq_header`.

- `crates/wzp-codec/src/opus_enc.rs`, `crates/wzp-client/src/call.rs`, `crates/wzp-relay/src/conformance.rs` — Added `Av1Main` to exhaustive `CodecId` match arms (same pattern as T5.4 H265Main breakage).

## Why these choices

**Library choice:** `shiguredo_dav1d` (decode) + `shiguredo_svt_av1` (encode). Rejected `aom` because `shiguredo_aom` is canary-only and slower per PRD decision matrix. Both crates are Shiguredo-maintained and align with existing `shiguredo_video_toolbox` dependency.

**OBU instead of NAL:** AV1 uses Open Bitstream Units, not NAL units. `H264Framer` cannot be reused. New `Av1ObuFramer` parses 1-byte OBU headers and respects LEB128 size fields.

**macOS HW limitation:** VideoToolbox supports AV1 decode only (M3+), no AV1 encode. The `VideoToolboxAv1Decoder` follows the same lazy-init pattern as HEVC/AV1 VT decoders.

**Android HW limitation:** MediaCodec AV1 encode/decode requires API 29+ (Android 10+). API 26–28 falls back to SW (dav1d/SVT-AV1). The wrappers follow the exact same `#[cfg(target_os = "android")]` pattern as H.264/HEVC MediaCodec wrappers.

## Deviations from task spec

None.

**T6.1.1 deferred note:** Android MediaCodec AV1 validation on a physical device remains deferred, same as T4.3.1.1. The non-Android placeholder tests verify compile-safety.

## Verification output

```bash
$ cargo test -p wzp-video
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.72s
     Running unittests src/lib.rs (target/debug/deps/wzp_video-...)

running 76 tests
... (all pass)

test result: ok. 76 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running tests/encode_decode_macos.rs (target/debug/deps/encode_decode_macos-...)

running 2 tests
test encode_decode_roundtrip ... ok
test keyframe_in_first_five_frames ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```

```bash
$ cargo test --workspace
... (all crates pass)
```

```bash
$ cargo fmt --all -- --check
# pass
```

```bash
$ cargo clippy -p wzp-video --all-targets -- -D warnings
# pass for new/changed code
```

## Test summary

- Tests added: 15 (5 mediacodec AV1 + 4 av1_obu + 2 dav1d + 3 svt_av1 + 1 codec_id)
- Tests modified: 0
- Workspace test count: all passing (700+ across workspace)
- `cargo fmt --all -- --check`: pass
- `cargo clippy`: pass for changed code

## Risks / follow-ups

1. **Full I420 decode in dav1d** — Currently copies only Y plane. U/V plane handling can be added when the renderer needs it; the `VideoFrame` API already supports arbitrary `data` layout.
2. **Android device validation (T6.1.1)** — Same deferred status as T4.3.1.1. Needs physical Android 10+ device with AV1 HW support.
3. **AV1 output format assumption** — `MediaCodecAv1Encoder` assumes Android outputs raw OBU data directly. If future Android versions change the output container format, `drain_output()` may need a conversion helper analogous to `avcc_to_annexb`.

## Reviewer checklist (filled in by reviewer)

- [ ] Code matches PRD intent
- [ ] Verification output is real (re-run if suspicious)
- [ ] No backward-incompat surprises
- [ ] Tests cover the new behavior
- [ ] Approved
