1use bytes::BytesMut;
2use std::cmp;
3use std::convert::TryFrom;
4use std::io::{Read, Seek, SeekFrom, Write};
5use std::time::Duration;
6
7use crate::mp4box::traf::TrafBox;
8use crate::mp4box::trak::TrakBox;
9use crate::mp4box::trun::TrunBox;
10use crate::mp4box::{
11 avc1::Avc1Box, co64::Co64Box, ctts::CttsBox, ctts::CttsEntry, hev1::Hev1Box, mp4a::Mp4aBox,
12 smhd::SmhdBox, stco::StcoBox, stsc::StscEntry, stss::StssBox, stts::SttsEntry, tx3g::Tx3gBox,
13 vmhd::VmhdBox, vp09::Vp09Box,
14};
15use crate::*;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct TrackConfig {
19 pub track_type: TrackType,
20 pub timescale: u32,
21 pub language: String,
22 pub media_conf: MediaConfig,
23}
24
25impl From<MediaConfig> for TrackConfig {
26 fn from(media_conf: MediaConfig) -> Self {
27 match media_conf {
28 MediaConfig::AvcConfig(avc_conf) => Self::from(avc_conf),
29 MediaConfig::HevcConfig(hevc_conf) => Self::from(hevc_conf),
30 MediaConfig::AacConfig(aac_conf) => Self::from(aac_conf),
31 MediaConfig::TtxtConfig(ttxt_conf) => Self::from(ttxt_conf),
32 MediaConfig::Vp9Config(vp9_config) => Self::from(vp9_config),
33 }
34 }
35}
36
37impl From<AvcConfig> for TrackConfig {
38 fn from(avc_conf: AvcConfig) -> Self {
39 Self {
40 track_type: TrackType::Video,
41 timescale: 1000, language: String::from("und"), media_conf: MediaConfig::AvcConfig(avc_conf),
44 }
45 }
46}
47
48impl From<HevcConfig> for TrackConfig {
49 fn from(hevc_conf: HevcConfig) -> Self {
50 Self {
51 track_type: TrackType::Video,
52 timescale: 1000, language: String::from("und"), media_conf: MediaConfig::HevcConfig(hevc_conf),
55 }
56 }
57}
58
59impl From<AacConfig> for TrackConfig {
60 fn from(aac_conf: AacConfig) -> Self {
61 Self {
62 track_type: TrackType::Audio,
63 timescale: 1000, language: String::from("und"), media_conf: MediaConfig::AacConfig(aac_conf),
66 }
67 }
68}
69
70impl From<TtxtConfig> for TrackConfig {
71 fn from(txtt_conf: TtxtConfig) -> Self {
72 Self {
73 track_type: TrackType::Subtitle,
74 timescale: 1000, language: String::from("und"), media_conf: MediaConfig::TtxtConfig(txtt_conf),
77 }
78 }
79}
80
81impl From<Vp9Config> for TrackConfig {
82 fn from(vp9_conf: Vp9Config) -> Self {
83 Self {
84 track_type: TrackType::Video,
85 timescale: 1000, language: String::from("und"), media_conf: MediaConfig::Vp9Config(vp9_conf),
88 }
89 }
90}
91
92fn sample_times(trak: &TrakBox) -> Result<Vec<(u64, u32)>> {
93 let stts = &trak.mdia.minf.stbl.stts;
94
95 let mut sample_count: u32 = 1;
96 let mut elapsed = 0;
97
98 let mut result = Vec::new();
99
100 let mut sample_id = 1;
101
102 for entry in stts.entries.iter() {
103 let new_sample_count =
104 sample_count
105 .checked_add(entry.sample_count)
106 .ok_or(Error::InvalidData(
107 "attempt to sum stts entries sample_count with overflow",
108 ))?;
109
110 while sample_id < new_sample_count {
111 let start_time =
112 (sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed;
113 sample_id += 1;
114 result.push((start_time, entry.sample_delta));
115 }
116
117 sample_count = new_sample_count;
118 elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
119 }
120
121 Ok(result)
122}
123
124#[derive(Debug)]
125pub struct Mp4Track {
126 pub trak: TrakBox,
127 pub trafs: Vec<TrafBox>,
128 pub moof_offsets: Vec<u64>,
129
130 pub default_sample_duration: u32,
132
133 sample_time_cache: Vec<(u64, u32)>,
134}
135
136impl Mp4Track {
137 pub(crate) fn from(trak: &TrakBox) -> Self {
138 let trak = trak.clone();
139 let sample_time_cache = sample_times(&trak).unwrap();
140 Self {
141 trak,
142 trafs: Vec::new(),
143 moof_offsets: Vec::new(),
144 default_sample_duration: 0,
145 sample_time_cache,
146 }
147 }
148
149 pub fn track_id(&self) -> u32 {
150 self.trak.tkhd.track_id
151 }
152
153 pub fn track_type(&self) -> Result<TrackType> {
154 TrackType::try_from(&self.trak.mdia.hdlr.handler_type)
155 }
156
157 pub fn media_type(&self) -> Result<MediaType> {
158 if self.trak.mdia.minf.stbl.stsd.avc1.is_some() {
159 Ok(MediaType::H264)
160 } else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() {
161 Ok(MediaType::H265)
162 } else if self.trak.mdia.minf.stbl.stsd.vp09.is_some() {
163 Ok(MediaType::VP9)
164 } else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
165 Ok(MediaType::AAC)
166 } else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
167 Ok(MediaType::TTXT)
168 } else {
169 Err(Error::InvalidData("unsupported media type"))
170 }
171 }
172
173 pub fn box_type(&self) -> Result<FourCC> {
174 if self.trak.mdia.minf.stbl.stsd.avc1.is_some() {
175 Ok(FourCC::from(BoxType::Avc1Box))
176 } else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() {
177 Ok(FourCC::from(BoxType::Hev1Box))
178 } else if self.trak.mdia.minf.stbl.stsd.vp09.is_some() {
179 Ok(FourCC::from(BoxType::Vp09Box))
180 } else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() {
181 Ok(FourCC::from(BoxType::Mp4aBox))
182 } else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
183 Ok(FourCC::from(BoxType::Tx3gBox))
184 } else {
185 Err(Error::InvalidData("unsupported sample entry box"))
186 }
187 }
188
189 pub fn width(&self) -> u16 {
190 if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
191 avc1.width
192 } else {
193 self.trak.tkhd.width.value()
194 }
195 }
196
197 pub fn height(&self) -> u16 {
198 if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
199 avc1.height
200 } else {
201 self.trak.tkhd.height.value()
202 }
203 }
204
205 pub fn frame_rate(&self) -> f64 {
206 let dur = self.duration();
207 if dur.is_zero() {
208 0.0
209 } else {
210 self.sample_count() as f64 / dur.as_secs_f64()
211 }
212 }
213
214 pub fn sample_freq_index(&self) -> Result<SampleFreqIndex> {
215 if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
216 if let Some(ref esds) = mp4a.esds {
217 SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index)
218 } else {
219 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
220 }
221 } else {
222 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
223 }
224 }
225
226 pub fn channel_config(&self) -> Result<ChannelConfig> {
227 if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
228 if let Some(ref esds) = mp4a.esds {
229 ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf)
230 } else {
231 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
232 }
233 } else {
234 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
235 }
236 }
237
238 pub fn language(&self) -> &str {
239 &self.trak.mdia.mdhd.language
240 }
241
242 pub fn timescale(&self) -> u32 {
243 self.trak.mdia.mdhd.timescale
244 }
245
246 pub fn duration(&self) -> Duration {
247 Duration::from_micros(
248 self.trak.mdia.mdhd.duration * 1_000_000 / self.trak.mdia.mdhd.timescale as u64,
249 )
250 }
251
252 pub fn bitrate(&self) -> u32 {
253 if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
254 if let Some(ref esds) = mp4a.esds {
255 esds.es_desc.dec_config.avg_bitrate
256 } else {
257 0
258 }
259 } else {
261 let dur = self.duration();
262 if dur.is_zero() {
263 0
264 } else {
265 let bitrate = self.total_sample_size() as f64 * 8.0 / dur.as_secs_f64();
266 bitrate as u32
267 }
268 }
269 }
270
271 pub fn sample_count(&self) -> u32 {
272 if !self.trafs.is_empty() {
273 let mut sample_count = 0u32;
274 for traf in self.trafs.iter() {
275 if let Some(ref trun) = traf.trun {
276 sample_count = sample_count
277 .checked_add(trun.sample_count)
278 .expect("attempt to sum trun sample_count with overflow");
279 }
280 }
281 sample_count
282 } else {
283 self.trak.mdia.minf.stbl.stsz.sample_count
284 }
285 }
286
287 pub fn video_profile(&self) -> Result<AvcProfile> {
288 if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
289 AvcProfile::try_from((
290 avc1.avcc.avc_profile_indication,
291 avc1.avcc.profile_compatibility,
292 ))
293 } else {
294 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box))
295 }
296 }
297
298 pub fn sequence_parameter_set(&self) -> Result<&[u8]> {
299 if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
300 match avc1.avcc.sequence_parameter_sets.get(0) {
301 Some(nal) => Ok(nal.bytes.as_ref()),
302 None => Err(Error::EntryInStblNotFound(
303 self.track_id(),
304 BoxType::AvcCBox,
305 0,
306 )),
307 }
308 } else {
309 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box))
310 }
311 }
312
313 pub fn picture_parameter_set(&self) -> Result<&[u8]> {
314 if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
315 match avc1.avcc.picture_parameter_sets.get(0) {
316 Some(nal) => Ok(nal.bytes.as_ref()),
317 None => Err(Error::EntryInStblNotFound(
318 self.track_id(),
319 BoxType::AvcCBox,
320 0,
321 )),
322 }
323 } else {
324 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box))
325 }
326 }
327
328 pub fn audio_profile(&self) -> Result<AudioObjectType> {
329 if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
330 if let Some(ref esds) = mp4a.esds {
331 AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile)
332 } else {
333 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
334 }
335 } else {
336 Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
337 }
338 }
339
340 fn stsc_index(&self, sample_id: u32) -> Result<usize> {
341 if self.trak.mdia.minf.stbl.stsc.entries.is_empty() {
342 return Err(Error::InvalidData("no stsc entries"));
343 }
344 for (i, entry) in self.trak.mdia.minf.stbl.stsc.entries.iter().enumerate() {
345 if sample_id < entry.first_sample {
346 return if i == 0 {
347 Err(Error::InvalidData("sample not found"))
348 } else {
349 Ok(i - 1)
350 };
351 }
352 }
353 Ok(self.trak.mdia.minf.stbl.stsc.entries.len() - 1)
354 }
355
356 fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
357 if self.trak.mdia.minf.stbl.stco.is_none() && self.trak.mdia.minf.stbl.co64.is_none() {
358 return Err(Error::InvalidData("must have either stco or co64 boxes"));
359 }
360 if let Some(ref stco) = self.trak.mdia.minf.stbl.stco {
361 if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
362 return Ok(*offset as u64);
363 } else {
364 return Err(Error::EntryInStblNotFound(
365 self.track_id(),
366 BoxType::StcoBox,
367 chunk_id,
368 ));
369 }
370 } else if let Some(ref co64) = self.trak.mdia.minf.stbl.co64 {
371 if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
372 return Ok(*offset);
373 } else {
374 return Err(Error::EntryInStblNotFound(
375 self.track_id(),
376 BoxType::Co64Box,
377 chunk_id,
378 ));
379 }
380 }
381 Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box))
382 }
383
384 fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
385 let ctts = self.trak.mdia.minf.stbl.ctts.as_ref().unwrap();
386 let mut sample_count: u32 = 1;
387 for (i, entry) in ctts.entries.iter().enumerate() {
388 let next_sample_count =
389 sample_count
390 .checked_add(entry.sample_count)
391 .ok_or(Error::InvalidData(
392 "attempt to sum ctts entries sample_count with overflow",
393 ))?;
394 if sample_id < next_sample_count {
395 return Ok((i, sample_count));
396 }
397 sample_count = next_sample_count;
398 }
399
400 Err(Error::EntryInStblNotFound(
401 self.track_id(),
402 BoxType::CttsBox,
403 sample_id,
404 ))
405 }
406
407 fn find_traf_idx_and_sample_idx(&self, sample_id: u32) -> Option<(usize, usize)> {
409 let global_idx = sample_id - 1;
410 let mut offset = 0;
411 for traf_idx in 0..self.trafs.len() {
412 if let Some(trun) = &self.trafs[traf_idx].trun {
413 let sample_count = trun.sample_count;
414 if sample_count > (global_idx - offset) {
415 return Some((traf_idx, (global_idx - offset) as _));
416 }
417 offset = offset
418 .checked_add(sample_count)
419 .expect("attempt to sum trun sample_count with overflow");
420 }
421 }
422 None
423 }
424
425 fn sample_size(&self, sample_id: u32) -> Result<u32> {
426 if !self.trafs.is_empty() {
427 if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
428 if let Some(size) = self.trafs[traf_idx]
429 .trun
430 .as_ref()
431 .unwrap()
432 .sample_sizes
433 .get(sample_idx)
434 {
435 Ok(*size)
436 } else {
437 Err(Error::EntryInTrunNotFound(
438 self.track_id(),
439 BoxType::TrunBox,
440 sample_id,
441 ))
442 }
443 } else {
444 Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox))
445 }
446 } else {
447 let stsz = &self.trak.mdia.minf.stbl.stsz;
448 if stsz.sample_size > 0 {
449 return Ok(stsz.sample_size);
450 }
451 if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
452 Ok(*size)
453 } else {
454 Err(Error::EntryInStblNotFound(
455 self.track_id(),
456 BoxType::StszBox,
457 sample_id,
458 ))
459 }
460 }
461 }
462
463 fn total_sample_size(&self) -> u64 {
464 let stsz = &self.trak.mdia.minf.stbl.stsz;
465 if stsz.sample_size > 0 {
466 stsz.sample_size as u64 * self.sample_count() as u64
467 } else {
468 let mut total_size = 0;
469 for size in stsz.sample_sizes.iter() {
470 total_size += *size as u64;
471 }
472 total_size
473 }
474 }
475
476 pub fn sample_offset(&self, sample_id: u32) -> Result<u64> {
477 if !self.trafs.is_empty() {
478 if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
479 let mut sample_offset = self.trafs[traf_idx]
480 .tfhd
481 .base_data_offset
482 .unwrap_or(self.moof_offsets[traf_idx]);
483
484 if let Some(data_offset) = self.trafs[traf_idx]
485 .trun
486 .as_ref()
487 .and_then(|trun| trun.data_offset)
488 {
489 sample_offset = sample_offset.checked_add_signed(data_offset as i64).ok_or(
490 Error::InvalidData("attempt to calculate trun sample offset with overflow"),
491 )?;
492 }
493
494 let first_sample_in_trun = sample_id - sample_idx as u32;
495 for i in first_sample_in_trun..sample_id {
496 sample_offset = sample_offset
497 .checked_add(self.sample_size(i)? as u64)
498 .ok_or(Error::InvalidData(
499 "attempt to calculate trun entry sample offset with overflow",
500 ))?;
501 }
502
503 Ok(sample_offset)
504 } else {
505 Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox))
506 }
507 } else {
508 let stsc_index = self.stsc_index(sample_id)?;
509
510 let stsc = &self.trak.mdia.minf.stbl.stsc;
511 let stsc_entry = stsc.entries.get(stsc_index).unwrap();
512
513 let first_chunk = stsc_entry.first_chunk;
514 let first_sample = stsc_entry.first_sample;
515 let samples_per_chunk = stsc_entry.samples_per_chunk;
516
517 let chunk_id = sample_id
518 .checked_sub(first_sample)
519 .map(|n| n / samples_per_chunk)
520 .and_then(|n| n.checked_add(first_chunk))
521 .ok_or(Error::InvalidData(
522 "attempt to calculate stsc chunk_id with overflow",
523 ))?;
524
525 let chunk_offset = self.chunk_offset(chunk_id)?;
526
527 let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
528
529 let mut sample_offset: u64 = 0;
530 for i in first_sample_in_chunk..sample_id {
531 sample_offset += self.sample_size(i)? as u64;
532 }
533
534 Ok(chunk_offset + sample_offset)
535 }
536 }
537
538 pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
539 if !self.trafs.is_empty() {
540 let mut base_start_time = 0;
541 let mut default_sample_duration = self.default_sample_duration;
542 if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
543 let traf = &self.trafs[traf_idx];
544 if let Some(tfdt) = &traf.tfdt {
545 base_start_time = tfdt.base_media_decode_time;
546 }
547 if let Some(duration) = traf.tfhd.default_sample_duration {
548 default_sample_duration = duration;
549 }
550 if let Some(trun) = &traf.trun {
551 if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 {
552 let mut start_offset = 0u64;
553 for duration in &trun.sample_durations[..sample_idx] {
554 start_offset = start_offset.checked_add(*duration as u64).ok_or(
555 Error::InvalidData("attempt to sum sample durations with overflow"),
556 )?;
557 }
558 let duration = trun.sample_durations[sample_idx];
559 return Ok((base_start_time + start_offset, duration));
560 }
561 }
562 }
563 let start_offset = ((sample_id - 1) * default_sample_duration) as u64;
564 Ok((base_start_time + start_offset, default_sample_duration))
565 } else {
566 Ok(self.sample_time_cache[sample_id as usize - 1])
567 }
568 }
569
570 fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
571 if !self.trafs.is_empty() {
572 if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
573 if let Some(cts) = self.trafs[traf_idx]
574 .trun
575 .as_ref()
576 .and_then(|trun| trun.sample_cts.get(sample_idx))
577 {
578 return *cts as i32;
579 }
580 }
581 } else if let Some(ref ctts) = self.trak.mdia.minf.stbl.ctts {
582 if let Ok((ctts_index, _)) = self.ctts_index(sample_id) {
583 let ctts_entry = ctts.entries.get(ctts_index).unwrap();
584 return ctts_entry.sample_offset;
585 }
586 }
587 0
588 }
589
590 fn is_sync_sample(&self, sample_id: u32) -> bool {
591 if !self.trafs.is_empty() {
592 let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
593 return sample_id == 1 || sample_id % sample_sizes_count == 0;
594 }
595
596 if let Some(ref stss) = self.trak.mdia.minf.stbl.stss {
597 stss.entries.binary_search(&sample_id).is_ok()
598 } else {
599 true
600 }
601 }
602
603 pub(crate) fn read_sample<R: Read + Seek>(
604 &self,
605 reader: &mut R,
606 sample_id: u32,
607 ) -> Result<Option<Mp4Sample>> {
608 let sample_offset = match self.sample_offset(sample_id) {
609 Ok(offset) => offset,
610 Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
611 Err(err) => return Err(err),
612 };
613 let sample_size = match self.sample_size(sample_id) {
614 Ok(size) => size,
615 Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
616 Err(err) => return Err(err),
617 };
618
619 let mut buffer = vec![0x0u8; sample_size as usize];
620 reader.seek(SeekFrom::Start(sample_offset))?;
621 reader.read_exact(&mut buffer)?;
622
623 let (start_time, duration) = self.sample_time(sample_id).unwrap(); let rendering_offset = self.sample_rendering_offset(sample_id);
625 let is_sync = self.is_sync_sample(sample_id);
626
627 Ok(Some(Mp4Sample {
628 start_time,
629 duration,
630 rendering_offset,
631 is_sync,
632 bytes: Bytes::from(buffer),
633 }))
634 }
635}
636
637#[derive(Debug, Default)]
639pub(crate) struct Mp4TrackWriter {
640 trak: TrakBox,
641
642 sample_id: u32,
643 fixed_sample_size: u32,
644 is_fixed_sample_size: bool,
645 chunk_samples: u32,
646 chunk_duration: u32,
647 chunk_buffer: BytesMut,
648
649 samples_per_chunk: u32,
650 duration_per_chunk: u32,
651}
652
653impl Mp4TrackWriter {
654 pub(crate) fn new(track_id: u32, config: &TrackConfig) -> Result<Self> {
655 let mut trak = TrakBox::default();
656 trak.tkhd.track_id = track_id;
657 trak.mdia.mdhd.timescale = config.timescale;
658 trak.mdia.mdhd.language = config.language.to_owned();
659 trak.mdia.hdlr.handler_type = config.track_type.into();
660 trak.mdia.minf.stbl.co64 = Some(Co64Box::default());
661 match config.media_conf {
662 MediaConfig::AvcConfig(ref avc_config) => {
663 trak.tkhd.set_width(avc_config.width);
664 trak.tkhd.set_height(avc_config.height);
665
666 let vmhd = VmhdBox::default();
667 trak.mdia.minf.vmhd = Some(vmhd);
668
669 let avc1 = Avc1Box::new(avc_config);
670 trak.mdia.minf.stbl.stsd.avc1 = Some(avc1);
671 }
672 MediaConfig::HevcConfig(ref hevc_config) => {
673 trak.tkhd.set_width(hevc_config.width);
674 trak.tkhd.set_height(hevc_config.height);
675
676 let vmhd = VmhdBox::default();
677 trak.mdia.minf.vmhd = Some(vmhd);
678
679 let hev1 = Hev1Box::new(hevc_config);
680 trak.mdia.minf.stbl.stsd.hev1 = Some(hev1);
681 }
682 MediaConfig::Vp9Config(ref config) => {
683 trak.tkhd.set_width(config.width);
684 trak.tkhd.set_height(config.height);
685
686 trak.mdia.minf.stbl.stsd.vp09 = Some(Vp09Box::new(config));
687 }
688 MediaConfig::AacConfig(ref aac_config) => {
689 let smhd = SmhdBox::default();
690 trak.mdia.minf.smhd = Some(smhd);
691
692 let mp4a = Mp4aBox::new(aac_config);
693 trak.mdia.minf.stbl.stsd.mp4a = Some(mp4a);
694 }
695 MediaConfig::TtxtConfig(ref _ttxt_config) => {
696 let tx3g = Tx3gBox::default();
697 trak.mdia.minf.stbl.stsd.tx3g = Some(tx3g);
698 }
699 }
700 Ok(Mp4TrackWriter {
701 trak,
702 chunk_buffer: BytesMut::new(),
703 sample_id: 1,
704 duration_per_chunk: config.timescale, ..Self::default()
706 })
707 }
708
709 fn update_sample_sizes(&mut self, size: u32) {
710 if self.trak.mdia.minf.stbl.stsz.sample_count == 0 {
711 if size == 0 {
712 self.trak.mdia.minf.stbl.stsz.sample_size = 0;
713 self.is_fixed_sample_size = false;
714 self.trak.mdia.minf.stbl.stsz.sample_sizes.push(0);
715 } else {
716 self.trak.mdia.minf.stbl.stsz.sample_size = size;
717 self.fixed_sample_size = size;
718 self.is_fixed_sample_size = true;
719 }
720 } else if self.is_fixed_sample_size {
721 if self.fixed_sample_size != size {
722 self.is_fixed_sample_size = false;
723 if self.trak.mdia.minf.stbl.stsz.sample_size > 0 {
724 self.trak.mdia.minf.stbl.stsz.sample_size = 0;
725 for _ in 0..self.trak.mdia.minf.stbl.stsz.sample_count {
726 self.trak
727 .mdia
728 .minf
729 .stbl
730 .stsz
731 .sample_sizes
732 .push(self.fixed_sample_size);
733 }
734 }
735 self.trak.mdia.minf.stbl.stsz.sample_sizes.push(size);
736 }
737 } else {
738 self.trak.mdia.minf.stbl.stsz.sample_sizes.push(size);
739 }
740 self.trak.mdia.minf.stbl.stsz.sample_count += 1;
741 }
742
743 fn update_sample_times(&mut self, dur: u32) {
744 if let Some(ref mut entry) = self.trak.mdia.minf.stbl.stts.entries.last_mut() {
745 if entry.sample_delta == dur {
746 entry.sample_count += 1;
747 return;
748 }
749 }
750
751 let entry = SttsEntry {
752 sample_count: 1,
753 sample_delta: dur,
754 };
755 self.trak.mdia.minf.stbl.stts.entries.push(entry);
756 }
757
758 fn update_rendering_offsets(&mut self, offset: i32) {
759 let ctts = if let Some(ref mut ctts) = self.trak.mdia.minf.stbl.ctts {
760 ctts
761 } else {
762 if offset == 0 {
763 return;
764 }
765 let mut ctts = CttsBox::default();
766 if self.sample_id > 1 {
767 let entry = CttsEntry {
768 sample_count: self.sample_id - 1,
769 sample_offset: 0,
770 };
771 ctts.entries.push(entry);
772 }
773 self.trak.mdia.minf.stbl.ctts = Some(ctts);
774 self.trak.mdia.minf.stbl.ctts.as_mut().unwrap()
775 };
776
777 if let Some(ref mut entry) = ctts.entries.last_mut() {
778 if entry.sample_offset == offset {
779 entry.sample_count += 1;
780 return;
781 }
782 }
783
784 let entry = CttsEntry {
785 sample_count: 1,
786 sample_offset: offset,
787 };
788 ctts.entries.push(entry);
789 }
790
791 fn update_sync_samples(&mut self, is_sync: bool) {
792 if let Some(ref mut stss) = self.trak.mdia.minf.stbl.stss {
793 if !is_sync {
794 return;
795 }
796
797 stss.entries.push(self.sample_id);
798 } else {
799 if !is_sync {
800 return;
801 }
802
803 let mut stss = StssBox::default();
805 stss.entries.push(self.sample_id);
806 self.trak.mdia.minf.stbl.stss = Some(stss);
807 };
808 }
809
810 fn is_chunk_full(&self) -> bool {
811 if self.samples_per_chunk > 0 {
812 self.chunk_samples >= self.samples_per_chunk
813 } else {
814 self.chunk_duration >= self.duration_per_chunk
815 }
816 }
817
818 fn update_durations(&mut self, dur: u32, movie_timescale: u32) {
819 self.trak.mdia.mdhd.duration += dur as u64;
820 if self.trak.mdia.mdhd.duration > (u32::MAX as u64) {
821 self.trak.mdia.mdhd.version = 1
822 }
823 self.trak.tkhd.duration +=
824 dur as u64 * movie_timescale as u64 / self.trak.mdia.mdhd.timescale as u64;
825 if self.trak.tkhd.duration > (u32::MAX as u64) {
826 self.trak.tkhd.version = 1
827 }
828 }
829
830 pub(crate) fn write_sample<W: Write + Seek>(
831 &mut self,
832 writer: &mut W,
833 sample: &Mp4Sample,
834 movie_timescale: u32,
835 ) -> Result<u64> {
836 self.chunk_buffer.extend_from_slice(&sample.bytes);
837 self.chunk_samples += 1;
838 self.chunk_duration += sample.duration;
839 self.update_sample_sizes(sample.bytes.len() as u32);
840 self.update_sample_times(sample.duration);
841 self.update_rendering_offsets(sample.rendering_offset);
842 self.update_sync_samples(sample.is_sync);
843 if self.is_chunk_full() {
844 self.write_chunk(writer)?;
845 }
846 self.update_durations(sample.duration, movie_timescale);
847
848 self.sample_id += 1;
849
850 Ok(self.trak.tkhd.duration)
851 }
852
853 fn chunk_count(&self) -> u32 {
854 let co64 = self.trak.mdia.minf.stbl.co64.as_ref().unwrap();
855 co64.entries.len() as u32
856 }
857
858 fn update_sample_to_chunk(&mut self, chunk_id: u32) {
859 if let Some(entry) = self.trak.mdia.minf.stbl.stsc.entries.last() {
860 if entry.samples_per_chunk == self.chunk_samples {
861 return;
862 }
863 }
864
865 let entry = StscEntry {
866 first_chunk: chunk_id,
867 samples_per_chunk: self.chunk_samples,
868 sample_description_index: 1,
869 first_sample: self.sample_id - self.chunk_samples + 1,
870 };
871 self.trak.mdia.minf.stbl.stsc.entries.push(entry);
872 }
873
874 fn update_chunk_offsets(&mut self, offset: u64) {
875 let co64 = self.trak.mdia.minf.stbl.co64.as_mut().unwrap();
876 co64.entries.push(offset);
877 }
878
879 fn write_chunk<W: Write + Seek>(&mut self, writer: &mut W) -> Result<()> {
880 if self.chunk_buffer.is_empty() {
881 return Ok(());
882 }
883 let chunk_offset = writer.stream_position()?;
884
885 writer.write_all(&self.chunk_buffer)?;
886
887 self.update_sample_to_chunk(self.chunk_count() + 1);
888 self.update_chunk_offsets(chunk_offset);
889
890 self.chunk_buffer.clear();
891 self.chunk_samples = 0;
892 self.chunk_duration = 0;
893
894 Ok(())
895 }
896
897 fn max_sample_size(&self) -> u32 {
898 if self.trak.mdia.minf.stbl.stsz.sample_size > 0 {
899 self.trak.mdia.minf.stbl.stsz.sample_size
900 } else {
901 let mut max_size = 0;
902 for sample_size in self.trak.mdia.minf.stbl.stsz.sample_sizes.iter() {
903 max_size = cmp::max(max_size, *sample_size);
904 }
905 max_size
906 }
907 }
908
909 pub(crate) fn write_end<W: Write + Seek>(&mut self, writer: &mut W) -> Result<TrakBox> {
910 self.write_chunk(writer)?;
911
912 let max_sample_size = self.max_sample_size();
913 if let Some(ref mut mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
914 if let Some(ref mut esds) = mp4a.esds {
915 esds.es_desc.dec_config.buffer_size_db = max_sample_size;
916 }
917 }
921 if let Ok(stco) = StcoBox::try_from(self.trak.mdia.minf.stbl.co64.as_ref().unwrap()) {
922 self.trak.mdia.minf.stbl.stco = Some(stco);
923 self.trak.mdia.minf.stbl.co64 = None;
924 }
925
926 Ok(self.trak.clone())
927 }
928}