scuffle_transmuxer/codecs/
av1.rs

1use bytes::Bytes;
2use isobmff::UnknownBox;
3use isobmff::boxes::{
4    ColourInformation, ColourInformationBox, NclxColourInformation, PixelAspectRatioBox, SampleEntry, SampleFlags,
5    TrackRunBoxSample, VisualSampleEntry,
6};
7use scuffle_av1::boxes::{AV1CodecConfigurationBox, AV1SampleEntry};
8use scuffle_av1::seq::SequenceHeaderObu;
9use scuffle_av1::{AV1CodecConfigurationRecord, ObuHeader, ObuType};
10use scuffle_bytes_util::zero_copy::ZeroCopyReader;
11use scuffle_flv::video::header::VideoFrameType;
12
13use crate::TransmuxError;
14
15pub(crate) fn stsd_entry(config: AV1CodecConfigurationRecord) -> Result<(AV1SampleEntry, SequenceHeaderObu), TransmuxError> {
16    let mut config_obu_reader = scuffle_bytes_util::zero_copy::Slice::from(config.config_obu.as_bytes());
17    let header = ObuHeader::parse(&mut config_obu_reader.as_std())?;
18    let data = if let Some(size) = header.size {
19        config_obu_reader.try_read(size as usize)?
20    } else {
21        config_obu_reader.try_read_to_end()?
22    };
23
24    if header.obu_type != ObuType::SequenceHeader {
25        return Err(TransmuxError::InvalidAv1DecoderConfigurationRecord);
26    }
27
28    let seq_obu = SequenceHeaderObu::parse(header, &mut std::io::Cursor::new(data))?;
29
30    // Unfortunate there does not seem to be a way to get the
31    // frame rate from the sequence header unless the timing_info is present
32    // Which it almost never is.
33    // So for AV1 we rely on the framerate being set in the scriptdata tag
34
35    let colr = ColourInformationBox {
36        colour_info: ColourInformation::Nclx(NclxColourInformation {
37            colour_primaries: seq_obu.color_config.color_primaries as u16,
38            matrix_coefficients: seq_obu.color_config.matrix_coefficients as u16,
39            transfer_characteristics: seq_obu.color_config.transfer_characteristics as u16,
40            full_range_flag: seq_obu.color_config.full_color_range,
41        }),
42    };
43
44    let visual_sample_entry = VisualSampleEntry::new(
45        SampleEntry::default(),
46        seq_obu.max_frame_width as u16,
47        seq_obu.max_frame_height as u16,
48        [0; 32],
49    );
50
51    let av01 = AV1SampleEntry {
52        sample_entry: visual_sample_entry,
53        av1c: AV1CodecConfigurationBox::new(config),
54        sub_boxes: vec![
55            UnknownBox::try_from_box(PixelAspectRatioBox::default())?,
56            UnknownBox::try_from_box(colr)?,
57        ],
58    };
59
60    Ok((av01, seq_obu))
61}
62
63pub(crate) fn trun_sample(
64    frame_type: VideoFrameType,
65    duration: u32,
66    data: &Bytes,
67) -> Result<TrackRunBoxSample, TransmuxError> {
68    Ok(TrackRunBoxSample {
69        sample_composition_time_offset: None,
70        sample_duration: Some(duration),
71        sample_flags: Some(SampleFlags {
72            reserved: 0,
73            is_leading: 0,
74            sample_degradation_priority: 0,
75            sample_depends_on: if frame_type == VideoFrameType::KeyFrame { 2 } else { 1 },
76            sample_has_redundancy: 0,
77            sample_is_depended_on: 0,
78            sample_is_non_sync_sample: frame_type != VideoFrameType::KeyFrame,
79            sample_padding_value: 0,
80        }),
81        sample_size: Some(data.len() as u32),
82    })
83}