Add seek forwards/backwards
This commit is contained in:
parent
48ba7e7278
commit
d39c10b946
3 changed files with 73 additions and 19 deletions
|
|
@ -42,10 +42,10 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
|
||||||
let mut key_binds = HashMap::new();
|
let mut key_binds = HashMap::new();
|
||||||
|
|
||||||
macro_rules! bind {
|
macro_rules! bind {
|
||||||
([$($modifier:ident),+ $(,)?], $key_code:ident, $action:ident) => {{
|
([$($modifier:ident),* $(,)?], $key_code:ident, $action:ident) => {{
|
||||||
key_binds.insert(
|
key_binds.insert(
|
||||||
KeyBind {
|
KeyBind {
|
||||||
modifiers: vec![$(Modifier::$modifier),+],
|
modifiers: vec![$(Modifier::$modifier),*],
|
||||||
key_code: KeyCode::$key_code,
|
key_code: KeyCode::$key_code,
|
||||||
},
|
},
|
||||||
Action::$action,
|
Action::$action,
|
||||||
|
|
@ -54,6 +54,8 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: key bindings
|
//TODO: key bindings
|
||||||
|
bind!([], Left, SeekBackward);
|
||||||
|
bind!([], Right, SeekForward);
|
||||||
|
|
||||||
key_binds
|
key_binds
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
src/main.rs
23
src/main.rs
|
|
@ -19,20 +19,21 @@ use std::{
|
||||||
env,
|
env,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process,
|
process,
|
||||||
sync::{Arc, Mutex},
|
sync::{mpsc, Arc, Mutex},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use config::{AppTheme, Config, CONFIG_VERSION};
|
use config::{AppTheme, Config, CONFIG_VERSION};
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
mod ffmpeg;
|
|
||||||
|
|
||||||
use key_bind::{key_binds, KeyBind};
|
use key_bind::{key_binds, KeyBind};
|
||||||
mod key_bind;
|
mod key_bind;
|
||||||
|
|
||||||
mod localize;
|
mod localize;
|
||||||
|
|
||||||
|
use player::{PlayerMessage, VideoFrame};
|
||||||
|
mod player;
|
||||||
|
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
@ -66,7 +67,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let video_frame_lock = ffmpeg::run(path);
|
let (player_tx, video_frame_lock) = player::run(path);
|
||||||
|
|
||||||
let mut settings = Settings::default();
|
let mut settings = Settings::default();
|
||||||
settings = settings.theme(config.app_theme.theme());
|
settings = settings.theme(config.app_theme.theme());
|
||||||
|
|
@ -83,7 +84,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let flags = Flags {
|
let flags = Flags {
|
||||||
config_handler,
|
config_handler,
|
||||||
config,
|
config,
|
||||||
video_frame_lock
|
player_tx,
|
||||||
|
video_frame_lock,
|
||||||
};
|
};
|
||||||
cosmic::app::run::<App>(settings, flags)?;
|
cosmic::app::run::<App>(settings, flags)?;
|
||||||
|
|
||||||
|
|
@ -93,12 +95,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Todo,
|
Todo,
|
||||||
|
SeekBackward,
|
||||||
|
SeekForward,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Action {
|
impl Action {
|
||||||
pub fn message(&self) -> Message {
|
pub fn message(&self) -> Message {
|
||||||
match self {
|
match self {
|
||||||
Self::Todo => Message::Todo,
|
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 {
|
pub struct Flags {
|
||||||
config_handler: Option<cosmic_config::Config>,
|
config_handler: Option<cosmic_config::Config>,
|
||||||
config: Config,
|
config: Config,
|
||||||
video_frame_lock: Arc<Mutex<Option<ffmpeg::VideoFrame>>>,
|
player_tx: mpsc::Sender<PlayerMessage>,
|
||||||
|
video_frame_lock: Arc<Mutex<Option<VideoFrame>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages that are used specifically by our [`App`].
|
/// Messages that are used specifically by our [`App`].
|
||||||
|
|
@ -117,6 +124,7 @@ pub enum Message {
|
||||||
AppTheme(AppTheme),
|
AppTheme(AppTheme),
|
||||||
Config(Config),
|
Config(Config),
|
||||||
Key(Modifiers, KeyCode),
|
Key(Modifiers, KeyCode),
|
||||||
|
Player(PlayerMessage),
|
||||||
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
||||||
Tick(Instant),
|
Tick(Instant),
|
||||||
ToggleContextPage(ContextPage),
|
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) => {
|
Message::SystemThemeModeChange(_theme_mode) => {
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,11 @@ use ffmpeg::{
|
||||||
media::Type,
|
media::Type,
|
||||||
software::{resampling, scaling},
|
software::{resampling, scaling},
|
||||||
util::{
|
util::{
|
||||||
channel_layout,
|
channel_layout, error,
|
||||||
format::sample,
|
format::sample,
|
||||||
frame::{audio::Audio, video::Video},
|
frame::{audio::Audio, video::Video},
|
||||||
},
|
},
|
||||||
|
Packet,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
|
@ -25,6 +26,11 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum PlayerMessage {
|
||||||
|
SeekRelative(f64),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct VideoFrame(pub Video);
|
pub struct VideoFrame(pub Video);
|
||||||
|
|
||||||
impl VideoFrame {
|
impl VideoFrame {
|
||||||
|
|
@ -143,6 +149,7 @@ where
|
||||||
|
|
||||||
fn ffmpeg_thread<P: AsRef<Path>>(
|
fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
path: P,
|
path: P,
|
||||||
|
player_rx: mpsc::Receiver<PlayerMessage>,
|
||||||
video_frame_lock: Arc<Mutex<Option<VideoFrame>>>,
|
video_frame_lock: Arc<Mutex<Option<VideoFrame>>>,
|
||||||
audio_config: cpal::SupportedStreamConfig,
|
audio_config: cpal::SupportedStreamConfig,
|
||||||
audio_queue_lock: Arc<Mutex<VecDeque<f32>>>,
|
audio_queue_lock: Arc<Mutex<VecDeque<f32>>>,
|
||||||
|
|
@ -329,13 +336,39 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (stream, packet) in ictx.packets() {
|
loop {
|
||||||
let start = Instant::now();
|
let mut pts_opt = None;
|
||||||
if stream.index() == video_stream_index {
|
let mut packet = Packet::empty();
|
||||||
video_packet_tx.send(packet).unwrap();
|
match packet.read(&mut ictx) {
|
||||||
} else if stream.index() == audio_stream_index {
|
Ok(()) => {
|
||||||
audio_decoder.send_packet(&packet)?;
|
pts_opt = packet.pts();
|
||||||
receive_and_process_decoded_audio_frames(&mut audio_decoder)?;
|
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<P: AsRef<Path>>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(path: PathBuf) -> Arc<Mutex<Option<VideoFrame>>> {
|
pub fn run(path: PathBuf) -> (mpsc::Sender<PlayerMessage>, Arc<Mutex<Option<VideoFrame>>>) {
|
||||||
ffmpeg::init().unwrap();
|
ffmpeg::init().unwrap();
|
||||||
|
|
||||||
let audio_queue_lock = Arc::new(Mutex::new(VecDeque::new()));
|
let audio_queue_lock = Arc::new(Mutex::new(VecDeque::new()));
|
||||||
let audio_config = cpal(audio_queue_lock.clone());
|
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 = Arc::new(Mutex::new(None));
|
||||||
{
|
{
|
||||||
let video_frame_lock = video_frame_lock.clone();
|
let video_frame_lock = video_frame_lock.clone();
|
||||||
thread::spawn(move || {
|
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)
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue