#![warn(missing_docs)]
#![deny(unused_results)]
#![deny(clippy::as_conversions)]
#![deny(clippy::panic)]
#![deny(clippy::unwrap_used)]
use std::collections::{HashMap, VecDeque};
use std::convert::TryInto;
use std::error::Error;
use std::io::{Read, Seek, SeekFrom};
use std::num::NonZeroU64;
use ebml::{
collect_children, expect_master, find_bool_or, find_custom_type, find_float_or, find_nonzero,
find_nonzero_or, find_string, find_unsigned, find_unsigned_or, next_element,
parse_children_at_offset, parse_element_header, try_find_binary, try_find_custom_type,
try_find_custom_type_or, try_find_date, try_find_float, try_find_nonzero, try_find_string,
try_find_unsigned, try_parse_child, try_parse_children, ElementData, ParsableElement,
};
pub use element_id::ElementId;
use element_id::ID_TO_ELEMENT_ID;
pub use enums::*;
pub use error::DemuxError;
use crate::block::{parse_block_header, LacedFrame};
use crate::ebml::{parse_child, try_find_bool};
mod block;
mod ebml;
pub(crate) mod element_id;
mod enums;
mod error;
const DEMUXER_DOC_TYPE_VERSION: u64 = 4;
type Result<T> = std::result::Result<T, DemuxError>;
#[derive(Clone, Debug, Default)]
pub struct Frame {
pub track: u64,
pub timestamp: u64,
pub data: Vec<u8>,
pub is_invisible: bool,
pub is_keyframe: Option<bool>,
pub is_discardable: Option<bool>,
}
#[derive(Clone, Debug)]
pub struct EbmlHeader {
version: Option<u64>,
read_version: Option<u64>,
max_id_length: u64,
max_size_length: u64,
doc_type: String,
doc_type_version: u64,
doc_type_read_version: u64,
}
impl<R: Read + Seek> ParsableElement<R> for EbmlHeader {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let version = try_find_unsigned(fields, ElementId::EbmlVersion)?;
let read_version = try_find_unsigned(fields, ElementId::EbmlReadVersion)?;
let max_id_length = find_unsigned_or(fields, ElementId::EbmlMaxIdLength, 4)?;
let max_size_length = find_unsigned_or(fields, ElementId::EbmlMaxSizeLength, 8)?;
let doc_type = find_string(fields, ElementId::DocType)?;
let doc_type_version = find_unsigned(fields, ElementId::DocTypeVersion)?;
let doc_type_read_version = find_unsigned(fields, ElementId::DocTypeReadVersion)?;
if doc_type != "matroska" && doc_type != "webm" {
return Err(DemuxError::InvalidEbmlHeader(format!(
"unsupported DocType: {}",
doc_type
)));
}
if doc_type_read_version >= DEMUXER_DOC_TYPE_VERSION {
return Err(DemuxError::InvalidEbmlHeader(format!(
"unsupported DocTypeReadVersion: {}",
doc_type_read_version
)));
}
if max_id_length > 4 {
return Err(DemuxError::InvalidEbmlHeader(format!(
"unsupported MaxIdLength: {}",
max_id_length
)));
}
if max_size_length > 8 {
return Err(DemuxError::InvalidEbmlHeader(format!(
"unsupported MaxSizeLength: {}",
max_size_length
)));
}
Ok(Self {
version,
read_version,
max_id_length,
max_size_length,
doc_type,
doc_type_version,
doc_type_read_version,
})
}
}
impl EbmlHeader {
pub fn version(&self) -> Option<u64> {
self.version
}
pub fn read_version(&self) -> Option<u64> {
self.read_version
}
pub fn max_id_length(&self) -> u64 {
self.max_id_length
}
pub fn max_size_length(&self) -> u64 {
self.max_size_length
}
pub fn doc_type(&self) -> &str {
&self.doc_type
}
pub fn doc_type_version(&self) -> u64 {
self.doc_type_version
}
pub fn doc_type_read_version(&self) -> u64 {
self.doc_type_read_version
}
}
#[derive(Clone, Debug)]
pub struct Info {
timestamp_scale: NonZeroU64,
duration: Option<f64>,
date_utc: Option<i64>,
title: Option<String>,
muxing_app: String,
writing_app: String,
}
impl<R: Read + Seek> ParsableElement<R> for Info {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let timestamp_scale = find_nonzero_or(fields, ElementId::TimestampScale, 1000000)?;
let duration = try_find_float(fields, ElementId::Duration)?;
let date_utc = try_find_date(fields, ElementId::DateUtc)?;
let title = try_find_string(fields, ElementId::Title)?;
let muxing_app = find_string(fields, ElementId::MuxingApp)?;
let writing_app = find_string(fields, ElementId::WritingApp)?;
if let Some(duration) = duration {
if duration < 0.0 {
return Err(DemuxError::PositiveValueIsNotPositive);
}
}
Ok(Self {
timestamp_scale,
duration,
date_utc,
title,
muxing_app,
writing_app,
})
}
}
impl Info {
pub fn timestamp_scale(&self) -> NonZeroU64 {
self.timestamp_scale
}
pub fn duration(&self) -> Option<f64> {
self.duration
}
pub fn date_utc(&self) -> Option<i64> {
self.date_utc
}
pub fn title(&self) -> Option<&str> {
match self.title.as_ref() {
None => None,
Some(title) => Some(title),
}
}
pub fn muxing_app(&self) -> &str {
&self.muxing_app
}
pub fn writing_app(&self) -> &str {
&self.writing_app
}
}
#[derive(Clone, Debug)]
pub struct TrackEntry {
track_number: NonZeroU64,
track_uid: NonZeroU64,
track_type: TrackType,
flag_enabled: bool,
flag_default: bool,
flag_forced: bool,
flag_lacing: bool,
default_duration: Option<NonZeroU64>,
name: Option<String>,
language: Option<String>,
codec_id: String,
codec_private: Option<Vec<u8>>,
codec_name: Option<String>,
codec_delay: Option<u64>,
seek_pre_roll: Option<u64>,
audio: Option<Audio>,
video: Option<Video>,
content_encodings: Option<Vec<ContentEncoding>>,
}
impl<R: Read + Seek> ParsableElement<R> for TrackEntry {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let track_number = find_nonzero(fields, ElementId::TrackNumber)?;
let track_uid = find_nonzero(fields, ElementId::TrackUid)?;
let track_type = find_custom_type(fields, ElementId::TrackType)?;
let flag_enabled = find_bool_or(fields, ElementId::FlagEnabled, true)?;
let flag_default = find_bool_or(fields, ElementId::FlagDefault, true)?;
let flag_forced = find_bool_or(fields, ElementId::FlagForced, false)?;
let flag_lacing = find_bool_or(fields, ElementId::FlagLacing, false)?;
let default_duration = try_find_nonzero(fields, ElementId::DefaultDuration)?;
let name = try_find_string(fields, ElementId::Name)?;
let language = try_find_string(fields, ElementId::Language)?;
let codec_id = find_string(fields, ElementId::CodecId)?;
let codec_private = try_find_binary(r, fields, ElementId::CodecPrivate)?;
let codec_name = try_find_string(fields, ElementId::CodecName)?;
let codec_delay = try_find_unsigned(fields, ElementId::CodecDelay)?;
let seek_pre_roll = try_find_unsigned(fields, ElementId::SeekPreRoll)?;
let audio = try_parse_child::<_, Audio>(r, fields, ElementId::Audio)?;
let video = try_parse_child::<_, Video>(r, fields, ElementId::Video)?;
let content_encodings = try_parse_children::<_, ContentEncoding>(
r,
fields,
ElementId::ContentEncodings,
ElementId::ContentEncoding,
)?;
Ok(Self {
track_number,
track_uid,
track_type,
flag_enabled,
flag_default,
flag_forced,
flag_lacing,
default_duration,
name,
language,
codec_id,
codec_private,
codec_name,
codec_delay,
seek_pre_roll,
audio,
video,
content_encodings,
})
}
}
impl TrackEntry {
pub fn track_number(&self) -> NonZeroU64 {
self.track_number
}
pub fn track_uid(&self) -> NonZeroU64 {
self.track_uid
}
pub fn track_type(&self) -> TrackType {
self.track_type
}
pub fn flag_enabled(&self) -> bool {
self.flag_enabled
}
pub fn flag_default(&self) -> bool {
self.flag_default
}
pub fn flag_forced(&self) -> bool {
self.flag_forced
}
pub fn flag_lacing(&self) -> bool {
self.flag_lacing
}
pub fn default_duration(&self) -> Option<NonZeroU64> {
self.default_duration
}
pub fn name(&self) -> Option<&str> {
match self.name.as_ref() {
None => None,
Some(name) => Some(name),
}
}
pub fn language(&self) -> Option<&str> {
match self.language.as_ref() {
None => None,
Some(language) => Some(language),
}
}
pub fn codec_id(&self) -> &str {
&self.codec_id
}
pub fn codec_private(&self) -> Option<&[u8]> {
match self.codec_private.as_ref() {
None => None,
Some(data) => Some(data),
}
}
pub fn codec_name(&self) -> Option<&str> {
match self.codec_name.as_ref() {
None => None,
Some(codec_name) => Some(codec_name),
}
}
pub fn codec_delay(&self) -> Option<u64> {
self.codec_delay
}
pub fn seek_pre_roll(&self) -> Option<u64> {
self.seek_pre_roll
}
pub fn video(&self) -> Option<&Video> {
self.video.as_ref()
}
pub fn audio(&self) -> Option<&Audio> {
self.audio.as_ref()
}
pub fn content_encodings(&self) -> Option<&[ContentEncoding]> {
match &self.content_encodings {
None => None,
Some(content_encodings) => Some(content_encodings),
}
}
}
#[derive(Clone, Debug)]
pub struct Audio {
sampling_frequency: f64,
output_sampling_frequency: Option<f64>,
channels: NonZeroU64,
bit_depth: Option<NonZeroU64>,
}
impl<R: Read + Seek> ParsableElement<R> for Audio {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let sampling_frequency = find_float_or(fields, ElementId::SamplingFrequency, 8000.0)?;
let output_sampling_frequency = try_find_float(fields, ElementId::OutputSamplingFrequency)?;
let channels = find_nonzero_or(fields, ElementId::Channels, 1)?;
let bit_depth = try_find_nonzero(fields, ElementId::BitDepth)?;
if sampling_frequency < 0.0 {
return Err(DemuxError::PositiveValueIsNotPositive);
}
if let Some(output_sampling_frequency) = output_sampling_frequency {
if output_sampling_frequency < 0.0 {
return Err(DemuxError::PositiveValueIsNotPositive);
}
}
Ok(Audio {
sampling_frequency,
output_sampling_frequency,
channels,
bit_depth,
})
}
}
impl Audio {
pub fn sampling_frequency(&self) -> f64 {
self.sampling_frequency
}
pub fn output_sampling_frequency(&self) -> Option<f64> {
self.output_sampling_frequency
}
pub fn channels(&self) -> NonZeroU64 {
self.channels
}
pub fn bit_depth(&self) -> Option<NonZeroU64> {
self.bit_depth
}
}
#[derive(Clone, Debug)]
pub struct Video {
flag_interlaced: FlagInterlaced,
stereo_mode: Option<StereoMode>,
alpha_mode: Option<u64>,
pixel_width: NonZeroU64,
pixel_height: NonZeroU64,
pixel_crop_bottom: Option<u64>,
pixel_crop_top: Option<u64>,
pixel_crop_left: Option<u64>,
pixel_crop_right: Option<u64>,
display_width: Option<NonZeroU64>,
display_height: Option<NonZeroU64>,
display_unit: Option<DisplayUnit>,
aspect_ratio_type: Option<AspectRatioType>,
colour: Option<Colour>,
}
impl<R: Read + Seek> ParsableElement<R> for Video {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let flag_interlaced =
try_find_custom_type_or(fields, ElementId::FlagInterlaced, FlagInterlaced::Unknown)?;
let stereo_mode = try_find_custom_type(fields, ElementId::StereoMode)?;
let alpha_mode = try_find_unsigned(fields, ElementId::AlphaMode)?;
let pixel_width = find_nonzero(fields, ElementId::PixelWidth)?;
let pixel_height = find_nonzero(fields, ElementId::PixelHeight)?;
let pixel_crop_bottom = try_find_unsigned(fields, ElementId::PixelCropBottom)?;
let pixel_crop_top = try_find_unsigned(fields, ElementId::PixelCropTop)?;
let pixel_crop_left = try_find_unsigned(fields, ElementId::PixelCropLeft)?;
let pixel_crop_right = try_find_unsigned(fields, ElementId::PixelCropRight)?;
let display_width = try_find_nonzero(fields, ElementId::DisplayWidth)?;
let display_height = try_find_nonzero(fields, ElementId::DisplayHeight)?;
let display_unit = try_find_custom_type(fields, ElementId::DisplayUnit)?;
let aspect_ratio_type = try_find_custom_type(fields, ElementId::AspectRatioType)?;
let colour = try_parse_child::<_, Colour>(r, fields, ElementId::Colour)?;
Ok(Self {
flag_interlaced,
stereo_mode,
alpha_mode,
pixel_width,
pixel_height,
pixel_crop_bottom,
pixel_crop_top,
pixel_crop_left,
pixel_crop_right,
display_width,
display_height,
display_unit,
aspect_ratio_type,
colour,
})
}
}
impl Video {
pub fn flag_interlaced(&self) -> FlagInterlaced {
self.flag_interlaced
}
pub fn stereo_mode(&self) -> Option<StereoMode> {
self.stereo_mode
}
pub fn alpha_mode(&self) -> Option<u64> {
self.alpha_mode
}
pub fn pixel_width(&self) -> NonZeroU64 {
self.pixel_width
}
pub fn pixel_height(&self) -> NonZeroU64 {
self.pixel_height
}
pub fn pixel_crop_bottom(&self) -> Option<u64> {
self.pixel_crop_bottom
}
pub fn pixel_crop_top(&self) -> Option<u64> {
self.pixel_crop_top
}
pub fn pixel_crop_left(&self) -> Option<u64> {
self.pixel_crop_left
}
pub fn pixel_crop_right(&self) -> Option<u64> {
self.pixel_crop_right
}
pub fn display_width(&self) -> Option<NonZeroU64> {
self.display_width
}
pub fn display_height(&self) -> Option<NonZeroU64> {
self.display_height
}
pub fn display_unit(&self) -> Option<DisplayUnit> {
self.display_unit
}
pub fn aspect_ratio_type(&self) -> Option<AspectRatioType> {
self.aspect_ratio_type
}
pub fn colour(&self) -> Option<&Colour> {
self.colour.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct Colour {
matrix_coefficients: Option<MatrixCoefficients>,
bits_per_channel: Option<u64>,
chroma_subsampling_horz: Option<u64>,
chroma_subsampling_vert: Option<u64>,
cb_subsampling_horz: Option<u64>,
cb_subsampling_vert: Option<u64>,
chroma_sitting_horz: Option<ChromaSitingHorz>,
chroma_sitting_vert: Option<ChromaSitingVert>,
range: Option<Range>,
transfer_characteristics: Option<TransferCharacteristics>,
primaries: Option<Primaries>,
max_cll: Option<u64>,
max_fall: Option<u64>,
mastering_metadata: Option<MasteringMetadata>,
}
impl<R: Read + Seek> ParsableElement<R> for Colour {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let matrix_coefficients = try_find_custom_type(fields, ElementId::MatrixCoefficients)?;
let bits_per_channel = try_find_unsigned(fields, ElementId::BitsPerChannel)?;
let chroma_subsampling_horz = try_find_unsigned(fields, ElementId::ChromaSubsamplingHorz)?;
let chroma_subsampling_vert = try_find_unsigned(fields, ElementId::ChromaSubsamplingVert)?;
let cb_subsampling_horz = try_find_unsigned(fields, ElementId::CbSubsamplingHorz)?;
let cb_subsampling_vert = try_find_unsigned(fields, ElementId::CbSubsamplingVert)?;
let chroma_sitting_horz = try_find_custom_type(fields, ElementId::ChromaSitingHorz)?;
let chroma_sitting_vert = try_find_custom_type(fields, ElementId::ChromaSitingVert)?;
let range = try_find_custom_type(fields, ElementId::Range)?;
let transfer_characteristics =
try_find_custom_type(fields, ElementId::TransferCharacteristics)?;
let primaries = try_find_custom_type(fields, ElementId::Primaries)?;
let max_cll = try_find_unsigned(fields, ElementId::MatrixCoefficients)?;
let max_fall = try_find_unsigned(fields, ElementId::MatrixCoefficients)?;
let mastering_metadata =
try_parse_child::<_, MasteringMetadata>(r, fields, ElementId::MasteringMetadata)?;
Ok(Self {
matrix_coefficients,
bits_per_channel,
chroma_subsampling_horz,
chroma_subsampling_vert,
cb_subsampling_horz,
cb_subsampling_vert,
chroma_sitting_horz,
chroma_sitting_vert,
range,
transfer_characteristics,
primaries,
max_cll,
max_fall,
mastering_metadata,
})
}
}
impl Colour {
pub fn matrix_coefficients(&self) -> Option<MatrixCoefficients> {
self.matrix_coefficients
}
pub fn bits_per_channel(&self) -> Option<u64> {
self.bits_per_channel
}
pub fn chroma_subsampling_horz(&self) -> Option<u64> {
self.chroma_subsampling_horz
}
pub fn chroma_subsampling_vert(&self) -> Option<u64> {
self.chroma_subsampling_vert
}
pub fn cb_subsampling_horz(&self) -> Option<u64> {
self.cb_subsampling_horz
}
pub fn cb_subsampling_vert(&self) -> Option<u64> {
self.cb_subsampling_vert
}
pub fn chroma_sitting_horz(&self) -> Option<ChromaSitingHorz> {
self.chroma_sitting_horz
}
pub fn chroma_sitting_vert(&self) -> Option<ChromaSitingVert> {
self.chroma_sitting_vert
}
pub fn range(&self) -> Option<Range> {
self.range
}
pub fn transfer_characteristics(&self) -> Option<TransferCharacteristics> {
self.transfer_characteristics
}
pub fn primaries(&self) -> Option<Primaries> {
self.primaries
}
pub fn max_cll(&self) -> Option<u64> {
self.max_cll
}
pub fn max_fall(&self) -> Option<u64> {
self.max_fall
}
pub fn mastering_metadata(&self) -> Option<&MasteringMetadata> {
self.mastering_metadata.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct MasteringMetadata {
primary_r_chromaticity_x: Option<f64>,
primary_r_chromaticity_y: Option<f64>,
primary_g_chromaticity_x: Option<f64>,
primary_g_chromaticity_y: Option<f64>,
primary_b_chromaticity_x: Option<f64>,
primary_b_chromaticity_y: Option<f64>,
white_point_chromaticity_x: Option<f64>,
white_point_chromaticity_y: Option<f64>,
luminance_max: Option<f64>,
luminance_min: Option<f64>,
}
impl<R: Read + Seek> ParsableElement<R> for MasteringMetadata {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let primary_r_chromaticity_x = try_find_float(fields, ElementId::PrimaryRChromaticityX)?;
let primary_r_chromaticity_y = try_find_float(fields, ElementId::PrimaryRChromaticityX)?;
let primary_g_chromaticity_x = try_find_float(fields, ElementId::PrimaryGChromaticityX)?;
let primary_g_chromaticity_y = try_find_float(fields, ElementId::PrimaryGChromaticityX)?;
let primary_b_chromaticity_x = try_find_float(fields, ElementId::PrimaryBChromaticityX)?;
let primary_b_chromaticity_y = try_find_float(fields, ElementId::PrimaryBChromaticityX)?;
let white_point_chromaticity_x =
try_find_float(fields, ElementId::WhitePointChromaticityX)?;
let white_point_chromaticity_y =
try_find_float(fields, ElementId::WhitePointChromaticityY)?;
let luminance_max = try_find_float(fields, ElementId::LuminanceMax)?;
let luminance_min = try_find_float(fields, ElementId::LuminanceMin)?;
Ok(Self {
primary_r_chromaticity_x,
primary_r_chromaticity_y,
primary_g_chromaticity_x,
primary_g_chromaticity_y,
primary_b_chromaticity_x,
primary_b_chromaticity_y,
white_point_chromaticity_x,
white_point_chromaticity_y,
luminance_max,
luminance_min,
})
}
}
impl MasteringMetadata {
pub fn primary_r_chromaticity_x(&self) -> Option<f64> {
self.primary_r_chromaticity_x
}
pub fn primary_r_chromaticity_y(&self) -> Option<f64> {
self.primary_r_chromaticity_y
}
pub fn primary_g_chromaticity_x(&self) -> Option<f64> {
self.primary_g_chromaticity_x
}
pub fn primary_g_chromaticity_y(&self) -> Option<f64> {
self.primary_g_chromaticity_y
}
pub fn primary_b_chromaticity_x(&self) -> Option<f64> {
self.primary_g_chromaticity_x
}
pub fn primary_b_chromaticity_y(&self) -> Option<f64> {
self.primary_g_chromaticity_y
}
pub fn white_point_chromaticity_x(&self) -> Option<f64> {
self.primary_g_chromaticity_x
}
pub fn white_point_chromaticity_y(&self) -> Option<f64> {
self.primary_g_chromaticity_y
}
pub fn luminance_max(&self) -> Option<f64> {
self.luminance_max
}
pub fn luminance_min(&self) -> Option<f64> {
self.luminance_min
}
}
#[derive(Clone, Debug)]
pub struct ContentEncoding {
order: u64,
scope: u64,
encoding_type: ContentEncodingType,
encryption: Option<ContentEncryption>,
}
impl<R: Read + Seek> ParsableElement<R> for ContentEncoding {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let order = find_unsigned_or(fields, ElementId::ContentEncodingOrder, 0)?;
let scope = find_unsigned_or(fields, ElementId::ContentEncodingScope, 1)?;
let encoding_type = try_find_custom_type_or(
fields,
ElementId::ContentEncodingType,
ContentEncodingType::Compression,
)?;
let encryption =
try_parse_child::<_, ContentEncryption>(r, fields, ElementId::ContentEncryption)?;
Ok(Self {
order,
scope,
encoding_type,
encryption,
})
}
}
impl ContentEncoding {
pub fn order(&self) -> u64 {
self.order
}
pub fn scope(&self) -> u64 {
self.scope
}
pub fn encoding_type(&self) -> ContentEncodingType {
self.encoding_type
}
pub fn encryption(&self) -> Option<&ContentEncryption> {
self.encryption.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct ContentEncryption {
algo: ContentEncAlgo,
key_id: Option<Vec<u8>>,
aes_settings: Option<ContentEncAesSettings>,
}
impl<R: Read + Seek> ParsableElement<R> for ContentEncryption {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let algo = try_find_custom_type_or(
fields,
ElementId::ContentEncAlgo,
ContentEncAlgo::NotEncrypted,
)?;
let key_id = try_find_binary(r, fields, ElementId::ContentEncKeyId)?;
let aes_settings = try_parse_child::<_, ContentEncAesSettings>(
r,
fields,
ElementId::ContentEncAesSettings,
)?;
Ok(Self {
algo,
key_id,
aes_settings,
})
}
}
impl ContentEncryption {
pub fn algo(&self) -> ContentEncAlgo {
self.algo
}
pub fn key_id(&self) -> Option<&[u8]> {
match self.key_id.as_ref() {
None => None,
Some(key_id) => Some(key_id),
}
}
pub fn aes_settings(&self) -> Option<&ContentEncAesSettings> {
self.aes_settings.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct ContentEncAesSettings {
aes_settings_cipher_mode: Option<AesSettingsCipherMode>,
}
impl<R: Read + Seek> ParsableElement<R> for ContentEncAesSettings {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let aes_settings_cipher_mode =
try_find_custom_type(fields, ElementId::AesSettingsCipherMode)?;
Ok(Self {
aes_settings_cipher_mode,
})
}
}
impl ContentEncAesSettings {
pub fn aes_settings_cipher_mode(&self) -> Option<AesSettingsCipherMode> {
self.aes_settings_cipher_mode
}
}
#[derive(Clone, Debug)]
pub struct EditionEntry {
chapter_atoms: Vec<ChapterAtom>,
}
impl<R: Read + Seek> ParsableElement<R> for EditionEntry {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let chapter_atoms =
find_children_in_fields::<_, ChapterAtom>(r, fields, ElementId::ChapterAtom)?;
Ok(Self { chapter_atoms })
}
}
impl EditionEntry {
pub fn chapter_atoms(&self) -> &[ChapterAtom] {
self.chapter_atoms.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct ChapterAtom {
uid: NonZeroU64,
string_uid: Option<String>,
time_start: u64,
time_end: Option<u64>,
displays: Vec<ChapterDisplay>,
}
impl<R: Read + Seek> ParsableElement<R> for ChapterAtom {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let uid = find_nonzero(fields, ElementId::ChapterUid)?;
let string_uid = try_find_string(fields, ElementId::ChapterStringUid)?;
let time_start = find_unsigned(fields, ElementId::ChapterTimeStart)?;
let time_end = try_find_unsigned(fields, ElementId::ChapterTimeEnd)?;
let displays =
find_children_in_fields::<_, ChapterDisplay>(r, fields, ElementId::ChapterDisplay)?;
Ok(Self {
uid,
string_uid,
time_start,
time_end,
displays,
})
}
}
impl ChapterAtom {
pub fn uid(&self) -> NonZeroU64 {
self.uid
}
pub fn string_uid(&self) -> Option<&str> {
match self.string_uid.as_ref() {
None => None,
Some(string_uid) => Some(string_uid),
}
}
pub fn time_start(&self) -> u64 {
self.time_start
}
pub fn time_end(&self) -> Option<u64> {
self.time_end
}
pub fn displays(&self) -> &[ChapterDisplay] {
self.displays.as_ref()
}
}
#[derive(Clone, Debug)]
pub struct ChapterDisplay {
string: String,
language: Option<String>,
language_ietf: Option<String>,
country: Option<String>,
}
impl<R: Read + Seek> ParsableElement<R> for ChapterDisplay {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let string = find_string(fields, ElementId::ChapString)?;
let language = try_find_string(fields, ElementId::ChapLanguage)?;
let language_ietf = try_find_string(fields, ElementId::ChapLanguageIetf)?;
let country = try_find_string(fields, ElementId::ChapCountry)?;
Ok(Self {
string,
language,
language_ietf,
country,
})
}
}
impl ChapterDisplay {
pub fn string(&self) -> &str {
self.string.as_ref()
}
pub fn language(&self) -> Option<&str> {
match self.language.as_ref() {
None => None,
Some(language) => Some(language),
}
}
pub fn language_ietf(&self) -> Option<&str> {
match self.language_ietf.as_ref() {
None => None,
Some(language_ietf) => Some(language_ietf),
}
}
pub fn country(&self) -> Option<&str> {
match self.country.as_ref() {
None => None,
Some(country) => Some(country),
}
}
}
#[derive(Clone, Debug)]
pub struct Tag {
targets: Option<Targets>,
simple_tags: Vec<SimpleTag>,
}
impl<R: Read + Seek> ParsableElement<R> for Tag {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let targets = try_parse_child::<_, Targets>(r, fields, ElementId::Targets)?;
let simple_tags = find_children_in_fields::<_, SimpleTag>(r, fields, ElementId::SimpleTag)?;
Ok(Self {
targets,
simple_tags,
})
}
}
impl Tag {
pub fn targets(&self) -> Option<&Targets> {
self.targets.as_ref()
}
pub fn simple_tags(&self) -> &[SimpleTag] {
self.simple_tags.as_slice()
}
}
#[derive(Clone, Debug)]
pub struct Targets {
target_type_value: Option<u64>,
target_type: Option<String>,
tag_track_uid: Option<u64>,
}
impl<R: Read + Seek> ParsableElement<R> for Targets {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let target_type_value = try_find_unsigned(fields, ElementId::TargetTypeValue)?;
let target_type = try_find_string(fields, ElementId::TargetType)?;
let tag_track_uid = try_find_unsigned(fields, ElementId::TagTrackUid)?;
Ok(Self {
target_type_value,
target_type,
tag_track_uid,
})
}
}
impl Targets {
pub fn target_type_value(&self) -> Option<u64> {
self.target_type_value
}
pub fn tag_track_uid(&self) -> Option<u64> {
self.tag_track_uid
}
}
#[derive(Clone, Debug)]
pub struct SimpleTag {
name: String,
language: Option<String>,
default: Option<bool>,
string: Option<String>,
binary: Option<Vec<u8>>,
}
impl<R: Read + Seek> ParsableElement<R> for SimpleTag {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let name = find_string(fields, ElementId::TagName)?;
let language = try_find_string(fields, ElementId::TagLanguage)?;
let default = try_find_bool(fields, ElementId::TagDefault)?;
let string = try_find_string(fields, ElementId::TagString)?;
let binary = try_find_binary(r, fields, ElementId::TagBinary)?;
Ok(Self {
name,
language,
default,
string,
binary,
})
}
}
impl SimpleTag {
pub fn name(&self) -> &str {
&self.name
}
pub fn language(&self) -> Option<&str> {
match self.language.as_ref() {
None => None,
Some(language) => Some(language),
}
}
pub fn default(&self) -> Option<bool> {
self.default
}
pub fn string(&self) -> Option<&str> {
match self.string.as_ref() {
None => None,
Some(string) => Some(string),
}
}
pub fn binary(&self) -> Option<&[u8]> {
match self.binary.as_ref() {
None => None,
Some(binary) => Some(binary),
}
}
}
#[derive(Clone, Copy, Debug)]
struct SeekEntry {
id: ElementId,
offset: u64,
}
impl<R: Read + Seek> ParsableElement<R> for SeekEntry {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let id: u32 = find_unsigned(fields, ElementId::SeekId)?.try_into()?;
let id = *ID_TO_ELEMENT_ID.get(&id).unwrap_or(&ElementId::Unknown);
let offset = find_unsigned(fields, ElementId::SeekPosition)?;
Ok(Self { id, offset })
}
}
#[derive(Clone, Debug)]
struct CuePoint {
time: u64,
track_position: CueTrackPositions,
}
impl<R: Read + Seek> ParsableElement<R> for CuePoint {
type Output = Self;
fn new(r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let time = find_unsigned(fields, ElementId::CueTime)?;
let track_position =
parse_child::<_, CueTrackPositions>(r, fields, ElementId::CueTrackPositions)?;
Ok(Self {
time,
track_position,
})
}
}
#[derive(Clone, Debug)]
struct CueTrackPositions {
track: u64,
cluster_position: u64,
relative_position: Option<u64>,
duration: Option<u64>,
block_number: Option<u64>,
}
impl<R: Read + Seek> ParsableElement<R> for CueTrackPositions {
type Output = Self;
fn new(_r: &mut R, fields: &[(ElementId, ElementData)]) -> Result<Self> {
let track = find_unsigned(fields, ElementId::CueTrack)?;
let cluster_position = find_unsigned(fields, ElementId::CueClusterPosition)?;
let relative_position = try_find_unsigned(fields, ElementId::CueRelativePosition)?;
let duration = try_find_unsigned(fields, ElementId::CueDuration)?;
let block_number = try_find_unsigned(fields, ElementId::CueBlockNumber)?;
Ok(Self {
track,
cluster_position,
relative_position,
duration,
block_number,
})
}
}
#[derive(Clone, Debug)]
pub struct MatroskaFile<R> {
file: R,
ebml_header: EbmlHeader,
seek_head: HashMap<ElementId, u64>,
info: Info,
tracks: Vec<TrackEntry>,
cue_points: Option<Vec<CuePoint>>,
chapters: Option<Vec<EditionEntry>>,
tags: Option<Vec<Tag>>,
cluster_timestamp: u64,
queued_frames: VecDeque<LacedFrame>,
}
impl<R: Read + Seek> MatroskaFile<R> {
pub fn open(mut file: R) -> Result<Self> {
let ebml_header = parse_ebml_header(&mut file)?;
let (segment_data_offset, _) = expect_master(&mut file, ElementId::Segment, None)?;
let optional_seek_head = search_seek_head(&mut file, segment_data_offset)?;
let mut seek_head = parse_seek_head(&mut file, segment_data_offset, optional_seek_head)?;
if seek_head.is_empty() {
build_seek_head(&mut file, segment_data_offset, &mut seek_head)?;
}
if seek_head.get(&ElementId::Cluster).is_none() {
find_first_cluster_offset(&mut file, &mut seek_head)?;
}
let info = parse_segment_info(&mut file, &seek_head)?;
let tracks = try_parse_top_element_collection::<_, TrackEntry>(
&mut file,
&seek_head,
ElementId::Tracks,
ElementId::TrackEntry,
)?
.ok_or(DemuxError::ElementNotFound(ElementId::Tracks))?;
let mut cue_points = try_parse_top_element_collection::<_, CuePoint>(
&mut file,
&seek_head,
ElementId::Cues,
ElementId::CuePoint,
)?;
if let Some(cue_points) = cue_points.as_mut() {
cue_points
.iter_mut()
.for_each(|p| p.track_position.cluster_position += segment_data_offset);
}
let chapters = try_parse_top_element_collection::<_, EditionEntry>(
&mut file,
&seek_head,
ElementId::Chapters,
ElementId::EditionEntry,
)?;
let tags = try_parse_top_element_collection::<_, Tag>(
&mut file,
&seek_head,
ElementId::Tags,
ElementId::Tag,
)?;
seek_to_first_cluster(&mut file, &seek_head)?;
Ok(Self {
file,
ebml_header,
seek_head,
info,
tracks,
cue_points,
chapters,
tags,
cluster_timestamp: 0,
queued_frames: VecDeque::with_capacity(8),
})
}
pub fn ebml_header(&self) -> &EbmlHeader {
&self.ebml_header
}
pub fn info(&self) -> &Info {
&self.info
}
pub fn tracks(&self) -> &[TrackEntry] {
self.tracks.as_ref()
}
pub fn chapters(&self) -> Option<&[EditionEntry]> {
match self.chapters.as_ref() {
None => None,
Some(chapters) => Some(chapters),
}
}
pub fn tags(&self) -> Option<&[Tag]> {
match self.tags.as_ref() {
None => None,
Some(tags) => Some(tags),
}
}
pub fn next_frame(&mut self, frame: &mut Frame) -> Result<bool> {
if self.try_pop_frame(frame)? {
return Ok(true);
};
loop {
match next_element(&mut self.file) {
Ok((element_id, element_data)) => match element_id {
ElementId::Cluster | ElementId::BlockGroup => {
if let ElementData::Location { offset, .. } = element_data {
let _ = self.file.seek(SeekFrom::Start(offset))?;
} else {
return Err(DemuxError::UnexpectedDataType);
}
}
ElementId::Timestamp => {
if let ElementData::Unsigned(timestamp) = element_data {
self.cluster_timestamp = timestamp;
} else {
return Err(DemuxError::UnexpectedDataType);
}
}
ElementId::SimpleBlock | ElementId::Block => {
if let ElementData::Location { offset, size } = element_data {
let _ = self.file.seek(SeekFrom::Start(offset))?;
parse_block_header(
&mut self.file,
&mut self.queued_frames,
size,
self.cluster_timestamp,
element_id == ElementId::SimpleBlock,
)?;
let _ = self.try_pop_frame(frame)?;
return Ok(true);
} else {
return Err(DemuxError::UnexpectedDataType);
}
}
_ => { }
},
Err(err) => {
if let Some(err) = err.source() {
if err.downcast_ref::<std::io::Error>().is_some() {
return Ok(false);
}
}
return Err(err);
}
}
}
}
fn try_pop_frame(&mut self, frame: &mut Frame) -> Result<bool> {
if let Some(queued_frame) = self.queued_frames.pop_front() {
let size: usize = queued_frame.size.try_into()?;
if frame.data.len() < size {
frame.data.resize(size, 0_u8);
}
frame.timestamp = queued_frame.timestamp;
frame.track = queued_frame.track;
frame.is_discardable = queued_frame.is_discardable;
frame.is_invisible = queued_frame.is_invisible;
frame.is_keyframe = queued_frame.is_keyframe;
self.file.read_exact(&mut frame.data[0..size])?;
Ok(true)
} else {
Ok(false)
}
}
pub fn seek(&mut self, timestamp: u64) -> Result<()> {
self.cluster_timestamp = 0;
self.queued_frames.clear();
if let Some(cue_points) = self.cue_points.as_ref() {
let seek_pos = match cue_points.binary_search_by(|p| p.time.cmp(×tamp)) {
Ok(seek_pos) => seek_pos,
Err(seek_pos) => seek_pos - 1,
};
if let Some(point) = cue_points.get(seek_pos) {
let _ = self
.file
.seek(SeekFrom::Start(point.track_position.cluster_position));
}
} else {
let cluster_start = self
.seek_head
.get(&ElementId::Cluster)
.ok_or(DemuxError::CantFindCluster)?;
let _ = self.file.seek(SeekFrom::Start(*cluster_start));
}
Ok(())
}
}
fn parse_ebml_header<R: Read + Seek>(r: &mut R) -> Result<EbmlHeader> {
let (master_offset, master_size) = expect_master(r, ElementId::Ebml, None)?;
let master_children = collect_children(r, master_offset, master_size)?;
let header = EbmlHeader::new(r, &master_children)?;
Ok(header)
}
fn parse_seek_head<R: Read + Seek>(
mut file: &mut R,
segment_data_offset: u64,
optional_seek_head: Option<(u64, u64)>,
) -> Result<HashMap<ElementId, u64>> {
let mut seek_head = HashMap::new();
if let Some((seek_head_data_offset, seek_head_data_size)) = optional_seek_head {
let seek_head_entries =
collect_children(&mut file, seek_head_data_offset, seek_head_data_size)?;
for (entry_id, entry_data) in &seek_head_entries {
if let ElementId::Seek = entry_id {
if let ElementData::Location { offset, size } = entry_data {
let seek_fields = collect_children(&mut file, *offset, *size)?;
if let Ok(seek_entry) = SeekEntry::new(&mut file, &seek_fields) {
let _ = seek_head
.insert(seek_entry.id, segment_data_offset + seek_entry.offset);
}
}
}
}
}
Ok(seek_head)
}
fn search_seek_head<R: Read + Seek>(
r: &mut R,
segment_data_offset: u64,
) -> Result<Option<(u64, u64)>> {
loop {
let (element_id, size) = parse_element_header(r, Some(segment_data_offset))?;
match element_id {
ElementId::SeekHead => {
let current_pos = r.stream_position()?;
return Ok(Some((current_pos, size)));
}
ElementId::Crc32 => continue,
_ => return Ok(None),
}
}
}
fn build_seek_head<R: Read + Seek>(
r: &mut R,
segment_data_offset: u64,
seek_head: &mut HashMap<ElementId, u64>,
) -> Result<()> {
let _ = r.seek(SeekFrom::Start(segment_data_offset))?;
loop {
let position = r.stream_position()?;
match next_element(r) {
Ok((element_id, _)) => {
if element_id == ElementId::Info
|| element_id == ElementId::Tracks
|| element_id == ElementId::Chapters
|| element_id == ElementId::Cues
|| element_id == ElementId::Tags
|| element_id == ElementId::Cluster
{
if element_id != ElementId::Cluster
|| !seek_head.contains_key(&ElementId::Cluster)
{
let _ = seek_head.insert(element_id, position);
}
}
}
Err(_) => {
break;
}
}
}
Ok(())
}
fn find_first_cluster_offset<R: Read + Seek>(
r: &mut R,
seek_head: &mut HashMap<ElementId, u64>,
) -> Result<()> {
let (tracks_offset, tracks_size) = if let Some(offset) = seek_head.get(&ElementId::Tracks) {
expect_master(r, ElementId::Tracks, Some(*offset))?
} else {
return Err(DemuxError::CantFindCluster);
};
let _ = r.seek(SeekFrom::Start(tracks_offset + tracks_size))?;
loop {
match next_element(r) {
Ok((element_id, element_data)) => {
if let ElementId::Cluster = element_id {
if let ElementData::Location { offset, .. } = element_data {
let _ = seek_head.insert(ElementId::Cluster, offset);
break;
} else {
return Err(DemuxError::UnexpectedDataType);
}
}
if let ElementData::Location { size, .. } = element_data {
if size == u64::MAX {
return Err(DemuxError::CantFindCluster);
}
}
}
Err(_) => {
return Err(DemuxError::CantFindCluster);
}
}
}
Ok(())
}
fn parse_segment_info<R: Read + Seek>(
r: &mut R,
seek_head: &HashMap<ElementId, u64>,
) -> Result<Info> {
if let Some(offset) = seek_head.get(&ElementId::Info) {
let (info_data_offset, info_data_size) = expect_master(r, ElementId::Info, Some(*offset))?;
let child_fields = collect_children(r, info_data_offset, info_data_size)?;
let info = Info::new(r, &child_fields)?;
Ok(info)
} else {
Err(DemuxError::ElementNotFound(ElementId::Info))
}
}
fn try_parse_top_element_collection<R, T>(
r: &mut R,
seek_head: &HashMap<ElementId, u64>,
master_id: ElementId,
child_id: ElementId,
) -> Result<Option<Vec<T::Output>>>
where
R: Read + Seek,
T: ParsableElement<R>,
{
let cue_points = if let Some(offset) = seek_head.get(&master_id) {
let cue_points = parse_children_at_offset::<_, T>(r, *offset, master_id, child_id)?;
Some(cue_points)
} else {
None
};
Ok(cue_points)
}
fn find_children_in_fields<R, T>(
r: &mut R,
fields: &[(ElementId, ElementData)],
child_id: ElementId,
) -> Result<Vec<T::Output>>
where
R: Read + Seek,
T: ParsableElement<R>,
{
let mut children = vec![];
for (_, data) in fields.iter().filter(|(id, _)| *id == child_id) {
if let ElementData::Location { offset, size } = data {
let child_fields = collect_children(r, *offset, *size)?;
let child = T::new(r, &child_fields)?;
children.push(child);
} else {
return Err(DemuxError::UnexpectedDataType);
}
}
Ok(children)
}
fn seek_to_first_cluster<R: Read + Seek>(
r: &mut R,
seek_head: &HashMap<ElementId, u64>,
) -> Result<()> {
if let Some(offset) = seek_head.get(&ElementId::Cluster) {
let _ = r.seek(SeekFrom::Start(*offset))?;
Ok(())
} else {
Err(DemuxError::ElementNotFound(ElementId::Cluster))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::panic)]
use std::io::Cursor;
use super::*;
#[test]
fn test_parse_ebml_header() -> Result<()> {
let data: Vec<u8> = vec![
0x1A, 0x45, 0xDF, 0xA3, 0xA2, 0x42, 0x86, 0x81, 0x01, 0x42, 0xF7, 0x81, 0x01, 0x42,
0xF2, 0x81, 0x04, 0x42, 0xF3, 0x81, 0x08, 0x42, 0x82, 0x88, 0x6D, 0x61, 0x74, 0x72,
0x6F, 0x73, 0x6B, 0x61, 0x42, 0x87, 0x81, 0x04, 0x42, 0x85, 0x81, 0x02,
];
let mut cursor = Cursor::new(data);
let ebml_header = parse_ebml_header(&mut cursor)?;
assert_eq!(ebml_header.version, Some(1));
assert_eq!(ebml_header.read_version, Some(1));
assert_eq!(ebml_header.max_id_length, 4);
assert_eq!(ebml_header.max_size_length, 8);
assert_eq!(&ebml_header.doc_type, "matroska");
assert_eq!(ebml_header.doc_type_version, 4);
assert_eq!(ebml_header.doc_type_read_version, 2);
Ok(())
}
}