use mpeg2ts_reader::demultiplex; use mpeg2ts_reader::packet::Packet; use std::fs::File; use std::io::Read; use std::{env, io, marker}; pub enum StripFilterSwitch { Write(PacketWriter), Strip(PacketStripper), } impl demultiplex::PacketFilter for StripFilterSwitch { type Ctx = StripDemuxContext; #[inline(always)] fn consume(&mut self, ctx: &mut StripDemuxContext, pk: &Packet<'_>) { match self { &mut StripFilterSwitch::Write(ref mut f) => f.consume(ctx, pk), &mut StripFilterSwitch::Strip(ref mut f) => f.consume(ctx, pk), } } } pub struct StripDemuxContext { changeset: demultiplex::FilterChangeset>, out: W, written: u64, stripped: u64, } impl demultiplex::DemuxContext for StripDemuxContext { type F = StripFilterSwitch; fn filter_changeset(&mut self) -> &mut demultiplex::FilterChangeset { &mut self.changeset } fn construct(&mut self, req: demultiplex::FilterRequest<'_, '_>) -> Self::F { match req { // 'Stuffing' data on PID 0x1fff may be used to pad-out parts of the transport stream // so that it has constant overall bitrate. Since we are stripping these packets, // we use an implementation of PacketFilter that just ignores them demultiplex::FilterRequest::ByPid(mpeg2ts_reader::STUFFING_PID) => { StripFilterSwitch::Strip(PacketStripper::default()) } // for any other packet, we will use a PacketFilter implementation that writes the // packet to the output _ => StripFilterSwitch::Write(PacketWriter::default()), } } } impl StripDemuxContext { pub fn new(out: W) -> Self { StripDemuxContext { changeset: demultiplex::FilterChangeset::default(), out, written: 0, stripped: 0, } } } pub struct PacketWriter(marker::PhantomData); impl demultiplex::PacketFilter for PacketWriter { type Ctx = StripDemuxContext; fn consume(&mut self, ctx: &mut Self::Ctx, pk: &Packet<'_>) { ctx.out .write_all(pk.buffer()) .expect("writing to stdout failed"); ctx.written += 1; } } impl Default for PacketWriter { fn default() -> Self { PacketWriter(marker::PhantomData::default()) } } pub struct PacketStripper(marker::PhantomData); impl demultiplex::PacketFilter for PacketStripper { type Ctx = StripDemuxContext; fn consume(&mut self, ctx: &mut Self::Ctx, _pk: &Packet<'_>) { ctx.stripped += 1; } } impl Default for PacketStripper { fn default() -> Self { PacketStripper(marker::PhantomData::default()) } } fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); // open input file named on command line, let name = env::args().nth(1).unwrap(); let mut f = File::open(&name).unwrap_or_else(|_| panic!("file not found: {}", &name)); let out = io::stdout(); let buf = io::BufWriter::new(out); // create the context object that stores the state of the transport stream demultiplexing // process let mut ctx = StripDemuxContext::new(buf); // 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]), } } eprintln!( "Written {} TS packets, stripped {} TS packets", ctx.written, ctx.stripped ); }