├── .gitignore ├── renovate.json ├── run.sh ├── src ├── result.rs ├── section │ ├── cat.rs │ ├── bat.rs │ ├── nit.rs │ ├── mod.rs │ ├── traits.rs │ ├── pat.rs │ ├── sdt.rs │ ├── pmt.rs │ └── eit.rs ├── iso_639.rs ├── descriptor │ ├── desc_dvb_0x54.rs │ ├── desc_dvb_0x56.rs │ ├── desc_dvb_0x6a.rs │ ├── desc_dvb_0x53.rs │ ├── desc_dvb_0x4e.rs │ ├── desc_dvb_0x4d.rs │ ├── desc_dvb_0x48.rs │ ├── desc_0x0a.rs │ ├── mod.rs │ └── tag.rs ├── subtable_id.rs ├── rational.rs ├── lib.rs ├── pcr.rs ├── pid.rs ├── annex_c.rs ├── duration_fmt.rs ├── error.rs ├── table_id.rs ├── header.rs ├── stream_type.rs ├── packet.rs ├── annex_a2.rs ├── pes.rs └── demuxer.rs ├── dockerfiles ├── tsplay │ └── Dockerfile ├── docker-compose.yml └── build-rs │ └── Dockerfile ├── .editorconfig ├── Cargo.toml ├── README.md ├── LICENSE └── examples └── probe ├── error.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:recommended" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker-compose -f ./dockerfiles/docker-compose.yml up --build 3 | -------------------------------------------------------------------------------- /src/result.rs: -------------------------------------------------------------------------------- 1 | use std::result::Result as StdResult; 2 | 3 | use crate::error::Error; 4 | 5 | pub type Result = StdResult; 6 | -------------------------------------------------------------------------------- /src/section/cat.rs: -------------------------------------------------------------------------------- 1 | /// ISO/IEC 13818-1 2 | /// 3 | /// Conditional Access Table 4 | #[allow(dead_code)] 5 | pub struct CAT<'buf> { 6 | buf: &'buf [u8], 7 | } 8 | -------------------------------------------------------------------------------- /src/section/bat.rs: -------------------------------------------------------------------------------- 1 | /// ETSI EN 300 468 V1.15.1 2 | /// 3 | /// Bouquet Association Table 4 | #[allow(dead_code)] 5 | pub struct BAT<'buf> { 6 | buf: &'buf [u8], 7 | } 8 | -------------------------------------------------------------------------------- /src/section/nit.rs: -------------------------------------------------------------------------------- 1 | /// ETSI EN 300 468 V1.15.1 2 | /// 3 | /// Network Information Table 4 | #[allow(dead_code)] 5 | pub struct NIT<'buf> { 6 | buf: &'buf [u8], 7 | } 8 | -------------------------------------------------------------------------------- /dockerfiles/tsplay/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | tstools 5 | 6 | CMD ["tsplay", "/mnt/dumps/1.ts", "239.255.1.1:5500", "-loop"] 7 | # CMD ["tsplay", "/mnt/dumps/2.ts", "239.255.1.1:5500", "-loop"] 8 | # CMD ["tsplay", "/mnt/dumps/3.ts", "239.255.1.1:5500", "-loop"] 9 | -------------------------------------------------------------------------------- /dockerfiles/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | build-rs: 5 | build: 6 | dockerfile: ./dockerfiles/build-rs/Dockerfile 7 | context: .. 8 | 9 | tsplay: 10 | build: 11 | dockerfile: ./dockerfiles/tsplay/Dockerfile 12 | context: .. 13 | volumes: 14 | - /tmp/va-dumps/:/mnt/dumps/ 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [dockerfiles/**/nginx/**/*.conf] 12 | indent_style = tab 13 | indent_size = 2 14 | 15 | [dockerfiles/**/nginx/**/default] 16 | indent_style = tab 17 | indent_size = 2 18 | 19 | [Makefile] 20 | indent_style = tab 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /src/section/mod.rs: -------------------------------------------------------------------------------- 1 | mod bat; 2 | mod cat; 3 | mod eit; 4 | mod nit; 5 | mod pat; 6 | mod pmt; 7 | mod sdt; 8 | mod traits; 9 | 10 | pub use self::bat::BAT; 11 | pub use self::cat::CAT; 12 | pub use self::eit::EIT; 13 | pub use self::nit::NIT; 14 | pub use self::pat::PAT; 15 | pub use self::pmt::PMT; 16 | pub use self::sdt::SDT; 17 | pub use self::traits::{Bufer, Cursor, Szer, TryNewer}; 18 | pub use self::traits::{WithHeader, WithSyntaxSection}; 19 | -------------------------------------------------------------------------------- /src/iso_639.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug)] 4 | pub struct ISO639([char; 3]); 5 | 6 | impl ISO639 { 7 | pub fn must_from_bytes_3(b: &[u8]) -> ISO639 { 8 | ISO639([char::from(b[0]), char::from(b[1]), char::from(b[2])]) 9 | } 10 | } 11 | 12 | impl fmt::Display for ISO639 { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | write!(f, "{}{}{}", self.0[0], self.0[1], self.0[2]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x54.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // TODO: implement 4 | 5 | /// ETSI EN 300 468 V1.15.1 6 | /// 7 | /// Content descriptor 8 | #[derive(Clone)] 9 | pub struct DescDVB0x54<'buf> { 10 | buf: &'buf [u8], 11 | } 12 | 13 | impl<'buf> DescDVB0x54<'buf> { 14 | #[inline(always)] 15 | pub fn new(buf: &'buf [u8]) -> DescDVB0x54<'buf> { 16 | DescDVB0x54 { buf } 17 | } 18 | } 19 | 20 | impl<'buf> fmt::Debug for DescDVB0x54<'buf> { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | write!(f, ":dvb-0x54") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x56.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // TODO: implement 4 | 5 | /// ETSI EN 300 468 V1.15.1 6 | /// 7 | /// Teletext descriptor 8 | #[derive(Clone)] 9 | pub struct DescDVB0x56<'buf> { 10 | buf: &'buf [u8], 11 | } 12 | 13 | impl<'buf> DescDVB0x56<'buf> { 14 | #[inline(always)] 15 | pub fn new(buf: &'buf [u8]) -> DescDVB0x56<'buf> { 16 | DescDVB0x56 { buf } 17 | } 18 | } 19 | 20 | impl<'buf> fmt::Debug for DescDVB0x56<'buf> { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | write!(f, ":dvb-0x56") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x6a.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // TODO: implement 4 | 5 | /// ETSI EN 300 468 V1.15.1 6 | /// 7 | /// AC-3 descriptor 8 | #[derive(Clone)] 9 | pub struct DescDVB0x6A<'buf> { 10 | buf: &'buf [u8], 11 | } 12 | 13 | impl<'buf> DescDVB0x6A<'buf> { 14 | #[inline(always)] 15 | pub fn new(buf: &'buf [u8]) -> DescDVB0x6A<'buf> { 16 | DescDVB0x6A { buf } 17 | } 18 | } 19 | 20 | impl<'buf> fmt::Debug for DescDVB0x6A<'buf> { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | write!(f, ":dvb-0x6a") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x53.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // TODO: implement 4 | 5 | /// ETSI EN 300 468 V1.15.1 6 | /// 7 | /// CA identifier descriptor 8 | #[derive(Clone)] 9 | pub struct DescDVB0x53<'buf> { 10 | buf: &'buf [u8], 11 | } 12 | 13 | impl<'buf> DescDVB0x53<'buf> { 14 | #[inline(always)] 15 | pub fn new(buf: &'buf [u8]) -> DescDVB0x53<'buf> { 16 | DescDVB0x53 { buf } 17 | } 18 | } 19 | 20 | impl<'buf> fmt::Debug for DescDVB0x53<'buf> { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | write!(f, ":dvb-0x53") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x4e.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // TODO: implement 4 | 5 | /// ETSI EN 300 468 V1.15.1 6 | /// 7 | /// Extended event descriptor 8 | #[derive(Clone)] 9 | pub struct DescDVB0x4E<'buf> { 10 | buf: &'buf [u8], 11 | } 12 | 13 | impl<'buf> DescDVB0x4E<'buf> { 14 | #[allow(dead_code)] 15 | const HEADER_SZ: usize = 4; 16 | 17 | #[inline(always)] 18 | pub fn new(buf: &'buf [u8]) -> DescDVB0x4E<'buf> { 19 | DescDVB0x4E { buf } 20 | } 21 | } 22 | 23 | impl<'buf> fmt::Debug for DescDVB0x4E<'buf> { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | write!(f, ":dvb-0x4e") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/subtable_id.rs: -------------------------------------------------------------------------------- 1 | use crate::table_id::TableID; 2 | 3 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 4 | pub enum SubtableID { 5 | /// (table-id, transport-stream-id(ext) [, version-number]) 6 | PAT(TableID, u16, u8), 7 | 8 | /// (table-id, program-number(ext) [, version-number]) 9 | PMT(TableID, u16, u8), 10 | 11 | /// (table-id, transport-stream-id(ext), original-network-id, version-number) 12 | SDT(TableID, u16, u16, u8), 13 | 14 | /// (table-id, service-id(ext), transport-stream-id, original-network-id, version-number) 15 | EIT(TableID, u16, u16, u16, u8), 16 | } 17 | 18 | pub trait SubtableIDer { 19 | fn subtable_id(&self) -> SubtableID; 20 | } 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "va-ts" 3 | version = "0.0.4" 4 | edition = "2018" 5 | authors = ["Ivan Egorov "] 6 | license = "MIT" 7 | repository = "https://github.com/video-audio/va-ts" 8 | homepage = "https://github.com/video-audio/va-ts" 9 | documentation = "https://docs.rs/va-ts/0.0.3/va-ts" 10 | description = """ 11 | MPEG-TS implementation for Rust 12 | """ 13 | keywords = [ 14 | "ts", "mpeg-ts", "dvb", 15 | "muxer", "demuxer" 16 | ] 17 | categories = ["video-audio"] 18 | exclude = [ 19 | "dockerfiles/*", 20 | "dumps/*", 21 | "run.sh" 22 | ] 23 | 24 | [dependencies] 25 | chrono = "~0.4.31" 26 | encoding_rs = "~0.8.33" 27 | 28 | [dev-dependencies] 29 | clap = "~4.5.0" 30 | url = "~2.5.0" 31 | -------------------------------------------------------------------------------- /src/rational.rs: -------------------------------------------------------------------------------- 1 | pub const TB_27MHZ: Rational = Rational { 2 | num: 1, 3 | den: 27_000_000, 4 | }; 5 | #[allow(dead_code)] 6 | pub const TB_90KHZ: Rational = Rational { 7 | num: 1, 8 | den: 90_000, 9 | }; 10 | #[allow(dead_code)] 11 | pub const TB_1MS: Rational = Rational { 12 | num: 1, 13 | den: 1_000_000, 14 | }; 15 | pub const TB_1NS: Rational = Rational { 16 | num: 1, 17 | den: 1_000_000_000, 18 | }; 19 | 20 | pub struct Rational { 21 | num: u64, 22 | den: u64, 23 | } 24 | 25 | pub fn rescale(v: u64, src: Rational, dst: Rational) -> u64 { 26 | let num = u128::from(src.num) * u128::from(dst.den); 27 | let den = u128::from(src.den) * u128::from(dst.num); 28 | 29 | ((v as f64) * ((num as f64) / (den as f64))) as u64 30 | } 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod result; 3 | 4 | mod annex_a2; 5 | mod annex_c; 6 | mod demuxer; 7 | mod descriptor; 8 | mod duration_fmt; 9 | mod header; 10 | mod iso_639; 11 | mod packet; 12 | mod pcr; 13 | mod pes; 14 | mod pid; 15 | mod rational; 16 | mod section; 17 | mod stream_type; 18 | mod subtable_id; 19 | mod table_id; 20 | 21 | pub use annex_a2::AnnexA2; 22 | pub use demuxer::{Demuxer, DemuxerEvents, Packet as DemuxedPacket, Table as DemuxedTable}; 23 | pub use descriptor::{DescDVB0x48, DescDVB0x4D, Tag, TagDVB}; 24 | pub use duration_fmt::DurationFmt; 25 | pub use packet::Packet; 26 | pub use pes::PES; 27 | pub use pid::PID; 28 | pub use result::Result; 29 | pub use section::Bufer; 30 | pub use section::{WithHeader, WithSyntaxSection, EIT, PAT, PMT, SDT}; 31 | pub use stream_type::StreamType; 32 | pub use subtable_id::SubtableID; 33 | pub use table_id::TableID; 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # video-audio mpeg-ts muxer/demuxer 2 | 3 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 4 | [![Crate](http://meritbadge.herokuapp.com/va-ts)](https://crates.io/crates/va-ts) 5 | 6 | MPEG-TS implementation for Rust. 7 | 8 | ## Overview 9 | 10 | sub-table-id: 11 | 12 | - **PAT** - (table-id, transport-stream-id(ext) [, version-number]) 13 | - **PMT** - (table-id, program-number(ext) [, version-number]) 14 | - **SDT** - (table-id, transport-stream-id(ext), original-network-id, version-number) 15 | - **EIT** - (table-id, service-id(ext), transport-stream-id, original-network-id, version-number) 16 | 17 | table-id-extension: 18 | 19 | - **PAT** - transport-stream-id 20 | - **PMT** - program-number 21 | - **SDT** - transport-stream-id 22 | - **EIT** - service-id 23 | 24 | ## License 25 | 26 | va-ts is provided under the MIT license. See [LICENSE](LICENSE). 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Ivan Egorov 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /dockerfiles/build-rs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12 as build-rs 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y \ 5 | curl \ 6 | musl-tools 7 | 8 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 9 | ENV PATH="/root/.cargo/bin:${PATH}" 10 | 11 | RUN rustup target add x86_64-unknown-linux-musl 12 | 13 | WORKDIR /usr/local/src/udp-mpegts 14 | 15 | RUN mkdir -pv src/ 16 | RUN mkdir -pv examples/probe 17 | RUN echo "fn main() {println!(\"---\")}" > ./src/main.rs 18 | RUN echo "fn main() {println!(\"---\")}" > ./examples/probe/main.rs 19 | COPY ./Cargo.toml ./ 20 | RUN RUSTFLAGS=-Clinker=musl-gcc cargo \ 21 | build \ 22 | --release \ 23 | --verbose \ 24 | --target=x86_64-unknown-linux-musl 25 | RUN RUSTFLAGS=-Clinker=musl-gcc cargo \ 26 | build \ 27 | --release \ 28 | --verbose \ 29 | --example probe \ 30 | --target=x86_64-unknown-linux-musl 31 | 32 | RUN rm -rvf ./target/x86_64-unknown-linux-musl/debug/deps/va-ts* 33 | RUN rm -rvf ./target/x86_64-unknown-linux-musl/debug/va-ts* 34 | RUN rm -rvf ./target/x86_64-unknown-linux-musl/release/deps/va-ts* 35 | RUN rm -rvf ./target/x86_64-unknown-linux-musl/release/va-ts* 36 | RUN rm -rvf ./src/* 37 | RUN rm -rvf ./examples/* 38 | 39 | COPY ./src/ ./src/ 40 | COPY ./examples/ ./examples/ 41 | RUN RUSTFLAGS=-Clinker=musl-gcc cargo \ 42 | build \ 43 | --release \ 44 | --verbose \ 45 | --example probe \ 46 | --target=x86_64-unknown-linux-musl 47 | 48 | # RUN cargo test -- --nocapture 49 | 50 | # ------------------------------------- 51 | FROM alpine:latest 52 | 53 | COPY --from=build-rs /usr/local/src/udp-mpegts/target/x86_64-unknown-linux-musl/release/examples/probe /usr/local/bin/probe 54 | 55 | RUN ls -lah /usr/local/bin/probe 56 | 57 | ENV RUST_BACKTRACE=full 58 | 59 | CMD ["probe", "--input", "udp://239.255.1.1:5500"] 60 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x4d.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::annex_a2::AnnexA2; 4 | 5 | /// ETSI EN 300 468 V1.15.1 6 | /// 7 | /// Short event descriptor 8 | #[derive(Clone)] 9 | pub struct DescDVB0x4D<'buf> { 10 | buf: &'buf [u8], 11 | } 12 | 13 | impl<'buf> DescDVB0x4D<'buf> { 14 | const HEADER_SZ: usize = 4; 15 | 16 | #[inline(always)] 17 | pub fn new(buf: &'buf [u8]) -> DescDVB0x4D<'buf> { 18 | DescDVB0x4D { buf } 19 | } 20 | 21 | #[inline(always)] 22 | fn buf_pos_event_name(&self) -> usize { 23 | Self::HEADER_SZ 24 | } 25 | 26 | #[inline(always)] 27 | fn buf_pos_text_length(&self) -> usize { 28 | self.buf_pos_event_name() + (self.event_name_length() as usize) 29 | } 30 | 31 | #[inline(always)] 32 | fn buf_pos_text(&self) -> usize { 33 | self.buf_pos_text_length() + 1 34 | } 35 | 36 | #[inline(always)] 37 | fn event_name_length(&self) -> u8 { 38 | self.buf[3] 39 | } 40 | 41 | #[inline(always)] 42 | pub fn event_name(&self) -> &'buf [u8] { 43 | &self.buf[self.buf_pos_event_name()..self.buf_pos_text_length()] 44 | } 45 | 46 | #[inline(always)] 47 | fn text_length(&self) -> u8 { 48 | self.buf[self.buf_pos_text_length()] 49 | } 50 | 51 | #[inline(always)] 52 | pub fn text(&self) -> &'buf [u8] { 53 | let lft = self.buf_pos_text(); 54 | let rght = lft + (self.text_length() as usize); 55 | &self.buf[lft..rght] 56 | } 57 | } 58 | 59 | impl<'buf> fmt::Debug for DescDVB0x4D<'buf> { 60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 | write!(f, ":dvb-0x4d (")?; 62 | 63 | let mut dst_buf = [0u8; 256]; 64 | let mut dst_str = std::str::from_utf8_mut(&mut dst_buf).unwrap(); 65 | 66 | write!(f, ":event-name")?; 67 | match AnnexA2::decode(self.event_name(), &mut dst_str) { 68 | Ok(..) => write!(f, r#" "{}""#, dst_str), 69 | Err(err) => write!(f, " (error: {:?})", err), 70 | }?; 71 | 72 | dst_buf = [0u8; 256]; 73 | dst_str = std::str::from_utf8_mut(&mut dst_buf).unwrap(); 74 | 75 | write!(f, " :text")?; 76 | match AnnexA2::decode(self.text(), &mut dst_str) { 77 | Ok(..) => write!(f, r#" "{}""#, dst_str), 78 | Err(err) => write!(f, " (error: {})", err), 79 | }?; 80 | 81 | write!(f, ")") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/pcr.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::time::Duration; 3 | 4 | use crate::duration_fmt::DurationFmt; 5 | use crate::error::{Error, Kind as ErrorKind}; 6 | use crate::rational; 7 | use crate::rational::Rational; 8 | use crate::result::Result; 9 | 10 | /// Program clock reference, 11 | /// stored as 33 bits base, 6 bits reserved, 9 bits extension. 12 | /// The value is calculated as base * 300 + extension. 13 | pub struct PCR<'buf> { 14 | buf: &'buf [u8], 15 | } 16 | 17 | impl<'buf> PCR<'buf> { 18 | pub const SZ: usize = 6; 19 | const TB: Rational = rational::TB_27MHZ; 20 | 21 | #[inline(always)] 22 | pub fn new(buf: &'buf [u8]) -> PCR<'buf> { 23 | PCR { buf } 24 | } 25 | 26 | #[inline(always)] 27 | #[allow(dead_code)] 28 | fn try_new(buf: &'buf [u8]) -> Result> { 29 | let a = Self::new(buf); 30 | a.validate()?; 31 | Ok(a) 32 | } 33 | 34 | #[inline(always)] 35 | fn validate(&self) -> Result<()> { 36 | if self.buf.len() < Self::SZ { 37 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::SZ))) 38 | } else { 39 | Ok(()) 40 | } 41 | } 42 | 43 | #[inline(always)] 44 | fn base(&self) -> u64 { 45 | (u64::from(self.buf[0]) << 25) 46 | | (u64::from(self.buf[1]) << 17) 47 | | (u64::from(self.buf[2]) << 9) 48 | | (u64::from(self.buf[3]) << 1) 49 | | u64::from((self.buf[4] & 0b1000_0000) >> 7) 50 | } 51 | 52 | #[inline(always)] 53 | fn ext(&self) -> u16 { 54 | (u16::from(self.buf[4] & 0b0000_00001) << 8) | u16::from(self.buf[5]) 55 | } 56 | 57 | /// 27MHz 58 | pub fn value(&self) -> u64 { 59 | self.base() * 300 + u64::from(self.ext()) 60 | } 61 | 62 | /// nanoseconds 63 | pub fn ns(&self) -> u64 { 64 | rational::rescale(self.value(), Self::TB, rational::TB_1NS) 65 | } 66 | } 67 | 68 | impl<'buf> From<&PCR<'buf>> for Duration { 69 | fn from(pcr: &PCR) -> Self { 70 | Duration::from_nanos(pcr.ns()) 71 | } 72 | } 73 | 74 | impl<'buf> From<&PCR<'buf>> for DurationFmt { 75 | fn from(pcr: &PCR) -> Self { 76 | DurationFmt::from_nanos(pcr.ns()) 77 | } 78 | } 79 | 80 | impl<'buf> fmt::Debug for PCR<'buf> { 81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 82 | write!( 83 | f, 84 | ":pcr (:raw {:08X}:{:04X} :v(27MHz) {} :duration {})", 85 | self.base(), 86 | self.ext(), 87 | self.value(), 88 | DurationFmt::from(self) 89 | ) 90 | } 91 | } 92 | 93 | impl<'buf> fmt::Display for PCR<'buf> { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | write!(f, ":pcr {}", DurationFmt::from(self)) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/descriptor/desc_dvb_0x48.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::annex_a2::AnnexA2; 4 | 5 | // TODO: implement 6 | 7 | /// ETSI EN 300 468 V1.15.1 8 | /// 9 | /// Service descriptor 10 | #[derive(Clone)] 11 | pub struct DescDVB0x48<'buf> { 12 | buf: &'buf [u8], 13 | } 14 | 15 | impl<'buf> DescDVB0x48<'buf> { 16 | const HEADER_SZ: usize = 2; 17 | 18 | #[inline(always)] 19 | pub fn new(buf: &'buf [u8]) -> DescDVB0x48<'buf> { 20 | DescDVB0x48 { buf } 21 | } 22 | 23 | #[inline(always)] 24 | pub fn service_type(&self) -> u8 { 25 | self.buf[0] 26 | } 27 | 28 | #[inline(always)] 29 | fn buf_pos_service_provider_name(&self) -> usize { 30 | Self::HEADER_SZ 31 | } 32 | 33 | #[inline(always)] 34 | fn buf_pos_service_name_length(&self) -> usize { 35 | self.buf_pos_service_provider_name() + (self.service_provider_name_length() as usize) 36 | } 37 | 38 | #[inline(always)] 39 | fn buf_pos_service_name(&self) -> usize { 40 | self.buf_pos_service_name_length() + 1 41 | } 42 | 43 | #[inline(always)] 44 | pub fn service_provider_name_length(&self) -> u8 { 45 | self.buf[1] 46 | } 47 | 48 | #[inline(always)] 49 | pub fn service_provider_name(&self) -> &'buf [u8] { 50 | &self.buf[self.buf_pos_service_provider_name()..self.buf_pos_service_name_length()] 51 | } 52 | 53 | #[inline(always)] 54 | pub fn service_name_length(&self) -> u8 { 55 | self.buf[self.buf_pos_service_name_length()] 56 | } 57 | 58 | #[inline(always)] 59 | pub fn service_name(&self) -> &'buf [u8] { 60 | let lft = self.buf_pos_service_name(); 61 | let rgh = lft + (self.service_name_length() as usize); 62 | &self.buf[lft..rgh] 63 | } 64 | } 65 | 66 | impl<'buf> fmt::Debug for DescDVB0x48<'buf> { 67 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 68 | write!( 69 | f, 70 | ":dvb-0x48 (:service-type 0x{:02}/{}", 71 | self.service_type(), 72 | self.service_type() 73 | )?; 74 | 75 | let mut dst_buf = [0u8; 256]; 76 | let mut dst_str = std::str::from_utf8_mut(&mut dst_buf).unwrap(); 77 | 78 | write!(f, " :provider")?; 79 | match AnnexA2::decode(self.service_provider_name(), &mut dst_str) { 80 | Ok(..) => write!(f, r#" "{}""#, dst_str), 81 | Err(err) => write!(f, " (error: {:?})", err), 82 | }?; 83 | 84 | dst_buf = [0u8; 256]; 85 | dst_str = std::str::from_utf8_mut(&mut dst_buf).unwrap(); 86 | 87 | write!(f, " :service")?; 88 | match AnnexA2::decode(self.service_name(), &mut dst_str) { 89 | Ok(..) => write!(f, r#" "{}""#, dst_str), 90 | Err(err) => write!(f, " (error: {:?})", err), 91 | }?; 92 | 93 | write!(f, ")") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/descriptor/desc_0x0a.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::error::{Error, Kind as ErrorKind}; 4 | use crate::iso_639::ISO639; 5 | use crate::result::Result; 6 | use crate::section::{Cursor, Szer, TryNewer}; 7 | 8 | /// ISO/IEC 13818-1 9 | /// 10 | /// ISO 639 language descriptor 11 | #[derive(Clone)] 12 | pub struct Desc0x0A<'buf> { 13 | buf: &'buf [u8], 14 | } 15 | 16 | impl<'buf> Desc0x0A<'buf> { 17 | #[inline(always)] 18 | pub fn new(buf: &'buf [u8]) -> Desc0x0A<'buf> { 19 | Desc0x0A { buf } 20 | } 21 | 22 | #[inline(always)] 23 | pub fn languages(&self) -> Cursor<'buf, Language> { 24 | Cursor::new(self.buf) 25 | } 26 | } 27 | 28 | impl<'buf> fmt::Debug for Desc0x0A<'buf> { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | write!(f, ":0x0a :languages")?; 31 | 32 | for resl in self.languages() { 33 | write!(f, "\n ")?; 34 | match resl { 35 | Ok(l) => { 36 | l.fmt(f)?; 37 | } 38 | Err(err) => { 39 | write!(f, "error parse 0x0a language: {}", err)?; 40 | } 41 | } 42 | } 43 | 44 | Ok(()) 45 | } 46 | } 47 | 48 | pub struct Language<'buf> { 49 | buf: &'buf [u8], 50 | } 51 | 52 | impl<'buf> Language<'buf> { 53 | const SZ: usize = 4; 54 | 55 | #[inline(always)] 56 | pub fn new(buf: &'buf [u8]) -> Language<'buf> { 57 | Language { buf } 58 | } 59 | 60 | #[inline(always)] 61 | pub fn validate(&self) -> Result<()> { 62 | if self.buf.len() < Self::SZ { 63 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::SZ))) 64 | } else { 65 | Ok(()) 66 | } 67 | } 68 | 69 | #[inline(always)] 70 | pub fn iso_639_language_code(&self) -> ISO639 { 71 | ISO639::must_from_bytes_3(self.buf) 72 | } 73 | 74 | #[inline(always)] 75 | pub fn audio_type(&self) -> u8 { 76 | self.buf[3] 77 | } 78 | } 79 | 80 | impl<'buf> Szer for Language<'buf> { 81 | #[inline(always)] 82 | fn sz(&self) -> usize { 83 | Self::SZ 84 | } 85 | } 86 | 87 | impl<'buf> TryNewer<'buf> for Language<'buf> { 88 | #[inline(always)] 89 | fn try_new(buf: &'buf [u8]) -> Result> { 90 | let s = Language::new(buf); 91 | s.validate()?; 92 | Ok(s) 93 | } 94 | } 95 | 96 | impl<'buf> fmt::Debug for Language<'buf> { 97 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 98 | write!( 99 | f, 100 | r#" :language (:iso-639 "{}" :audio_type {}/0x{:02X})"#, 101 | self.iso_639_language_code(), 102 | self.audio_type(), 103 | self.audio_type() 104 | ) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/pid.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 2 | pub enum PID { 3 | PAT, 4 | CAT, 5 | TSDT, 6 | 7 | /// NIT, ST 8 | NIT, 9 | /// SDT, BAT, ST 10 | SDT, 11 | /// EIT, ST CIT (TS 102 323 \[13\]) 12 | EIT, 13 | /// RST, ST 14 | RST, 15 | /// TDT, TOT, ST 16 | TDT, 17 | /// network synchronization 18 | NetworkSynchronization, 19 | /// RNT (TS 102 323 \[13\]) 20 | RNT, 21 | 22 | InbandSignalling, 23 | Measurement, 24 | DIT, 25 | SIT, 26 | 27 | NULL, 28 | 29 | /// 0x0003...0x000F 30 | /// 0x0017...0x001B 31 | Reserved(u16), 32 | 33 | Other(u16), 34 | } 35 | 36 | impl PID { 37 | #[inline(always)] 38 | pub fn is_section(self) -> bool { 39 | match self { 40 | PID::Other(..) | PID::NULL | PID::Reserved(..) => false, 41 | _ => true, 42 | } 43 | } 44 | 45 | #[inline(always)] 46 | pub fn is_null(self) -> bool { 47 | match self { 48 | PID::NULL => true, 49 | _ => false, 50 | } 51 | } 52 | 53 | #[inline(always)] 54 | pub fn is_other(self) -> bool { 55 | match self { 56 | PID::Other(..) => true, 57 | _ => false, 58 | } 59 | } 60 | } 61 | 62 | impl From for PID { 63 | fn from(d: u16) -> Self { 64 | match d { 65 | 0x0000 => PID::PAT, 66 | 0x0001 => PID::CAT, 67 | 0x0002 => PID::TSDT, 68 | 0x0003..=0x000F => PID::Reserved(d), 69 | 0x0010 => PID::NIT, 70 | 0x0011 => PID::SDT, 71 | 0x0012 => PID::EIT, 72 | 0x0013 => PID::RST, 73 | 0x0014 => PID::TDT, 74 | 0x0015 => PID::NetworkSynchronization, 75 | 0x0016 => PID::RNT, 76 | 0x0017..=0x001B => PID::Reserved(d), 77 | 0x001C => PID::InbandSignalling, 78 | 0x001D => PID::Measurement, 79 | 0x001E => PID::DIT, 80 | 0x001F => PID::SIT, 81 | 82 | 0x1FFF => PID::NULL, 83 | 84 | _ => PID::Other(d), 85 | } 86 | } 87 | } 88 | 89 | impl From for u16 { 90 | fn from(pid: PID) -> u16 { 91 | match pid { 92 | PID::PAT => 0x0000, 93 | PID::CAT => 0x0001, 94 | PID::TSDT => 0x0002, 95 | PID::NIT => 0x0010, 96 | PID::SDT => 0x0011, 97 | PID::EIT => 0x0012, 98 | PID::RST => 0x0013, 99 | PID::TDT => 0x0014, 100 | PID::NetworkSynchronization => 0x0015, 101 | PID::RNT => 0x0016, 102 | PID::InbandSignalling => 0x001C, 103 | PID::Measurement => 0x001D, 104 | PID::DIT => 0x001E, 105 | PID::SIT => 0x001F, 106 | 107 | PID::NULL => 0x1FFF, 108 | 109 | PID::Reserved(d) => d, 110 | 111 | PID::Other(d) => d, 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /examples/probe/error.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::convert::Into; 3 | use std::error::Error as StdError; 4 | use std::fmt; 5 | use std::fmt::Error as FmtError; 6 | use std::io::Error as IoError; 7 | use std::result::Result as StdResult; 8 | use std::str::Utf8Error; 9 | 10 | use ts::error::Error as TsError; 11 | 12 | pub type Result = StdResult; 13 | 14 | macro_rules! from { 15 | ($src:path, $dst:path) => { 16 | impl From<$src> for Error { 17 | fn from(err: $src) -> Error { 18 | Error::new($dst(err)) 19 | } 20 | } 21 | }; 22 | } 23 | 24 | #[derive(Debug)] 25 | pub enum Kind { 26 | InputUrlMissingHost, 27 | InputUrlHostMustBeDomain, 28 | Io(IoError), 29 | Fmt(FmtError), 30 | Encoding(Utf8Error), 31 | SyncPoison, // TODO: rewrite 32 | Ts(TsError), 33 | 34 | Unknown(Box), 35 | } 36 | 37 | pub struct Error { 38 | pub kind: Kind, 39 | pub details: Option>, 40 | } 41 | 42 | impl Error { 43 | pub fn new(kind: Kind) -> Error { 44 | Error { 45 | kind: kind, 46 | details: None, 47 | } 48 | } 49 | 50 | pub fn new_with_details(kind: Kind, details: I) -> Error 51 | where 52 | I: Into>, 53 | { 54 | Error { 55 | kind: kind, 56 | details: Some(details.into()), 57 | } 58 | } 59 | } 60 | 61 | impl fmt::Display for Error { 62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 | write!(f, "{:?}", self) 64 | } 65 | } 66 | 67 | impl fmt::Debug for Error { 68 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 69 | write!(f, r#"(:error {:?} ("{}""#, self.kind, self.description())?; 70 | 71 | if let Some(details) = self.details.as_ref() { 72 | write!(f, r#" "{}""#, details)?; 73 | } 74 | 75 | write!(f, "))") 76 | } 77 | } 78 | 79 | impl StdError for Error { 80 | fn description(&self) -> &str { 81 | match self.kind { 82 | Kind::InputUrlMissingHost => "missing host inside input URL", 83 | Kind::InputUrlHostMustBeDomain => "provided host must be valid domain", 84 | Kind::Encoding(ref err) => err.description(), 85 | Kind::Io(ref err) => err.description(), 86 | Kind::Fmt(ref err) => err.description(), 87 | Kind::SyncPoison => "sync lock/condvar poison error", 88 | Kind::Ts(ref err) => err.description(), 89 | 90 | Kind::Unknown(ref err) => err.description(), 91 | } 92 | } 93 | 94 | fn cause(&self) -> Option<&dyn StdError> { 95 | match self.kind { 96 | Kind::InputUrlMissingHost => None, 97 | Kind::InputUrlHostMustBeDomain => None, 98 | Kind::Encoding(ref err) => Some(err), 99 | Kind::Io(ref err) => Some(err), 100 | Kind::Fmt(ref err) => Some(err), 101 | Kind::SyncPoison => None, 102 | Kind::Ts(ref err) => Some(err), 103 | 104 | Kind::Unknown(ref err) => Some(err.as_ref()), 105 | } 106 | } 107 | } 108 | 109 | from!(Utf8Error, Kind::Encoding); 110 | from!(IoError, Kind::Io); 111 | from!(FmtError, Kind::Fmt); 112 | from!(TsError, Kind::Ts); 113 | 114 | impl From> for Error 115 | where 116 | B: StdError + Send + Sync + 'static, 117 | { 118 | fn from(err: Box) -> Error { 119 | Error::new(Kind::Unknown(err)) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/annex_c.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use chrono::prelude::*; 4 | 5 | use crate::error::{Error, Kind as ErrorKind}; 6 | use crate::result::Result; 7 | 8 | /// simple binary-coded decimal converter 9 | #[inline(always)] 10 | fn bcd(hex: u8) -> u8 { 11 | let digit1 = (hex & 0xF0) >> 4; 12 | let digit2 = hex & 0x0F; 13 | 14 | digit1 * 10 + digit2 15 | } 16 | 17 | /// Modified Julian Date to YMD 18 | fn mjb_to_ymd(mjd: u16) -> (i32, u32, u32) { 19 | let y_y = ((f32::from(mjd) - 15_078.2) / 365.25) as i32; 20 | let m_m = 21 | ((f32::from(mjd) - 14_956.1 - ((((y_y as f32) * 365.25) as i32) as f32)) / 30.6001) as i32; 22 | let d = (i32::from(mjd) 23 | - 14_956 24 | - (((y_y as f32) * 365.25) as i32) 25 | - (((m_m as f32) * 30.6001) as i32)) as u32; 26 | 27 | let k = if m_m == 14 || m_m == 15 { 1 } else { 0 }; 28 | 29 | let y = y_y + k + 1900; 30 | let m = (m_m - 1 - k * 12) as u32; 31 | 32 | (y, m, d) 33 | } 34 | 35 | #[allow(dead_code)] 36 | pub fn from_bytes_into_date_time_utc(buf: &[u8]) -> Result> { 37 | if buf.len() < 5 { 38 | return Err(Error::new(ErrorKind::AnnexCBuf(buf.len(), 5))); 39 | } 40 | 41 | let mjd = (u16::from(buf[0]) << 8) | u16::from(buf[1]); 42 | let (hh, mm, ss) = ( 43 | u32::from(bcd(buf[2])), 44 | u32::from(bcd(buf[3])), 45 | u32::from(bcd(buf[4])), 46 | ); 47 | 48 | let (y, m, d) = mjb_to_ymd(mjd); 49 | 50 | Ok(Utc.ymd(y, m, d).and_hms(hh, mm, ss)) 51 | } 52 | 53 | #[allow(dead_code)] 54 | pub fn from_bytes_into_duration(buf: &[u8]) -> Result { 55 | if buf.len() < 3 { 56 | return Err(Error::new(ErrorKind::AnnexCBuf(buf.len(), 3))); 57 | } 58 | 59 | let (hh, mm, ss) = ( 60 | u64::from(bcd(buf[0])), 61 | u64::from(bcd(buf[1])), 62 | u64::from(bcd(buf[2])), 63 | ); 64 | 65 | Ok(Duration::new(hh * 3600 + mm * 60 + ss, 0)) 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::from_bytes_into_date_time_utc; 71 | use super::from_bytes_into_duration; 72 | use crate::error::{Error, Kind as ErrorKind}; 73 | use chrono::prelude::*; 74 | use std::time::Duration; 75 | 76 | #[test] 77 | fn parse_datetime() { 78 | let buf: [u8; 5] = [0xE1, 0x71, 0x15, 0x00, 0x00]; 79 | 80 | let dt = from_bytes_into_date_time_utc(&buf); 81 | 82 | assert!(dt.is_ok()); 83 | assert_eq!( 84 | dt.unwrap_or(Utc::now()), 85 | Utc.ymd(2016, 11, 21).and_hms(15, 00, 00) 86 | ); 87 | } 88 | 89 | #[test] 90 | fn err_parse_datetime() { 91 | let buf: [u8; 4] = [0xE1, 0x71, 0x15, 0x00]; 92 | 93 | let dt = from_bytes_into_date_time_utc(&buf); 94 | 95 | assert!(dt.is_err()); 96 | if let Err(e) = dt { 97 | assert_eq!(e, Error::new(ErrorKind::AnnexCBuf(4, 5))); 98 | } 99 | } 100 | 101 | #[test] 102 | fn parse_duration() { 103 | let buf: [u8; 3] = [0x00, 0x45, 0x00]; 104 | 105 | let d = from_bytes_into_duration(&buf); 106 | 107 | assert!(d.is_ok()); 108 | assert_eq!( 109 | d.unwrap_or(Duration::new(0, 0)), 110 | Duration::from_secs(45 * 60) 111 | ) 112 | } 113 | 114 | #[test] 115 | fn err_parse_duration() { 116 | let buf: [u8; 2] = [0x00, 0x45]; 117 | 118 | let d = from_bytes_into_duration(&buf); 119 | 120 | assert!(d.is_err()); 121 | if let Err(e) = d { 122 | assert_eq!(e, Error::new(ErrorKind::AnnexCBuf(2, 3))); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/section/traits.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::result::Result; 4 | use crate::table_id::TableID; 5 | 6 | pub trait Bufer<'buf> { 7 | /// borrow a reference to the underlying buffer 8 | fn buf(&self) -> &'buf [u8]; 9 | } 10 | 11 | pub const HEADER_SZ: usize = 3; 12 | #[allow(dead_code)] 13 | pub const HEADER_MAX_SECTION_LENGTH: usize = 0x3FD; // 1021 14 | 15 | pub trait WithHeader<'buf>: Bufer<'buf> { 16 | /// buffer seeked 17 | #[inline(always)] 18 | fn b(&self) -> &'buf [u8] { 19 | self.buf() 20 | } 21 | 22 | #[inline(always)] 23 | fn table_id(&self) -> TableID { 24 | TableID::from(self.b()[0]) 25 | } 26 | 27 | /// if set to 1 (true) - 4th and 5th bytes 28 | /// are table-id-extension 29 | /// 30 | /// must be set to 1 for: 31 | /// PAT, CAT, PMT 32 | /// NIT, EIT, BAT, SDT 33 | #[inline(always)] 34 | fn section_syntax_indicator(&self) -> bool { 35 | (self.b()[1] & 0b1000_0000) != 0 36 | } 37 | 38 | #[inline(always)] 39 | fn section_length(&self) -> u16 { 40 | (u16::from(self.b()[1] & 0b0000_1111) << 8) | u16::from(self.b()[2]) 41 | } 42 | 43 | /// complete section length 44 | #[inline(always)] 45 | fn sz(&self) -> usize { 46 | HEADER_SZ + usize::from(self.section_length()) 47 | } 48 | } 49 | 50 | pub trait WithTableIDExtension<'buf>: Bufer<'buf> { 51 | /// buffer seeked 52 | #[inline(always)] 53 | fn b(&self) -> &'buf [u8] { 54 | self.buf() 55 | } 56 | 57 | #[inline(always)] 58 | fn table_id_extension(&self) -> u16 { 59 | (u16::from(self.b()[3]) << 8) | u16::from(self.b()[4]) 60 | } 61 | } 62 | 63 | pub const SYNTAX_SECTION_SZ: usize = 5; 64 | 65 | pub trait WithSyntaxSection<'buf>: Bufer<'buf> { 66 | /// buffer seeked 67 | #[inline(always)] 68 | fn b(&self) -> &'buf [u8] { 69 | &self.buf()[HEADER_SZ..] 70 | } 71 | 72 | #[inline(always)] 73 | #[allow(dead_code)] 74 | fn version_number(&self) -> u8 { 75 | (self.b()[2] & 0b0011_1110) >> 1 76 | } 77 | 78 | #[inline(always)] 79 | fn current_next_indicator(&self) -> bool { 80 | (self.b()[2] & 0b0000_0001) != 0 81 | } 82 | 83 | #[inline(always)] 84 | fn section_number(&self) -> u8 { 85 | self.b()[3] 86 | } 87 | 88 | #[inline(always)] 89 | fn last_section_number(&self) -> u8 { 90 | self.b()[4] 91 | } 92 | } 93 | 94 | pub trait Szer { 95 | fn sz(&self) -> usize; 96 | } 97 | 98 | pub trait TryNewer<'buf> { 99 | fn try_new(buf: &'buf [u8]) -> Result 100 | where 101 | Self: Sized; 102 | } 103 | 104 | pub struct Cursor<'buf, T> { 105 | buf: &'buf [u8], 106 | phantom: PhantomData, 107 | } 108 | 109 | impl<'buf, T> Cursor<'buf, T> { 110 | #[inline(always)] 111 | pub fn new(buf: &'buf [u8]) -> Cursor<'buf, T> { 112 | Cursor { 113 | buf, 114 | phantom: PhantomData, 115 | } 116 | } 117 | 118 | #[inline(always)] 119 | fn buf_drain(&mut self) { 120 | self.buf = &self.buf[self.buf.len()..]; 121 | } 122 | } 123 | 124 | impl<'buf, T> Iterator for Cursor<'buf, T> 125 | where 126 | T: TryNewer<'buf> + Szer, 127 | { 128 | type Item = Result; 129 | 130 | fn next(&mut self) -> Option { 131 | if self.buf.is_empty() { 132 | return None; 133 | } 134 | 135 | let row = match T::try_new(self.buf) { 136 | Ok(row) => row, 137 | Err(e) => { 138 | self.buf_drain(); 139 | return Some(Err(e)); 140 | } 141 | }; 142 | 143 | // seek buf 144 | if self.buf.len() > row.sz() { 145 | self.buf = &self.buf[row.sz()..]; 146 | } else { 147 | self.buf_drain(); 148 | } 149 | 150 | Some(Ok(row)) 151 | } 152 | } 153 | 154 | pub const CRC32_SZ: usize = 4; 155 | 156 | pub(crate) trait WithCRC32<'buf>: Bufer<'buf> {} 157 | -------------------------------------------------------------------------------- /src/descriptor/mod.rs: -------------------------------------------------------------------------------- 1 | mod tag; 2 | 3 | mod desc_0x0a; 4 | mod desc_dvb_0x48; 5 | mod desc_dvb_0x4d; 6 | mod desc_dvb_0x4e; 7 | mod desc_dvb_0x53; 8 | mod desc_dvb_0x54; 9 | mod desc_dvb_0x56; 10 | mod desc_dvb_0x6a; 11 | 12 | use std::fmt; 13 | use std::str; 14 | 15 | use crate::error::{Error, Kind as ErrorKind}; 16 | use crate::result::Result; 17 | use crate::section::{Szer, TryNewer}; 18 | 19 | pub use self::desc_0x0a::Desc0x0A; 20 | pub use self::desc_dvb_0x48::DescDVB0x48; 21 | pub use self::desc_dvb_0x4d::DescDVB0x4D; 22 | pub use self::desc_dvb_0x4e::DescDVB0x4E; 23 | pub use self::desc_dvb_0x53::DescDVB0x53; 24 | pub use self::desc_dvb_0x54::DescDVB0x54; 25 | pub use self::desc_dvb_0x56::DescDVB0x56; 26 | pub use self::desc_dvb_0x6a::DescDVB0x6A; 27 | pub use self::tag::{Tag, TagDVB}; 28 | 29 | #[derive(Clone)] 30 | pub struct Descriptor<'buf> { 31 | buf: &'buf [u8], 32 | } 33 | 34 | impl<'buf> Descriptor<'buf> { 35 | const HEADER_SZ: usize = 2; 36 | 37 | #[inline(always)] 38 | pub fn new(buf: &'buf [u8]) -> Descriptor<'buf> { 39 | Descriptor { buf } 40 | } 41 | 42 | #[inline(always)] 43 | pub fn validate(&self) -> Result<()> { 44 | if self.buf.len() < Self::HEADER_SZ { 45 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::HEADER_SZ))) 46 | } else if self.buf.len() < self.sz() { 47 | Err(Error::new(ErrorKind::Buf(self.buf.len(), self.sz()))) 48 | } else { 49 | Ok(()) 50 | } 51 | } 52 | 53 | #[inline(always)] 54 | pub fn tag(&self) -> Tag { 55 | Tag::from(self.buf[0]) 56 | } 57 | 58 | #[inline(always)] 59 | pub fn is_dvb_service(&self) -> bool { 60 | self.tag().is_dvb_service() 61 | } 62 | 63 | #[inline(always)] 64 | pub fn is_dvb_short_event(&self) -> bool { 65 | self.tag().is_dvb_short_event() 66 | } 67 | 68 | #[inline(always)] 69 | fn len(&self) -> u8 { 70 | self.buf[1] 71 | } 72 | 73 | /// seek 74 | #[inline(always)] 75 | pub fn buf_data(&self) -> &'buf [u8] { 76 | &self.buf[Self::HEADER_SZ..] 77 | } 78 | 79 | #[inline(always)] 80 | fn data_as_unicode(&'buf self) -> &'buf str { 81 | str::from_utf8(&self.buf_data()).unwrap_or("---") 82 | } 83 | } 84 | 85 | impl<'buf> Szer for Descriptor<'buf> { 86 | #[inline(always)] 87 | fn sz(&self) -> usize { 88 | Self::HEADER_SZ + (self.len() as usize) 89 | } 90 | } 91 | 92 | impl<'buf> TryNewer<'buf> for Descriptor<'buf> { 93 | #[inline(always)] 94 | fn try_new(buf: &'buf [u8]) -> Result> { 95 | let mut d = Descriptor::new(buf); 96 | d.validate()?; 97 | d.buf = &buf[..d.sz()]; // slice 98 | Ok(d) 99 | } 100 | } 101 | 102 | impl<'buf> fmt::Debug for Descriptor<'buf> { 103 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 104 | write!(f, ":desc (:tag {:?} :length {})", self.tag(), self.len())?; 105 | write!(f, "\n ")?; 106 | 107 | match self.tag() { 108 | Tag::ISO639 => { 109 | Desc0x0A::new(self.buf_data()).fmt(f)?; 110 | } 111 | Tag::DVB(TagDVB::Service) => { 112 | DescDVB0x48::new(self.buf_data()).fmt(f)?; 113 | } 114 | Tag::DVB(TagDVB::ShortEvent) => { 115 | DescDVB0x4D::new(self.buf_data()).fmt(f)?; 116 | } 117 | Tag::DVB(TagDVB::ExtendedEvent) => { 118 | DescDVB0x4E::new(self.buf_data()).fmt(f)?; 119 | } 120 | Tag::DVB(TagDVB::CAIdentifier) => { 121 | DescDVB0x53::new(self.buf_data()).fmt(f)?; 122 | } 123 | Tag::DVB(TagDVB::Content) => { 124 | DescDVB0x54::new(self.buf_data()).fmt(f)?; 125 | } 126 | Tag::DVB(TagDVB::Teletext) => { 127 | DescDVB0x56::new(self.buf_data()).fmt(f)?; 128 | } 129 | Tag::DVB(TagDVB::AC3) => { 130 | DescDVB0x6A::new(self.buf_data()).fmt(f)?; 131 | } 132 | _ => { 133 | write!(f, ":data {}", self.data_as_unicode())?; 134 | } 135 | } 136 | 137 | Ok(()) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/duration_fmt.rs: -------------------------------------------------------------------------------- 1 | //! golang style duration format wrapper 2 | use std::cmp; 3 | use std::fmt; 4 | use std::time::Duration; 5 | 6 | pub struct DurationFmt(pub Duration); 7 | 8 | impl DurationFmt { 9 | pub fn from_nanos(nanos: u64) -> DurationFmt { 10 | DurationFmt(Duration::from_nanos(nanos)) 11 | } 12 | 13 | #[inline(always)] 14 | fn duration(&self) -> Duration { 15 | self.0 16 | } 17 | 18 | #[inline(always)] 19 | fn pure_nanos(&self) -> u128 { 20 | self.0.as_nanos() % Duration::from_micros(1).as_nanos() 21 | } 22 | 23 | #[inline(always)] 24 | fn pure_micros(&self) -> u128 { 25 | (self.0.as_nanos() % Duration::from_millis(1).as_nanos()) 26 | / Duration::from_micros(1).as_nanos() 27 | } 28 | 29 | #[inline(always)] 30 | fn pure_millis(&self) -> u128 { 31 | (self.0.as_nanos() % Duration::from_secs(1).as_nanos()) 32 | / Duration::from_millis(1).as_nanos() 33 | } 34 | 35 | #[inline(always)] 36 | fn pure_secs_as_f64(&self) -> f64 { 37 | ((self.0.as_nanos() % Duration::from_secs(60).as_nanos()) as f64) 38 | / (Duration::from_secs(1).as_nanos() as f64) 39 | } 40 | 41 | #[inline(always)] 42 | fn pure_mins(&self) -> u128 { 43 | (self.0.as_nanos() % Duration::from_secs(60 * 60).as_nanos()) 44 | / Duration::from_secs(60).as_nanos() 45 | } 46 | 47 | #[inline(always)] 48 | fn pure_hours(&self) -> u128 { 49 | self.0.as_nanos() / Duration::from_secs(60 * 60).as_nanos() 50 | } 51 | } 52 | 53 | impl cmp::PartialEq for DurationFmt { 54 | fn eq(&self, other: &Self) -> bool { 55 | self.duration() == other.duration() 56 | } 57 | } 58 | 59 | impl From for DurationFmt { 60 | #[inline(always)] 61 | fn from(d: Duration) -> Self { 62 | DurationFmt(d) 63 | } 64 | } 65 | 66 | impl fmt::Display for DurationFmt { 67 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 68 | match self.duration() { 69 | d if d <= Duration::from_micros(1) => write!(f, "{}ns", self.pure_nanos()), 70 | d if d <= Duration::from_millis(1) => { 71 | let ns = self.pure_nanos(); 72 | let mcs = self.pure_micros(); 73 | 74 | match ns { 75 | 0 => write!(f, "{}us", mcs), 76 | _ => write!(f, "{}us{}ns", mcs, ns), 77 | } 78 | } 79 | d if d <= (Duration::from_secs(1) / 10) => { 80 | let mcs = self.pure_micros(); 81 | let ms = self.pure_millis(); 82 | 83 | match mcs { 84 | 0 => write!(f, "{}ms", ms), 85 | _ => write!(f, "{}ms{}us", ms, mcs), 86 | } 87 | } 88 | _ => { 89 | let h = self.pure_hours(); 90 | let m = self.pure_mins(); 91 | let s = self.pure_secs_as_f64(); 92 | 93 | if h != 0 { 94 | write!(f, "{}h", h)?; 95 | } 96 | 97 | if m != 0 { 98 | write!(f, "{}m", m)?; 99 | } 100 | 101 | if s != 0.0 { 102 | write!(f, "{:.2}s", s) 103 | } else { 104 | Ok(()) 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | impl fmt::Debug for DurationFmt { 112 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 113 | write!(f, "{}", self) 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use super::DurationFmt; 120 | 121 | use std::time::Duration; 122 | 123 | #[test] 124 | fn fmt_ns() { 125 | assert_eq!(format!("{}", DurationFmt::from_nanos(1)), "1ns"); 126 | } 127 | 128 | #[test] 129 | fn fmt_h_m_s() { 130 | assert_eq!( 131 | format!( 132 | "{}", 133 | DurationFmt::from( 134 | Duration::from_secs(10*3600) + // 10h 135 | Duration::from_secs(30*60) + // 30m 136 | Duration::from_secs(15) + // 15s 137 | Duration::from_millis(100) // 0.1s 138 | ) 139 | ), 140 | "10h30m15.10s" 141 | ); 142 | } 143 | 144 | #[test] 145 | fn fmt_ms_us() { 146 | assert_eq!( 147 | format!( 148 | "{}", 149 | DurationFmt::from( 150 | Duration::from_millis(23) + // 23ms 151 | Duration::from_micros(17) + // 17us 152 | Duration::from_nanos(40) // 40ns 153 | ) 154 | ), 155 | "23ms17us" 156 | ); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error as StdError; 2 | use std::fmt; 3 | use std::io::Error as IoError; 4 | 5 | #[derive(Debug)] 6 | pub enum Kind { 7 | SyncByte(u8), 8 | Buf(usize, usize), 9 | PESStartCode(u32), 10 | SectionSyntaxIndicatorNotSet, 11 | AnnexA2EmptyBuf, 12 | AnnexA2UnsupportedEncoding, 13 | AnnexA2Decode, 14 | AnnexA2TableA3Unexpected(u8), 15 | AnnexA2TableA4Buf(usize, usize), 16 | AnnexA2TableA4Unexpected(u8), 17 | AnnexCBuf(usize, usize), 18 | 19 | Io(IoError), 20 | } 21 | 22 | pub struct Error(Kind); 23 | 24 | impl Error { 25 | pub fn new(kind: Kind) -> Error { 26 | Error(kind) 27 | } 28 | } 29 | 30 | impl fmt::Display for Error { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | write!(f, "{:?}", self) 33 | } 34 | } 35 | 36 | impl fmt::Debug for Error { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | write!( 39 | f, 40 | r#"(:error ({:?}) (:txt "{}""#, 41 | self.0, 42 | self.description() 43 | )?; 44 | 45 | match self.0 { 46 | Kind::SyncByte(b) => write!(f, " (:got 0x{:02X})", b)?, 47 | Kind::Buf(actual, expected) => { 48 | write!(f, " (:sz-actual {} :sz-expected {})", actual, expected)? 49 | } 50 | Kind::PESStartCode(actual) => write!(f, " (:actual 0x{:08X})", actual)?, 51 | 52 | Kind::AnnexA2TableA3Unexpected(b) => write!(f, " (:got 0x{:02X})", b)?, 53 | Kind::AnnexA2TableA4Buf(actual, expected) => { 54 | write!(f, " (:sz-actual {} :sz-expected {})", actual, expected)? 55 | } 56 | Kind::AnnexA2TableA4Unexpected(b) => write!(f, " (:got 0x{:02X})", b)?, 57 | 58 | Kind::AnnexCBuf(actual, expected) => { 59 | write!(f, " (:sz-actual {} :sz-expected {})", actual, expected)? 60 | } 61 | 62 | _ => {} 63 | } 64 | 65 | write!(f, "))") 66 | } 67 | } 68 | 69 | impl StdError for Error { 70 | fn description(&self) -> &str { 71 | match self.0 { 72 | Kind::SyncByte(..) => "expected sync byte as first element", 73 | Kind::Buf(..) => "buffer is too small, more data required", 74 | Kind::PESStartCode(..) => "(pes) unexpected start code", 75 | Kind::SectionSyntaxIndicatorNotSet => "(psi) section-syntax-indicator must be set", 76 | 77 | Kind::AnnexA2UnsupportedEncoding => "(annex-a2) unsupported encoding", 78 | Kind::AnnexA2Decode => "(annex-a2) decode error", 79 | Kind::AnnexA2EmptyBuf => "(annex-a2 parse) got empty character buffer", 80 | Kind::AnnexA2TableA3Unexpected(..) => "(annex-a2 table-a3 parse) unexpected value", 81 | Kind::AnnexA2TableA4Buf(..) => { 82 | "(annex-a2 table-a4 parse) buffer is too small, more data required" 83 | } 84 | Kind::AnnexA2TableA4Unexpected(..) => "(annex-a2 table-a4 parse) unexpected value", 85 | 86 | Kind::AnnexCBuf(..) => "(annex-c parse) buffer is too small, more data required", 87 | 88 | Kind::Io(ref err) => err.description(), 89 | } 90 | } 91 | 92 | fn cause(&self) -> Option<&dyn StdError> { 93 | match self.0 { 94 | Kind::Io(ref err) => Some(err), 95 | _ => None, 96 | } 97 | } 98 | } 99 | 100 | impl PartialEq for Error { 101 | fn eq(&self, other: &Error) -> bool { 102 | match (&self.0, &other.0) { 103 | (Kind::SyncByte(a1), Kind::SyncByte(a2)) => a1 == a2, 104 | (Kind::Buf(a1, b1), Kind::Buf(a2, b2)) => a1 == a2 && b1 == b2, 105 | (Kind::PESStartCode(a1), Kind::PESStartCode(a2)) => a1 == a2, 106 | (Kind::SectionSyntaxIndicatorNotSet, Kind::SectionSyntaxIndicatorNotSet) => true, 107 | (Kind::AnnexA2EmptyBuf, Kind::AnnexA2EmptyBuf) => true, 108 | (Kind::AnnexA2UnsupportedEncoding, Kind::AnnexA2UnsupportedEncoding) => true, 109 | (Kind::AnnexA2Decode, Kind::AnnexA2Decode) => true, 110 | (Kind::AnnexA2TableA3Unexpected(a1), Kind::AnnexA2TableA3Unexpected(a2)) => a1 == a2, 111 | (Kind::AnnexA2TableA4Buf(a1, b1), Kind::AnnexA2TableA4Buf(a2, b2)) => { 112 | a1 == a2 && b1 == b2 113 | } 114 | (Kind::AnnexA2TableA4Unexpected(a1), Kind::AnnexA2TableA4Unexpected(a2)) => a1 == a2, 115 | (Kind::AnnexCBuf(a1, a2), Kind::AnnexCBuf(b1, b2)) => a1 == a2 && b1 == b2, 116 | (Kind::Io(..), Kind::Io(..)) => true, 117 | _ => false, 118 | } 119 | } 120 | } 121 | impl Eq for Error {} 122 | 123 | impl From for Error { 124 | fn from(err: IoError) -> Error { 125 | Error::new(Kind::Io(err)) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/table_id.rs: -------------------------------------------------------------------------------- 1 | /// ETSI EN 300 468 V1.15.1 (2016-03) 2 | /// ISO/IEC 13818-1 3 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 4 | pub enum TableID { 5 | ProgramAssociationSection, 6 | ConditionalAccessSection, 7 | ProgramMapSection, 8 | TransportStreamDescriptionSection, 9 | 10 | NetworkInformationSectionActualNetwork, 11 | NetworkInformationSectionOtherNetwork, 12 | ServiceDescriptionSectionActualTransportStream, 13 | 14 | ServiceDescriptionSectionOtherTransportStream, 15 | BouquetAssociationSection, 16 | EISActualTransportStream, 17 | EISOtherTransportStream, 18 | 19 | EISActualTransportStreamSchedule(u8), 20 | EISOtherTransportStreamSchedule(u8), 21 | 22 | TimeDateSection, 23 | RunningStatusSection, 24 | StuffingSection, 25 | TimeOffsetSection, 26 | ApplicationInformationSection, 27 | ContainerSection, 28 | RelatedContentSection, 29 | ContentIdentifierSection, 30 | MPEFECSection, 31 | ResolutionNotificationSection, 32 | MPEIFECSection, 33 | DiscontinuityInformationSection, 34 | SelectionInformationSection, 35 | 36 | Reserved(u8), 37 | 38 | Other(u8), 39 | } 40 | 41 | impl From for TableID { 42 | fn from(d: u8) -> Self { 43 | match d { 44 | 0x00 => TableID::ProgramAssociationSection, 45 | 0x01 => TableID::ConditionalAccessSection, 46 | 0x02 => TableID::ProgramMapSection, 47 | 0x03 => TableID::TransportStreamDescriptionSection, 48 | 49 | 0x40 => TableID::NetworkInformationSectionActualNetwork, 50 | 0x41 => TableID::NetworkInformationSectionOtherNetwork, 51 | 0x42 => TableID::ServiceDescriptionSectionActualTransportStream, 52 | 53 | 0x46 => TableID::ServiceDescriptionSectionOtherTransportStream, 54 | 0x4A => TableID::BouquetAssociationSection, 55 | 0x4E => TableID::EISActualTransportStream, 56 | 0x4F => TableID::EISOtherTransportStream, 57 | 58 | 0x70 => TableID::TimeDateSection, 59 | 0x71 => TableID::RunningStatusSection, 60 | 0x72 => TableID::StuffingSection, 61 | 0x73 => TableID::TimeOffsetSection, 62 | 0x74 => TableID::ApplicationInformationSection, 63 | 0x75 => TableID::ContainerSection, 64 | 0x76 => TableID::RelatedContentSection, 65 | 0x77 => TableID::ContentIdentifierSection, 66 | 0x78 => TableID::MPEFECSection, 67 | 0x79 => TableID::ResolutionNotificationSection, 68 | 0x7A => TableID::MPEIFECSection, 69 | 0x7E => TableID::DiscontinuityInformationSection, 70 | 0x7F => TableID::SelectionInformationSection, 71 | 72 | 0x04..=0x3F | 0x43..=0x45 => TableID::Reserved(d), 73 | 74 | 0x50..=0x5F => TableID::EISActualTransportStreamSchedule(d), 75 | 0x60..=0x6F => TableID::EISOtherTransportStreamSchedule(d), 76 | 77 | _ => TableID::Other(d), 78 | } 79 | } 80 | } 81 | 82 | impl From for u8 { 83 | fn from(id: TableID) -> u8 { 84 | match id { 85 | TableID::ProgramAssociationSection => 0x00, 86 | TableID::ConditionalAccessSection => 0x01, 87 | TableID::ProgramMapSection => 0x02, 88 | TableID::TransportStreamDescriptionSection => 0x03, 89 | 90 | TableID::NetworkInformationSectionActualNetwork => 0x40, 91 | TableID::NetworkInformationSectionOtherNetwork => 0x41, 92 | TableID::ServiceDescriptionSectionActualTransportStream => 0x42, 93 | 94 | TableID::ServiceDescriptionSectionOtherTransportStream => 0x46, 95 | TableID::BouquetAssociationSection => 0x4A, 96 | TableID::EISActualTransportStream => 0x4E, 97 | TableID::EISOtherTransportStream => 0x4F, 98 | 99 | TableID::TimeDateSection => 0x70, 100 | TableID::RunningStatusSection => 0x71, 101 | TableID::StuffingSection => 0x72, 102 | TableID::TimeOffsetSection => 0x73, 103 | TableID::ApplicationInformationSection => 0x74, 104 | TableID::ContainerSection => 0x75, 105 | TableID::RelatedContentSection => 0x76, 106 | TableID::ContentIdentifierSection => 0x77, 107 | TableID::MPEFECSection => 0x78, 108 | TableID::ResolutionNotificationSection => 0x79, 109 | TableID::MPEIFECSection => 0x7A, 110 | TableID::DiscontinuityInformationSection => 0x7E, 111 | TableID::SelectionInformationSection => 0x7F, 112 | 113 | TableID::Reserved(d) => d, 114 | 115 | TableID::EISActualTransportStreamSchedule(d) => d, 116 | TableID::EISOtherTransportStreamSchedule(d) => d, 117 | 118 | TableID::Other(d) => d, 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/section/pat.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::error::{Error, Kind as ErrorKind}; 4 | use crate::pid::PID as TsPID; 5 | use crate::result::Result; 6 | use crate::subtable_id::{SubtableID, SubtableIDer}; 7 | 8 | use super::traits::*; 9 | 10 | /// ISO/IEC 13818-1 11 | /// 12 | /// Program association Table 13 | pub struct PAT<'buf> { 14 | buf: &'buf [u8], 15 | } 16 | 17 | impl<'buf> PAT<'buf> { 18 | const HEADER_FULL_SZ: usize = HEADER_SZ + SYNTAX_SECTION_SZ; 19 | 20 | #[inline(always)] 21 | pub fn new(buf: &'buf [u8]) -> PAT<'buf> { 22 | PAT { buf } 23 | } 24 | 25 | #[inline(always)] 26 | pub fn try_new(buf: &'buf [u8]) -> Result> { 27 | let s = Self::new(buf); 28 | s.validate()?; 29 | Ok(s) 30 | } 31 | 32 | #[inline(always)] 33 | pub fn validate(&self) -> Result<()> { 34 | if self.buf.len() < Self::HEADER_FULL_SZ { 35 | Err(Error::new(ErrorKind::Buf( 36 | self.buf.len(), 37 | Self::HEADER_FULL_SZ, 38 | ))) 39 | } else { 40 | Ok(()) 41 | } 42 | } 43 | 44 | /// slice buf 45 | #[inline(always)] 46 | fn buf_programs(&self) -> &'buf [u8] { 47 | let lft = Self::HEADER_FULL_SZ; 48 | let mut rght = HEADER_SZ + (self.section_length() as usize); 49 | 50 | if rght >= self.buf.len() { 51 | rght = self.buf.len(); 52 | } 53 | 54 | rght -= CRC32_SZ; 55 | 56 | &self.buf[lft..rght] 57 | } 58 | 59 | #[inline(always)] 60 | pub fn programs(&self) -> Cursor<'buf, Program> { 61 | Cursor::new(self.buf_programs()) 62 | } 63 | 64 | pub fn first_program_map_pid(&self) -> Option { 65 | self.programs().next().and_then(|res| match res { 66 | Ok(p) => match p.pid() { 67 | PID::ProgramMap(v) => Some(v), 68 | _ => None, 69 | }, 70 | _ => None, 71 | }) 72 | } 73 | 74 | #[inline(always)] 75 | pub fn transport_stream_id(&self) -> u16 { 76 | self.table_id_extension() 77 | } 78 | } 79 | 80 | impl<'buf> Bufer<'buf> for PAT<'buf> { 81 | fn buf(&self) -> &'buf [u8] { 82 | self.buf 83 | } 84 | } 85 | 86 | impl<'buf> WithHeader<'buf> for PAT<'buf> {} 87 | impl<'buf> WithTableIDExtension<'buf> for PAT<'buf> {} 88 | impl<'buf> WithSyntaxSection<'buf> for PAT<'buf> {} 89 | impl<'buf> WithCRC32<'buf> for PAT<'buf> {} 90 | 91 | impl<'buf> fmt::Debug for PAT<'buf> { 92 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 93 | write!( 94 | f, 95 | ":PAT (:id {:?} :transport-stream-id {})", 96 | self.subtable_id(), 97 | self.transport_stream_id(), 98 | )?; 99 | 100 | write!(f, "\n :programs")?; 101 | for p in self.programs().filter_map(Result::ok) { 102 | write!(f, "\n ")?; 103 | p.fmt(f)?; 104 | } 105 | 106 | Ok(()) 107 | } 108 | } 109 | 110 | impl<'buf> SubtableIDer for PAT<'buf> { 111 | #[inline(always)] 112 | fn subtable_id(&self) -> SubtableID { 113 | SubtableID::PAT( 114 | self.table_id(), 115 | self.transport_stream_id(), 116 | self.version_number(), 117 | ) 118 | } 119 | } 120 | 121 | #[derive(Debug)] 122 | pub enum PID { 123 | Network(u16), 124 | 125 | ProgramMap(u16), 126 | } 127 | 128 | impl PID { 129 | #[inline(always)] 130 | pub fn is_program_map(&self) -> bool { 131 | match self { 132 | PID::ProgramMap(..) => true, 133 | _ => false, 134 | } 135 | } 136 | } 137 | 138 | impl From for TsPID { 139 | fn from(id: PID) -> TsPID { 140 | match id { 141 | PID::Network(v) => TsPID::Other(v), 142 | PID::ProgramMap(v) => TsPID::Other(v), 143 | } 144 | } 145 | } 146 | 147 | pub struct Program<'buf> { 148 | buf: &'buf [u8], 149 | } 150 | 151 | impl<'buf> Program<'buf> { 152 | const SZ: usize = 4; 153 | 154 | #[inline(always)] 155 | pub fn new(buf: &'buf [u8]) -> Program<'buf> { 156 | Program { buf } 157 | } 158 | 159 | #[inline(always)] 160 | pub fn number(&self) -> u16 { 161 | (u16::from(self.buf[0]) << 8) | u16::from(self.buf[1]) 162 | } 163 | 164 | #[inline(always)] 165 | pub fn pid_raw(&self) -> u16 { 166 | (u16::from(self.buf[2] & 0b0001_1111) << 8) | u16::from(self.buf[3]) 167 | } 168 | 169 | #[inline(always)] 170 | pub fn pid(&self) -> PID { 171 | match self.number() { 172 | 0 => PID::Network(self.pid_raw()), 173 | _ => PID::ProgramMap(self.pid_raw()), 174 | } 175 | } 176 | } 177 | 178 | impl<'buf> Szer for Program<'buf> { 179 | #[inline(always)] 180 | fn sz(&self) -> usize { 181 | Program::SZ 182 | } 183 | } 184 | 185 | impl<'buf> TryNewer<'buf> for Program<'buf> { 186 | #[inline(always)] 187 | fn try_new(buf: &'buf [u8]) -> Result> { 188 | let p = Program::new(buf); 189 | Ok(p) 190 | } 191 | } 192 | 193 | impl<'buf> fmt::Debug for Program<'buf> { 194 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 195 | write!( 196 | f, 197 | ":program (:number {:?} :pid {:?}/0x{:02X})", 198 | self.number(), 199 | self.pid(), 200 | self.pid_raw(), 201 | ) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/section/sdt.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::descriptor::Descriptor; 4 | use crate::result::Result; 5 | use crate::subtable_id::{SubtableID, SubtableIDer}; 6 | 7 | use super::traits::*; 8 | 9 | /// ETSI EN 300 468 V1.15.1 10 | /// 11 | /// Service Description Table 12 | pub struct SDT<'buf> { 13 | buf: &'buf [u8], 14 | } 15 | 16 | impl<'buf> SDT<'buf> { 17 | const HEADER_SPECIFIC_SZ: usize = 3; 18 | const HEADER_FULL_SZ: usize = HEADER_SZ + SYNTAX_SECTION_SZ + Self::HEADER_SPECIFIC_SZ; 19 | 20 | #[inline(always)] 21 | pub fn new(buf: &'buf [u8]) -> SDT<'buf> { 22 | SDT { buf } 23 | } 24 | 25 | #[inline(always)] 26 | pub fn try_new(buf: &'buf [u8]) -> Result> { 27 | let s = Self::new(buf); 28 | s.validate()?; 29 | Ok(s) 30 | } 31 | 32 | #[inline(always)] 33 | pub fn validate(&self) -> Result<()> { 34 | Ok(()) 35 | } 36 | 37 | /// seek 38 | #[inline(always)] 39 | fn buf_streams(&self) -> &'buf [u8] { 40 | let lft = Self::HEADER_FULL_SZ; 41 | let mut rght = HEADER_SZ + (self.section_length() as usize); 42 | 43 | if rght >= self.buf.len() { 44 | rght = self.buf.len(); 45 | } 46 | 47 | rght -= CRC32_SZ; 48 | 49 | &self.buf[lft..rght] 50 | } 51 | 52 | #[inline(always)] 53 | pub fn streams(&self) -> Cursor<'buf, Stream> { 54 | Cursor::new(self.buf_streams()) 55 | } 56 | 57 | #[inline(always)] 58 | pub fn transport_stream_id(&self) -> u16 { 59 | self.table_id_extension() 60 | } 61 | } 62 | 63 | trait WithSDTHeaderSpecific<'buf>: Bufer<'buf> { 64 | /// buffer seeked 65 | #[inline(always)] 66 | fn b(&self) -> &'buf [u8] { 67 | &self.buf()[HEADER_SZ + SYNTAX_SECTION_SZ..] 68 | } 69 | 70 | #[inline(always)] 71 | fn original_network_id(&self) -> u16 { 72 | u16::from(self.b()[0]) | u16::from(self.b()[1]) 73 | } 74 | } 75 | 76 | impl<'buf> Bufer<'buf> for SDT<'buf> { 77 | fn buf(&self) -> &'buf [u8] { 78 | self.buf 79 | } 80 | } 81 | 82 | impl<'buf> WithHeader<'buf> for SDT<'buf> {} 83 | impl<'buf> WithTableIDExtension<'buf> for SDT<'buf> {} 84 | impl<'buf> WithSyntaxSection<'buf> for SDT<'buf> {} 85 | impl<'buf> WithSDTHeaderSpecific<'buf> for SDT<'buf> {} 86 | impl<'buf> WithCRC32<'buf> for SDT<'buf> {} 87 | 88 | impl<'buf> fmt::Debug for SDT<'buf> { 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 90 | write!( 91 | f, 92 | ":SDT (:id {:?} :transport-stream-id {} :section-length {} :section {}/{})", 93 | self.subtable_id(), 94 | self.transport_stream_id(), 95 | self.section_length(), 96 | self.section_number(), 97 | self.last_section_number(), 98 | )?; 99 | 100 | write!(f, "\n :streams")?; 101 | for p in self.streams().filter_map(Result::ok) { 102 | write!(f, "\n ")?; 103 | p.fmt(f)?; 104 | } 105 | 106 | Ok(()) 107 | } 108 | } 109 | 110 | impl<'buf> SubtableIDer for SDT<'buf> { 111 | #[inline(always)] 112 | fn subtable_id(&self) -> SubtableID { 113 | SubtableID::SDT( 114 | self.table_id(), 115 | self.transport_stream_id(), 116 | self.original_network_id(), 117 | self.version_number(), 118 | ) 119 | } 120 | } 121 | 122 | pub struct Stream<'buf> { 123 | buf: &'buf [u8], 124 | } 125 | 126 | impl<'buf> Stream<'buf> { 127 | const HEADER_SZ: usize = 5; 128 | 129 | #[inline(always)] 130 | pub fn new(buf: &'buf [u8]) -> Stream<'buf> { 131 | Stream { buf } 132 | } 133 | 134 | #[inline(always)] 135 | pub fn service_id(&self) -> u16 { 136 | u16::from(self.buf[0]) | u16::from(self.buf[1]) 137 | } 138 | 139 | #[inline(always)] 140 | pub fn eit_schedule_flag(&self) -> bool { 141 | ((self.buf[2] & 0b0000_0010) >> 1) != 0 142 | } 143 | 144 | #[inline(always)] 145 | pub fn eit_present_following_flag(&self) -> bool { 146 | (self.buf[2] & 0b0000_0001) != 0 147 | } 148 | 149 | // TODO: add enum 150 | #[inline(always)] 151 | pub fn running_status(&self) -> u8 { 152 | (self.buf[3] & 0b1110_0000) >> 5 153 | } 154 | 155 | #[inline(always)] 156 | pub fn free_ca_mode(&self) -> bool { 157 | (self.buf[3] & 0b0001_0000) != 0 158 | } 159 | 160 | #[inline(always)] 161 | pub fn descriptors_loop_length(&self) -> u16 { 162 | (u16::from(self.buf[3] & 0b0000_1111) << 8) | u16::from(self.buf[4]) 163 | } 164 | 165 | /// seek 166 | #[inline(always)] 167 | fn buf_descriptors(&self) -> &'buf [u8] { 168 | let lft = Self::HEADER_SZ; 169 | let mut rght = lft + (self.descriptors_loop_length() as usize); 170 | 171 | if rght >= self.buf.len() { 172 | rght = self.buf.len(); 173 | } 174 | 175 | &self.buf[lft..rght] 176 | } 177 | 178 | #[inline(always)] 179 | pub fn descriptors(&self) -> Option> { 180 | if self.descriptors_loop_length() != 0 { 181 | Some(Cursor::new(self.buf_descriptors())) 182 | } else { 183 | None 184 | } 185 | } 186 | } 187 | 188 | impl<'buf> Szer for Stream<'buf> { 189 | #[inline(always)] 190 | fn sz(&self) -> usize { 191 | Self::HEADER_SZ + (self.descriptors_loop_length() as usize) 192 | } 193 | } 194 | 195 | impl<'buf> TryNewer<'buf> for Stream<'buf> { 196 | #[inline(always)] 197 | fn try_new(buf: &'buf [u8]) -> Result> { 198 | let s = Stream::new(buf); 199 | Ok(s) 200 | } 201 | } 202 | 203 | impl<'buf> fmt::Debug for Stream<'buf> { 204 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 205 | write!( 206 | f, 207 | ":stream (:service-id {:?} :running-status {:?})", 208 | self.service_id(), 209 | self.running_status(), 210 | )?; 211 | 212 | write!(f, "\n :descriptors")?; 213 | match self.descriptors() { 214 | Some(descs) => { 215 | for d in descs.filter_map(Result::ok) { 216 | write!(f, "\n ")?; 217 | d.fmt(f)?; 218 | } 219 | } 220 | None => write!(f, " ~")?, 221 | } 222 | 223 | Ok(()) 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/header.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Kind as ErrorKind}; 2 | use crate::pcr::PCR; 3 | use crate::pid::PID; 4 | use crate::result::Result; 5 | 6 | #[derive(Clone, Copy, Debug, PartialEq)] 7 | pub enum TransportScramblingControl { 8 | NotScrambled, 9 | ScrambledReserved, 10 | ScrambledEven, 11 | ScrambledOdd, 12 | } 13 | 14 | impl From for TransportScramblingControl { 15 | #[inline(always)] 16 | fn from(d: u8) -> Self { 17 | match d { 18 | 0b00 => TransportScramblingControl::NotScrambled, 19 | 0b01 => TransportScramblingControl::ScrambledReserved, 20 | 0b10 => TransportScramblingControl::ScrambledEven, 21 | 0b11 => TransportScramblingControl::ScrambledOdd, 22 | 23 | _ => TransportScramblingControl::NotScrambled, 24 | } 25 | } 26 | } 27 | 28 | pub struct Adaptation<'buf> { 29 | buf: &'buf [u8], 30 | } 31 | 32 | impl<'buf> Adaptation<'buf> { 33 | const HEADER_SZ: usize = 1; 34 | const HEADER_FULL_SZ: usize = 2; 35 | 36 | #[inline(always)] 37 | pub fn new(buf: &'buf [u8]) -> Adaptation<'buf> { 38 | Adaptation { buf } 39 | } 40 | 41 | #[inline(always)] 42 | pub fn try_new(buf: &'buf [u8]) -> Result> { 43 | let a = Self::new(buf); 44 | a.validate()?; 45 | Ok(a) 46 | } 47 | 48 | #[inline(always)] 49 | fn validate(&self) -> Result<()> { 50 | if self.buf.len() < Self::HEADER_SZ { 51 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::HEADER_SZ))) 52 | } else if self.buf.len() < self.sz() { 53 | Err(Error::new(ErrorKind::Buf(self.buf.len(), self.sz()))) 54 | } else { 55 | Ok(()) 56 | } 57 | } 58 | 59 | #[inline(always)] 60 | pub fn sz(&self) -> usize { 61 | Self::HEADER_SZ + self.field_length() 62 | } 63 | 64 | #[inline(always)] 65 | fn field_length(&self) -> usize { 66 | self.buf[0] as usize 67 | } 68 | 69 | #[inline(always)] 70 | #[allow(dead_code)] 71 | fn discontinuity_indicator(&self) -> bool { 72 | (self.buf[1] & 0b1000_0000) != 0 73 | } 74 | 75 | #[inline(always)] 76 | #[allow(dead_code)] 77 | fn random_access_indicator(&self) -> bool { 78 | (self.buf[1] & 0b0100_0000) != 0 79 | } 80 | 81 | #[inline(always)] 82 | #[allow(dead_code)] 83 | pub fn elementary_stream_priority_indicator(&self) -> bool { 84 | (self.buf[1] & 0b0010_0000) != 0 85 | } 86 | 87 | /// PCR field is present? 88 | #[inline(always)] 89 | #[allow(dead_code)] 90 | fn pcr_flag(&self) -> bool { 91 | (self.buf[1] & 0b0001_0000) != 0 92 | } 93 | 94 | /// OPCR field is present? 95 | #[inline(always)] 96 | #[allow(dead_code)] 97 | pub fn opcr_flag(&self) -> bool { 98 | (self.buf[1] & 0b0000_1000) != 0 99 | } 100 | 101 | /// splice countdown field is present? 102 | #[inline(always)] 103 | #[allow(dead_code)] 104 | pub fn splicing_point_flag(&self) -> bool { 105 | (self.buf[1] & 0b0000_0100) != 0 106 | } 107 | 108 | /// transport private data is present? 109 | #[inline(always)] 110 | #[allow(dead_code)] 111 | pub fn transport_private_data_flag(&self) -> bool { 112 | (self.buf[1] & 0b0000_0010) != 0 113 | } 114 | 115 | /// transport private data is present? 116 | #[inline(always)] 117 | #[allow(dead_code)] 118 | pub fn adaptation_field_extension_flag(&self) -> bool { 119 | (self.buf[1] & 0b0000_0001) != 0 120 | } 121 | 122 | /// seek to PCR start position 123 | #[inline(always)] 124 | #[allow(dead_code)] 125 | fn buf_seek_pcr(&self) -> &'buf [u8] { 126 | &self.buf[Self::HEADER_FULL_SZ..] 127 | } 128 | 129 | /// seek to OPCR start position 130 | #[inline(always)] 131 | #[allow(dead_code)] 132 | fn buf_seek_opcr(&self) -> &'buf [u8] { 133 | let mut buf = self.buf_seek_pcr(); 134 | if self.pcr_flag() { 135 | buf = &buf[PCR::SZ..]; 136 | } 137 | buf 138 | } 139 | 140 | #[inline(always)] 141 | pub fn pcr(&self) -> Option> { 142 | if self.pcr_flag() { 143 | Some(PCR::new(self.buf_seek_pcr())) 144 | } else { 145 | None 146 | } 147 | } 148 | 149 | #[inline(always)] 150 | #[allow(dead_code)] 151 | pub fn opcr(&self) -> Option { 152 | if self.opcr_flag() { 153 | Some(PCR::new(self.buf_seek_opcr())) 154 | } else { 155 | None 156 | } 157 | } 158 | 159 | #[inline(always)] 160 | #[allow(dead_code)] 161 | pub fn splice_countdown(&self) -> Option { 162 | if self.splicing_point_flag() { 163 | // TODO: implement 164 | unimplemented!() 165 | } else { 166 | None 167 | } 168 | } 169 | } 170 | 171 | pub struct Header<'buf> { 172 | buf: &'buf [u8], 173 | } 174 | 175 | impl<'buf> Header<'buf> { 176 | pub const SZ: usize = 4; 177 | 178 | #[inline(always)] 179 | pub fn new(buf: &'buf [u8]) -> Header<'buf> { 180 | Header { buf } 181 | } 182 | 183 | #[inline(always)] 184 | #[allow(dead_code)] 185 | fn tei(&self) -> bool { 186 | (self.buf[1] & 0b1000_0000) != 0 187 | } 188 | 189 | #[inline(always)] 190 | pub fn pusi(&self) -> bool { 191 | (self.buf[1] & 0b0100_0000) != 0 192 | } 193 | 194 | #[inline(always)] 195 | #[allow(dead_code)] 196 | fn transport_priority(&self) -> bool { 197 | (self.buf[1] & 0b0010_0000) != 0 198 | } 199 | 200 | /// Packet Identifier, describing the payload data. 201 | #[inline(always)] 202 | pub fn pid(&self) -> PID { 203 | PID::from((u16::from(self.buf[1] & 0b0001_1111) << 8) | u16::from(self.buf[2])) 204 | } 205 | 206 | /// transport-scrambling-control 207 | #[inline(always)] 208 | #[allow(dead_code)] 209 | fn tsc(&self) -> TransportScramblingControl { 210 | TransportScramblingControl::from((self.buf[3] & 0b1100_0000) >> 6) 211 | } 212 | 213 | #[inline(always)] 214 | pub fn got_adaptation(&self) -> bool { 215 | (self.buf[3] & 0b0010_0000) != 0 216 | } 217 | 218 | #[inline(always)] 219 | pub fn got_payload(&self) -> bool { 220 | (self.buf[3] & 0b0001_0000) != 0 221 | } 222 | 223 | #[inline(always)] 224 | pub fn cc(&self) -> u8 { 225 | self.buf[3] & 0b0000_1111 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/stream_type.rs: -------------------------------------------------------------------------------- 1 | /// ETSI EN 300 468 V1.15.1 (2016-03) 2 | /// ISO/IEC 13818-1 3 | #[derive(Clone, Debug)] 4 | pub enum StreamType { 5 | MPEG1Video, 6 | H262, 7 | MPEG1Audio, // mp2 8 | MPEG2Audio, // mp2 9 | MPEG2TabledData, 10 | MPEG2PacketizedData, // mp3 11 | MHEG, 12 | DSMCCInAPacketizedStream, 13 | H222AuxiliaryData, 14 | DSMCCMultiprotocolEncapsulation, 15 | DSMCCUNMessages, 16 | DSMCCStreamDescriptors, 17 | DSMCCTabledData, 18 | ISOIEC138181AuxiliaryData, 19 | AAC, // AAC 20 | MPEG4H263Video, 21 | MPEG4LOAS, 22 | MPEG4FlexMux, 23 | MPEG4FlexMuxTables, 24 | DSMCCSynchronizedDownloadProtocol, 25 | PacketizedMetadata, 26 | SectionedMetadata, 27 | DSMCCDataCarouselMetadata, 28 | DSMCCObjectCarouselMetadata, 29 | SynchronizedDownloadProtocolMetadata, 30 | IPMP, 31 | H264, 32 | MPEG4RawAudio, 33 | MPEG4Text, 34 | MPEG4AuxiliaryVideo, 35 | SVC, 36 | MVC, 37 | JPEG2000Video, 38 | 39 | H265, 40 | 41 | ChineseVideoStandard, 42 | 43 | IPMPDRM, 44 | H262DES64CBC, 45 | AC3, // AC3 46 | SCTESubtitle, // SCTE 47 | DolbyTrueHDAudio, 48 | AC3DolbyDigitalPlus, 49 | DTS8, 50 | SCTE35, 51 | AC3DolbyDigitalPlus16, 52 | 53 | // 0x00 54 | // 0x22 55 | // 0x23 56 | // 0x25 57 | // 0x41 58 | // 0x43...0x7E 59 | // 0x88...0x8F 60 | Reserved(u8), 61 | 62 | Other(u8), 63 | } 64 | 65 | impl From for StreamType { 66 | fn from(d: u8) -> Self { 67 | match d { 68 | 0x01 => StreamType::MPEG1Video, 69 | 0x02 => StreamType::H262, 70 | 0x03 => StreamType::MPEG1Audio, // mp2 71 | 0x04 => StreamType::MPEG2Audio, // mp2 72 | 0x05 => StreamType::MPEG2TabledData, 73 | 0x06 => StreamType::MPEG2PacketizedData, // mp3 74 | 0x07 => StreamType::MHEG, 75 | 0x08 => StreamType::DSMCCInAPacketizedStream, 76 | 0x09 => StreamType::H222AuxiliaryData, 77 | 0x0A => StreamType::DSMCCMultiprotocolEncapsulation, 78 | 0x0B => StreamType::DSMCCUNMessages, 79 | 0x0C => StreamType::DSMCCStreamDescriptors, 80 | 0x0D => StreamType::DSMCCTabledData, 81 | 0x0E => StreamType::ISOIEC138181AuxiliaryData, 82 | 0x0F => StreamType::AAC, // AAC 83 | 0x10 => StreamType::MPEG4H263Video, 84 | 0x11 => StreamType::MPEG4LOAS, 85 | 0x12 => StreamType::MPEG4FlexMux, 86 | 0x13 => StreamType::MPEG4FlexMuxTables, 87 | 0x14 => StreamType::DSMCCSynchronizedDownloadProtocol, 88 | 0x15 => StreamType::PacketizedMetadata, 89 | 0x16 => StreamType::SectionedMetadata, 90 | 0x17 => StreamType::DSMCCDataCarouselMetadata, 91 | 0x18 => StreamType::DSMCCObjectCarouselMetadata, 92 | 0x19 => StreamType::SynchronizedDownloadProtocolMetadata, 93 | 0x1A => StreamType::IPMP, 94 | 0x1B => StreamType::H264, 95 | 0x1C => StreamType::MPEG4RawAudio, 96 | 0x1D => StreamType::MPEG4Text, 97 | 0x1E => StreamType::MPEG4AuxiliaryVideo, 98 | 0x1F => StreamType::SVC, 99 | 0x20 => StreamType::MVC, 100 | 0x21 => StreamType::JPEG2000Video, 101 | 102 | 0x24 => StreamType::H265, 103 | 104 | 0x42 => StreamType::ChineseVideoStandard, 105 | 106 | 0x7F => StreamType::IPMPDRM, 107 | 0x80 => StreamType::H262DES64CBC, 108 | 0x81 => StreamType::AC3, // AC3 109 | 0x82 => StreamType::SCTESubtitle, // SCTE 110 | 0x83 => StreamType::DolbyTrueHDAudio, 111 | 0x84 => StreamType::AC3DolbyDigitalPlus, 112 | 0x85 => StreamType::DTS8, 113 | 0x86 => StreamType::SCTE35, 114 | 0x87 => StreamType::AC3DolbyDigitalPlus16, 115 | 116 | 0x00 | 0x22 | 0x23 | 0x25 | 0x41 | 0x43..=0x7E | 0x88..=0x8F => StreamType::Reserved(d), 117 | 118 | _ => StreamType::Other(d), 119 | } 120 | } 121 | } 122 | 123 | impl From for u8 { 124 | fn from(st: StreamType) -> u8 { 125 | match st { 126 | StreamType::MPEG1Video => 0x01, 127 | StreamType::H262 => 0x02, 128 | StreamType::MPEG1Audio => 0x03, 129 | StreamType::MPEG2Audio => 0x04, 130 | StreamType::MPEG2TabledData => 0x05, 131 | StreamType::MPEG2PacketizedData => 0x06, 132 | StreamType::MHEG => 0x07, 133 | StreamType::DSMCCInAPacketizedStream => 0x08, 134 | StreamType::H222AuxiliaryData => 0x09, 135 | StreamType::DSMCCMultiprotocolEncapsulation => 0x0A, 136 | StreamType::DSMCCUNMessages => 0x0B, 137 | StreamType::DSMCCStreamDescriptors => 0x0C, 138 | StreamType::DSMCCTabledData => 0x0D, 139 | StreamType::ISOIEC138181AuxiliaryData => 0x0E, 140 | StreamType::AAC => 0x0F, 141 | StreamType::MPEG4H263Video => 0x10, 142 | StreamType::MPEG4LOAS => 0x11, 143 | StreamType::MPEG4FlexMux => 0x12, 144 | StreamType::MPEG4FlexMuxTables => 0x13, 145 | StreamType::DSMCCSynchronizedDownloadProtocol => 0x14, 146 | StreamType::PacketizedMetadata => 0x15, 147 | StreamType::SectionedMetadata => 0x16, 148 | StreamType::DSMCCDataCarouselMetadata => 0x17, 149 | StreamType::DSMCCObjectCarouselMetadata => 0x18, 150 | StreamType::SynchronizedDownloadProtocolMetadata => 0x19, 151 | StreamType::IPMP => 0x1A, 152 | StreamType::H264 => 0x1B, 153 | StreamType::MPEG4RawAudio => 0x1C, 154 | StreamType::MPEG4Text => 0x1D, 155 | StreamType::MPEG4AuxiliaryVideo => 0x1E, 156 | StreamType::SVC => 0x1F, 157 | StreamType::MVC => 0x20, 158 | StreamType::JPEG2000Video => 0x21, 159 | 160 | StreamType::H265 => 0x24, 161 | 162 | StreamType::ChineseVideoStandard => 0x42, 163 | 164 | StreamType::IPMPDRM => 0x7F, 165 | StreamType::H262DES64CBC => 0x80, 166 | StreamType::AC3 => 0x81, 167 | StreamType::SCTESubtitle => 0x82, 168 | StreamType::DolbyTrueHDAudio => 0x83, 169 | StreamType::AC3DolbyDigitalPlus => 0x84, 170 | StreamType::DTS8 => 0x85, 171 | StreamType::SCTE35 => 0x86, 172 | StreamType::AC3DolbyDigitalPlus16 => 0x87, 173 | 174 | StreamType::Reserved(d) => d, 175 | 176 | StreamType::Other(d) => d, 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/packet.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Kind as ErrorKind}; 2 | use crate::header::{Adaptation, Header}; 3 | use crate::pcr::PCR; 4 | use crate::pid::PID; 5 | use crate::result::Result; 6 | 7 | pub struct Packet<'buf> { 8 | buf: &'buf [u8], 9 | } 10 | 11 | impl<'buf> Packet<'buf> { 12 | pub const SZ: usize = 188; 13 | const SYNC_BYTE: u8 = 0x47; 14 | 15 | #[inline(always)] 16 | pub fn new(buf: &'buf [u8]) -> Result> { 17 | let pkt = Packet { buf }; 18 | 19 | pkt.validate()?; 20 | 21 | Ok(pkt) 22 | } 23 | 24 | #[inline(always)] 25 | fn validate(&self) -> Result<()> { 26 | if self.buf.len() != Self::SZ { 27 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::SZ))) 28 | } else if self.buf[0] != Self::SYNC_BYTE { 29 | Err(Error::new(ErrorKind::SyncByte(self.buf[0]))) 30 | } else { 31 | Ok(()) 32 | } 33 | } 34 | 35 | /// adaptation start position 36 | #[inline(always)] 37 | fn buf_pos_adaptation() -> usize { 38 | Header::SZ 39 | } 40 | 41 | // TODO: try_seek? 42 | // or pos_ + seek? 43 | /// position payload start 44 | #[inline(always)] 45 | fn buf_pos_payload(&self, is_section: bool) -> usize { 46 | let mut pos = Self::buf_pos_adaptation(); 47 | let header = self.header(); 48 | 49 | if header.got_adaptation() { 50 | // TODO: Adaptation::sz(self.buf) 51 | // self.adaptation() + self.try_adaptation() 52 | let adapt = Adaptation::new(self.buf_seek(pos)); 53 | pos += adapt.sz(); 54 | } 55 | 56 | if header.pusi() && is_section { 57 | // payload data start 58 | // 59 | // https://stackoverflow.com/a/27525217 60 | // From the en300 468 spec: 61 | // 62 | // Sections may start at the beginning of the payload of a TS packet, 63 | // but this is not a requirement, because the start of the first 64 | // section in the payload of a TS packet is pointed to by the pointer_field. 65 | // 66 | // So the section start actually is an offset from the payload: 67 | // 68 | // uint8_t* section_start = payload + *payload + 1; 69 | pos += (self.buf[pos] as usize) + 1; 70 | } 71 | 72 | pos 73 | } 74 | 75 | #[inline(always)] 76 | fn buf_seek(&self, offset: usize) -> &'buf [u8] { 77 | &self.buf[offset..] 78 | } 79 | 80 | #[inline(always)] 81 | fn buf_try_seek(&self, offset: usize) -> Result<&'buf [u8]> { 82 | if self.buf.len() <= offset { 83 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::SZ))) 84 | } else { 85 | Ok(self.buf_seek(offset)) 86 | } 87 | } 88 | 89 | #[inline(always)] 90 | fn buf_adaptation(&self) -> Result<&'buf [u8]> { 91 | self.buf_try_seek(Self::buf_pos_adaptation()) 92 | } 93 | 94 | #[inline(always)] 95 | fn buf_payload(&self, is_section: bool) -> Result<&'buf [u8]> { 96 | self.buf_try_seek(self.buf_pos_payload(is_section)) 97 | } 98 | 99 | #[inline(always)] 100 | pub fn buf_payload_section(&self) -> Result<&'buf [u8]> { 101 | self.buf_payload(true) 102 | } 103 | 104 | #[inline(always)] 105 | pub fn buf_payload_pes(&self) -> Result<&'buf [u8]> { 106 | self.buf_payload(false) 107 | } 108 | 109 | // TODO: merge Header and Packet? 110 | #[inline(always)] 111 | fn header(&self) -> Header<'buf> { 112 | Header::new(self.buf) 113 | } 114 | 115 | #[inline(always)] 116 | fn adaptation(&self) -> Option>> { 117 | let header = self.header(); 118 | 119 | if header.got_adaptation() { 120 | // TODO: move to macro? or optional-result crate 121 | match self.buf_adaptation() { 122 | Ok(buf) => Some(Adaptation::try_new(buf)), 123 | Err(e) => Some(Err(e)), 124 | } 125 | } else { 126 | None 127 | } 128 | } 129 | 130 | #[inline(always)] 131 | pub fn pid(&self) -> PID { 132 | self.header().pid() 133 | } 134 | 135 | #[inline(always)] 136 | pub fn cc(&self) -> u8 { 137 | self.header().cc() 138 | } 139 | 140 | #[inline(always)] 141 | pub fn pusi(&self) -> bool { 142 | self.header().pusi() 143 | } 144 | 145 | #[inline(always)] 146 | pub fn pcr(&self) -> Result>> { 147 | self.adaptation() 148 | .and_then(|res| match res { 149 | Ok(adapt) => adapt.pcr().map(Ok), 150 | Err(e) => Some(Err(e)), 151 | }) 152 | .transpose() 153 | } 154 | 155 | // TODO: generic pmt, pat method 156 | #[inline(always)] 157 | pub fn pat(&self) -> Result> { 158 | let header = self.header(); 159 | 160 | if !header.got_payload() { 161 | return Ok(None); 162 | } 163 | 164 | let res = if self.pid() == PID::PAT { 165 | // TODO: move to macro? or optional-result crate 166 | match self.buf_payload_section() { 167 | Ok(buf) => Some(Ok(buf)), 168 | Err(e) => Some(Err(e)), 169 | } 170 | } else { 171 | None 172 | }; 173 | 174 | res.transpose() 175 | } 176 | 177 | // TODO: refactoring 178 | #[inline(always)] 179 | pub fn pmt(&self, pid: u16) -> Result> { 180 | let header = self.header(); 181 | 182 | if !header.got_payload() { 183 | return Ok(None); 184 | } 185 | 186 | let res = if u16::from(self.pid()) == pid { 187 | // TODO: move to macro? or optional-result crate 188 | match self.buf_payload_section() { 189 | Ok(buf) => Some(Ok(buf)), 190 | Err(e) => Some(Err(e)), 191 | } 192 | } else { 193 | None 194 | }; 195 | 196 | res.transpose() 197 | } 198 | 199 | // TODO: refactoring 200 | #[inline(always)] 201 | pub fn eit(&self) -> Result> { 202 | let header = self.header(); 203 | 204 | if !header.got_payload() { 205 | return Ok(None); 206 | } 207 | 208 | let res = if self.pid() == PID::EIT { 209 | // TODO: move to macro? or optional-result crate 210 | match self.buf_payload_section() { 211 | Ok(buf) => Some(Ok(buf)), 212 | Err(e) => Some(Err(e)), 213 | } 214 | } else { 215 | None 216 | }; 217 | 218 | res.transpose() 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/section/pmt.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::descriptor::Descriptor; 4 | use crate::result::Result; 5 | use crate::stream_type::StreamType; 6 | use crate::subtable_id::{SubtableID, SubtableIDer}; 7 | 8 | use super::traits::*; 9 | 10 | /// ISO/IEC 13818-1 11 | /// 12 | /// Program Map Table 13 | pub struct PMT<'buf> { 14 | buf: &'buf [u8], 15 | } 16 | 17 | impl<'buf> PMT<'buf> { 18 | const HEADER_SPECIFIC_SZ: usize = 4; 19 | const HEADER_FULL_SZ: usize = HEADER_SZ + SYNTAX_SECTION_SZ + Self::HEADER_SPECIFIC_SZ; 20 | 21 | #[inline(always)] 22 | pub fn new(buf: &'buf [u8]) -> PMT<'buf> { 23 | PMT { buf } 24 | } 25 | 26 | #[inline(always)] 27 | pub fn try_new(buf: &'buf [u8]) -> Result> { 28 | let s = Self::new(buf); 29 | s.validate()?; 30 | Ok(s) 31 | } 32 | 33 | #[inline(always)] 34 | pub fn validate(&self) -> Result<()> { 35 | Ok(()) 36 | } 37 | 38 | /// seek 39 | #[inline(always)] 40 | fn buf_streams(&self) -> &'buf [u8] { 41 | let lft = Self::HEADER_FULL_SZ + (self.program_info_length() as usize); 42 | let mut rght = HEADER_SZ + (self.section_length() as usize); 43 | 44 | if rght >= self.buf.len() { 45 | rght = self.buf.len(); 46 | } 47 | 48 | rght -= CRC32_SZ; 49 | 50 | &self.buf[lft..rght] 51 | } 52 | 53 | /// seek 54 | #[inline(always)] 55 | fn buf_descriptors(&self) -> &'buf [u8] { 56 | let lft = Self::HEADER_FULL_SZ; 57 | let rght = Self::HEADER_FULL_SZ + (self.program_info_length() as usize); 58 | 59 | &self.buf[lft..rght] 60 | } 61 | 62 | #[inline(always)] 63 | pub fn descriptors(&self) -> Option> { 64 | if self.program_info_length() != 0 { 65 | Some(Cursor::new(self.buf_descriptors())) 66 | } else { 67 | None 68 | } 69 | } 70 | 71 | #[inline(always)] 72 | pub fn streams(&self) -> Cursor<'buf, Stream> { 73 | Cursor::new(self.buf_streams()) 74 | } 75 | 76 | #[inline(always)] 77 | pub fn program_number(&self) -> u16 { 78 | self.table_id_extension() 79 | } 80 | } 81 | 82 | trait WithPMTHeaderSpecific<'buf>: Bufer<'buf> { 83 | /// buffer seeked 84 | #[inline(always)] 85 | fn b(&self) -> &'buf [u8] { 86 | &self.buf()[HEADER_SZ + SYNTAX_SECTION_SZ..] 87 | } 88 | 89 | #[inline(always)] 90 | fn pcr_pid(&self) -> u16 { 91 | u16::from(self.b()[0] & 0b0001_1111) | u16::from(self.b()[1]) 92 | } 93 | 94 | #[inline(always)] 95 | fn program_info_length(&self) -> u16 { 96 | u16::from(self.b()[2] & 0b0000_1111) | u16::from(self.b()[3]) 97 | } 98 | } 99 | 100 | impl<'buf> Bufer<'buf> for PMT<'buf> { 101 | fn buf(&self) -> &'buf [u8] { 102 | self.buf 103 | } 104 | } 105 | 106 | impl<'buf> WithHeader<'buf> for PMT<'buf> {} 107 | impl<'buf> WithTableIDExtension<'buf> for PMT<'buf> {} 108 | impl<'buf> WithSyntaxSection<'buf> for PMT<'buf> {} 109 | impl<'buf> WithPMTHeaderSpecific<'buf> for PMT<'buf> {} 110 | impl<'buf> WithCRC32<'buf> for PMT<'buf> {} 111 | 112 | impl<'buf> fmt::Debug for PMT<'buf> { 113 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 114 | write!( 115 | f, 116 | ":PMT (:id {:?} :pcr-pid {})", 117 | self.subtable_id(), 118 | self.pcr_pid(), 119 | )?; 120 | 121 | write!(f, "\n :descriptors")?; 122 | match self.descriptors() { 123 | Some(descs) => { 124 | for d in descs.filter_map(Result::ok) { 125 | write!(f, "\n ")?; 126 | d.fmt(f)?; 127 | } 128 | } 129 | None => write!(f, " ~")?, 130 | } 131 | 132 | write!(f, "\n :streams")?; 133 | for s in self.streams().filter_map(Result::ok) { 134 | write!(f, "\n ")?; 135 | s.fmt(f)?; 136 | } 137 | 138 | Ok(()) 139 | } 140 | } 141 | 142 | impl<'buf> SubtableIDer for PMT<'buf> { 143 | #[inline(always)] 144 | fn subtable_id(&self) -> SubtableID { 145 | SubtableID::PMT( 146 | self.table_id(), 147 | self.program_number(), 148 | self.version_number(), 149 | ) 150 | } 151 | } 152 | 153 | pub struct Stream<'buf> { 154 | buf: &'buf [u8], 155 | } 156 | 157 | impl<'buf> Stream<'buf> { 158 | const HEADER_SZ: usize = 5; 159 | 160 | #[inline(always)] 161 | pub fn new(buf: &'buf [u8]) -> Stream<'buf> { 162 | Stream { buf } 163 | } 164 | 165 | #[inline(always)] 166 | fn stream_type(&self) -> StreamType { 167 | StreamType::from(self.buf[0]) 168 | } 169 | 170 | #[inline(always)] 171 | pub fn pid(&self) -> u16 { 172 | (u16::from(self.buf[1] & 0b0001_1111) << 8) | u16::from(self.buf[2]) 173 | } 174 | 175 | #[inline(always)] 176 | fn es_info_length(&self) -> u16 { 177 | (u16::from(self.buf[3] & 0b0000_1111) << 8) | u16::from(self.buf[4]) 178 | } 179 | 180 | /// seek 181 | #[inline(always)] 182 | fn buf_descriptors(&self) -> &'buf [u8] { 183 | let lft = Self::HEADER_SZ; 184 | let mut rght = lft + (self.es_info_length() as usize); 185 | 186 | if rght >= self.buf.len() { 187 | rght = self.buf.len(); 188 | } 189 | 190 | &self.buf[lft..rght] 191 | } 192 | 193 | #[inline(always)] 194 | pub fn descriptors(&self) -> Option> { 195 | if self.es_info_length() != 0 { 196 | Some(Cursor::new(self.buf_descriptors())) 197 | } else { 198 | None 199 | } 200 | } 201 | } 202 | 203 | impl<'buf> Szer for Stream<'buf> { 204 | #[inline(always)] 205 | fn sz(&self) -> usize { 206 | Self::HEADER_SZ + (self.es_info_length() as usize) 207 | } 208 | } 209 | 210 | impl<'buf> TryNewer<'buf> for Stream<'buf> { 211 | #[inline(always)] 212 | fn try_new(buf: &'buf [u8]) -> Result> { 213 | let p = Stream::new(buf); 214 | Ok(p) 215 | } 216 | } 217 | 218 | impl<'buf> fmt::Debug for Stream<'buf> { 219 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 220 | write!( 221 | f, 222 | ":stream (:pid {:?} :stream-type {:?})", 223 | self.pid(), 224 | self.stream_type() 225 | )?; 226 | 227 | write!(f, "\n :descriptors")?; 228 | match self.descriptors() { 229 | Some(descs) => { 230 | for d in descs.filter_map(Result::ok) { 231 | write!(f, "\n ")?; 232 | d.fmt(f)?; 233 | } 234 | } 235 | None => write!(f, " ~")?, 236 | } 237 | 238 | Ok(()) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/section/eit.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::time::Duration; 3 | 4 | use chrono::prelude::*; 5 | 6 | use crate::annex_c; 7 | use crate::descriptor::Descriptor; 8 | use crate::duration_fmt::DurationFmt; 9 | use crate::error::{Error, Kind as ErrorKind}; 10 | use crate::result::Result; 11 | use crate::subtable_id::{SubtableID, SubtableIDer}; 12 | 13 | use super::traits::*; 14 | 15 | /// ETSI EN 300 468 V1.15.1 16 | /// 17 | /// Event Information Table 18 | pub struct EIT<'buf> { 19 | buf: &'buf [u8], 20 | } 21 | 22 | impl<'buf> EIT<'buf> { 23 | const HEADER_SPECIFIC_SZ: usize = 6; 24 | const HEADER_FULL_SZ: usize = HEADER_SZ + SYNTAX_SECTION_SZ + Self::HEADER_SPECIFIC_SZ; 25 | 26 | #[inline(always)] 27 | pub fn new(buf: &'buf [u8]) -> EIT<'buf> { 28 | EIT { buf } 29 | } 30 | 31 | #[inline(always)] 32 | pub fn try_new(buf: &'buf [u8]) -> Result> { 33 | let s = Self::new(buf); 34 | s.validate()?; 35 | Ok(s) 36 | } 37 | 38 | #[inline(always)] 39 | pub fn validate(&self) -> Result<()> { 40 | Ok(()) 41 | } 42 | 43 | /// seek 44 | #[inline(always)] 45 | fn buf_events(&self) -> &'buf [u8] { 46 | let lft = Self::HEADER_FULL_SZ; 47 | let mut rght = HEADER_SZ + (self.section_length() as usize); 48 | 49 | if rght >= self.buf.len() { 50 | rght = self.buf.len(); 51 | } 52 | 53 | rght -= CRC32_SZ; 54 | 55 | &self.buf[lft..rght] 56 | } 57 | 58 | #[inline(always)] 59 | pub fn events(&self) -> Cursor<'buf, Event> { 60 | Cursor::new(self.buf_events()) 61 | } 62 | 63 | #[inline(always)] 64 | pub fn service_id(&self) -> u16 { 65 | self.table_id_extension() 66 | } 67 | } 68 | 69 | trait WithEITHeaderSpecific<'buf>: Bufer<'buf> { 70 | /// buffer seeked 71 | #[inline(always)] 72 | fn b(&self) -> &'buf [u8] { 73 | &self.buf()[HEADER_SZ + SYNTAX_SECTION_SZ..] 74 | } 75 | 76 | #[inline(always)] 77 | fn transport_stream_id(&self) -> u16 { 78 | u16::from(self.b()[0]) | u16::from(self.b()[1]) 79 | } 80 | 81 | #[inline(always)] 82 | fn original_network_id(&self) -> u16 { 83 | u16::from(self.b()[2]) | u16::from(self.b()[3]) 84 | } 85 | 86 | #[inline(always)] 87 | fn segment_last_section_number(&self) -> u8 { 88 | self.b()[4] 89 | } 90 | 91 | #[inline(always)] 92 | fn last_table_id(&self) -> u8 { 93 | self.b()[5] 94 | } 95 | } 96 | 97 | impl<'buf> Bufer<'buf> for EIT<'buf> { 98 | fn buf(&self) -> &'buf [u8] { 99 | self.buf 100 | } 101 | } 102 | 103 | impl<'buf> WithHeader<'buf> for EIT<'buf> {} 104 | impl<'buf> WithTableIDExtension<'buf> for EIT<'buf> {} 105 | impl<'buf> WithSyntaxSection<'buf> for EIT<'buf> {} 106 | impl<'buf> WithEITHeaderSpecific<'buf> for EIT<'buf> {} 107 | impl<'buf> WithCRC32<'buf> for EIT<'buf> {} 108 | 109 | impl<'buf> fmt::Debug for EIT<'buf> { 110 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 111 | write!( 112 | f, 113 | ":EIT (:id {:?} :section-length {:3} :section {}/{})", 114 | self.subtable_id(), 115 | self.section_length(), 116 | self.section_number(), 117 | self.last_section_number(), 118 | )?; 119 | 120 | write!(f, "\n :events")?; 121 | for rese in self.events() { 122 | write!(f, "\n ")?; 123 | match rese { 124 | Ok(e) => { 125 | e.fmt(f)?; 126 | } 127 | Err(err) => { 128 | write!(f, "error parse EIT event: {}", err)?; 129 | } 130 | } 131 | } 132 | 133 | Ok(()) 134 | } 135 | } 136 | 137 | impl<'buf> SubtableIDer for EIT<'buf> { 138 | #[inline(always)] 139 | fn subtable_id(&self) -> SubtableID { 140 | SubtableID::EIT( 141 | self.table_id(), 142 | self.service_id(), 143 | self.transport_stream_id(), 144 | self.original_network_id(), 145 | self.version_number(), 146 | ) 147 | } 148 | } 149 | 150 | pub struct Event<'buf> { 151 | buf: &'buf [u8], 152 | } 153 | 154 | impl<'buf> Event<'buf> { 155 | const HEADER_SZ: usize = 12; 156 | 157 | #[inline(always)] 158 | pub fn new(buf: &'buf [u8]) -> Event<'buf> { 159 | Event { buf } 160 | } 161 | 162 | #[inline(always)] 163 | pub fn validate(&self) -> Result<()> { 164 | if self.buf.len() < Self::HEADER_SZ { 165 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::HEADER_SZ))) 166 | } else if self.buf.len() < self.sz() { 167 | Err(Error::new(ErrorKind::Buf(self.buf.len(), self.sz()))) 168 | } else { 169 | Ok(()) 170 | } 171 | } 172 | 173 | #[inline(always)] 174 | pub fn event_id(&self) -> u16 { 175 | (u16::from(self.buf[0]) << 8) | u16::from(self.buf[1]) 176 | } 177 | 178 | #[inline(always)] 179 | pub fn start_time(&self) -> DateTime { 180 | // must 181 | annex_c::from_bytes_into_date_time_utc(&self.buf[2..7]).unwrap() 182 | } 183 | 184 | #[inline(always)] 185 | pub fn duration(&self) -> Duration { 186 | // must 187 | annex_c::from_bytes_into_duration(&self.buf[7..10]).unwrap() 188 | } 189 | 190 | /// seek 191 | #[inline(always)] 192 | fn buf_descriptors(&self) -> &'buf [u8] { 193 | let lft = Self::HEADER_SZ; 194 | let mut rght = lft + (self.descriptors_loop_length() as usize); 195 | 196 | if rght >= self.buf.len() { 197 | rght = self.buf.len(); 198 | } 199 | 200 | &self.buf[lft..rght] 201 | } 202 | 203 | #[inline(always)] 204 | pub fn descriptors(&self) -> Option> { 205 | if self.descriptors_loop_length() != 0 { 206 | Some(Cursor::new(self.buf_descriptors())) 207 | } else { 208 | None 209 | } 210 | } 211 | 212 | #[inline(always)] 213 | pub fn descriptors_loop_length(&self) -> u16 { 214 | (u16::from(self.buf[10] & 0b0000_1111) << 8) | u16::from(self.buf[11]) 215 | } 216 | } 217 | 218 | impl<'buf> Szer for Event<'buf> { 219 | #[inline(always)] 220 | fn sz(&self) -> usize { 221 | Self::HEADER_SZ + (self.descriptors_loop_length() as usize) 222 | } 223 | } 224 | 225 | impl<'buf> TryNewer<'buf> for Event<'buf> { 226 | #[inline(always)] 227 | fn try_new(buf: &'buf [u8]) -> Result> { 228 | let s = Event::new(buf); 229 | s.validate()?; 230 | Ok(s) 231 | } 232 | } 233 | 234 | impl<'buf> fmt::Debug for Event<'buf> { 235 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 236 | write!( 237 | f, 238 | ":event (:start-time {} :duration {})", 239 | self.start_time(), 240 | DurationFmt::from(self.duration()), 241 | )?; 242 | 243 | write!(f, "\n :descriptors")?; 244 | match self.descriptors() { 245 | Some(descs) => { 246 | for resd in descs { 247 | write!(f, "\n ")?; 248 | match resd { 249 | Ok(d) => { 250 | d.fmt(f)?; 251 | } 252 | Err(err) => { 253 | write!(f, "error parse descriptor: {}", err)?; 254 | } 255 | } 256 | } 257 | } 258 | None => write!(f, " ~")?, 259 | } 260 | 261 | Ok(()) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/annex_a2.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Kind as ErrorKind}; 2 | use std::convert::TryFrom; 3 | 4 | #[derive(Clone, Copy, Debug)] 5 | pub enum TableA3 { 6 | IsoIec8859_5, 7 | IsoIec8859_6, 8 | IsoIec8859_7, 9 | IsoIec8859_8, 10 | IsoIec8859_9, 11 | IsoIec8859_10, 12 | IsoIec8859_11, 13 | IsoIec8859_13, 14 | IsoIec8859_14, 15 | IsoIec8859_15, 16 | 17 | IsoIec10646, 18 | KSX10012004, 19 | Gb2312_1980, 20 | Big5subsetOfIsoIec10646, 21 | Utf8encodingOfIsoIec10646, 22 | DescribedByEncodingTypeId, 23 | 24 | Reserved(u8), 25 | } 26 | 27 | impl TableA3 { 28 | pub fn encoding(self) -> Option<&'static encoding_rs::Encoding> { 29 | match self { 30 | TableA3::IsoIec8859_5 => Some(encoding_rs::ISO_8859_5), 31 | TableA3::IsoIec8859_6 => Some(encoding_rs::ISO_8859_6), 32 | TableA3::IsoIec8859_7 => Some(encoding_rs::ISO_8859_7), 33 | TableA3::IsoIec8859_8 => Some(encoding_rs::ISO_8859_8), 34 | TableA3::IsoIec8859_13 => Some(encoding_rs::ISO_8859_13), 35 | TableA3::IsoIec8859_14 => Some(encoding_rs::ISO_8859_14), 36 | TableA3::IsoIec8859_15 => Some(encoding_rs::ISO_8859_15), 37 | TableA3::Big5subsetOfIsoIec10646 => Some(encoding_rs::BIG5), 38 | TableA3::Gb2312_1980 => Some(encoding_rs::GBK), 39 | TableA3::Utf8encodingOfIsoIec10646 => Some(encoding_rs::UTF_8), 40 | _ => None, 41 | } 42 | } 43 | } 44 | 45 | impl TryFrom for TableA3 { 46 | type Error = Error; 47 | 48 | fn try_from(d: u8) -> Result { 49 | if d > 0x1F { 50 | return Err(Error::new(ErrorKind::AnnexA2TableA3Unexpected(d))); 51 | } 52 | 53 | Ok(match d { 54 | 0x01 => TableA3::IsoIec8859_5, 55 | 0x02 => TableA3::IsoIec8859_6, 56 | 0x03 => TableA3::IsoIec8859_7, 57 | 0x04 => TableA3::IsoIec8859_8, 58 | 0x05 => TableA3::IsoIec8859_9, 59 | 0x06 => TableA3::IsoIec8859_10, 60 | 0x07 => TableA3::IsoIec8859_11, 61 | 62 | 0x08 => TableA3::Reserved(d), 63 | 64 | 0x09 => TableA3::IsoIec8859_13, 65 | 0x0A => TableA3::IsoIec8859_14, 66 | 0x0B => TableA3::IsoIec8859_15, 67 | 68 | 0x0C..=0x0F => TableA3::Reserved(d), 69 | 70 | 0x11 => TableA3::IsoIec10646, 71 | 0x12 => TableA3::KSX10012004, 72 | 0x13 => TableA3::Gb2312_1980, 73 | 0x14 => TableA3::Big5subsetOfIsoIec10646, 74 | 0x15 => TableA3::Utf8encodingOfIsoIec10646, 75 | 76 | 0x16..=0x1E => TableA3::Reserved(d), 77 | 78 | 0x1F => TableA3::DescribedByEncodingTypeId, 79 | 80 | _ => panic!("(annex-a2 table-a3 parse) unexpected value;"), 81 | }) 82 | } 83 | } 84 | 85 | #[derive(Clone, Copy, Debug)] 86 | pub enum TableA4 { 87 | IsoIec8859_1, 88 | IsoIec8859_2, 89 | IsoIec8859_3, 90 | IsoIec8859_4, 91 | IsoIec8859_5, 92 | IsoIec8859_6, 93 | IsoIec8859_7, 94 | IsoIec8859_8, 95 | IsoIec8859_9, 96 | IsoIec8859_10, 97 | IsoIec8859_11, 98 | IsoIec8859_13, 99 | IsoIec8859_14, 100 | IsoIec8859_15, 101 | 102 | Reserved(u8, u8), 103 | } 104 | 105 | impl TableA4 { 106 | const SYNC_BYTE: u8 = 0x10; 107 | } 108 | 109 | impl TableA4 { 110 | pub fn encoding(self) -> Option<&'static encoding_rs::Encoding> { 111 | match self { 112 | TableA4::IsoIec8859_1 => Some(encoding_rs::UTF_8), 113 | TableA4::IsoIec8859_2 => Some(encoding_rs::ISO_8859_2), 114 | TableA4::IsoIec8859_3 => Some(encoding_rs::ISO_8859_3), 115 | TableA4::IsoIec8859_4 => Some(encoding_rs::ISO_8859_4), 116 | TableA4::IsoIec8859_5 => Some(encoding_rs::ISO_8859_5), 117 | TableA4::IsoIec8859_6 => Some(encoding_rs::ISO_8859_6), 118 | TableA4::IsoIec8859_7 => Some(encoding_rs::ISO_8859_7), 119 | TableA4::IsoIec8859_8 => Some(encoding_rs::ISO_8859_8), 120 | TableA4::IsoIec8859_10 => Some(encoding_rs::ISO_8859_10), 121 | TableA4::IsoIec8859_13 => Some(encoding_rs::ISO_8859_13), 122 | TableA4::IsoIec8859_14 => Some(encoding_rs::ISO_8859_14), 123 | TableA4::IsoIec8859_15 => Some(encoding_rs::ISO_8859_15), 124 | _ => None, 125 | } 126 | } 127 | } 128 | 129 | impl<'buf> TryFrom<&'buf [u8]> for TableA4 { 130 | type Error = Error; 131 | 132 | fn try_from(buf: &'buf [u8]) -> Result { 133 | if buf.len() < 3 { 134 | return Err(Error::new(ErrorKind::AnnexA2TableA4Buf(buf.len(), 3))); 135 | } 136 | 137 | let (b1, b2, b3) = (buf[0], buf[1], buf[2]); 138 | 139 | if b1 != Self::SYNC_BYTE { 140 | return Err(Error::new(ErrorKind::AnnexA2TableA4Unexpected(b1))); 141 | } 142 | 143 | Ok(match (b2, b3) { 144 | (0x00, 0x01) => TableA4::IsoIec8859_1, 145 | (0x00, 0x02) => TableA4::IsoIec8859_2, 146 | (0x00, 0x03) => TableA4::IsoIec8859_3, 147 | (0x00, 0x04) => TableA4::IsoIec8859_4, 148 | (0x00, 0x05) => TableA4::IsoIec8859_5, 149 | (0x00, 0x06) => TableA4::IsoIec8859_6, 150 | (0x00, 0x07) => TableA4::IsoIec8859_7, 151 | (0x00, 0x08) => TableA4::IsoIec8859_8, 152 | (0x00, 0x09) => TableA4::IsoIec8859_9, 153 | (0x00, 0x0A) => TableA4::IsoIec8859_10, 154 | (0x00, 0x0B) => TableA4::IsoIec8859_11, 155 | (0x00, 0x0D) => TableA4::IsoIec8859_13, 156 | (0x00, 0x0E) => TableA4::IsoIec8859_14, 157 | (0x00, 0x0F) => TableA4::IsoIec8859_15, 158 | 159 | (0x00, 0x00) => TableA4::Reserved(b1, b2), 160 | (0x00, 0x0C) => TableA4::Reserved(b1, b2), 161 | (0x00, 0x10..=0xFF) => TableA4::Reserved(b1, b2), 162 | (0x01..=0xFF, 0x00..=0xFF) => TableA4::Reserved(b1, b2), 163 | }) 164 | } 165 | } 166 | 167 | #[derive(Clone, Copy, Debug)] 168 | pub enum AnnexA2 { 169 | A3(TableA3), 170 | A4(TableA4), 171 | 172 | Reserved(u8), 173 | Zero, 174 | 175 | Default, 176 | } 177 | 178 | /// ETSI EN 300 468 V1.15.1 179 | impl AnnexA2 { 180 | fn encoding(self) -> Option<&'static encoding_rs::Encoding> { 181 | match self { 182 | AnnexA2::A3(a3) => a3.encoding(), 183 | AnnexA2::A4(a4) => a4.encoding(), 184 | AnnexA2::Default => Some(encoding_rs::UTF_8), 185 | AnnexA2::Reserved(..) => None, 186 | AnnexA2::Zero => None, 187 | } 188 | } 189 | 190 | // TODO: maybe use "encoding" (rust-encoding) crate? 191 | pub fn decode<'buf>(src_buf: &'buf [u8], dst_str: &'buf mut str) -> Result { 192 | let a2 = AnnexA2::try_from(src_buf)?; 193 | 194 | let src_buf = &src_buf[a2.sz()..]; 195 | 196 | let encoding = match a2.encoding() { 197 | Some(encoding) => encoding, 198 | None => return Err(Error::new(ErrorKind::AnnexA2UnsupportedEncoding)), 199 | }; 200 | 201 | let mut decoder = encoding.new_decoder(); 202 | 203 | let (result, _, _, had_errors) = decoder.decode_to_str(src_buf, dst_str, false); 204 | 205 | match result { 206 | encoding_rs::CoderResult::InputEmpty => { 207 | // We have consumed the current input buffer 208 | } 209 | encoding_rs::CoderResult::OutputFull => {} 210 | } 211 | 212 | if had_errors { 213 | Err(Error::new(ErrorKind::AnnexA2Decode)) 214 | } else { 215 | Ok(a2) 216 | } 217 | } 218 | 219 | // sz to skip in buffer 220 | fn sz(self) -> usize { 221 | match self { 222 | AnnexA2::A3(..) => 1, 223 | AnnexA2::A4(..) => 3, 224 | AnnexA2::Default => 0, 225 | _ => 0, 226 | } 227 | } 228 | } 229 | 230 | impl<'buf> TryFrom<&'buf [u8]> for AnnexA2 { 231 | type Error = Error; 232 | 233 | fn try_from(buf: &'buf [u8]) -> Result { 234 | if buf.is_empty() { 235 | return Err(Error::new(ErrorKind::AnnexA2EmptyBuf)); 236 | } 237 | 238 | let v = match buf[0] { 239 | 0x00 => AnnexA2::Zero, 240 | 241 | 0x20..=0xFF => AnnexA2::Default, 242 | 243 | 0x01..=0x07 | 0x09..=0x0B | 0x11..=0x15 | 0x1F => { 244 | AnnexA2::A3(TableA3::try_from(buf[0])?) 245 | } 246 | 247 | 0x10 => AnnexA2::A4(TableA4::try_from(buf)?), 248 | 249 | 0x08 | 0x0C..=0x0F | 0x16..=0x1E => AnnexA2::Reserved(buf[0]), 250 | }; 251 | 252 | Ok(v) 253 | } 254 | } 255 | 256 | #[cfg(test)] 257 | mod tests { 258 | #[test] 259 | fn decode() {} 260 | } 261 | -------------------------------------------------------------------------------- /examples/probe/main.rs: -------------------------------------------------------------------------------- 1 | extern crate va_ts as ts; 2 | 3 | mod error; 4 | 5 | use std::collections::HashSet; 6 | use std::collections::VecDeque; 7 | use std::fmt; 8 | use std::net::{Ipv4Addr, UdpSocket}; 9 | use std::process; 10 | use std::sync::{Arc, Condvar, Mutex}; 11 | use std::thread; 12 | use std::time::Duration; 13 | 14 | use clap::{App, Arg}; 15 | use url::{Host, Url}; 16 | 17 | use error::{Error, Kind as ErrorKind, Result}; 18 | 19 | trait Input { 20 | fn open(&mut self) -> Result<()>; 21 | fn read(&mut self) -> Result<()>; 22 | fn close(&mut self) -> Result<()>; 23 | } 24 | 25 | struct DemuxerTSEvents { 26 | done_once: HashSet, 27 | } 28 | 29 | impl Default for DemuxerTSEvents { 30 | fn default() -> Self { 31 | DemuxerTSEvents { 32 | done_once: Default::default(), 33 | } 34 | } 35 | } 36 | 37 | struct EITFmt<'t>(&'t ts::DemuxedTable); 38 | 39 | impl<'t> fmt::Display for EITFmt<'t> { 40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 | for section_ref in self.0.sections.0.iter() { 42 | let section = (*section_ref).borrow(); 43 | let raw = section.buf.0.get_ref().as_slice(); 44 | 45 | let eit = ts::EIT::new(raw); 46 | 47 | for event in eit.events().filter_map(ts::Result::ok) { 48 | write!( 49 | f, 50 | " {} ~ {}\n", 51 | event.start_time(), 52 | ts::DurationFmt::from(event.duration()), 53 | )?; 54 | 55 | if let Some(descs) = event.descriptors() { 56 | for desc in descs 57 | .filter_map(ts::Result::ok) 58 | .filter(|d| d.is_dvb_short_event()) 59 | { 60 | match desc.tag() { 61 | ts::Tag::DVB(ts::TagDVB::ShortEvent) => { 62 | let desc = ts::DescDVB0x4D::new(desc.buf_data()); 63 | 64 | let mut dst_buf = [0u8; 256]; 65 | let mut dst_str = std::str::from_utf8_mut(&mut dst_buf).unwrap(); 66 | 67 | match ts::AnnexA2::decode(desc.event_name(), &mut dst_str) { 68 | Ok(..) => write!(f, r#" "{}""#, dst_str), 69 | Err(err) => write!(f, " (error: {:?})", err), 70 | }?; 71 | 72 | dst_buf = [0u8; 256]; 73 | dst_str = std::str::from_utf8_mut(&mut dst_buf).unwrap(); 74 | 75 | match ts::AnnexA2::decode(desc.text(), &mut dst_str) { 76 | Ok(..) => write!(f, r#" "{}""#, dst_str), 77 | Err(err) => write!(f, " (error: {})", err), 78 | }?; 79 | 80 | write!(f, "\n")?; 81 | } 82 | _ => {} 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | Ok(()) 90 | } 91 | } 92 | 93 | impl ts::DemuxerEvents for DemuxerTSEvents { 94 | fn on_table(&mut self, id: ts::SubtableID, tbl: &ts::DemuxedTable) { 95 | if self.done_once.contains(&id) { 96 | return; 97 | } else { 98 | self.done_once.insert(id); 99 | } 100 | 101 | match id { 102 | ts::SubtableID::EIT(..) => { 103 | print!(":EIT\n{}", EITFmt(tbl)); 104 | } 105 | _ => { 106 | for section_ref in tbl.sections.0.iter() { 107 | let section = (*section_ref).borrow(); 108 | let raw = section.buf.0.get_ref().as_slice(); 109 | 110 | match id { 111 | ts::SubtableID::PAT(..) => { 112 | println!("{:?}", ts::PAT::new(raw)); 113 | } 114 | ts::SubtableID::SDT(..) => { 115 | println!("{:?}", ts::SDT::new(raw)); 116 | } 117 | ts::SubtableID::PMT(..) => { 118 | println!("{:?}", ts::PMT::new(raw)); 119 | } 120 | ts::SubtableID::EIT(..) => { 121 | println!("{:?}", ts::EIT::new(raw)); 122 | } 123 | }; 124 | } 125 | } 126 | } 127 | } 128 | 129 | fn on_packet(&mut self, pkt: &ts::DemuxedPacket) { 130 | println!( 131 | "(0x{:016X}) :pid {:?} :pts {:?} :dts {:?} :sz {}", 132 | pkt.offset, 133 | pkt.pid, 134 | pkt.pts.map(ts::DurationFmt::from), 135 | pkt.dts.map(ts::DurationFmt::from), 136 | pkt.buf.sz(), 137 | ); 138 | } 139 | } 140 | 141 | struct InputUDP { 142 | url: Url, 143 | 144 | // circullar-buffer / fifo 145 | buf: Arc<(Mutex>, Condvar)>, 146 | 147 | demuxer: ts::Demuxer, 148 | } 149 | 150 | impl InputUDP { 151 | pub fn new(url: Url, buf_cap: usize) -> InputUDP { 152 | InputUDP { 153 | url: url, 154 | buf: Arc::new((Mutex::new(VecDeque::with_capacity(buf_cap)), Condvar::new())), 155 | 156 | demuxer: ts::Demuxer::new(Default::default()), 157 | } 158 | } 159 | } 160 | 161 | impl Input for InputUDP { 162 | fn open(&mut self) -> Result<()> { 163 | let input_host = self 164 | .url 165 | .host() 166 | .ok_or(Error::new(ErrorKind::InputUrlMissingHost))?; 167 | 168 | let input_port = self.url.port().unwrap_or(5500); 169 | 170 | let input_host_domain = match input_host { 171 | Host::Domain(v) => Ok(v), 172 | _ => Err(Error::new(ErrorKind::InputUrlHostMustBeDomain)), 173 | }?; 174 | 175 | let iface = Ipv4Addr::new(0, 0, 0, 0); 176 | // let socket = try!(UdpSocket::bind((input_host_domain, input_port)));; 177 | 178 | // let iface = Ipv4Addr::new(127, 0, 0, 1); 179 | println!( 180 | "[<] {:?}: {:?} @ {:?}", 181 | input_host_domain, input_port, iface 182 | ); 183 | 184 | let input_host_ip_v4: Ipv4Addr = input_host_domain.parse().unwrap(); 185 | 186 | let socket = UdpSocket::bind((input_host_domain, input_port))?; 187 | 188 | if let Err(e) = socket.join_multicast_v4(&input_host_ip_v4, &iface) { 189 | eprintln!("error join-multiocast-v4: {}", e); 190 | } 191 | 192 | let pair = self.buf.clone(); 193 | thread::spawn(move || { 194 | let mut ts_pkt_raw: [u8; ts::Packet::SZ] = [0; ts::Packet::SZ]; 195 | 196 | loop { 197 | // MTU (maximum transmission unit) == 1500 for Ethertnet 198 | // 7*ts::Packet::SZ = 7*188 = 1316 < 1500 => OK 199 | let mut pkts_raw = [0; 7 * ts::Packet::SZ]; 200 | let (_, _) = socket.recv_from(&mut pkts_raw).unwrap(); 201 | 202 | let &(ref lock, ref cvar) = &*pair; 203 | let mut buf = match lock.lock() { 204 | Err(e) => { 205 | eprintln!("lock and get buffer failed: {}", e); 206 | continue; 207 | } 208 | Ok(buf) => buf, 209 | }; 210 | 211 | for pkt_index in 0..7 * ts::Packet::SZ / ts::Packet::SZ { 212 | let ts_pkt_raw_src = 213 | &pkts_raw[pkt_index * ts::Packet::SZ..(pkt_index + 1) * ts::Packet::SZ]; 214 | 215 | ts_pkt_raw.copy_from_slice(ts_pkt_raw_src); 216 | buf.push_back(ts_pkt_raw); 217 | } 218 | 219 | cvar.notify_all(); 220 | } 221 | }); 222 | 223 | Ok(()) 224 | } 225 | 226 | fn read(&mut self) -> Result<()> { 227 | let pair = self.buf.clone(); 228 | let &(ref lock, ref cvar) = &*pair; 229 | let mut buf = lock.lock().ok().ok_or(Error::new_with_details( 230 | ErrorKind::SyncPoison, 231 | "udp read lock error", 232 | ))?; 233 | 234 | buf = cvar.wait(buf).ok().ok_or(Error::new_with_details( 235 | ErrorKind::SyncPoison, 236 | "udp read cwar wait error", 237 | ))?; 238 | 239 | while !buf.is_empty() { 240 | let ts_pkt_raw = buf.pop_front().unwrap(); 241 | 242 | if let Err(e) = self.demuxer.demux(&ts_pkt_raw) { 243 | eprintln!("error demux ts-packet: ({:?})", e); 244 | } 245 | } 246 | 247 | Ok(()) 248 | } 249 | 250 | fn close(&mut self) -> Result<()> { 251 | println!("<<< UDP close"); 252 | 253 | Ok(()) 254 | } 255 | } 256 | 257 | struct Wrkr { 258 | input: Arc>, 259 | } 260 | 261 | impl Wrkr 262 | where 263 | I: Input + std::marker::Send + 'static, 264 | { 265 | pub fn new(input: I) -> Wrkr { 266 | Wrkr { 267 | input: Arc::new(Mutex::new(input)), 268 | } 269 | } 270 | 271 | pub fn run(&self) -> Result<()> { 272 | let input = self.input.clone(); 273 | { 274 | input.lock().unwrap().open()?; 275 | } 276 | 277 | thread::spawn(move || loop { 278 | match input.lock().unwrap().read() { 279 | Err(err) => { 280 | eprintln!("error read {}", err); 281 | return; 282 | } 283 | Ok(_) => {} 284 | } 285 | }); 286 | 287 | Ok(()) 288 | } 289 | } 290 | 291 | fn main() { 292 | // let args: Vec = env::args().collect(); 293 | // println!("{:?}", args); 294 | let matches = App::new("V/A tool") 295 | .version("0.0.3") 296 | .author("Ivan Egorov ") 297 | .about("simple mpeg-ts mcast probe") 298 | .arg( 299 | Arg::with_name("input") 300 | // .index(1) 301 | .short("i") 302 | .long("input") 303 | .help("Sets the input file to use") 304 | .required(true) 305 | .takes_value(true), 306 | ) 307 | .get_matches(); 308 | 309 | let input_raw = matches.value_of("input").unwrap(); 310 | let input_url = match Url::parse(input_raw) { 311 | Ok(v) => v, 312 | Err(err) => { 313 | eprintln!("error parse input url: {:?}\n", err); 314 | process::exit(1); 315 | } 316 | }; 317 | 318 | let input = InputUDP::new(input_url, 5000 * 7); 319 | 320 | let wrkr = Wrkr::new(input); 321 | 322 | if let Err(err) = wrkr.run() { 323 | eprintln!("error start worker: {:?}\n", err); 324 | process::exit(1); 325 | } 326 | 327 | loop { 328 | thread::sleep(Duration::from_secs(1)); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/pes.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::time::Duration; 3 | 4 | use crate::duration_fmt::DurationFmt; 5 | use crate::error::{Error, Kind as ErrorKind}; 6 | use crate::rational; 7 | use crate::rational::Rational; 8 | use crate::result::Result; 9 | 10 | /// ISO/IEC 13818-1 11 | pub struct Timestamp<'buf> { 12 | buf: &'buf [u8], 13 | } 14 | 15 | impl<'buf> Timestamp<'buf> { 16 | const SZ: usize = 5; 17 | const TB: Rational = rational::TB_90KHZ; 18 | 19 | #[inline(always)] 20 | fn new(buf: &'buf [u8]) -> Timestamp<'buf> { 21 | Timestamp { buf } 22 | } 23 | 24 | /// 90kHz 25 | pub fn value(&self) -> u64 { 26 | ((u64::from(self.buf[0]) & 0b0000_1110) << 29) // (>> 1 (<< 30)) 27 | | (u64::from(self.buf[1]) << 22) 28 | | (u64::from(self.buf[2] & 0b1111_1110) << 14) // (>> 1 (<< 15)) 29 | | (u64::from(self.buf[3]) << 7) 30 | | u64::from((self.buf[4] & 0b1111_1110) >> 1) 31 | } 32 | 33 | /// nanoseconds 34 | pub fn ns(&self) -> u64 { 35 | rational::rescale(self.value(), Self::TB, rational::TB_1NS) 36 | } 37 | } 38 | 39 | impl<'buf> From<&Timestamp<'buf>> for Duration { 40 | fn from(t: &Timestamp) -> Self { 41 | Duration::from_nanos(t.ns()) 42 | } 43 | } 44 | 45 | impl<'buf> From> for Duration { 46 | fn from(t: Timestamp) -> Self { 47 | Duration::from(&t) 48 | } 49 | } 50 | 51 | impl<'buf> From<&Timestamp<'buf>> for DurationFmt { 52 | fn from(t: &Timestamp) -> Self { 53 | DurationFmt::from_nanos(t.ns()) 54 | } 55 | } 56 | 57 | impl<'buf> fmt::Debug for Timestamp<'buf> { 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 59 | write!( 60 | f, 61 | ":v(90kHz) {} :v(ns) {} :duration {}", 62 | self.value(), 63 | self.ns(), 64 | DurationFmt::from(self) 65 | ) 66 | } 67 | } 68 | 69 | impl<'buf> fmt::Display for Timestamp<'buf> { 70 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 71 | DurationFmt::from(self).fmt(f) 72 | } 73 | } 74 | 75 | /// ISO/IEC 13818-1 76 | #[derive(Debug, PartialEq)] 77 | pub enum StreamID { 78 | ProgramStreamMap, 79 | PrivateStream1, 80 | PaddingStream, 81 | PrivateStream2, 82 | AudioStreamNumber(u8), 83 | VideoStreamNumber(u8), 84 | ECMStream, 85 | EMMStream, 86 | DSMCCStream, 87 | ISOIEC13522Stream, 88 | TypeA, 89 | TypeB, 90 | TypeC, 91 | TypeD, 92 | TypeE, 93 | AncillaryStream, 94 | SLPacketizedStream, 95 | FlexMuxStream, 96 | MetadataStream, 97 | ExtendedStreamId, 98 | ReservedDataStream, 99 | ProgramStreamDirectory, 100 | Other(u8), 101 | } 102 | 103 | impl StreamID { 104 | /// if (stream_id != program_stream_map 105 | /// && stream_id != padding_stream 106 | /// && stream_id != private_stream_2 107 | /// && stream_id != ECM 108 | /// && stream_id != EMM 109 | /// && stream_id != program_stream_directory 110 | /// && stream_id != DSMCC_stream 111 | /// && stream_id != ITU-T Rec. H.222.1 type E stream) 112 | #[inline(always)] 113 | pub fn is1(self) -> bool { 114 | self != StreamID::ProgramStreamMap 115 | && self != StreamID::PaddingStream 116 | && self != StreamID::PrivateStream2 117 | && self != StreamID::ECMStream 118 | && self != StreamID::EMMStream 119 | && self != StreamID::ProgramStreamDirectory 120 | && self != StreamID::DSMCCStream 121 | && self != StreamID::TypeE 122 | } 123 | 124 | /// if ( stream_id == program_stream_map 125 | /// || stream_id == private_stream_2 126 | /// || stream_id == ECM 127 | /// || stream_id == EMM 128 | /// || stream_id == program_stream_directory 129 | /// || stream_id == DSMCC_stream 130 | /// || stream_id == ITU-T Rec. H.222.1 type E stream) 131 | #[inline(always)] 132 | pub fn is2(self) -> bool { 133 | self == StreamID::ProgramStreamMap 134 | || self == StreamID::PrivateStream2 135 | || self == StreamID::ECMStream 136 | || self == StreamID::EMMStream 137 | || self == StreamID::ProgramStreamDirectory 138 | || self == StreamID::DSMCCStream 139 | || self == StreamID::TypeE 140 | } 141 | 142 | /// if ( stream_id == padding_stream) 143 | #[inline(always)] 144 | pub fn is3(self) -> bool { 145 | self == StreamID::PaddingStream 146 | } 147 | } 148 | 149 | impl From for StreamID { 150 | fn from(d: u8) -> Self { 151 | match d { 152 | 0b1011_1100 => StreamID::ProgramStreamMap, 153 | 0b1011_1101 => StreamID::PrivateStream1, 154 | 0b1011_1110 => StreamID::PaddingStream, 155 | 0b1011_1111 => StreamID::PrivateStream2, 156 | 0b1100_0000..=0b1101_1111 => StreamID::AudioStreamNumber(d), 157 | 0b1110_0000..=0b1110_1111 => StreamID::VideoStreamNumber(d), 158 | 0b1111_0000 => StreamID::ECMStream, 159 | 0b1111_0001 => StreamID::EMMStream, 160 | 0b1111_0010 => StreamID::DSMCCStream, 161 | 0b1111_0011 => StreamID::ISOIEC13522Stream, 162 | 0b1111_0100 => StreamID::TypeA, 163 | 0b1111_0101 => StreamID::TypeB, 164 | 0b1111_0110 => StreamID::TypeC, 165 | 0b1111_0111 => StreamID::TypeD, 166 | 0b1111_1000 => StreamID::TypeE, 167 | 0b1111_1001 => StreamID::AncillaryStream, 168 | 0b1111_1010 => StreamID::SLPacketizedStream, 169 | 0b1111_1011 => StreamID::FlexMuxStream, 170 | 0b1111_1100 => StreamID::MetadataStream, 171 | 0b1111_1101 => StreamID::ExtendedStreamId, 172 | 0b1111_1110 => StreamID::ReservedDataStream, 173 | 0b1111_1111 => StreamID::ProgramStreamDirectory, 174 | _ => StreamID::Other(d), 175 | } 176 | } 177 | } 178 | 179 | impl From for u8 { 180 | fn from(sid: StreamID) -> u8 { 181 | match sid { 182 | StreamID::ProgramStreamMap => 0b1011_1100, 183 | StreamID::PrivateStream1 => 0b1011_1101, 184 | StreamID::PaddingStream => 0b1011_1110, 185 | StreamID::PrivateStream2 => 0b1011_1111, 186 | StreamID::AudioStreamNumber(d) => d, 187 | StreamID::VideoStreamNumber(d) => d, 188 | StreamID::ECMStream => 0b1111_0000, 189 | StreamID::EMMStream => 0b1111_0001, 190 | StreamID::DSMCCStream => 0b1111_0010, 191 | StreamID::ISOIEC13522Stream => 0b1111_0011, 192 | StreamID::TypeA => 0b1111_0100, 193 | StreamID::TypeB => 0b1111_0101, 194 | StreamID::TypeC => 0b1111_0110, 195 | StreamID::TypeD => 0b1111_0111, 196 | StreamID::TypeE => 0b1111_1000, 197 | StreamID::AncillaryStream => 0b1111_1001, 198 | StreamID::SLPacketizedStream => 0b1111_1010, 199 | StreamID::FlexMuxStream => 0b1111_1011, 200 | StreamID::MetadataStream => 0b1111_1100, 201 | StreamID::ExtendedStreamId => 0b1111_1101, 202 | StreamID::ReservedDataStream => 0b1111_1110, 203 | StreamID::ProgramStreamDirectory => 0b1111_1111, 204 | StreamID::Other(d) => d, 205 | } 206 | } 207 | } 208 | 209 | /// ISO/IEC 13818-1 210 | #[derive(Clone, Copy, Debug, PartialEq)] 211 | pub enum ScramblingControl { 212 | NotScrabled, 213 | UserDefined(u8), 214 | } 215 | 216 | impl From for ScramblingControl { 217 | #[inline(always)] 218 | fn from(d: u8) -> Self { 219 | match d { 220 | 0b00 => ScramblingControl::NotScrabled, 221 | _ => ScramblingControl::UserDefined(d), 222 | } 223 | } 224 | } 225 | 226 | /// ISO/IEC 13818-1 227 | #[derive(Clone, Copy, Debug, PartialEq)] 228 | pub enum PtsDtsFlag { 229 | No, 230 | Pts, 231 | PtsDts, 232 | Forbidden, 233 | } 234 | 235 | impl From for PtsDtsFlag { 236 | #[inline(always)] 237 | fn from(d: u8) -> Self { 238 | match d { 239 | 0b00 => PtsDtsFlag::No, 240 | 0b10 => PtsDtsFlag::Pts, 241 | 0b11 => PtsDtsFlag::PtsDts, 242 | 243 | _ => PtsDtsFlag::Forbidden, 244 | } 245 | } 246 | } 247 | 248 | /// ISO/IEC 13818-1 249 | /// 250 | /// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html 251 | pub struct PES<'buf> { 252 | buf: &'buf [u8], 253 | } 254 | 255 | impl<'buf> PES<'buf> { 256 | const HEADER_SZ: usize = 6; 257 | const HEADER_SZ_1: usize = 3; 258 | const START_CODE: u32 = 0x0000_0001; 259 | const PTS_OFFSET_LFT: usize = 9; 260 | const PTS_OFFSET_RGHT: usize = Self::PTS_OFFSET_LFT + Timestamp::SZ; 261 | const DTS_OFFSET_LFT: usize = Self::PTS_OFFSET_RGHT; 262 | const DTS_OFFSET_RGHT: usize = Self::DTS_OFFSET_LFT + Timestamp::SZ; 263 | 264 | #[inline(always)] 265 | pub fn new(buf: &'buf [u8]) -> PES<'buf> { 266 | PES { buf } 267 | } 268 | 269 | #[inline(always)] 270 | pub fn try_new(buf: &'buf [u8]) -> Result> { 271 | let p = PES::new(buf); 272 | p.validate()?; 273 | Ok(p) 274 | } 275 | 276 | #[inline(always)] 277 | pub fn validate(&self) -> Result<()> { 278 | let sz1 = || PES::HEADER_SZ + PES::HEADER_SZ_1 + (self.pes_header_data_length() as usize); 279 | 280 | if self.buf.len() < Self::HEADER_SZ { 281 | Err(Error::new(ErrorKind::Buf(self.buf.len(), Self::HEADER_SZ))) 282 | } else if self.start_code() != Self::START_CODE { 283 | Err(Error::new(ErrorKind::PESStartCode(self.start_code()))) 284 | } else if self.stream_id().is1() && self.buf.len() < sz1() { 285 | Err(Error::new(ErrorKind::Buf(self.buf.len(), sz1()))) 286 | } else { 287 | Ok(()) 288 | } 289 | } 290 | 291 | #[inline(always)] 292 | fn start_code(&self) -> u32 { 293 | (u32::from(self.buf[0]) << 16) | (u32::from(self.buf[1]) << 8) | u32::from(self.buf[2]) 294 | } 295 | 296 | #[inline(always)] 297 | fn stream_id(&self) -> StreamID { 298 | StreamID::from(self.buf[3]) 299 | } 300 | 301 | #[inline(always)] 302 | #[allow(dead_code)] 303 | fn packet_length(&self) -> u16 { 304 | u16::from(self.buf[4]) << 8 | u16::from(self.buf[5]) 305 | } 306 | 307 | #[inline(always)] 308 | fn pts_dts_flag(&self) -> Option { 309 | if self.stream_id().is1() { 310 | Some(PtsDtsFlag::from((self.buf[7] & 0b1100_0000) >> 6)) 311 | } else { 312 | None 313 | } 314 | } 315 | 316 | #[inline(always)] 317 | fn pes_header_data_length(&self) -> usize { 318 | usize::from(self.buf[8]) 319 | } 320 | 321 | #[inline(always)] 322 | pub fn pts(&self) -> Option { 323 | self.pts_dts_flag().and_then(|flag| match flag { 324 | PtsDtsFlag::Pts | PtsDtsFlag::PtsDts => Some(Timestamp::new( 325 | &self.buf[Self::PTS_OFFSET_LFT..Self::PTS_OFFSET_RGHT], 326 | )), 327 | _ => None, 328 | }) 329 | } 330 | 331 | #[inline(always)] 332 | pub fn dts(&self) -> Option { 333 | self.pts_dts_flag().and_then(|flag| match flag { 334 | PtsDtsFlag::PtsDts => Some(Timestamp::new( 335 | &self.buf[Self::DTS_OFFSET_LFT..Self::DTS_OFFSET_RGHT], 336 | )), 337 | _ => None, 338 | }) 339 | } 340 | 341 | #[inline(always)] 342 | pub fn buf_seek_payload(&self) -> &'buf [u8] { 343 | if self.stream_id().is1() { 344 | &self.buf[(Self::HEADER_SZ + Self::HEADER_SZ_1)..] 345 | } else { 346 | &self.buf[Self::HEADER_SZ..] 347 | } 348 | } 349 | } 350 | 351 | impl<'buf> fmt::Debug for PES<'buf> { 352 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 353 | write!(f, ":PES (")?; 354 | 355 | if let Some(pts) = self.pts() { 356 | write!(f, ":pts {}", pts)?; 357 | } 358 | 359 | if let Some(dts) = self.dts() { 360 | write!(f, " :dts {}", dts)?; 361 | } 362 | 363 | write!(f, ")") 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /src/demuxer.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::HashMap; 3 | use std::io::{Cursor, Write}; 4 | use std::rc::Rc; 5 | use std::time::Duration; 6 | 7 | use crate::packet::Packet as TsPacket; 8 | use crate::pes::PES; 9 | use crate::pid::PID; 10 | use crate::result::Result; 11 | use crate::section::{WithHeader, WithSyntaxSection}; 12 | use crate::subtable_id::{SubtableID, SubtableIDer}; 13 | use crate::{EIT, PAT, PMT, SDT}; 14 | 15 | pub struct Buf(pub Cursor>); 16 | 17 | impl Buf { 18 | #[inline(always)] 19 | fn reset(&mut self) { 20 | self.0.set_position(0); 21 | self.0.get_mut().clear(); 22 | } 23 | 24 | #[inline(always)] 25 | fn is_empty(&self) -> bool { 26 | self.0.position() == 0 27 | } 28 | 29 | #[inline(always)] 30 | pub fn sz(&self) -> usize { 31 | self.0.position() as usize 32 | } 33 | } 34 | 35 | impl Default for Buf { 36 | fn default() -> Self { 37 | Buf(Cursor::new(Vec::with_capacity(2048))) 38 | } 39 | } 40 | 41 | pub struct Section { 42 | /// parent table-id 43 | table_id: SubtableID, 44 | 45 | /// number inside table sections 46 | number: u8, 47 | 48 | /// full section size with header, data, CRC 49 | sz: usize, 50 | 51 | pub buf: Buf, 52 | } 53 | 54 | impl Section { 55 | fn new(table_id: SubtableID, number: u8, sz: usize) -> Section { 56 | Section { 57 | table_id, 58 | number, 59 | sz, 60 | buf: Default::default(), 61 | } 62 | } 63 | 64 | #[inline(always)] 65 | fn into_ref(self) -> SectionRef { 66 | Rc::new(RefCell::new(Box::new(self))) 67 | } 68 | 69 | /// section consumed all data 70 | #[inline(always)] 71 | fn done(&self) -> bool { 72 | self.sz_need() == 0 73 | } 74 | 75 | /// sz need to read 76 | #[inline(always)] 77 | fn sz_need(&self) -> usize { 78 | self.sz - self.buf.sz() 79 | } 80 | } 81 | 82 | type SectionRef = Rc>>; 83 | 84 | pub struct Sections(pub Vec); 85 | 86 | impl Sections { 87 | #[inline(always)] 88 | fn get_mut(&mut self, number: u8) -> Option<&mut SectionRef> { 89 | self.0.iter_mut().find(|s| s.borrow().number == number) 90 | } 91 | 92 | #[inline(always)] 93 | fn push(&mut self, s: SectionRef) { 94 | self.0.push(s); 95 | self.0 96 | .sort_unstable_by(|a, b| a.borrow().number.cmp(&b.borrow().number)); 97 | } 98 | } 99 | 100 | impl Default for Sections { 101 | fn default() -> Self { 102 | Sections(Vec::with_capacity(1)) 103 | } 104 | } 105 | 106 | pub struct Table { 107 | /// mpeg-ts last-section-number 108 | last_section_number: u8, 109 | pub sections: Sections, 110 | } 111 | 112 | impl Table { 113 | fn new(last_section_number: u8) -> Table { 114 | Table { 115 | last_section_number, 116 | sections: Default::default(), 117 | } 118 | } 119 | 120 | #[inline(always)] 121 | fn done(&self) -> bool { 122 | match self.sections.0.len() { 123 | 0 => false, 124 | n => { 125 | let last = (&self.sections.0[n - 1]).borrow(); 126 | let first = (&self.sections.0[0]).borrow(); 127 | 128 | first.number == 0 129 | && last.number == self.last_section_number 130 | && first.done() 131 | && last.done() 132 | } 133 | } 134 | } 135 | } 136 | 137 | struct Tables { 138 | map: HashMap, 139 | /// current demuxing section 140 | current: Option, 141 | } 142 | 143 | impl Tables {} 144 | 145 | impl Default for Tables { 146 | fn default() -> Self { 147 | Tables { 148 | map: HashMap::new(), 149 | current: None, 150 | } 151 | } 152 | } 153 | 154 | pub struct Packet { 155 | pub pid: PID, 156 | 157 | pub offset: usize, 158 | 159 | /// presentation time stamp 160 | pub pts: Option, 161 | 162 | /// decode time stamp 163 | pub dts: Option, 164 | 165 | pub buf: Buf, 166 | 167 | /// got ts PUSI 168 | started: bool, 169 | } 170 | 171 | impl Packet { 172 | fn new(pid: PID) -> Packet { 173 | Packet { 174 | pid, 175 | offset: 0, 176 | pts: None, 177 | dts: None, 178 | buf: Default::default(), 179 | started: false, 180 | } 181 | } 182 | } 183 | 184 | #[derive(Default)] 185 | struct Packets(HashMap); 186 | 187 | /// pid, packet-constructed 188 | #[derive(Debug)] 189 | struct PMTPids(Vec<(PID, bool)>); 190 | 191 | impl PMTPids { 192 | #[inline(always)] 193 | fn has(&self, pid: PID) -> bool { 194 | self.0.iter().any(|p| (*p).0 == pid) 195 | } 196 | 197 | #[inline(always)] 198 | fn push_uniq(&mut self, pid: PID) { 199 | if !self.has(pid) { 200 | self.0.push((pid, false)) 201 | } 202 | } 203 | 204 | /// got pid? and pid is parsed and packet builded 205 | #[inline(always)] 206 | fn is_packet_builded(&self, pid: PID) -> Option { 207 | self.0.iter().find(|p| p.0 == pid).map(|p| p.1) 208 | } 209 | 210 | #[inline(always)] 211 | fn set_is_packet_builded(&mut self, pid: PID, v: bool) { 212 | if let Some(p) = self.0.iter_mut().find(|p| p.0 == pid) { 213 | p.1 = v; 214 | } 215 | } 216 | 217 | /// all pids are parsed? 218 | #[inline(always)] 219 | #[allow(dead_code)] 220 | fn are_all_packets_builded(&self) -> bool { 221 | !self.0.iter().any(|p| !(*p).1) 222 | } 223 | } 224 | 225 | impl Default for PMTPids { 226 | fn default() -> Self { 227 | PMTPids(Vec::with_capacity(3)) 228 | } 229 | } 230 | 231 | pub trait DemuxerEvents { 232 | fn on_table(&mut self, _: SubtableID, _: &Table) {} 233 | fn on_packet(&mut self, _: &Packet) {} 234 | } 235 | 236 | /// TODO: use tree, redix tree here 237 | /// TODO: add benches 238 | pub struct Demuxer 239 | where 240 | T: DemuxerEvents, 241 | { 242 | offset: usize, 243 | 244 | pat: Tables, 245 | pmt: Tables, 246 | eit: Tables, 247 | sdt: Tables, 248 | 249 | #[allow(dead_code)] 250 | nit: Tables, 251 | #[allow(dead_code)] 252 | cat: Tables, 253 | #[allow(dead_code)] 254 | bat: Tables, 255 | 256 | packets: Packets, 257 | 258 | // TODO: add PID with state(is-parsed or not) 259 | // for multiple PMTs 260 | pmt_pids: PMTPids, 261 | 262 | events: T, 263 | } 264 | 265 | unsafe impl Send for Demuxer where T: DemuxerEvents + Send {} 266 | 267 | impl Demuxer 268 | where 269 | T: DemuxerEvents, 270 | { 271 | pub fn new(events: T) -> Demuxer { 272 | Demuxer { 273 | offset: 0, 274 | 275 | pat: Default::default(), 276 | pmt: Default::default(), 277 | eit: Default::default(), 278 | sdt: Default::default(), 279 | nit: Default::default(), 280 | cat: Default::default(), 281 | bat: Default::default(), 282 | 283 | pmt_pids: Default::default(), 284 | 285 | packets: Default::default(), 286 | 287 | events, 288 | } 289 | } 290 | 291 | /// cache pmt pids 292 | // TODO: also do via iterator 293 | // TODO: .iter().collect() for lazy collection 294 | #[inline(always)] 295 | fn build_pmt_pids(&mut self) { 296 | for (_, table) in self.pat.map.iter() { 297 | for section_ref in table.sections.0.iter() { 298 | let section = (*section_ref).borrow(); 299 | let raw = section.buf.0.get_ref().as_slice(); 300 | let pat = PAT::new(raw); 301 | 302 | // TODO: refactor via iter/to-iter 303 | for pid in pat 304 | .programs() 305 | .filter_map(Result::ok) 306 | .filter(|p| p.pid().is_program_map()) 307 | .map(|p| PID::from(p.pid())) 308 | { 309 | self.pmt_pids.push_uniq(pid) 310 | } 311 | } 312 | } 313 | } 314 | 315 | /// build packets cache 316 | // TODO: also do via iterator 317 | // TODO: .iter().collect() for lazy collection 318 | #[inline(always)] 319 | fn build_packets(&mut self) { 320 | for (_, table) in self.pmt.map.iter() { 321 | for section_ref in table.sections.0.iter() { 322 | let section = (*section_ref).borrow(); 323 | let raw = section.buf.0.get_ref().as_slice(); 324 | let pmt = PMT::new(raw); 325 | 326 | // TODO: refactor via iter/to-iter 327 | for pid in pmt 328 | .streams() 329 | .filter_map(Result::ok) 330 | .map(|s| PID::from(s.pid())) 331 | { 332 | self.packets 333 | .0 334 | .entry(pid) 335 | .or_insert_with(|| Packet::new(pid)); 336 | } 337 | } 338 | } 339 | } 340 | 341 | // TODO: move to macros? 342 | #[inline(always)] 343 | fn demux_section(&mut self, pid_or_pmt: (PID, bool), pkt: &TsPacket) -> Result<()> { 344 | let tables = match pid_or_pmt { 345 | (PID::PAT, false) => &mut self.pat, 346 | (PID::SDT, false) => &mut self.sdt, 347 | (PID::EIT, false) => &mut self.eit, 348 | (PID::NIT, false) => &mut self.nit, 349 | (PID::CAT, false) => &mut self.cat, 350 | (_, true) => &mut self.pmt, 351 | _ => unreachable!(), 352 | }; 353 | 354 | let buf = pkt.buf_payload_section()?; 355 | 356 | if pkt.pusi() { 357 | let (id, sz, section_number, last_section_number) = match pid_or_pmt { 358 | (PID::PAT, false) => { 359 | let s = PAT::try_new(buf)?; 360 | ( 361 | s.subtable_id(), 362 | s.sz(), 363 | s.section_number(), 364 | s.last_section_number(), 365 | ) 366 | } 367 | (PID::SDT, false) => { 368 | let s = SDT::try_new(buf)?; 369 | ( 370 | s.subtable_id(), 371 | s.sz(), 372 | s.section_number(), 373 | s.last_section_number(), 374 | ) 375 | } 376 | (PID::EIT, false) => { 377 | let s = EIT::try_new(buf)?; 378 | ( 379 | s.subtable_id(), 380 | s.sz(), 381 | s.section_number(), 382 | s.last_section_number(), 383 | ) 384 | } 385 | (_, true) => { 386 | let s = PMT::try_new(buf)?; 387 | ( 388 | s.subtable_id(), 389 | s.sz(), 390 | s.section_number(), 391 | s.last_section_number(), 392 | ) 393 | } 394 | _ => unreachable!(), 395 | }; 396 | 397 | let table = tables 398 | .map 399 | .entry(id) 400 | .or_insert_with(|| Table::new(last_section_number)); 401 | 402 | let section_ref = match table.sections.get_mut(section_number) { 403 | Some(section_ref) => { 404 | let mut section = (*section_ref).borrow_mut(); 405 | section.buf.reset(); 406 | section.sz = sz; 407 | 408 | section_ref.clone() 409 | } 410 | None => { 411 | let section_ref = Section::new(id, section_number, sz).into_ref(); 412 | table.sections.push(section_ref.clone()); 413 | section_ref 414 | } 415 | }; 416 | 417 | tables.current = Some(section_ref); 418 | } 419 | 420 | if let Some(section_ref) = &tables.current { 421 | { 422 | let mut section = (*section_ref).borrow_mut(); 423 | let sz_need = section.sz_need(); 424 | 425 | // remove null/padding bytes 426 | let buf = if buf.len() > sz_need { 427 | &buf[0..sz_need] 428 | } else { 429 | buf 430 | }; 431 | 432 | section.buf.0.write_all(buf)?; 433 | } 434 | 435 | { 436 | let section = (*section_ref).borrow(); 437 | if section.done() { 438 | if let Some(table) = tables.map.get(§ion.table_id) { 439 | if table.done() { 440 | // emit 441 | self.events.on_table(section.table_id, &table); 442 | } 443 | } 444 | } 445 | } 446 | } 447 | 448 | Ok(()) 449 | } 450 | 451 | pub fn demux(&mut self, raw: &[u8]) -> Result<()> { 452 | if self.demux_tables(raw)? { 453 | return Ok(()); 454 | } 455 | 456 | self.demux_packets(raw) 457 | } 458 | 459 | /// ffmpeg::avformat_open_input analog 460 | /// probe input 461 | /// return: is pid handled? 462 | pub fn demux_tables(&mut self, raw: &[u8]) -> Result<(bool)> { 463 | self.offset += raw.len(); 464 | 465 | let pkt = TsPacket::new(&raw)?; 466 | let pid = pkt.pid(); 467 | 468 | if pid.is_null() { 469 | // null packet PID 470 | return Ok(true); 471 | } 472 | 473 | match pid { 474 | PID::PAT => { 475 | self.demux_section((pid, false), &pkt)?; 476 | 477 | // extract pids from PAT 478 | if self.pmt_pids.0.is_empty() { 479 | self.pmt_pids.0.clear(); 480 | self.packets.0.clear(); 481 | self.build_pmt_pids(); 482 | } 483 | } 484 | PID::SDT | PID::EIT /* | PID::NIT | PID::CAT | PID::BAT */ => 485 | self.demux_section((pid, false), &pkt)?, 486 | 487 | PID::Other(..) => { 488 | // PAT not ready yet 489 | // wait for PAT 490 | if self.pmt_pids.0.is_empty() { 491 | return Ok(true); 492 | } 493 | 494 | match self.pmt_pids.is_packet_builded(pid) { 495 | Some(true) => { // got PMT and already builded 496 | self.demux_section((pid, true), &pkt)?; 497 | }, 498 | Some(false) => { // got PMT and not builded 499 | self.demux_section((pid, true), &pkt)?; 500 | 501 | self.build_packets(); 502 | 503 | self.pmt_pids.set_is_packet_builded(pid, true); 504 | }, 505 | None => {return Ok(false); } 506 | } 507 | } 508 | _ => {} 509 | } 510 | 511 | Ok(true) 512 | } 513 | 514 | /// ffmpeg::av_read_frame analog 515 | pub fn demux_packets(&mut self, raw: &[u8]) -> Result<()> { 516 | self.offset += raw.len(); 517 | 518 | let pkt = TsPacket::new(&raw)?; 519 | let pid = pkt.pid(); 520 | 521 | if pid.is_null() // null packet PID 522 | && !pid.is_other() // PID is section/table PID 523 | // PAT not ready yet 524 | // wait for pat 525 | && !self.pmt_pids.0.is_empty() 526 | { 527 | return Ok(()); 528 | } 529 | 530 | let mut packet = match self.packets.0.get_mut(&pid) { 531 | Some(packet) => packet, 532 | None => return Ok(()), // packet is not builder - wait fot PMT 533 | }; 534 | 535 | let mut buf = pkt.buf_payload_pes()?; 536 | 537 | if pkt.pusi() { 538 | let pes = PES::new(buf); 539 | 540 | if !packet.buf.is_empty() { 541 | // emit 542 | self.events.on_packet(packet); 543 | } 544 | 545 | packet.buf.reset(); 546 | packet.started = true; 547 | packet.offset += self.offset + raw.len() - buf.len(); 548 | packet.pts = pes.pts().map(Duration::from); 549 | packet.dts = pes.dts().map(Duration::from); 550 | 551 | buf = pes.buf_seek_payload(); 552 | } 553 | 554 | if packet.started { 555 | packet.buf.0.write_all(buf)?; 556 | } 557 | 558 | Ok(()) 559 | } 560 | } 561 | 562 | #[cfg(test)] 563 | mod tests {} 564 | -------------------------------------------------------------------------------- /src/descriptor/tag.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub enum TagDVB { 3 | NetworkName, 4 | ServiceList, 5 | Stuffing, 6 | SatelliteDeliverySystem, 7 | CableDeliverySystem, 8 | VBIData, 9 | VBITeletext, 10 | BouquetName, 11 | Service, 12 | CountryAvailability, 13 | Linkage, 14 | NVODReference, 15 | TimeShiftedService, 16 | ShortEvent, 17 | ExtendedEvent, 18 | TimeShiftedEvent, 19 | Component, 20 | Mosaic, 21 | StreamIdentifier, 22 | CAIdentifier, 23 | Content, 24 | ParentalRating, 25 | Teletext, 26 | Telephone, 27 | LocalTimeOffset, 28 | Subtitling, 29 | TerrestrialDeliverySystem, 30 | MultilingualNetworkName, 31 | MultilingualBouquetName, 32 | MultilingualServiceName, 33 | MultilingualComponent, 34 | PrivateDataSpecifier, 35 | ServiceMove, 36 | ShortSmoothingBuffer, 37 | FrequencyList, 38 | PartialTransportStream, 39 | DataBroadcast, 40 | Scrambling, 41 | DataBroadcastId, 42 | TransportStream, 43 | DSNG, 44 | PDC, 45 | AC3, 46 | AncillaryData, 47 | CellList, 48 | CellFrequencyLink, 49 | AnnouncementSupport, 50 | ApplicationSignalling, 51 | AdaptationFieldData, 52 | ServiceIdentifier, 53 | ServiceAvailability, 54 | DefaultAuthority, 55 | RelatedContent, 56 | TVAId, 57 | ContentIdentifier, 58 | TimeSliceFecIdentifier, 59 | ECMRepetitionRate, 60 | S2SatelliteDeliverySystem, 61 | EnhancedAC3, 62 | DTSDescriptor, 63 | AAC, 64 | XAITLocation, 65 | FTAContentManagement, 66 | Extension, 67 | } 68 | 69 | #[derive(Clone, Debug)] 70 | pub enum Tag { 71 | VH2621381811172, 72 | A1381811172, 73 | Hierarchy, 74 | RegistrationPrivate, 75 | DataStreamAlignment, 76 | TargetBackgroundGrid, 77 | VideoWindow, 78 | CASEMMECMPID, 79 | ISO639, 80 | SystemClockExternalReference, 81 | MultiplexBufferUtilizationBounds, 82 | CopyrightIdentificationSystemAndReference, 83 | MaximumBitRate, 84 | PrivateDataIndicator, 85 | SmoothingBuffer, 86 | STDVideoBufferLeakControl, 87 | IBPVideoIFrameIndicator, 88 | DSMCCCarouselIdentifier, 89 | DSMCCAssociationTag, 90 | DSMCCDeferredAssociationTag, 91 | DSMCCReserved, 92 | DSMCCNPTReference, 93 | DSMCCNPTEndpoint, 94 | DSMCCStreamMode, 95 | DSMCCStreamEvent, 96 | VideoStreamHeaderParametersMPEG4H263Based, 97 | AudioStreamHeaderParametersMPEG4LOASMultiFormatFramed, 98 | IODParametersForISOIEC144961, 99 | SLParametersForISOIEC144961, 100 | FMCParametersForISOIEC144961, 101 | ExternalESIdentifierForISOIEC144961, 102 | MuxCodeForISOIEC144961, 103 | FMXBufferSizeForISOIEC144961, 104 | MultiplexBufferForISOIEC144961, 105 | ContentLabelingForISOIEC144961, 106 | MetadataPointer, 107 | Metadata, 108 | MetadataSTD, 109 | VideoStreamHeaderParametersForITUTRecH264AndISOIEC1449610, 110 | ISOIEC1381811IPMPDRM, 111 | TimingAndHRDForITUTRecH264andISOIEC1449610, 112 | AudioStreamHeaderParametersForISOIEC138187ADTSAAC, 113 | FlexMuxTimingForISOIEC144961, 114 | TextStreamHeaderParametersForISOIEC14496, 115 | AudioExtensionStreamHeaderParametersForISOIEC144963MPEG4LOASMultiFormatFramed, 116 | VideoAuxiliaryStreamHeaderParameters, 117 | VideoScalableStreamHeaderParameters, 118 | VideoMultiStreamHeaderParameters, 119 | VideoStreamHeaderParametersForITUTRecT800AndISOIEC15444, 120 | VideoMultiOperationPointStreamHeaderParameters, 121 | VideoStereoscopic3DStreamHeaderParametersForITUTRecH262ISOIEC138182AndISOIEC111722, 122 | ProgramStereoscopic3DInformation, 123 | VideoStereoscopic3DInformation, 124 | 125 | VideoLANFourCCVideoSizeAndCodecInitializationData, 126 | 127 | // 0x40...0x7F 128 | DVB(TagDVB), 129 | 130 | // 0x00...0x01 131 | // 0x37...0x3F 132 | Reserved(u8), 133 | 134 | // 0x80...0x9F | 0xA1...0xCF 135 | ATSC(u8), 136 | 137 | // 0xD0...0xDF 138 | ISDB(u8), 139 | 140 | // 0xE0...0xE9 141 | CableLabs(u8), 142 | 143 | // 0xEA...0xFE 144 | Other(u8), 145 | 146 | // 0xFF 147 | Forbidden, 148 | } 149 | 150 | impl Tag { 151 | #[inline(always)] 152 | pub fn is_dvb_service(&self) -> bool { 153 | match self { 154 | Tag::DVB(TagDVB::Service) => true, 155 | _ => false, 156 | } 157 | } 158 | 159 | #[inline(always)] 160 | pub fn is_dvb_short_event(&self) -> bool { 161 | match self { 162 | Tag::DVB(TagDVB::ShortEvent) => true, 163 | _ => false, 164 | } 165 | } 166 | } 167 | 168 | impl From for Tag { 169 | fn from(d: u8) -> Self { 170 | match d { 171 | 0x02 => Tag::VH2621381811172, 172 | 0x03 => Tag::A1381811172, 173 | 0x04 => Tag::Hierarchy, 174 | 0x05 => Tag::RegistrationPrivate, 175 | 0x06 => Tag::DataStreamAlignment, 176 | 0x07 => Tag::TargetBackgroundGrid, 177 | 0x08 => Tag::VideoWindow, 178 | 0x09 => Tag::CASEMMECMPID, 179 | 0x0A => Tag::ISO639, 180 | 0x0B => Tag::SystemClockExternalReference, 181 | 0x0C => Tag::MultiplexBufferUtilizationBounds, 182 | 0x0D => Tag::CopyrightIdentificationSystemAndReference, 183 | 0x0E => Tag::MaximumBitRate, 184 | 0x0F => Tag::PrivateDataIndicator, 185 | 0x10 => Tag::SmoothingBuffer, 186 | 0x11 => Tag::STDVideoBufferLeakControl, 187 | 0x12 => Tag::IBPVideoIFrameIndicator, 188 | 0x13 => Tag::DSMCCCarouselIdentifier, 189 | 0x14 => Tag::DSMCCAssociationTag, 190 | 0x15 => Tag::DSMCCDeferredAssociationTag, 191 | 0x16 => Tag::DSMCCReserved, 192 | 0x17 => Tag::DSMCCNPTReference, 193 | 0x18 => Tag::DSMCCNPTEndpoint, 194 | 0x19 => Tag::DSMCCStreamMode, 195 | 0x1A => Tag::DSMCCStreamEvent, 196 | 0x1B => Tag::VideoStreamHeaderParametersMPEG4H263Based, 197 | 0x1C => Tag::AudioStreamHeaderParametersMPEG4LOASMultiFormatFramed, 198 | 0x1D => Tag::IODParametersForISOIEC144961, 199 | 0x1E => Tag::SLParametersForISOIEC144961, 200 | 0x1F => Tag::FMCParametersForISOIEC144961, 201 | 0x20 => Tag::ExternalESIdentifierForISOIEC144961, 202 | 0x21 => Tag::MuxCodeForISOIEC144961, 203 | 0x22 => Tag::FMXBufferSizeForISOIEC144961, 204 | 0x23 => Tag::MultiplexBufferForISOIEC144961, 205 | 0x24 => Tag::ContentLabelingForISOIEC144961, 206 | 0x25 => Tag::MetadataPointer, 207 | 0x26 => Tag::Metadata, 208 | 0x27 => Tag::MetadataSTD, 209 | 0x28 => Tag::VideoStreamHeaderParametersForITUTRecH264AndISOIEC1449610, 210 | 0x29 => Tag::ISOIEC1381811IPMPDRM, 211 | 0x2A => Tag::TimingAndHRDForITUTRecH264andISOIEC1449610, 212 | 0x2B => Tag::AudioStreamHeaderParametersForISOIEC138187ADTSAAC, 213 | 0x2C => Tag::FlexMuxTimingForISOIEC144961, 214 | 0x2D => Tag::TextStreamHeaderParametersForISOIEC14496, 215 | 0x2E => Tag::AudioExtensionStreamHeaderParametersForISOIEC144963MPEG4LOASMultiFormatFramed, 216 | 0x2F => Tag::VideoAuxiliaryStreamHeaderParameters, 217 | 0x30 => Tag::VideoScalableStreamHeaderParameters, 218 | 0x31 => Tag::VideoMultiStreamHeaderParameters, 219 | 0x32 => Tag::VideoStreamHeaderParametersForITUTRecT800AndISOIEC15444, 220 | 0x33 => Tag::VideoMultiOperationPointStreamHeaderParameters, 221 | 0x34 => Tag::VideoStereoscopic3DStreamHeaderParametersForITUTRecH262ISOIEC138182AndISOIEC111722, 222 | 0x35 => Tag::ProgramStereoscopic3DInformation, 223 | 0x36 => Tag::VideoStereoscopic3DInformation, 224 | 225 | 0xA0 => Tag::VideoLANFourCCVideoSizeAndCodecInitializationData, 226 | 227 | 0x40 => Tag::DVB(TagDVB::NetworkName), 228 | 0x41 => Tag::DVB(TagDVB::ServiceList), 229 | 0x42 => Tag::DVB(TagDVB::Stuffing), 230 | 0x43 => Tag::DVB(TagDVB::SatelliteDeliverySystem), 231 | 0x44 => Tag::DVB(TagDVB::CableDeliverySystem), 232 | 0x45 => Tag::DVB(TagDVB::VBIData), 233 | 0x46 => Tag::DVB(TagDVB::VBITeletext), 234 | 0x47 => Tag::DVB(TagDVB::BouquetName), 235 | 0x48 => Tag::DVB(TagDVB::Service), 236 | 0x49 => Tag::DVB(TagDVB::CountryAvailability), 237 | 0x4A => Tag::DVB(TagDVB::Linkage), 238 | 0x4B => Tag::DVB(TagDVB::NVODReference), 239 | 0x4C => Tag::DVB(TagDVB::TimeShiftedService), 240 | 0x4D => Tag::DVB(TagDVB::ShortEvent), 241 | 0x4E => Tag::DVB(TagDVB::ExtendedEvent), 242 | 0x4F => Tag::DVB(TagDVB::TimeShiftedEvent), 243 | 0x50 => Tag::DVB(TagDVB::Component), 244 | 0x51 => Tag::DVB(TagDVB::Mosaic), 245 | 0x52 => Tag::DVB(TagDVB::StreamIdentifier), 246 | 0x53 => Tag::DVB(TagDVB::CAIdentifier), 247 | 0x54 => Tag::DVB(TagDVB::Content), 248 | 0x55 => Tag::DVB(TagDVB::ParentalRating), 249 | 0x56 => Tag::DVB(TagDVB::Teletext), 250 | 0x57 => Tag::DVB(TagDVB::Telephone), 251 | 0x58 => Tag::DVB(TagDVB::LocalTimeOffset), 252 | 0x59 => Tag::DVB(TagDVB::Subtitling), 253 | 0x5A => Tag::DVB(TagDVB::TerrestrialDeliverySystem), 254 | 0x5B => Tag::DVB(TagDVB::MultilingualNetworkName), 255 | 0x5C => Tag::DVB(TagDVB::MultilingualBouquetName), 256 | 0x5D => Tag::DVB(TagDVB::MultilingualServiceName), 257 | 0x5E => Tag::DVB(TagDVB::MultilingualComponent), 258 | 0x5F => Tag::DVB(TagDVB::PrivateDataSpecifier), 259 | 0x60 => Tag::DVB(TagDVB::ServiceMove), 260 | 0x61 => Tag::DVB(TagDVB::ShortSmoothingBuffer), 261 | 0x62 => Tag::DVB(TagDVB::FrequencyList), 262 | 0x63 => Tag::DVB(TagDVB::PartialTransportStream), 263 | 0x64 => Tag::DVB(TagDVB::DataBroadcast), 264 | 0x65 => Tag::DVB(TagDVB::Scrambling), 265 | 0x66 => Tag::DVB(TagDVB::DataBroadcastId), 266 | 0x67 => Tag::DVB(TagDVB::TransportStream), 267 | 0x68 => Tag::DVB(TagDVB::DSNG), 268 | 0x69 => Tag::DVB(TagDVB::PDC), 269 | 0x6A => Tag::DVB(TagDVB::AC3), 270 | 0x6B => Tag::DVB(TagDVB::AncillaryData), 271 | 0x6C => Tag::DVB(TagDVB::CellList), 272 | 0x6D => Tag::DVB(TagDVB::CellFrequencyLink), 273 | 0x6E => Tag::DVB(TagDVB::AnnouncementSupport), 274 | 0x6F => Tag::DVB(TagDVB::ApplicationSignalling), 275 | 0x70 => Tag::DVB(TagDVB::AdaptationFieldData), 276 | 0x71 => Tag::DVB(TagDVB::ServiceIdentifier), 277 | 0x72 => Tag::DVB(TagDVB::ServiceAvailability), 278 | 0x73 => Tag::DVB(TagDVB::DefaultAuthority), 279 | 0x74 => Tag::DVB(TagDVB::RelatedContent), 280 | 0x75 => Tag::DVB(TagDVB::TVAId), 281 | 0x76 => Tag::DVB(TagDVB::ContentIdentifier), 282 | 0x77 => Tag::DVB(TagDVB::TimeSliceFecIdentifier), 283 | 0x78 => Tag::DVB(TagDVB::ECMRepetitionRate), 284 | 0x79 => Tag::DVB(TagDVB::S2SatelliteDeliverySystem), 285 | 0x7A => Tag::DVB(TagDVB::EnhancedAC3), 286 | 0x7B => Tag::DVB(TagDVB::DTSDescriptor), 287 | 0x7C => Tag::DVB(TagDVB::AAC), 288 | 0x7D => Tag::DVB(TagDVB::XAITLocation), 289 | 0x7E => Tag::DVB(TagDVB::FTAContentManagement), 290 | 0x7F => Tag::DVB(TagDVB::Extension), 291 | 292 | 0x00..=0x01 | 0x37..=0x3F => Tag::Reserved(d), 293 | 0x80..=0x9F | 0xA1..=0xCF => Tag::ATSC(d), 294 | 295 | 0xD0..=0xDF => Tag::ISDB(d), 296 | 297 | 0xE0..=0xE9 => Tag::CableLabs(d), 298 | 299 | 0xEA..=0xFE => Tag::Other(d), 300 | 301 | 0xFF => Tag::Forbidden, 302 | } 303 | } 304 | } 305 | 306 | impl From for u8 { 307 | fn from(dt: Tag) -> u8 { 308 | match dt { 309 | Tag::VH2621381811172 => 0x02, 310 | Tag::A1381811172 => 0x03, 311 | Tag::Hierarchy => 0x04, 312 | Tag::RegistrationPrivate => 0x05, 313 | Tag::DataStreamAlignment => 0x06, 314 | Tag::TargetBackgroundGrid => 0x07, 315 | Tag::VideoWindow => 0x08, 316 | Tag::CASEMMECMPID => 0x09, 317 | Tag::ISO639 => 0x0A, 318 | Tag::SystemClockExternalReference => 0x0B, 319 | Tag::MultiplexBufferUtilizationBounds => 0x0C, 320 | Tag::CopyrightIdentificationSystemAndReference => 0x0D, 321 | Tag::MaximumBitRate => 0x0E, 322 | Tag::PrivateDataIndicator => 0x0F, 323 | Tag::SmoothingBuffer => 0x10, 324 | Tag::STDVideoBufferLeakControl => 0x11, 325 | Tag::IBPVideoIFrameIndicator => 0x12, 326 | Tag::DSMCCCarouselIdentifier => 0x13, 327 | Tag::DSMCCAssociationTag => 0x14, 328 | Tag::DSMCCDeferredAssociationTag => 0x15, 329 | Tag::DSMCCReserved => 0x16, 330 | Tag::DSMCCNPTReference => 0x17, 331 | Tag::DSMCCNPTEndpoint => 0x18, 332 | Tag::DSMCCStreamMode => 0x19, 333 | Tag::DSMCCStreamEvent => 0x1A, 334 | Tag::VideoStreamHeaderParametersMPEG4H263Based => 0x1B, 335 | Tag::AudioStreamHeaderParametersMPEG4LOASMultiFormatFramed => 0x1C, 336 | Tag::IODParametersForISOIEC144961 => 0x1D, 337 | Tag::SLParametersForISOIEC144961 => 0x1E, 338 | Tag::FMCParametersForISOIEC144961 => 0x1F, 339 | Tag::ExternalESIdentifierForISOIEC144961 => 0x20, 340 | Tag::MuxCodeForISOIEC144961 => 0x21, 341 | Tag::FMXBufferSizeForISOIEC144961 => 0x22, 342 | Tag::MultiplexBufferForISOIEC144961 => 0x23, 343 | Tag::ContentLabelingForISOIEC144961 => 0x24, 344 | Tag::MetadataPointer => 0x25, 345 | Tag::Metadata => 0x26, 346 | Tag::MetadataSTD => 0x27, 347 | Tag::VideoStreamHeaderParametersForITUTRecH264AndISOIEC1449610 => 0x28, 348 | Tag::ISOIEC1381811IPMPDRM => 0x29, 349 | Tag::TimingAndHRDForITUTRecH264andISOIEC1449610 => 0x2A, 350 | Tag::AudioStreamHeaderParametersForISOIEC138187ADTSAAC => 0x2B, 351 | Tag::FlexMuxTimingForISOIEC144961 => 0x2C, 352 | Tag::TextStreamHeaderParametersForISOIEC14496 => 0x2D, 353 | Tag::AudioExtensionStreamHeaderParametersForISOIEC144963MPEG4LOASMultiFormatFramed => 0x2E, 354 | Tag::VideoAuxiliaryStreamHeaderParameters => 0x2F, 355 | Tag::VideoScalableStreamHeaderParameters => 0x30, 356 | Tag::VideoMultiStreamHeaderParameters => 0x31, 357 | Tag::VideoStreamHeaderParametersForITUTRecT800AndISOIEC15444 => 0x32, 358 | Tag::VideoMultiOperationPointStreamHeaderParameters => 0x33, 359 | Tag::VideoStereoscopic3DStreamHeaderParametersForITUTRecH262ISOIEC138182AndISOIEC111722 => 0x34, 360 | Tag::ProgramStereoscopic3DInformation => 0x35, 361 | Tag::VideoStereoscopic3DInformation => 0x36, 362 | 363 | Tag::VideoLANFourCCVideoSizeAndCodecInitializationData => 0xA0, 364 | 365 | Tag::DVB(TagDVB::NetworkName) => 0x40, 366 | Tag::DVB(TagDVB::ServiceList) => 0x41, 367 | Tag::DVB(TagDVB::Stuffing) => 0x42, 368 | Tag::DVB(TagDVB::SatelliteDeliverySystem) => 0x43, 369 | Tag::DVB(TagDVB::CableDeliverySystem) => 0x44, 370 | Tag::DVB(TagDVB::VBIData) => 0x45, 371 | Tag::DVB(TagDVB::VBITeletext) => 0x46, 372 | Tag::DVB(TagDVB::BouquetName) => 0x47, 373 | Tag::DVB(TagDVB::Service) => 0x48, 374 | Tag::DVB(TagDVB::CountryAvailability) => 0x49, 375 | Tag::DVB(TagDVB::Linkage) => 0x4A, 376 | Tag::DVB(TagDVB::NVODReference) => 0x4B, 377 | Tag::DVB(TagDVB::TimeShiftedService) => 0x4C, 378 | Tag::DVB(TagDVB::ShortEvent) => 0x4D, 379 | Tag::DVB(TagDVB::ExtendedEvent) => 0x4E, 380 | Tag::DVB(TagDVB::TimeShiftedEvent) => 0x4F, 381 | Tag::DVB(TagDVB::Component) => 0x50, 382 | Tag::DVB(TagDVB::Mosaic) => 0x51, 383 | Tag::DVB(TagDVB::StreamIdentifier) => 0x52, 384 | Tag::DVB(TagDVB::CAIdentifier) => 0x53, 385 | Tag::DVB(TagDVB::Content) => 0x54, 386 | Tag::DVB(TagDVB::ParentalRating) => 0x55, 387 | Tag::DVB(TagDVB::Teletext) => 0x56, 388 | Tag::DVB(TagDVB::Telephone) => 0x57, 389 | Tag::DVB(TagDVB::LocalTimeOffset) => 0x58, 390 | Tag::DVB(TagDVB::Subtitling) => 0x59, 391 | Tag::DVB(TagDVB::TerrestrialDeliverySystem) => 0x5A, 392 | Tag::DVB(TagDVB::MultilingualNetworkName) => 0x5B, 393 | Tag::DVB(TagDVB::MultilingualBouquetName) => 0x5C, 394 | Tag::DVB(TagDVB::MultilingualServiceName) => 0x5D, 395 | Tag::DVB(TagDVB::MultilingualComponent) => 0x5E, 396 | Tag::DVB(TagDVB::PrivateDataSpecifier) => 0x5F, 397 | Tag::DVB(TagDVB::ServiceMove) => 0x60, 398 | Tag::DVB(TagDVB::ShortSmoothingBuffer) => 0x61, 399 | Tag::DVB(TagDVB::FrequencyList) => 0x62, 400 | Tag::DVB(TagDVB::PartialTransportStream) => 0x63, 401 | Tag::DVB(TagDVB::DataBroadcast) => 0x64, 402 | Tag::DVB(TagDVB::Scrambling) => 0x65, 403 | Tag::DVB(TagDVB::DataBroadcastId) => 0x66, 404 | Tag::DVB(TagDVB::TransportStream) => 0x67, 405 | Tag::DVB(TagDVB::DSNG) => 0x68, 406 | Tag::DVB(TagDVB::PDC) => 0x69, 407 | Tag::DVB(TagDVB::AC3) => 0x6A, 408 | Tag::DVB(TagDVB::AncillaryData) => 0x6B, 409 | Tag::DVB(TagDVB::CellList) => 0x6C, 410 | Tag::DVB(TagDVB::CellFrequencyLink) => 0x6D, 411 | Tag::DVB(TagDVB::AnnouncementSupport) => 0x6E, 412 | Tag::DVB(TagDVB::ApplicationSignalling) => 0x6F, 413 | Tag::DVB(TagDVB::AdaptationFieldData) => 0x70, 414 | Tag::DVB(TagDVB::ServiceIdentifier) => 0x71, 415 | Tag::DVB(TagDVB::ServiceAvailability) => 0x72, 416 | Tag::DVB(TagDVB::DefaultAuthority) => 0x73, 417 | Tag::DVB(TagDVB::RelatedContent) => 0x74, 418 | Tag::DVB(TagDVB::TVAId) => 0x75, 419 | Tag::DVB(TagDVB::ContentIdentifier) => 0x76, 420 | Tag::DVB(TagDVB::TimeSliceFecIdentifier) => 0x77, 421 | Tag::DVB(TagDVB::ECMRepetitionRate) => 0x78, 422 | Tag::DVB(TagDVB::S2SatelliteDeliverySystem) => 0x79, 423 | Tag::DVB(TagDVB::EnhancedAC3) => 0x7A, 424 | Tag::DVB(TagDVB::DTSDescriptor) => 0x7B, 425 | Tag::DVB(TagDVB::AAC) => 0x7C, 426 | Tag::DVB(TagDVB::XAITLocation) => 0x7D, 427 | Tag::DVB(TagDVB::FTAContentManagement) => 0x7E, 428 | Tag::DVB(TagDVB::Extension) => 0x7F, 429 | 430 | Tag::Reserved(d) => d, 431 | Tag::ATSC(d) => d, 432 | 433 | Tag::ISDB(d) => d, 434 | 435 | Tag::CableLabs(d) => d, 436 | 437 | Tag::Other(d) => d, 438 | 439 | Tag::Forbidden => 0xFF, 440 | } 441 | } 442 | } 443 | --------------------------------------------------------------------------------