#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum AirPlayFeature {
SupportsAirPlayVideoV1 = 0,
SupportsAirPlayPhoto = 1,
SupportsAirPlayVideoFairPlay = 2,
SupportsAirPlaySlideshow = 5,
SupportsAirPlayVideoVolumeControl = 6,
SupportsAirPlayScreen = 7,
SupportsAirPlayAudio = 9,
AudioRedundant = 11,
Authentication4 = 14,
MetadataArtwork = 15,
MetadataProgress = 16,
MetadataNowPlayingDaap = 17,
AudioFormats0 = 18,
AudioFormats1 = 19,
AudioFormats2 = 20,
AudioFormats3 = 21,
AudioFormats4 = 22,
Authentication1 = 23,
Authentication8Mfi = 26,
SupportsLegacyPairing = 27,
HasUnifiedAdvertiserInfo = 30,
SupportsVolume = 32,
SupportsAirPlayVideoPlayQueue = 33,
SupportsAirPlayFromCloud = 34,
SupportsTlsPsk = 35,
SupportsUnifiedMediaControl = 38,
SupportsBufferedAudio = 40,
SupportsPtp = 41,
SupportsScreenMultiCodec = 42,
SupportsSystemPairing = 43,
IsApValeriaScreenSender = 44,
SupportsHkPairingAndAccessControl = 46,
SupportsTransientPairing = 47,
SupportsCoreUtilsPairingAndEncryption = 48,
SupportsAirPlayVideoV2 = 49,
MetadataNowPlayingBplist = 50,
SupportsUnifiedPairSetupAndMfi = 51,
SupportsSetPeersExtendedMessage = 52,
SupportsApSync = 54,
SupportsWoL55 = 55,
SupportsWoL56 = 56,
SupportsHangdogRemoteControl = 58,
SupportsAudioStreamConnectionSetup = 59,
SupportsAudioMediaDataControl = 60,
SupportsRfc2198Redundancy = 61,
}
pub fn features_from(flags: &[AirPlayFeature]) -> u64 {
let mut val: u64 = 0;
for &f in flags {
val |= 1u64 << (f as u8);
}
val
}
pub fn features_to_mdns(features: u64) -> String {
let lo = features & 0xFFFFFFFF;
let hi = (features >> 32) & 0xFFFFFFFF;
format!("0x{lo:X},0x{hi:X}")
}
pub fn receiver_features() -> u64 {
#[cfg(not(feature = "video"))]
use AirPlayFeature::*;
#[cfg(feature = "video")]
{
use AirPlayFeature::*;
let mut val = features_from(&[
SupportsAirPlayPhoto, SupportsAirPlayVideoFairPlay, SupportsAirPlaySlideshow, SupportsAirPlayVideoVolumeControl, SupportsAirPlayScreen, SupportsAirPlayAudio, AudioRedundant, Authentication4, MetadataArtwork, MetadataProgress, MetadataNowPlayingDaap, AudioFormats0, AudioFormats1, AudioFormats2, AudioFormats3, HasUnifiedAdvertiserInfo, ]);
val |= (1 << 10) | (1 << 12) | (1 << 13) | (1 << 22) | (1 << 25) | (1 << 28);
val
}
#[cfg(not(feature = "video"))]
{
let bits: Vec<AirPlayFeature> = vec![
SupportsAirPlayAudio, AudioRedundant, Authentication4, MetadataProgress, AudioFormats0, AudioFormats1, HasUnifiedAdvertiserInfo, SupportsUnifiedMediaControl, SupportsBufferedAudio, SupportsPtp, SupportsHkPairingAndAccessControl, SupportsCoreUtilsPairingAndEncryption, AudioFormats2, AudioFormats4, SupportsTransientPairing, ];
features_from(&bits)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn feature_bits_correct() {
assert_eq!(1u64 << (AirPlayFeature::SupportsAirPlayAudio as u8), 1 << 9);
assert_eq!(1u64 << (AirPlayFeature::SupportsPtp as u8), 1 << 41);
assert_eq!(1u64 << (AirPlayFeature::SupportsHangdogRemoteControl as u8), 1 << 58);
}
#[test]
fn features_from_builds_bitmask() {
let f = features_from(&[AirPlayFeature::SupportsAirPlayAudio, AirPlayFeature::SupportsPtp]);
assert!(f & (1 << 9) != 0);
assert!(f & (1 << 41) != 0);
assert!(f & (1 << 0) == 0);
}
#[test]
fn mdns_format() {
let f = 0x1234567890ABCDEFu64;
let s = features_to_mdns(f);
assert_eq!(s, "0x90ABCDEF,0x12345678");
}
#[test]
#[cfg(not(feature = "video"))]
fn audio_receiver_has_required_bits() {
let f = receiver_features();
assert!(f & (1 << 9) != 0, "SupportsAirPlayAudio");
assert!(f & (1 << 11) != 0, "AudioRedundant");
assert!(f & (1 << 40) != 0, "SupportsBufferedAudio");
assert!(f & (1 << 41) != 0, "SupportsPtp");
assert!(f & (1 << 14) != 0, "Authentication4 (FairPlay)");
assert!(f & (1 << 38) != 0, "SupportsUnifiedMediaControl");
assert!(f & (1 << 46) != 0, "SupportsHKPairing");
assert!(f & (1 << 48) != 0, "SupportsCoreUtilsPairing");
}
#[test]
#[cfg(feature = "video")]
fn video_receiver_uses_uxplay_features() {
assert_eq!(receiver_features(), 0x527FFEE6);
}
}