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}