Rust reader for MPEG2 Transport Stream data
Dump timestamps attached to any ADTS audio or H264 video streams.
```rust
extern crate mpeg2ts_reader;
use std::env; use std::fs::File; use std::io::Read; use mpeg2tsreader::demultiplex; use mpeg2tsreader::pes; use mpeg2ts_reader::StreamType;
packetfilterswitch!{
DumpFilterSwitch
pub struct DumpStreamConstructor; impl demultiplex::StreamConstructor for DumpStreamConstructor { type F = DumpFilterSwitch;
fn construct(&mut self, req: demultiplex::FilterRequest) -> Self::F {
match req {
demultiplex::FilterRequest::ByPid(0) => DumpFilterSwitch::Pat(demultiplex::PatPacketFilter::new()),
demultiplex::FilterRequest::ByPid(_) => DumpFilterSwitch::Null(demultiplex::NullPacketFilter::new()),
demultiplex::FilterRequest::ByStream(StreamType::H264, pmt_section, stream_info) => PtsDumpElementaryStreamConsumer::construct(pmt_section, stream_info),
demultiplex::FilterRequest::ByStream(StreamType::Adts, pmt_section, stream_info) => PtsDumpElementaryStreamConsumer::construct(pmt_section, stream_info),
demultiplex::FilterRequest::ByStream(_stype, _pmt_section, _stream_info) => DumpFilterSwitch::Null(demultiplex::NullPacketFilter::new()),
demultiplex::FilterRequest::Pmt{pid, program_number} => DumpFilterSwitch::Pmt(demultiplex::PmtPacketFilter::new(pid, program_number)),
}
}
}
// Implement the ElementaryStreamConsumer to just dump and PTS/DTS timestamps to stdout pub struct PtsDumpElementaryStreamConsumer { pid: u16, } impl PtsDumpElementaryStreamConsumer { fn construct(pmtsect: &demultiplex::PmtSection, streaminfo: &demultiplex::StreamInfo) -> DumpFilterSwitch { let filter = pes::PesPacketFilter::new( PtsDumpElementaryStreamConsumer { pid: streaminfo.elementarypid(), } ); DumpFilterSwitch::Pes(filter) } } impl pes::ElementaryStreamConsumer for PtsDumpElementaryStreamConsumer { fn startstream(&mut self) { } fn beginpacket(&mut self, header: pes::PesHeader) { match header.contents() { pes::PesContents::Parsed(Some(parsed)) => { match parsed.ptsdts() { pes::PtsDts::PtsOnly(Ok(pts)) => { println!("PID {}: pts {:#x}", self.pid, pts.value()) }, pes::PtsDts::Both{pts:Ok(pts), dts:Ok(dts)} => { println!("PID {}: pts={:#x} dts={:#x}", self.pid, pts.value(), dts.value()) }, _ => (), } }, pes::PesContents::Parsed(None) => (), pes::PesContents::Payload() => { }, } } fn continuepacket(&mut self, data: &[u8]) { } fn endpacket(&mut self) { } fn continuity_error(&mut self) { } }
fn main() { // open input file named on command line, let name = env::args().nth(1).unwrap(); let mut f = File::open(&name).expect(&format!("file not found: {}", &name));
// create the context object that stores the state of the transport stream demultiplexing
// process
let mut ctx = DumpDemuxContext::new(DumpStreamConstructor);
// create the demultiplexer, which will use the ctx to create a filter for pid 0 (PAT)
let mut demux = demultiplex::Demultiplex::new(&mut ctx);
// consume the input file,
let mut buf = [0u8; 188*1024];
loop {
match f.read(&mut buf[..]).expect("read failed") {
0 => break ,
n => demux.push(&mut ctx, &buf[0..n]),
}
}
} ```
On my laptop (which can read sequentially from main memory at around 16GiByte/s), a microbenchmark that parses TS structure, but ignores the audio and video contained within, can process at a rate of 10 GiBytes/s (80 Gibits/s).
Real usage that actually processes the contents of the stream will of course be slower!
The conditions of the test are, * the data is already in memory (no network/disk access) * test dataset is larger than CPU cache * processing is happening on a single core (no multiprocessing of the stream).
Not all Transport Stream features are supported yet. Here's a summary of what's available, and what's yet to come: