1use crate::error::{PackagerError, PackagerResult};
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6use std::time::Duration;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
10pub enum PackagingFormat {
11 HlsTs,
13 #[default]
15 HlsFmp4,
16 Dash,
18 Both,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24pub enum SegmentFormat {
25 MpegTs,
27 Fmp4,
29 Cmaf,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
35pub enum EncryptionMethod {
36 #[default]
38 None,
39 Aes128,
41 SampleAes,
43 Cenc,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct BitrateEntry {
50 pub bitrate: u32,
52 pub width: u32,
54 pub height: u32,
56 pub codec: String,
58 pub framerate: Option<f64>,
60}
61
62impl BitrateEntry {
63 #[must_use]
65 pub fn new(bitrate: u32, width: u32, height: u32, codec: &str) -> Self {
66 Self {
67 bitrate,
68 width,
69 height,
70 codec: codec.to_string(),
71 framerate: None,
72 }
73 }
74
75 #[must_use]
77 pub fn with_framerate(mut self, fps: f64) -> Self {
78 self.framerate = Some(fps);
79 self
80 }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct BitrateLadder {
86 pub entries: Vec<BitrateEntry>,
88 pub auto_generate: bool,
90}
91
92impl Default for BitrateLadder {
93 fn default() -> Self {
94 Self {
95 entries: Vec::new(),
96 auto_generate: true,
97 }
98 }
99}
100
101impl BitrateLadder {
102 #[must_use]
104 pub fn new() -> Self {
105 Self::default()
106 }
107
108 pub fn add_entry(&mut self, entry: BitrateEntry) {
110 self.entries.push(entry);
111 }
112
113 #[must_use]
115 pub fn with_auto_generate(mut self, enabled: bool) -> Self {
116 self.auto_generate = enabled;
117 self
118 }
119
120 pub fn validate(&self) -> PackagerResult<()> {
122 if !self.auto_generate && self.entries.is_empty() {
123 return Err(PackagerError::invalid_config(
124 "Bitrate ladder has no entries and auto-generation is disabled",
125 ));
126 }
127
128 for entry in &self.entries {
129 if entry.bitrate == 0 {
130 return Err(PackagerError::invalid_config("Bitrate cannot be zero"));
131 }
132 if entry.width == 0 || entry.height == 0 {
133 return Err(PackagerError::invalid_config(
134 "Width and height must be greater than zero",
135 ));
136 }
137 }
138
139 Ok(())
140 }
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SegmentConfig {
146 pub duration: Duration,
148 pub format: SegmentFormat,
150 pub keyframe_alignment: bool,
152 pub fast_start: bool,
154}
155
156impl Default for SegmentConfig {
157 fn default() -> Self {
158 Self {
159 duration: Duration::from_secs(6),
160 format: SegmentFormat::Fmp4,
161 keyframe_alignment: true,
162 fast_start: true,
163 }
164 }
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct EncryptionConfig {
170 pub method: EncryptionMethod,
172 pub key: Option<Vec<u8>>,
174 pub key_uri: Option<String>,
176 pub iv: Option<Vec<u8>>,
178}
179
180impl Default for EncryptionConfig {
181 fn default() -> Self {
182 Self {
183 method: EncryptionMethod::None,
184 key: None,
185 key_uri: None,
186 iv: None,
187 }
188 }
189}
190
191impl EncryptionConfig {
192 #[must_use]
194 pub fn is_enabled(&self) -> bool {
195 self.method != EncryptionMethod::None
196 }
197
198 pub fn validate(&self) -> PackagerResult<()> {
200 if !self.is_enabled() {
201 return Ok(());
202 }
203
204 match self.method {
205 EncryptionMethod::None => Ok(()),
206 EncryptionMethod::Aes128 | EncryptionMethod::SampleAes => {
207 if self.key.is_none() {
208 return Err(PackagerError::invalid_config("Encryption key is required"));
209 }
210 if let Some(key) = &self.key {
211 if key.len() != 16 {
212 return Err(PackagerError::invalid_config(
213 "AES-128 key must be 16 bytes",
214 ));
215 }
216 }
217 if self.key_uri.is_none() {
218 return Err(PackagerError::invalid_config("Key URI is required for HLS"));
219 }
220 Ok(())
221 }
222 EncryptionMethod::Cenc => {
223 if self.key.is_none() {
224 return Err(PackagerError::invalid_config("Encryption key is required"));
225 }
226 Ok(())
227 }
228 }
229 }
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct OutputConfig {
235 pub directory: PathBuf,
237 pub base_url: Option<String>,
239 pub s3_upload: bool,
241 pub s3_bucket: Option<String>,
243 pub s3_prefix: Option<String>,
245 pub keep_local: bool,
247 pub max_segments: Option<usize>,
249}
250
251impl Default for OutputConfig {
252 fn default() -> Self {
253 Self {
254 directory: PathBuf::from("output"),
255 base_url: None,
256 s3_upload: false,
257 s3_bucket: None,
258 s3_prefix: None,
259 keep_local: true,
260 max_segments: None,
261 }
262 }
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize, Default)]
267pub struct PackagerConfig {
268 pub format: PackagingFormat,
270 pub ladder: BitrateLadder,
272 pub segment: SegmentConfig,
274 pub encryption: EncryptionConfig,
276 pub output: OutputConfig,
278 pub low_latency: bool,
280 pub manifest_versioning: bool,
282 #[serde(skip)]
287 pub variant_set: Option<crate::variant_stream::VariantSet>,
288}
289
290impl PackagerConfig {
291 #[must_use]
293 pub fn new() -> Self {
294 Self::default()
295 }
296
297 #[must_use]
299 pub fn with_format(mut self, format: PackagingFormat) -> Self {
300 self.format = format;
301 self
302 }
303
304 #[must_use]
306 pub fn with_ladder(mut self, ladder: BitrateLadder) -> Self {
307 self.ladder = ladder;
308 self
309 }
310
311 #[must_use]
313 pub fn with_segment_config(mut self, segment: SegmentConfig) -> Self {
314 self.segment = segment;
315 self
316 }
317
318 #[must_use]
320 pub fn with_encryption(mut self, encryption: EncryptionConfig) -> Self {
321 self.encryption = encryption;
322 self
323 }
324
325 #[must_use]
327 pub fn with_output(mut self, output: OutputConfig) -> Self {
328 self.output = output;
329 self
330 }
331
332 #[must_use]
334 pub fn with_low_latency(mut self, enabled: bool) -> Self {
335 self.low_latency = enabled;
336 self
337 }
338
339 #[must_use]
341 pub fn with_variant_set(mut self, vs: crate::variant_stream::VariantSet) -> Self {
342 self.variant_set = Some(vs);
343 self
344 }
345
346 pub fn validate(&self) -> PackagerResult<()> {
348 self.ladder.validate()?;
349 self.encryption.validate()?;
350
351 if self.output.s3_upload && self.output.s3_bucket.is_none() {
352 return Err(PackagerError::invalid_config("S3 bucket is required"));
353 }
354
355 if let Some(vs) = &self.variant_set {
356 vs.validate()?;
357 }
358
359 Ok(())
360 }
361}