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 Avc {
12 profile: u8,
13 constraint_set: u8,
14 level: u8,
15 },
16 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 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, profile_compatibility,
84 if *tier { 'H' } else { 'L' },
85 level, 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}