const BD_TS_PACKET_SIZE: usize = 192;
const TS_PACKET_SIZE: usize = 188;
const SYNC_BYTE: u8 = 0x47;
const SCAN_HEAD_SIZE: usize = 1024 * 1024;
const SCAN_TAIL_SIZE: usize = 2 * 1024 * 1024;
#[derive(Debug)]
pub struct PesPacket {
pub pid: u16,
pub pts: Option<i64>,
pub dts: Option<i64>,
pub data: Vec<u8>,
}
struct PesAssembler {
pid: u16,
buffer: Vec<u8>,
pts: Option<i64>,
dts: Option<i64>,
active: bool,
}
impl PesAssembler {
fn new(pid: u16) -> Self {
Self {
pid,
buffer: Vec::with_capacity(256 * 1024),
pts: None,
dts: None,
active: false,
}
}
fn start(&mut self, pts: Option<i64>, dts: Option<i64>) -> Option<PesPacket> {
let completed = if self.active && !self.buffer.is_empty() {
Some(PesPacket {
pid: self.pid,
pts: self.pts,
dts: self.dts,
data: std::mem::replace(&mut self.buffer, Vec::with_capacity(256 * 1024)),
})
} else {
self.buffer.clear();
None
};
self.pts = pts;
self.dts = dts;
self.active = true;
completed
}
fn push(&mut self, data: &[u8]) {
if self.active {
self.buffer.extend_from_slice(data);
}
}
fn flush(&mut self) -> Option<PesPacket> {
if self.active && !self.buffer.is_empty() {
self.active = false;
Some(PesPacket {
pid: self.pid,
pts: self.pts,
dts: self.dts,
data: std::mem::take(&mut self.buffer),
})
} else {
None
}
}
}
pub struct TsDemuxer {
assemblers: Vec<PesAssembler>,
pid_index: [i16; 8192], remainder: Vec<u8>, }
impl TsDemuxer {
pub fn take_remainder(&mut self) -> Vec<u8> {
std::mem::take(&mut self.remainder)
}
pub fn set_remainder(&mut self, data: Vec<u8>) {
self.remainder = data;
}
pub fn new(pids: &[u16]) -> Self {
let mut pid_index = [-1i16; 8192];
let mut assemblers = Vec::with_capacity(pids.len());
for (i, &pid) in pids.iter().enumerate() {
pid_index[pid as usize] = i as i16;
assemblers.push(PesAssembler::new(pid));
}
Self {
assemblers,
pid_index,
remainder: Vec::new(),
}
}
pub fn feed(&mut self, data: &[u8]) -> Vec<PesPacket> {
let mut completed = Vec::with_capacity(4);
let mut combined: Vec<u8> = Vec::new();
let work: &[u8] = if !self.remainder.is_empty() {
combined.reserve(self.remainder.len() + data.len());
combined.extend_from_slice(&self.remainder);
combined.extend_from_slice(data);
self.remainder.clear();
&combined
} else {
data
};
let mut offset = 0;
while offset + BD_TS_PACKET_SIZE <= work.len() {
let packet = &work[offset..offset + BD_TS_PACKET_SIZE];
offset += BD_TS_PACKET_SIZE;
if packet[4] != SYNC_BYTE {
continue;
}
let ts = &packet[4..];
let pid = (((ts[1] & 0x1F) as u16) << 8) | ts[2] as u16;
let pusi = ts[1] & 0x40 != 0; let adaptation = (ts[3] >> 4) & 0x03;
let idx = self.pid_index[pid as usize];
if idx < 0 {
continue;
}
let asm = &mut self.assemblers[idx as usize];
let payload_start = if adaptation == 0x03 || adaptation == 0x02 {
let af_len = ts[4] as usize;
if af_len > 183 {
continue; }
5 + af_len
} else {
4
};
if payload_start >= TS_PACKET_SIZE {
continue;
}
if adaptation == 0x02 {
continue;
}
let payload = &ts[payload_start..];
if pusi {
let (pts, dts, pes_data_start) = parse_pes_header(payload);
if let Some(prev) = asm.start(pts, dts) {
completed.push(prev);
}
if pes_data_start < payload.len() {
asm.push(&payload[pes_data_start..]);
}
} else {
asm.push(payload);
}
}
if offset < work.len() {
let leftover = &work[offset..];
if leftover.len() < BD_TS_PACKET_SIZE {
self.remainder.extend_from_slice(leftover);
} else {
self.remainder.clear();
}
}
completed
}
pub fn flush(&mut self) -> Vec<PesPacket> {
let mut completed = Vec::new();
for asm in &mut self.assemblers {
if let Some(pkt) = asm.flush() {
completed.push(pkt);
}
}
completed
}
}
fn parse_pes_header(data: &[u8]) -> (Option<i64>, Option<i64>, usize) {
if data.len() < 9 || data[0] != 0x00 || data[1] != 0x00 || data[2] != 0x01 {
return (None, None, 0);
}
let stream_id = data[3];
if stream_id == 0xBC
|| stream_id == 0xBE
|| stream_id == 0xBF
|| stream_id == 0xF0
|| stream_id == 0xF1
|| stream_id == 0xFF
{
return (None, None, 6);
}
if data.len() < 9 {
return (None, None, 6);
}
let pts_dts_flags = (data[7] >> 6) & 0x03;
let header_data_len = data[8] as usize;
let data_start = (9 + header_data_len).min(data.len());
let mut pts = None;
let mut dts = None;
if pts_dts_flags >= 2 && header_data_len >= 5 && data.len() >= 14 {
pts = parse_timestamp(&data[9..14]);
}
if pts_dts_flags == 3 && header_data_len >= 10 && data.len() >= 19 {
dts = parse_timestamp(&data[14..19]);
}
(pts, dts, data_start)
}
fn parse_timestamp(data: &[u8]) -> Option<i64> {
if data.len() < 5 {
return None;
}
if (data[2] & 0x01) == 0 || (data[4] & 0x01) == 0 {
return None;
}
let b0 = data[0] as i64;
let b1 = data[1] as i64;
let b2 = data[2] as i64;
let b3 = data[3] as i64;
let b4 = data[4] as i64;
Some(((b0 >> 1) & 0x07) << 30 | b1 << 22 | (b2 >> 1) << 15 | b3 << 7 | b4 >> 1)
}
pub fn scan_streams(data: &[u8]) -> Option<Vec<crate::disc::Stream>> {
use crate::disc::*;
let mut pat_pmt_pid: Option<u16> = None;
let mut offset = 0;
while offset + BD_TS_PACKET_SIZE <= data.len() {
if data[offset + 4] != SYNC_BYTE {
offset += 1;
continue;
}
let pid = (((data[offset + 5] & 0x1F) as u16) << 8) | data[offset + 6] as u16;
let pusi = data[offset + 5] & 0x40 != 0;
if pid == 0 && pusi {
let payload_start = offset + 4 + 4;
if payload_start + 12 < data.len() {
let pointer = data[payload_start] as usize;
let pat_start = payload_start + 1 + pointer;
if pat_start + 12 < data.len() && data[pat_start] == 0x00 {
let section_len = (((data[pat_start + 1] & 0x0F) as usize) << 8)
| data[pat_start + 2] as usize;
let entries_start = pat_start + 8;
if section_len < 4 { offset += BD_TS_PACKET_SIZE; continue; }
let entries_end = pat_start + 3 + section_len - 4;
let mut e = entries_start;
while e + 4 <= data.len() && e < entries_end {
let prog_num = ((data[e] as u16) << 8) | data[e + 1] as u16;
let p = (((data[e + 2] & 0x1F) as u16) << 8) | data[e + 3] as u16;
if prog_num != 0 {
pat_pmt_pid = Some(p);
break;
}
e += 4;
}
}
}
}
offset += BD_TS_PACKET_SIZE;
}
let pmt_pid = pat_pmt_pid?;
let mut streams = Vec::new();
offset = 0;
while offset + BD_TS_PACKET_SIZE <= data.len() {
if data[offset + 4] != SYNC_BYTE {
offset += 1;
continue;
}
let pid = (((data[offset + 5] & 0x1F) as u16) << 8) | data[offset + 6] as u16;
let pusi = data[offset + 5] & 0x40 != 0;
if pid == pmt_pid && pusi {
let payload_start = offset + 4 + 4;
if payload_start + 1 >= data.len() {
offset += BD_TS_PACKET_SIZE;
continue;
}
let pointer = data[payload_start] as usize;
let pmt_start = payload_start + 1 + pointer;
if pmt_start + 12 >= data.len() {
offset += BD_TS_PACKET_SIZE;
continue;
}
if data[pmt_start] != 0x02 {
offset += BD_TS_PACKET_SIZE;
continue;
}
let section_len =
(((data[pmt_start + 1] & 0x0F) as usize) << 8) | data[pmt_start + 2] as usize;
let prog_info_len =
(((data[pmt_start + 10] & 0x0F) as usize) << 8) | data[pmt_start + 11] as usize;
let mut pos = pmt_start + 12 + prog_info_len;
let end = pmt_start + 3 + section_len - 4;
while pos + 5 <= data.len() && pos < end {
let stream_type = data[pos];
let es_pid = (((data[pos + 1] & 0x1F) as u16) << 8) | data[pos + 2] as u16;
let es_info_len = (((data[pos + 3] & 0x0F) as usize) << 8) | data[pos + 4] as usize;
let stream = match stream_type {
0x1B => Some(Stream::Video(VideoStream {
pid: es_pid,
codec: Codec::H264,
resolution: Resolution::R1080p,
frame_rate: FrameRate::Unknown,
hdr: HdrFormat::Sdr,
color_space: ColorSpace::Bt709,
secondary: false,
label: String::new(),
})),
0x24 => Some(Stream::Video(VideoStream {
pid: es_pid,
codec: Codec::Hevc,
resolution: Resolution::R2160p,
frame_rate: FrameRate::Unknown,
hdr: HdrFormat::Sdr,
color_space: ColorSpace::Bt709,
secondary: false,
label: String::new(),
})),
0xEA => Some(Stream::Video(VideoStream {
pid: es_pid,
codec: Codec::Vc1,
resolution: Resolution::R1080p,
frame_rate: FrameRate::Unknown,
hdr: HdrFormat::Sdr,
color_space: ColorSpace::Bt709,
secondary: false,
label: String::new(),
})),
0x02 => Some(Stream::Video(VideoStream {
pid: es_pid,
codec: Codec::Mpeg2,
resolution: Resolution::R1080i,
frame_rate: FrameRate::Unknown,
hdr: HdrFormat::Sdr,
color_space: ColorSpace::Bt709,
secondary: false,
label: String::new(),
})),
0x81 => Some(Stream::Audio(AudioStream {
pid: es_pid,
codec: Codec::Ac3,
channels: AudioChannels::Surround51,
language: "und".into(),
sample_rate: SampleRate::S48,
secondary: false,
label: String::new(),
})),
0x83 => Some(Stream::Audio(AudioStream {
pid: es_pid,
codec: Codec::TrueHd,
channels: AudioChannels::Surround51,
language: "und".into(),
sample_rate: SampleRate::S48,
secondary: false,
label: String::new(),
})),
0x84 | 0xA1 => Some(Stream::Audio(AudioStream {
pid: es_pid,
codec: Codec::Ac3Plus,
channels: AudioChannels::Surround51,
language: "und".into(),
sample_rate: SampleRate::S48,
secondary: false,
label: String::new(),
})),
0x85 | 0x86 => Some(Stream::Audio(AudioStream {
pid: es_pid,
codec: Codec::DtsHdMa,
channels: AudioChannels::Surround51,
language: "und".into(),
sample_rate: SampleRate::S48,
secondary: false,
label: String::new(),
})),
0x82 => Some(Stream::Audio(AudioStream {
pid: es_pid,
codec: Codec::Dts,
channels: AudioChannels::Surround51,
language: "und".into(),
sample_rate: SampleRate::S48,
secondary: false,
label: String::new(),
})),
0x90 => Some(Stream::Subtitle(SubtitleStream {
pid: es_pid,
codec: Codec::Pgs,
language: "und".into(),
forced: false,
codec_data: None,
})),
_ => None,
};
if let Some(s) = stream {
streams.push(s);
}
pos += 5 + es_info_len;
}
break;
}
offset += BD_TS_PACKET_SIZE;
}
if streams.is_empty() {
None
} else {
Some(streams)
}
}
pub fn scan_first_pts(data: &[u8], target_pid: u16) -> Option<i64> {
let mut offset = 0;
while offset + BD_TS_PACKET_SIZE <= data.len() {
if data[offset + 4] != SYNC_BYTE {
offset += 1;
continue;
}
let pid = (((data[offset + 5] & 0x1F) as u16) << 8) | data[offset + 6] as u16;
let pusi = data[offset + 5] & 0x40 != 0;
if pid == target_pid && pusi {
let ts = &data[offset + 4..offset + BD_TS_PACKET_SIZE];
let afc = (ts[3] >> 4) & 0x03;
let payload_start = if afc == 3 { 5 + ts[4] as usize } else { 4 };
if payload_start < TS_PACKET_SIZE {
let payload = &ts[payload_start..];
if payload.len() >= 14 && payload[0] == 0 && payload[1] == 0 && payload[2] == 1 {
let pts_dts_flags = (payload[7] >> 6) & 0x03;
if pts_dts_flags >= 2 {
return parse_timestamp(&payload[9..14]);
}
}
}
}
offset += BD_TS_PACKET_SIZE;
}
None
}
pub fn scan_last_pts(data: &[u8], target_pid: u16) -> Option<i64> {
let mut last_pts = None;
let mut offset = 0;
while offset + BD_TS_PACKET_SIZE <= data.len() {
if data[offset + 4] != SYNC_BYTE {
offset += 1;
continue;
}
let pid = (((data[offset + 5] & 0x1F) as u16) << 8) | data[offset + 6] as u16;
let pusi = data[offset + 5] & 0x40 != 0;
if pid == target_pid && pusi {
let ts = &data[offset + 4..offset + BD_TS_PACKET_SIZE];
let afc = (ts[3] >> 4) & 0x03;
let payload_start = if afc == 3 { 5 + ts[4] as usize } else { 4 };
if payload_start < TS_PACKET_SIZE {
let payload = &ts[payload_start..];
if payload.len() >= 14 && payload[0] == 0 && payload[1] == 0 && payload[2] == 1 {
let pts_dts_flags = (payload[7] >> 6) & 0x03;
if pts_dts_flags >= 2 {
last_pts = parse_timestamp(&payload[9..14]);
}
}
}
}
offset += BD_TS_PACKET_SIZE;
}
last_pts
}
pub fn scan_duration<R: std::io::Read + std::io::Seek>(r: &mut R, video_pid: u16) -> Option<f64> {
use std::io::SeekFrom;
let start_pos = r.stream_position().ok()?;
let mut head_buf = vec![0u8; SCAN_HEAD_SIZE];
r.seek(SeekFrom::Start(0)).ok()?;
let head_n = r.read(&mut head_buf).ok()?;
let first_pts = scan_first_pts(&head_buf[..head_n], video_pid)?;
let file_size = r.seek(SeekFrom::End(0)).ok()?;
let tail_size: u64 = SCAN_TAIL_SIZE as u64;
let raw_pos = file_size.saturating_sub(tail_size);
let seek_pos = (raw_pos / BD_TS_PACKET_SIZE as u64) * BD_TS_PACKET_SIZE as u64;
r.seek(SeekFrom::Start(seek_pos)).ok()?;
let mut tail_buf = vec![0u8; tail_size as usize];
let tail_n = r.read(&mut tail_buf).ok()?;
let last_pts = scan_last_pts(&tail_buf[..tail_n], video_pid)?;
let _ = r.seek(SeekFrom::Start(start_pos));
if last_pts > first_pts {
Some((last_pts - first_pts) as f64 / 90000.0)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_timestamp() {
let data = [0x21, 0x00, 0x01, 0x00, 0x01];
assert_eq!(parse_timestamp(&data), Some(0));
let data2 = [0x21, 0x00, 0x07, 0xE9, 0x01]; let pts = parse_timestamp(&data2);
assert!(pts.is_some() && pts.unwrap() >= 0);
let bad = [0x00, 0x00, 0x00, 0x00, 0x00]; assert_eq!(parse_timestamp(&bad), None);
}
#[test]
fn test_demuxer_empty() {
let mut demux = TsDemuxer::new(&[0x1011]);
let result = demux.feed(&[]);
assert!(result.is_empty());
}
}