use crate::audio::AudioInfoFrame;
use crate::avi::AviInfoFrame;
use crate::decoded::Decoded;
use crate::dynamic_hdr::{DynamicHdrFragment, DynamicHdrInfoFrame};
use crate::encode::{IntoPackets, SinglePacketIter};
use crate::hdmi_forum_vsi::HdmiForumVsi;
use crate::hdr_static::HdrStaticInfoFrame;
use crate::warn::Warning;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum InfoFrame {
Avi(AviInfoFrame),
Audio(AudioInfoFrame),
HdrStatic(HdrStaticInfoFrame),
HdmiForumVsi(HdmiForumVsi),
DynamicHdr(DynamicHdrInfoFrame),
Unknown {
type_code: u8,
version: u8,
payload: [u8; 27],
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum InfoFramePacket {
Avi(AviInfoFrame),
Audio(AudioInfoFrame),
HdrStatic(HdrStaticInfoFrame),
HdmiForumVsi(HdmiForumVsi),
DynamicHdrFragment(DynamicHdrFragment),
Unknown {
type_code: u8,
version: u8,
payload: [u8; 27],
},
}
pub struct InfoFrameIter(Option<SinglePacketIter>);
impl Iterator for InfoFrameIter {
type Item = [u8; 31];
fn next(&mut self) -> Option<[u8; 31]> {
self.0.as_mut()?.next()
}
}
impl IntoPackets for InfoFrame {
type Iter = InfoFrameIter;
type Warning = Warning;
fn into_packets(self) -> Decoded<InfoFrameIter, Warning> {
match self {
InfoFrame::Avi(f) => f
.into_packets()
.wrap(|iter| InfoFrameIter(Some(iter)), Warning::Avi),
InfoFrame::Audio(f) => f
.into_packets()
.wrap(|iter| InfoFrameIter(Some(iter)), Warning::Audio),
InfoFrame::HdrStatic(f) => f
.into_packets()
.wrap(|iter| InfoFrameIter(Some(iter)), Warning::HdrStatic),
InfoFrame::HdmiForumVsi(f) => f
.into_packets()
.wrap(|iter| InfoFrameIter(Some(iter)), Warning::HdmiForumVsi),
InfoFrame::DynamicHdr(_) => Decoded::new(InfoFrameIter(None)), InfoFrame::Unknown {
type_code,
version,
payload,
} => {
let mut hp = [0u8; 30];
hp[0] = type_code;
hp[1] = version;
hp[2] = 27;
hp[3..30].copy_from_slice(&payload);
let checksum = crate::checksum::compute_checksum(&hp);
let mut packet = [0u8; 31];
packet[..3].copy_from_slice(&hp[..3]);
packet[3] = checksum;
packet[4..].copy_from_slice(&hp[3..]);
Decoded::new(InfoFrameIter(Some(SinglePacketIter::new(packet))))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::audio::{
AudioCodingType, AudioInfoFrame, ChannelCount, LfePlaybackLevel, SampleFrequency,
SampleSize,
};
use crate::avi::{
AviInfoFrame, BarInfo, Colorimetry, ExtendedColorimetry, ItContentType, NonUniformScaling,
PictureAspectRatio, RgbQuantization, ScanInfo, YccQuantization,
};
use crate::dynamic_hdr::DynamicHdrInfoFrame;
use crate::hdmi_forum_vsi::HdmiForumVsi;
use crate::hdr_static::{Eotf, HdrStaticInfoFrame, StaticMetadata, StaticMetadataType1};
use display_types::cea861::hdmi_forum::HdmiDscMaxSlices;
use display_types::{ColorFormat, HdmiForumFrl};
#[test]
fn avi_variant_produces_one_packet() {
let frame = InfoFrame::Avi(AviInfoFrame {
color_format: ColorFormat::YCbCr444,
active_format_present: false,
bar_info: BarInfo::NotPresent,
scan_info: ScanInfo::Underscanned,
colorimetry: Colorimetry::Bt709,
extended_colorimetry: ExtendedColorimetry::XvYCC601,
picture_aspect_ratio: PictureAspectRatio::SixteenByNine,
active_format_aspect_ratio: 0x08,
it_content: false,
rgb_quantization: RgbQuantization::Default,
non_uniform_scaling: NonUniformScaling::None,
vic: 16,
ycc_quantization: YccQuantization::LimitedRange,
it_content_type: ItContentType::Graphics,
pixel_repetition: 0,
top_bar: 0,
bottom_bar: 0,
left_bar: 0,
right_bar: 0,
});
let mut iter = frame.into_packets().value;
let packet = iter.next().expect("expected one packet");
assert_eq!(packet[0], 0x82); assert!(iter.next().is_none());
}
#[test]
fn audio_variant_produces_one_packet() {
let frame = InfoFrame::Audio(AudioInfoFrame {
coding_type: AudioCodingType::Lpcm,
channel_count: ChannelCount::Count(2),
sample_freq: SampleFrequency::Hz48000,
sample_size: SampleSize::Bits16,
coding_ext: 0,
channel_allocation: 0,
lfe_playback_level: LfePlaybackLevel::NoInfo,
downmix_inhibit: false,
});
let mut iter = frame.into_packets().value;
let packet = iter.next().expect("expected one packet");
assert_eq!(packet[0], 0x84); assert!(iter.next().is_none());
}
#[test]
fn hdr_static_variant_produces_one_packet() {
let frame = InfoFrame::HdrStatic(HdrStaticInfoFrame {
eotf: Eotf::Pq,
metadata: StaticMetadata::Type1(StaticMetadataType1 {
primaries_green: [0, 0],
primaries_blue: [0, 0],
primaries_red: [0, 0],
white_point: [0, 0],
max_mastering_luminance: 1000,
min_mastering_luminance: 1,
max_cll: 1000,
max_fall: 400,
}),
});
let mut iter = frame.into_packets().value;
let packet = iter.next().expect("expected one packet");
assert_eq!(packet[0], 0x87); assert!(iter.next().is_none());
}
#[test]
fn hdmi_forum_vsi_variant_produces_one_packet() {
let frame = InfoFrame::HdmiForumVsi(HdmiForumVsi {
allm: false,
frl_rate: HdmiForumFrl::NotSupported,
fapa_start_location: false,
fva: false,
vrr_en: false,
m_const: false,
qms_en: false,
neg_mvrr: false,
m_vrr: 0,
dsc_1p2: false,
dsc_native_420: false,
dsc_all_bpc: false,
dsc_max_frl_rate: HdmiForumFrl::NotSupported,
dsc_max_slices: HdmiDscMaxSlices::NotSupported,
dsc_10bpc: false,
dsc_12bpc: false,
});
let mut iter = frame.into_packets().value;
let packet = iter.next().expect("expected one packet");
assert_eq!(packet[0], 0x81); assert!(iter.next().is_none());
}
#[test]
fn dynamic_hdr_variant_yields_no_packets() {
let frame = InfoFrame::DynamicHdr(DynamicHdrInfoFrame::Unknown { format_id: 0x04 });
assert!(frame.into_packets().value.next().is_none());
}
#[test]
fn unknown_variant_produces_one_packet() {
let mut payload = [0u8; 27];
payload[0] = 0xDE;
payload[1] = 0xAD;
let frame = InfoFrame::Unknown {
type_code: 0xFE,
version: 0x02,
payload,
};
let mut iter = frame.into_packets().value;
let packet = iter.next().expect("expected one packet");
assert_eq!(packet[0], 0xFE); assert_eq!(packet[1], 0x02); assert_eq!(packet[2], 27); assert_eq!(packet[4], 0xDE); assert_eq!(packet[5], 0xAD); assert!(iter.next().is_none());
}
#[test]
fn unknown_variant_checksum_is_valid() {
let frame = InfoFrame::Unknown {
type_code: 0xCC,
version: 0x01,
payload: [0u8; 27],
};
let packet = frame.into_packets().value.next().unwrap();
let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
assert_eq!(sum, 0, "checksum must make all-bytes sum equal 0");
}
}