//! Device-profile policy: turning what a device *reports* about its audio //! output into the constraints we send Jellyfin. //! //! The platform layer reports raw facts (what `MediaCodecList` enumerates, how //! many channels the current audio route accepts); deciding what those facts //! mean for a `DeviceProfile` is domain logic and lives here, on the Rust side //! of the boundary, where it is testable without a device. /// Channel count assumed when the platform cannot tell us — every audio route /// can voice stereo, so it is the only safe floor. const FALLBACK_AUDIO_CHANNELS: u32 = 2; /// Upper bound we are willing to claim. Jellyfin profiles top out at 7.1, and a /// nonsense reading from a driver should not become a nonsense profile. const MAX_SUPPORTED_AUDIO_CHANNELS: u32 = 8; /// Decide the `MaxAudioChannels` to advertise, given what the current audio /// route reported. /// /// Without this constraint Jellyfin is free to direct-play a 5.1 or 7.1 track to /// a sink that only has two channels. What the user hears then is device /// dependent and rarely correct — a failed `AudioSink` configuration (silence), /// or centre-channel dialogue folded away to near-inaudibility. Naming the real /// channel count makes the server downmix instead, which is always audible. /// /// A missing or zero reading means "route not established yet", not "no audio": /// fall back to stereo rather than claiming a capability we have not seen. /// /// TRACES: UR-004 | DR-141 | UT-141 pub fn clamp_max_audio_channels(reported: Option) -> u32 { match reported { Some(channels) if channels >= 1 => channels.min(MAX_SUPPORTED_AUDIO_CHANNELS), _ => FALLBACK_AUDIO_CHANNELS, } } /// The channel cap for this device, reading the platform's report where one /// exists. /// /// TRACES: UR-004 | DR-141 | UT-141 pub fn max_audio_channels() -> u32 { #[cfg(target_os = "android")] let reported = crate::player::get_detected_codecs().and_then(|(_, _, channels)| channels); // Desktop plays video through the WebKitGTK HTML5