mux/dts: keep DTS-HD extension substream across PES boundaries
The DTS parser emitted a core-only frame and discarded the trailing DTS-HD extension substream whenever the extension straddled a PES boundary (it advanced past only the core, then re-synced on the core syncword, skipping the leftover extension bytes). That silently downgrades DTS-HD MA / HRA to lossy DTS core. Now: when an extension sync is visible after the core — full, or a partial prefix at the buffer edge — wait for the full extension instead of splitting it off; only 'nothing after the core' (final unit / EOF, no parser flush) is taken as a genuine lossy core-only unit. Adds a boundary-split regression test asserting core+extension is preserved.
This commit is contained in:
+74
-16
@@ -64,23 +64,40 @@ impl CodecParser for DtsParser {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for DTS-HD extension after core
|
// Include the DTS-HD extension substream if one immediately follows
|
||||||
let mut total_size = core_size;
|
// the core. The extension carries the LOSSLESS (DTS-HD MA / HRA)
|
||||||
if start + core_size + 4 <= data.len() {
|
// data; emitting a core-only frame and dropping the trailing
|
||||||
if let Some(0) = find_sync(
|
// extension silently downgrades the track to lossy DTS core. If we
|
||||||
&data[start + core_size..start + core_size + 4],
|
// can't yet tell whether an extension follows, or it's present but
|
||||||
&DTS_HD_EXT_SYNC,
|
// not fully buffered, WAIT for more PES data (break, leaving `pos`
|
||||||
) {
|
// at this access unit's core sync so the buffer keeps the partial
|
||||||
let ext = &data[start + core_size..];
|
// unit) rather than splitting the extension off and losing it.
|
||||||
if ext.len() >= 9 {
|
let after = start + core_size;
|
||||||
let ext_size = dts_hd_ext_frame_size(ext);
|
let avail = data.len() - after;
|
||||||
if start + core_size + ext_size <= data.len() {
|
// Does a DTS-HD extension substream follow the core? Match the full
|
||||||
total_size = core_size + ext_size;
|
// sync when it's buffered, or a partial PREFIX when the buffer ends
|
||||||
}
|
// mid-sync — so we wait for the rest instead of splitting the
|
||||||
// If ext incomplete, just emit core
|
// extension off and losing the lossless data. Nothing after the core
|
||||||
}
|
// (e.g. the final access unit / EOF, with no parser flush) is taken
|
||||||
|
// as a legitimate lossy core-only unit.
|
||||||
|
let ext_follows = if avail >= 4 {
|
||||||
|
data[after..after + 4] == DTS_HD_EXT_SYNC
|
||||||
|
} else if avail > 0 {
|
||||||
|
DTS_HD_EXT_SYNC[..avail] == data[after..]
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let total_size = if !ext_follows {
|
||||||
|
core_size
|
||||||
|
} else if avail < 9 {
|
||||||
|
break; // extension present but its size header isn't buffered — wait
|
||||||
|
} else {
|
||||||
|
let ext_size = dts_hd_ext_frame_size(&data[after..]);
|
||||||
|
if ext_size == 0 || avail < ext_size {
|
||||||
|
break; // extension known but not fully buffered — wait
|
||||||
}
|
}
|
||||||
}
|
core_size + ext_size
|
||||||
|
};
|
||||||
|
|
||||||
frames.push(Frame {
|
frames.push(Frame {
|
||||||
pts_ns,
|
pts_ns,
|
||||||
@@ -204,6 +221,47 @@ mod tests {
|
|||||||
assert_eq!(frames[0].data.len(), 512);
|
assert_eq!(frames[0].data.len(), 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a DTS-HD extension substream of `size` bytes with a valid sync +
|
||||||
|
/// size header (matching `dts_hd_ext_frame_size`).
|
||||||
|
fn make_dts_ext(size: usize) -> Vec<u8> {
|
||||||
|
let raw = size - 1;
|
||||||
|
let mut e = vec![0u8; size];
|
||||||
|
e[0..4].copy_from_slice(&DTS_HD_EXT_SYNC);
|
||||||
|
e[6] = ((raw >> 11) & 0x1F) as u8;
|
||||||
|
e[7] = ((raw >> 3) & 0xFF) as u8;
|
||||||
|
e[8] = (((raw & 0x07) << 5) as u8) | (e[8] & 0x1F);
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keeps_dts_hd_extension_across_pes_boundary() {
|
||||||
|
// DTS-HD MA access unit = core + extension substream. When the
|
||||||
|
// extension straddles a PES boundary, the parser must WAIT and emit the
|
||||||
|
// full unit — not a core-only frame (which would drop the lossless
|
||||||
|
// data, the Dunkirk lossy-core bug).
|
||||||
|
let mut parser = DtsParser::new();
|
||||||
|
let core = make_dts_core(512);
|
||||||
|
let ext = make_dts_ext(256);
|
||||||
|
let mut au = core;
|
||||||
|
au.extend_from_slice(&ext); // 768-byte access unit
|
||||||
|
|
||||||
|
// Split mid-extension: first PES carries the core + 100 ext bytes.
|
||||||
|
let split = 512 + 100;
|
||||||
|
let f1 = parser.parse(&make_pes(au[..split].to_vec(), Some(90000)));
|
||||||
|
assert!(
|
||||||
|
f1.is_empty(),
|
||||||
|
"must wait for the full extension, not emit a core-only frame"
|
||||||
|
);
|
||||||
|
|
||||||
|
let f2 = parser.parse(&make_pes(au[split..].to_vec(), Some(90000)));
|
||||||
|
assert_eq!(f2.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
f2[0].data.len(),
|
||||||
|
768,
|
||||||
|
"frame must include core + extension (lossless preserved)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn codec_private_none() {
|
fn codec_private_none() {
|
||||||
let parser = DtsParser::new();
|
let parser = DtsParser::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user