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, language: String::from("und"), 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, language: String::from("und"), 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, language: String::from("und"), 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, language: String::from("und"), 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, language: String::from("und"), 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 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 } 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 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(); 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#[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, ..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 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 }
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}