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 // -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 /// **Note:** This method accepts [`FramePipelineBuilder`] instead of [`FramePipeline`](crate::core::filter::frame_pipeline::FramePipeline).
541 /// For details on why [`FramePipelineBuilder`] is used, see its documentation.
542 ///
543 /// # Parameters
544 /// * `frame_pipelines` - A list of [`FramePipelineBuilder`] instances defining the
545 /// transformations to apply before encoding.
546 ///
547 /// # Returns
548 /// * `Self` - Returns the modified `Output`, enabling method chaining.
549 ///
550 /// # Example
551 /// ```rust
552 /// let output = Output::from("some_url")
553 /// .set_frame_pipelines(vec![
554 /// FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_VIDEO).filter("opengl", Box::new(my_filter)),
555 /// // Additional pipelines...
556 /// ]);
557 /// ```
558 pub fn set_frame_pipelines(mut self, frame_pipelines: Vec<FramePipelineBuilder>) -> Self {
559 self.frame_pipelines = Some(frame_pipelines);
560 self
561 }
562
563 /// Adds a single [`FramePipelineBuilder`] to the existing pipeline list.
564 ///
565 /// If no pipelines are currently defined, this method creates a new pipeline list.
566 /// Otherwise, it appends the provided pipeline to the existing transformations.
567 ///
568 /// **Note:** This method accepts [`FramePipelineBuilder`] instead of [`FramePipeline`](crate::core::filter::frame_pipeline::FramePipeline).
569 /// For details on why [`FramePipelineBuilder`] is used, see its documentation.
570 ///
571 /// # Parameters
572 /// * `frame_pipeline` - A [`FramePipelineBuilder`] defining a transformation.
573 ///
574 /// # Returns
575 /// * `Self` - Returns the modified `Output`, enabling method chaining.
576 ///
577 /// # Example
578 /// ```rust
579 /// let output = Output::from("some_url")
580 /// .add_frame_pipeline(FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_VIDEO).filter("opengl", Box::new(my_filter)))
581 /// .add_frame_pipeline(FramePipelineBuilder::new(AVMediaType::AVMEDIA_TYPE_AUDIO).filter("my_custom_filter1", Box::new(...)).filter("my_custom_filter2", Box::new(...)));
582 /// ```
583 pub fn add_frame_pipeline(mut self, frame_pipeline: FramePipelineBuilder) -> Self {
584 if self.frame_pipelines.is_none() {
585 self.frame_pipelines = Some(vec![frame_pipeline]);
586 } else {
587 self.frame_pipelines
588 .as_mut()
589 .unwrap()
590 .push(frame_pipeline);
591 }
592 self
593 }
594
595 /// Adds a **stream mapping** for a specific stream or stream type,
596 /// **re-encoding** it according to this output’s codec settings.
597 ///
598 /// # Linklabel (FFmpeg-like Specifier)
599 ///
600 /// This string typically follows `"<input_index>:<media_type>"` syntax:
601 /// - **`"0:v"`** – the video stream(s) from input #0.
602 /// - **`"1:a?"`** – audio from input #1, **ignore** if none present (due to `?`).
603 /// - Other possibilities include `"0:s"`, `"0:d"`, etc. for subtitles/data, optionally with `?`.
604 ///
605 /// By calling `add_stream_map`, **you force re-encoding** of the chosen stream(s).
606 /// If the user wants a bit-for-bit copy, see [`add_stream_map_with_copy`](Self::add_stream_map_with_copy).
607 ///
608 /// # Parameters
609 /// - `linklabel`: An FFmpeg-style specifier referencing the desired input index and
610 /// media type, like `"0:v"`, `"1:a?"`, etc.
611 ///
612 /// # Returns
613 /// * `Self` - for chained method calls.
614 ///
615 /// # Example
616 /// ```rust
617 /// // Re-encode the video stream from input #0 (fail if no video).
618 /// let output = Output::from("output.mp4")
619 /// .add_stream_map("0:v");
620 /// ```
621 pub fn add_stream_map(mut self, linklabel: impl Into<String>) -> Self {
622 self.stream_maps.push(linklabel.into().into());
623 self
624 }
625
626 /// Adds a **stream mapping** for a specific stream or stream type,
627 /// **copying** it bit-for-bit from the source without re-encoding.
628 ///
629 /// # Linklabel (FFmpeg-like Specifier)
630 ///
631 /// Follows the same `"<input_index>:<media_type>"` pattern as [`add_stream_map`](Self::add_stream_map):
632 /// - **`"0:a"`** – audio stream(s) from input #0.
633 /// - **`"0:a?"`** – same, but ignore errors if no audio exists.
634 /// - And so on for video (`v`), subtitles (`s`), attachments (`t`), etc.
635 ///
636 /// # Copy vs. Re-encode
637 ///
638 /// Here, `copy = true` by default, meaning the chosen stream(s) are passed through
639 /// **without** decoding/encoding. This generally **only** works if the source’s codec
640 /// is compatible with the container/format you’re outputting to.
641 /// If you require re-encoding (e.g., to ensure compatibility or apply filters),
642 /// use [`add_stream_map`](Self::add_stream_map).
643 ///
644 /// # Parameters
645 /// - `linklabel`: An FFmpeg-style specifier referencing the desired input index and
646 /// media type, like `"0:v?"`.
647 ///
648 /// # Returns
649 /// * `Self` - for chained method calls.
650 ///
651 /// # Example
652 /// ```rust
653 /// // Copy the audio stream(s) from input #0 if present, no re-encode:
654 /// let output = Output::from("output.mkv")
655 /// .add_stream_map_with_copy("0:a?");
656 /// ```
657 pub fn add_stream_map_with_copy(mut self, linklabel: impl Into<String>) -> Self {
658 self.stream_maps.push(StreamMap {
659 linklabel: linklabel.into(),
660 copy: true,
661 });
662 self
663 }
664
665 /// Sets the **start time** (in microseconds) for output encoding.
666 ///
667 /// If this is set, FFmpeg will attempt to start encoding from the specified
668 /// timestamp in the input stream. This can be used to skip initial content.
669 ///
670 /// # Parameters
671 /// * `start_time_us` - The start time in microseconds.
672 ///
673 /// # Returns
674 /// * `Self` - The modified `Output`, allowing method chaining.
675 ///
676 /// # Example
677 /// ```rust
678 /// let output = Output::from("output.mp4")
679 /// .set_start_time_us(2_000_000); // Start at 2 seconds
680 /// ```
681 pub fn set_start_time_us(mut self, start_time_us: i64) -> Self {
682 self.start_time_us = Some(start_time_us);
683 self
684 }
685
686 /// Sets the **recording time** (in microseconds) for output encoding.
687 ///
688 /// This indicates how many microseconds of data should be processed
689 /// (i.e., maximum duration to encode). Once this time is reached,
690 /// FFmpeg will stop encoding.
691 ///
692 /// # Parameters
693 /// * `recording_time_us` - The maximum duration (in microseconds) to process.
694 ///
695 /// # Returns
696 /// * `Self` - The modified `Output`, allowing method chaining.
697 ///
698 /// # Example
699 /// ```rust
700 /// let output = Output::from("output.mp4")
701 /// .set_recording_time_us(5_000_000); // Record for 5 seconds
702 /// ```
703 pub fn set_recording_time_us(mut self, recording_time_us: i64) -> Self {
704 self.recording_time_us = Some(recording_time_us);
705 self
706 }
707
708 /// Sets a **stop time** (in microseconds) for output encoding.
709 ///
710 /// If set, FFmpeg will stop encoding once the input’s timestamp
711 /// surpasses this value. Effectively, encoding ends at this timestamp
712 /// regardless of remaining data.
713 ///
714 /// # Parameters
715 /// * `stop_time_us` - The timestamp (in microseconds) at which to stop.
716 ///
717 /// # Returns
718 /// * `Self` - The modified `Output`, allowing method chaining.
719 ///
720 /// # Example
721 /// ```rust
722 /// let output = Output::from("output.mp4")
723 /// .set_stop_time_us(10_000_000); // Stop at 10 seconds
724 /// ```
725 pub fn set_stop_time_us(mut self, stop_time_us: i64) -> Self {
726 self.stop_time_us = Some(stop_time_us);
727 self
728 }
729
730 /// Sets a **target frame rate** (`AVRational`) for output encoding.
731 ///
732 /// This can force the output to use a specific frame rate (e.g., 30/1 for 30 FPS).
733 /// If unset, FFmpeg typically preserves the source frame rate or uses defaults
734 /// based on the selected codec/container.
735 ///
736 /// # Parameters
737 /// * `framerate` - An `AVRational` representing the desired frame rate
738 /// numerator/denominator (e.g., `AVRational { num: 30, den: 1 }` for 30fps).
739 ///
740 /// # Returns
741 /// * `Self` - The modified `Output`, allowing method chaining.
742 ///
743 /// # Example
744 /// ```rust
745 /// use ffmpeg_sys_next::AVRational;
746 /// let output = Output::from("output.mp4")
747 /// .set_framerate(AVRational { num: 30, den: 1 });
748 /// ```
749 pub fn set_framerate(mut self, framerate: AVRational) -> Self {
750 self.framerate = Some(framerate);
751 self
752 }
753
754 /// Sets the **video sync method** to be used during encoding.
755 ///
756 /// FFmpeg uses a variety of vsync policies to handle frame presentation times,
757 /// dropping/duplicating frames as needed. Adjusting this can be useful when
758 /// you need strict CFR (constant frame rate), or to pass frames through
759 /// without modification (`VsyncPassthrough`).
760 ///
761 /// # Parameters
762 /// * `method` - A variant of [`VSyncMethod`], such as `VsyncCfr` or `VsyncVfr`.
763 ///
764 /// # Returns
765 /// * `Self` - The modified `Output`, allowing method chaining.
766 ///
767 /// # Example
768 /// ```rust
769 /// let output = Output::from("output.mp4")
770 /// .set_vsync_method(VSyncMethod::VsyncCfr);
771 /// ```
772 pub fn set_vsync_method(mut self, method: VSyncMethod) -> Self {
773 self.vsync_method = Some(method);
774 self
775 }
776
777 /// Sets the **bits per raw sample** for video encoding.
778 ///
779 /// This value can influence quality or color depth when dealing with
780 /// certain pixel formats. Commonly used for high-bit-depth workflows
781 /// or specialized encoding scenarios.
782 ///
783 /// # Parameters
784 /// * `bits` - The bits per raw sample (e.g., 8, 10, 12).
785 ///
786 /// # Returns
787 /// * `Self` - The modified `Output`, allowing method chaining.
788 ///
789 /// # Example
790 /// ```rust
791 /// let output = Output::from("output.mkv")
792 /// .set_bits_per_raw_sample(10); // e.g., 10-bit
793 /// ```
794 pub fn set_bits_per_raw_sample(mut self, bits: i32) -> Self {
795 self.bits_per_raw_sample = Some(bits);
796 self
797 }
798
799 /// Sets the **audio sample rate** (in Hz) for output encoding.
800 ///
801 /// This method allows you to specify the desired audio sample rate for the output.
802 /// Common values include 44100 (CD quality), 48000 (standard for digital video),
803 /// and 22050 or 16000 (for lower bitrate applications).
804 ///
805 /// # Parameters
806 /// * `audio_sample_rate` - The sample rate in Hertz (e.g., 44100, 48000).
807 ///
808 /// # Returns
809 /// * `Self` - The modified `Output`, allowing method chaining.
810 ///
811 /// # Example
812 /// ```rust
813 /// let output = Output::from("output.mp4")
814 /// .set_audio_sample_rate(48000); // Set to 48kHz
815 /// ```
816 pub fn set_audio_sample_rate(mut self, audio_sample_rate: i32) -> Self {
817 self.audio_sample_rate = Some(audio_sample_rate);
818 self
819 }
820
821 /// Sets the number of **audio channels** for output encoding.
822 ///
823 /// Common values include 1 (mono), 2 (stereo), 5.1 (6 channels), and 7.1 (8 channels).
824 /// This setting affects the spatial audio characteristics of the output.
825 ///
826 /// # Parameters
827 /// * `audio_channels` - The number of audio channels (e.g., 1 for mono, 2 for stereo).
828 ///
829 /// # Returns
830 /// * `Self` - The modified `Output`, allowing method chaining.
831 ///
832 /// # Example
833 /// ```rust
834 /// let output = Output::from("output.mp4")
835 /// .set_audio_channels(2); // Set to stereo
836 /// ```
837 pub fn set_audio_channels(mut self, audio_channels: i32) -> Self {
838 self.audio_channels = Some(audio_channels);
839 self
840 }
841
842 /// Sets the **audio sample format** for output encoding.
843 ///
844 /// This method allows you to specify the audio sample format, which affects
845 /// how audio samples are represented. Common formats include:
846 /// - `AV_SAMPLE_FMT_S16` (signed 16-bit)
847 /// - `AV_SAMPLE_FMT_S32` (signed 32-bit)
848 /// - `AV_SAMPLE_FMT_FLT` (32-bit float)
849 /// - `AV_SAMPLE_FMT_FLTP` (32-bit float, planar)
850 ///
851 /// The format choice can impact quality, processing requirements, and compatibility.
852 ///
853 /// # Parameters
854 /// * `sample_fmt` - An `AVSampleFormat` enum value specifying the desired sample format.
855 ///
856 /// # Returns
857 /// * `Self` - The modified `Output`, allowing method chaining.
858 ///
859 /// # Example
860 /// ```rust
861 /// use ffmpeg_sys_next::AVSampleFormat::AV_SAMPLE_FMT_S16;
862 ///
863 /// let output = Output::from("output.mp4")
864 /// .set_audio_sample_fmt(AV_SAMPLE_FMT_S16); // Set to signed 16-bit
865 /// ```
866 pub fn set_audio_sample_fmt(mut self, sample_fmt: AVSampleFormat) -> Self {
867 self.audio_sample_fmt = Some(sample_fmt);
868 self
869 }
870
871 /// Sets the **video quality scale** (VBR) for encoding.
872 ///
873 /// This method configures a fixed quality scale for variable bitrate (VBR) video encoding.
874 /// Lower values result in higher quality but larger file sizes, while higher values
875 /// produce lower quality with smaller file sizes.
876 ///
877 /// # Note on Modern Usage
878 /// While still supported, using fixed quality scale (`-q:v`) is generally not recommended
879 /// for modern video encoding workflows with codecs like H.264 and H.265. Instead, consider:
880 /// * For H.264/H.265: Use CRF (Constant Rate Factor) via `-crf` parameter
881 /// * For two-pass encoding: Use target bitrate settings
882 ///
883 /// This parameter is primarily useful for older codecs or specific scenarios where
884 /// direct quality scale control is needed.
885 ///
886 /// # Quality Scale Ranges by Codec
887 /// * **H.264/H.265**: 0-51 (if needed: 17-28)
888 /// - 17-18: Visually lossless
889 /// - 23: High quality
890 /// - 28: Good quality with reasonable file size
891 /// * **MPEG-4/MPEG-2**: 2-31 (recommended: 2-6)
892 /// - Lower values = higher quality
893 /// * **VP9**: 0-63 (if needed: 15-35)
894 ///
895 /// # Parameters
896 /// * `video_qscale` - The quality scale value for video encoding.
897 ///
898 /// # Returns
899 /// * `Self` - The modified `Output`, allowing method chaining.
900 ///
901 /// # Example
902 /// ```rust
903 /// // For MJPEG encoding of image sequences
904 /// let output = Output::from("output.jpg")
905 /// .set_video_qscale(2); // High quality JPEG images
906 ///
907 /// // For legacy image format conversion
908 /// let output = Output::from("output.png")
909 /// .set_video_qscale(3); // Controls compression level
910 /// ```
911 pub fn set_video_qscale(mut self, video_qscale: i32) -> Self {
912 self.video_qscale = Some(video_qscale);
913 self
914 }
915
916 /// Sets the **audio quality scale** for encoding.
917 ///
918 /// This method configures codec-specific audio quality settings. The range, behavior,
919 /// and optimal values depend entirely on the audio codec being used.
920 ///
921 /// # Quality Scale Ranges by Codec
922 /// * **MP3 (libmp3lame)**: 0-9 (recommended: 2-5)
923 /// - 0: Highest quality
924 /// - 2: Near-transparent quality (~190-200 kbps)
925 /// - 5: Good quality (~130 kbps)
926 /// - 9: Lowest quality
927 /// * **AAC**: 0.1-255 (recommended: 1-5)
928 /// - 1: Highest quality (~250 kbps)
929 /// - 3: Good quality (~160 kbps)
930 /// - 5: Medium quality (~100 kbps)
931 /// * **Vorbis**: -1 to 10 (recommended: 3-8)
932 /// - 10: Highest quality
933 /// - 5: Good quality
934 /// - 3: Medium quality
935 ///
936 /// # Parameters
937 /// * `audio_qscale` - The quality scale value for audio encoding.
938 ///
939 /// # Returns
940 /// * `Self` - The modified `Output`, allowing method chaining.
941 ///
942 /// # Example
943 /// ```rust
944 /// // For MP3 encoding at high quality
945 /// let output = Output::from("output.mp3")
946 /// .set_audio_codec("libmp3lame")
947 /// .set_audio_qscale(2);
948 ///
949 /// // For AAC encoding at good quality
950 /// let output = Output::from("output.m4a")
951 /// .set_audio_codec("aac")
952 /// .set_audio_qscale(3);
953 ///
954 /// // For Vorbis encoding at high quality
955 /// let output = Output::from("output.ogg")
956 /// .set_audio_codec("libvorbis")
957 /// .set_audio_qscale(7);
958 /// ```
959 pub fn set_audio_qscale(mut self, audio_qscale: i32) -> Self {
960 self.audio_qscale = Some(audio_qscale);
961 self
962 }
963
964 /// **Sets the maximum number of video frames to encode (`-frames:v`).**
965 ///
966 /// **Equivalent FFmpeg Command:**
967 /// ```sh
968 /// ffmpeg -i input.mp4 -frames:v 100 output.mp4
969 /// ```
970 ///
971 /// **Example Usage:**
972 /// ```rust
973 /// let output = Output::from("some_url")
974 /// .set_max_video_frames(500);
975 /// ```
976 pub fn set_max_video_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
977 self.max_video_frames = max_frames.into();
978 self
979 }
980
981 /// **Sets the maximum number of audio frames to encode (`-frames:a`).**
982 ///
983 /// **Equivalent FFmpeg Command:**
984 /// ```sh
985 /// ffmpeg -i input.mp4 -frames:a 500 output.mp4
986 /// ```
987 ///
988 /// **Example Usage:**
989 /// ```rust
990 /// let output = Output::from("some_url")
991 /// .set_max_audio_frames(500);
992 /// ```
993 pub fn set_max_audio_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
994 self.max_audio_frames = max_frames.into();
995 self
996 }
997
998 /// **Sets the maximum number of subtitle frames to encode (`-frames:s`).**
999 ///
1000 /// **Equivalent FFmpeg Command:**
1001 /// ```sh
1002 /// ffmpeg -i input.mp4 -frames:s 200 output.mp4
1003 /// ```
1004 ///
1005 /// **Example Usage:**
1006 /// ```rust
1007 /// let output = Output::from("some_url")
1008 /// .set_max_subtitle_frames(200);
1009 /// ```
1010 pub fn set_max_subtitle_frames(mut self, max_frames: impl Into<Option<i64>>) -> Self {
1011 self.max_subtitle_frames = max_frames.into();
1012 self
1013 }
1014
1015 /// Sets a **video codec-specific option**.
1016 ///
1017 /// These options control **video encoding parameters** such as compression, quality, and speed.
1018 ///
1019 /// **Supported Parameters:**
1020 /// | Parameter | Description |
1021 /// |-----------|-------------|
1022 /// | `crf=0-51` | Quality level for x264/x265, lower means higher quality (`0` is lossless) |
1023 /// | `preset=ultrafast, superfast, fast, medium, slow, veryslow` | Encoding speed, affects compression efficiency |
1024 /// | `tune=film, animation, grain, stillimage, fastdecode, zerolatency` | Optimizations for specific types of content |
1025 /// | `b=4M` | Bitrate (e.g., `4M` for 4 Mbps) |
1026 /// | `g=50` | GOP (Group of Pictures) size, affects keyframe frequency |
1027 ///
1028 /// **Example Usage:**
1029 /// ```rust
1030 /// let output = Output::from("some_url")
1031 /// .set_video_codec_opt("crf", "18")
1032 /// .set_video_codec_opt("preset", "fast");
1033 /// ```
1034 pub fn set_video_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1035 if let Some(ref mut opts) = self.video_codec_opts {
1036 opts.insert(key.into(), value.into());
1037 } else {
1038 let mut opts = HashMap::new();
1039 opts.insert(key.into(), value.into());
1040 self.video_codec_opts = Some(opts);
1041 }
1042 self
1043 }
1044
1045 /// **Sets multiple video codec options at once.**
1046 ///
1047 /// **Example Usage:**
1048 /// ```rust
1049 /// let output = Output::from("some_url")
1050 /// .set_video_codec_opts(vec![
1051 /// ("crf", "18"),
1052 /// ("preset", "fast")
1053 /// ]);
1054 /// ```
1055 pub fn set_video_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1056 let video_opts = self.video_codec_opts.get_or_insert_with(HashMap::new);
1057 for (key, value) in opts {
1058 video_opts.insert(key.into(), value.into());
1059 }
1060 self
1061 }
1062
1063 /// Sets a **audio codec-specific option**.
1064 ///
1065 /// These options control **audio encoding parameters** such as bitrate, sample rate, and format.
1066 ///
1067 /// **Supported Parameters:**
1068 /// | Parameter | Description |
1069 /// |-----------|-------------|
1070 /// | `b=192k` | Bitrate (e.g., `128k` for 128 Kbps, `320k` for 320 Kbps) |
1071 /// | `compression_level=0-12` | Compression efficiency for formats like FLAC |
1072 ///
1073 /// **Example Usage:**
1074 /// ```rust
1075 /// let output = Output::from("some_url")
1076 /// .set_audio_codec_opt("b", "320k")
1077 /// .set_audio_codec_opt("compression_level", "6");
1078 /// ```
1079 pub fn set_audio_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1080 if let Some(ref mut opts) = self.audio_codec_opts {
1081 opts.insert(key.into(), value.into());
1082 } else {
1083 let mut opts = HashMap::new();
1084 opts.insert(key.into(), value.into());
1085 self.audio_codec_opts = Some(opts);
1086 }
1087 self
1088 }
1089
1090 /// **Sets multiple audio codec options at once.**
1091 ///
1092 /// **Example Usage:**
1093 /// ```rust
1094 /// let output = Output::from("some_url")
1095 /// .set_audio_codec_opts(vec![
1096 /// ("b", "320k"),
1097 /// ("compression_level", "6")
1098 /// ]);
1099 /// ```
1100 pub fn set_audio_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1101 let audio_opts = self.audio_codec_opts.get_or_insert_with(HashMap::new);
1102 for (key, value) in opts {
1103 audio_opts.insert(key.into(), value.into());
1104 }
1105 self
1106 }
1107
1108 /// Sets a **subtitle codec-specific option**.
1109 ///
1110 /// These options control **subtitle encoding parameters** such as format and character encoding.
1111 ///
1112 /// **Supported Parameters:**
1113 /// | Parameter | Description |
1114 /// |-----------|-------------|
1115 /// | `mov_text` | Subtitle format for MP4 files |
1116 /// | `srt` | Subtitle format for `.srt` files |
1117 /// | `ass` | Advanced SubStation Alpha (ASS) subtitle format |
1118 /// | `forced_subs=1` | Forces the subtitles to always be displayed |
1119 ///
1120 /// **Example Usage:**
1121 /// ```rust
1122 /// let output = Output::from("some_url")
1123 /// .set_subtitle_codec_opt("mov_text", "");
1124 /// ```
1125 pub fn set_subtitle_codec_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1126 if let Some(ref mut opts) = self.subtitle_codec_opts {
1127 opts.insert(key.into(), value.into());
1128 } else {
1129 let mut opts = HashMap::new();
1130 opts.insert(key.into(), value.into());
1131 self.subtitle_codec_opts = Some(opts);
1132 }
1133 self
1134 }
1135
1136 /// **Sets multiple subtitle codec options at once.**
1137 ///
1138 /// **Example Usage:**
1139 /// ```rust
1140 /// let output = Output::from("some_url")
1141 /// .set_subtitle_codec_opts(vec![
1142 /// ("mov_text", ""),
1143 /// ("forced_subs", "1")
1144 /// ]);
1145 /// ```
1146 pub fn set_subtitle_codec_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1147 let subtitle_opts = self.subtitle_codec_opts.get_or_insert_with(HashMap::new);
1148 for (key, value) in opts {
1149 subtitle_opts.insert(key.into(), value.into());
1150 }
1151 self
1152 }
1153
1154 /// Sets a format-specific option for the output container.
1155 ///
1156 /// FFmpeg supports various format-specific options that can be passed to the muxer.
1157 /// These options allow fine-tuning of the output container’s behavior.
1158 ///
1159 /// **Example Usage:**
1160 /// ```rust
1161 /// let output = Output::from("some_url")
1162 /// .set_format_opt("movflags", "faststart")
1163 /// .set_format_opt("flvflags", "no_duration_filesize");
1164 /// ```
1165 ///
1166 /// ### Common Format Options:
1167 /// | Format | Option | Description |
1168 /// |--------|--------|-------------|
1169 /// | `mp4` | `movflags=faststart` | Moves moov atom to the beginning of the file for faster playback start |
1170 /// | `flv` | `flvflags=no_duration_filesize` | Removes duration/size metadata for live streaming |
1171 ///
1172 /// **Parameters:**
1173 /// - `key`: The format option name (e.g., `"movflags"`, `"flvflags"`).
1174 /// - `value`: The value to set (e.g., `"faststart"`, `"no_duration_filesize"`).
1175 ///
1176 /// Returns the modified `Output` struct for chaining.
1177 pub fn set_format_opt(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1178 if let Some(ref mut opts) = self.format_opts {
1179 opts.insert(key.into(), value.into());
1180 } else {
1181 let mut opts = HashMap::new();
1182 opts.insert(key.into(), value.into());
1183 self.format_opts = Some(opts);
1184 }
1185 self
1186 }
1187
1188 /// Sets multiple format-specific options at once.
1189 ///
1190 /// This method allows setting multiple format options in a single call.
1191 ///
1192 /// **Example Usage:**
1193 /// ```rust
1194 /// let output = Output::from("some_url")
1195 /// .set_format_opts(vec![
1196 /// ("movflags", "faststart"),
1197 /// ("flvflags", "no_duration_filesize")
1198 /// ]);
1199 /// ```
1200 ///
1201 /// **Parameters:**
1202 /// - `opts`: A vector of key-value pairs representing format options.
1203 ///
1204 /// Returns the modified `Output` struct for chaining.
1205 pub fn set_format_opts(mut self, opts: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
1206 if let Some(ref mut format_opts) = self.format_opts {
1207 for (key, value) in opts {
1208 format_opts.insert(key.into(), value.into());
1209 }
1210 } else {
1211 let mut format_opts = HashMap::new();
1212 for (key, value) in opts {
1213 format_opts.insert(key.into(), value.into());
1214 }
1215 self.format_opts = Some(format_opts);
1216 }
1217 self
1218 }
1219}
1220
1221impl From<Box<dyn FnMut(&[u8]) -> i32>> for Output {
1222 fn from(write_callback_and_format: Box<dyn FnMut(&[u8]) -> i32>) -> Self {
1223 Self {
1224 url: None,
1225 write_callback: Some(write_callback_and_format),
1226 seek_callback: None,
1227 frame_pipelines: None,
1228 stream_maps: vec![],
1229 format: None,
1230 video_codec: None,
1231 audio_codec: None,
1232 subtitle_codec: None,
1233 start_time_us: None,
1234 recording_time_us: None,
1235 stop_time_us: None,
1236 framerate: None,
1237 vsync_method: None,
1238 bits_per_raw_sample: None,
1239 audio_sample_rate: None,
1240 audio_channels: None,
1241 audio_sample_fmt: None,
1242 video_qscale: None,
1243 audio_qscale: None,
1244 max_video_frames: None,
1245 max_audio_frames: None,
1246 max_subtitle_frames: None,
1247 video_codec_opts: None,
1248 audio_codec_opts: None,
1249 subtitle_codec_opts: None,
1250 format_opts: None,
1251 }
1252 }
1253}
1254
1255impl From<String> for Output {
1256 fn from(url: String) -> Self {
1257 Self {
1258 url: Some(url),
1259 write_callback: None,
1260 seek_callback: None,
1261 frame_pipelines: None,
1262 stream_maps: vec![],
1263 format: None,
1264 video_codec: None,
1265 audio_codec: None,
1266 subtitle_codec: None,
1267 start_time_us: None,
1268 recording_time_us: None,
1269 stop_time_us: None,
1270 framerate: None,
1271 vsync_method: None,
1272 bits_per_raw_sample: None,
1273 audio_sample_rate: None,
1274 audio_channels: None,
1275 audio_sample_fmt: None,
1276 video_qscale: None,
1277 audio_qscale: None,
1278 max_video_frames: None,
1279 max_audio_frames: None,
1280 max_subtitle_frames: None,
1281 video_codec_opts: None,
1282 audio_codec_opts: None,
1283 subtitle_codec_opts: None,
1284 format_opts: None,
1285 }
1286 }
1287}
1288
1289impl From<&str> for Output {
1290 fn from(url: &str) -> Self {
1291 Self::from(String::from(url))
1292 }
1293}
1294
1295#[derive(Debug, Clone)]
1296pub(crate) struct StreamMap {
1297 pub(crate) linklabel: String,
1298 pub(crate) copy: bool,
1299}
1300
1301impl<T: Into<String>> From<T> for StreamMap {
1302 fn from(linklabel: T) -> Self {
1303 Self {
1304 linklabel: linklabel.into(),
1305 copy: false,
1306 }
1307 }
1308}