1use crate::{
7 AbrLadder, AudioFilter, LoudnessStandard, MultiPassMode, NormalizationConfig, QualityMode,
8 TranscodeBuilder, TranscodePipeline, VideoFilter,
9};
10
11#[must_use]
27pub fn youtube_1080p_upload(input: &str, output: &str) -> TranscodePipeline {
28 TranscodePipelineBuilder::new()
29 .input(input)
30 .output(output)
31 .video_codec("h264")
32 .audio_codec("aac")
33 .quality(QualityMode::High)
34 .build()
35 .expect("Failed to build pipeline")
36}
37
38#[must_use]
40pub fn archival_transcode(input: &str, output: &str) -> TranscodePipeline {
41 TranscodePipelineBuilder::new()
42 .input(input)
43 .output(output)
44 .video_codec("vp9")
45 .audio_codec("opus")
46 .quality(QualityMode::VeryHigh)
47 .multipass(MultiPassMode::TwoPass)
48 .build()
49 .expect("Failed to build pipeline")
50}
51
52#[must_use]
54pub fn instagram_square(input: &str, output: &str) -> TranscodePipeline {
55 let _video_filters = VideoFilter::new().scale(1080, 1080).sharpen(0.5);
56
57 TranscodePipelineBuilder::new()
58 .input(input)
59 .output(output)
60 .video_codec("h264")
61 .audio_codec("aac")
62 .quality(QualityMode::High)
63 .build()
64 .expect("Failed to build pipeline")
65}
66
67#[must_use]
69pub fn broadcast_hd_ebu(input: &str, output: &str) -> TranscodePipeline {
70 let normalization = NormalizationConfig::new(LoudnessStandard::EbuR128);
71
72 TranscodePipelineBuilder::new()
73 .input(input)
74 .output(output)
75 .video_codec("h264")
76 .audio_codec("aac")
77 .quality(QualityMode::VeryHigh)
78 .normalization(normalization)
79 .build()
80 .expect("Failed to build pipeline")
81}
82
83#[must_use]
85pub fn low_latency_stream(input: &str, output: &str) -> TranscodePipeline {
86 TranscodePipelineBuilder::new()
87 .input(input)
88 .output(output)
89 .video_codec("h264")
90 .audio_codec("aac")
91 .quality(QualityMode::Medium)
92 .build()
93 .expect("Failed to build pipeline")
94}
95
96#[must_use]
98pub fn hdr_4k_transcode(input: &str, output: &str) -> TranscodePipeline {
99 TranscodePipelineBuilder::new()
100 .input(input)
101 .output(output)
102 .video_codec("vp9")
103 .audio_codec("opus")
104 .quality(QualityMode::VeryHigh)
105 .multipass(MultiPassMode::TwoPass)
106 .build()
107 .expect("Failed to build pipeline")
108}
109
110pub fn create_abr_ladder(input: &str, output_dir: &str) {
112 let ladder = AbrLadder::hls_standard();
113
114 for rung in &ladder.rungs {
117 let output = format!("{}/output_{}p.mp4", output_dir, rung.height);
118 let _pipeline = TranscodePipelineBuilder::new()
119 .input(input)
120 .output(&output)
121 .video_codec(&rung.codec)
122 .build()
123 .expect("Failed to build pipeline");
124 }
125}
126
127pub fn batch_transcode_parallel(inputs: Vec<&str>, outputs: Vec<&str>) {
129 use crate::ParallelEncodeBuilder;
130
131 let mut builder = ParallelEncodeBuilder::new().max_parallel(4);
132
133 for (input, output) in inputs.iter().zip(outputs.iter()) {
134 let config = TranscodeBuilder::new()
135 .input(*input)
136 .output(*output)
137 .video_codec("h264")
138 .audio_codec("aac")
139 .quality(QualityMode::Medium)
140 .build()
141 .expect("Failed to build config");
142
143 builder = builder.add_job(config);
144 }
145
146 let _encoder = builder.build();
147}
148
149#[must_use]
151pub fn sd_to_hd_upscale(input: &str, output: &str) -> TranscodePipeline {
152 let _video_filters = VideoFilter::new()
153 .deinterlace()
154 .scale(1920, 1080)
155 .sharpen(1.0);
156
157 TranscodePipelineBuilder::new()
158 .input(input)
159 .output(output)
160 .video_codec("h264")
161 .audio_codec("aac")
162 .quality(QualityMode::High)
163 .build()
164 .expect("Failed to build pipeline")
165}
166
167#[must_use]
169pub fn crop_to_widescreen(input: &str, output: &str) -> TranscodePipeline {
170 let _video_filters = VideoFilter::new()
171 .crop(1920, 800, 0, 140) .scale(1920, 1080); TranscodePipelineBuilder::new()
175 .input(input)
176 .output(output)
177 .video_codec("h264")
178 .audio_codec("aac")
179 .quality(QualityMode::High)
180 .build()
181 .expect("Failed to build pipeline")
182}
183
184#[must_use]
186pub fn audio_ducking(input: &str, output: &str) -> TranscodePipeline {
187 let _audio_filters = AudioFilter::new()
188 .compress(-20.0, 4.0)
189 .normalize(-23.0)
190 .fade_in(1.0)
191 .fade_out(58.0, 2.0);
192
193 TranscodePipelineBuilder::new()
194 .input(input)
195 .output(output)
196 .audio_codec("opus")
197 .build()
198 .expect("Failed to build pipeline")
199}
200
201#[must_use]
203pub fn color_grade(input: &str, output: &str) -> TranscodePipeline {
204 let _video_filters = VideoFilter::new().color_correct(0.05, 1.1, 1.15);
205
206 TranscodePipelineBuilder::new()
207 .input(input)
208 .output(output)
209 .video_codec("h264")
210 .audio_codec("aac")
211 .quality(QualityMode::VeryHigh)
212 .multipass(MultiPassMode::TwoPass)
213 .build()
214 .expect("Failed to build pipeline")
215}
216
217#[must_use]
219pub fn film_restoration(input: &str, output: &str) -> TranscodePipeline {
220 let _video_filters = VideoFilter::new()
221 .deinterlace()
222 .denoise(2.0)
223 .sharpen(0.8)
224 .color_correct(0.0, 1.05, 1.0);
225
226 TranscodePipelineBuilder::new()
227 .input(input)
228 .output(output)
229 .video_codec("vp9")
230 .audio_codec("opus")
231 .quality(QualityMode::VeryHigh)
232 .multipass(MultiPassMode::ThreePass)
233 .build()
234 .expect("Failed to build pipeline")
235}
236
237#[must_use]
239pub fn podcast_audio(input: &str, output: &str) -> TranscodePipeline {
240 let _audio_filters = AudioFilter::new()
241 .highpass(80.0) .compress(-18.0, 3.0) .normalize(-16.0); TranscodePipelineBuilder::new()
246 .input(input)
247 .output(output)
248 .audio_codec("opus")
249 .build()
250 .expect("Failed to build pipeline")
251}
252
253#[must_use]
255pub fn screen_recording_optimize(input: &str, output: &str) -> TranscodePipeline {
256 TranscodePipelineBuilder::new()
257 .input(input)
258 .output(output)
259 .video_codec("h264")
260 .audio_codec("opus")
261 .quality(QualityMode::High)
262 .build()
263 .expect("Failed to build pipeline")
264}
265
266#[must_use]
268pub fn anime_encode(input: &str, output: &str) -> TranscodePipeline {
269 TranscodePipelineBuilder::new()
270 .input(input)
271 .output(output)
272 .video_codec("h264")
273 .audio_codec("opus")
274 .quality(QualityMode::VeryHigh)
275 .build()
276 .expect("Failed to build pipeline")
277}
278
279#[must_use]
281pub fn gaming_video(input: &str, output: &str) -> TranscodePipeline {
282 TranscodePipelineBuilder::new()
283 .input(input)
284 .output(output)
285 .video_codec("h264")
286 .audio_codec("opus")
287 .quality(QualityMode::High)
288 .build()
289 .expect("Failed to build pipeline")
290}
291
292#[must_use]
294pub fn music_video(input: &str, output: &str) -> TranscodePipeline {
295 let normalization = NormalizationConfig::new(LoudnessStandard::Spotify);
296
297 TranscodePipelineBuilder::new()
298 .input(input)
299 .output(output)
300 .video_codec("vp9")
301 .audio_codec("opus")
302 .quality(QualityMode::VeryHigh)
303 .normalization(normalization)
304 .multipass(MultiPassMode::TwoPass)
305 .build()
306 .expect("Failed to build pipeline")
307}
308
309#[must_use]
311pub fn news_broadcast(input: &str, output: &str) -> TranscodePipeline {
312 let normalization = NormalizationConfig::new(LoudnessStandard::AtscA85);
313
314 TranscodePipelineBuilder::new()
315 .input(input)
316 .output(output)
317 .video_codec("h264")
318 .audio_codec("aac")
319 .quality(QualityMode::VeryHigh)
320 .normalization(normalization)
321 .build()
322 .expect("Failed to build pipeline")
323}
324
325#[must_use]
327pub fn sports_broadcast(input: &str, output: &str) -> TranscodePipeline {
328 TranscodePipelineBuilder::new()
329 .input(input)
330 .output(output)
331 .video_codec("h264")
332 .audio_codec("aac")
333 .quality(QualityMode::VeryHigh)
334 .build()
335 .expect("Failed to build pipeline")
336}
337
338#[must_use]
340pub fn elearning_content(input: &str, output: &str) -> TranscodePipeline {
341 TranscodePipelineBuilder::new()
342 .input(input)
343 .output(output)
344 .video_codec("h264")
345 .audio_codec("aac")
346 .quality(QualityMode::Medium)
347 .build()
348 .expect("Failed to build pipeline")
349}
350
351#[must_use]
353pub fn security_footage(input: &str, output: &str) -> TranscodePipeline {
354 TranscodePipelineBuilder::new()
355 .input(input)
356 .output(output)
357 .video_codec("h264")
358 .audio_codec("opus")
359 .quality(QualityMode::Low)
360 .build()
361 .expect("Failed to build pipeline")
362}
363
364#[must_use]
366pub fn timelapse_create(input: &str, output: &str) -> TranscodePipeline {
367 let _video_filters = VideoFilter::new().framerate(30.0);
368
369 TranscodePipelineBuilder::new()
370 .input(input)
371 .output(output)
372 .video_codec("h264")
373 .quality(QualityMode::High)
374 .build()
375 .expect("Failed to build pipeline")
376}
377
378#[must_use]
380pub fn slow_motion_create(input: &str, output: &str) -> TranscodePipeline {
381 let _video_filters = VideoFilter::new().framerate(120.0);
382
383 TranscodePipelineBuilder::new()
384 .input(input)
385 .output(output)
386 .video_codec("h264")
387 .audio_codec("aac")
388 .quality(QualityMode::VeryHigh)
389 .build()
390 .expect("Failed to build pipeline")
391}
392
393#[must_use]
395pub fn documentary_encode(input: &str, output: &str) -> TranscodePipeline {
396 let normalization = NormalizationConfig::new(LoudnessStandard::EbuR128);
397
398 TranscodePipelineBuilder::new()
399 .input(input)
400 .output(output)
401 .video_codec("vp9")
402 .audio_codec("opus")
403 .quality(QualityMode::VeryHigh)
404 .normalization(normalization)
405 .multipass(MultiPassMode::TwoPass)
406 .build()
407 .expect("Failed to build pipeline")
408}
409
410#[must_use]
412pub fn corporate_video(input: &str, output: &str) -> TranscodePipeline {
413 TranscodePipelineBuilder::new()
414 .input(input)
415 .output(output)
416 .video_codec("h264")
417 .audio_codec("aac")
418 .quality(QualityMode::High)
419 .build()
420 .expect("Failed to build pipeline")
421}
422
423#[must_use]
425pub fn wedding_video(input: &str, output: &str) -> TranscodePipeline {
426 let _video_filters = VideoFilter::new().color_correct(0.1, 1.05, 1.1);
427
428 TranscodePipelineBuilder::new()
429 .input(input)
430 .output(output)
431 .video_codec("h264")
432 .audio_codec("aac")
433 .quality(QualityMode::VeryHigh)
434 .multipass(MultiPassMode::TwoPass)
435 .build()
436 .expect("Failed to build pipeline")
437}
438
439#[must_use]
441pub fn real_estate_tour(input: &str, output: &str) -> TranscodePipeline {
442 TranscodePipelineBuilder::new()
443 .input(input)
444 .output(output)
445 .video_codec("h264")
446 .audio_codec("aac")
447 .quality(QualityMode::High)
448 .build()
449 .expect("Failed to build pipeline")
450}
451
452#[must_use]
454pub fn medical_video(input: &str, output: &str) -> TranscodePipeline {
455 TranscodePipelineBuilder::new()
456 .input(input)
457 .output(output)
458 .video_codec("h264")
459 .audio_codec("aac")
460 .quality(QualityMode::VeryHigh)
461 .build()
462 .expect("Failed to build pipeline")
463}
464
465#[must_use]
467pub fn drone_footage(input: &str, output: &str) -> TranscodePipeline {
468 let _video_filters = VideoFilter::new().denoise(1.0).sharpen(0.5);
469
470 TranscodePipelineBuilder::new()
471 .input(input)
472 .output(output)
473 .video_codec("vp9")
474 .audio_codec("opus")
475 .quality(QualityMode::VeryHigh)
476 .multipass(MultiPassMode::TwoPass)
477 .build()
478 .expect("Failed to build pipeline")
479}
480
481#[must_use]
483pub fn action_camera_footage(input: &str, output: &str) -> TranscodePipeline {
484 let _video_filters = VideoFilter::new().denoise(1.5);
485
486 TranscodePipelineBuilder::new()
487 .input(input)
488 .output(output)
489 .video_codec("h264")
490 .audio_codec("aac")
491 .quality(QualityMode::High)
492 .build()
493 .expect("Failed to build pipeline")
494}
495
496#[must_use]
498pub fn product_demo(input: &str, output: &str) -> TranscodePipeline {
499 TranscodePipelineBuilder::new()
500 .input(input)
501 .output(output)
502 .video_codec("h264")
503 .audio_codec("aac")
504 .quality(QualityMode::High)
505 .build()
506 .expect("Failed to build pipeline")
507}
508
509#[must_use]
511pub fn cooking_video(input: &str, output: &str) -> TranscodePipeline {
512 let _video_filters = VideoFilter::new().color_correct(0.05, 1.1, 1.2); TranscodePipelineBuilder::new()
515 .input(input)
516 .output(output)
517 .video_codec("h264")
518 .audio_codec("aac")
519 .quality(QualityMode::High)
520 .build()
521 .expect("Failed to build pipeline")
522}
523
524#[must_use]
526pub fn travel_vlog(input: &str, output: &str) -> TranscodePipeline {
527 let _video_filters = VideoFilter::new().color_correct(0.05, 1.05, 1.1);
528
529 TranscodePipelineBuilder::new()
530 .input(input)
531 .output(output)
532 .video_codec("h264")
533 .audio_codec("aac")
534 .quality(QualityMode::High)
535 .build()
536 .expect("Failed to build pipeline")
537}
538
539#[must_use]
541pub fn fashion_video(input: &str, output: &str) -> TranscodePipeline {
542 let _video_filters = VideoFilter::new()
543 .sharpen(0.5)
544 .color_correct(0.1, 1.05, 1.05);
545
546 TranscodePipelineBuilder::new()
547 .input(input)
548 .output(output)
549 .video_codec("h264")
550 .audio_codec("aac")
551 .quality(QualityMode::VeryHigh)
552 .build()
553 .expect("Failed to build pipeline")
554}
555
556struct TranscodePipelineBuilder {
558 input: String,
559 output: String,
560 video_codec: Option<String>,
561 audio_codec: Option<String>,
562 quality: Option<QualityMode>,
563 multipass: Option<MultiPassMode>,
564 normalization: Option<NormalizationConfig>,
565}
566
567impl TranscodePipelineBuilder {
568 fn new() -> Self {
569 Self {
570 input: String::new(),
571 output: String::new(),
572 video_codec: None,
573 audio_codec: None,
574 quality: None,
575 multipass: None,
576 normalization: None,
577 }
578 }
579
580 fn input(mut self, input: &str) -> Self {
581 self.input = input.to_string();
582 self
583 }
584
585 fn output(mut self, output: &str) -> Self {
586 self.output = output.to_string();
587 self
588 }
589
590 fn video_codec(mut self, codec: &str) -> Self {
591 self.video_codec = Some(codec.to_string());
592 self
593 }
594
595 fn audio_codec(mut self, codec: &str) -> Self {
596 self.audio_codec = Some(codec.to_string());
597 self
598 }
599
600 fn quality(mut self, quality: QualityMode) -> Self {
601 self.quality = Some(quality);
602 self
603 }
604
605 fn multipass(mut self, mode: MultiPassMode) -> Self {
606 self.multipass = Some(mode);
607 self
608 }
609
610 fn normalization(mut self, config: NormalizationConfig) -> Self {
611 self.normalization = Some(config);
612 self
613 }
614
615 fn build(self) -> crate::Result<TranscodePipeline> {
616 TranscodePipeline::builder()
617 .input(&self.input)
618 .output(&self.output)
619 .build()
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626
627 #[test]
628 fn test_youtube_example() {
629 let _pipeline = youtube_1080p_upload("/tmp/input.mp4", "/tmp/output.mp4");
630 }
631
632 #[test]
633 fn test_archival_example() {
634 let _pipeline = archival_transcode("/tmp/input.mp4", "/tmp/output.mkv");
635 }
636
637 #[test]
638 fn test_instagram_example() {
639 let _pipeline = instagram_square("/tmp/input.mp4", "/tmp/output.mp4");
640 }
641}