use log::{debug, info};
use std::sync::mpsc;
use std::time::Instant;
use crate::cache::{Cache, CacheMessage};
use crate::frame::Frame;
const FPS_PRESETS: &[f32] = &[1.0, 2.0, 4.0, 8.0, 12.0, 24.0, 30.0, 60.0, 120.0, 240.0];
pub struct Player {
pub cache: Cache,
pub is_playing: bool,
pub fps_base: f32, pub fps_play: f32, pub loop_enabled: bool,
pub play_direction: f32, last_frame_time: Option<Instant>,
pub selected_seq_idx: Option<usize>, }
impl Player {
pub fn new() -> (Self, mpsc::Receiver<CacheMessage>) {
Self::new_with_config(0.75, None)
}
pub fn new_with_config(
max_mem_fraction: f64,
workers: Option<usize>,
) -> (Self, mpsc::Receiver<CacheMessage>) {
info!("Player initialized with new architecture");
let (cache, ui_rx) = Cache::new(max_mem_fraction, workers);
let player = Self {
cache,
is_playing: false,
fps_base: 24.0,
fps_play: 24.0,
loop_enabled: true,
play_direction: 1.0,
last_frame_time: None,
selected_seq_idx: None,
};
(player, ui_rx)
}
pub fn get_current_frame(&mut self) -> Option<&Frame> {
let frame_idx = self.cache.frame();
self.cache.get_frame(frame_idx)
}
pub fn update(&mut self) {
if !self.is_playing || self.cache.total_frames() == 0 {
return;
}
if self.fps_play < self.fps_base {
self.fps_play = self.fps_base;
}
let now = Instant::now();
if let Some(last_time) = self.last_frame_time {
let elapsed = now.duration_since(last_time).as_secs_f32();
let frame_duration = 1.0 / self.fps_play;
if elapsed >= frame_duration {
self.advance_frame();
self.last_frame_time = Some(now);
}
} else {
self.last_frame_time = Some(now);
}
}
fn advance_frame(&mut self) {
let total_frames = self.cache.total_frames();
if total_frames == 0 {
return;
}
let current = self.cache.frame();
let (play_start, play_end) = self.cache.get_play_range();
if self.play_direction > 0.0 {
let next = current + 1;
if next > play_end {
if self.loop_enabled {
debug!("Frame loop: {} -> {}", current, play_start);
self.cache.set_frame(play_start);
} else {
debug!("Reached play range end, stopping");
self.cache.set_frame(play_end);
self.is_playing = false;
}
} else {
self.cache.set_frame(next);
}
} else {
if current <= play_start {
if self.loop_enabled {
debug!("Frame loop: {} -> {}", current, play_end);
self.cache.set_frame(play_end);
} else {
debug!("Reached play range start, stopping");
self.is_playing = false;
}
} else {
self.cache.set_frame(current - 1);
}
}
}
pub fn toggle_play_pause(&mut self) {
self.is_playing = !self.is_playing;
if self.is_playing {
debug!("Playback started at frame {}", self.cache.frame());
self.last_frame_time = Some(Instant::now());
self.cache.signal_preload();
} else {
debug!("Playback paused at frame {}", self.cache.frame());
self.last_frame_time = None;
self.fps_play = self.fps_base;
}
}
pub fn stop(&mut self) {
if self.is_playing {
self.is_playing = false;
debug!("Playback stopped at frame {}", self.cache.frame());
self.last_frame_time = None;
self.fps_play = self.fps_base;
}
}
pub fn to_start(&mut self) {
debug!("Rewinding to frame 0");
self.cache.set_frame(0);
self.last_frame_time = None;
self.cache.signal_preload();
}
pub fn to_end(&mut self) {
let (_, global_end) = self.cache.range();
debug!("Skipping to end: frame {}", global_end);
self.cache.set_frame(global_end);
self.last_frame_time = None;
self.cache.signal_preload();
}
pub fn set_frame(&mut self, frame: usize) {
let (_, global_end) = self.cache.range();
let clamped = frame.min(global_end);
self.cache.set_frame(clamped);
self.last_frame_time = None;
self.cache.signal_preload();
}
#[inline]
pub fn current_frame(&self) -> usize {
self.cache.frame()
}
pub fn total_frames(&self) -> usize {
self.cache.total_frames()
}
pub fn jog_forward(&mut self) {
if !self.is_playing {
self.play_direction = 1.0;
self.is_playing = true;
self.fps_play = self.fps_base; self.last_frame_time = Some(Instant::now());
} else if self.play_direction < 0.0 {
self.play_direction = 1.0; self.fps_play = self.fps_base; } else {
self.increase_fps_play(); }
}
pub fn jog_backward(&mut self) {
if !self.is_playing {
self.play_direction = -1.0;
self.is_playing = true;
self.fps_play = self.fps_base; self.last_frame_time = Some(Instant::now());
} else if self.play_direction > 0.0 {
self.play_direction = -1.0; self.fps_play = self.fps_base; } else {
self.increase_fps_play(); }
}
pub fn increase_fps_base(&mut self) {
if let Some(idx) = FPS_PRESETS.iter().position(|&f| f >= self.fps_base) {
if idx + 1 < FPS_PRESETS.len() {
self.fps_base = FPS_PRESETS[idx + 1];
if !self.is_playing {
self.fps_play = self.fps_base;
}
debug!("Base FPS increased to {}", self.fps_base);
}
}
}
pub fn decrease_fps_base(&mut self) {
if let Some(idx) = FPS_PRESETS.iter().rposition(|&f| f <= self.fps_base) {
if idx > 0 {
self.fps_base = FPS_PRESETS[idx - 1];
if !self.is_playing {
self.fps_play = self.fps_base;
}
debug!("Base FPS decreased to {}", self.fps_base);
}
}
}
fn increase_fps_play(&mut self) {
if let Some(idx) = FPS_PRESETS.iter().position(|&f| f >= self.fps_play) {
if idx + 1 < FPS_PRESETS.len() {
self.fps_play = FPS_PRESETS[idx + 1];
debug!("Play FPS increased to {}", self.fps_play);
}
}
}
pub fn decrease_fps_play(&mut self) {
if self.is_playing {
if let Some(idx) = FPS_PRESETS.iter().rposition(|&f| f <= self.fps_play) {
if idx > 0 {
self.fps_play = FPS_PRESETS[idx - 1];
debug!("Play FPS decreased to {}", self.fps_play);
}
}
}
}
pub fn jump_next_sequence(&mut self) {
let sequences = self.cache.sequences();
if sequences.is_empty() {
return;
}
let (global_start, global_end) = self.cache.range();
let current_frame = self.cache.frame();
if current_frame >= global_end {
if self.loop_enabled {
self.cache.set_frame(global_start);
debug!("Looped from end to start: frame {}", global_start);
}
} else if let Some((seq_idx, _local_frame)) = self.cache.current_sequence() {
if seq_idx + 1 < sequences.len() {
if let Some(next_start) = self.cache.local_to_global(seq_idx + 1, 0) {
self.cache.set_frame(next_start);
debug!("Jumped to next sequence start: frame {}", next_start);
}
} else {
self.cache.set_frame(global_end);
debug!("Jumped to end: frame {}", global_end);
}
}
self.last_frame_time = None;
self.cache.signal_preload();
}
pub fn jump_prev_sequence(&mut self) {
let sequences = self.cache.sequences();
if sequences.is_empty() {
return;
}
let (_, global_end) = self.cache.range();
let current_frame = self.cache.frame();
if let Some((seq_idx, local_frame)) = self.cache.current_sequence() {
if local_frame == 0 {
if seq_idx > 0 {
if let Some(prev_start) = self.cache.local_to_global(seq_idx - 1, 0) {
self.cache.set_frame(prev_start);
debug!("Jumped to previous sequence start: frame {}", prev_start);
}
} else if self.loop_enabled {
self.cache.set_frame(global_end);
debug!("Looped to end: frame {}", global_end);
}
} else {
if let Some(cur_start) = self.cache.local_to_global(seq_idx, 0) {
self.cache.set_frame(cur_start);
debug!("Jumped to current sequence start: frame {}", cur_start);
}
}
} else if current_frame >= global_end && self.loop_enabled {
self.cache.set_frame(global_end);
debug!("At end, positioned at frame {}", global_end);
}
self.last_frame_time = None;
self.cache.signal_preload();
}
pub fn reset_settings(&mut self) {
self.fps_base = 24.0;
self.fps_play = 24.0;
self.loop_enabled = true;
info!("Player settings reset");
}
}
impl Default for Player {
fn default() -> Self {
let (player, _rx) = Self::new();
player
}
}