From d39c10b94611852aab318420868658d9f54de928 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 24 Jan 2024 21:06:07 -0700 Subject: [PATCH] Add seek forwards/backwards --- src/key_bind.rs | 6 ++-- src/main.rs | 23 +++++++++---- src/{ffmpeg.rs => player.rs} | 63 +++++++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 19 deletions(-) rename src/{ffmpeg.rs => player.rs} (85%) diff --git a/src/key_bind.rs b/src/key_bind.rs index ab2e3e0..422a161 100644 --- a/src/key_bind.rs +++ b/src/key_bind.rs @@ -42,10 +42,10 @@ pub fn key_binds() -> HashMap { let mut key_binds = HashMap::new(); macro_rules! bind { - ([$($modifier:ident),+ $(,)?], $key_code:ident, $action:ident) => {{ + ([$($modifier:ident),* $(,)?], $key_code:ident, $action:ident) => {{ key_binds.insert( KeyBind { - modifiers: vec![$(Modifier::$modifier),+], + modifiers: vec![$(Modifier::$modifier),*], key_code: KeyCode::$key_code, }, Action::$action, @@ -54,6 +54,8 @@ pub fn key_binds() -> HashMap { } //TODO: key bindings + bind!([], Left, SeekBackward); + bind!([], Right, SeekForward); key_binds } diff --git a/src/main.rs b/src/main.rs index fd2ea28..d3e7078 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,20 +19,21 @@ use std::{ env, path::PathBuf, process, - sync::{Arc, Mutex}, + sync::{mpsc, Arc, Mutex}, time::Instant, }; use config::{AppTheme, Config, CONFIG_VERSION}; mod config; -mod ffmpeg; - use key_bind::{key_binds, KeyBind}; mod key_bind; mod localize; +use player::{PlayerMessage, VideoFrame}; +mod player; + /// Runs application with these settings #[rustfmt::skip] fn main() -> Result<(), Box> { @@ -66,7 +67,7 @@ fn main() -> Result<(), Box> { } }; - let video_frame_lock = ffmpeg::run(path); + let (player_tx, video_frame_lock) = player::run(path); let mut settings = Settings::default(); settings = settings.theme(config.app_theme.theme()); @@ -83,7 +84,8 @@ fn main() -> Result<(), Box> { let flags = Flags { config_handler, config, - video_frame_lock + player_tx, + video_frame_lock, }; cosmic::app::run::(settings, flags)?; @@ -93,12 +95,16 @@ fn main() -> Result<(), Box> { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Action { Todo, + SeekBackward, + SeekForward, } impl Action { pub fn message(&self) -> Message { match self { Self::Todo => Message::Todo, + Self::SeekBackward => Message::Player(PlayerMessage::SeekRelative(-10.0)), + Self::SeekForward => Message::Player(PlayerMessage::SeekRelative(10.0)), } } } @@ -107,7 +113,8 @@ impl Action { pub struct Flags { config_handler: Option, config: Config, - video_frame_lock: Arc>>, + player_tx: mpsc::Sender, + video_frame_lock: Arc>>, } /// Messages that are used specifically by our [`App`]. @@ -117,6 +124,7 @@ pub enum Message { AppTheme(AppTheme), Config(Config), Key(Modifiers, KeyCode), + Player(PlayerMessage), SystemThemeModeChange(cosmic_theme::ThemeMode), Tick(Instant), ToggleContextPage(ContextPage), @@ -281,6 +289,9 @@ impl Application for App { } } } + Message::Player(player_message) => { + self.flags.player_tx.send(player_message).unwrap(); + } Message::SystemThemeModeChange(_theme_mode) => { return self.update_config(); } diff --git a/src/ffmpeg.rs b/src/player.rs similarity index 85% rename from src/ffmpeg.rs rename to src/player.rs index 2bdacbe..606fa19 100644 --- a/src/ffmpeg.rs +++ b/src/player.rs @@ -10,10 +10,11 @@ use ffmpeg::{ media::Type, software::{resampling, scaling}, util::{ - channel_layout, + channel_layout, error, format::sample, frame::{audio::Audio, video::Video}, }, + Packet, }; use std::{ collections::VecDeque, @@ -25,6 +26,11 @@ use std::{ time::{Duration, Instant}, }; +#[derive(Clone, Debug)] +pub enum PlayerMessage { + SeekRelative(f64), +} + pub struct VideoFrame(pub Video); impl VideoFrame { @@ -143,6 +149,7 @@ where fn ffmpeg_thread>( path: P, + player_rx: mpsc::Receiver, video_frame_lock: Arc>>, audio_config: cpal::SupportedStreamConfig, audio_queue_lock: Arc>>, @@ -329,13 +336,39 @@ fn ffmpeg_thread>( Ok(()) }; - for (stream, packet) in ictx.packets() { - let start = Instant::now(); - if stream.index() == video_stream_index { - video_packet_tx.send(packet).unwrap(); - } else if stream.index() == audio_stream_index { - audio_decoder.send_packet(&packet)?; - receive_and_process_decoded_audio_frames(&mut audio_decoder)?; + loop { + let mut pts_opt = None; + let mut packet = Packet::empty(); + match packet.read(&mut ictx) { + Ok(()) => { + pts_opt = packet.pts(); + if packet.stream() == video_stream_index { + video_packet_tx.send(packet).unwrap(); + } else if packet.stream() == audio_stream_index { + audio_decoder.send_packet(&packet)?; + receive_and_process_decoded_audio_frames(&mut audio_decoder)?; + } + } + Err(error::Error::Eof) => break, + Err(_err) => {} + } + + while let Ok(message) = player_rx.try_recv() { + match message { + PlayerMessage::SeekRelative(seek_seconds) => { + if let Some(pts) = pts_opt { + //TODO: use time base instead of hardcoded values + let timestamp = (pts + (seek_seconds * 1000.0) as i64) * 1000; + if seek_seconds.is_sign_negative() { + println!("backwards {} = {}", seek_seconds, timestamp); + ictx.seek(timestamp, ..timestamp)?; + } else { + println!("forwards {} = {}", seek_seconds, timestamp); + ictx.seek(timestamp, timestamp..)?; + } + } + } + } } } @@ -345,18 +378,26 @@ fn ffmpeg_thread>( Ok(()) } -pub fn run(path: PathBuf) -> Arc>> { +pub fn run(path: PathBuf) -> (mpsc::Sender, Arc>>) { ffmpeg::init().unwrap(); let audio_queue_lock = Arc::new(Mutex::new(VecDeque::new())); let audio_config = cpal(audio_queue_lock.clone()); + let (player_tx, player_rx) = mpsc::channel(); let video_frame_lock = Arc::new(Mutex::new(None)); { let video_frame_lock = video_frame_lock.clone(); thread::spawn(move || { - ffmpeg_thread(path, video_frame_lock, audio_config, audio_queue_lock).unwrap(); + ffmpeg_thread( + path, + player_rx, + video_frame_lock, + audio_config, + audio_queue_lock, + ) + .unwrap(); }); } - video_frame_lock + (player_tx, video_frame_lock) }