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:
parent
6c3fbfe0ca
commit
70914b6c3e
@ -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(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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: >k::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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user