scuffle_transmuxer/codecs/
hevc.rs

1use std::io;
2
3use bytes::Bytes;
4use scuffle_flv::video::header::VideoFrameType;
5use scuffle_h265::{HEVCDecoderConfigurationRecord, SpsRbsp};
6use scuffle_mp4::DynBox;
7use scuffle_mp4::types::colr::{ColorType, Colr};
8use scuffle_mp4::types::hev1::Hev1;
9use scuffle_mp4::types::hvcc::HvcC;
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: HEVCDecoderConfigurationRecord) -> Result<(DynBox, SpsRbsp), TransmuxError> {
16    let Some(sps) = config
17        .arrays
18        .iter()
19        .find(|a| a.nal_unit_type == scuffle_h265::NALUnitType::SpsNut)
20        .and_then(|v| v.nalus.first())
21    else {
22        return Err(TransmuxError::InvalidHEVCDecoderConfigurationRecord);
23    };
24
25    let sps = scuffle_h265::SpsNALUnit::parse(io::Cursor::new(sps.clone()))?.rbsp;
26
27    let colr = sps.vui_parameters.as_ref().map(|v| &v.video_signal_type).map(|color_config| {
28        Colr::new(ColorType::Nclx {
29            color_primaries: color_config.colour_primaries as u16,
30            matrix_coefficients: color_config.matrix_coeffs as u16,
31            transfer_characteristics: color_config.transfer_characteristics as u16,
32            full_range_flag: color_config.video_full_range_flag,
33        })
34    });
35
36    Ok((
37        Hev1::new(
38            SampleEntry::new(VisualSampleEntry::new(
39                sps.cropped_width() as u16,
40                sps.cropped_height() as u16,
41                colr,
42            )),
43            HvcC::new(config),
44            None,
45        )
46        .into(),
47        sps,
48    ))
49}
50
51pub(crate) fn trun_sample(
52    frame_type: VideoFrameType,
53    composition_time: i32,
54    duration: u32,
55    data: &Bytes,
56) -> Result<TrunSample, TransmuxError> {
57    Ok(TrunSample {
58        composition_time_offset: Some(composition_time as i64),
59        duration: Some(duration),
60        flags: Some(TrunSampleFlag {
61            reserved: 0,
62            is_leading: 0,
63            sample_degradation_priority: 0,
64            sample_depends_on: if frame_type == VideoFrameType::KeyFrame { 2 } else { 1 },
65            sample_has_redundancy: 0,
66            sample_is_depended_on: 0,
67            sample_is_non_sync_sample: frame_type != VideoFrameType::KeyFrame,
68            sample_padding_value: 0,
69        }),
70        size: Some(data.len() as u32),
71    })
72}