scuffle_transmuxer/codecs/
hevc.rs

1use std::io;
2
3use bytes::Bytes;
4use isobmff::UnknownBox;
5use isobmff::boxes::{
6    ColourInformation, ColourInformationBox, NclxColourInformation, PixelAspectRatioBox, SampleEntry, SampleFlags,
7    TrackRunBoxSample, VisualSampleEntry,
8};
9use scuffle_flv::video::header::VideoFrameType;
10use scuffle_h265::boxes::{HEVCConfigurationBox, HEVCSampleEntryHev1};
11use scuffle_h265::{HEVCDecoderConfigurationRecord, SpsRbsp};
12
13use crate::TransmuxError;
14
15pub(crate) fn stsd_entry(config: HEVCDecoderConfigurationRecord) -> Result<(HEVCSampleEntryHev1, 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 mut sub_boxes = vec![UnknownBox::try_from_box(PixelAspectRatioBox::default())?];
28
29    if let Some(cc) = sps.vui_parameters.as_ref().map(|v| &v.video_signal_type) {
30        let colr = ColourInformationBox {
31            colour_info: ColourInformation::Nclx(NclxColourInformation {
32                colour_primaries: cc.colour_primaries as u16,
33                matrix_coefficients: cc.matrix_coeffs as u16,
34                transfer_characteristics: cc.transfer_characteristics as u16,
35                full_range_flag: cc.video_full_range_flag,
36            }),
37        };
38        sub_boxes.push(UnknownBox::try_from_box(colr)?);
39    }
40
41    let visual_sample_entry = VisualSampleEntry::new(
42        SampleEntry::default(),
43        sps.cropped_width() as u16,
44        sps.cropped_height() as u16,
45        [0; 32],
46    );
47
48    let hev1 = HEVCSampleEntryHev1 {
49        sample_entry: visual_sample_entry,
50        config: HEVCConfigurationBox::new(config),
51        mpeg4_extension: None,
52        sub_boxes,
53    };
54
55    Ok((hev1, sps))
56}
57
58pub(crate) fn trun_sample(
59    frame_type: VideoFrameType,
60    composition_time: i32,
61    duration: u32,
62    data: &Bytes,
63) -> Result<TrackRunBoxSample, TransmuxError> {
64    Ok(TrackRunBoxSample {
65        sample_duration: Some(duration),
66        sample_composition_time_offset: Some(composition_time as i64),
67        sample_flags: Some(SampleFlags {
68            reserved: 0,
69            is_leading: 0,
70            sample_degradation_priority: 0,
71            sample_depends_on: if frame_type == VideoFrameType::KeyFrame { 2 } else { 1 },
72            sample_has_redundancy: 0,
73            sample_is_depended_on: 0,
74            sample_is_non_sync_sample: frame_type != VideoFrameType::KeyFrame,
75            sample_padding_value: 0,
76        }),
77        sample_size: Some(data.len() as u32),
78    })
79}