use crate::bitstream::TsStreamBuffer;
use crate::stream::TsVideoStream;
pub fn scan(stream: &mut TsVideoStream, buffer: &mut TsStreamBuffer, tag: &mut Option<String>) {
let mut parse: i32 = 0;
let mut picture_parse: i32 = 0;
let mut sequence_header_parse: i32 = 0;
for _ in 0..buffer.length() {
parse = parse.wrapping_shl(8).wrapping_add(i32::from(buffer.read_byte(false)));
if parse == 0x0000_0100 {
picture_parse = 2;
} else if parse == 0x0000_01B3 {
sequence_header_parse = 7;
} else if sequence_header_parse > 0 {
sequence_header_parse = sequence_header_parse.wrapping_sub(1);
if sequence_header_parse == 0 {
stream.base.is_vbr = true;
stream.base.is_initialized = true;
}
} else if picture_parse > 0 {
picture_parse = picture_parse.wrapping_sub(1);
if picture_parse == 0 {
match (parse & 0x38).wrapping_shr(3) {
1 => *tag = Some("I".to_owned()),
2 => *tag = Some("P".to_owned()),
3 => *tag = Some("B".to_owned()),
_ => {}
}
if stream.base.is_initialized {
return;
}
}
}
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::{any, proptest};
use super::scan;
use crate::bitstream::TsStreamBuffer;
use crate::stream::{TsAspectRatio, TsFrameRate, TsStreamType, TsVideoFormat, TsVideoStream};
fn buf(data: &[u8]) -> TsStreamBuffer {
let mut b = TsStreamBuffer::new();
b.add(data, 0, data.len());
b.begin_read();
b
}
fn stream() -> TsVideoStream {
let mut s = TsVideoStream::default();
s.base.stream_type = TsStreamType::Mpeg2Video;
s
}
fn run(data: &[u8]) -> (TsVideoStream, Option<String>) {
let mut s = stream();
let mut b = buf(data);
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
(s, tag)
}
fn sequence_header() -> Vec<u8> {
vec![0x00, 0x00, 0x01, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn picture(coding_type: u8) -> Vec<u8> {
vec![0x00, 0x00, 0x01, 0x00, 0x00, coding_type << 3, 0x00]
}
#[test]
fn sequence_header_initializes_the_stream() {
let (s, tag) = run(&sequence_header());
assert!(s.base.is_vbr);
assert!(s.base.is_initialized);
assert!(s.encoding_profile.is_none()); assert_eq!(tag, None);
}
#[test]
fn sequence_header_with_clpi_metadata_matches_the_expected_mpeg2_desc() {
let mut s = stream();
s.set_video_format(TsVideoFormat::Videoformat1080p);
s.set_frame_rate(TsFrameRate::Framerate24);
s.aspect_ratio = TsAspectRatio::Aspect16_9;
let mut b = buf(&sequence_header());
scan(&mut s, &mut b, &mut None);
assert_eq!(s.description(), "1080p / 24 fps / 16:9");
assert_eq!(s.codec_short_name(), "MPEG-2");
assert_eq!(s.codec_name(), "MPEG-2 Video");
}
#[test]
fn picture_start_code_sets_the_picture_type_tag() {
assert_eq!(run(&picture(1)).1.as_deref(), Some("I"));
assert_eq!(run(&picture(2)).1.as_deref(), Some("P"));
assert_eq!(run(&picture(3)).1.as_deref(), Some("B"));
assert_eq!(run(&picture(0)).1, None); assert_eq!(run(&picture(4)).1, None); }
#[test]
fn a_picture_on_an_initialized_stream_returns() {
let mut data = sequence_header();
data.extend_from_slice(&picture(2)); let (s, tag) = run(&data);
assert_eq!(tag.as_deref(), Some("P"));
assert!(s.base.is_initialized);
}
#[test]
fn a_picture_before_initialization_sets_the_tag_without_returning() {
let mut data = picture(1); data.extend_from_slice(&sequence_header());
let (s, tag) = run(&data);
assert_eq!(tag.as_deref(), Some("I"));
assert!(s.base.is_initialized);
}
#[test]
fn no_start_codes_leaves_the_stream_untouched() {
let (s, tag) = run(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
assert!(!s.base.is_initialized);
assert!(!s.base.is_vbr);
assert_eq!(tag, None);
}
#[test]
fn a_truncated_sequence_header_does_not_initialize() {
let (s, _) = run(&[0x00, 0x00, 0x01, 0xB3, 0x00, 0x00, 0x00]);
assert!(!s.base.is_initialized);
assert!(!s.base.is_vbr);
}
#[test]
fn a_picture_started_inside_a_sequence_header_decodes_after_init() {
let data = [
0x00, 0x00, 0x01, 0xB3, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x10, ];
let (s, tag) = run(&data);
assert!(s.base.is_initialized);
assert_eq!(tag.as_deref(), Some("I"));
}
proptest! {
#[test]
fn scan_never_panics_on_arbitrary_bytes(data in any::<Vec<u8>>()) {
let mut s = stream();
let mut b = buf(&data);
let mut tag = None;
scan(&mut s, &mut b, &mut tag);
}
}
}