feat(android): Automatic prefers AV1 where the silicon says it should
ci / docs-site (push) Successful in 2m12s
ci / web (push) Successful in 2m36s
ci / rust-arm64 (push) Successful in 3m20s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 8s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 6s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 6s
android / android (push) Successful in 6m13s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 6s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 11s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 42s
ci / rust (push) Canceled after 6m49s
docker / builders-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Successful in 3m42s
arch / build-publish (push) Successful in 7m46s
deb / build-publish (push) Successful in 5m58s
deb / build-publish-host (push) Successful in 5m58s
apple / swift (push) Successful in 6m9s
apple / screenshots (push) Canceled after 3m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 6m53s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 6m21s
windows-host / package (push) Canceled after 0s
windows-host / canary-manifest (push) Canceled after 0s
windows-host / winget-source (push) Canceled after 0s
ci / docs-site (push) Successful in 2m12s
ci / web (push) Successful in 2m36s
ci / rust-arm64 (push) Successful in 3m20s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 8s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 6s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 6s
android / android (push) Successful in 6m13s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 6s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 11s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 42s
ci / rust (push) Canceled after 6m49s
docker / builders-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Successful in 3m42s
arch / build-publish (push) Successful in 7m46s
deb / build-publish (push) Successful in 5m58s
deb / build-publish-host (push) Successful in 5m58s
apple / swift (push) Successful in 6m9s
apple / screenshots (push) Canceled after 3m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 6m53s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 6m21s
windows-host / package (push) Canceled after 0s
windows-host / canary-manifest (push) Canceled after 0s
windows-host / winget-source (push) Canceled after 0s
The P3 format A/B (NP3 ↔ RTX 4090, identical conditions) measured AV1 ~1.2 ms faster end-to-end than HEVC with slightly better codec-pure decode time. Under "Automatic" the client now sends AV1 as its soft preference when this device hardware-decodes it (the advertised AV1 bit is already gated on a real, non-blocked hardware decoder) AND it lacks FEATURE_PartialFrame — a partial-frame device keeps HEVC, whose slice-progressive overlap AV1 cannot ride (no slices, the chunked poll never arms). The host honors the preference only inside its probed shared codec set, so an AV1-less encoder still resolves HEVC, and an explicit user choice wins unchanged. The codec picker caption mirrors the same rule so "Automatic" says what it does on this device. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -47,15 +47,27 @@ suspend fun connectToHost(
|
||||
// sockets) — must be applied before connect, since sockets are tagged at creation.
|
||||
NativeBridge.nativeSetLowLatencyMode(settings.lowLatencyMode)
|
||||
val multiSlice = VideoDecoders.multiSliceTolerant()
|
||||
val partialFrame = VideoDecoders.partialFrameCapable()
|
||||
// Slice-progressive delivery: decoder truth AND the async decode loop — the legacy
|
||||
// sync loop feeds whole AUs only, so parts must never arrive when it is selected.
|
||||
val frameParts = settings.lowLatencyMode && VideoDecoders.partialFrameCapable()
|
||||
val frameParts = settings.lowLatencyMode && partialFrame
|
||||
val codecBits = VideoDecoders.decodableCodecBits()
|
||||
// Automatic codec (P5, measured NP3 ↔ RTX 4090): AV1 beat HEVC by ~1.2 ms end-to-end at
|
||||
// identical conditions, so under "Automatic" this device prefers AV1 when it hardware-
|
||||
// decodes it (the AV1 bit is only ever set for a real, non-blocked hardware decoder) AND
|
||||
// it lacks FEATURE_PartialFrame — a partial-frame device keeps HEVC, whose slice overlap
|
||||
// AV1 cannot ride (AV1 has no slices; the host's chunked poll never arms). The host
|
||||
// honors the preference only inside the probed shared codec set, so an AV1-less encoder
|
||||
// still resolves HEVC. An explicit user choice always wins unchanged.
|
||||
val preferredCodec = settings.preferredCodec().takeIf { it != 0 }
|
||||
?: if (codecBits and 4 != 0 && !partialFrame) 4 else 0
|
||||
// The connect-time capability readout (`adb logcat -s pf.caps`): the P2 slice pipeline
|
||||
// is client-inert unless BOTH probes pass — this line says which decoder failed one.
|
||||
Log.i(
|
||||
"pf.caps",
|
||||
VideoDecoders.capsReport() +
|
||||
" → multiSlice=$multiSlice parts=$frameParts (lowLatency=${settings.lowLatencyMode})",
|
||||
" → multiSlice=$multiSlice parts=$frameParts prefer=$preferredCodec" +
|
||||
" (lowLatency=${settings.lowLatencyMode})",
|
||||
)
|
||||
NativeBridge.nativeConnect(
|
||||
host, port, w, h, hz,
|
||||
@@ -65,8 +77,9 @@ suspend fun connectToHost(
|
||||
frameParts,
|
||||
settings.audioChannels,
|
||||
// What this device can decode (H.264|HEVC always, AV1 when a real decoder exists) +
|
||||
// the user's soft codec preference — the host resolves the emitted codec from both.
|
||||
VideoDecoders.decodableCodecBits(), settings.preferredCodec(), timeoutMs,
|
||||
// the soft codec preference (user choice, or the Automatic AV1 rule above) — the
|
||||
// host resolves the emitted codec from both.
|
||||
codecBits, preferredCodec, timeoutMs,
|
||||
launch,
|
||||
// The host's approval-list / trust-store label for this device — the same
|
||||
// Build.MODEL convention the pairing dialogs use for nativePair.
|
||||
|
||||
@@ -673,12 +673,22 @@ private fun DisplaySettings(s: Settings, update: (Settings) -> Unit, context: an
|
||||
// Only codecs this device can actually decode are offered — a preference the client never
|
||||
// advertises would be a dead setting (see [codecOptionsFor]).
|
||||
val av1Capable = remember { VideoDecoders.pickDecoder("video/av01") != null }
|
||||
// Mirror the Automatic AV1 rule in HostConnect (hardware AV1 AND no partial-frame
|
||||
// support) so the picker says what "Automatic" actually does on THIS device.
|
||||
val autoPrefersAv1 = remember {
|
||||
VideoDecoders.decodableCodecBits() and 4 != 0 && !VideoDecoders.partialFrameCapable()
|
||||
}
|
||||
SettingDropdown(
|
||||
label = "Video codec",
|
||||
options = codecOptionsFor(s.codec, av1Capable),
|
||||
selected = s.codec,
|
||||
field = "codec",
|
||||
caption = "A preference — the host falls back if it can't encode this one.",
|
||||
caption = if (autoPrefersAv1) {
|
||||
"A preference — the host falls back if it can't encode this one. " +
|
||||
"Automatic prefers AV1 on this device."
|
||||
} else {
|
||||
"A preference — the host falls back if it can't encode this one."
|
||||
},
|
||||
) { c -> update(s.copy(codec = c)) }
|
||||
|
||||
// HDR is only meaningful on a panel that can present HDR10; on an SDR display the toggle is
|
||||
|
||||
Reference in New Issue
Block a user