PlayerWigdet: Refactor the way the duration label is updated.

This now connect's directly to gst_player::Player::connect_duration_changed
method.

The method then sends a cross-thread msg to the Action channel in the main loop that
then updates the widget.
This commit is contained in:
Jordan Petridis 2018-06-15 16:38:50 +03:00
parent 6c3fbfe0ca
commit 70914b6c3e
4 changed files with 51 additions and 46 deletions

View File

@ -5,6 +5,7 @@ use gio::{
SimpleAction, SimpleActionExt, SimpleAction, SimpleActionExt,
}; };
use glib; use glib;
use gst::ClockTime;
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::SettingsExt as GtkSettingsExt; use gtk::SettingsExt as GtkSettingsExt;
@ -17,7 +18,8 @@ use settings::{self, WindowGeometry};
use stacks::{Content, PopulatedState}; use stacks::{Content, PopulatedState};
use utils; use utils;
use widgets::appnotif::{InAppNotification, UndoState}; use widgets::appnotif::{InAppNotification, UndoState};
use widgets::{about_dialog, mark_all_notif, remove_show_notif, PlayerWidget}; use widgets::player::*;
use widgets::{about_dialog, mark_all_notif, remove_show_notif};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -54,6 +56,7 @@ pub enum Action {
RemoveShow(Arc<Podcast>), RemoveShow(Arc<Podcast>),
ErrorNotification(String), ErrorNotification(String),
InitEpisode(i32), InitEpisode(i32),
PlayerDurationChanged(ClockTime),
} }
#[derive(Debug)] #[derive(Debug)]
@ -208,6 +211,7 @@ impl App {
notif.show(&overlay); notif.show(&overlay);
}, },
Ok(Action::InitEpisode(rowid)) => player.initialize_episode(rowid).unwrap(), Ok(Action::InitEpisode(rowid)) => player.initialize_episode(rowid).unwrap(),
Ok(Action::PlayerDurationChanged(clock)) => player.on_duration_changed(clock),
Err(_) => (), Err(_) => (),
} }

View File

@ -10,8 +10,8 @@ extern crate gdk;
extern crate gdk_pixbuf; extern crate gdk_pixbuf;
extern crate gio; extern crate gio;
extern crate glib; extern crate glib;
extern crate gstreamer; extern crate gstreamer as gst;
extern crate gstreamer_player; extern crate gstreamer_player as gst_player;
extern crate gtk; extern crate gtk;
#[macro_use] #[macro_use]
@ -44,7 +44,6 @@ extern crate url;
use log::Level; use log::Level;
use gstreamer as gst;
use gtk::prelude::*; use gtk::prelude::*;
// http://gtk-rs.org/tuto/closures // http://gtk-rs.org/tuto/closures

View File

@ -3,7 +3,7 @@ pub mod appnotif;
mod empty; mod empty;
mod episode; mod episode;
mod home_view; mod home_view;
mod player; pub mod player;
mod show; mod show;
mod shows_view; mod shows_view;
@ -11,7 +11,6 @@ pub use self::aboutdialog::about_dialog;
pub use self::empty::EmptyView; pub use self::empty::EmptyView;
pub use self::episode::EpisodeWidget; pub use self::episode::EpisodeWidget;
pub use self::home_view::HomeView; pub use self::home_view::HomeView;
pub use self::player::PlayerWidget;
pub use self::show::ShowWidget; pub use self::show::ShowWidget;
pub use self::show::{mark_all_notif, remove_show_notif}; pub use self::show::{mark_all_notif, remove_show_notif};
pub use self::shows_view::ShowsView; pub use self::shows_view::ShowsView;

View File

