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 { profile: u8, constraint_set: u8, level: u8 },
12 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 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, profile_compatibility,
80 if *tier { 'H' } else { 'L' },
81 level, 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}