vtx_sdk/host/
vtx_ffmpeg.rs

1//! Host-side FFmpeg helpers.
2
3use crate::bindings::vtx::api::vtx_ffmpeg::{self, FfmpegOption, TranscodeProfile};
4use crate::bindings::vtx::api::vtx_types::HttpResponse;
5use crate::bindings::vtx::api::vtx_vfs::Buffer;
6use crate::vtx_error::{VtxError, VtxResult};
7
8/// FFmpeg task builder for running host-side transcoding.
9///
10/// # Example
11///
12/// ```rust
13/// use vtx_sdk::prelude::*;
14///
15/// fn handle_video(vid: String) -> VtxResult<Response> {
16///     FfmpegTask::new("mini", vid)
17///         .option("ss", "10")
18///         .option("t", "30")
19///         .execute()
20/// }
21/// ```
22pub struct FfmpegTask {
23    profile: String,
24    input_id: String,
25    options: Vec<FfmpegOption>,
26}
27
28impl FfmpegTask {
29    /// Create a new FFmpeg task.
30    ///
31    /// - `profile`: Target FFmpeg profile (e.g., "mini", "remux", "thumbnail").
32    /// - `input_id`: Input resource ID (UUID) or "pipe:0".
33    pub fn new(profile: impl Into<String>, input_id: impl Into<String>) -> Self {
34        Self {
35            profile: profile.into(),
36            input_id: input_id.into(),
37            options: Vec::new(),
38        }
39    }
40
41    /// Create a task that uses stdin as input (`input_id = "pipe:0"`).
42    pub fn new_pipe(profile: impl Into<String>) -> Self {
43        Self::new(profile, "pipe:0")
44    }
45
46    /// Add a key/value FFmpeg option (encoded as `-key=value`).
47    pub fn option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
48        self.options.push(FfmpegOption {
49            key: key.into(),
50            value: Some(value.into()),
51        });
52        self
53    }
54
55    /// Add a flag-style FFmpeg option (encoded as `-key`).
56    pub fn flag(mut self, key: impl Into<String>) -> Self {
57        self.options.push(FfmpegOption {
58            key: key.into(),
59            value: None,
60        });
61        self
62    }
63
64    /// Add a batch of key/value options.
65    pub fn options<I, K, V>(mut self, options: I) -> Self
66    where
67        I: IntoIterator<Item = (K, V)>,
68        K: Into<String>,
69        V: Into<String>,
70    {
71        for (key, value) in options {
72            self.options.push(FfmpegOption {
73                key: key.into(),
74                value: Some(value.into()),
75            });
76        }
77        self
78    }
79
80    /// Helper: Set output format (equivalent to `-f=format`).
81    pub fn format(self, format: &str) -> Self {
82        self.option("f", format)
83    }
84
85    /// Helper: Set seek window (equivalent to `-ss` + optional `-t`).
86    pub fn seek(self, start: &str, duration: Option<&str>) -> Self {
87        let mut s = self.option("ss", start);
88        if let Some(d) = duration {
89            s = s.option("t", d);
90        }
91        s
92    }
93
94    /// Execute and return the stdout pipe buffer.
95    ///
96    /// This method performs an IO operation.
97    pub fn execute_buffer(self) -> VtxResult<Buffer> {
98        let params = TranscodeProfile {
99            profile: self.profile,
100            input_id: self.input_id,
101            options: self.options,
102        };
103
104        vtx_ffmpeg::execute(&params).map_err(VtxError::from_host_message)
105    }
106
107    /// Execute and return an HTTP response (`200` with stdout pipe body).
108    ///
109    /// This method performs an IO operation.
110    pub fn execute(self) -> VtxResult<HttpResponse> {
111        let buffer = self.execute_buffer()?;
112        Ok(HttpResponse {
113            status: 200,
114            body: Some(buffer),
115        })
116    }
117}