mp4/
track.rs

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