@ -1,15 +1,18 @@
// #![allow(warnings)]
use gio::{File, FileExt}; use gio::{File, FileExt};
use glib::SignalHandlerId; use glib::SignalHandlerId;
use gst;
use gst::prelude::*; use gst::prelude::*;
use gstreamer as gst; use gst::ClockTime;
use gstreamer::ClockTime; use gst_player;
use gstreamer_player as gst_player;
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use failure::Error; use failure::Error;
// use send_cell::SendCell;
use hammond_data::{dbqueries, USER_AGENT}; use hammond_data::{dbqueries, USER_AGENT};
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery}; use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
@ -19,6 +22,7 @@ use utils::set_image_from_path;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum SeekDirection { pub enum SeekDirection {
@ -68,12 +72,13 @@ impl PlayerInfo {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct PlayerTimes { pub struct PlayerTimes {
container: gtk::Box, container: gtk::Box,
progressed: gtk::Label, progressed: gtk::Label,
duration: gtk::Label, duration: gtk::Label,
separator: gtk::Label, separator: gtk::Label,
scalebar: gtk::Scale, slider: gtk::Scale,
slider_update: Arc<SignalHandlerId>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -125,14 +130,16 @@ impl Default for PlayerWidget {
let progressed = builder.get_object("progress_time_label").unwrap(); let progressed = builder.get_object("progress_time_label").unwrap();
let duration = builder.get_object("total_duration_label").unwrap(); let duration = builder.get_object("total_duration_label").unwrap();
let separator = builder.get_object("separator").unwrap(); let separator = builder.get_object("separator").unwrap();
let scalebar: gtk::Scale = builder.get_object("seek").unwrap(); let slider: gtk::Scale = builder.get_object("seek").unwrap();
scalebar.set_range(0.0, 1.0); slider.set_range(0.0, 1.0);
let slider_update = Arc::new(Self::connect_update_slider(&slider, &player));
let timer = PlayerTimes { let timer = PlayerTimes {
container: timer_container, container: timer_container,
progressed, progressed,
duration, duration,
separator, separator,
scalebar, slider,
slider_update,
}; };
let labels = builder.get_object("info").unwrap(); let labels = builder.get_object("info").unwrap();
@ -189,8 +196,13 @@ impl PlayerWidget {
})); }));
let slider_update_signal_id = Self::connect_update_seekbar_signal(s); s.player.connect_duration_changed(clone!(sender => move |_, duration| {
Self::connect_timers(s, slider_update_signal_id); sender.send(Action::PlayerDurationChanged(duration))
.map_err(|err| error!("Error: {}", err))
.ok();
}));
Self::connect_timers(s);
} }
fn reveal(&self) { fn reveal(&self) {
@ -228,62 +240,53 @@ impl PlayerWidget {
// FIXME: Refactor to use gst_player::Player instead of raw pipeline. // FIXME: Refactor to use gst_player::Player instead of raw pipeline.
// FIXME: Refactor the labels to use some kind of Human™ time/values. // FIXME: Refactor the labels to use some kind of Human™ time/values.
// Adapted from https://github.com/sdroege/gstreamer-rs/blob/f4d57a66522183d4927b47af422e8f321182111f/tutorials/src/bin/basic-tutorial-5.rs#L131-L164 // Adapted from https://github.com/sdroege/gstreamer-rs/blob/f4d57a66522183d4927b47af422e8f321182111f/tutorials/src/bin/basic-tutorial-5.rs#L131-L164
fn connect_timers(s: &Rc<Self>, update_signal_id: SignalHandlerId) { fn connect_timers(s: &Rc<Self>) {
// Update the PlayerTimes // Update the PlayerTimes
gtk::timeout_add( gtk::timeout_add(
250, 250,
clone!(s => move || { clone!(s => move || {
// TODO: use Player::connect_duration_changed() instead
s.on_duration_changed(&update_signal_id);
// TODO: use Player::connect_position_updated() instead // TODO: use Player::connect_position_updated() instead
s.on_position_changed(&update_signal_id); s.on_position_changed();
Continue(true) Continue(true)
}), }),
); );
} }
fn connect_update_seekbar_signal(s: &Rc<Self>) -> SignalHandlerId { fn connect_update_slider(slider: &gtk::Scale, player: &gst_player::Player) -> SignalHandlerId {
s.timer slider.connect_value_changed(clone!(player => move |slider| {
.scalebar
.connect_value_changed(clone!(s => move |slider| {
let player = &s.player;
let value = slider.get_value() as u64; let value = slider.get_value() as u64;
player.seek(gst::ClockTime::from_seconds(value as u64)); player.seek(ClockTime::from_seconds(value as u64));
})) }))
} }
/// Update the duration `gtk::Label` and the max range of the `gtk::SclaeBar`. /// Update the duration `gtk::Label` and the max range of the `gtk::SclaeBar`.
fn on_duration_changed(&self, slider_update: &SignalHandlerId) { // FIXME: Refactor the labels to use some kind of Human™ time/values.
let pipeline = &self.player.get_pipeline(); pub fn on_duration_changed(&self, duration: ClockTime) {
let slider = &self.timer.scalebar; let slider = &self.timer.slider;
let seconds = duration.seconds().map(|v| v as f64).unwrap_or(0.0);
if let Some(dur) = pipeline.query_duration::<ClockTime>() { slider.block_signal(&self.timer.slider_update);
let seconds = dur / gst::SECOND;
let seconds = seconds.map(|v| v as f64).unwrap_or(0.0);
slider.block_signal(&slider_update);
slider.set_range(0.0, seconds); slider.set_range(0.0, seconds);
slider.unblock_signal(&slider_update); slider.unblock_signal(&self.timer.slider_update);
self.timer self.timer
.duration .duration
.set_text(&format!("{:.2}", seconds / 60.0)); .set_text(&format!("{:.2}", seconds / 60.0));
} }
}
/// Update the `gtk::SclaeBar` when the pipeline position is changed.. /// Update the `gtk::SclaeBar` when the pipeline position is changed..
fn on_position_changed(&self, slider_update: &SignalHandlerId) { fn on_position_changed(&self) {
let pipeline = &self.player.get_pipeline(); let pipeline = &self.player.get_pipeline();
let slider = &self.timer.scalebar; let slider = &self.timer.slider;
if let Some(pos) = pipeline.query_position::<ClockTime>() { if let Some(pos) = pipeline.query_position::<ClockTime>() {
let seconds = pos / gst::SECOND; let seconds = pos / gst::SECOND;
let seconds = seconds.map(|v| v as f64).unwrap_or(0.0); let seconds = seconds.map(|v| v as f64).unwrap_or(0.0);
slider.block_signal(&slider_update); slider.block_signal(&self.timer.slider_update);
slider.set_value(seconds); slider.set_value(seconds);
slider.unblock_signal(&slider_update); slider.unblock_signal(&self.timer.slider_update);
self.timer self.timer
.progressed .progressed