Skip to content

Commit e7c7027

Browse files
committed
feat(mp4): add object descriptors
1 parent d9cdb8d commit e7c7027

File tree

8 files changed

+1296
-1
lines changed

8 files changed

+1296
-1
lines changed

crates/mp4/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ scuffle-bytes-util = { path = "../bytes-util", version = "0.2.0" }
2424
scuffle-changelog = { optional = true, path = "../changelog", version = "0.1.0" }
2525
document-features = { optional = true, version = "0.2" }
2626
scuffle-workspace-hack.workspace = true
27+
nutype-enum = { path = "../nutype_enum", version = "0.1.5" }
2728

2829
[dev-dependencies]
2930
serde = { features = ["derive"], version = "1" }

crates/mp4/src/boxes.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
use isobmff::boxes::{AudioSampleEntry, SampleEntry, VisualSampleEntry};
66
use isobmff::{FullBoxHeader, IsoBox};
77
use scuffle_bytes_util::BytesCow;
8+
use scuffle_bytes_util::zero_copy::U24Be;
9+
10+
use crate::object_description::ESDescriptor;
811

912
/// Object Descriptor Box
1013
///
@@ -85,5 +88,18 @@ pub struct ESDBox<'a> {
8588
/// The ES Descriptor for this stream.
8689
///
8790
/// Defined in ISO/IEC 14496-1.
88-
pub es: BytesCow<'a>,
91+
pub es: ESDescriptor<'a>,
92+
}
93+
94+
impl<'a> ESDBox<'a> {
95+
/// Creates a new ESDBox with the given ES descriptor.
96+
pub fn new(es: ESDescriptor<'a>) -> Self {
97+
Self {
98+
header: FullBoxHeader {
99+
version: 0,
100+
flags: U24Be(0),
101+
},
102+
es,
103+
}
104+
}
89105
}

crates/mp4/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! This crates implements the MP4 ISO Base Media File Format (ISOBMFF) boxes defined by ISO/IEC 14496-14 - 6.
44
//!
55
//! For an implementation of ISO/IEC 14496-12 (the ISO Base Media File Format itself), see the [isobmff](https://crates.io/crates/isobmff) crate.
6+
//!
7+
//! Additionally it implements the Object Description Framework defined by ISO/IEC 14496-1 - 7.2.
68
#![cfg_attr(feature = "docs", doc = "\n\nSee the [changelog][changelog] for a full release history.")]
79
#![cfg_attr(feature = "docs", doc = "## Feature flags")]
810
#![cfg_attr(feature = "docs", doc = document_features::document_features!())]
@@ -19,6 +21,7 @@
1921
#![deny(unreachable_pub)]
2022

2123
pub mod boxes;
24+
pub mod object_description;
2225

2326
/// Changelogs generated by [scuffle_changelog]
2427
#[cfg(feature = "docs")]
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
use isobmff::IsoSized;
2+
use nutype_enum::nutype_enum;
3+
use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, U24Be};
4+
use scuffle_bytes_util::{BitWriter, IoResultExt};
5+
6+
use super::profile_level_indication_index::ProfileLevelIndicationIndexDescriptor;
7+
use super::{BaseDescriptor, DescriptorTag, UnknownDescriptor};
8+
9+
nutype_enum! {
10+
/// ObjectTypeIndication
11+
///
12+
/// ISO/IEC 14496-1 - 7.2.6.6.2
13+
pub enum ObjectTypeIndication(u8) {
14+
/// Forbidden
15+
Forbidden = 0x00,
16+
/// Systems ISO/IEC 14496-1 a
17+
Systems14496_1_a = 0x01,
18+
/// Systems ISO/IEC 14496-1 b
19+
Systems14496_1_b = 0x02,
20+
/// Interaction Stream
21+
InteractionStream = 0x03,
22+
/// Systems ISO/IEC 14496-1 Extended BIFS Configuration
23+
Systems14496_1_ExtendedBIFSConfiguration = 0x04,
24+
/// Systems ISO/IEC 14496-1 AFX
25+
Systems14496_1_AFX = 0x05,
26+
/// Font Data Stream
27+
FontDataStream = 0x06,
28+
/// Synthesized Texture Stream
29+
SynthesizedTextureStream = 0x07,
30+
/// Streaming Text Stream
31+
StreamingTextStream = 0x08,
32+
/// Visual ISO/IEC 14496-2
33+
Visual14496_2 = 0x20,
34+
/// Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10
35+
Visual14496_10 = 0x21,
36+
/// Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10
37+
ParameterSets_14496_10 = 0x22,
38+
/// Audio ISO/IEC 14496-3
39+
Audio14496_3 = 0x40,
40+
/// Visual ISO/IEC 13818-2 Simple Profile
41+
Visual13818_2_SimpleProfile = 0x60,
42+
/// Visual ISO/IEC 13818-2 Main Profile
43+
Visual13818_2_MainProfile = 0x61,
44+
/// Visual ISO/IEC 13818-2 SNR Profile
45+
Visual13818_2_SNRProfile = 0x62,
46+
/// Visual ISO/IEC 13818-2 Spatial Profile
47+
Visual13818_2_SpatialProfile = 0x63,
48+
/// Visual ISO/IEC 13818-2 High Profile
49+
Visual13818_2_HighProfile = 0x64,
50+
/// Visual ISO/IEC 13818-2 422 Profile
51+
Visual13818_2_422Profile = 0x65,
52+
/// Audio ISO/IEC 13818-7 Main Profile
53+
Audio13818_7_MainProfile = 0x66,
54+
/// Audio ISO/IEC 13818-7 LowComplexity Profile
55+
Audio13818_7_LowComplexityProfile = 0x67,
56+
/// Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile
57+
Audio13818_7_ScaleableSamplingRateProfile = 0x68,
58+
/// Audio ISO/IEC 13818-3
59+
Audio13818_3 = 0x69,
60+
/// Visual ISO/IEC 11172-2
61+
Visual11172_2 = 0x6A,
62+
/// Audio ISO/IEC 11172-3
63+
Audio11172_3 = 0x6B,
64+
/// Visual ISO/IEC 10918-1
65+
Visual10918_1 = 0x6C,
66+
/// Visual ISO/IEC 15444-1
67+
Visual15444_1 = 0x6E,
68+
/// No object type specified
69+
Unspecified = 0xFF,
70+
}
71+
}
72+
73+
impl<'a> Deserialize<'a> for ObjectTypeIndication {
74+
fn deserialize<R>(reader: R) -> std::io::Result<Self>
75+
where
76+
R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
77+
{
78+
u8::deserialize(reader).map(Into::into)
79+
}
80+
}
81+
82+
impl Serialize for ObjectTypeIndication {
83+
fn serialize<W>(&self, writer: W) -> std::io::Result<()>
84+
where
85+
W: std::io::Write,
86+
{
87+
self.0.serialize(writer)
88+
}
89+
}
90+
91+
impl IsoSized for ObjectTypeIndication {
92+
fn size(&self) -> usize {
93+
1
94+
}
95+
}
96+
97+
nutype_enum! {
98+
/// StreamType
99+
///
100+
/// ISO/IEC 14496-1 - 7.2.6.6.2
101+
pub enum StreamType(u8) {
102+
/// Forbidden
103+
Forbidden = 0x00,
104+
/// ObjectDescriptorStream
105+
///
106+
/// See ISO/IEC 14496-1 - 7.2.5
107+
ObjectDescriptorStream = 0x01,
108+
/// ClockReferenceStream
109+
///
110+
/// See ISO/IEC 14496-1 - 7.3.2.5
111+
ClockReferenceStream = 0x02,
112+
/// SceneDescriptionStream
113+
///
114+
/// See ISO/IEC 14496-11
115+
SceneDescriptionStream = 0x03,
116+
/// VisualStream
117+
VisualStream = 0x04,
118+
/// AudioStream
119+
AudioStream = 0x05,
120+
/// MPEG7Stream
121+
MPEG7Stream = 0x06,
122+
/// IPMPStream
123+
///
124+
/// See ISO/IEC 14496-1 - 7.2.3.2
125+
IPMPStream = 0x07,
126+
/// ObjectContentInfoStream
127+
///
128+
/// See ISO/IEC 14496-1 - 7.2.4.2
129+
ObjectContentInfoStream = 0x08,
130+
/// MPEGJStream
131+
MPEGJStream = 0x09,
132+
/// Interaction Stream
133+
InteractionStream = 0x0A,
134+
/// IPMPToolStream
135+
///
136+
/// See ISO/IEC 14496-13
137+
IPMPToolStream = 0x0B,
138+
}
139+
}
140+
141+
/// Deocder Config Descriptor
142+
///
143+
/// ISO/IEC 14496-1 - 7.2.6.6
144+
#[derive(Debug)]
145+
pub struct DecoderConfigDescriptor<'a> {
146+
/// An indication of the object or scene description type that needs to be supported
147+
/// by the decoder for this elementary stream.
148+
pub object_type_indication: ObjectTypeIndication,
149+
/// Conveys the type of this elementary stream.
150+
pub stream_type: StreamType,
151+
/// Indicates that this stream is used for upstream information.
152+
pub up_stream: bool,
153+
/// Reserved bit.
154+
pub reserved: bool,
155+
/// Is the size of the decoding buffer for this elementary stream in bytes.
156+
pub buffer_size_db: U24Be,
157+
/// Is the maximum bitrate in bits per second of this elementary stream in any time window of
158+
/// one second duration.
159+
pub max_bitrate: u32,
160+
/// Is the average bitrate in bits per second of this elementary stream. For streams with variable
161+
/// bitrate this value shall be set to zero.
162+
pub avg_bitrate: u32,
163+
/// Decoder specific information.
164+
pub dec_specific_info: Option<UnknownDescriptor<'a>>,
165+
/// A list of [`ProfileLevelIndicationIndexDescriptor`]s.
166+
pub profile_level_indication_index_descr: Vec<ProfileLevelIndicationIndexDescriptor>,
167+
/// Any other unknown descriptors that are contained in this descriptor but not deserialized.
168+
pub unknown_descriptors: Vec<UnknownDescriptor<'a>>,
169+
}
170+
171+
impl DecoderConfigDescriptor<'_> {
172+
fn payload_size(&self) -> usize {
173+
let mut size = 0;
174+
size += self.object_type_indication.size();
175+
size += 1; // stream_type + up_stream + reserved
176+
size += self.buffer_size_db.size();
177+
size += self.max_bitrate.size();
178+
size += self.avg_bitrate.size();
179+
180+
size += self.dec_specific_info.size();
181+
size += self.profile_level_indication_index_descr.size();
182+
size += self.unknown_descriptors.size();
183+
184+
size
185+
}
186+
187+
/// Returns the base descriptor.
188+
pub fn base_descriptor(&self) -> BaseDescriptor {
189+
BaseDescriptor {
190+
tag: DescriptorTag::DecoderConfigDescrTag,
191+
size_of_instance: self.payload_size() as u32,
192+
}
193+
}
194+
}
195+
196+
impl<'a> Deserialize<'a> for DecoderConfigDescriptor<'a> {
197+
fn deserialize<R>(mut reader: R) -> std::io::Result<Self>
198+
where
199+
R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
200+
{
201+
let base_descriptor = BaseDescriptor::deserialize(&mut reader)?;
202+
let mut reader = reader.take(base_descriptor.size_of_instance as usize);
203+
204+
let object_type_indication = ObjectTypeIndication::deserialize(&mut reader)?;
205+
206+
let byte = u8::deserialize(&mut reader)?;
207+
let stream_type = StreamType::from((byte & 0b111_1100) >> 2);
208+
let up_stream = (byte & 0b0000_0010) != 0;
209+
let reserved = (byte & 0b0000_0001) != 0;
210+
let buffer_size_db = U24Be::deserialize(&mut reader)?;
211+
let max_bitrate = u32::deserialize(&mut reader)?;
212+
let avg_bitrate = u32::deserialize(&mut reader)?;
213+
214+
let mut dec_specific_info = None;
215+
let mut profile_level_indication_index_descr = Vec::new();
216+
let mut unknown_descriptors = Vec::new();
217+
218+
loop {
219+
let Some(base_descriptor) = BaseDescriptor::deserialize(&mut reader).eof_to_none()? else {
220+
break;
221+
};
222+
223+
match base_descriptor.tag {
224+
DescriptorTag::DecSpecificInfoTag => {
225+
let Some(descr) = UnknownDescriptor::deserialize_seed(&mut reader, base_descriptor).eof_to_none()?
226+
else {
227+
break;
228+
};
229+
dec_specific_info = Some(descr);
230+
}
231+
DescriptorTag::profileLevelIndicationIndexDescrTag => {
232+
let Some(descr) = ProfileLevelIndicationIndexDescriptor::deserialize_seed(&mut reader, base_descriptor)
233+
.eof_to_none()?
234+
else {
235+
break;
236+
};
237+
profile_level_indication_index_descr.push(descr);
238+
}
239+
_ => {
240+
let Some(descr) = UnknownDescriptor::deserialize_seed(&mut reader, base_descriptor).eof_to_none()?
241+
else {
242+
break;
243+
};
244+
unknown_descriptors.push(descr);
245+
}
246+
}
247+
}
248+
249+
Ok(Self {
250+
object_type_indication,
251+
stream_type,
252+
up_stream,
253+
reserved,
254+
buffer_size_db,
255+
max_bitrate,
256+
avg_bitrate,
257+
dec_specific_info,
258+
profile_level_indication_index_descr,
259+
unknown_descriptors,
260+
})
261+
}
262+
}
263+
264+
impl Serialize for DecoderConfigDescriptor<'_> {
265+
fn serialize<W>(&self, writer: W) -> std::io::Result<()>
266+
where
267+
W: std::io::Write,
268+
{
269+
let mut bit_writer = BitWriter::new(writer);
270+
271+
self.base_descriptor().serialize(&mut bit_writer)?;
272+
self.object_type_indication.serialize(&mut bit_writer)?;
273+
bit_writer.write_bits(self.stream_type.0 as u64, 6)?;
274+
bit_writer.write_bit(self.up_stream)?;
275+
bit_writer.write_bit(self.reserved)?;
276+
self.buffer_size_db.serialize(&mut bit_writer)?;
277+
self.max_bitrate.serialize(&mut bit_writer)?;
278+
self.avg_bitrate.serialize(&mut bit_writer)?;
279+
280+
if let Some(dec_specific_info) = &self.dec_specific_info {
281+
dec_specific_info.serialize(&mut bit_writer)?;
282+
}
283+
284+
for profile_level_indication_index_descr in &self.profile_level_indication_index_descr {
285+
profile_level_indication_index_descr.serialize(&mut bit_writer)?;
286+
}
287+
288+
for unknown_descriptor in &self.unknown_descriptors {
289+
unknown_descriptor.serialize(&mut bit_writer)?;
290+
}
291+
292+
Ok(())
293+
}
294+
}
295+
296+
impl IsoSized for DecoderConfigDescriptor<'_> {
297+
fn size(&self) -> usize {
298+
self.base_descriptor().size() + self.payload_size()
299+
}
300+
}

0 commit comments

Comments
 (0)