Skip to main content

oximedia_transcode/
examples.rs

1//! Comprehensive examples for common transcoding scenarios.
2//!
3//! This module provides pre-built example configurations for various
4//! transcoding use cases.
5
6use crate::{
7    AbrLadder, AudioFilter, LoudnessStandard, MultiPassMode, NormalizationConfig, QualityMode,
8    TranscodeBuilder, TranscodePipeline, VideoFilter,
9};
10
11/// `YouTube` upload optimization example.
12///
13/// # Example
14///
15/// ```rust,no_run
16/// use oximedia_transcode::examples;
17///
18/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
19/// let config = examples::youtube_1080p_upload(
20///     "input.mp4",
21///     "output.mp4"
22/// )?;
23/// # Ok(())
24/// # }
25/// ```
26#[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/// High-quality archival transcode example.
38#[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/// Social media optimized transcode (Instagram).
51#[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/// Broadcast-ready transcode with loudness normalization.
65#[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/// Low-latency streaming transcode.
80#[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/// 4K HDR transcode example.
92#[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
104/// Adaptive bitrate ladder for HLS/DASH.
105pub fn create_abr_ladder(input: &str, output_dir: &str) {
106    let ladder = AbrLadder::hls_standard();
107
108    // In a real implementation, would create multiple output files
109    // for each rung in the ladder
110    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
120/// Parallel batch transcode example.
121pub 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/// Deinterlace and upscale SD to HD.
143#[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/// Crop and resize for different aspect ratios.
160#[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) // Crop to 2.40:1
164        .scale(1920, 1080); // Letterbox to 16:9
165
166    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/// Audio ducking and mixing example.
176#[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/// Professional color grading workflow.
192#[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/// Film restoration workflow.
207#[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/// Podcast audio optimization.
226#[must_use]
227pub fn podcast_audio(input: &str, output: &str) -> crate::Result<TranscodePipeline> {
228    let _audio_filters = AudioFilter::new()
229        .highpass(80.0) // Remove low-frequency rumble
230        .compress(-18.0, 3.0) // Compress dynamic range
231        .normalize(-16.0); // Normalize to podcast standard
232
233    TranscodePipelineBuilder::new()
234        .input(input)
235        .output(output)
236        .audio_codec("opus")
237        .build()
238}
239
240/// Screen recording optimization.
241#[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/// Anime/animation optimized encoding.
253#[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/// Gaming video optimization.
265#[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/// Music video encoding.
277#[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/// News broadcast optimized.
293#[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/// Sports broadcast encoding.
308#[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/// E-learning content optimization.
320#[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/// Security camera footage optimization.
332#[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/// Time-lapse video creation.
344#[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/// Slow motion video creation.
357#[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/// Documentary film encoding.
371#[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/// Corporate video production.
387#[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/// Wedding video encoding.
399#[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/// Real estate video tour.
414#[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/// Medical/scientific video encoding.
426#[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/// Drone footage optimization.
438#[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/// GoPro/action camera footage.
453#[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/// Product demo video.
467#[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/// Recipe/cooking video.
479#[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); // Enhance food colors
482
483    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/// Travel vlog encoding.
493#[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/// Fashion/beauty video.
507#[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
522/// Placeholder builder struct (temporary for examples).
523struct 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}