scuffle_transmuxer/codecs/
av1.rs

1use bytes::{Buf, Bytes};
2use scuffle_av1::seq::SequenceHeaderObu;
3use scuffle_av1::{AV1CodecConfigurationRecord, ObuHeader, ObuType};
4use scuffle_bytes_util::BytesCursorExt;
5use scuffle_flv::video::header::VideoFrameType;
6use scuffle_mp4::DynBox;
7use scuffle_mp4::types::av01::Av01;
8use scuffle_mp4::types::av1c::Av1C;
9use scuffle_mp4::types::colr::{ColorType, Colr};
10use scuffle_mp4::types::stsd::{SampleEntry, VisualSampleEntry};
11use scuffle_mp4::types::trun::{TrunSample, TrunSampleFlag};
12
13use crate::TransmuxError;
14
15pub(crate) fn stsd_entry(config: AV1CodecConfigurationRecord) -> Result<(DynBox, SequenceHeaderObu), TransmuxError> {
16    let mut cursor = std::io::Cursor::new(config.config_obu.clone());
17    let header = ObuHeader::parse(&mut cursor)?;
18    let data = cursor.extract_bytes(header.size.unwrap_or(cursor.remaining() as u64) as usize)?;
19
20    if header.obu_type != ObuType::SequenceHeader {
21        return Err(TransmuxError::InvalidAv1DecoderConfigurationRecord);
22    }
23
24    let seq_obu = SequenceHeaderObu::parse(header, &mut std::io::Cursor::new(data))?;
25
26    // Unfortunate there does not seem to be a way to get the
27    // frame rate from the sequence header unless the timing_info is present
28    // Which it almost never is.
29    // So for AV1 we rely on the framerate being set in the scriptdata tag
30
31    Ok((
32        Av01::new(
33            SampleEntry::new(VisualSampleEntry::new(
34                seq_obu.max_frame_width as u16,
35                seq_obu.max_frame_height as u16,
36                Some(Colr::new(ColorType::Nclx {
37                    color_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            Av1C::new(config),
44            None,
45        )
46        .into(),
47        seq_obu,
48    ))
49}
50
51pub(crate) fn trun_sample(frame_type: VideoFrameType, duration: u32, data: &Bytes) -> Result<TrunSample, TransmuxError> {
52    Ok(TrunSample {
53        composition_time_offset: None,
54        duration: Some(duration),
55        flags: Some(TrunSampleFlag {
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        size: Some(data.len() as u32),
66    })
67}