bump deps, remove num rational
This commit is contained in:
parent
935d9320f9
commit
51794fc0b1
4 changed files with 2226 additions and 1635 deletions
3777
Cargo.lock
generated
3777
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
@ -6,16 +6,14 @@ edition = "2018"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { version = "0.3", features = ["image", "tokio"] }
|
iced = { version = "0.12", features = ["image", "tokio"] }
|
||||||
iced_native = "0.3"
|
iced_native = "0.10"
|
||||||
gstreamer = "0.17"
|
gstreamer = "0.22"
|
||||||
gstreamer-app = "0.17" # appsink
|
gstreamer-app = "0.22" # appsink
|
||||||
glib = "0.14" # gobject traits and error type
|
glib = "0.19" # gobject traits and error type
|
||||||
tokio = { version = "1", features = ["time"] }
|
tokio = { version = "1", features = ["time"] }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
url = "2" # media uri
|
url = "2" # media uri
|
||||||
num-rational = "0.4" # framerates come in rationals
|
|
||||||
num-traits = "0.2" # convert rationals to floats (ToPrimitive)
|
|
||||||
|
|
||||||
[package.metadata.nix]
|
[package.metadata.nix]
|
||||||
systems = ["x86_64-linux"]
|
systems = ["x86_64-linux"]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use iced::{
|
use iced::{
|
||||||
button, executor, Application, Button, Column, Command, Element, Row, Subscription, Text,
|
executor,
|
||||||
|
widget::{Button, Column, Row, Text},
|
||||||
|
Application, Command, Element, Subscription, Theme,
|
||||||
};
|
};
|
||||||
use iced_video_player::{VideoPlayer, VideoPlayerMessage};
|
use iced_video_player::{VideoPlayer, VideoPlayerMessage};
|
||||||
|
|
||||||
|
@ -16,14 +18,13 @@ enum Message {
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
video: VideoPlayer,
|
video: VideoPlayer,
|
||||||
pause_btn: button::State,
|
|
||||||
loop_btn: button::State,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for App {
|
impl Application for App {
|
||||||
type Executor = executor::Default;
|
type Executor = executor::Default;
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
type Theme = Theme;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
fn new(_flags: ()) -> (Self, Command<Message>) {
|
||||||
let video = VideoPlayer::new(
|
let video = VideoPlayer::new(
|
||||||
|
@ -39,21 +40,14 @@ impl Application for App {
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(
|
(App { video }, Command::none())
|
||||||
App {
|
|
||||||
video,
|
|
||||||
pause_btn: Default::default(),
|
|
||||||
loop_btn: Default::default(),
|
|
||||||
},
|
|
||||||
Command::none(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn title(&self) -> String {
|
||||||
String::from("Video Player")
|
String::from("Video Player")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message, _: &mut iced::Clipboard) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::TogglePause => {
|
Message::TogglePause => {
|
||||||
self.video.set_paused(!self.video.paused());
|
self.video.set_paused(!self.video.paused());
|
||||||
|
@ -73,28 +67,26 @@ impl Application for App {
|
||||||
self.video.subscription().map(Message::VideoPlayerMessage)
|
self.video.subscription().map(Message::VideoPlayerMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&mut self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(self.video.frame_view())
|
.push(self.video.frame_view())
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(5)
|
.spacing(5)
|
||||||
.push(
|
.push(
|
||||||
Button::new(
|
Button::new(Text::new(if self.video.paused() {
|
||||||
&mut self.pause_btn,
|
"Play"
|
||||||
Text::new(if self.video.paused() { "Play" } else { "Pause" }),
|
} else {
|
||||||
)
|
"Pause"
|
||||||
|
}))
|
||||||
.on_press(Message::TogglePause),
|
.on_press(Message::TogglePause),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Button::new(
|
Button::new(Text::new(if self.video.looping() {
|
||||||
&mut self.loop_btn,
|
"Disable Loop"
|
||||||
Text::new(if self.video.looping() {
|
} else {
|
||||||
"Disable Loop"
|
"Enable Loop"
|
||||||
} else {
|
}))
|
||||||
"Enable Loop"
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.on_press(Message::ToggleLoop),
|
.on_press(Message::ToggleLoop),
|
||||||
)
|
)
|
||||||
.push(Text::new(format!(
|
.push(Text::new(format!(
|
||||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -1,8 +1,10 @@
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gstreamer as gst;
|
use gstreamer as gst;
|
||||||
use gstreamer_app as gst_app;
|
use gstreamer_app as gst_app;
|
||||||
use iced::{image as img, Command, Image, Subscription};
|
use iced::{
|
||||||
use num_traits::ToPrimitive;
|
widget::{image as img, Image},
|
||||||
|
Command, Subscription,
|
||||||
|
};
|
||||||
use std::convert::identity;
|
use std::convert::identity;
|
||||||
use std::future;
|
use std::future;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
@ -24,7 +26,7 @@ impl From<Position> for gst::GenericFormattedValue {
|
||||||
fn from(pos: Position) -> Self {
|
fn from(pos: Position) -> Self {
|
||||||
match pos {
|
match pos {
|
||||||
Position::Time(t) => gst::ClockTime::from_nseconds(t.as_nanos() as _).into(),
|
Position::Time(t) => gst::ClockTime::from_nseconds(t.as_nanos() as _).into(),
|
||||||
Position::Frame(f) => gst::format::Default(f).into(),
|
Position::Frame(f) => gst::format::Default::from_u64(f).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,10 +115,10 @@ impl VideoPlayer {
|
||||||
pub fn new(uri: &url::Url, live: bool) -> Result<Self, Error> {
|
pub fn new(uri: &url::Url, live: bool) -> Result<Self, Error> {
|
||||||
gst::init()?;
|
gst::init()?;
|
||||||
|
|
||||||
let source = gst::parse_launch(&format!("playbin uri=\"{}\" video-sink=\"videoconvert ! videoscale ! appsink name=app_sink caps=video/x-raw,format=BGRA,pixel-aspect-ratio=1/1\"", uri.as_str()))?;
|
let source = gst::parse::launch(&format!("playbin uri=\"{}\" video-sink=\"videoconvert ! videoscale ! appsink name=app_sink caps=video/x-raw,format=RGBA,pixel-aspect-ratio=1/1\"", uri.as_str()))?;
|
||||||
let source = source.downcast::<gst::Bin>().unwrap();
|
let source = source.downcast::<gst::Bin>().unwrap();
|
||||||
|
|
||||||
let video_sink: gst::Element = source.property("video-sink").unwrap().get().unwrap();
|
let video_sink: gst::Element = source.property("video-sink");
|
||||||
let pad = video_sink.pads().get(0).cloned().unwrap();
|
let pad = video_sink.pads().get(0).cloned().unwrap();
|
||||||
let pad = pad.dynamic_cast::<gst::GhostPad>().unwrap();
|
let pad = pad.dynamic_cast::<gst::GhostPad>().unwrap();
|
||||||
let bin = pad
|
let bin = pad
|
||||||
|
@ -193,11 +195,7 @@ impl VideoPlayer {
|
||||||
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
framerate: num_rational::Rational32::new(
|
framerate: framerate.numer() as f64 / framerate.denom() as f64,
|
||||||
*framerate.numer() as _,
|
|
||||||
*framerate.denom() as _,
|
|
||||||
)
|
|
||||||
.to_f64().unwrap(/* if the video framerate is bad then it would've been implicitly caught far earlier */),
|
|
||||||
duration,
|
duration,
|
||||||
|
|
||||||
frame,
|
frame,
|
||||||
|
@ -227,13 +225,13 @@ impl VideoPlayer {
|
||||||
///
|
///
|
||||||
/// This uses a linear scale, for example `0.5` is perceived as half as loud.
|
/// This uses a linear scale, for example `0.5` is perceived as half as loud.
|
||||||
pub fn set_volume(&mut self, volume: f64) {
|
pub fn set_volume(&mut self, volume: f64) {
|
||||||
self.source.set_property("volume", &volume).unwrap(/* this property is guaranteed to exist */);
|
self.source.set_property("volume", &volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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, muted: bool) {
|
pub fn set_muted(&mut self, muted: bool) {
|
||||||
self.muted = muted;
|
self.muted = muted;
|
||||||
self.source.set_property("mute", &muted).unwrap();
|
self.source.set_property("mute", &muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get if the audio is muted or not.
|
/// Get if the audio is muted or not.
|
||||||
|
@ -286,8 +284,10 @@ impl VideoPlayer {
|
||||||
/// Jumps to a specific position 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> {
|
pub fn seek(&mut self, position: impl Into<Position>) -> Result<(), Error> {
|
||||||
self.source
|
self.source.seek_simple(
|
||||||
.seek_simple(gst::SeekFlags::FLUSH, position.into())?;
|
gst::SeekFlags::FLUSH,
|
||||||
|
gst::GenericFormattedValue::from(position.into()),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ impl VideoPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap the output of `frame_image` in an `Image` widget.
|
/// Wrap the output of `frame_image` in an `Image` widget.
|
||||||
pub fn frame_view(&mut self) -> Image {
|
pub fn frame_view(&self) -> Image<img::Handle> {
|
||||||
Image::new(self.frame_image())
|
Image::new(self.frame_image())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue