-
Notifications
You must be signed in to change notification settings - Fork 334
/
mp4.rs
96 lines (80 loc) · 2.49 KB
/
mp4.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#![allow(clippy::map_err_ignore)]
use crate::TimeMs;
use super::{Config, Sample, Segment, VideoData, VideoLoadError};
use ::mp4;
use mp4::TrackKind;
pub fn load_mp4(bytes: &[u8]) -> Result<VideoData, VideoLoadError> {
let mp4 = ::mp4::read(bytes)?;
let video_track = mp4
.tracks()
.values()
.find(|t| t.kind == TrackKind::Video)
.ok_or_else(|| VideoLoadError::NoVideoTrack)?;
let (codec, description);
if let Some(::mp4::Av01Box { av1c, av1c_raw, .. }) =
&video_track.trak(&mp4).mdia.minf.stbl.stsd.av01
{
let profile = av1c.profile;
let level = av1c.level;
let tier = if av1c.tier == 0 { "M" } else { "H" };
let bit_depth = av1c.bit_depth;
codec = format!("av01.{profile}.{level:02}{tier}.{bit_depth:02}");
description = av1c_raw.clone();
} else {
// TODO(jan): support h.264, h.265, vp8, vp9
let stsd = &video_track.trak(&mp4).mdia.minf.stbl.stsd;
let codec_name = if stsd.avc1.is_some() {
"avc"
} else if stsd.hev1.is_some() {
"hevc"
} else if stsd.vp09.is_some() {
"vp9"
} else {
"unknown"
};
return Err(VideoLoadError::UnsupportedCodec(codec_name.into()));
}
let coded_height = video_track.height;
let coded_width = video_track.width;
let config = Config {
codec,
description,
coded_height,
coded_width,
};
let duration = TimeMs::new(video_track.duration_ms());
let mut samples = Vec::<Sample>::new();
let mut segments = Vec::<Segment>::new();
let data = video_track.data.clone();
for sample in &video_track.samples {
if sample.is_sync && !samples.is_empty() {
segments.push(Segment {
timestamp: samples[0].timestamp,
samples,
});
samples = Vec::new();
}
let timestamp = TimeMs::new(sample.timestamp_ms());
let duration = TimeMs::new(sample.duration_ms());
let byte_offset = sample.offset as u32;
let byte_length = sample.size as u32;
samples.push(Sample {
timestamp,
duration,
byte_offset,
byte_length,
});
}
if !samples.is_empty() {
segments.push(Segment {
timestamp: samples[0].timestamp,
samples,
});
}
Ok(VideoData {
config,
data,
duration,
segments,
})
}