#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CodingType {
I,
P,
B,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FieldOrder {
Tff,
Bff,
Progressive,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Mpeg2Coding {
pub top_field_first: bool,
pub repeat_first_field: bool,
pub progressive_frame: bool,
pub progressive_sequence: bool,
pub frame_picture: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CodingDetail {
Mpeg2(Mpeg2Coding),
CodingTypeOnly,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Hdr10Metadata {
pub display_primaries_x: [u16; 3],
pub display_primaries_y: [u16; 3],
pub white_point_x: u16,
pub white_point_y: u16,
pub max_display_mastering_luminance: u32,
pub min_display_mastering_luminance: u32,
pub max_content_light_level: u16,
pub max_pic_average_light_level: u16,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PictureInfo {
coding_type: CodingType,
detail: CodingDetail,
hdr10: Option<Hdr10Metadata>,
}
impl PictureInfo {
pub fn mpeg2(coding_type: CodingType, m: Mpeg2Coding) -> Self {
Self {
coding_type,
detail: CodingDetail::Mpeg2(m),
hdr10: None,
}
}
pub fn coding_type_only(coding_type: CodingType) -> Self {
Self {
coding_type,
detail: CodingDetail::CodingTypeOnly,
hdr10: None,
}
}
pub fn with_hdr10(mut self, hdr10: Option<Hdr10Metadata>) -> Self {
self.hdr10 = hdr10;
self
}
pub fn hdr10(&self) -> Option<Hdr10Metadata> {
self.hdr10
}
pub fn coding_type(&self) -> CodingType {
self.coding_type
}
pub fn field_order(&self) -> Option<FieldOrder> {
match self.detail {
CodingDetail::Mpeg2(m) => {
if !m.frame_picture {
Some(if m.top_field_first {
FieldOrder::Tff
} else {
FieldOrder::Bff
})
} else if m.progressive_sequence || m.progressive_frame {
Some(FieldOrder::Progressive)
} else if m.top_field_first {
Some(FieldOrder::Tff)
} else {
Some(FieldOrder::Bff)
}
}
CodingDetail::CodingTypeOnly => None,
}
}
pub fn nb_fields(&self) -> u8 {
match self.detail {
CodingDetail::Mpeg2(m) => {
if !m.frame_picture {
return 1;
}
if !m.repeat_first_field {
return 2;
}
if m.progressive_sequence {
if m.top_field_first { 6 } else { 4 }
} else if m.progressive_frame {
3
} else {
2
}
}
CodingDetail::CodingTypeOnly => 2,
}
}
pub fn progressive(&self) -> Option<bool> {
match self.detail {
CodingDetail::Mpeg2(m) => Some(m.progressive_sequence || m.progressive_frame),
CodingDetail::CodingTypeOnly => None,
}
}
pub fn keyframe(&self) -> bool {
self.coding_type == CodingType::I
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mpeg2(
ct: CodingType,
tff: bool,
rff: bool,
prog_frame: bool,
prog_seq: bool,
frame_pic: bool,
) -> PictureInfo {
PictureInfo::mpeg2(
ct,
Mpeg2Coding {
top_field_first: tff,
repeat_first_field: rff,
progressive_frame: prog_frame,
progressive_sequence: prog_seq,
frame_picture: frame_pic,
},
)
}
#[test]
fn coding_type_accessor_returns_stored_type() {
assert_eq!(
mpeg2(CodingType::I, true, false, false, false, true).coding_type(),
CodingType::I
);
assert_eq!(
mpeg2(CodingType::B, true, false, false, false, true).coding_type(),
CodingType::B
);
}
#[test]
fn keyframe_only_for_intra() {
assert!(mpeg2(CodingType::I, true, false, false, false, true).keyframe());
assert!(!mpeg2(CodingType::P, true, false, false, false, true).keyframe());
assert!(!mpeg2(CodingType::B, true, false, false, false, true).keyframe());
}
#[test]
fn mpeg2_field_order_tff_when_top_field_first() {
assert_eq!(
mpeg2(CodingType::I, true, false, false, false, true).field_order(),
Some(FieldOrder::Tff)
);
}
#[test]
fn mpeg2_field_order_bff_when_not_top_field_first() {
assert_eq!(
mpeg2(CodingType::I, false, false, false, false, true).field_order(),
Some(FieldOrder::Bff)
);
}
#[test]
fn mpeg2_field_order_progressive_for_progressive_frame() {
assert_eq!(
mpeg2(CodingType::I, true, false, true, false, true).field_order(),
Some(FieldOrder::Progressive)
);
assert_eq!(
mpeg2(CodingType::I, true, false, false, true, true).field_order(),
Some(FieldOrder::Progressive)
);
}
#[test]
fn mpeg2_nb_fields_normal_and_telecine() {
assert_eq!(
mpeg2(CodingType::P, true, false, false, false, true).nb_fields(),
2
);
assert_eq!(
mpeg2(CodingType::P, false, true, true, false, true).nb_fields(),
3
);
assert_eq!(
mpeg2(CodingType::P, false, false, false, false, false).nb_fields(),
1
);
assert_eq!(
mpeg2(CodingType::P, true, true, false, true, true).nb_fields(),
6
);
assert_eq!(
mpeg2(CodingType::P, false, true, false, true, true).nb_fields(),
4
);
}
#[test]
fn mpeg2_progressive_accessor() {
assert_eq!(
mpeg2(CodingType::I, true, false, true, false, true).progressive(),
Some(true)
);
assert_eq!(
mpeg2(CodingType::I, true, false, false, false, true).progressive(),
Some(false)
);
}
#[test]
fn coding_type_only_reports_unknown_field_and_progressive() {
let p = PictureInfo::coding_type_only(CodingType::P);
assert_eq!(p.coding_type(), CodingType::P);
assert_eq!(p.field_order(), None);
assert_eq!(p.progressive(), None);
assert_eq!(p.nb_fields(), 2);
}
}