minor enhancements
This commit is contained in:
parent
828fde9976
commit
76de72cdca
2 changed files with 67 additions and 30 deletions
|
@ -1,4 +1,6 @@
|
||||||
use iced::{button, executor, Application, Button, Column, Command, Element, Subscription, Text};
|
use iced::{
|
||||||
|
button, executor, Application, Button, Column, Command, Element, Row, Subscription, Text,
|
||||||
|
};
|
||||||
use iced_video_player::{VideoPlayer, VideoPlayerMessage};
|
use iced_video_player::{VideoPlayer, VideoPlayerMessage};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -65,8 +67,20 @@ impl Application for App {
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(self.video.frame_view())
|
.push(self.video.frame_view())
|
||||||
.push(
|
.push(
|
||||||
Button::new(&mut self.pause_btn, Text::new("Toggle Pause"))
|
Row::new()
|
||||||
.on_press(Message::TogglePause),
|
.spacing(5)
|
||||||
|
.push(
|
||||||
|
Button::new(
|
||||||
|
&mut self.pause_btn,
|
||||||
|
Text::new(if self.video.paused() { "Play" } else { "Pause" }),
|
||||||
|
)
|
||||||
|
.on_press(Message::TogglePause),
|
||||||
|
)
|
||||||
|
.push(Text::new(format!(
|
||||||
|
"{:#?}s / {:#?}s",
|
||||||
|
self.video.position().unwrap().as_secs(),
|
||||||
|
self.video.duration().as_secs()
|
||||||
|
))),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
77
src/lib.rs
77
src/lib.rs
|
@ -7,6 +7,37 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Position in the media.
|
||||||
|
pub enum Position {
|
||||||
|
/// Position based on time.
|
||||||
|
///
|
||||||
|
/// Not the most accurate format for videos.
|
||||||
|
Time(std::time::Duration),
|
||||||
|
/// Position based on nth frame.
|
||||||
|
Frame(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Position> for gst::GenericFormattedValue {
|
||||||
|
fn from(pos: Position) -> Self {
|
||||||
|
match pos {
|
||||||
|
Position::Time(t) => gst::ClockTime::from_nseconds(t.as_nanos() as _).into(),
|
||||||
|
Position::Frame(f) => gst::format::Default(Some(f)).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::time::Duration> for Position {
|
||||||
|
fn from(t: std::time::Duration) -> Self {
|
||||||
|
Position::Time(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for Position {
|
||||||
|
fn from(f: u64) -> Self {
|
||||||
|
Position::Frame(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
@ -45,7 +76,8 @@ pub struct VideoPlayer {
|
||||||
duration: std::time::Duration,
|
duration: std::time::Duration,
|
||||||
|
|
||||||
frame: Arc<Mutex<Option<img::Handle>>>,
|
frame: Arc<Mutex<Option<img::Handle>>>,
|
||||||
pause: bool,
|
paused: bool,
|
||||||
|
muted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VideoPlayer {
|
impl Drop for VideoPlayer {
|
||||||
|
@ -118,8 +150,8 @@ impl VideoPlayer {
|
||||||
|
|
||||||
source.set_state(gst::State::Playing)?;
|
source.set_state(gst::State::Playing)?;
|
||||||
|
|
||||||
// wait for up to 1 second until the decoder gets the source capabilities
|
// wait for up to 5 seconds until the decoder gets the source capabilities
|
||||||
source.get_state(gst::ClockTime::from_seconds(1)).0?;
|
source.get_state(gst::ClockTime::from_seconds(5)).0?;
|
||||||
|
|
||||||
// extract resolution and framerate
|
// extract resolution and framerate
|
||||||
// TODO(jazzfool): maybe we want to extract some other information too?
|
// TODO(jazzfool): maybe we want to extract some other information too?
|
||||||
|
@ -160,7 +192,8 @@ impl VideoPlayer {
|
||||||
duration,
|
duration,
|
||||||
|
|
||||||
frame,
|
frame,
|
||||||
pause: false,
|
paused: false,
|
||||||
|
muted: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,24 +216,19 @@ impl VideoPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set if the audio is muted or not, without changing the volume.
|
/// Set if the audio is muted or not, without changing the volume.
|
||||||
pub fn set_muted(&mut self, mute: bool) {
|
pub fn set_muted(&mut self, muted: bool) {
|
||||||
self.source.set_property("mute", &mute).unwrap();
|
self.muted = muted;
|
||||||
|
self.source.set_property("mute", &muted).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get if the audio is muted or not.
|
/// Get if the audio is muted or not.
|
||||||
pub fn muted(&self) -> bool {
|
pub fn muted(&self) -> bool {
|
||||||
// guaranteed to be a boolean
|
self.muted
|
||||||
self.source
|
|
||||||
.get_property("mute")
|
|
||||||
.unwrap()
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set if the media is paused or not.
|
/// Set if the media is paused or not.
|
||||||
pub fn set_paused(&mut self, pause: bool) {
|
pub fn set_paused(&mut self, pause: bool) {
|
||||||
self.pause = pause;
|
self.paused = pause;
|
||||||
self.source
|
self.source
|
||||||
.set_state(if pause {
|
.set_state(if pause {
|
||||||
gst::State::Paused
|
gst::State::Paused
|
||||||
|
@ -212,24 +240,18 @@ impl VideoPlayer {
|
||||||
|
|
||||||
/// Get if the media is paused or not.
|
/// Get if the media is paused or not.
|
||||||
pub fn paused(&self) -> bool {
|
pub fn paused(&self) -> bool {
|
||||||
self.pause
|
self.paused
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Jumps to a specific time in the media.
|
/// Jumps to a specific position in the media.
|
||||||
/// The seeking is not perfectly accurate.
|
/// The seeking is not perfectly accurate.
|
||||||
///
|
pub fn seek(&mut self, position: impl Into<Position>) -> Result<(), Error> {
|
||||||
/// The position is converted to nanoseconds, so any duration with values more significant that nanoseconds is truncated.
|
self.source
|
||||||
pub fn seek(&mut self, position: std::time::Duration) -> Result<(), Error> {
|
.seek_simple(gst::SeekFlags::FLUSH, position.into())?;
|
||||||
self.source.seek_simple(
|
|
||||||
gst::SeekFlags::empty(),
|
|
||||||
gst::GenericFormattedValue::Time(gst::ClockTime::from_nseconds(
|
|
||||||
position.as_nanos() as _
|
|
||||||
)),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current playback position.
|
/// Get the current playback position in time.
|
||||||
pub fn position(&self) -> Option<std::time::Duration> {
|
pub fn position(&self) -> Option<std::time::Duration> {
|
||||||
std::time::Duration::from_nanos(
|
std::time::Duration::from_nanos(
|
||||||
self.source
|
self.source
|
||||||
|
@ -257,7 +279,7 @@ impl VideoPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscription(&self) -> Subscription<VideoPlayerMessage> {
|
pub fn subscription(&self) -> Subscription<VideoPlayerMessage> {
|
||||||
if !self.pause {
|
if !self.paused {
|
||||||
time::every(Duration::from_secs_f64(0.5 / self.framerate))
|
time::every(Duration::from_secs_f64(0.5 / self.framerate))
|
||||||
.map(|_| VideoPlayerMessage::NextFrame)
|
.map(|_| VideoPlayerMessage::NextFrame)
|
||||||
} else {
|
} else {
|
||||||
|
@ -280,6 +302,7 @@ impl VideoPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// until iced 0.2 is released, which has this built-in
|
||||||
mod time {
|
mod time {
|
||||||
use iced::futures;
|
use iced::futures;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue