mirror of
https://github.com/freemkv/libfreemkv.git
synced 2026-10-07 18:13:10 +00:00
mux: unknown SD aspect, coded-size square pixels, no MPEG-1 DAR guess, no BLA at gap-flagged joins
This commit is contained in:
+8
-12
@@ -419,11 +419,6 @@ impl CodecParser for HevcParser {
|
||||
let explicit_pts = pes.pts.or(pes.dts).map(pts_to_ns);
|
||||
let pts_ns = explicit_pts.unwrap_or(0);
|
||||
|
||||
// A concealed gap orphans RASL that follow the next CRA; arm the CRA->BLA rewrite.
|
||||
if pes.discontinuity {
|
||||
self.pending_clip_boundary = true;
|
||||
}
|
||||
|
||||
// Auto-detect a non-seamless clip boundary: mpls connection_condition
|
||||
// isn't plumbed through the (threaded) mux pipeline, so a PTS backstep
|
||||
// beyond `BACKSTEP_TICKS` on the unwrapped/high-water timeline arms the rewrite.
|
||||
@@ -2293,18 +2288,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// A gap-flagged PES arms the rewrite so the next CRA's RASL are discarded by the decoder.
|
||||
/// A seamless clip join restarts CC (flagged as a gap) with continuous PTS; the next CRA's
|
||||
/// RASL stay decodable, so it must not become BLA (the decoder would drop them).
|
||||
#[test]
|
||||
fn cra_after_a_gap_is_rewritten_to_bla() {
|
||||
fn cra_after_a_seamless_join_is_left_a_cra() {
|
||||
let mut parser = HevcParser::new();
|
||||
let mut gap = make_pes(vec![0, 0, 1, 0x02, 0x01, 0x80], Some(0));
|
||||
gap.discontinuity = true;
|
||||
parser.parse(&gap);
|
||||
let mut join = make_pes(vec![0, 0, 1, 0x02, 0x01, 0x80], Some(0));
|
||||
join.discontinuity = true;
|
||||
parser.parse(&join);
|
||||
let f = parser.parse(&make_pes(cra_au(&[0x10]), Some(3000)));
|
||||
assert_eq!(
|
||||
nal_type_of(&nals_of(&f[0].data)[0]),
|
||||
NAL_BLA_W_LP,
|
||||
"the first CRA after a gap must become BLA_W_LP"
|
||||
NAL_CRA_NUT,
|
||||
"a CRA at a seamless join must stay a CRA"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+24
-6
@@ -140,12 +140,14 @@ fn probe_video(es: &[u8], map_type: Option<u8>) -> Option<Probed> {
|
||||
8 => FrameRate::F60,
|
||||
_ => FrameRate::Unknown,
|
||||
};
|
||||
// aspect_ratio_information: 1 square pixels, 2 = 4:3, 3 = 16:9, 4 = 2.21:1.
|
||||
dar = match h[3] >> 4 {
|
||||
1 => res.pixels(),
|
||||
2 => Some((4, 3)),
|
||||
3 => Some((16, 9)),
|
||||
4 => Some((221, 100)),
|
||||
// aspect_ratio_information: 1 = square pixels (coded size). 2/3/4 are 4:3, 16:9, 2.21:1
|
||||
// only in 13818-2; in 11172-2 they are pel ratios, so MPEG-1 leaves them unknown.
|
||||
let width = (u32::from(h[0]) << 4) | u32::from(h[1] >> 4);
|
||||
dar = match (h[3] >> 4, codec) {
|
||||
(1, _) if width > 0 && height > 0 => Some((width, height)),
|
||||
(2, Codec::Mpeg2) => Some((4, 3)),
|
||||
(3, Codec::Mpeg2) => Some((16, 9)),
|
||||
(4, Codec::Mpeg2) => Some((221, 100)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -511,4 +513,20 @@ mod dar_tests {
|
||||
assert_eq!(res, Resolution::R576p);
|
||||
assert_eq!(dar, Some((16, 9)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn square_pixel_code_uses_the_coded_size() {
|
||||
// 320x240, aspect code 1 (square pixels), frame-rate code 4.
|
||||
let es = [0, 0, 1, 0xB3, 0x14, 0x00, 0xF0, 0x14, 0, 0];
|
||||
let (_, _, _, dar) = probe_video(&es, Some(0x02)).expect("video");
|
||||
assert_eq!(dar, Some((320, 240)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mpeg1_pel_aspect_code_is_not_a_display_ratio() {
|
||||
// MPEG-1 352x288, code 3 is a pel aspect ratio (11172-2), not 16:9.
|
||||
let es = [0, 0, 1, 0xB3, 0x16, 0x01, 0x20, 0x33, 0, 0];
|
||||
let (_, _, _, dar) = probe_video(&es, Some(0x01)).expect("video");
|
||||
assert_eq!(dar, None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1874,6 +1874,18 @@ fn scan_video() -> Vec<u8> {
|
||||
v
|
||||
}
|
||||
|
||||
// scan() carries the sequence header's aspect (code 2, 4:3) onto the video stream.
|
||||
#[test]
|
||||
fn scan_sets_the_video_display_aspect() {
|
||||
let ps = scan_pack(&[(0xE0, scan_video())], None);
|
||||
let s = scan::scan_streams(&ps).unwrap();
|
||||
assert!(
|
||||
matches!(&s[0], DiscStream::Video(v) if v.display_aspect == Some((4, 3))),
|
||||
"{:?}",
|
||||
s[0]
|
||||
);
|
||||
}
|
||||
|
||||
// Design §4 step 3: without a map, a `0xD0-0xD7` packet that starts mid-frame is classified
|
||||
// by the first sync word in its bytes, not by its first two bytes.
|
||||
#[test]
|
||||
|
||||
+4
-4
@@ -241,8 +241,8 @@ impl MapHeader {
|
||||
}
|
||||
|
||||
/// Display aspect ratio as `(num, den)`. Anamorphic titles carry an explicit
|
||||
/// `display_aspect`; without one, SD is never square-pixel (assume 4:3) and
|
||||
/// HD uses the coded pixel dimensions.
|
||||
/// `display_aspect`; without one, SD is never square-pixel so its aspect is
|
||||
/// unknown `(0, 1)`, and HD uses the coded pixel dimensions.
|
||||
fn display_aspect_ratio(v: &VideoStream, w: u32, h: u32) -> (u32, u32) {
|
||||
let sd = matches!(
|
||||
v.resolution,
|
||||
@@ -250,7 +250,7 @@ fn display_aspect_ratio(v: &VideoStream, w: u32, h: u32) -> (u32, u32) {
|
||||
);
|
||||
match v.display_aspect {
|
||||
Some((a, b)) if b != 0 => (a, b),
|
||||
_ if sd => (4, 3),
|
||||
_ if sd => (0, 1),
|
||||
_ if h != 0 => (w, h),
|
||||
_ => (0, 1),
|
||||
}
|
||||
@@ -617,7 +617,7 @@ mod tests {
|
||||
let h = MapHeader::from_title(&t, src(Medium::Iso, "iso://x.iso", 2));
|
||||
assert_eq!(h.stream.codec, "mpeg2video");
|
||||
assert_eq!((h.stream.width, h.stream.height), (720, 576));
|
||||
assert_eq!(h.stream.dar, (4, 3)); // SD is never square-pixel
|
||||
assert_eq!(h.stream.dar, (0, 1)); // SD with no aspect: unknown
|
||||
assert_eq!(h.stream.frame_rate, (25, 1));
|
||||
assert_eq!(h.stream.scan, Scan::Interlaced);
|
||||
assert_eq!(h.stream.colour.matrix, 5);
|
||||
|
||||
Reference in New Issue
Block a user