1use std::collections::VecDeque;
8use std::path::Path;
9use std::time::Duration;
10
11use crate::demux::Demuxer;
12use crate::decode::Decoder;
13use crate::error::{Error, Result};
14use crate::frame::Frame;
15use crate::plugin::Plugin;
16
17const QUEUE: usize = 30;
19
20pub struct VideoSource {
31 pub persistent_id: String,
33
34 demuxer: Box<dyn Demuxer>,
36
37 decoder: Box<dyn Decoder>,
39
40 video_track_id: u32,
42
43 frame_duration: Option<Duration>,
45
46 frame_queue: VecDeque<Frame>,
48
49 plugins: Vec<Box<dyn Plugin>>,
51
52 eof_reached: bool,
54
55 discard_before: Option<Duration>,
58
59 origin: Option<Duration>,
62
63 cache: Option<Cached>,
65}
66
67struct Cached {
69 cache: FrameCache,
70 next: u64,
72 decoder_in_step: bool,
75}
76
77impl VideoSource {
78 #[cfg(feature = "demux")]
85 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
86 let path = path.as_ref();
87 Self::from_demuxer(crate::open_media(path)?, path)
88 }
89
90 #[cfg(feature = "demux")]
98 pub fn from_reader(reader: Box<dyn crate::ReadSeek>, name: &str) -> Result<Self> {
99 Self::from_demuxer(crate::open_media_reader(reader, name)?, Path::new(name))
100 }
101
102 #[cfg(feature = "demux")]
103 fn from_demuxer(demuxer: Box<dyn crate::Demuxer>, path: &Path) -> Result<Self> {
104 let media_info = demuxer.info();
106 let video_track = media_info
107 .video()
108 .ok_or_else(|| crate::Error::Unsupported {
109 what: format!("{}: no video track in it", path.display()),
110 })?;
111 let video_track_id = video_track.id;
112 let frame_duration = video_track
113 .frame_rate
114 .map(|rate| rate.frame_duration())
115 .filter(|duration| !duration.is_zero());
116
117 let config = crate::decode::DecoderConfig::from_track(video_track)?;
122 let decoder = crate::decode::open(&config)?;
123
124 let persistent_id = format!("{:x}", {
125 use std::collections::hash_map::DefaultHasher;
126 use std::hash::{Hash, Hasher};
127 let mut hasher = DefaultHasher::new();
128 path.to_string_lossy().hash(&mut hasher);
129 hasher.finish()
130 });
131
132 Ok(VideoSource {
133 persistent_id,
134 demuxer,
135 decoder,
136 video_track_id,
137 frame_duration,
138 frame_queue: VecDeque::with_capacity(QUEUE),
139 plugins: Vec::new(),
140 eof_reached: false,
141 discard_before: None,
142 origin: None,
143 cache: None,
144 })
145 }
146
147 pub fn add_plugin(&mut self, plugin: Box<dyn Plugin>) {
149 self.plugins.push(plugin);
150 }
151
152 pub fn persistent_id(&self) -> &str {
154 &self.persistent_id
155 }
156
157 pub fn info(&self) -> &crate::media::MediaInfo {
159 self.demuxer.info()
160 }
161
162 pub fn decoder(&self) -> &dyn Decoder {
165 self.decoder.as_ref()
166 }
167
168 pub fn frame_duration(&self) -> Option<Duration> {
170 self.frame_duration
171 }
172
173 pub fn set_cache(&mut self, cache: FrameCache) -> Result<()> {
182 if self.frame_duration.is_none() {
183 return Err(Error::unsupported(
184 "this file states no frame rate, and a frame cache is addressed by frame number",
185 ));
186 }
187
188 self.origin()?;
189
190 self.cache = Some(Cached {
191 cache,
192 next: 0,
193 decoder_in_step: false,
194 });
195
196 self.rewind()
197 }
198
199 pub fn advance(&mut self, _delta: Duration) -> Result<()> {
206 while self.frame_queue.len() < QUEUE && !self.eof_reached {
207 if self.serve_from_cache()? {
208 continue;
209 }
210
211 match self.demuxer.next_packet()? {
213 Some(packet) => {
214 if packet.track_id == self.video_track_id {
216 if let Some(frame) = self.decoder.decode(&packet)? {
217 self.take(frame)?;
218 }
219 }
220 }
221 None => {
222 for frame in self.decoder.flush()? {
224 self.take(frame)?;
225 }
226
227 let in_step = self
233 .cache
234 .as_ref()
235 .is_none_or(|cached| cached.decoder_in_step);
236
237 self.eof_reached = in_step && !self.cache_has_next();
238 }
239 }
240 }
241
242 Ok(())
243 }
244
245 pub fn origin(&mut self) -> Result<Duration> {
257 if let Some(origin) = self.origin {
258 return Ok(origin);
259 }
260
261 self.frame_queue.clear();
262 self.reposition(Duration::ZERO)?;
263
264 let origin = loop {
267 match self.demuxer.next_packet()? {
268 Some(packet) if packet.track_id == self.video_track_id => {
269 if let Some(frame) = self.decoder.decode(&packet)? {
270 break frame.pts();
271 }
272 }
273 Some(_) => {}
274 None => {
275 break self
276 .decoder
277 .flush()?
278 .first()
279 .map_or(Duration::ZERO, Frame::pts)
280 }
281 }
282 };
283
284 self.origin = Some(origin);
285 self.reposition(Duration::ZERO)?;
286
287 Ok(origin)
288 }
289
290 pub fn seek(&mut self, position: Duration) -> Result<()> {
301 self.frame_queue.clear();
302 self.reposition(position)?;
303
304 if let (Some(cached), Some(duration)) = (self.cache.as_mut(), self.frame_duration) {
305 let origin = self.origin.unwrap_or_default();
306 cached.next = frame_number(position.saturating_sub(origin), duration);
307 cached.decoder_in_step = true;
308 }
309
310 Ok(())
311 }
312
313 fn reposition(&mut self, position: Duration) -> Result<()> {
317 self.demuxer.seek(position)?;
318 self.decoder.reset()?;
319 self.eof_reached = false;
320 let slack = self.frame_duration.unwrap_or_default() / 2;
323 self.discard_before = Some(position.saturating_sub(slack)).filter(|at| !at.is_zero());
324
325 Ok(())
326 }
327
328 pub fn rewind(&mut self) -> Result<()> {
331 self.seek(Duration::ZERO)
332 }
333
334 pub fn current_frame(&self) -> Option<&Frame> {
336 self.frame_queue.front()
337 }
338
339 pub fn pop_frame(&mut self) -> Option<Frame> {
341 self.frame_queue.pop_front()
342 }
343
344 pub fn queued_frames(&self) -> usize {
346 self.frame_queue.len()
347 }
348
349 pub fn is_finished(&self) -> bool {
351 self.eof_reached && self.frame_queue.is_empty()
352 }
353
354 fn take(&mut self, frame: Frame) -> Result<()> {
357 if let Some(before) = self.discard_before {
358 if frame.pts() < before {
359 return Ok(());
360 }
361
362 self.discard_before = None;
363 }
364
365 if let (Some(cached), Some(duration)) = (self.cache.as_mut(), self.frame_duration) {
366 let origin = self.origin.unwrap_or_default();
367 let number = frame_number(frame.pts().saturating_sub(origin), duration);
368
369 if number < cached.next {
371 return Ok(());
372 }
373
374 if cached.cache.get(number).is_some() {
377 cached.decoder_in_step = false;
378 return Ok(());
379 }
380
381 cached.next = number + 1;
382 }
383
384 let mut frame = frame;
385 for plugin in &mut self.plugins {
386 frame = plugin.process(frame)?;
387 }
388
389 self.frame_queue.push_back(frame);
390 Ok(())
391 }
392
393 fn serve_from_cache(&mut self) -> Result<bool> {
397 let (Some(cached), Some(duration)) = (self.cache.as_mut(), self.frame_duration) else {
398 return Ok(false);
399 };
400 let origin = self.origin.unwrap_or_default();
401
402 if let Some(frame) = cached.cache.get(cached.next) {
403 let frame = frame.with_pts(origin + frame_time(cached.next, duration));
406 cached.next += 1;
407 cached.decoder_in_step = false;
408
409 let mut frame = frame;
410 for plugin in &mut self.plugins {
411 frame = plugin.process(frame)?;
412 }
413
414 self.frame_queue.push_back(frame);
415 return Ok(true);
416 }
417
418 if !cached.decoder_in_step {
419 cached.decoder_in_step = true;
420 let position = origin + frame_time(cached.next, duration);
421
422 self.reposition(position)?;
423 }
424
425 Ok(false)
426 }
427
428 fn cache_has_next(&self) -> bool {
430 self.cache
431 .as_ref()
432 .is_some_and(|cached| cached.cache.get(cached.next).is_some())
433 }
434}
435
436fn frame_number(position: Duration, frame_duration: Duration) -> u64 {
438 let frame = frame_duration.as_nanos().max(1);
439 ((position.as_nanos() + frame / 2) / frame) as u64
442}
443
444fn frame_time(number: u64, frame_duration: Duration) -> Duration {
446 let nanos = u128::from(number) * frame_duration.as_nanos();
447 Duration::from_nanos(nanos.min(u128::from(u64::MAX)) as u64)
448}
449
450#[derive(Clone, Debug)]
458pub struct FrameCache {
459 ranges: Vec<(u64, u64)>,
460 offsets: Vec<usize>,
462 frames: Vec<Frame>,
463}
464
465impl FrameCache {
466 pub fn new(mut ranges: Vec<(u64, u64)>, frames: Vec<Frame>) -> Result<Self> {
474 ranges.sort_unstable();
475
476 let mut offsets = Vec::with_capacity(ranges.len());
477 let mut total = 0_usize;
478 let mut previous_end = 0_u64;
479
480 for (index, &(start, end)) in ranges.iter().enumerate() {
481 if start >= end {
482 return Err(Error::unsupported(format!(
483 "frame range {start}..{end} holds no frames"
484 )));
485 }
486
487 if index > 0 && start < previous_end {
488 return Err(Error::unsupported(format!(
489 "frame range {start}..{end} overlaps the one before it, which ends at {previous_end}"
490 )));
491 }
492
493 offsets.push(total);
494 total += usize::try_from(end - start).map_err(|_| {
495 Error::unsupported(format!("frame range {start}..{end} is too long to hold"))
496 })?;
497 previous_end = end;
498 }
499
500 if total != frames.len() {
501 return Err(Error::unsupported(format!(
502 "the ranges cover {total} frames and {} were given",
503 frames.len()
504 )));
505 }
506
507 Ok(FrameCache {
508 ranges,
509 offsets,
510 frames,
511 })
512 }
513
514 pub fn get(&self, number: u64) -> Option<&Frame> {
516 let index = self
517 .ranges
518 .partition_point(|&(start, _)| start <= number)
519 .checked_sub(1)?;
520 let (start, end) = self.ranges[index];
521
522 if number >= end {
523 return None;
524 }
525
526 self.frames
527 .get(self.offsets[index] + (number - start) as usize)
528 }
529
530 pub fn ranges(&self) -> &[(u64, u64)] {
532 &self.ranges
533 }
534
535 pub fn len(&self) -> usize {
537 self.frames.len()
538 }
539
540 pub fn is_empty(&self) -> bool {
542 self.frames.is_empty()
543 }
544
545 pub fn byte_len(&self) -> usize {
547 self.frames.iter().map(Frame::byte_len).sum()
548 }
549}
550
551#[cfg(feature = "demux")]
564pub fn cache_frames(path: impl AsRef<Path>, ranges: &[(u64, u64)]) -> Result<Vec<Frame>> {
565 let mut source = VideoSource::from_file(path)?;
566 let duration = source.frame_duration().ok_or_else(|| {
567 Error::unsupported("this file states no frame rate, so its frames cannot be numbered")
568 })?;
569
570 let origin = source.origin()?;
571
572 let mut sorted = ranges.to_vec();
573 sorted.sort_unstable();
574
575 let mut frames = Vec::new();
576
577 for (start, end) in sorted {
578 source.seek(origin + frame_time(start, duration))?;
579 let mut number = start;
580
581 while number < end {
582 source.advance(Duration::ZERO)?;
583
584 let Some(frame) = source.pop_frame() else {
585 return Err(Error::Decode {
586 encoding: source.decoder().encoding(),
587 reason: format!(
588 "frame range {start}..{end} runs past the end of the film, at frame {number}"
589 ),
590 });
591 };
592
593 frames.push(frame);
594 number += 1;
595 }
596 }
597
598 Ok(frames)
599}
600
601#[cfg(test)]
602mod tests {
603 use super::*;
604 use crate::color::ColorSpace;
605 use crate::frame::PixelFormat;
606
607 fn frame(tag: u8) -> Frame {
608 Frame::packed(
609 2,
610 2,
611 PixelFormat::Rgba8,
612 ColorSpace::srgb(),
613 Duration::ZERO,
614 vec![tag; 16],
615 )
616 .unwrap()
617 }
618
619 fn tag(frame: &Frame) -> u8 {
620 frame.data()[0]
621 }
622
623 #[test]
624 fn a_cache_finds_each_frame_by_number() {
625 let cache = FrameCache::new(
626 vec![(10, 12), (0, 2)],
627 vec![frame(0), frame(1), frame(10), frame(11)],
628 )
629 .unwrap();
630
631 assert_eq!(cache.get(0).map(tag), Some(0));
632 assert_eq!(cache.get(1).map(tag), Some(1));
633 assert!(cache.get(2).is_none());
634 assert!(cache.get(9).is_none());
635 assert_eq!(cache.get(10).map(tag), Some(10));
636 assert_eq!(cache.get(11).map(tag), Some(11));
637 assert!(cache.get(12).is_none());
638 assert_eq!(cache.len(), 4);
639 }
640
641 #[test]
643 fn a_cache_that_does_not_add_up_is_refused() {
644 assert!(FrameCache::new(vec![(0, 3)], vec![frame(0), frame(1)]).is_err());
645 assert!(FrameCache::new(vec![(5, 5)], vec![]).is_err());
646 assert!(FrameCache::new(vec![(0, 4), (2, 6)], vec![frame(0); 8]).is_err());
647 }
648
649 #[test]
650 fn frame_numbers_round_rather_than_floor() {
651 let frame = Duration::from_nanos(41_708_333);
652
653 assert_eq!(frame_number(Duration::ZERO, frame), 0);
654 assert_eq!(frame_number(frame * 24 - Duration::from_nanos(3), frame), 24);
655 assert_eq!(frame_number(frame * 24 + Duration::from_nanos(3), frame), 24);
656 }
657}