b2cf4e908cb1b9524dbf611ca291dc33ac327d67
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6a6be17ce7 |
fix(client/android): a session that receives no video at all now asks, and says so
The keyframe backstop added for the black LG TV only arms while AUs are actually going into the decoder (`fed > fed_at_output`) — deliberately, so an idle stream never asks for anything. That leaves its mirror image uncovered: a session that receives NOTHING. A decoder cannot be starved of output when it was handed no input, so no signal in either loop fires, and the session sits connected — audio, input and the control plane all alive — behind a black surface. That state is what a user just reported as "the stats are all basically 0": fps and Mb/s are counted at AU receipt (`note_received`), so all-zero stats with a drawn overlay means the decode thread started and received nothing. Same bug as the black screen, seen from the HUD. Both loops now watch for it: nothing received 1.5 s into a session ⇒ request a keyframe and log it, re-asking every 2 s while it lasts. Where it can help it does — the host encoding fine while every picture references an IDR this client never saw is precisely a keyframe request away. Where it can't, the log line is the point: "no video received N ms into the session" separates "the host never sent a picture" from "we received AUs and lost them", which no previous black-screen report could distinguish. Not a root cause. The remaining occurrences are still unattributed — this makes the next report diagnosable and recovers the case that is ours to recover. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e44baf6768 |
fix(client/android): a decoder fed nothing it can decode must ask for a new anchor
android / android (push) Canceled after 33s
apple / swift (push) Canceled after 27s
apple / screenshots (push) Canceled after 0s
arch / build-publish (push) Canceled after 34s
ci / rust (push) Canceled after 8s
ci / rust-arm64 (push) Canceled after 3s
ci / web (push) Canceled after 2s
ci / docs-site (push) Canceled after 3s
ci / bench (push) Canceled after 2s
deb / build-publish (push) Canceled after 6s
deb / build-publish-host (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Canceled after 0s
decky / build-publish (push) Canceled after 7s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 15s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 14s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 6s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 6s
An Android TV box reported a black screen with a perfectly healthy HUD: AUs arriving at 40 fps, ~312 bytes each. That size is all-P-frames — no IDR anywhere in the window, and nothing asking for one. The stats being readable is the rest of the story: the overlay is a layer over the SurfaceView in the same window, so the panel was fine and the surface simply never received a frame. The decode thread only starts at `surfaceCreated`, so a slow box can be handed the stream mid-GOP. A hardware decoder does not error on references it never had; it emits nothing at all. Under infinite GOP the host sends no further IDR unless asked, and neither Android loop ever asked: every recovery trigger they have keys off a drop, a gap or a decode error, and a decoder that quietly produces nothing trips none of them. The session stayed black for its whole life. The shared gate has this case (`on_no_output`, which pf-client-core and the Apple client both feed) but its per-AU streak counts one-in/one-out decodes, and MediaCodec is pipelined — "this AU produced no output" is not something these loops can observe. A wall-clock silence window is the same signal in the shape Android can measure: fed for 500 ms with nothing coming back arms the freeze and requests a re-anchor keyframe, and the gate's deadline keeps re-asking until one lands. 500 ms so it can never fire on a decoder that is merely slow to spin up. Also log the first presented frame. The periodic tally starts at 300 rendered frames, which is no help whatsoever on a session that renders none — its absence is what separates "never reached glass" from "reached glass and looked wrong". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a784682d4c |
fix(client/net): split the receipt stamp from the pull + bleed standing latency
ci / docs-site (push) Successful in 56s
apple / swift (push) Successful in 1m14s
ci / web (push) Successful in 2m30s
decky / build-publish (push) Successful in 19s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
ci / bench (push) Successful in 5m57s
release / apple (push) Successful in 9m8s
deb / build-publish (push) Successful in 9m33s
arch / build-publish (push) Successful in 13m14s
docker / deploy-docs (push) Successful in 23s
flatpak / build-publish (push) Failing after 8m1s
deb / build-publish-host (push) Successful in 10m24s
android / android (push) Successful in 14m56s
apple / screenshots (push) Successful in 6m43s
ci / rust (push) Successful in 19m29s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m35s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m29s
windows-host / package (push) Successful in 15m13s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 5m48s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 6m38s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 7m45s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 8m51s
The two-pair investigation (wired Mac clients stuck at a rock-steady ~18-19 ms "network" that survived the load ending and cleared only on reconnect) exposed two structural gaps, one of measurement and one of recovery: - Receipt was stamped at the hand-off PULL (Swift nextAU, pf-client-core, Android decode loops), not at reassembly completion — so any client-side standing state between the reassembler and the pull read as NETWORK latency, undiagnosable from the HUD. ABI v9: `PunktfunkFrame`/`Frame` grow `received_ns`, stamped by `Session::poll_frame` as the AU crosses the session boundary. Every embedder now uses the core stamp; the Apple client keeps the pull instant as `AccessUnit.pulledNs` and shows the receipt→pull wait as its own "client queue" term (detailed HUD tier from 2 ms + a `queue_p50` stats-log field). Decode stages keep their pull anchor on all platforms, so no historical stage shifts meaning. - The jump-to-live detectors deliberately ignore anything under 6 queued frames / 400 ms behind — so a small, constant, loss-free elevation (a sub-frame standing backlog, or a stale clock offset after a wall-clock step/slew) is carried for the rest of the session. New third detector (`StandingLatency`, unit-tested ladder): window-MIN one-way delay ≥ 10 ms above the session floor with zero loss for ~4.5 s escalates gently — a free clock re-sync first (an applied re-sync re-bases the floor), then at most 3 flush+keyframe bleeds sharing the jump-to-live cooldown, then a loud disarm naming what it means. Loss windows reset the run: congestion belongs to FEC/ABR, not this detector. Also: mid-stream re-sync apply/discard logs debug→info — they are the forensic trail for the stale-offset case and were invisible in the field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f439b69451 |
refactor(android/W8): split decode.rs into decode/ directory module
Break the 1747-line clients/android/native/src/decode.rs into a decode/ directory
module (mod.rs + 5 concern submodules):
- decode/setup.rs : codec creation + low-latency config + thread/frame-rate
tuning + HDR static-info encode
- decode/display.rs : DisplayTracker + render-callback registration + HDR dataspace
- decode/latency.rs : realtime clock + decoded-pts / user-flags stat recording
- decode/sync_loop.rs : the synchronous poll decode loop (+ feed/drain) — moved WHOLE
- decode/async_loop.rs : the event-driven async decode loop (+ helpers) — moved WHOLE
decode/mod.rs keeps the consts, DecodeOptions, and the `run` entry point + the
`codec_mime`/`codec_label` re-export, so every crate::decode::X path stays byte-stable.
The module has no decoder struct (free functions + small types), so both decode loops
move byte-for-byte and their separately-inlined received-stat recording is NOT unified.
16 helper fns/types became pub(super) for sibling access; zero field bumps. lib.rs
unchanged (`#[cfg(target_os="android")] mod decode;` resolves to decode/mod.rs).
Verified: cargo-ndk check (aarch64-linux-android, clean) + the gradle cargoNdkDebug
build (arm64-v8a / armeabi-v7a / x86_64). On-device runtime re-verification still owed
per the plan (the two decode loops are a hot path).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|