scuffle_transmuxer/codecs/
avc.rs

1use bytes::Bytes;
2use isobmff::UnknownBox;
3use isobmff::boxes::{
4    ColourInformation, ColourInformationBox, NclxColourInformation, PixelAspectRatioBox, SampleEntry, SampleFlags,
5    TrackRunBoxSample, VisualSampleEntry,
6};
7use scuffle_flv::video::header::VideoFrameType;
8use scuffle_h264::boxes::{AVCConfigurationBox, AVCSampleEntry1};
9use scuffle_h264::{AVCDecoderConfigurationRecord, Sps};
10
11use crate::TransmuxError;
12
13pub(crate) fn stsd_entry<'a>(
14    config: AVCDecoderConfigurationRecord<'a>,
15    sps: &'a Sps,
16) -> Result<AVCSampleEntry1<'a>, TransmuxError> {
17    if config.sps.is_empty() {
18        return Err(TransmuxError::InvalidAVCDecoderConfigurationRecord);
19    }
20
21    let mut sub_boxes = vec![UnknownBox::try_from_box(PixelAspectRatioBox::default())?];
22
23    if let Some(cc) = sps.color_config.as_ref() {
24        let colr = ColourInformationBox {
25            colour_info: ColourInformation::Nclx(NclxColourInformation {
26                colour_primaries: cc.color_primaries as u16,
27                matrix_coefficients: cc.matrix_coefficients as u16,
28                transfer_characteristics: cc.transfer_characteristics as u16,
29                full_range_flag: cc.video_full_range_flag,
30            }),
31        };
32        sub_boxes.push(UnknownBox::try_from_box(colr)?);
33    }
34
35    let visual_sample_entry =
36        VisualSampleEntry::new(SampleEntry::default(), sps.width() as u16, sps.height() as u16, [0; 32]);
37
38    Ok(AVCSampleEntry1 {
39        visual_sample_entry,
40        config: AVCConfigurationBox::new(config),
41        mpeg4_extension: None,
42        sub_boxes,
43    })
44}
45
46pub(crate) fn trun_sample(
47    frame_type: VideoFrameType,
48    composition_time: u32,
49    duration: u32,
50    data: &Bytes,
51) -> Result<TrackRunBoxSample, TransmuxError> {
52    Ok(TrackRunBoxSample {
53        sample_duration: Some(duration),
54        sample_size: Some(data.len() as u32),
55        sample_flags: Some(SampleFlags {
56            reserved: 0,
57            is_leading: 0,
58            sample_degradation_priority: 0,
59            sample_depends_on: if frame_type == VideoFrameType::KeyFrame { 2 } else { 1 },
60            sample_has_redundancy: 0,
61            sample_is_depended_on: 0,
62            sample_is_non_sync_sample: frame_type != VideoFrameType::KeyFrame,
63            sample_padding_value: 0,
64        }),
65        sample_composition_time_offset: Some(composition_time as i64),
66    })
67}