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