Files
wz-phone/docs/PRD/PRD-android-mediacodec-ndk9.md
Siavash Sameni 06253fdeeb feat(video+desktop): camera capture, video UI, E2E AEAD wiring, test fixes
Blockers 4 & 5: browser getUserMedia → JPEG IPC → Rust I420 pipeline;
remote video strip renders decoded frames via canvas; EncryptingTransport
wraps QuinnTransport so WZP AEAD is applied to all media (C2 fix).

Test fixes: HandshakeResult.session destructuring across relay/client/crypto
integration tests; video_codecs field added to all CallOffer/CallAnswer
structs; wzp-video pipeline_roundtrip integration tests added.

PRD docs: five Kimi-ready specs for E2E encryption, Android NDK 0.9 migration,
quality upgrade flow, wire-format hardening, and clippy debt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:30:26 +04:00

226 lines
7.6 KiB
Markdown
Raw Permalink 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.
# PRD: Android MediaCodec NDK 0.9 Compatibility
> **Status:** proposed
> **Resolves:** 31 compile errors in `crates/wzp-video/src/mediacodec.rs` blocking all Android video.
> **Depends on:** Remote build server `manwe@188.245.59.196` with Docker image `wzp-android-builder:latest`.
## Problem
`crates/wzp-video/src/mediacodec.rs` fails to compile for
`aarch64-linux-android` against the NDK 0.9 Rust crate. There are 31 errors
in 5 categories. Android video is completely blocked.
The file already compiles for non-Android targets (all Android code is behind
`#[cfg(target_os = "android")]`). Only the Android target path needs fixing.
## Goals
- `cargo build --target aarch64-linux-android -p wzp-video` produces 0 errors on the remote server.
- Each fix category lands in a separate commit so failures can be bisected.
- Non-Android compilation is not broken.
## Non-goals
- Upgrading the NDK Docker image or changing the NDK version.
- Fixing video functionality beyond compilation (runtime testing is a separate task).
- Any files outside `crates/wzp-video/`.
## Design
### Build command (run after each fix)
```bash
ssh manwe@188.245.59.196 'cd ~/wzp-builder/data/source && \
git fetch github && git reset --hard github/experimental-ui && \
docker run --rm \
-v ~/wzp-builder/data/source:/build/source \
-v ~/wzp-builder/data/cache/cargo-registry:/home/builder/.cargo/registry \
-v ~/wzp-builder/data/cache/cargo-git:/home/builder/.cargo/git \
-v ~/wzp-builder/data/cache/target:/build/source/target \
wzp-android-builder:latest bash -c \
"cd /build/source && cargo build --target aarch64-linux-android -p wzp-video 2>&1 | grep -E \"^error\" | head -30"'
```
### Fix order (commit one per category)
#### Fix 1 — `E0433`: `ndk_sys` not declared as a dependency
**Symptom**: `use of undeclared crate or module 'ndk_sys'`
**File**: `crates/wzp-video/Cargo.toml`
NDK 0.9 no longer re-exports raw `ndk_sys` symbols; they must be declared as
a direct dependency. Add to the `[target.'cfg(target_os = "android")'.dependencies]`
section (or create it if absent):
```toml
[target.'cfg(target_os = "android")'.dependencies]
ndk = { version = "0.9" }
ndk-sys = { version = "0.6" } # ndk 0.9 depends on ndk-sys 0.6
```
If `mediacodec.rs` only uses safe wrappers from the `ndk` crate and the
`ndk_sys` imports are not strictly needed, remove the `use ndk_sys::*` lines
from `mediacodec.rs` instead — whichever approach results in fewer changes.
After this fix the `E0433` errors should drop from the build output.
#### Fix 2 — `E0425`: `BITRATE_MODE_CBR` constant missing
**Symptom**: `cannot find value 'BITRATE_MODE_CBR' in this scope`
**File**: `crates/wzp-video/src/mediacodec.rs`
`BITRATE_MODE_CBR` is already defined as a local constant at line 44:
```rust
#[cfg(target_os = "android")]
const BITRATE_MODE_CBR: i32 = 2;
```
If the error persists after Fix 1, the issue is that `ndk_sys` was providing
a conflicting symbol. Verify the constant is still at line 44 after Fix 1. If
NDK 0.9 moved `BITRATE_MODE_CBR` to an enum, update the usage at line 516
(`format.set_i32("bitrate-mode", BITRATE_MODE_CBR)`) to use the integer
value directly (`2`) or update the constant's value.
If `ndk 0.9` defines `MediaCodecBitrateMode::Cbr` as an enum, the call site
in `MediaCodecAv1Encoder::new` (line ~516) can be updated to:
```rust
format.set_i32(
"bitrate-mode",
ndk::media::media_codec::MediaCodecBitrateMode::Cbr as i32,
);
```
#### Fix 3 — `E0308`: `InputBuffer` returns `&mut [MaybeUninit<u8>]`
**Symptom**: `expected &mut [u8], found &mut [MaybeUninit<u8>]`
**File**: `crates/wzp-video/src/mediacodec.rs`
NDK 0.9 changed `InputBuffer::buffer_mut()` from `&mut [u8]` to
`&mut [MaybeUninit<u8>]`. There are multiple write sites in the file — all
follow the same pattern:
```rust
// Before (NDK 0.8):
let buf = buffer.buffer_mut(); // &mut [u8]
let n = frame.data.len().min(buf.len());
buf[..n].copy_from_slice(&frame.data[..n]);
```
```rust
// After (NDK 0.9):
let buf = buffer.buffer_mut(); // &mut [MaybeUninit<u8>]
let n = frame.data.len().min(buf.len());
for (d, &s) in buf[..n].iter_mut().zip(frame.data[..n].iter()) {
d.write(s);
}
```
The file already uses the `d.write(s)` pattern in some places (lines 125127,
297299, etc.). Search for **every** occurrence of `buffer.buffer_mut()` and
`buffer_mut()` and apply the same pattern. Affected structs:
`MediaCodecEncoder::encode` (~line 123), `MediaCodecDecoder::decode`
(~line 294), `MediaCodecHevcEncoder::encode` (~line 439),
`MediaCodecHevcDecoder::decode` (~line 773), `MediaCodecAv1Encoder::encode`
(~line 560), `MediaCodecAv1Decoder::decode` (~line 907).
Do NOT use `unsafe { std::mem::transmute }` — the `d.write(s)` pattern is
already present and safe.
Note: if the file already uses `d.write(s)` everywhere, this category may
already be addressed by the existing code. Check the actual error count.
#### Fix 4 — `E0599`: `.index()` is private
**Symptom**: `method 'index' is private`
**File**: `crates/wzp-video/src/mediacodec.rs`
NDK 0.9 removed the public `.index()` method from `DequeuedInputBuffer` and
`DequeuedOutputBuffer`. The pattern that broke:
```rust
// Broken: buffer.index() is private in NDK 0.9
let idx = buffer.index();
codec.queue_input_buffer_index(idx, ...);
```
In NDK 0.9 the correct API is to pass the buffer object directly to
`queue_input_buffer`:
```rust
codec.queue_input_buffer(buffer, offset, size, pts_us, flags)?;
```
The file already uses `codec.queue_input_buffer(buffer, 0, to_copy, ...)` in
most places (lines 131, 303, 447, etc.). Search for any remaining `.index()`
calls on buffer objects and replace them with the direct-pass pattern shown
above.
#### Fix 5 — `E0277`: `NonNull<AMediaCodec>` is not `Send`
**Symptom**: `NonNull<AMediaCodec>` cannot be sent between threads safely
**File**: `crates/wzp-video/src/mediacodec.rs`
Each codec struct must have an `unsafe impl Send` declaration. Audit all six
codec structs:
| Struct | `unsafe impl Send` present? |
|--------|----------------------------|
| `MediaCodecEncoder` | Yes (line 51) |
| `MediaCodecDecoder` | Yes (line 228) |
| `MediaCodecHevcEncoder` | Yes (line 374) |
| `MediaCodecHevcDecoder` | Yes (line 705) |
| `MediaCodecAv1Encoder` | Yes (line 503) |
| `MediaCodecAv1Decoder` | Yes (line 844) |
If any are missing, add them with a safety comment:
```rust
// SAFETY: AMediaCodec is documented as thread-safe.
#[cfg(target_os = "android")]
unsafe impl Send for MediaCodecXxxYyy {}
```
This category may already be clean. Confirm with the build output.
## Implementation steps
1. Push the current branch to `github/experimental-ui` before starting.
2. **Commit 1**: Fix `ndk_sys` dependency (`Cargo.toml`). Push. Run build.
Confirm `E0433` errors drop.
3. **Commit 2**: Fix `BITRATE_MODE_CBR`. Push. Run build. Confirm `E0425` gone.
4. **Commit 3**: Fix `MaybeUninit` write sites. Push. Run build. Confirm
`E0308` gone.
5. **Commit 4**: Remove any `.index()` calls. Push. Run build. Confirm
`E0599` gone.
6. **Commit 5**: Add missing `unsafe impl Send` if any. Push. Run build.
Confirm `E0277` gone and total error count is 0.
## Files to read before implementing
- `crates/wzp-video/src/mediacodec.rs` (full file — 45 KB; read in chunks)
- `crates/wzp-video/Cargo.toml` (check existing `[dependencies]` sections)
## Verify
Final build command (see Design section). Expected output: no lines matching
`^error`.
Also verify non-Android host still compiles:
```bash
cargo check -p wzp-video
```
## Done when
`cargo build --target aarch64-linux-android -p wzp-video` on the remote
server produces 0 `error[...]` lines. Non-Android `cargo check -p wzp-video`
also passes.