Skip to content

Commit a6669bf

Browse files
committed
EBML: Start defining structure for writes
1 parent 81fc15d commit a6669bf

16 files changed

+695
-240
lines changed

lofty/src/ebml/element_reader.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::ebml::vint::VInt;
1+
use crate::ebml::vint::{ElementId, VInt};
22
use crate::error::Result;
33
use crate::macros::{decode_err, try_vec};
44

@@ -10,8 +10,8 @@ use lofty_attr::ebml_master_elements;
1010

1111
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
1212
pub struct ElementHeader {
13-
pub(crate) id: VInt,
14-
pub(crate) size: VInt,
13+
pub(crate) id: ElementId,
14+
pub(crate) size: VInt<u64>,
1515
}
1616

1717
impl ElementHeader {
@@ -20,8 +20,8 @@ impl ElementHeader {
2020
R: Read,
2121
{
2222
Ok(Self {
23-
id: VInt::parse_from_element_id(reader, max_id_length)?,
24-
size: VInt::parse(reader, max_vint_length)?,
23+
id: ElementId::parse(reader, max_id_length)?,
24+
size: VInt::<u64>::parse(reader, max_vint_length)?,
2525
})
2626
}
2727
}
@@ -41,7 +41,7 @@ pub enum ElementDataType {
4141
#[derive(Copy, Clone, Debug)]
4242
struct MasterElement {
4343
id: ElementIdent,
44-
children: &'static [(VInt, ChildElementDescriptor)],
44+
children: &'static [(ElementId, ChildElementDescriptor)],
4545
}
4646

4747
#[derive(Copy, Clone, Debug)]
@@ -247,7 +247,7 @@ const ROOT_DEPTH: u8 = 1;
247247
#[derive(Copy, Clone, Debug)]
248248
struct Depth {
249249
level: u8,
250-
length: VInt,
250+
length: VInt<u64>,
251251
}
252252

253253
#[derive(Copy, Clone, Debug)]
@@ -302,7 +302,7 @@ impl ElementReaderContext {
302302
self.masters.get((self.depth - 1) as usize).copied()
303303
}
304304

305-
fn current_master_length(&self) -> VInt {
305+
fn current_master_length(&self) -> VInt<u64> {
306306
assert!(self.depth > 0);
307307
self.current_master()
308308
.expect("should have current master element")
@@ -316,7 +316,7 @@ impl ElementReaderContext {
316316
}
317317
}
318318

319-
fn remaining_lock_length(&self) -> VInt {
319+
fn remaining_lock_length(&self) -> VInt<u64> {
320320
assert!(self.locked && !self.lock_depths.is_empty());
321321

322322
let lock_depth = *self.lock_depths.last().unwrap();
@@ -326,8 +326,8 @@ impl ElementReaderContext {
326326

327327
#[derive(Debug)]
328328
pub(crate) enum ElementReaderYield {
329-
Master((ElementIdent, VInt)),
330-
Child((ChildElementDescriptor, VInt)),
329+
Master((ElementIdent, VInt<u64>)),
330+
Child((ChildElementDescriptor, VInt<u64>)),
331331
Unknown(ElementHeader),
332332
Eof,
333333
}
@@ -412,7 +412,7 @@ where
412412
self.ctx.max_size_length = len
413413
}
414414

415-
fn push_new_master(&mut self, master: MasterElement, size: VInt) -> Result<()> {
415+
fn push_new_master(&mut self, master: MasterElement, size: VInt<u64>) -> Result<()> {
416416
log::debug!("New master element: {:?}", master.id);
417417

418418
if self.ctx.depth == MAX_DEPTH {
@@ -662,7 +662,7 @@ where
662662
// https://www.rfc-editor.org/rfc/rfc8794.html#section-7.8
663663
// A Binary Element MUST declare a length in octets from zero to VINTMAX.
664664

665-
if element_length > VInt::MAX {
665+
if element_length > VInt::<u64>::MAX {
666666
decode_err!(@BAIL Ebml, "Binary element length is too large")
667667
}
668668

lofty/src/ebml/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use lofty_attr::LoftyFile;
1111

1212
pub use properties::EbmlProperties;
1313
pub use tag::*;
14-
pub use vint::VInt;
14+
pub use vint::*;
1515

1616
/// An EBML file
1717
#[derive(LoftyFile, Default)]

lofty/src/ebml/read.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod segment_tracks;
99
use super::EbmlFile;
1010
use crate::config::ParseOptions;
1111
use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield};
12-
use crate::ebml::vint::VInt;
12+
use crate::ebml::vint::ElementId;
1313
use crate::ebml::EbmlProperties;
1414
use crate::error::Result;
1515
use crate::macros::decode_err;
@@ -18,6 +18,9 @@ use std::io::{Read, Seek};
1818

1919
const SUPPORTED_DOC_TYPES: &[&str] = &["matroska", "webm"];
2020

21+
const CRC32_ID: ElementId = ElementId(0xBF);
22+
const VOID_ID: ElementId = ElementId(0xEC);
23+
2124
pub(super) fn read_from<R>(reader: &mut R, parse_options: ParseOptions) -> Result<EbmlFile>
2225
where
2326
R: Read + Seek,
@@ -45,7 +48,7 @@ where
4548
// CRC-32 (0xBF) and Void (0xEC) elements can occur at the top level.
4649
// This is valid, and we can just skip them.
4750
ElementReaderYield::Unknown(ElementHeader {
48-
id: VInt(id @ (0xBF | 0xEC)),
51+
id: id @ (CRC32_ID | VOID_ID),
4952
size,
5053
}) => {
5154
log::debug!("Skipping global element: {:X}", id);

lofty/src/ebml/read/segment.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::config::ParseOptions;
33
use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield};
44
use crate::ebml::properties::EbmlProperties;
55
use crate::ebml::tag::EbmlTag;
6-
use crate::ebml::VInt;
6+
use crate::ebml::ElementId;
77
use crate::error::Result;
88

99
use std::io::{Read, Seek};
@@ -72,7 +72,7 @@ where
7272
// elements, so we can just skip any useless ones.
7373

7474
children_reader.skip_element(ElementHeader {
75-
id: VInt(id as u64),
75+
id: ElementId(id as u64),
7676
size,
7777
})?;
7878
},

lofty/src/ebml/read/segment_attachments.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ where
7474
uid = Some(children_reader.read_unsigned_int(size)?);
7575
},
7676
ElementIdent::FileReferral => {
77-
referral = Some(children_reader.read_string(size)?);
77+
referral = Some(children_reader.read_binary(size)?);
7878
},
7979
ElementIdent::FileUsedStartTime => {
8080
used_start_time = Some(children_reader.read_unsigned_int(size)?);

lofty/src/ebml/read/segment_tracks.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::io::{Read, Seek};
1010
pub(super) fn read_from<R>(
1111
children_reader: &mut ElementChildIterator<'_, R>,
1212
parse_options: ParseOptions,
13-
properties: &mut EbmlProperties,
13+
_properties: &mut EbmlProperties,
1414
) -> Result<()>
1515
where
1616
R: Read + Seek,
@@ -44,7 +44,7 @@ const AUDIO_TRACK_TYPE: u64 = 2;
4444

4545
fn read_track_entry<R>(
4646
children_reader: &mut ElementChildIterator<'_, R>,
47-
parse_options: ParseOptions,
47+
_parse_options: ParseOptions,
4848
audio_tracks: &mut Vec<AudioTrack>,
4949
) -> Result<()>
5050
where

lofty/src/ebml/tag/attached_file.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct AttachedFile {
2525
/// Unique ID representing the file, as random as possible.
2626
pub uid: u64,
2727
/// A binary value that a track/codec can refer to when the attachment is needed.
28-
pub referral: Option<String>,
28+
pub referral: Option<Vec<u8>>,
2929
/// The timestamp at which this optimized font attachment comes into context.
3030
///
3131
/// This is expressed in Segment Ticks which is based on `TimestampScale`. This element is

lofty/src/ebml/tag/target.rs

+13
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,16 @@ impl From<TargetType> for Target {
102102
}
103103
}
104104
}
105+
106+
impl Target {
107+
// TargetType::Album is the default value. If nothing else is set, it is valid to write
108+
// a zero-sized Targets element.
109+
pub(super) fn is_empty_candidate(&self) -> bool {
110+
self.target_type == TargetType::Album
111+
&& self.name.is_none()
112+
&& self.track_uids.is_none()
113+
&& self.edition_uids.is_none()
114+
&& self.chapter_uids.is_none()
115+
&& self.attachment_uids.is_none()
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::ebml::tag::write::{write_element, ElementWriterCtx, WriteableElement};
2+
use crate::ebml::{AttachedFile, ElementId, VInt};
3+
use crate::io::FileLike;
4+
5+
const FileDescription_ID: ElementId = ElementId(0x467E);
6+
const FileName_ID: ElementId = ElementId(0x466E);
7+
const FileMediaType_ID: ElementId = ElementId(0x4660);
8+
const FileData_ID: ElementId = ElementId(0x465C);
9+
const FileUID_ID: ElementId = ElementId(0x46AE);
10+
const FileReferral_ID: ElementId = ElementId(0x4675);
11+
const FileUsedStartTime_ID: ElementId = ElementId(0x4661);
12+
const FileUsedEndTime_ID: ElementId = ElementId(0x4662);
13+
14+
impl WriteableElement for AttachedFile {
15+
const ID: ElementId = ElementId(0x61A7);
16+
17+
fn write_element<F: FileLike>(
18+
&self,
19+
ctx: ElementWriterCtx,
20+
writer: &mut F,
21+
) -> crate::error::Result<()> {
22+
self.validate()?;
23+
24+
let mut element_children = Vec::new();
25+
if let Some(description) = &self.description {
26+
write_element(
27+
ctx,
28+
FileDescription_ID,
29+
&description.as_str(),
30+
&mut element_children,
31+
)?;
32+
}
33+
34+
write_element(
35+
ctx,
36+
FileName_ID,
37+
&self.file_name.as_str(),
38+
&mut element_children,
39+
)?;
40+
41+
write_element(
42+
ctx,
43+
FileMediaType_ID,
44+
&self.mime_type.as_str(),
45+
&mut element_children,
46+
)?;
47+
48+
write_element(
49+
ctx,
50+
FileData_ID,
51+
&self.file_data.as_slice(),
52+
&mut element_children,
53+
)?;
54+
55+
let uid = VInt::<u64>::try_from(self.uid)?;
56+
write_element(ctx, FileUID_ID, &uid, &mut element_children)?;
57+
58+
if let Some(referral) = &self.referral {
59+
write_element(
60+
ctx,
61+
FileReferral_ID,
62+
&referral.as_slice(),
63+
&mut element_children,
64+
)?;
65+
}
66+
67+
if let Some(start_time) = &self.used_start_time {
68+
let vint = VInt::<u64>::try_from(*start_time)?;
69+
write_element(ctx, FileUsedStartTime_ID, &vint, &mut element_children)?;
70+
}
71+
72+
if let Some(end_time) = &self.used_end_time {
73+
let vint = VInt::<u64>::try_from(*end_time)?;
74+
write_element(ctx, FileUsedEndTime_ID, &vint, &mut element_children)?;
75+
}
76+
77+
write_element(ctx, Self::ID, &element_children.as_slice(), writer)?;
78+
79+
Ok(())
80+
}
81+
}
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub(super) mod attached_file;
2+
pub(super) mod target;
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::ebml::tag::write::{write_element, EbmlWriteExt, ElementWriterCtx, WriteableElement};
2+
use crate::ebml::{ElementId, Target, TargetType, VInt};
3+
use crate::io::FileLike;
4+
5+
const TargetTypeValue_ID: ElementId = ElementId(0x68CA);
6+
const TargetType_ID: ElementId = ElementId(0x63CA);
7+
const TagTrackUID_ID: ElementId = ElementId(0x63C5);
8+
const TagEditionUID_ID: ElementId = ElementId(0x63C9);
9+
const TagChapterUID_ID: ElementId = ElementId(0x63C4);
10+
const TagAttachmentUID_ID: ElementId = ElementId(0x63C6);
11+
12+
impl WriteableElement for Target {
13+
const ID: ElementId = ElementId(0x63C0);
14+
15+
fn write_element<F: FileLike>(
16+
&self,
17+
ctx: ElementWriterCtx,
18+
writer: &mut F,
19+
) -> crate::error::Result<()> {
20+
if self.is_empty_candidate() {
21+
writer.write_id(ctx, Self::ID)?;
22+
writer.write_size(ctx, VInt::<u64>::ZERO)?;
23+
return Ok(());
24+
}
25+
26+
let mut element_children = Vec::new();
27+
if self.target_type == TargetType::Album {
28+
write_element(
29+
ctx,
30+
TargetTypeValue_ID,
31+
&[].as_slice(),
32+
&mut element_children,
33+
)?;
34+
} else {
35+
let vint = VInt::<u64>::try_from(self.target_type as u64)?;
36+
write_element(ctx, TargetTypeValue_ID, &vint, &mut element_children)?;
37+
}
38+
39+
if let Some(name) = &self.name {
40+
write_element(ctx, TargetType_ID, &name.as_str(), &mut element_children)?;
41+
}
42+
43+
if let Some(track_uids) = &self.track_uids {
44+
for &uid in track_uids {
45+
let vint = VInt::<u64>::try_from(uid)?;
46+
write_element(ctx, TagTrackUID_ID, &vint, &mut element_children)?;
47+
}
48+
}
49+
50+
if let Some(edition_uids) = &self.edition_uids {
51+
for &uid in edition_uids {
52+
let vint = VInt::<u64>::try_from(uid)?;
53+
write_element(ctx, TagEditionUID_ID, &vint, &mut element_children)?;
54+
}
55+
}
56+
57+
if let Some(chapter_uids) = &self.chapter_uids {
58+
for &uid in chapter_uids {
59+
let vint = VInt::<u64>::try_from(uid)?;
60+
write_element(ctx, TagChapterUID_ID, &vint, &mut element_children)?;
61+
}
62+
}
63+
64+
if let Some(attachment_uids) = &self.attachment_uids {
65+
for &uid in attachment_uids {
66+
let vint = VInt::<u64>::try_from(uid)?;
67+
write_element(ctx, TagAttachmentUID_ID, &vint, &mut element_children)?;
68+
}
69+
}
70+
71+
write_element(ctx, Self::ID, &element_children.as_slice(), writer)?;
72+
73+
Ok(())
74+
}
75+
}
76+
77+
#[cfg(test)]
78+
mod tests {
79+
use super::*;
80+
81+
use std::io::Cursor;
82+
83+
#[test_log::test]
84+
fn write_empty_default() {
85+
let target = Target::default();
86+
87+
let mut buf = Cursor::new(Vec::new());
88+
target
89+
.write_element(
90+
ElementWriterCtx {
91+
max_id_len: 4,
92+
max_size_len: 8,
93+
},
94+
&mut buf,
95+
)
96+
.unwrap();
97+
98+
let expected = vec![0x63, 0xC0, 0x80];
99+
100+
assert_eq!(buf.into_inner(), expected);
101+
}
102+
}

0 commit comments

Comments
 (0)