vtx_sdk/host/
ffmpeg.rs

1//! Host-side FFmpeg helpers.
2
3use crate::bindings::vtx::api::ffmpeg::{self, TranscodeParams};
4use crate::bindings::vtx::api::stream_io::Buffer;
5use crate::bindings::vtx::api::types::HttpResponse;
6use crate::error::{VtxError, VtxResult};
7
8/// FFmpeg 任务构建器
9///
10/// 用于构建并执行服务端的 FFmpeg 转码任务。
11/// 采用 Builder 模式,支持链式调用。
12///
13/// # Example
14///
15/// ```rust
16/// use vtx_sdk::prelude::*;
17///
18/// fn handle_video(vid: String) -> VtxResult<Response> {
19///     FfmpegTask::new("mini", vid)
20///         .arg("-ss 10")
21///         .arg("-t 30")
22///         .execute()
23/// }
24/// ```
25pub struct FfmpegTask {
26    profile: String,
27    input_id: String,
28    args: Vec<String>,
29}
30
31impl FfmpegTask {
32    /// 创建一个新的 FFmpeg 任务
33    ///
34    /// # Parameters
35    /// - `profile`: 目标 Profile 名称 (如 "mini", "remux", "thumbnail")
36    /// - `input_id`: 输入视频的唯一资源 ID (UUID)
37    pub fn new(profile: impl Into<String>, input_id: impl Into<String>) -> Self {
38        Self {
39            profile: profile.into(),
40            input_id: input_id.into(),
41            args: Vec::new(),
42        }
43    }
44
45    /// 创建一个使用 stdin 管道作为输入的任务(等价于 `input_id = "pipe:0"`)。
46    pub fn new_pipe(profile: impl Into<String>) -> Self {
47        Self::new(profile, "pipe:0")
48    }
49
50    /// 添加单个 FFmpeg 参数
51    ///
52    /// 自动处理参数转义,防止注入风险。
53    ///
54    /// # Example
55    /// `.arg("-ss").arg("10")`
56    pub fn arg(mut self, arg: impl Into<String>) -> Self {
57        self.args.push(arg.into());
58        self
59    }
60
61    /// 批量添加参数
62    pub fn args<I, S>(mut self, args: I) -> Self
63    where
64        I: IntoIterator<Item = S>,
65        S: Into<String>,
66    {
67        for arg in args {
68            self.args.push(arg.into());
69        }
70        self
71    }
72
73    /// 快捷方法:设置输出格式
74    /// 等同于 `.arg("-f").arg(format)`
75    pub fn format(self, format: &str) -> Self {
76        self.arg("-f").arg(format)
77    }
78
79    /// 快捷方法:设置时间裁剪
80    /// 等同于 `.arg("-ss").arg(start).arg("-t").arg(duration)`
81    pub fn seek(self, start: &str, duration: Option<&str>) -> Self {
82        let mut s = self.arg("-ss").arg(start);
83        if let Some(d) = duration {
84            s = s.arg("-t").arg(d);
85        }
86        s
87    }
88
89    /// 执行任务并返回 Buffer 资源句柄。
90    ///
91    /// 这允许你在返回响应前,使用 `buffer.write(...)` 往 `stdin` 写入数据(当 `input_id="pipe:0"` 时)。
92    pub fn execute_buffer(self) -> VtxResult<Buffer> {
93        let params = TranscodeParams {
94            profile: self.profile,
95            input_id: self.input_id,
96            args: self.args,
97        };
98
99        ffmpeg::execute(&params).map_err(VtxError::from_host_message)
100    }
101
102    /// 执行任务并返回 HTTP 响应(`200` + body=stdout 管道 Buffer)。
103    ///
104    /// 该方法会阻塞等待子进程启动,并立即返回包含 stdout 管道流的 HttpResponse。
105    /// 数据将以流式传输给客户端,无需等待转码完成。
106    pub fn execute(self) -> VtxResult<HttpResponse> {
107        let buffer = self.execute_buffer()?;
108        Ok(HttpResponse {
109            status: 200,
110            body: Some(buffer),
111        })
112    }
113}