ez_ffmpeg/core/context/
output.rs

1use std::collections::HashMap;
2use crate::core::filter::frame_pipeline_builder::FramePipelineBuilder;
3use ffmpeg_sys_next::{AVRational, AVSampleFormat};
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<FramePipelineBuilder>>,
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: Option<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    /// Maximum number of **video** frames to encode (equivalent to `-frames:v` in FFmpeg).
203    ///
204    /// This option limits the number of **video** frames processed by the encoder.
205    ///
206    /// **Equivalent FFmpeg Command:**
207    /// ```sh
208    /// ffmpeg -i input.mp4 -frames:v 100 output.mp4
209    /// ```
210    ///
211    /// **Example Usage:**
212    /// ```rust
213    /// let output = Output::from("some_url")
214    ///     .set_max_video_frames(300);
215    /// ```
216    pub(crate) max_video_frames: Option<i64>,
217
218    /// Maximum number of **audio** frames to encode (equivalent to `-frames:a` in FFmpeg).
219    ///
220    /// This option limits the number of **audio** frames processed by the encoder.
221    ///
222    /// **Equivalent FFmpeg Command:**
223    /// ```sh
224    /// ffmpeg -i input.mp4 -frames:a 500 output.mp4
225    /// ```
226    ///
227    /// **Example Usage:**
228    /// ```rust
229    /// let output = Output::from("some_url")
230    ///     .set_max_audio_frames(500);
231    /// ```
232    pub(crate) max_audio_frames: Option<i64>,
233
234    /// Maximum number of **subtitle** frames to encode (equivalent to `-frames:s` in FFmpeg).
235    ///
236    /// This option limits the number of **subtitle** frames processed by the encoder.
237    ///
238    /// **Equivalent FFmpeg Command:**
239    /// ```sh
240    /// ffmpeg -i input.mp4 -frames:s 200 output.mp4
241    /// ```
242    ///
243    /// **Example Usage:**
244    /// ```rust
245    /// let output = Output::from("some_url")
246    ///     .set_max_subtitle_frames(200);
247    /// ```
248    pub(crate) max_subtitle_frames: Option<i64>,
249
250    /// Video encoder-specific options.
251    ///
252    /// This field stores key-value pairs for configuring the **video encoder**.
253    /// These options are passed to the video encoder before encoding begins.
254    ///
255    /// **Common Examples:**
256    /// - `crf=0` (for lossless quality in x264/x265)
257    /// - `preset=ultrafast` (for faster encoding speed in H.264)
258    /// - `tune=zerolatency` (for real-time streaming)
259    pub(crate) video_codec_opts: Option<HashMap<String, String>>,
260
261    /// Audio encoder-specific options.
262    ///
263    /// This field stores key-value pairs for configuring the **audio encoder**.
264    /// These options are passed to the audio encoder before encoding begins.
265    ///
266    /// **Common Examples:**
267    /// - `b=192k` (for setting bitrate in AAC/MP3)
268    /// - `ar=44100` (for setting sample rate)
269    pub(crate) audio_codec_opts: Option<HashMap<String, String>>,
270
271    /// Subtitle encoder-specific options.
272    ///
273    /// This field stores key-value pairs for configuring the **subtitle encoder**.
274    /// These options are passed to the subtitle encoder before encoding begins.
275    ///
276    /// **Common Examples:**
277    /// - `mov_text` (for MP4 subtitles)
278    /// - `srt` (for subtitle format)
279    pub(crate) subtitle_codec_opts: Option<HashMap<String, String>>,
280
281    /// The output format options for the container.
282    ///
283    /// This field stores additional format-specific options that are passed to the FFmpeg muxer.
284    /// It is a collection of key-value pairs that can modify the behavior of the output format.
285    ///
286    /// Common examples include:
287    /// - `movflags=faststart` (for MP4 files)
288    /// - `flvflags=no_duration_filesize` (for FLV files)
289    ///
290    /// These options are used when initializing the FFmpeg output format.
291    ///
292    /// **Example Usage:**
293    /// ```rust
294    /// let output = Output::from("some_url")
295    ///     .set_format_opt("movflags", "faststart");
296    /// ```
297    pub(crate) format_opts: Option<HashMap<String, String>>,
298
299}
300
301#[derive(Copy, Clone, PartialEq)]
302pub enum VSyncMethod {
303    VsyncAuto,
304    VsyncCfr,
305    VsyncVfr,
306    VsyncPassthrough,
307    VsyncVscfr,
308}
309
310impl Output {
311    pub fn new(url: impl Into<String>) -> Self {
312        url.into().into()
313    }
314
315    /// Creates a new `Output` instance with a custom write callback and format string.
316    ///
317    /// This method initializes an `Output` object that uses a provided `write_callback` function
318    /// to handle the encoded data being written to the output stream. You can optionally specify
319    /// the desired output format via the `format` method.
320    ///
321    /// ### Parameters:
322    /// - `write_callback: fn(buf: &[u8]) -> i32`: A function that processes the provided buffer of
323    ///   encoded data and writes it to the destination. The function should return the number of bytes
324    ///   successfully written (positive value) or a negative value in case of error.
325    ///
326    /// ### Return Value:
327    /// - Returns a new `Output` instance configured with the specified `write_callback` function.
328    ///
329    /// ### Behavior of `write_callback`:
330    /// - **Positive Value**: Indicates the number of bytes successfully written.
331    /// - **Negative Value**: Indicates an error occurred. For example:
332    ///   - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)`: Represents an input/output error.
333    ///   - Other custom-defined error codes can also be returned to signal specific issues.
334    ///
335    /// ### Example:
336    /// ```rust
337    /// let output = Output::new_by_write_callback(move |buf| {
338    ///     println!("Processing {} bytes of data for output", buf.len());
339    ///     buf.len() as i32 // Return the number of bytes processed
340    /// })
341    /// .set_format("mp4");
342    /// ```
343    pub fn new_by_write_callback<F>(write_callback: F) -> Self
344    where
345        F: FnMut(&[u8]) -> i32 + 'static,
346    {
347        (Box::new(write_callback) as Box<dyn FnMut(&[u8]) -> i32>).into()
348    }
349
350    /// Sets a custom seek callback for the output stream.
351    ///
352    /// This function assigns a user-defined function that handles seeking within the output stream.
353    /// Seeking is required for certain formats (e.g., `mp4`, `mkv`) where metadata or index information
354    /// needs to be updated at specific positions in the file.
355    ///
356    /// **Why is `seek_callback` necessary?**
357    /// - Some formats (e.g., MP4) require `seek` operations to update metadata (`moov`, `mdat`).
358    /// - If no `seek_callback` is provided for formats that require seeking, FFmpeg will fail with:
359    ///   ```text
360    ///   [mp4 @ 0x...] muxer does not support non seekable output
361    ///   ```
362    /// - For streaming formats (`flv`, `ts`, `rtmp`, `hls`), seeking is **not required**.
363    ///
364    /// **FFmpeg may invoke `seek_callback` from different threads.**
365    /// - If using a `File` as the output, **wrap it in `Arc<Mutex<File>>`** to ensure thread-safe access.
366    ///
367    /// ### Parameters:
368    /// - `seek_callback: FnMut(i64, i32) -> i64`
369    ///   - `offset: i64`: The target seek position in the stream.
370    ///   - `whence: i32`: The seek mode determining how `offset` should be interpreted:
371    ///     - `ffmpeg_sys_next::SEEK_SET` (0): Seek to an absolute position.
372    ///     - `ffmpeg_sys_next::SEEK_CUR` (1): Seek relative to the current position.
373    ///     - `ffmpeg_sys_next::SEEK_END` (2): Seek relative to the end of the output.
374    ///     - `ffmpeg_sys_next::AVSEEK_FLAG_BYTE` (2): Seek using **byte offset** instead of timestamps.
375    ///     - `ffmpeg_sys_next::AVSEEK_SIZE` (65536): Query the **total size** of the stream.
376    ///     - `ffmpeg_sys_next::AVSEEK_FORCE` (131072): **Force seeking, even if normally restricted.**
377    ///
378    /// ### Return Value:
379    /// - **Positive Value**: The new offset position after seeking.
380    /// - **Negative Value**: An error occurred. Common errors include:
381    ///   - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::ESPIPE)`: Seek is not supported.
382    ///   - `ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO)`: General I/O error.
383    ///
384    /// ### Example (Thread-safe seek callback using `Arc<Mutex<File>>`):
385    /// Since `FFmpeg` may call `write_callback` and `seek_callback` from different threads,
386    /// **use `Arc<Mutex<File>>` to ensure safe concurrent access.**
387    ///
388    /// ```rust
389    /// use std::fs::File;
390    /// use std::io::{Seek, SeekFrom, Write};
391    /// use std::sync::{Arc, Mutex};
392    ///
393    /// // ✅ Create a thread-safe file handle
394    /// let file = Arc::new(Mutex::new(File::create("output.mp4").expect("Failed to create file")));
395    ///
396    /// // ✅ Define the write callback (data writing logic)
397    /// let write_callback = {
398    ///     let file = Arc::clone(&file);
399    ///     move |buf: &[u8]| -> i32 {
400    ///         let mut file = file.lock().unwrap();
401    ///         match file.write_all(buf) {
402    ///             Ok(_) => buf.len() as i32,
403    ///             Err(e) => {
404    ///                 println!("Write error: {}", e);
405    ///                 ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i32
406    ///             }
407    ///         }
408    ///     }
409    /// };
410    ///
411    /// // ✅ Define the seek callback (position adjustment logic)
412    /// let seek_callback = {
413    ///     let file = Arc::clone(&file);
414    ///     Box::new(move |offset: i64, whence: i32| -> i64 {
415    ///         let mut file = file.lock().unwrap();
416    ///
417    ///         match whence {
418    ///             // ✅ Handle AVSEEK_SIZE: Return total file size
419    ///             ffmpeg_sys_next::AVSEEK_SIZE => {
420    ///                 if let Ok(size) = file.metadata().map(|m| m.len() as i64) {
421    ///                     println!("FFmpeg requested stream size: {}", size);
422    ///                     return size;
423    ///                 }
424    ///                 return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
425    ///             }
426    ///
427    ///             // ✅ Handle AVSEEK_FLAG_BYTE: Seek using byte offset
428    ///             ffmpeg_sys_next::AVSEEK_FLAG_BYTE => {
429    ///                 println!("FFmpeg requested byte-based seeking. Seeking to byte offset: {}", offset);
430    ///                 if let Ok(new_pos) = file.seek(SeekFrom::Start(offset as u64)) {
431    ///                     return new_pos as i64;
432    ///                 }
433    ///                 return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
434    ///             }
435    ///
436    ///             // ✅ Standard seek modes
437    ///             ffmpeg_sys_next::SEEK_SET => file.seek(SeekFrom::Start(offset as u64)),
438    ///             ffmpeg_sys_next::SEEK_CUR => file.seek(SeekFrom::Current(offset)),
439    ///             ffmpeg_sys_next::SEEK_END => file.seek(SeekFrom::End(offset)),
440    ///             _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Unsupported seek mode")),
441    ///         }.map_or(ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64, |pos| pos as i64)
442    ///     })
443    /// };
444    ///
445    /// // ✅ Create an output with both callbacks
446    /// let output = Output::new_by_write_callback(write_callback, "mp4")
447    ///     .set_seek_callback(seek_callback);
448    /// ```
449    pub fn set_seek_callback<F>(mut self, seek_callback: F) -> Self
450    where
451        F: FnMut(i64, i32) -> i64 + 'static,
452    {
453        self.seek_callback = Some(Box::new(seek_callback) as Box<dyn FnMut(i64, i32) -> i64>);
454        self
455    }
456
457    /// Sets the output format for the container.
458    ///
459    /// This method allows you to specify the output format for the container. If no format is specified,
460    /// FFmpeg will attempt to detect it automatically based on the file extension or output URL.
461    ///
462    /// ### Parameters:
463    /// - `format: &str`: A string specifying the desired output format (e.g., `mp4`, `flv`, `mkv`).
464    ///
465    /// ### Return Value:
466    /// - Returns the `Output` instance with the newly set format.
467    pub fn set_format(mut self, format: impl Into<String>) -> Self {
468        self.format = Some(format.into());
469        self
470    }
471
472    /// Sets the **video codec** to be used for encoding.
473    ///
474    /// # Arguments
475    /// * `video_codec` - A string slice representing the desired video codec (e.g., `"h264"`, `"hevc"`).
476    ///
477    /// # Returns
478    /// * `Self` - Returns the modified `Output` struct, allowing for method chaining.
479    ///
480    /// # Examples
481    /// ```rust
482    /// let output = Output::from("rtmp://localhost/live/stream")
483    ///     .set_video_codec("h264");
484    /// ```
485    pub fn set_video_codec(mut self, video_codec: impl Into<String>) -> Self {
486        self.video_codec = Some(video_codec.into());
487        self
488    }
489
490    /// Sets the **audio codec** to be used for encoding.
491    ///
492    /// # Arguments
493    /// * `audio_codec` - A string slice representing the desired audio codec (e.g., `"aac"`, `"mp3"`).
494    ///
495    /// # Returns
496    /// * `Self` - Returns the modified `Output` struct, allowing for method chaining.
497    ///
498    /// # Examples
499    /// ```rust
500    /// let output = Output::from("rtmp://localhost/live/stream")
501    ///     .set_audio_codec("aac");
502    /// ```
503    pub fn set_audio_codec(mut self, audio_codec: impl Into<String>) -> Self {
504        self.audio_codec = Some(audio_codec.into());
505        self
506    }
507
508    /// Sets the **subtitle codec** to be used for encoding.
509    ///
510    /// # Arguments
511    /// * `subtitle_codec` - A string slice representing the desired subtitle codec
512    ///   (e.g., `"mov_text"`, `"webvtt"`).
513    ///
514    /// # Returns
515    /// * `Self` - Returns the modified `Output` struct, allowing for method chaining.
516    ///
517    /// # Examples
518    /// ```rust
519    /// let output = Output::from("rtmp://localhost/live/stream")
520    ///     .set_subtitle_codec("mov_text");
521    /// ```
522    pub fn set_subtitle_codec(mut self, subtitle_codec: impl Into<String>) -> Self {
523        self.subtitle_codec = Some(subtitle_codec.into());
524        self
525    }
526
527    /// Replaces the entire frame-processing pipeline with a new sequence
528    /// of transformations for **pre-encoding** frames on this `Output`.
529    ///
530    /// This method clears any previously set pipelines and replaces them with the provided list.
531    ///
532    /// **Note:** This method accepts [`FramePipelineBuilder`] instead of [`FramePipeline`](crate::core::filter::frame_pipeline::FramePipeline).
533    /// For details on why [`FramePipelineBuilder`] is used, see its documentation.
534    ///
535    /// # Parameters
536    /// * `frame_pipelines` - A list of [`FramePipelineBuilder`] instances defining the
537    ///   transformations to apply before encoding.
538    ///
539    /// # Returns
540    /// * `Self` - Returns the modified `Output`, enabling method chaining.
541    ///
542    /// # Example
543    /// ```rust
544    /// let output = Output::from("some_url")
545    ///     .set_frame_pipelines(vec![
546    ///         FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_VIDEO).filter("opengl", Box::new(my_filter)),
547    ///         // Additional pipelines...
548    ///     ]);
549    /// ```
550    pub fn set_frame_pipelines(mut self, frame_pipelines: Vec<FramePipelineBuilder>) -> Self {
551        self.frame_pipelines = Some(frame_pipelines);
552        self
553    }
554
555    /// Adds a single [`FramePipelineBuilder`] to the existing pipeline list.
556    ///
557    /// If no pipelines are currently defined, this method creates a new pipeline list.
558    /// Otherwise, it appends the provided pipeline to the existing transformations.
559    ///
560    /// **Note:** This method accepts [`FramePipelineBuilder`] instead of [`FramePipeline`](crate::core::filter::frame_pipeline::FramePipeline).
561    /// For details on why [`FramePipelineBuilder`] is used, see its documentation.
562    ///
563    /// # Parameters
564    /// * `frame_pipeline` - A [`FramePipelineBuilder`] defining a transformation.
565    ///
566    /// # Returns
567    /// * `Self` - Returns the modified `Output`, enabling method chaining.
568    ///
569    /// # Example
570    /// ```rust
571    /// let output = Output::from("some_url")
572    ///     .add_frame_pipeline(FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_VIDEO).filter("opengl", Box::new(my_filter)))
573    ///     .add_frame_pipeline(FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_AUDIO).filter("my_custom_filter1", Box::new(...)).filter("my_custom_filter2", Box::new(...)));
574    /// ```
575    pub fn add_frame_pipeline(mut self, frame_pipeline: FramePipelineBuilder) -> Self {
576        if self.frame_pipelines.is_none() {
577            self.frame_pipelines = Some(vec![frame_pipeline]);
578        } else {
579            self.frame_pipelines
580                .as_mut()
581                .unwrap()
582                .push(frame_pipeline);
583        }
584        self
585    }
586
587    /// Adds a **stream mapping** for a specific stream or stream type,
588    /// **re-encoding** it according to this output’s codec settings.
589    ///
590    /// # Linklabel (FFmpeg-like Specifier)
591    ///
592    /// This string typically follows `"<input_index>:<media_type>"` syntax:
593    /// - **`"0:v"`** – the video stream(s) from input #0.
594    /// - **`"1:a?"`** – audio from input #1, **ignore** if none present (due to `?`).
595    /// - Other possibilities include `"0:s"`, `"0:d"`, etc. for subtitles/data, optionally with `?`.
596    ///
597    /// By calling `add_stream_map`, **you force re-encoding** of the chosen stream(s).
598    /// If the user wants a bit-for-bit copy, see [`add_stream_map_with_copy`](Self::add_stream_map_with_copy).
599    ///
600    /// # Parameters
601    /// - `linklabel`: An FFmpeg-style specifier referencing the desired input index and
602    ///   media type, like `"0:v"`, `"1:a?"`, etc.
603    ///
604    /// # Returns
605    /// * `Self` - for chained method calls.
606    ///
607    /// # Example
608    /// ```rust
609    /// // Re-encode the video stream from input #0 (fail if no video).
610    /// let output = Output::from("output.mp4")
611    ///     .add_stream_map("0:v");
612    /// ```
613    pub fn add_stream_map(mut self, linklabel: impl Into<String>) -> Self {
614        self.stream_maps.push(linklabel.into().into());
615        self
616    }
617
618    /// Adds a **stream mapping** for a specific stream or stream type,
619    /// **copying** it bit-for-bit from the source without re-encoding.
620    ///
621    /// # Linklabel (FFmpeg-like Specifier)
622    ///
623    /// Follows the same `"<input_index>:<media_type>"` pattern as [`add_stream_map`](Self::add_stream_map):
624    /// - **`"0:a"`** – audio stream(s) from input #0.
625    /// - **`"0:a?"`** – same, but ignore errors if no audio exists.
626    /// - And so on for video (`v`), subtitles (`s`), attachments (`t`), etc.
627    ///
628    /// # Copy vs. Re-encode
629    ///
630    /// Here, `copy = true` by default, meaning the chosen stream(s) are passed through
631    /// **without** decoding/encoding. This generally **only** works if the source’s codec
632    /// is compatible with the container/format you’re outputting to.
633    /// If you require re-encoding (e.g., to ensure compatibility or apply filters),
634    /// use [`add_stream_map`](Self::add_stream_map).
635    ///
636    /// # Parameters
637    /// - `linklabel`: An FFmpeg-style specifier referencing the desired input index and
638    ///   media type, like `"0:v?"`.
639    ///
640    /// # Returns
641    /// * `Self` - for chained method calls.
642    ///
643    /// # Example
644    /// ```rust
645    /// // Copy the audio stream(s) from input #0 if present, no re-encode:
646    /// let output = Output::from("output.mkv")
647    ///     .add_stream_map_with_copy("0:a?");
648    /// ```
649    pub fn add_stream_map_with_copy(mut self, linklabel: impl Into<String>) -> Self {
650        self.stream_maps.push(StreamMap {
651            linklabel: linklabel.into(),
652            copy: true,
653        });
654        self
655    }
656
657    /// Sets the **start time** (in microseconds) for output encoding.
658    ///
659    /// If this is set, FFmpeg will attempt to start encoding from the specified
660    /// timestamp in the input stream. This can be used to skip initial content.
661    ///
662    /// # Parameters
663    /// * `start_time_us` - The start time in microseconds.
664    ///
665    /// # Returns
666    /// * `Self` - The modified `Output`, allowing method chaining.
667    ///
668    /// # Example
669    /// ```rust
670    /// let output = Output::from("output.mp4")
671    ///     .set_start_time_us(2_000_000); // Start at 2 seconds
672    /// ```
673    pub fn set_start_time_us(mut self, start_time_us: i64) -> Self {
674        self.start_time_us = Some(start_time_us);
675        self
676    }
677
678    /// Sets the **recording time** (in microseconds) for output encoding.
679    ///
680    /// This indicates how many microseconds of data should be processed
681    /// (i.e., maximum duration to encode). Once this time is reached,
682    /// FFmpeg will stop encoding.
683    ///
684    /// # Parameters
685    /// * `recording_time_us` - The maximum duration (in microseconds) to process.
686    ///
687    /// # Returns
688    /// * `Self` - The modified `Output`, allowing method chaining.
689    ///
690    /// # Example
691    /// ```rust
692    /// let output = Output::from("output.mp4")
693    ///     .set_recording_time_us(5_000_000); // Record for 5 seconds
694    /// ```
695    pub fn set_recording_time_us(mut self, recording_time_us: i64) -> Self {
696        self.recording_time_us = Some(recording_time_us);
697        self
698    }
699
700    /// Sets a **stop time** (in microseconds) for output encoding.
701    ///
702    /// If set, FFmpeg will stop encoding once the input’s timestamp
703    /// surpasses this value. Effectively, encoding ends at this timestamp
704    /// regardless of remaining data.
705    ///
706    /// # Parameters
707    /// * `stop_time_us` - The timestamp (in microseconds) at which to stop.
708    ///
709    /// # Returns
710    /// * `Self` - The modified `Output`, allowing method chaining.
711    ///
712    /// # Example
713    /// ```rust
714    /// let output = Output::from("output.mp4")
715    ///     .set_stop_time_us(10_000_000); // Stop at 10 seconds
716    /// ```
717    pub fn set_stop_time_us(mut self, stop_time_us: i64) -> Self {
718        self.stop_time_us = Some(stop_time_us);
719        self
720    }
721
722    /// Sets a **target frame rate** (`AVRational`) for output encoding.
723    ///
724    /// This can force the output to use a specific frame rate (e.g., 30/1 for 30 FPS).
725    /// If unset, FFmpeg typically preserves the source frame rate or uses defaults
726    /// based on the selected codec/container.
727    ///
728    /// # Parameters
729    /// * `framerate` - An `AVRational` representing the desired frame rate
730    ///   numerator/denominator (e.g., `AVRational { num: 30, den: 1 }` for 30fps).
731    ///
732    /// # Returns
733    /// * `Self` - The modified `Output`, allowing method chaining.
734    ///
735    /// # Example
736    /// ```rust
737    /// use ffmpeg_sys_next::AVRational;
738    /// let output = Output::from("output.mp4")
739    ///     .set_framerate(AVRational { num: 30, den: 1 });
740    /// ```
741    pub fn set_framerate(mut self, framerate: AVRational) -> Self {
742        self.framerate = Some(framerate);
743        self
744    }
745
746    /// Sets the **video sync method** to be used during encoding.
747    ///
748    /// FFmpeg uses a variety of vsync policies to handle frame presentation times,
749    /// dropping/duplicating frames as needed. Adjusting this can be useful when
750    /// you need strict CFR (constant frame rate), or to pass frames through
751    /// without modification (`VsyncPassthrough`).
752    ///
753    /// # Parameters
754    /// * `method` - A variant of [`VSyncMethod`], such as `VsyncCfr` or `VsyncVfr`.
755    ///
756    /// # Returns
757    /// * `Self` - The modified `Output`, allowing method chaining.
758    ///
759    /// # Example
760    /// ```rust
761    /// let output = Output::from("output.mp4")
762    ///     .set_vsync_method(VSyncMethod::VsyncCfr);
763    /// ```
764    pub fn set_vsync_method(mut self, method: VSyncMethod) -> Self {
765        self.vsync_method = Some(method);
766        self
767    }
768
769    /// Sets the **bits per raw sample** for video encoding.
770    ///
771    /// This value can influence quality or color depth when dealing with
772    /// certain pixel formats. Commonly used for high-bit-depth workflows
773    /// or specialized encoding scenarios.
774    ///
775    /// # Parameters
776    /// * `bits` - The bits per raw sample (e.g., 8, 10, 12).
777    ///
778    /// # Returns
779    /// * `Self` - The modified `Output`, allowing method chaining.
780    ///
781    /// # Example
782    /// ```rust
783    /// let output = Output::from("output.mkv")
784    ///     .set_bits_per_raw_sample(10); // e.g., 10-bit
785    /// ```
786    pub fn set_bits_per_raw_sample(mut self, bits: i32) -> Self {
787        self.bits_per_raw_sample = Some(bits);
788        self
789    }
790
791    /// Sets the **audio sample rate** (in Hz) for output encoding.
792    ///
793    /// This method allows you to specify the desired audio sample rate for the output.
794    /// Common values include 44100 (CD quality), 48000 (standard for digital video),
795    /// and 22050 or 16000 (for lower bitrate applications).
796    ///
797    /// # Parameters
798    /// * `audio_sample_rate` - The sample rate in Hertz (e.g., 44100, 48000).
799    ///
800    /// # Returns
801    /// * `Self` - The modified `Output`, allowing method chaining.
802    ///
803    /// # Example
804    /// ```rust
805    /// let output = Output::from("output.mp4")
806    ///     .set_audio_sample_rate(48000); // Set to 48kHz
807    /// ```
808    pub fn set_audio_sample_rate(mut self, audio_sample_rate: i32) -> Self {
809        self.audio_sample_rate = Some(audio_sample_rate);
810        self
811    }
812
813    /// Sets the number of **audio channels** for output encoding.
814    ///
815    /// Common values include 1 (mono), 2 (stereo), 5.1 (6 channels), and 7.1 (8 channels).
816    /// This setting affects the spatial audio characteristics of the output.
817    ///
818    /// # Parameters
819    /// * `audio_channels` - The number of audio channels (e.g., 1 for mono, 2 for stereo).
820    ///
821    /// # Returns
822    /// * `Self` - The modified `Output`, allowing method chaining.
823    ///
824    /// # Example
825    /// ```rust
826    /// let output = Output::from("output.mp4")
827    ///     .set_audio_channels(2); // Set to stereo
828    /// ```
829    pub fn set_audio_channels(mut self, audio_channels: i32) -> Self {
830        self.audio_channels = Some(audio_channels);
831        self
832    }
833
834    /// Sets the **audio sample format** for output encoding.
835    ///
836    /// This method allows you to specify the audio sample format, which affects
837    /// how audio samples are represented. Common formats include:
838    /// - `AV_SAMPLE_FMT_S16` (signed 16-bit)
839    /// - `AV_SAMPLE_FMT_S32` (signed 32-bit)
840    /// - `AV_SAMPLE_FMT_FLT` (32-bit float)
841    /// - `AV_SAMPLE_FMT_FLTP` (32-bit float, planar)
842    ///
843    /// The format choice can impact quality, processing requirements, and compatibility.
844    ///
845    /// # Parameters
846    /// * `sample_fmt` - An `AVSampleFormat` enum value specifying the desired sample format.
847    ///
848    /// # Returns
849    /// * `Self` - The modified `Output`, allowing method chaining.
850    ///
851    /// # Example
852    /// ```rust
853    /// use ffmpeg_sys_next::AVSampleFormat::AV_SAMPLE_FMT_S16;
854    ///
855    /// let output = Output::from("output.mp4")
856    ///     .set_audio_sample_fmt(AV_SAMPLE_FMT_S16); // Set to signed 16-bit
857    /// ```
858    pub fn set_audio_sample_fmt(mut self, sample_fmt: AVSampleFormat) -> Self {
859        self.audio_sample_fmt = Some(sample_fmt);
860        self
861    }
862
863    /// **Sets the maximum number of video frames to encode (`-frames:v`).**
864    ///
865    /// **Equivalent FFmpeg Command:**
866    /// ```sh
867    /// ffmpeg -i input.mp4 -frames:v 100 output.mp4
868    /// ```
869    ///
870    /// **Example Usage:**
871    /// ```rust
872    /// let output = Output::from("some_url")
873    ///     .set_max_video_frames(500);
874    /// ```
875    pub fn set_max_video_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
876        self.max_video_frames = max_frames.into();
877        self
878    }
879
880    /// **Sets the maximum number of audio frames to encode (`-frames:a`).**
881    ///
882    /// **Equivalent FFmpeg Command:**
883    /// ```sh
884    /// ffmpeg -i input.mp4 -frames:a 500 output.mp4
885    /// ```
886    ///
887    /// **Example Usage:**
888    /// ```rust
889    /// let output = Output::from("some_url")
890    ///     .set_max_audio_frames(500);
891    /// ```
892    pub fn set_max_audio_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
893        self.max_audio_frames = max_frames.into();
894        self
895    }
896
897    /// **Sets the maximum number of subtitle frames to encode (`-frames:s`).**
898    ///
899    /// **Equivalent FFmpeg Command:**
900    /// ```sh
901    /// ffmpeg -i input.mp4 -frames:s 200 output.mp4
902    /// ```
903    ///
904    /// **Example Usage:**
905    /// ```rust
906    /// let output = Output::from("some_url")
907    ///     .set_max_subtitle_frames(200);
908    /// ```
909    pub fn set_max_subtitle_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
910        self.max_subtitle_frames = max_frames.into();
911        self
912    }
913
914    /// Sets a **video codec-specific option**.
915    ///
916    /// These options control **video encoding parameters** such as compression, quality, and speed.
917    ///
918    /// **Supported Parameters:**
919    /// | Parameter | Description |
920    /// |-----------|-------------|
921    /// | `crf=0-51` | Quality level for x264/x265, lower means higher quality (`0` is lossless) |
922    /// | `preset=ultrafast, superfast, fast, medium, slow, veryslow` | Encoding speed, affects compression efficiency |
923    /// | `tune=film, animation, grain, stillimage, fastdecode, zerolatency` | Optimizations for specific types of content |
924    /// | `b=4M` | Bitrate (e.g., `4M` for 4 Mbps) |
925    /// | `g=50` | GOP (Group of Pictures) size, affects keyframe frequency |
926    ///
927    /// **Example Usage:**
928    /// ```rust
929    /// let output = Output::from("some_url")
930    ///     .set_video_codec_opt("crf", "18")
931    ///     .set_video_codec_opt("preset", "fast");
932    /// ```
933    pub fn set_video_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
934        if let Some(ref mut opts) = self.video_codec_opts {
935            opts.insert(key.into(), value.into());
936        } else {
937            let mut opts = HashMap::new();
938            opts.insert(key.into(), value.into());
939            self.video_codec_opts = Some(opts);
940        }
941        self
942    }
943
944    /// **Sets multiple video codec options at once.**
945    ///
946    /// **Example Usage:**
947    /// ```rust
948    /// let output = Output::from("some_url")
949    ///     .set_video_codec_opts(vec![
950    ///         ("crf", "18"),
951    ///         ("preset", "fast")
952    ///     ]);
953    /// ```
954    pub fn set_video_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
955        let video_opts = self.video_codec_opts.get_or_insert_with(HashMap::new);
956        for (key, value) in opts {
957            video_opts.insert(key.into(), value.into());
958        }
959        self
960    }
961
962    /// Sets a **audio codec-specific option**.
963    ///
964    /// These options control **audio encoding parameters** such as bitrate, sample rate, and format.
965    ///
966    /// **Supported Parameters:**
967    /// | Parameter | Description |
968    /// |-----------|-------------|
969    /// | `b=192k` | Bitrate (e.g., `128k` for 128 Kbps, `320k` for 320 Kbps) |
970    /// | `compression_level=0-12` | Compression efficiency for formats like FLAC |
971    ///
972    /// **Example Usage:**
973    /// ```rust
974    /// let output = Output::from("some_url")
975    ///     .set_audio_codec_opt("b", "320k")
976    ///     .set_audio_codec_opt("compression_level", "6");
977    /// ```
978    pub fn set_audio_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
979        if let Some(ref mut opts) = self.audio_codec_opts {
980            opts.insert(key.into(), value.into());
981        } else {
982            let mut opts = HashMap::new();
983            opts.insert(key.into(), value.into());
984            self.audio_codec_opts = Some(opts);
985        }
986        self
987    }
988
989    /// **Sets multiple audio codec options at once.**
990    ///
991    /// **Example Usage:**
992    /// ```rust
993    /// let output = Output::from("some_url")
994    ///     .set_audio_codec_opts(vec![
995    ///         ("b", "320k"),
996    ///         ("compression_level", "6")
997    ///     ]);
998    /// ```
999    pub fn set_audio_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1000        let audio_opts = self.audio_codec_opts.get_or_insert_with(HashMap::new);
1001        for (key, value) in opts {
1002            audio_opts.insert(key.into(), value.into());
1003        }
1004        self
1005    }
1006
1007    /// Sets a **subtitle codec-specific option**.
1008    ///
1009    /// These options control **subtitle encoding parameters** such as format and character encoding.
1010    ///
1011    /// **Supported Parameters:**
1012    /// | Parameter | Description |
1013    /// |-----------|-------------|
1014    /// | `mov_text` | Subtitle format for MP4 files |
1015    /// | `srt` | Subtitle format for `.srt` files |
1016    /// | `ass` | Advanced SubStation Alpha (ASS) subtitle format |
1017    /// | `forced_subs=1` | Forces the subtitles to always be displayed |
1018    ///
1019    /// **Example Usage:**
1020    /// ```rust
1021    /// let output = Output::from("some_url")
1022    ///     .set_subtitle_codec_opt("mov_text", "");
1023    /// ```
1024    pub fn set_subtitle_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1025        if let Some(ref mut opts) = self.subtitle_codec_opts {
1026            opts.insert(key.into(), value.into());
1027        } else {
1028            let mut opts = HashMap::new();
1029            opts.insert(key.into(), value.into());
1030            self.subtitle_codec_opts = Some(opts);
1031        }
1032        self
1033    }
1034
1035    /// **Sets multiple subtitle codec options at once.**
1036    ///
1037    /// **Example Usage:**
1038    /// ```rust
1039    /// let output = Output::from("some_url")
1040    ///     .set_subtitle_codec_opts(vec![
1041    ///         ("mov_text", ""),
1042    ///         ("forced_subs", "1")
1043    ///     ]);
1044    /// ```
1045    pub fn set_subtitle_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1046        let subtitle_opts = self.subtitle_codec_opts.get_or_insert_with(HashMap::new);
1047        for (key, value) in opts {
1048            subtitle_opts.insert(key.into(), value.into());
1049        }
1050        self
1051    }
1052
1053    /// Sets a format-specific option for the output container.
1054    ///
1055    /// FFmpeg supports various format-specific options that can be passed to the muxer.
1056    /// These options allow fine-tuning of the output container’s behavior.
1057    ///
1058    /// **Example Usage:**
1059    /// ```rust
1060    /// let output = Output::from("some_url")
1061    ///     .set_format_opt("movflags", "faststart")
1062    ///     .set_format_opt("flvflags", "no_duration_filesize");
1063    /// ```
1064    ///
1065    /// ### Common Format Options:
1066    /// | Format | Option | Description |
1067    /// |--------|--------|-------------|
1068    /// | `mp4`  | `movflags=faststart` | Moves moov atom to the beginning of the file for faster playback start |
1069    /// | `flv`  | `flvflags=no_duration_filesize` | Removes duration/size metadata for live streaming |
1070    ///
1071    /// **Parameters:**
1072    /// - `key`: The format option name (e.g., `"movflags"`, `"flvflags"`).
1073    /// - `value`: The value to set (e.g., `"faststart"`, `"no_duration_filesize"`).
1074    ///
1075    /// Returns the modified `Output` struct for chaining.
1076    pub fn set_format_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1077        if let Some(ref mut opts) = self.format_opts {
1078            opts.insert(key.into(), value.into());
1079        } else {
1080            let mut opts = HashMap::new();
1081            opts.insert(key.into(), value.into());
1082            self.format_opts = Some(opts);
1083        }
1084        self
1085    }
1086
1087    /// Sets multiple format-specific options at once.
1088    ///
1089    /// This method allows setting multiple format options in a single call.
1090    ///
1091    /// **Example Usage:**
1092    /// ```rust
1093    /// let output = Output::from("some_url")
1094    ///     .set_format_opts(vec![
1095    ///         ("movflags", "faststart"),
1096    ///         ("flvflags", "no_duration_filesize")
1097    ///     ]);
1098    /// ```
1099    ///
1100    /// **Parameters:**
1101    /// - `opts`: A vector of key-value pairs representing format options.
1102    ///
1103    /// Returns the modified `Output` struct for chaining.
1104    pub fn set_format_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1105        if let Some(ref mut format_opts) = self.format_opts {
1106            for (key, value) in opts {
1107                format_opts.insert(key.into(), value.into());
1108            }
1109        } else {
1110            let mut format_opts = HashMap::new();
1111            for (key, value) in opts {
1112                format_opts.insert(key.into(), value.into());
1113            }
1114            self.format_opts = Some(format_opts);
1115        }
1116        self
1117    }
1118}
1119
1120impl From<Box<dyn FnMut(&[u8]) -> i32>> for Output {
1121    fn from(write_callback_and_format: Box<dyn FnMut(&[u8]) -> i32>) -> Self {
1122        Self {
1123            url: None,
1124            write_callback: Some(write_callback_and_format),
1125            seek_callback: None,
1126            frame_pipelines: None,
1127            stream_maps: vec![],
1128            format: None,
1129            video_codec: None,
1130            audio_codec: None,
1131            subtitle_codec: None,
1132            start_time_us: None,
1133            recording_time_us: None,
1134            stop_time_us: None,
1135            framerate: None,
1136            vsync_method: None,
1137            bits_per_raw_sample: None,
1138            audio_sample_rate: None,
1139            audio_channels: None,
1140            audio_sample_fmt: None,
1141            max_video_frames: None,
1142            max_audio_frames: None,
1143            max_subtitle_frames: None,
1144            video_codec_opts: None,
1145            audio_codec_opts: None,
1146            subtitle_codec_opts: None,
1147            format_opts: None,
1148        }
1149    }
1150}
1151
1152impl From<String> for Output {
1153    fn from(url: String) -> Self {
1154        Self {
1155            url: Some(url),
1156            write_callback: None,
1157            seek_callback: None,
1158            frame_pipelines: None,
1159            stream_maps: vec![],
1160            format: None,
1161            video_codec: None,
1162            audio_codec: None,
1163            subtitle_codec: None,
1164            start_time_us: None,
1165            recording_time_us: None,
1166            stop_time_us: None,
1167            framerate: None,
1168            vsync_method: None,
1169            bits_per_raw_sample: None,
1170            audio_sample_rate: None,
1171            audio_channels: None,
1172            audio_sample_fmt: None,
1173            max_video_frames: None,
1174            max_audio_frames: None,
1175            max_subtitle_frames: None,
1176            video_codec_opts: None,
1177            audio_codec_opts: None,
1178            subtitle_codec_opts: None,
1179            format_opts: None,
1180        }
1181    }
1182}
1183
1184impl From<&str> for Output {
1185    fn from(url: &str) -> Self {
1186        Self::from(String::from(url))
1187    }
1188}
1189
1190#[derive(Debug, Clone)]
1191pub(crate) struct StreamMap {
1192    pub(crate) linklabel: String,
1193    pub(crate) copy: bool,
1194}
1195
1196impl<T: Into<String>> From<T> for StreamMap {
1197    fn from(linklabel: T) -> Self {
1198        Self {
1199            linklabel: linklabel.into(),
1200            copy: false,
1201        }
1202    }
1203}