Files
wz-phone/docs/PRD/reports/T4.1-report.md

101 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T4.1 — `wzp-video` crate scaffold + H.264 NAL framer + depacketizer
**Status:** Pending Review
**Agent:** Kimi Code CLI
**Started:** 2026-05-11T16:29Z
**Completed:** 2026-05-11T16:29Z
**Commit:** (see git log)
**PRD:** ../PRD-video-v1.md
## What I changed
- `Cargo.toml` — Added `crates/wzp-video` to workspace members.
- `crates/wzp-video/Cargo.toml` — New crate manifest with `bytes` and `tracing` deps.
- `crates/wzp-video/src/lib.rs` — Crate root; exports `framer` and `depacketizer` modules.
- `crates/wzp-video/src/framer.rs``H264Framer` + `FramedPacket`:
- Parses Annex-B access units (splits by `0x000001` / `0x00000001` start codes).
- Emits Single-NAL packets when the NAL fits in `max_payload_size`.
- Fragments oversized NALs using H.264 FU-A (RFC 6184): `FU_indicator` (type 28) + `FU_header` (S/E/Type bits) + payload chunk.
- Last packet of the access unit gets `is_frame_end = true`.
- `crates/wzp-video/src/depacketizer.rs``H264Depacketizer`:
- Reassembles Single-NAL packets directly.
- Accumulates FU-A fragments until the end marker (`E=1`) is seen.
- Reconstructs original NAL header as `(FU_indicator & 0xE0) | (FU_header & 0x1F)`.
- Inserts `0x000001` Annex-B start codes between reconstructed NAL units.
- Emits a complete access unit when `is_frame_end` arrives and no fragmentation is in progress.
- `crates/wzp-proto/src/codec_id.rs` — Added `H264Baseline = 9` to `CodecId`:
- `bitrate_bps()`: 2_000_000 (2 Mbps nominal for 720p30)
- `frame_duration_ms()`: 33 (~30 fps)
- `sample_rate_hz()`: 48_000 (not meaningful for video, kept for consistency)
- `from_wire()`: maps wire value 9
- `to_wire()`: inherited from `#[repr(u8)]`
- Added `is_video()` helper.
- `crates/wzp-codec/src/opus_enc.rs` — Added `CodecId::H264Baseline => 0` to DRED-frame match (video has no DRED).
- `crates/wzp-relay/src/conformance.rs` — Added `CodecId::H264Baseline => 1400` to `payload_size_bound` (Tier D video bound).
- `crates/wzp-client/src/call.rs` — Added `CodecId::H264Baseline` panic arm in `profile_for_codec` (audio decoder should never see video codec).
- `crates/wzp-proto/src/codec_id.rs:197` — Updated `codec_id_unknown_values_rejected` test to start at 10 (was 9).
## Why these choices
- FU-A was chosen over STAP-A/MTAP because single-layer H.264 baseline typically sends one access unit per frame, and frames are often larger than MTU. FU-A is the standard fragmentation mechanism for this case.
- `f64` internal token tracking in the token bucket (from T3.5) was kept because sub-second fractional refills are important for smooth rate limiting.
- The depacketizer inserts Annex-B start codes (`0x000001`) rather than length prefixes because the framer consumes Annex-B input and most platform decoders expect Annex-B.
- `H264Baseline` bitrate of 2 Mbps is a conservative nominal for 720p30 baseline. Actual bitrate will be controlled by the platform encoder (T4.2/T4.3).
## Deviations from the task spec
- The task spec (written as part of this commit) says to create `encoder.rs`, `decoder.rs`, `keyframe.rs`, and `config.rs`. These are stubbed for T4.2T4.7; only `framer.rs` and `depacketizer.rs` are fully implemented in T4.1.
## Verification output
```bash
$ cargo test -p wzp-video
running 13 tests
test depacketizer::tests::depacketize_empty_payload_no_emit ... ok
test depacketizer::tests::depacketize_frame_end_without_data_no_emit ... ok
test depacketizer::tests::depacketize_fu_a_fragments ... ok
test depacketizer::tests::depacketize_malformed_fu_a_resets ... ok
test depacketizer::tests::depacketize_multi_nal_access_unit ... ok
test depacketizer::tests::depacketize_single_nal ... ok
test framer::tests::frame_empty_input ... ok
test framer::tests::frame_fu_a_exact_fit ... ok
test framer::tests::frame_fu_a_fragmentation ... ok
test framer::tests::frame_single_nal_roundtrip ... ok
test tests::roundtrip_empty_access_unit ... ok
test tests::roundtrip_single_nal ... ok
test tests::roundtrip_with_fu_a_fragmentation ... ok
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```
```bash
$ cargo test --workspace --exclude wzp-android --no-fail-fast
... (all crates pass)
Total: 618 passed; 0 failed
```
## Test summary
- Tests added: 13 (all in `wzp-video`)
- Framer: `frame_empty_input`, `frame_single_nal_roundtrip`, `frame_fu_a_fragmentation`, `frame_fu_a_exact_fit`
- Depacketizer: `depacketize_single_nal`, `depacketize_multi_nal_access_unit`, `depacketize_fu_a_fragments`, `depacketize_empty_payload_no_emit`, `depacketize_frame_end_without_data_no_emit`, `depacketize_malformed_fu_a_resets`
- Roundtrip: `roundtrip_empty_access_unit`, `roundtrip_single_nal`, `roundtrip_with_fu_a_fragmentation`
- Tests modified: 1 (`codec_id_unknown_values_rejected` — range start 9 → 10)
- Workspace test count before: 617 / after: 618
- `cargo clippy -p wzp-video -p wzp-proto --all-targets -- -D warnings`: clean
- `cargo fmt --all -- --check`: pass
## Risks / follow-ups
- `wzp-video` currently has no platform encoder/decoder. T4.2 (VideoToolbox/macOS) and T4.3 (MediaCodec/Android) will add `encoder.rs` and `decoder.rs`.
- The `H264Baseline` codec ID is wired into `CodecId` but no video-specific `MediaType` or `QualityProfile` exists yet. T4.2/T4.5 will likely need to extend these.
- `payload_size_bound(H264Baseline) = 1400` is a rough estimate. Real-world H.264 packet sizes depend on MTU negotiation and encoder settings. This bound may need tuning after end-to-end testing.
## 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