scuffle_transmuxer/
define.rs

1use bytes::Bytes;
2use scuffle_aac::AudioObjectType;
3use scuffle_av1::AV1CodecConfigurationRecord;
4use scuffle_flv::audio::header::legacy::{SoundSize, SoundType};
5use scuffle_h264::AVCDecoderConfigurationRecord;
6use scuffle_h265::HEVCDecoderConfigurationRecord;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum VideoCodec {
10    /// <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter>
11    Avc { profile: u8, constraint_set: u8, level: u8 },
12    /// There is barely any documentation on this.
13    /// <https://hevcvideo.xp3.biz/html5_video.html>
14    Hevc {
15        general_profile_space: u8,
16        profile_compatibility: scuffle_h265::ProfileCompatibilityFlags,
17        profile: u8,
18        level: u8,
19        tier: bool,
20        constraint_indicator: u64,
21    },
22    /// <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#av1>
23    Av1 {
24        profile: u8,
25        level: u8,
26        tier: bool,
27        depth: u8,
28        monochrome: bool,
29        sub_sampling_x: bool,
30        sub_sampling_y: bool,
31        color_primaries: u8,
32        transfer_characteristics: u8,
33        matrix_coefficients: u8,
34        full_range_flag: bool,
35    },
36}
37
38impl std::fmt::Display for VideoCodec {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            VideoCodec::Avc {
42                profile,
43                constraint_set,
44                level,
45            } => write!(f, "avc1.{profile:02x}{constraint_set:02x}{level:02x}"),
46            VideoCodec::Hevc {
47                general_profile_space,
48                profile,
49                level,
50                tier,
51                profile_compatibility,
52                constraint_indicator,
53            } => {
54                let profile_compatibility = profile_compatibility
55                    .bits()
56                    .to_be_bytes()
57                    .into_iter()
58                    .filter(|b| *b != 0)
59                    .map(|b| format!("{b:x}"))
60                    .collect::<String>();
61                let constraint_indicator = constraint_indicator
62                    .to_be_bytes()
63                    .into_iter()
64                    .filter(|b| *b != 0)
65                    .map(|b| format!("{b:x}"))
66                    .collect::<Vec<_>>()
67                    .join(".");
68
69                write!(
70                    f,
71                    "hev1.{}{:x}.{}.{}{:x}.{}",
72                    match general_profile_space {
73                        1 => "A",
74                        2 => "B",
75                        3 => "C",
76                        _ => "",
77                    },
78                    profile, // 1 or 2 chars (hex)
79                    profile_compatibility,
80                    if *tier { 'H' } else { 'L' },
81                    level, // 1 or 2 chars (hex)
82                    constraint_indicator,
83                )
84            }
85            VideoCodec::Av1 {
86                profile,
87                level,
88                tier,
89                depth,
90                monochrome,
91                sub_sampling_x,
92                sub_sampling_y,
93                color_primaries,
94                transfer_characteristics,
95                matrix_coefficients,
96                full_range_flag,
97            } => write!(
98                f,
99                "av01.{}.{}{}.{:02}.{}.{}{}{}.{:02}.{:02}.{:02}.{}",
100                profile,
101                level,
102                if *tier { 'H' } else { 'M' },
103                depth,
104                if *monochrome { 1 } else { 0 },
105                if *sub_sampling_x { 1 } else { 0 },
106                if *sub_sampling_y { 1 } else { 0 },
107                if *monochrome { 1 } else { 0 },
108                color_primaries,
109                transfer_characteristics,
110                matrix_coefficients,
111                if *full_range_flag { 1 } else { 0 },
112            ),
113        }
114    }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum AudioCodec {
119    Aac { object_type: AudioObjectType },
120    Opus,
121}
122
123impl std::fmt::Display for AudioCodec {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        match self {
126            AudioCodec::Aac { object_type } => write!(f, "mp4a.40.{}", u16::from(*object_type)),
127            AudioCodec::Opus => write!(f, "opus"),
128        }
129    }
130}
131
132pub(crate) enum VideoSequenceHeader<'a> {
133    Avc(AVCDecoderConfigurationRecord<'a>),
134    Hevc(HEVCDecoderConfigurationRecord<'a>),
135    Av1(AV1CodecConfigurationRecord<'a>),
136}
137
138pub(crate) struct AudioSequenceHeader {
139    pub sound_size: SoundSize,
140    pub sound_type: SoundType,
141    pub data: AudioSequenceHeaderData,
142}
143
144pub(crate) enum AudioSequenceHeaderData {
145    Aac(Bytes),
146}
147
148#[derive(Debug, Clone)]
149pub enum TransmuxResult {
150    InitSegment {
151        video_settings: VideoSettings,
152        audio_settings: AudioSettings,
153        data: Bytes,
154    },
155    MediaSegment(MediaSegment),
156}
157
158#[derive(Debug, Clone, PartialEq)]
159pub struct VideoSettings {
160    pub width: u32,
161    pub height: u32,
162    pub framerate: f64,
163    pub bitrate: u32,
164    pub codec: VideoCodec,
165    pub timescale: u32,
166}
167
168#[derive(Debug, Clone, PartialEq)]
169pub struct AudioSettings {
170    pub sample_rate: u32,
171    pub channels: u8,
172    pub bitrate: u32,
173    pub codec: AudioCodec,
174    pub timescale: u32,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178pub enum MediaType {
179    Video,
180    Audio,
181}
182
183#[derive(Debug, Clone)]
184pub struct MediaSegment {
185    pub data: Bytes,
186    pub ty: MediaType,
187    pub keyframe: bool,
188    pub timestamp: u64,
189}
190
191impl TransmuxResult {
192    pub fn into_bytes(self) -> Bytes {
193        match self {
194            TransmuxResult::InitSegment { data, .. } => data,
195            TransmuxResult::MediaSegment(data) => data.data,
196        }
197    }
198}