vtx_sdk/
ffmpeg.rs

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