ez_ffmpeg/core/context/output/attachment.rs
1use super::Output;
2
3impl Output {
4 /// Embeds a file as a container **attachment** stream (FFmpeg `-attach`),
5 /// e.g. a `.ttf`/`.otf` font for Matroska subtitle rendering, or cover art.
6 ///
7 /// The MIME type is guessed from the file extension
8 /// (`.ttf` → `application/x-truetype-font`,
9 /// `.otf` → `application/vnd.ms-opentype`, otherwise
10 /// `application/octet-stream`). Use
11 /// [`add_attachment_with_mimetype`](Self::add_attachment_with_mimetype) to
12 /// set it explicitly.
13 ///
14 /// # Behavior & limitations
15 /// - **Local files only.** The path is read with `std::fs` at build time,
16 /// so protocol URLs (`http:`, `pipe:`, …) are **not** supported here
17 /// (unlike some FFmpeg inputs). A missing, unreadable, empty, or oversized
18 /// file surfaces as an `Err` from
19 /// [`FfmpegContext`](crate::FfmpegContext) build — never a panic. The
20 /// setter itself does no I/O and never fails.
21 /// - **Muxer support.** Attachments are supported only by **Matroska/WebM**
22 /// (`.mkv`/`.webm`). MP4, MOV, MPEG-TS and other muxers do not accept a
23 /// generic attachment stream and will reject the job at header-write time
24 /// (surfacing as an `Err` from the running job, not a panic).
25 /// - Must be used **alongside at least one mapped/encoded stream**: an
26 /// output whose only stream is an attachment is not supported.
27 ///
28 /// # Example
29 /// ```rust,ignore
30 /// let output = Output::from("output.mkv")
31 /// .add_stream_map("0:v")
32 /// .add_attachment("assets/DejaVuSans.ttf");
33 /// ```
34 pub fn add_attachment(mut self, path: impl Into<std::path::PathBuf>) -> Self {
35 self.attachments.push(AttachmentSpec {
36 path: path.into(),
37 mimetype: None,
38 });
39 self
40 }
41
42 /// Same as [`add_attachment`](Self::add_attachment) but with an explicit
43 /// MIME type (e.g. `"application/x-truetype-font"`, `"image/png"`) instead
44 /// of guessing from the extension.
45 ///
46 /// An empty `mimetype` is rejected at build time (Matroska requires a
47 /// non-empty mimetype tag for every attachment).
48 ///
49 /// # Example
50 /// ```rust,ignore
51 /// let output = Output::from("output.mkv")
52 /// .add_stream_map("0:v")
53 /// .add_attachment_with_mimetype("cover.png", "image/png");
54 /// ```
55 pub fn add_attachment_with_mimetype(
56 mut self,
57 path: impl Into<std::path::PathBuf>,
58 mimetype: impl Into<String>,
59 ) -> Self {
60 self.attachments.push(AttachmentSpec {
61 path: path.into(),
62 mimetype: Some(mimetype.into()),
63 });
64 self
65 }
66}
67
68/// One `-attach` request. The file is **not** read here; I/O is deferred to
69/// output build time so the [`Output`] setters stay infallible.
70///
71/// Created via [`Output::add_attachment`] /
72/// [`Output::add_attachment_with_mimetype`].
73#[derive(Debug, Clone)]
74pub(crate) struct AttachmentSpec {
75 /// Path to the file whose bytes become the attachment payload (local file).
76 pub(crate) path: std::path::PathBuf,
77 /// Explicit MIME type override. `None` ⇒ guess from the file extension.
78 pub(crate) mimetype: Option<String>,
79}