Skip to main content

unbundle/
metadata.rs

1//! Media metadata types.
2//!
3//! This module defines the metadata structures returned by
4//! [`MediaFile::metadata`](crate::MediaFile::metadata). Metadata is
5//! extracted once when the file is opened and cached for the lifetime of the
6//! unbundler.
7
8use std::collections::HashMap;
9use std::time::Duration;
10
11/// Complete metadata for a media file.
12///
13/// Contains optional video and audio stream metadata, plus container-level
14/// information such as total duration and format name.
15///
16/// # Example
17///
18/// ```no_run
19/// use unbundle::{MediaFile, UnbundleError};
20///
21/// let unbundler = MediaFile::open("input.mp4").unwrap();
22/// let metadata = unbundler.metadata();
23/// println!("Duration: {:?}", metadata.duration);
24/// println!("Format: {}", metadata.format);
25/// if let Some(tracks) = metadata.audio_tracks.as_ref() {
26///     println!("Audio tracks: {}", tracks.len());
27/// }
28/// ```
29#[derive(Debug, Clone)]
30#[must_use]
31pub struct MediaMetadata {
32    /// Video stream metadata, if a video stream is present.
33    pub video: Option<VideoMetadata>,
34    /// Metadata for all video tracks in the file.
35    ///
36    /// `None` if there are no video streams. When present, the first entry
37    /// matches [`video`](MediaMetadata::video) (the "best" stream).
38    pub video_tracks: Option<Vec<VideoMetadata>>,
39    /// Audio stream metadata for the best (default) audio stream.
40    pub audio: Option<AudioMetadata>,
41    /// Metadata for all audio tracks in the file.
42    ///
43    /// `None` if there are no audio streams. When present, the first entry
44    /// matches [`audio`](MediaMetadata::audio) (the "best" stream).
45    pub audio_tracks: Option<Vec<AudioMetadata>>,
46    /// Subtitle stream metadata for the best subtitle stream.
47    pub subtitle: Option<SubtitleMetadata>,
48    /// Metadata for all subtitle tracks in the file.
49    pub subtitle_tracks: Option<Vec<SubtitleMetadata>>,
50    /// Chapter metadata, if the container contains chapters.
51    ///
52    /// Chapters represent named time segments (e.g. scenes, acts) embedded in
53    /// the container. `None` when no chapters are present.
54    pub chapters: Option<Vec<ChapterMetadata>>,
55    /// Total duration of the media file.
56    pub duration: Duration,
57    /// Container format name (e.g. `"mp4"`, `"matroska"`, `"avi"`).
58    pub format: String,
59    /// Container-level metadata tags (e.g. title, artist, album, date).
60    ///
61    /// `None` when the container has no metadata tags.
62    pub tags: Option<HashMap<String, String>>,
63}
64
65/// Metadata for a video stream.
66///
67/// Includes dimensions, frame rate, estimated frame count, codec name,
68/// and colorspace information.
69#[derive(Debug, Clone)]
70#[must_use]
71pub struct VideoMetadata {
72    /// Frame width in pixels.
73    pub width: u32,
74    /// Frame height in pixels.
75    pub height: u32,
76    /// Frames per second (may be approximate for variable-frame-rate content).
77    pub frames_per_second: f64,
78    /// Estimated total number of frames, computed from duration and frame rate.
79    pub frame_count: u64,
80    /// Codec name (e.g. `"h264"`, `"vp9"`, `"av1"`).
81    pub codec: String,
82    /// Color space (e.g. `"BT709"`, `"BT2020NCL"`), if available.
83    pub color_space: Option<String>,
84    /// Color range (`"TV"` for limited, `"PC"` for full), if available.
85    pub color_range: Option<String>,
86    /// Color primaries (e.g. `"BT709"`, `"BT2020"`), if available.
87    pub color_primaries: Option<String>,
88    /// Color transfer characteristics (e.g. `"SMPTE2084"` for HDR10 PQ), if available.
89    pub color_transfer: Option<String>,
90    /// Bits per raw sample (e.g. 8, 10, 12), if available.
91    pub bits_per_raw_sample: Option<u32>,
92    /// Pixel format name (e.g. `"yuv420p"`, `"yuv420p10le"`), if available.
93    pub pixel_format_name: Option<String>,
94    /// Zero-based track number among all video streams in the file.
95    pub track_index: usize,
96    /// FFmpeg stream index within the container.
97    pub(crate) stream_index: usize,
98}
99
100/// Metadata for an audio stream.
101///
102/// Includes sample rate, channel count, codec name, and bit rate.
103/// When multiple audio tracks exist, use
104/// [`track_index`](AudioMetadata::track_index) to identify each.
105#[derive(Debug, Clone)]
106#[must_use]
107pub struct AudioMetadata {
108    /// Sample rate in hertz (e.g. `44100`, `48000`).
109    pub sample_rate: u32,
110    /// Number of audio channels (e.g. `2` for stereo).
111    pub channels: u16,
112    /// Codec name (e.g. `"aac"`, `"mp3"`, `"flac"`).
113    pub codec: String,
114    /// Bit rate in bits per second.
115    pub bit_rate: u64,
116    /// Zero-based track number among all audio streams in the file.
117    pub track_index: usize,
118    /// FFmpeg stream index within the container.
119    pub(crate) stream_index: usize,
120}
121
122/// Metadata for a chapter within a media file.
123///
124/// Chapters represent named time segments (e.g. scenes, acts, or songs)
125/// embedded in the container by the authoring tool. Not all containers
126/// support chapters; when present they are extracted at open time and
127/// stored in [`MediaMetadata::chapters`].
128///
129/// # Example
130///
131/// ```no_run
132/// use unbundle::{MediaFile, UnbundleError};
133///
134/// let unbundler = MediaFile::open("input.mkv")?;
135/// if let Some(chapters) = unbundler.metadata().chapters.as_ref() {
136///     for chapter in chapters {
137///         println!("[{:?}–{:?}] {}", chapter.start, chapter.end,
138///             chapter.title.as_deref().unwrap_or("(untitled)"));
139///     }
140/// }
141/// # Ok::<(), UnbundleError>(())
142/// ```
143#[derive(Debug, Clone)]
144#[must_use]
145pub struct ChapterMetadata {
146    /// Human-readable chapter title, if tagged (e.g. `"Opening Credits"`).
147    pub title: Option<String>,
148    /// Start time of the chapter.
149    pub start: Duration,
150    /// End time of the chapter.
151    pub end: Duration,
152    /// Zero-based chapter index within the container.
153    pub index: usize,
154    /// The chapter's unique identifier as stored in the container.
155    pub id: i64,
156}
157
158/// Metadata for a subtitle stream.
159///
160/// Includes codec name, language (if tagged), and track index.
161#[derive(Debug, Clone)]
162#[must_use]
163pub struct SubtitleMetadata {
164    /// Codec name (e.g. `"subrip"`, `"ass"`, `"mov_text"`).
165    pub codec: String,
166    /// Language tag from stream metadata (e.g. `"eng"`, `"fre"`), if available.
167    pub language: Option<String>,
168    /// Zero-based track number among all subtitle streams in the file.
169    pub track_index: usize,
170    /// FFmpeg stream index within the container.
171    pub(crate) stream_index: usize,
172}