1use alloc::boxed::Box;
10use alloc::string::String;
11use alloc::sync::Arc;
12use core::fmt;
13
14use zencodec::time::{FrameDuration, TimeBase};
15use zenpixels::Cicp;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum TrackKind {
21 Video,
23 Audio,
25 Subtitle,
27 Data,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq, Hash)]
35pub struct CodecTag(Box<str>);
36
37impl CodecTag {
38 pub fn new(tag: &str) -> Self {
40 Self(String::from(tag).into_boxed_str())
41 }
42
43 pub fn as_str(&self) -> &str {
45 &self.0
46 }
47}
48
49#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51#[non_exhaustive]
52pub enum CodecId {
53 Av1,
55 Vp9,
57 Vp8,
59 H264,
61 Hevc,
63 Aac,
65 Opus,
67 Flac,
69 Pcm,
71 Other(CodecTag),
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77#[non_exhaustive]
78pub struct VideoTrackInfo {
79 pub coded_width: u32,
81 pub coded_height: u32,
83 pub display: Option<(u32, u32)>,
85 pub color: Option<Cicp>,
88}
89
90impl VideoTrackInfo {
91 pub const fn new(coded_width: u32, coded_height: u32) -> Self {
93 Self {
94 coded_width,
95 coded_height,
96 display: None,
97 color: None,
98 }
99 }
100
101 pub const fn with_display(mut self, width: u32, height: u32) -> Self {
103 self.display = Some((width, height));
104 self
105 }
106
107 pub const fn with_color(mut self, color: Cicp) -> Self {
109 self.color = Some(color);
110 self
111 }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116#[non_exhaustive]
117pub struct ChannelLayout {
118 pub count: u16,
120 pub mask: Option<u64>,
122}
123
124impl ChannelLayout {
125 pub const fn new(count: u16) -> Self {
127 Self { count, mask: None }
128 }
129
130 pub const fn with_mask(mut self, mask: u64) -> Self {
132 self.mask = Some(mask);
133 self
134 }
135}
136
137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139#[non_exhaustive]
140pub struct AudioTrackInfo {
141 pub sample_rate: u32,
143 pub channels: ChannelLayout,
145}
146
147impl AudioTrackInfo {
148 pub const fn new(sample_rate: u32, channels: ChannelLayout) -> Self {
150 Self {
151 sample_rate,
152 channels,
153 }
154 }
155}
156
157#[derive(Clone, Debug)]
165pub struct TrackInfo {
166 index: u32,
167 kind: TrackKind,
168 codec: CodecId,
169 time_base: TimeBase,
170 codec_private: Option<Arc<[u8]>>,
171 config_epoch: u32,
172 video: Option<VideoTrackInfo>,
173 audio: Option<AudioTrackInfo>,
174 codec_delay: Option<FrameDuration>,
175 seek_preroll: Option<FrameDuration>,
176 edit_delay_ticks: Option<i64>,
177 declared_packets: Option<u64>,
178 declared_duration_ticks: Option<u64>,
179}
180
181#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183#[non_exhaustive]
184pub enum TrackError {
185 ZeroDimension,
187 ZeroSampleRate,
189 NoChannels,
191}
192
193impl fmt::Display for TrackError {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 f.write_str(match self {
196 Self::ZeroDimension => "video track has a zero dimension",
197 Self::ZeroSampleRate => "audio track has a zero sample rate",
198 Self::NoChannels => "audio track has no channels",
199 })
200 }
201}
202
203impl core::error::Error for TrackError {}
204
205#[derive(Clone, Debug)]
207pub struct TrackInfoBuilder(TrackInfo);
208
209impl TrackInfo {
210 fn base(index: u32, kind: TrackKind, codec: CodecId, time_base: TimeBase) -> TrackInfo {
211 TrackInfo {
212 index,
213 kind,
214 codec,
215 time_base,
216 codec_private: None,
217 config_epoch: 0,
218 video: None,
219 audio: None,
220 codec_delay: None,
221 seek_preroll: None,
222 edit_delay_ticks: None,
223 declared_packets: None,
224 declared_duration_ticks: None,
225 }
226 }
227
228 pub fn video(
230 index: u32,
231 codec: CodecId,
232 time_base: TimeBase,
233 video: VideoTrackInfo,
234 ) -> TrackInfoBuilder {
235 let mut t = Self::base(index, TrackKind::Video, codec, time_base);
236 t.video = Some(video);
237 TrackInfoBuilder(t)
238 }
239
240 pub fn audio(
242 index: u32,
243 codec: CodecId,
244 time_base: TimeBase,
245 audio: AudioTrackInfo,
246 ) -> TrackInfoBuilder {
247 let mut t = Self::base(index, TrackKind::Audio, codec, time_base);
248 t.audio = Some(audio);
249 TrackInfoBuilder(t)
250 }
251
252 pub fn other(
254 index: u32,
255 kind: TrackKind,
256 codec: CodecId,
257 time_base: TimeBase,
258 ) -> TrackInfoBuilder {
259 TrackInfoBuilder(Self::base(index, kind, codec, time_base))
260 }
261
262 pub fn index(&self) -> u32 {
264 self.index
265 }
266
267 pub fn kind(&self) -> TrackKind {
269 self.kind
270 }
271
272 pub fn codec(&self) -> &CodecId {
274 &self.codec
275 }
276
277 pub fn time_base(&self) -> TimeBase {
279 self.time_base
280 }
281
282 pub fn codec_private(&self) -> Option<&Arc<[u8]>> {
285 self.codec_private.as_ref()
286 }
287
288 pub fn config_epoch(&self) -> u32 {
290 self.config_epoch
291 }
292
293 pub fn video_info(&self) -> Option<&VideoTrackInfo> {
295 self.video.as_ref()
296 }
297
298 pub fn audio_info(&self) -> Option<&AudioTrackInfo> {
300 self.audio.as_ref()
301 }
302
303 pub fn codec_delay(&self) -> Option<FrameDuration> {
305 self.codec_delay
306 }
307
308 pub fn seek_preroll(&self) -> Option<FrameDuration> {
311 self.seek_preroll
312 }
313
314 pub fn edit_delay_ticks(&self) -> Option<i64> {
316 self.edit_delay_ticks
317 }
318
319 pub fn declared_packets(&self) -> Option<u64> {
321 self.declared_packets
322 }
323
324 pub fn declared_duration_ticks(&self) -> Option<u64> {
326 self.declared_duration_ticks
327 }
328}
329
330impl TrackInfoBuilder {
331 pub fn with_codec_private(mut self, bytes: Arc<[u8]>) -> Self {
333 self.0.codec_private = Some(bytes);
334 self
335 }
336
337 pub fn with_config_epoch(mut self, epoch: u32) -> Self {
339 self.0.config_epoch = epoch;
340 self
341 }
342
343 pub fn with_codec_delay(mut self, d: FrameDuration) -> Self {
345 self.0.codec_delay = Some(d);
346 self
347 }
348
349 pub fn with_seek_preroll(mut self, d: FrameDuration) -> Self {
351 self.0.seek_preroll = Some(d);
352 self
353 }
354
355 pub fn with_edit_delay_ticks(mut self, ticks: i64) -> Self {
357 self.0.edit_delay_ticks = Some(ticks);
358 self
359 }
360
361 pub fn with_declared_packets(mut self, n: u64) -> Self {
363 self.0.declared_packets = Some(n);
364 self
365 }
366
367 pub fn with_declared_duration_ticks(mut self, n: u64) -> Self {
369 self.0.declared_duration_ticks = Some(n);
370 self
371 }
372
373 pub fn build(self) -> Result<TrackInfo, TrackError> {
380 let t = self.0;
381 if let Some(v) = &t.video
382 && (v.coded_width == 0 || v.coded_height == 0)
383 {
384 return Err(TrackError::ZeroDimension);
385 }
386 if let Some(a) = &t.audio {
387 if a.sample_rate == 0 {
388 return Err(TrackError::ZeroSampleRate);
389 }
390 if a.channels.count == 0 {
391 return Err(TrackError::NoChannels);
392 }
393 }
394 Ok(t)
395 }
396}