mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 06:02:32 +00:00
EBML: Specify most properties we'll end up reading
Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com>
This commit is contained in:
parent
f783dd0aa2
commit
daef1a2b4a
2 changed files with 223 additions and 5 deletions
|
@ -11,29 +11,247 @@ pub struct EbmlHeaderProperties {
|
|||
pub(crate) doc_type_read_version: u64,
|
||||
}
|
||||
|
||||
impl EbmlHeaderProperties {
|
||||
/// The EBML version, should be `1`
|
||||
pub fn version(&self) -> u64 {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// The minimum EBML version required to read the file, <= [`version`]
|
||||
pub fn read_version(&self) -> u64 {
|
||||
self.read_version
|
||||
}
|
||||
|
||||
/// The maximum length of an EBML element ID, in octets
|
||||
pub fn max_id_length(&self) -> u8 {
|
||||
self.max_id_length
|
||||
}
|
||||
|
||||
/// The maximum length of an EBML element size, in octets
|
||||
pub fn max_size_length(&self) -> u8 {
|
||||
self.max_size_length
|
||||
}
|
||||
|
||||
/// A string that describes the type of document
|
||||
pub fn doc_type(&self) -> &str {
|
||||
&self.doc_type
|
||||
}
|
||||
|
||||
/// The version of DocType interpreter used to create the EBML Document
|
||||
pub fn doc_type_version(&self) -> u64 {
|
||||
self.doc_type_version
|
||||
}
|
||||
|
||||
/// The minimum DocType interpreter version needed to read the EBML Document
|
||||
pub fn doc_type_read_version(&self) -> u64 {
|
||||
self.doc_type_read_version
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct EbmlExtension {
|
||||
pub(crate) name: String,
|
||||
pub(crate) version: u64,
|
||||
}
|
||||
|
||||
impl EbmlExtension {
|
||||
/// The name of the extension
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The version of the extension
|
||||
pub fn version(&self) -> u64 {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct SegmentInfo {
|
||||
pub(crate) timecode_scale: u64,
|
||||
pub(crate) timestamp_scale: u64,
|
||||
pub(crate) muxing_app: String,
|
||||
pub(crate) writing_app: String,
|
||||
}
|
||||
|
||||
impl SegmentInfo {
|
||||
/// Base unit for Segment Ticks and Track Ticks, in nanoseconds.
|
||||
///
|
||||
/// A TimestampScale value of 1000000 means scaled timestamps in the Segment are expressed in milliseconds.
|
||||
pub fn timestamp_scale(&self) -> u64 {
|
||||
self.timestamp_scale
|
||||
}
|
||||
|
||||
/// Muxing application or library (example: "libmatroska-0.4.3").
|
||||
///
|
||||
/// Includes the full name of the application or library followed by the version number.
|
||||
pub fn muxing_app(&self) -> &str {
|
||||
&self.muxing_app
|
||||
}
|
||||
|
||||
/// Writing application (example: "mkvmerge-0.3.3").
|
||||
///
|
||||
/// Includes the full name of the application followed by the version number.
|
||||
pub fn writing_app(&self) -> &str {
|
||||
&self.writing_app
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct AudioTrackDescriptor {
|
||||
pub(crate) number: u64,
|
||||
pub(crate) uid: u64,
|
||||
pub(crate) language: String,
|
||||
pub(crate) default_duration: u64,
|
||||
pub(crate) codec_id: String,
|
||||
pub(crate) codec_private: Vec<u8>,
|
||||
pub(crate) settings: AudioTrackSettings,
|
||||
}
|
||||
|
||||
impl AudioTrackDescriptor {
|
||||
/// The track number
|
||||
pub fn number(&self) -> u64 {
|
||||
self.number
|
||||
}
|
||||
|
||||
/// A unique ID to identify the track
|
||||
pub fn uid(&self) -> u64 {
|
||||
self.uid
|
||||
}
|
||||
|
||||
/// The language of the track, in the Matroska languages form
|
||||
///
|
||||
/// NOTE: See [basics](https://matroska.org/technical/basics.html#language-codes) on language codes.
|
||||
pub fn language(&self) -> &str {
|
||||
&self.language
|
||||
}
|
||||
|
||||
/// The default duration of the track
|
||||
pub fn default_duration(&self) -> u64 {
|
||||
self.default_duration
|
||||
}
|
||||
|
||||
/// The codec ID of the track
|
||||
///
|
||||
/// NOTE: See [Matroska codec RFC] for more info.
|
||||
///
|
||||
/// [Matroska codec RFC]: https://matroska.org/technical/codec_specs.html
|
||||
pub fn codec_id(&self) -> &str {
|
||||
&self.codec_id
|
||||
}
|
||||
|
||||
/// Private data only known to the codec
|
||||
pub fn codec_private(&self) -> &[u8] {
|
||||
&self.codec_private
|
||||
}
|
||||
|
||||
/// The audio settings of the track
|
||||
pub fn settings(&self) -> &AudioTrackSettings {
|
||||
&self.settings
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct AudioTrackSettings {
|
||||
pub(crate) sampling_frequency: u32,
|
||||
pub(crate) output_sampling_frequency: u32,
|
||||
pub(crate) channels: u8,
|
||||
pub(crate) bit_depth: Option<u8>,
|
||||
pub(crate) emphasis: Option<EbmlAudioTrackEmphasis>,
|
||||
}
|
||||
|
||||
impl AudioTrackSettings {
|
||||
/// The sampling frequency of the track
|
||||
pub fn sampling_frequency(&self) -> u32 {
|
||||
self.sampling_frequency
|
||||
}
|
||||
|
||||
/// Real output sampling frequency in Hz (used for SBR techniques).
|
||||
///
|
||||
/// The default value for `output_sampling_frequency` of the same TrackEntry is equal to the [`Self::sampling_frequency`].
|
||||
pub fn output_sampling_frequency(&self) -> u32 {
|
||||
self.output_sampling_frequency
|
||||
}
|
||||
|
||||
/// The number of channels in the track
|
||||
pub fn channels(&self) -> u8 {
|
||||
self.channels
|
||||
}
|
||||
|
||||
/// The bit depth of the track
|
||||
pub fn bit_depth(&self) -> Option<u8> {
|
||||
self.bit_depth
|
||||
}
|
||||
|
||||
/// Audio emphasis applied on audio samples
|
||||
pub fn emphasis(&self) -> Option<EbmlAudioTrackEmphasis> {
|
||||
self.emphasis
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum EbmlAudioTrackEmphasis {
|
||||
None = 0,
|
||||
CdAudio = 1,
|
||||
Reserved = 2,
|
||||
CcitJ17 = 3,
|
||||
Fm50 = 4,
|
||||
Fm75 = 5,
|
||||
PhonoRiaa = 10,
|
||||
PhonoIecN78 = 11,
|
||||
PhonoTeldec = 12,
|
||||
PhonoEmi = 13,
|
||||
PhonoColumbiaLp = 14,
|
||||
PhonoLondon = 15,
|
||||
PhonoNartb = 16,
|
||||
}
|
||||
|
||||
/// EBML audio properties
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct EbmlProperties {
|
||||
pub(crate) header: EbmlHeaderProperties,
|
||||
pub(crate) extensions: Vec<EbmlExtension>,
|
||||
pub(crate) segment_info: SegmentInfo,
|
||||
pub(crate) default_audio_track: AudioTrackDescriptor,
|
||||
}
|
||||
|
||||
impl EbmlProperties {
|
||||
/// The EBML header properties
|
||||
///
|
||||
/// This includes the properties that are part of the EBML base specification.
|
||||
/// All Matroska-specific properties are in [`Self::segment_info`] and [`Self::default_audio_track`].
|
||||
pub fn header(&self) -> &EbmlHeaderProperties {
|
||||
&self.header
|
||||
}
|
||||
|
||||
/// The DocType extensions
|
||||
pub fn extensions(&self) -> &[EbmlExtension] {
|
||||
&self.extensions
|
||||
}
|
||||
|
||||
/// Information from the Matroska `\EBML\Segment\Info` element
|
||||
pub fn segment_info(&self) -> &SegmentInfo {
|
||||
&self.segment_info
|
||||
}
|
||||
|
||||
/// Information about the default audio track
|
||||
///
|
||||
/// The information is extracted from the first audio track with its default flag set
|
||||
/// in the Matroska `\EBML\Segment\Tracks` element.
|
||||
pub fn default_audio_track(&self) -> &AudioTrackDescriptor {
|
||||
&self.default_audio_track
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EbmlProperties> for FileProperties {
|
||||
fn from(_input: EbmlProperties) -> Self {
|
||||
todo!()
|
||||
fn from(input: EbmlProperties) -> Self {
|
||||
Self {
|
||||
duration: todo!("Support duration"),
|
||||
overall_bitrate: todo!("Support bitrate"),
|
||||
audio_bitrate: todo!("Support bitrate"),
|
||||
sample_rate: Some(input.default_audio_track.settings.sampling_frequency),
|
||||
bit_depth: input.default_audio_track.settings.bit_depth,
|
||||
channels: Some(input.default_audio_track.settings.channels),
|
||||
channel_mask: todo!("Channel mask"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,10 +31,10 @@ where
|
|||
ElementReaderYield::Child((child, size)) => {
|
||||
match child.ident {
|
||||
ElementIdent::TimecodeScale => {
|
||||
properties.segment_info.timecode_scale =
|
||||
properties.segment_info.timestamp_scale =
|
||||
element_reader.read_unsigned_int(size)?;
|
||||
|
||||
if properties.segment_info.timecode_scale == 0 {
|
||||
if properties.segment_info.timestamp_scale == 0 {
|
||||
log::warn!("Segment.Info.TimecodeScale is 0, which is invalid");
|
||||
if parse_options.parsing_mode == crate::probe::ParsingMode::Strict {
|
||||
decode_err!(@BAIL Ebml, "Segment.Info.TimecodeScale must be nonzero");
|
||||
|
|
Loading…
Reference in a new issue