mpeg2ts-reader 0.4.0

Parser for MPEG Transport Stream data
Documentation

mpeg2ts-reader

Rust reader for MPEG2 Transport Stream data

Build Status crates.io version

Example

Dump timestamps attached to any ADTS audio or H264 video streams.

#[macro_use]
extern crate mpeg2ts_reader;

use std::env;
use std::fs::File;
use std::io::Read;
use mpeg2ts_reader::demultiplex;
use mpeg2ts_reader::pes;
use mpeg2ts_reader::StreamType;

packet_filter_switch!{
    DumpFilterSwitch<DumpDemuxContext> {
        Pat: demultiplex::PatPacketFilter<DumpDemuxContext>,
        Pmt: demultiplex::PmtPacketFilter<DumpDemuxContext>,
        Null: demultiplex::NullPacketFilter<DumpDemuxContext>,
        Pes: pes::PesPacketFilter<DumpDemuxContext,PtsDumpElementaryStreamConsumer>,
    }
}
demux_context!(DumpDemuxContext, DumpStreamConstructor);

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(_pmt_sect: &demultiplex::PmtSection, stream_info: &demultiplex::StreamInfo)
        -> DumpFilterSwitch
    {
        let filter = pes::PesPacketFilter::new(
            PtsDumpElementaryStreamConsumer {
                pid: stream_info.elementary_pid(),
            }
        );
        DumpFilterSwitch::Pes(filter)
    }
}
impl pes::ElementaryStreamConsumer for PtsDumpElementaryStreamConsumer {
    fn start_stream(&mut self) { }
    fn begin_packet(&mut self, header: pes::PesHeader) {
        match header.contents() {
            pes::PesContents::Parsed(Some(parsed)) => {
                match parsed.pts_dts() {
                    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 continue_packet(&mut self, _data: &[u8]) { }
    fn end_packet(&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]),
        }
    }
}

Performance

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).

Supported Transport Stream features

Not all Transport Stream features are supported yet. Here's a summary of what's available, and what's yet to come:

  • Framing
    • ISO/IEC 13818-1 188-byte packets
    • ☐ m2ts 192-byte packets (would be nice if an external crate could support, at least)
    • ☐ recovery after loss of synchronisation
  • Transport Stream packet
    • ☑ Fixed headers
    • ☐ Adaptation field (Adaptation field size is accounted for in finding packet payload, adaptation field details are not yet exposed)
  • Program Specific Information tables
    • ☑ Section syntax
    • ☐ 'Multi-section' tables
    • ☑ PAT - Program Association Table
    • ☑ PMT - Program Mapping Table
    • ☐ TSDT - Transport Stream Description Table
  • Packetised Elementary Stream syntax
    • ☑ PES_packet_data
    • ☑ PTS/DTS
    • ☐ ESCR
    • ☐ ES_rate
    • ☐ DSM_trick_mode
    • ☐ additional_copy_info
    • ☐ PES_CRC
    • ☐ PES_extension
  • Descriptors
    • ☐ video_stream_descriptor
    • ☐ audio_stream_descriptor
    • ☐ hierarchy_descriptor
    • ☐ registration_descriptor
    • ☐ data_stream_alignment_descriptor
    • ☐ target_background_grid_descriptor
    • ☐ video_window_descriptor
    • ☐ ca_descriptor
    • ☐ iso_639_language_descriptor
    • ☐ system_clock_descriptor
    • ☐ multiplex_buffer_utilization_descriptor
    • ☐ copyright_descriptor
    • ☐ maximum_bitrate_descriptor
    • ☐ private_data_indicator_descriptor
    • ☐ smoothing_buffer_descriptor
    • ☐ std_descriptor
    • ☐ ibp_descriptor
    • ☐ mpeg4_video_descriptor
    • ☐ mpeg4_audio_descriptor
    • ☐ iod_descriptor
    • ☐ sl_descriptor
    • ☐ fmc_descriptor
    • ☐ external_es_id_descriptor
    • ☐ muxcode_descriptor
    • ☐ fmxbuffersize_descriptor
    • ☐ multiplexbuffer_descriptor