ez_ffmpeg/core/context/output.rs
1use std::collections::HashMap;
2use ffmpeg_sys_next::{AVRational, AVSampleFormat};
3use crate::filter::frame_pipeline::FramePipeline;
4
5unsafe impl Send for Output {}
6
7pub struct Output {
8 /// The URL of the output destination.
9 ///
10 /// This specifies where the output stream will be written. It can be:
11 /// - A local file path (e.g., `file:///path/to/output.mp4`).
12 /// - A network destination (e.g., `rtmp://example.com/live/stream`).
13 /// - Any other URL supported by FFmpeg (e.g., `udp://...`, `http://...`).
14 ///
15 /// The URL must be valid. If the URL is invalid or unsupported, the library will
16 /// return an error when attempting to initialize the output stream.
17 pub(crate) url: Option<String>,
18
19 /// A callback function for custom data writing.
20 ///
21 /// The `write_callback` function allows you to provide custom logic for handling data
22 /// output from the encoding process. This is useful for scenarios where the output is
23 /// not written directly to a standard destination (like a file or URL), but instead to
24 /// a custom data sink, such as an in-memory buffer or a custom network destination.
25 ///
26 /// The callback receives a buffer of encoded data (`buf: &[u8]`) and should return the number of bytes
27 /// successfully written. If an error occurs, a negative value should be returned. For example:
28 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)` indicates an I/O error.
29 ///
30 /// ### Parameters:
31 /// - `buf: &[u8]`: A buffer containing the encoded data to be written.
32 ///
33 /// ### Return Value:
34 /// - **Positive Value**: The number of bytes successfully written.
35 /// - **Negative Value**: Indicates an error occurred, such as:
36 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)`: General I/O error.
37 /// - Custom-defined error codes depending on your implementation.
38 ///
39 /// ### Example:
40 /// ```rust
41 /// fn custom_write_callback(buf: &[u8]) -> i32 {
42 /// println!("Writing data: {} bytes", buf.len());
43 /// buf.len() as i32 // Return the number of bytes successfully written
44 /// }
45 /// ```
46 /// ### Note:
47 /// It is recommended to set the `format` field to the desired output format (e.g., `mp4`, `flv`, etc.)
48 /// when using a custom `write_callback`. The `format` ensures that FFmpeg processes the output
49 /// correctly for the specified format.
50 pub(crate) write_callback: Option<Box<dyn FnMut(&[u8]) -> i32>>,
51
52 /// A callback function for custom seeking within the output stream.
53 ///
54 /// The `seek_callback` function allows custom logic for adjusting the write position in
55 /// the output stream. This is essential for formats that require seeking, such as `mp4`
56 /// and `mkv`, where metadata or index information must be updated at specific positions.
57 ///
58 /// If the output format requires seeking but no `seek_callback` is provided, the operation
59 /// may fail, resulting in errors such as:
60 /// ```text
61 /// [mp4 @ 0x...] muxer does not support non seekable output
62 /// ```
63 ///
64 /// **FFmpeg may invoke `seek_callback` from different threads, so thread safety is required.**
65 /// If the destination is a `File`, **wrap it in `Arc<Mutex<File>>`** to ensure safe access.
66 ///
67 /// ### Parameters:
68 /// - `offset: i64`: The target position in the output stream where seeking should occur.
69 /// - `whence: i32`: The seek mode, which determines how `offset` should be interpreted:
70 /// - `ffmpeg_sys_next::SEEK_SET` (0) - Seek to an absolute position.
71 /// - `ffmpeg_sys_next::SEEK_CUR` (1) - Seek relative to the current position.
72 /// - `ffmpeg_sys_next::SEEK_END` (2) - Seek relative to the end of the output.
73 /// - `ffmpeg_sys_next::AVSEEK_FLAG_BYTE` (2) - Seek using **byte offset** instead of timestamps.
74 /// - `ffmpeg_sys_next::AVSEEK_SIZE` (65536) - Query the **total size** of the stream.
75 /// - `ffmpeg_sys_next::AVSEEK_FORCE` (131072) - **Force seeking, even if normally restricted.**
76 ///
77 /// ### Return Value:
78 /// - **Positive Value**: The new offset position after seeking.
79 /// - **Negative Value**: An error occurred. Common errors include:
80 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::ESPIPE)`: Seek is not supported.
81 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)`: General I/O error.
82 ///
83 /// ### Example (Thread-safe seek callback using `Arc<Mutex<File>>`):
84 /// Since `FFmpeg` may call `write_callback` and `seek_callback` from different threads,
85 /// **use `Arc<Mutex<File>>` to ensure safe concurrent access.**
86 ///
87 /// ```rust
88 /// use std::fs::File;
89 /// use std::io::{Seek, SeekFrom};
90 /// use std::sync::{Arc, Mutex};
91 ///
92 /// let file = Arc::new(Mutex::new(File::create("output.mp4").expect("Failed to create file")));
93 ///
94 /// let seek_callback = {
95 /// let file = Arc::clone(&file);
96 /// Box::new(move |offset: i64, whence: i32| -> i64 {
97 /// let mut file = file.lock().unwrap();
98 ///
99 /// // ✅ Handle AVSEEK_SIZE: Return total file size
100 /// if whence == ffmpeg_sys_next::AVSEEK_SIZE {
101 /// if let Ok(size) = file.metadata().map(|m| m.len() as i64) {
102 /// println!("FFmpeg requested stream size: {}", size);
103 /// return size;
104 /// }
105 /// return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
106 /// }
107 ///
108 /// // ✅ Ignore AVSEEK_FORCE flag
109 /// let actual_whence = whence & !ffmpeg_sys_next::AVSEEK_FORCE;
110 ///
111 /// // ✅ Handle AVSEEK_FLAG_BYTE: Perform byte-based seek
112 /// if actual_whence & ffmpeg_sys_next::AVSEEK_FLAG_BYTE != 0 {
113 /// println!("FFmpeg requested byte-based seeking. Seeking to byte offset: {}", offset);
114 /// if let Ok(new_pos) = file.seek(SeekFrom::Start(offset as u64)) {
115 /// return new_pos as i64;
116 /// }
117 /// return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
118 /// }
119 ///
120 /// // ✅ Standard seek modes
121 /// let seek_result = match actual_whence {
122 /// ffmpeg_sys_next::SEEK_SET => file.seek(SeekFrom::Start(offset as u64)),
123 /// ffmpeg_sys_next::SEEK_CUR => file.seek(SeekFrom::Current(offset)),
124 /// ffmpeg_sys_next::SEEK_END => file.seek(SeekFrom::End(offset)),
125 /// _ => {
126 /// println!("Unsupported seek mode: {}", whence);
127 /// return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::ESPIPE) as i64;
128 /// }
129 /// };
130 ///
131 /// match seek_result {
132 /// Ok(new_pos) => {
133 /// println!("Seek successful, new position: {}", new_pos);
134 /// new_pos as i64
135 /// }
136 /// Err(e) => {
137 /// println!("Seek failed: {}", e);
138 /// ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64
139 /// }
140 /// }
141 /// })
142 /// };
143 /// ```
144 pub(crate) seek_callback: Option<Box<dyn FnMut(i64, i32) -> i64>>,
145
146 /// A pipeline specifying how frames will be processed **before encoding**.
147 ///
148 /// Once input data is decoded into [`Frame`]s, these frames pass through
149 /// this pipeline on their way to the encoder. The pipeline is composed of
150 /// one or more [`FrameFilter`]s, each providing a specific transformation,
151 /// effect, or filter (e.g., resizing, color correction, OpenGL shader
152 /// effects, etc.).
153 ///
154 /// If set to [`None`], no additional processing is applied — frames
155 /// are sent to the encoder as they are.
156 pub(crate) frame_pipelines: Option<Vec<FramePipeline>>,
157
158 pub(crate) stream_maps: Vec<StreamMap>,
159
160 /// The output format for the container.
161 ///
162 /// This field specifies the desired output format, such as `mp4`, `flv`, or `mkv`. If `None`, FFmpeg
163 /// will attempt to automatically detect the format based on the output URL or filename extension.
164 ///
165 /// The format can be specified explicitly for scenarios where the format detection is insufficient or
166 /// where you want to force a particular container format regardless of the URL or extension.
167 pub(crate) format: Option<String>,
168
169 /// The codec to be used for **video** encoding.
170 ///
171 /// If this field is `None`, FFmpeg will try to select an appropriate video codec based on the
172 /// output format or other settings. By setting this field to a specific codec (e.g., `"h264"`, `"hevc"`, etc.),
173 /// you can override FFmpeg’s default codec selection. If the specified codec is not available
174 /// in your FFmpeg build, an error will be returned during initialization.
175 pub(crate) video_codec: Option<String>,
176
177 /// The codec to be used for **audio** encoding.
178 ///
179 /// If this field is `None`, FFmpeg will try to select an appropriate audio codec based on the
180 /// output format or other settings. By providing a value (e.g., `"aac"`, `"mp3"`, etc.),
181 /// you override FFmpeg’s default codec choice. If the specified codec is not available
182 /// in your FFmpeg build, an error will be returned during initialization.
183 pub(crate) audio_codec: Option<String>,
184
185 /// The codec to be used for **subtitle** encoding.
186 ///
187 /// If this field is `None`, FFmpeg will try to select an appropriate subtitle codec based on
188 /// the output format or other settings. Setting this field (e.g., `"mov_text"` for MP4 subtitles)
189 /// forces FFmpeg to use the specified codec. If the chosen codec is not supported by your build of FFmpeg,
190 /// an error will be returned during initialization.
191 pub(crate) subtitle_codec: Option<String>,
192 pub(crate) start_time_us: Option<i64>,
193 pub(crate) recording_time_us: Option<i64>,
194 pub(crate) stop_time_us: Option<i64>,
195 pub(crate) framerate: Option<AVRational>,
196 pub(crate) vsync_method: VSyncMethod,
197 pub(crate) bits_per_raw_sample: Option<i32>,
198 pub(crate) audio_sample_rate: Option<i32>,
199 pub(crate) audio_channels: Option<i32>,
200 pub(crate) audio_sample_fmt: Option<AVSampleFormat>,
201
202 // -q:v
203 // use fixed quality scale (VBR)
204 pub(crate) video_qscale: Option<i32>,
205
206 // -q:a
207 // set audio quality (codec-specific)
208 pub(crate) audio_qscale: Option<i32>,
209
210 /// Maximum number of **video** frames to encode (equivalent to `-frames:v` in FFmpeg).
211 ///
212 /// This option limits the number of **video** frames processed by the encoder.
213 ///
214 /// **Equivalent FFmpeg Command:**
215 /// ```sh
216 /// ffmpeg -i input.mp4 -frames:v 100 output.mp4
217 /// ```
218 ///
219 /// **Example Usage:**
220 /// ```rust
221 /// let output = Output::from("some_url")
222 /// .set_max_video_frames(300);
223 /// ```
224 pub(crate) max_video_frames: Option<i64>,
225
226 /// Maximum number of **audio** frames to encode (equivalent to `-frames:a` in FFmpeg).
227 ///
228 /// This option limits the number of **audio** frames processed by the encoder.
229 ///
230 /// **Equivalent FFmpeg Command:**
231 /// ```sh
232 /// ffmpeg -i input.mp4 -frames:a 500 output.mp4
233 /// ```
234 ///
235 /// **Example Usage:**
236 /// ```rust
237 /// let output = Output::from("some_url")
238 /// .set_max_audio_frames(500);
239 /// ```
240 pub(crate) max_audio_frames: Option<i64>,
241
242 /// Maximum number of **subtitle** frames to encode (equivalent to `-frames:s` in FFmpeg).
243 ///
244 /// This option limits the number of **subtitle** frames processed by the encoder.
245 ///
246 /// **Equivalent FFmpeg Command:**
247 /// ```sh
248 /// ffmpeg -i input.mp4 -frames:s 200 output.mp4
249 /// ```
250 ///
251 /// **Example Usage:**
252 /// ```rust
253 /// let output = Output::from("some_url")
254 /// .set_max_subtitle_frames(200);
255 /// ```
256 pub(crate) max_subtitle_frames: Option<i64>,
257
258 /// Video encoder-specific options.
259 ///
260 /// This field stores key-value pairs for configuring the **video encoder**.
261 /// These options are passed to the video encoder before encoding begins.
262 ///
263 /// **Common Examples:**
264 /// - `crf=0` (for lossless quality in x264/x265)
265 /// - `preset=ultrafast` (for faster encoding speed in H.264)
266 /// - `tune=zerolatency` (for real-time streaming)
267 pub(crate) video_codec_opts: Option<HashMap<String, String>>,
268
269 /// Audio encoder-specific options.
270 ///
271 /// This field stores key-value pairs for configuring the **audio encoder**.
272 /// These options are passed to the audio encoder before encoding begins.
273 ///
274 /// **Common Examples:**
275 /// - `b=192k` (for setting bitrate in AAC/MP3)
276 /// - `ar=44100` (for setting sample rate)
277 pub(crate) audio_codec_opts: Option<HashMap<String, String>>,
278
279 /// Subtitle encoder-specific options.
280 ///
281 /// This field stores key-value pairs for configuring the **subtitle encoder**.
282 /// These options are passed to the subtitle encoder before encoding begins.
283 ///
284 /// **Common Examples:**
285 /// - `mov_text` (for MP4 subtitles)
286 /// - `srt` (for subtitle format)
287 pub(crate) subtitle_codec_opts: Option<HashMap<String, String>>,
288
289 /// The output format options for the container.
290 ///
291 /// This field stores additional format-specific options that are passed to the FFmpeg muxer.
292 /// It is a collection of key-value pairs that can modify the behavior of the output format.
293 ///
294 /// Common examples include:
295 /// - `movflags=faststart` (for MP4 files)
296 /// - `flvflags=no_duration_filesize` (for FLV files)
297 ///
298 /// These options are used when initializing the FFmpeg output format.
299 ///
300 /// **Example Usage:**
301 /// ```rust
302 /// let output = Output::from("some_url")
303 /// .set_format_opt("movflags", "faststart");
304 /// ```
305 pub(crate) format_opts: Option<HashMap<String, String>>,
306
307}
308
309#[derive(Copy, Clone, PartialEq)]
310pub enum VSyncMethod {
311 VsyncAuto,
312 VsyncCfr,
313 VsyncVfr,
314 VsyncPassthrough,
315 VsyncVscfr,
316}
317
318impl Output {
319 pub fn new(url: impl Into<String>) -> Self {
320 url.into().into()
321 }
322
323 /// Creates a new `Output` instance with a custom write callback and format string.
324 ///
325 /// This method initializes an `Output` object that uses a provided `write_callback` function
326 /// to handle the encoded data being written to the output stream. You can optionally specify
327 /// the desired output format via the `format` method.
328 ///
329 /// ### Parameters:
330 /// - `write_callback: fn(buf: &[u8]) -> i32`: A function that processes the provided buffer of
331 /// encoded data and writes it to the destination. The function should return the number of bytes
332 /// successfully written (positive value) or a negative value in case of error.
333 ///
334 /// ### Return Value:
335 /// - Returns a new `Output` instance configured with the specified `write_callback` function.
336 ///
337 /// ### Behavior of `write_callback`:
338 /// - **Positive Value**: Indicates the number of bytes successfully written.
339 /// - **Negative Value**: Indicates an error occurred. For example:
340 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)`: Represents an input/output error.
341 /// - Other custom-defined error codes can also be returned to signal specific issues.
342 ///
343 /// ### Example:
344 /// ```rust
345 /// let output = Output::new_by_write_callback(move |buf| {
346 /// println!("Processing {} bytes of data for output", buf.len());
347 /// buf.len() as i32 // Return the number of bytes processed
348 /// })
349 /// .set_format("mp4");
350 /// ```
351 pub fn new_by_write_callback<F>(write_callback: F) -> Self
352 where
353 F: FnMut(&[u8]) -> i32 + 'static,
354 {
355 (Box::new(write_callback) as Box<dyn FnMut(&[u8]) -> i32>).into()
356 }
357
358 /// Sets a custom seek callback for the output stream.
359 ///
360 /// This function assigns a user-defined function that handles seeking within the output stream.
361 /// Seeking is required for certain formats (e.g., `mp4`, `mkv`) where metadata or index information
362 /// needs to be updated at specific positions in the file.
363 ///
364 /// **Why is `seek_callback` necessary?**
365 /// - Some formats (e.g., MP4) require `seek` operations to update metadata (`moov`, `mdat`).
366 /// - If no `seek_callback` is provided for formats that require seeking, FFmpeg will fail with:
367 /// ```text
368 /// [mp4 @ 0x...] muxer does not support non seekable output
369 /// ```
370 /// - For streaming formats (`flv`, `ts`, `rtmp`, `hls`), seeking is **not required**.
371 ///
372 /// **FFmpeg may invoke `seek_callback` from different threads.**
373 /// - If using a `File` as the output, **wrap it in `Arc<Mutex<File>>`** to ensure thread-safe access.
374 ///
375 /// ### Parameters:
376 /// - `seek_callback: FnMut(i64, i32) -> i64`
377 /// - `offset: i64`: The target seek position in the stream.
378 /// - `whence: i32`: The seek mode determining how `offset` should be interpreted:
379 /// - `ffmpeg_sys_next::SEEK_SET` (0): Seek to an absolute position.
380 /// - `ffmpeg_sys_next::SEEK_CUR` (1): Seek relative to the current position.
381 /// - `ffmpeg_sys_next::SEEK_END` (2): Seek relative to the end of the output.
382 /// - `ffmpeg_sys_next::AVSEEK_FLAG_BYTE` (2): Seek using **byte offset** instead of timestamps.
383 /// - `ffmpeg_sys_next::AVSEEK_SIZE` (65536): Query the **total size** of the stream.
384 /// - `ffmpeg_sys_next::AVSEEK_FORCE` (131072): **Force seeking, even if normally restricted.**
385 ///
386 /// ### Return Value:
387 /// - **Positive Value**: The new offset position after seeking.
388 /// - **Negative Value**: An error occurred. Common errors include:
389 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::ESPIPE)`: Seek is not supported.
390 /// - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)`: General I/O error.
391 ///
392 /// ### Example (Thread-safe seek callback using `Arc<Mutex<File>>`):
393 /// Since `FFmpeg` may call `write_callback` and `seek_callback` from different threads,
394 /// **use `Arc<Mutex<File>>` to ensure safe concurrent access.**
395 ///
396 /// ```rust
397 /// use std::fs::File;
398 /// use std::io::{Seek, SeekFrom, Write};
399 /// use std::sync::{Arc, Mutex};
400 ///
401 /// // ✅ Create a thread-safe file handle
402 /// let file = Arc::new(Mutex::new(File::create("output.mp4").expect("Failed to create file")));
403 ///
404 /// // ✅ Define the write callback (data writing logic)
405 /// let write_callback = {
406 /// let file = Arc::clone(&file);
407 /// move |buf: &[u8]| -> i32 {
408 /// let mut file = file.lock().unwrap();
409 /// match file.write_all(buf) {
410 /// Ok(_) => buf.len() as i32,
411 /// Err(e) => {
412 /// println!("Write error: {}", e);
413 /// ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i32
414 /// }
415 /// }
416 /// }
417 /// };
418 ///
419 /// // ✅ Define the seek callback (position adjustment logic)
420 /// let seek_callback = {
421 /// let file = Arc::clone(&file);
422 /// Box::new(move |offset: i64, whence: i32| -> i64 {
423 /// let mut file = file.lock().unwrap();
424 ///
425 /// match whence {
426 /// // ✅ Handle AVSEEK_SIZE: Return total file size
427 /// ffmpeg_sys_next::AVSEEK_SIZE => {
428 /// if let Ok(size) = file.metadata().map(|m| m.len() as i64) {
429 /// println!("FFmpeg requested stream size: {}", size);
430 /// return size;
431 /// }
432 /// return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
433 /// }
434 ///
435 /// // ✅ Handle AVSEEK_FLAG_BYTE: Seek using byte offset
436 /// ffmpeg_sys_next::AVSEEK_FLAG_BYTE => {
437 /// println!("FFmpeg requested byte-based seeking. Seeking to byte offset: {}", offset);
438 /// if let Ok(new_pos) = file.seek(SeekFrom::Start(offset as u64)) {
439 /// return new_pos as i64;
440 /// }
441 /// return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
442 /// }
443 ///
444 /// // ✅ Standard seek modes
445 /// ffmpeg_sys_next::SEEK_SET => file.seek(SeekFrom::Start(offset as u64)),
446 /// ffmpeg_sys_next::SEEK_CUR => file.seek(SeekFrom::Current(offset)),
447 /// ffmpeg_sys_next::SEEK_END => file.seek(SeekFrom::End(offset)),
448 /// _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Unsupported seek mode")),
449 /// }.map_or(ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64, |pos| pos as i64)
450 /// })
451 /// };
452 ///
453 /// // ✅ Create an output with both callbacks
454 /// let output = Output::new_by_write_callback(write_callback, "mp4")
455 /// .set_seek_callback(seek_callback);
456 /// ```
457 pub fn set_seek_callback<F>(mut self, seek_callback: F) -> Self
458 where
459 F: FnMut(i64, i32) -> i64 + 'static,
460 {
461 self.seek_callback = Some(Box::new(seek_callback) as Box<dyn FnMut(i64, i32) -> i64>);
462 self
463 }
464
465 /// Sets the output format for the container.
466 ///
467 /// This method allows you to specify the output format for the container. If no format is specified,
468 /// FFmpeg will attempt to detect it automatically based on the file extension or output URL.
469 ///
470 /// ### Parameters:
471 /// - `format: &str`: A string specifying the desired output format (e.g., `mp4`, `flv`, `mkv`).
472 ///
473 /// ### Return Value:
474 /// - Returns the `Output` instance with the newly set format.
475 pub fn set_format(mut self, format: impl Into<String>) -> Self {
476 self.format = Some(format.into());
477 self
478 }
479
480 /// Sets the **video codec** to be used for encoding.
481 ///
482 /// # Arguments
483 /// * `video_codec` - A string slice representing the desired video codec (e.g., `"h264"`, `"hevc"`).
484 ///
485 /// # Returns
486 /// * `Self` - Returns the modified `Output` struct, allowing for method chaining.
487 ///
488 /// # Examples
489 /// ```rust
490 /// let output = Output::from("rtmp://localhost/live/stream")
491 /// .set_video_codec("h264");
492 /// ```
493 pub fn set_video_codec(mut self, video_codec: impl Into<String>) -> Self {
494 self.video_codec = Some(video_codec.into());
495 self
496 }
497
498 /// Sets the **audio codec** to be used for encoding.
499 ///
500 /// # Arguments
501 /// * `audio_codec` - A string slice representing the desired audio codec (e.g., `"aac"`, `"mp3"`).
502 ///
503 /// # Returns
504 /// * `Self` - Returns the modified `Output` struct, allowing for method chaining.
505 ///
506 /// # Examples
507 /// ```rust
508 /// let output = Output::from("rtmp://localhost/live/stream")
509 /// .set_audio_codec("aac");
510 /// ```
511 pub fn set_audio_codec(mut self, audio_codec: impl Into<String>) -> Self {
512 self.audio_codec = Some(audio_codec.into());
513 self
514 }
515
516 /// Sets the **subtitle codec** to be used for encoding.
517 ///
518 /// # Arguments
519 /// * `subtitle_codec` - A string slice representing the desired subtitle codec
520 /// (e.g., `"mov_text"`, `"webvtt"`).
521 ///
522 /// # Returns
523 /// * `Self` - Returns the modified `Output` struct, allowing for method chaining.
524 ///
525 /// # Examples
526 /// ```rust
527 /// let output = Output::from("rtmp://localhost/live/stream")
528 /// .set_subtitle_codec("mov_text");
529 /// ```
530 pub fn set_subtitle_codec(mut self, subtitle_codec: impl Into<String>) -> Self {
531 self.subtitle_codec = Some(subtitle_codec.into());
532 self
533 }
534
535 /// Replaces the entire frame-processing pipeline with a new sequence
536 /// of transformations for **pre-encoding** frames on this `Output`.
537 ///
538 /// This method clears any previously set pipelines and replaces them with the provided list.
539 ///
540 /// # Parameters
541 /// * `frame_pipelines` - A list of [`FramePipeline`] instances defining the
542 /// transformations to apply before encoding.
543 ///
544 /// # Returns
545 /// * `Self` - Returns the modified `Output`, enabling method chaining.
546 ///
547 /// # Example
548 /// ```rust
549 /// let output = Output::from("some_url")
550 /// .set_frame_pipelines(vec![
551 /// FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_VIDEO).filter("opengl", Box::new(my_filter)),
552 /// // Additional pipelines...
553 /// ]);
554 /// ```
555 pub fn set_frame_pipelines(mut self, frame_pipelines: Vec<impl Into<FramePipeline>>) -> Self {
556 self.frame_pipelines = Some(frame_pipelines.into_iter().map(|frame_pipeline| frame_pipeline.into()).collect());
557 self
558 }
559
560 /// Adds a single [`FramePipeline`] to the existing pipeline list.
561 ///
562 /// If no pipelines are currently defined, this method creates a new pipeline list.
563 /// Otherwise, it appends the provided pipeline to the existing transformations.
564 ///
565 /// # Parameters
566 /// * `frame_pipeline` - A [`FramePipeline`] defining a transformation.
567 ///
568 /// # Returns
569 /// * `Self` - Returns the modified `Output`, enabling method chaining.
570 ///
571 /// # Example
572 /// ```rust
573 /// let output = Output::from("some_url")
574 /// .add_frame_pipeline(FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_VIDEO).filter("opengl", Box::new(my_filter)).build())
575 /// .add_frame_pipeline(FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_AUDIO).filter("my_custom_filter1", Box::new(...)).filter("my_custom_filter2", Box::new(...)));
576 /// ```
577 pub fn add_frame_pipeline(mut self, frame_pipeline: impl Into<FramePipeline>) -> Self {
578 if self.frame_pipelines.is_none() {
579 self.frame_pipelines = Some(vec![frame_pipeline.into()]);
580 } else {
581 self.frame_pipelines
582 .as_mut()
583 .unwrap()
584 .push(frame_pipeline.into());
585 }
586 self
587 }
588
589 /// Adds a **stream mapping** for a specific stream or stream type,
590 /// **re-encoding** it according to this output’s codec settings.
591 ///
592 /// # Linklabel (FFmpeg-like Specifier)
593 ///
594 /// This string typically follows `"<input_index>:<media_type>"` syntax:
595 /// - **`"0:v"`** – the video stream(s) from input #0.
596 /// - **`"1:a?"`** – audio from input #1, **ignore** if none present (due to `?`).
597 /// - Other possibilities include `"0:s"`, `"0:d"`, etc. for subtitles/data, optionally with `?`.
598 ///
599 /// By calling `add_stream_map`, **you force re-encoding** of the chosen stream(s).
600 /// If the user wants a bit-for-bit copy, see [`add_stream_map_with_copy`](Self::add_stream_map_with_copy).
601 ///
602 /// # Parameters
603 /// - `linklabel`: An FFmpeg-style specifier referencing the desired input index and
604 /// media type, like `"0:v"`, `"1:a?"`, etc.
605 ///
606 /// # Returns
607 /// * `Self` - for chained method calls.
608 ///
609 /// # Example
610 /// ```rust
611 /// // Re-encode the video stream from input #0 (fail if no video).
612 /// let output = Output::from("output.mp4")
613 /// .add_stream_map("0:v");
614 /// ```
615 pub fn add_stream_map(mut self, linklabel: impl Into<String>) -> Self {
616 self.stream_maps.push(linklabel.into().into());
617 self
618 }
619
620 /// Adds a **stream mapping** for a specific stream or stream type,
621 /// **copying** it bit-for-bit from the source without re-encoding.
622 ///
623 /// # Linklabel (FFmpeg-like Specifier)
624 ///
625 /// Follows the same `"<input_index>:<media_type>"` pattern as [`add_stream_map`](Self::add_stream_map):
626 /// - **`"0:a"`** – audio stream(s) from input #0.
627 /// - **`"0:a?"`** – same, but ignore errors if no audio exists.
628 /// - And so on for video (`v`), subtitles (`s`), attachments (`t`), etc.
629 ///
630 /// # Copy vs. Re-encode
631 ///
632 /// Here, `copy = true` by default, meaning the chosen stream(s) are passed through
633 /// **without** decoding/encoding. This generally **only** works if the source’s codec
634 /// is compatible with the container/format you’re outputting to.
635 /// If you require re-encoding (e.g., to ensure compatibility or apply filters),
636 /// use [`add_stream_map`](Self::add_stream_map).
637 ///
638 /// # Parameters
639 /// - `linklabel`: An FFmpeg-style specifier referencing the desired input index and
640 /// media type, like `"0:v?"`.
641 ///
642 /// # Returns
643 /// * `Self` - for chained method calls.
644 ///
645 /// # Example
646 /// ```rust
647 /// // Copy the audio stream(s) from input #0 if present, no re-encode:
648 /// let output = Output::from("output.mkv")
649 /// .add_stream_map_with_copy("0:a?");
650 /// ```
651 pub fn add_stream_map_with_copy(mut self, linklabel: impl Into<String>) -> Self {
652 self.stream_maps.push(StreamMap {
653 linklabel: linklabel.into(),
654 copy: true,
655 });
656 self
657 }
658
659 /// Sets the **start time** (in microseconds) for output encoding.
660 ///
661 /// If this is set, FFmpeg will attempt to start encoding from the specified
662 /// timestamp in the input stream. This can be used to skip initial content.
663 ///
664 /// # Parameters
665 /// * `start_time_us` - The start time in microseconds.
666 ///
667 /// # Returns
668 /// * `Self` - The modified `Output`, allowing method chaining.
669 ///
670 /// # Example
671 /// ```rust
672 /// let output = Output::from("output.mp4")
673 /// .set_start_time_us(2_000_000); // Start at 2 seconds
674 /// ```
675 pub fn set_start_time_us(mut self, start_time_us: i64) -> Self {
676 self.start_time_us = Some(start_time_us);
677 self
678 }
679
680 /// Sets the **recording time** (in microseconds) for output encoding.
681 ///
682 /// This indicates how many microseconds of data should be processed
683 /// (i.e., maximum duration to encode). Once this time is reached,
684 /// FFmpeg will stop encoding.
685 ///
686 /// # Parameters
687 /// * `recording_time_us` - The maximum duration (in microseconds) to process.
688 ///
689 /// # Returns
690 /// * `Self` - The modified `Output`, allowing method chaining.
691 ///
692 /// # Example
693 /// ```rust
694 /// let output = Output::from("output.mp4")
695 /// .set_recording_time_us(5_000_000); // Record for 5 seconds
696 /// ```
697 pub fn set_recording_time_us(mut self, recording_time_us: i64) -> Self {
698 self.recording_time_us = Some(recording_time_us);
699 self
700 }
701
702 /// Sets a **stop time** (in microseconds) for output encoding.
703 ///
704 /// If set, FFmpeg will stop encoding once the input’s timestamp
705 /// surpasses this value. Effectively, encoding ends at this timestamp
706 /// regardless of remaining data.
707 ///
708 /// # Parameters
709 /// * `stop_time_us` - The timestamp (in microseconds) at which to stop.
710 ///
711 /// # Returns
712 /// * `Self` - The modified `Output`, allowing method chaining.
713 ///
714 /// # Example
715 /// ```rust
716 /// let output = Output::from("output.mp4")
717 /// .set_stop_time_us(10_000_000); // Stop at 10 seconds
718 /// ```
719 pub fn set_stop_time_us(mut self, stop_time_us: i64) -> Self {
720 self.stop_time_us = Some(stop_time_us);
721 self
722 }
723
724 /// Sets a **target frame rate** (`AVRational`) for output encoding.
725 ///
726 /// This can force the output to use a specific frame rate (e.g., 30/1 for 30 FPS).
727 /// If unset, FFmpeg typically preserves the source frame rate or uses defaults
728 /// based on the selected codec/container.
729 ///
730 /// # Parameters
731 /// * `framerate` - An `AVRational` representing the desired frame rate
732 /// numerator/denominator (e.g., `AVRational { num: 30, den: 1 }` for 30fps).
733 ///
734 /// # Returns
735 /// * `Self` - The modified `Output`, allowing method chaining.
736 ///
737 /// # Example
738 /// ```rust
739 /// use ffmpeg_sys_next::AVRational;
740 /// let output = Output::from("output.mp4")
741 /// .set_framerate(AVRational { num: 30, den: 1 });
742 /// ```
743 pub fn set_framerate(mut self, framerate: AVRational) -> Self {
744 self.framerate = Some(framerate);
745 self
746 }
747
748 /// Sets the **video sync method** to be used during encoding.
749 ///
750 /// FFmpeg uses a variety of vsync policies to handle frame presentation times,
751 /// dropping/duplicating frames as needed. Adjusting this can be useful when
752 /// you need strict CFR (constant frame rate), or to pass frames through
753 /// without modification (`VsyncPassthrough`).
754 ///
755 /// # Parameters
756 /// * `method` - A variant of [`VSyncMethod`], such as `VsyncCfr` or `VsyncVfr`.
757 ///
758 /// # Returns
759 /// * `Self` - The modified `Output`, allowing method chaining.
760 ///
761 /// # Example
762 /// ```rust
763 /// let output = Output::from("output.mp4")
764 /// .set_vsync_method(VSyncMethod::VsyncCfr);
765 /// ```
766 pub fn set_vsync_method(mut self, method: VSyncMethod) -> Self {
767 self.vsync_method = method;
768 self
769 }
770
771 /// Sets the **bits per raw sample** for video encoding.
772 ///
773 /// This value can influence quality or color depth when dealing with
774 /// certain pixel formats. Commonly used for high-bit-depth workflows
775 /// or specialized encoding scenarios.
776 ///
777 /// # Parameters
778 /// * `bits` - The bits per raw sample (e.g., 8, 10, 12).
779 ///
780 /// # Returns
781 /// * `Self` - The modified `Output`, allowing method chaining.
782 ///
783 /// # Example
784 /// ```rust
785 /// let output = Output::from("output.mkv")
786 /// .set_bits_per_raw_sample(10); // e.g., 10-bit
787 /// ```
788 pub fn set_bits_per_raw_sample(mut self, bits: i32) -> Self {
789 self.bits_per_raw_sample = Some(bits);
790 self
791 }
792
793 /// Sets the **audio sample rate** (in Hz) for output encoding.
794 ///
795 /// This method allows you to specify the desired audio sample rate for the output.
796 /// Common values include 44100 (CD quality), 48000 (standard for digital video),
797 /// and 22050 or 16000 (for lower bitrate applications).
798 ///
799 /// # Parameters
800 /// * `audio_sample_rate` - The sample rate in Hertz (e.g., 44100, 48000).
801 ///
802 /// # Returns
803 /// * `Self` - The modified `Output`, allowing method chaining.
804 ///
805 /// # Example
806 /// ```rust
807 /// let output = Output::from("output.mp4")
808 /// .set_audio_sample_rate(48000); // Set to 48kHz
809 /// ```
810 pub fn set_audio_sample_rate(mut self, audio_sample_rate: i32) -> Self {
811 self.audio_sample_rate = Some(audio_sample_rate);
812 self
813 }
814
815 /// Sets the number of **audio channels** for output encoding.
816 ///
817 /// Common values include 1 (mono), 2 (stereo), 5.1 (6 channels), and 7.1 (8 channels).
818 /// This setting affects the spatial audio characteristics of the output.
819 ///
820 /// # Parameters
821 /// * `audio_channels` - The number of audio channels (e.g., 1 for mono, 2 for stereo).
822 ///
823 /// # Returns
824 /// * `Self` - The modified `Output`, allowing method chaining.
825 ///
826 /// # Example
827 /// ```rust
828 /// let output = Output::from("output.mp4")
829 /// .set_audio_channels(2); // Set to stereo
830 /// ```
831 pub fn set_audio_channels(mut self, audio_channels: i32) -> Self {
832 self.audio_channels = Some(audio_channels);
833 self
834 }
835
836 /// Sets the **audio sample format** for output encoding.
837 ///
838 /// This method allows you to specify the audio sample format, which affects
839 /// how audio samples are represented. Common formats include:
840 /// - `AV_SAMPLE_FMT_S16` (signed 16-bit)
841 /// - `AV_SAMPLE_FMT_S32` (signed 32-bit)
842 /// - `AV_SAMPLE_FMT_FLT` (32-bit float)
843 /// - `AV_SAMPLE_FMT_FLTP` (32-bit float, planar)
844 ///
845 /// The format choice can impact quality, processing requirements, and compatibility.
846 ///
847 /// # Parameters
848 /// * `sample_fmt` - An `AVSampleFormat` enum value specifying the desired sample format.
849 ///
850 /// # Returns
851 /// * `Self` - The modified `Output`, allowing method chaining.
852 ///
853 /// # Example
854 /// ```rust
855 /// use ffmpeg_sys_next::AVSampleFormat::AV_SAMPLE_FMT_S16;
856 ///
857 /// let output = Output::from("output.mp4")
858 /// .set_audio_sample_fmt(AV_SAMPLE_FMT_S16); // Set to signed 16-bit
859 /// ```
860 pub fn set_audio_sample_fmt(mut self, sample_fmt: AVSampleFormat) -> Self {
861 self.audio_sample_fmt = Some(sample_fmt);
862 self
863 }
864
865 /// Sets the **video quality scale** (VBR) for encoding.
866 ///
867 /// This method configures a fixed quality scale for variable bitrate (VBR) video encoding.
868 /// Lower values result in higher quality but larger file sizes, while higher values
869 /// produce lower quality with smaller file sizes.
870 ///
871 /// # Note on Modern Usage
872 /// While still supported, using fixed quality scale (`-q:v`) is generally not recommended
873 /// for modern video encoding workflows with codecs like H.264 and H.265. Instead, consider:
874 /// * For H.264/H.265: Use CRF (Constant Rate Factor) via `-crf` parameter
875 /// * For two-pass encoding: Use target bitrate settings
876 ///
877 /// This parameter is primarily useful for older codecs or specific scenarios where
878 /// direct quality scale control is needed.
879 ///
880 /// # Quality Scale Ranges by Codec
881 /// * **H.264/H.265**: 0-51 (if needed: 17-28)
882 /// - 17-18: Visually lossless
883 /// - 23: High quality
884 /// - 28: Good quality with reasonable file size
885 /// * **MPEG-4/MPEG-2**: 2-31 (recommended: 2-6)
886 /// - Lower values = higher quality
887 /// * **VP9**: 0-63 (if needed: 15-35)
888 ///
889 /// # Parameters
890 /// * `video_qscale` - The quality scale value for video encoding.
891 ///
892 /// # Returns
893 /// * `Self` - The modified `Output`, allowing method chaining.
894 ///
895 /// # Example
896 /// ```rust
897 /// // For MJPEG encoding of image sequences
898 /// let output = Output::from("output.jpg")
899 /// .set_video_qscale(2); // High quality JPEG images
900 ///
901 /// // For legacy image format conversion
902 /// let output = Output::from("output.png")
903 /// .set_video_qscale(3); // Controls compression level
904 /// ```
905 pub fn set_video_qscale(mut self, video_qscale: i32) -> Self {
906 self.video_qscale = Some(video_qscale);
907 self
908 }
909
910 /// Sets the **audio quality scale** for encoding.
911 ///
912 /// This method configures codec-specific audio quality settings. The range, behavior,
913 /// and optimal values depend entirely on the audio codec being used.
914 ///
915 /// # Quality Scale Ranges by Codec
916 /// * **MP3 (libmp3lame)**: 0-9 (recommended: 2-5)
917 /// - 0: Highest quality
918 /// - 2: Near-transparent quality (~190-200 kbps)
919 /// - 5: Good quality (~130 kbps)
920 /// - 9: Lowest quality
921 /// * **AAC**: 0.1-255 (recommended: 1-5)
922 /// - 1: Highest quality (~250 kbps)
923 /// - 3: Good quality (~160 kbps)
924 /// - 5: Medium quality (~100 kbps)
925 /// * **Vorbis**: -1 to 10 (recommended: 3-8)
926 /// - 10: Highest quality
927 /// - 5: Good quality
928 /// - 3: Medium quality
929 ///
930 /// # Parameters
931 /// * `audio_qscale` - The quality scale value for audio encoding.
932 ///
933 /// # Returns
934 /// * `Self` - The modified `Output`, allowing method chaining.
935 ///
936 /// # Example
937 /// ```rust
938 /// // For MP3 encoding at high quality
939 /// let output = Output::from("output.mp3")
940 /// .set_audio_codec("libmp3lame")
941 /// .set_audio_qscale(2);
942 ///
943 /// // For AAC encoding at good quality
944 /// let output = Output::from("output.m4a")
945 /// .set_audio_codec("aac")
946 /// .set_audio_qscale(3);
947 ///
948 /// // For Vorbis encoding at high quality
949 /// let output = Output::from("output.ogg")
950 /// .set_audio_codec("libvorbis")
951 /// .set_audio_qscale(7);
952 /// ```
953 pub fn set_audio_qscale(mut self, audio_qscale: i32) -> Self {
954 self.audio_qscale = Some(audio_qscale);
955 self
956 }
957
958 /// **Sets the maximum number of video frames to encode (`-frames:v`).**
959 ///
960 /// **Equivalent FFmpeg Command:**
961 /// ```sh
962 /// ffmpeg -i input.mp4 -frames:v 100 output.mp4
963 /// ```
964 ///
965 /// **Example Usage:**
966 /// ```rust
967 /// let output = Output::from("some_url")
968 /// .set_max_video_frames(500);
969 /// ```
970 pub fn set_max_video_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
971 self.max_video_frames = max_frames.into();
972 self
973 }
974
975 /// **Sets the maximum number of audio frames to encode (`-frames:a`).**
976 ///
977 /// **Equivalent FFmpeg Command:**
978 /// ```sh
979 /// ffmpeg -i input.mp4 -frames:a 500 output.mp4
980 /// ```
981 ///
982 /// **Example Usage:**
983 /// ```rust
984 /// let output = Output::from("some_url")
985 /// .set_max_audio_frames(500);
986 /// ```
987 pub fn set_max_audio_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
988 self.max_audio_frames = max_frames.into();
989 self
990 }
991
992 /// **Sets the maximum number of subtitle frames to encode (`-frames:s`).**
993 ///
994 /// **Equivalent FFmpeg Command:**
995 /// ```sh
996 /// ffmpeg -i input.mp4 -frames:s 200 output.mp4
997 /// ```
998 ///
999 /// **Example Usage:**
1000 /// ```rust
1001 /// let output = Output::from("some_url")
1002 /// .set_max_subtitle_frames(200);
1003 /// ```
1004 pub fn set_max_subtitle_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
1005 self.max_subtitle_frames = max_frames.into();
1006 self
1007 }
1008
1009 /// Sets a **video codec-specific option**.
1010 ///
1011 /// These options control **video encoding parameters** such as compression, quality, and speed.
1012 ///
1013 /// **Supported Parameters:**
1014 /// | Parameter | Description |
1015 /// |-----------|-------------|
1016 /// | `crf=0-51` | Quality level for x264/x265, lower means higher quality (`0` is lossless) |
1017 /// | `preset=ultrafast, superfast, fast, medium, slow, veryslow` | Encoding speed, affects compression efficiency |
1018 /// | `tune=film, animation, grain, stillimage, fastdecode, zerolatency` | Optimizations for specific types of content |
1019 /// | `b=4M` | Bitrate (e.g., `4M` for 4 Mbps) |
1020 /// | `g=50` | GOP (Group of Pictures) size, affects keyframe frequency |
1021 ///
1022 /// **Example Usage:**
1023 /// ```rust
1024 /// let output = Output::from("some_url")
1025 /// .set_video_codec_opt("crf", "18")
1026 /// .set_video_codec_opt("preset", "fast");
1027 /// ```
1028 pub fn set_video_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1029 if let Some(ref mut opts) = self.video_codec_opts {
1030 opts.insert(key.into(), value.into());
1031 } else {
1032 let mut opts = HashMap::new();
1033 opts.insert(key.into(), value.into());
1034 self.video_codec_opts = Some(opts);
1035 }
1036 self
1037 }
1038
1039 /// **Sets multiple video codec options at once.**
1040 ///
1041 /// **Example Usage:**
1042 /// ```rust
1043 /// let output = Output::from("some_url")
1044 /// .set_video_codec_opts(vec![
1045 /// ("crf", "18"),
1046 /// ("preset", "fast")
1047 /// ]);
1048 /// ```
1049 pub fn set_video_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1050 let video_opts = self.video_codec_opts.get_or_insert_with(HashMap::new);
1051 for (key, value) in opts {
1052 video_opts.insert(key.into(), value.into());
1053 }
1054 self
1055 }
1056
1057 /// Sets a **audio codec-specific option**.
1058 ///
1059 /// These options control **audio encoding parameters** such as bitrate, sample rate, and format.
1060 ///
1061 /// **Supported Parameters:**
1062 /// | Parameter | Description |
1063 /// |-----------|-------------|
1064 /// | `b=192k` | Bitrate (e.g., `128k` for 128 Kbps, `320k` for 320 Kbps) |
1065 /// | `compression_level=0-12` | Compression efficiency for formats like FLAC |
1066 ///
1067 /// **Example Usage:**
1068 /// ```rust
1069 /// let output = Output::from("some_url")
1070 /// .set_audio_codec_opt("b", "320k")
1071 /// .set_audio_codec_opt("compression_level", "6");
1072 /// ```
1073 pub fn set_audio_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1074 if let Some(ref mut opts) = self.audio_codec_opts {
1075 opts.insert(key.into(), value.into());
1076 } else {
1077 let mut opts = HashMap::new();
1078 opts.insert(key.into(), value.into());
1079 self.audio_codec_opts = Some(opts);
1080 }
1081 self
1082 }
1083
1084 /// **Sets multiple audio codec options at once.**
1085 ///
1086 /// **Example Usage:**
1087 /// ```rust
1088 /// let output = Output::from("some_url")
1089 /// .set_audio_codec_opts(vec![
1090 /// ("b", "320k"),
1091 /// ("compression_level", "6")
1092 /// ]);
1093 /// ```
1094 pub fn set_audio_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1095 let audio_opts = self.audio_codec_opts.get_or_insert_with(HashMap::new);
1096 for (key, value) in opts {
1097 audio_opts.insert(key.into(), value.into());
1098 }
1099 self
1100 }
1101
1102 /// Sets a **subtitle codec-specific option**.
1103 ///
1104 /// These options control **subtitle encoding parameters** such as format and character encoding.
1105 ///
1106 /// **Supported Parameters:**
1107 /// | Parameter | Description |
1108 /// |-----------|-------------|
1109 /// | `mov_text` | Subtitle format for MP4 files |
1110 /// | `srt` | Subtitle format for `.srt` files |
1111 /// | `ass` | Advanced SubStation Alpha (ASS) subtitle format |
1112 /// | `forced_subs=1` | Forces the subtitles to always be displayed |
1113 ///
1114 /// **Example Usage:**
1115 /// ```rust
1116 /// let output = Output::from("some_url")
1117 /// .set_subtitle_codec_opt("mov_text", "");
1118 /// ```
1119 pub fn set_subtitle_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1120 if let Some(ref mut opts) = self.subtitle_codec_opts {
1121 opts.insert(key.into(), value.into());
1122 } else {
1123 let mut opts = HashMap::new();
1124 opts.insert(key.into(), value.into());
1125 self.subtitle_codec_opts = Some(opts);
1126 }
1127 self
1128 }
1129
1130 /// **Sets multiple subtitle codec options at once.**
1131 ///
1132 /// **Example Usage:**
1133 /// ```rust
1134 /// let output = Output::from("some_url")
1135 /// .set_subtitle_codec_opts(vec![
1136 /// ("mov_text", ""),
1137 /// ("forced_subs", "1")
1138 /// ]);
1139 /// ```
1140 pub fn set_subtitle_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1141 let subtitle_opts = self.subtitle_codec_opts.get_or_insert_with(HashMap::new);
1142 for (key, value) in opts {
1143 subtitle_opts.insert(key.into(), value.into());
1144 }
1145 self
1146 }
1147
1148 /// Sets a format-specific option for the output container.
1149 ///
1150 /// FFmpeg supports various format-specific options that can be passed to the muxer.
1151 /// These options allow fine-tuning of the output container’s behavior.
1152 ///
1153 /// **Example Usage:**
1154 /// ```rust
1155 /// let output = Output::from("some_url")
1156 /// .set_format_opt("movflags", "faststart")
1157 /// .set_format_opt("flvflags", "no_duration_filesize");
1158 /// ```
1159 ///
1160 /// ### Common Format Options:
1161 /// | Format | Option | Description |
1162 /// |--------|--------|-------------|
1163 /// | `mp4` | `movflags=faststart` | Moves moov atom to the beginning of the file for faster playback start |
1164 /// | `flv` | `flvflags=no_duration_filesize` | Removes duration/size metadata for live streaming |
1165 ///
1166 /// **Parameters:**
1167 /// - `key`: The format option name (e.g., `"movflags"`, `"flvflags"`).
1168 /// - `value`: The value to set (e.g., `"faststart"`, `"no_duration_filesize"`).
1169 ///
1170 /// Returns the modified `Output` struct for chaining.
1171 pub fn set_format_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1172 if let Some(ref mut opts) = self.format_opts {
1173 opts.insert(key.into(), value.into());
1174 } else {
1175 let mut opts = HashMap::new();
1176 opts.insert(key.into(), value.into());
1177 self.format_opts = Some(opts);
1178 }
1179 self
1180 }
1181
1182 /// Sets multiple format-specific options at once.
1183 ///
1184 /// This method allows setting multiple format options in a single call.
1185 ///
1186 /// **Example Usage:**
1187 /// ```rust
1188 /// let output = Output::from("some_url")
1189 /// .set_format_opts(vec![
1190 /// ("movflags", "faststart"),
1191 /// ("flvflags", "no_duration_filesize")
1192 /// ]);
1193 /// ```
1194 ///
1195 /// **Parameters:**
1196 /// - `opts`: A vector of key-value pairs representing format options.
1197 ///
1198 /// Returns the modified `Output` struct for chaining.
1199 pub fn set_format_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1200 if let Some(ref mut format_opts) = self.format_opts {
1201 for (key, value) in opts {
1202 format_opts.insert(key.into(), value.into());
1203 }
1204 } else {
1205 let mut format_opts = HashMap::new();
1206 for (key, value) in opts {
1207 format_opts.insert(key.into(), value.into());
1208 }
1209 self.format_opts = Some(format_opts);
1210 }
1211 self
1212 }
1213}
1214
1215impl From<Box<dyn FnMut(&[u8]) -> i32>> for Output {
1216 fn from(write_callback_and_format: Box<dyn FnMut(&[u8]) -> i32>) -> Self {
1217 Self {
1218 url: None,
1219 write_callback: Some(write_callback_and_format),
1220 seek_callback: None,
1221 frame_pipelines: None,
1222 stream_maps: vec![],
1223 format: None,
1224 video_codec: None,
1225 audio_codec: None,
1226 subtitle_codec: None,
1227 start_time_us: None,
1228 recording_time_us: None,
1229 stop_time_us: None,
1230 framerate: None,
1231 vsync_method: VSyncMethod::VsyncAuto,
1232 bits_per_raw_sample: None,
1233 audio_sample_rate: None,
1234 audio_channels: None,
1235 audio_sample_fmt: None,
1236 video_qscale: None,
1237 audio_qscale: None,
1238 max_video_frames: None,
1239 max_audio_frames: None,
1240 max_subtitle_frames: None,
1241 video_codec_opts: None,
1242 audio_codec_opts: None,
1243 subtitle_codec_opts: None,
1244 format_opts: None,
1245 }
1246 }
1247}
1248
1249impl From<String> for Output {
1250 fn from(url: String) -> Self {
1251 Self {
1252 url: Some(url),
1253 write_callback: None,
1254 seek_callback: None,
1255 frame_pipelines: None,
1256 stream_maps: vec![],
1257 format: None,
1258 video_codec: None,
1259 audio_codec: None,
1260 subtitle_codec: None,
1261 start_time_us: None,
1262 recording_time_us: None,
1263 stop_time_us: None,
1264 framerate: None,
1265 vsync_method: VSyncMethod::VsyncAuto,
1266 bits_per_raw_sample: None,
1267 audio_sample_rate: None,
1268 audio_channels: None,
1269 audio_sample_fmt: None,
1270 video_qscale: None,
1271 audio_qscale: None,
1272 max_video_frames: None,
1273 max_audio_frames: None,
1274 max_subtitle_frames: None,
1275 video_codec_opts: None,
1276 audio_codec_opts: None,
1277 subtitle_codec_opts: None,
1278 format_opts: None,
1279 }
1280 }
1281}
1282
1283impl From<&str> for Output {
1284 fn from(url: &str) -> Self {
1285 Self::from(String::from(url))
1286 }
1287}
1288
1289#[derive(Debug, Clone)]
1290pub(crate) struct StreamMap {
1291 pub(crate) linklabel: String,
1292 pub(crate) copy: bool,
1293}
1294
1295impl<T: Into<String>> From<T> for StreamMap {
1296 fn from(linklabel: T) -> Self {
1297 Self {
1298 linklabel: linklabel.into(),
1299 copy: false,
1300 }
1301 }
1302}