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