1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::*;

/// A struct which decorates a [`Collector`] implementation with a target frame
/// duration, in order to control the frame rate of the replay processing. If a
/// frame is processed and the next desired frame time is before the
/// `target_frame_duration`, [`TimeAdvance::Time`] is used to ensure the next
/// frame will only be processed after the `target_frame_duration` has passed.
pub struct FrameRateDecorator<'a, C> {
    collector: &'a mut C,
    target_frame_duration: f32,
}

impl<'a, C> FrameRateDecorator<'a, C> {
    /// Constructs a new [`FrameRateDecorator`] instance with a given target
    /// frame duration and underlying [`Collector`] reference.
    ///
    /// # Arguments
    ///
    /// * `target_frame_duration`: The target duration for each frame in seconds.
    /// * `collector`: A mutable reference to the underlying [`Collector`] instance.
    pub fn new(target_frame_duration: f32, collector: &'a mut C) -> Self {
        Self {
            collector,
            target_frame_duration,
        }
    }

    /// Constructs a new [`FrameRateDecorator`] instance with a desired frames
    /// per second (fps) rate and underlying [`Collector`] reference. The target
    /// frame duration is computed as the reciprocal of the fps value.
    ///
    /// # Arguments
    ///
    /// * `fps`: The desired frame rate in frames per second.
    /// * `collector`: A mutable reference to the underlying [`Collector`] instance.
    pub fn new_from_fps(fps: f32, collector: &'a mut C) -> Self {
        Self::new(1.0 / fps, collector)
    }
}

impl<'a, C: Collector> Collector for FrameRateDecorator<'a, C> {
    /// Processes the given frame, delegating to the underlying [`Collector`]'s
    /// [`process_frame`](Collector::process_frame) method, then adjusts the
    /// returned [`TimeAdvance`] based on the target frame duration.
    ///
    /// If the original [`TimeAdvance`] was [`TimeAdvance::NextFrame`] or a
    /// [`TimeAdvance::Time`] less than the target frame duration, this method
    /// returns a [`TimeAdvance::Time`] equal to the current time plus the
    /// target frame duration.
    ///
    /// If the original [`TimeAdvance`] was a [`TimeAdvance::Time`] greater than
    /// the target frame duration, this method returns the original
    /// [`TimeAdvance::Time`].
    fn process_frame(
        &mut self,
        processor: &ReplayProcessor,
        frame: &boxcars::Frame,
        frame_number: usize,
        current_time: f32,
    ) -> SubtrActorResult<TimeAdvance> {
        let original_advance =
            self.collector
                .process_frame(processor, frame, frame_number, current_time)?;

        let next_target = current_time + self.target_frame_duration;

        let next_target_time = match original_advance {
            TimeAdvance::NextFrame => TimeAdvance::Time(next_target),
            TimeAdvance::Time(t) => {
                TimeAdvance::Time(f32::max(t, current_time + self.target_frame_duration))
            }
        };

        Ok(next_target_time)
    }
}