feat(protocol,clients): codec preference negotiation + Linux client decodes per Welcome (Phase 2a)
Adds a client-selectable **preferred codec** and wires the core + ABI + probe + Linux client to
negotiate and decode it. (Windows/Apple/Android follow in 2b.)
**Core:**
- `Hello.preferred_codec` (a single CODEC_* bit, 0 = auto) — a soft hint appended after
`video_codecs`. `resolve_codec(client, host, preferred)` now honors the preference when the host
can also emit it, else falls back to precedence (HEVC > AV1 > H.264). Roundtrip + preference tests.
- `NativeClient::connect` takes `video_codecs` + `preferred_codec`; `NativeClient.codec` exposes the
resolved `Welcome.codec`.
- ABI: `punktfunk_connect_ex7` (adds the two codec params; `ex6` delegates to it advertising
HEVC-only) + `punktfunk_connection_codec` getter + `PUNKTFUNK_CODEC_{H264,HEVC,AV1}` constants
(drift-guarded against the wire values). Header regenerated.
**Host:** passes `hello.preferred_codec` into `resolve_codec`.
**probe:** `--codec h264|hevc|av1|auto` sets the preference (still advertises it can decode all
three); the dump extension already follows the resolved codec.
**Linux client:** advertises the codecs FFmpeg can actually decode (`decodable_codecs()`), threads
the user's `codec` setting as the preference, and builds the decoder — both the software and VAAPI
paths, plus the mid-session VAAPI→software demotion — from the negotiated `Welcome.codec` instead of
hardcoding HEVC. New "Video codec" dropdown in Preferences (Automatic/HEVC/H.264/AV1).
Live-validated on the dev box: probe `--codec hevc` against a software (H.264-only) host resolves to
H.264 (graceful soft-preference fallback), no failure. clippy + core (57) + host (133) tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,9 @@ const RESOLUTIONS: &[(u32, u32)] = &[
|
||||
const REFRESH: &[u32] = &[0, 30, 60, 90, 120, 144, 165, 240];
|
||||
const GAMEPADS: &[&str] = &["auto", "xbox360", "dualsense", "xboxone", "dualshock4"];
|
||||
const COMPOSITORS: &[&str] = &["auto", "kwin", "wlroots", "mutter", "gamescope"];
|
||||
/// Codec setting values (persisted) paired with their display labels below.
|
||||
const CODECS: &[&str] = &["auto", "hevc", "h264", "av1"];
|
||||
const CODEC_LABELS: &[&str] = &["Automatic", "HEVC (H.265)", "H.264 (AVC)", "AV1"];
|
||||
|
||||
/// punktfunk's own license (MIT OR Apache-2.0), shown on the About dialog's Legal page.
|
||||
const APP_LICENSE: &str = concat!(
|
||||
@@ -193,6 +196,12 @@ pub fn show(
|
||||
]))
|
||||
.build();
|
||||
audio.add(&surround_row);
|
||||
let codec_row = adw::ComboRow::builder()
|
||||
.title("Video codec")
|
||||
.subtitle("Preferred codec — the host falls back if it can't encode this one")
|
||||
.model(>k::StringList::new(CODEC_LABELS))
|
||||
.build();
|
||||
stream.add(&codec_row);
|
||||
let mic_row = adw::SwitchRow::builder()
|
||||
.title("Stream microphone")
|
||||
.subtitle("Send the default input device to the host's virtual microphone")
|
||||
@@ -242,6 +251,8 @@ pub fn show(
|
||||
8 => 2,
|
||||
_ => 0,
|
||||
});
|
||||
let codec_i = CODECS.iter().position(|&c| c == s.codec).unwrap_or(0);
|
||||
codec_row.set_selected(codec_i as u32);
|
||||
}
|
||||
|
||||
let dialog = adw::PreferencesDialog::new();
|
||||
@@ -263,6 +274,7 @@ pub fn show(
|
||||
2 => 8,
|
||||
_ => 2,
|
||||
};
|
||||
s.codec = CODECS[(codec_row.selected() as usize).min(CODECS.len() - 1)].to_string();
|
||||
s.save();
|
||||
});
|
||||
dialog.present(Some(parent));
|
||||
|
||||
Reference in New Issue
Block a user