From cdca284d8641868fe6a5f8229f4d1331bd82c5c9 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 21 Apr 2023 23:15:16 +0200 Subject: [PATCH 1/2] Implement EBML Date Element --- src/ebml/error.rs | 4 ++++ src/ebml/parse.rs | 19 +++++++++++++++++++ src/elements.rs | 4 ++-- src/serializer/ebml.rs | 9 ++++++++- src/serializer/elements.rs | 4 +++- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/ebml/error.rs b/src/ebml/error.rs index d7f0843..04bde24 100644 --- a/src/ebml/error.rs +++ b/src/ebml/error.rs @@ -59,6 +59,10 @@ pub enum ErrorKind { /// which is not allowed. FloatWidthIncorrect, + /// A date element has declared a length that is not 0 or 8 octets, + /// which is not allowed. + DateWidthIncorrect, + /// A string element contains non-UTF-8 data, which is not allowed. StringNotUtf8, diff --git a/src/ebml/parse.rs b/src/ebml/parse.rs index 53435eb..3bb8737 100644 --- a/src/ebml/parse.rs +++ b/src/ebml/parse.rs @@ -58,6 +58,25 @@ impl<'a> EbmlParsable<'a> for f64 { } } +/// Date Element. Contains the number of nanoseconds since +/// 2001-01-01T00:00:00.000000000 UTC. +/// +/// This struct can't really do anything by itself. If you want +/// date/time handling, you should use a crate like [time]. +/// +/// [time]: https://crates.io/crates/time +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Date(pub i64); + +impl<'a> EbmlParsable<'a> for Date { + fn try_parse(data: &'a [u8]) -> Result { + match data.len() { + 0 | 8 => i64::try_parse(data).map(Date), + _ => Err(ErrorKind::DateWidthIncorrect), + } + } +} + impl<'a> EbmlParsable<'a> for String { fn try_parse(data: &'a [u8]) -> Result { String::from_utf8(data.to_vec()).map_err(|_| ErrorKind::StringNotUtf8) diff --git a/src/elements.rs b/src/elements.rs index 801041d..3b999b4 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -7,8 +7,8 @@ use nom::{ pub use uuid::Uuid; -use crate::ebml::macros::impl_ebml_master; use crate::ebml::{check_id, checksum, crc, elem_size, vid, vint, EbmlParsable, EbmlResult, Error}; +use crate::ebml::{macros::impl_ebml_master, Date}; use crate::elements; #[derive(Debug, Clone, PartialEq)] @@ -108,7 +108,7 @@ impl_ebml_master! { // [0x6924] chapter_translate: (Option), [0x2AD7B1] timestamp_scale: (u64) = 1000000, [0x4489] duration: (Option), // FIXME: should be float - [0x4461] date_utc: (Option>), // FIXME: should be date + [0x4461] date_utc: (Option), [0x7BA9] title: (Option), [0x4D80] muxing_app: (String), [0x5741] writing_app: (String), diff --git a/src/serializer/ebml.rs b/src/serializer/ebml.rs index 6c32d59..481c7d7 100644 --- a/src/serializer/ebml.rs +++ b/src/serializer/ebml.rs @@ -3,7 +3,7 @@ use cookie_factory::gen_slice; use cookie_factory::GenError; use nom::AsBytes; -use crate::ebml::EbmlHeader; +use crate::ebml::{Date, EbmlHeader}; use crate::serializer::cookie_utils::{ gen_at_offset, gen_skip, gen_slice, set_be_f64, set_be_i8, tuple, }; @@ -315,6 +315,13 @@ impl EbmlSize for f64 { } } +impl EbmlSize for Date { + fn capacity(&self) -> usize { + // FIXME: Handle zero-sized date + 8 + } +} + impl EbmlSize for Option { fn capacity(&self) -> usize { match *self { diff --git a/src/serializer/elements.rs b/src/serializer/elements.rs index 5ddd46c..ba121ef 100644 --- a/src/serializer/elements.rs +++ b/src/serializer/elements.rs @@ -87,7 +87,9 @@ pub(crate) fn gen_info<'a, 'b>( gen_opt(i.segment_family.as_ref(), |v| gen_ebml_binary(0x4444, v)), gen_ebml_uint(0x2AD7B1, i.timestamp_scale), gen_opt(i.duration.as_ref(), |v| gen_f64(0x4489, *v)), - gen_opt(i.date_utc.as_ref(), |v| gen_ebml_binary(0x4461, v)), + gen_opt(i.date_utc.as_ref(), |v| { + gen_ebml_binary(0x4461, v.0.to_be_bytes()) + }), gen_opt(i.title.as_ref(), |v| gen_ebml_str(0x7BA9, v)), gen_ebml_str(0x4D80, &i.muxing_app), gen_ebml_str(0x5741, &i.writing_app), From c39743d376b9d703c3b5d6c78fc62a7c97b65905 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 21 Apr 2023 23:35:55 +0200 Subject: [PATCH 2/2] Add Info::date_utc to info output --- tools/Cargo.toml | 1 + tools/src/matroska_info.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 143361c..e59c14b 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -17,3 +17,4 @@ matroska = { path = ".." } nom = "7.0" pretty_env_logger = "0.4" err-derive = "0.3.0" +time = { version = "0.3.20", features = ["formatting"] } diff --git a/tools/src/matroska_info.rs b/tools/src/matroska_info.rs index ed9ea71..4bdc1c4 100644 --- a/tools/src/matroska_info.rs +++ b/tools/src/matroska_info.rs @@ -181,6 +181,20 @@ fn run(filename: &str) -> Result<(), InfoError> { } println!("| + Multiplexing application: {}", i.muxing_app); println!("| + Writing application: {}", i.writing_app); + if let Some(ref date) = i.date_utc { + use time::format_description::well_known::Rfc3339; + use time::{Date, Duration}; + + let formatted = Date::from_ordinal_date(2001, 1) + .unwrap() + .midnight() + .assume_utc() + .saturating_add(Duration::nanoseconds(date.0)) + .format(&Rfc3339) + .unwrap(); + + println!("| + Date: {formatted}"); + } if info.is_some() { return Err(InfoError::InfoElement); } else {