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}