video layout based on iced image
This commit is contained in:
parent
c44141c717
commit
5f53b18796
5 changed files with 89 additions and 41 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1784,7 +1784,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_video_player"
|
name = "iced_video_player"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glib",
|
"glib",
|
||||||
"gstreamer",
|
"gstreamer",
|
||||||
|
|
|
@ -6,7 +6,7 @@ repository = "https://github.com/jazzfool/iced_video_player"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["gui", "iced", "video"]
|
keywords = ["gui", "iced", "video"]
|
||||||
categories = ["gui", "multimedia"]
|
categories = ["gui", "multimedia"]
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
authors = ["jazzfool"]
|
authors = ["jazzfool"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use iced::{
|
use iced::{
|
||||||
widget::{Button, Column, Row, Slider, Text},
|
widget::{Button, Column, Container, Row, Slider, Space, Text},
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
use iced_video_player::{Video, VideoPlayer};
|
use iced_video_player::{Video, VideoPlayer};
|
||||||
|
@ -82,21 +82,42 @@ impl App {
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(
|
.push(
|
||||||
|
Container::new(
|
||||||
VideoPlayer::new(&self.video)
|
VideoPlayer::new(&self.video)
|
||||||
|
.width(iced::Length::Shrink)
|
||||||
|
.height(iced::Length::Shrink)
|
||||||
|
.content_fit(iced::ContentFit::Contain)
|
||||||
.on_end_of_stream(Message::EndOfStream)
|
.on_end_of_stream(Message::EndOfStream)
|
||||||
.on_new_frame(Message::NewFrame),
|
.on_new_frame(Message::NewFrame),
|
||||||
)
|
)
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.height(iced::Length::Fill)
|
||||||
|
.center(iced::Length::Fill),
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
Container::new(
|
||||||
|
Slider::new(
|
||||||
|
0.0..=self.video.duration().as_secs_f64(),
|
||||||
|
self.position,
|
||||||
|
Message::Seek,
|
||||||
|
)
|
||||||
|
.step(0.1)
|
||||||
|
.on_release(Message::SeekRelease),
|
||||||
|
)
|
||||||
|
.padding(iced::Padding::new(5.0).left(10.0).right(10.0)),
|
||||||
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(5)
|
.spacing(5)
|
||||||
.align_y(iced::alignment::Vertical::Center)
|
.align_y(iced::alignment::Vertical::Center)
|
||||||
.padding(iced::Padding::new(5.0))
|
.padding(iced::Padding::new(10.0).top(0.0))
|
||||||
.push(
|
.push(
|
||||||
Button::new(Text::new(if self.video.paused() {
|
Button::new(Text::new(if self.video.paused() {
|
||||||
"Play"
|
"Play"
|
||||||
} else {
|
} else {
|
||||||
"Pause"
|
"Pause"
|
||||||
}))
|
}))
|
||||||
|
.width(80.0)
|
||||||
.on_press(Message::TogglePause),
|
.on_press(Message::TogglePause),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
|
@ -105,21 +126,19 @@ impl App {
|
||||||
} else {
|
} else {
|
||||||
"Enable Loop"
|
"Enable Loop"
|
||||||
}))
|
}))
|
||||||
|
.width(120.0)
|
||||||
.on_press(Message::ToggleLoop),
|
.on_press(Message::ToggleLoop),
|
||||||
)
|
)
|
||||||
.push(Text::new(format!(
|
|
||||||
"{:#?}s / {:#?}s",
|
|
||||||
self.position as u64,
|
|
||||||
self.video.duration().as_secs()
|
|
||||||
)))
|
|
||||||
.push(
|
.push(
|
||||||
Slider::new(
|
Text::new(format!(
|
||||||
0.0..=self.video.duration().as_secs_f64(),
|
"{}:{:02}s / {}:{:02}s",
|
||||||
self.position,
|
self.position as u64 / 60,
|
||||||
Message::Seek,
|
self.position as u64 % 60,
|
||||||
)
|
self.video.duration().as_secs() / 60,
|
||||||
.step(0.1)
|
self.video.duration().as_secs() % 60,
|
||||||
.on_release(Message::SeekRelease),
|
))
|
||||||
|
.width(iced::Length::Fill)
|
||||||
|
.align_x(iced::alignment::Horizontal::Right),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
|
|
13
src/video.rs
13
src/video.rs
|
@ -258,13 +258,11 @@ impl Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the size/resolution of the video as `(width, height)`.
|
/// Get the size/resolution of the video as `(width, height)`.
|
||||||
#[inline(always)]
|
|
||||||
pub fn size(&self) -> (i32, i32) {
|
pub fn size(&self) -> (i32, i32) {
|
||||||
(self.0.borrow().width, self.0.borrow().height)
|
(self.0.borrow().width, self.0.borrow().height)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the framerate of the video as frames per second.
|
/// Get the framerate of the video as frames per second.
|
||||||
#[inline(always)]
|
|
||||||
pub fn framerate(&self) -> f64 {
|
pub fn framerate(&self) -> f64 {
|
||||||
self.0.borrow().framerate
|
self.0.borrow().framerate
|
||||||
}
|
}
|
||||||
|
@ -285,25 +283,21 @@ impl Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get if the audio is muted or not.
|
/// Get if the audio is muted or not.
|
||||||
#[inline(always)]
|
|
||||||
pub fn muted(&self) -> bool {
|
pub fn muted(&self) -> bool {
|
||||||
self.0.borrow().muted
|
self.0.borrow().muted
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get if the stream ended or not.
|
/// Get if the stream ended or not.
|
||||||
#[inline(always)]
|
|
||||||
pub fn eos(&self) -> bool {
|
pub fn eos(&self) -> bool {
|
||||||
self.0.borrow().is_eos
|
self.0.borrow().is_eos
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get if the media will loop or not.
|
/// Get if the media will loop or not.
|
||||||
#[inline(always)]
|
|
||||||
pub fn looping(&self) -> bool {
|
pub fn looping(&self) -> bool {
|
||||||
self.0.borrow().looping
|
self.0.borrow().looping
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set if the media will loop or not.
|
/// Set if the media will loop or not.
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_looping(&mut self, looping: bool) {
|
pub fn set_looping(&mut self, looping: bool) {
|
||||||
self.0.get_mut().looping = looping;
|
self.0.get_mut().looping = looping;
|
||||||
}
|
}
|
||||||
|
@ -315,7 +309,6 @@ impl Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get if the media is paused or not.
|
/// Get if the media is paused or not.
|
||||||
#[inline(always)]
|
|
||||||
pub fn paused(&self) -> bool {
|
pub fn paused(&self) -> bool {
|
||||||
self.0.borrow().paused
|
self.0.borrow().paused
|
||||||
}
|
}
|
||||||
|
@ -350,7 +343,6 @@ impl Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the media duration.
|
/// Get the media duration.
|
||||||
#[inline(always)]
|
|
||||||
pub fn duration(&self) -> std::time::Duration {
|
pub fn duration(&self) -> std::time::Duration {
|
||||||
self.0.borrow().duration
|
self.0.borrow().duration
|
||||||
}
|
}
|
||||||
|
@ -360,6 +352,11 @@ impl Video {
|
||||||
self.0.borrow_mut().restart_stream()
|
self.0.borrow_mut().restart_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the underlying GStreamer pipeline.
|
||||||
|
pub fn pipeline(&self) -> gst::Pipeline {
|
||||||
|
self.0.borrow().source.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a list of thumbnails based on a set of positions in the media.
|
/// Generates a list of thumbnails based on a set of positions in the media.
|
||||||
///
|
///
|
||||||
/// Slow; only needs to be called once for each instance.
|
/// Slow; only needs to be called once for each instance.
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{pipeline::VideoPrimitive, video::Video};
|
||||||
use gstreamer as gst;
|
use gstreamer as gst;
|
||||||
use iced::{
|
use iced::{
|
||||||
advanced::{self, graphics::core::event::Status, layout, widget, Widget},
|
advanced::{self, graphics::core::event::Status, layout, widget, Widget},
|
||||||
Element,
|
Element, Size,
|
||||||
};
|
};
|
||||||
use iced_wgpu::primitive::Renderer as PrimitiveRenderer;
|
use iced_wgpu::primitive::Renderer as PrimitiveRenderer;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
@ -15,6 +15,9 @@ where
|
||||||
Renderer: PrimitiveRenderer,
|
Renderer: PrimitiveRenderer,
|
||||||
{
|
{
|
||||||
video: &'a Video,
|
video: &'a Video,
|
||||||
|
content_fit: iced::ContentFit,
|
||||||
|
width: iced::Length,
|
||||||
|
height: iced::Length,
|
||||||
on_end_of_stream: Option<Message>,
|
on_end_of_stream: Option<Message>,
|
||||||
on_new_frame: Option<Message>,
|
on_new_frame: Option<Message>,
|
||||||
on_error: Option<Box<dyn Fn(&glib::Error) -> Message + 'a>>,
|
on_error: Option<Box<dyn Fn(&glib::Error) -> Message + 'a>>,
|
||||||
|
@ -29,6 +32,9 @@ where
|
||||||
pub fn new(video: &'a Video) -> Self {
|
pub fn new(video: &'a Video) -> Self {
|
||||||
VideoPlayer {
|
VideoPlayer {
|
||||||
video,
|
video,
|
||||||
|
content_fit: iced::ContentFit::default(),
|
||||||
|
width: iced::Length::Shrink,
|
||||||
|
height: iced::Length::Shrink,
|
||||||
on_end_of_stream: None,
|
on_end_of_stream: None,
|
||||||
on_new_frame: None,
|
on_new_frame: None,
|
||||||
on_error: None,
|
on_error: None,
|
||||||
|
@ -36,6 +42,30 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the width of the `VideoPlayer` boundaries.
|
||||||
|
pub fn width(self, width: impl Into<iced::Length>) -> Self {
|
||||||
|
VideoPlayer {
|
||||||
|
width: width.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of the `VideoPlayer` boundaries.
|
||||||
|
pub fn height(self, height: impl Into<iced::Length>) -> Self {
|
||||||
|
VideoPlayer {
|
||||||
|
height: height.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `ContentFit` of the `VideoPlayer`.
|
||||||
|
pub fn content_fit(self, content_fit: iced::ContentFit) -> Self {
|
||||||
|
VideoPlayer {
|
||||||
|
content_fit: content_fit,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Message to send when the video reaches the end of stream (i.e., the video ends).
|
/// Message to send when the video reaches the end of stream (i.e., the video ends).
|
||||||
pub fn on_end_of_stream(self, on_end_of_stream: Message) -> Self {
|
pub fn on_end_of_stream(self, on_end_of_stream: Message) -> Self {
|
||||||
VideoPlayer {
|
VideoPlayer {
|
||||||
|
@ -82,22 +112,24 @@ where
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
let (width, height) = self.video.size();
|
let (video_width, video_height) = self.video.size();
|
||||||
let (width, height) = (width as f32, height as f32);
|
|
||||||
let size = limits.resolve(
|
|
||||||
iced::Length::Fill,
|
|
||||||
iced::Length::Fill,
|
|
||||||
iced::Size::new(width, height),
|
|
||||||
);
|
|
||||||
|
|
||||||
// fixed aspect ratio + never exceed available size
|
// based on `Image::layout`
|
||||||
let size = if (size.width / size.height) > (width / height) {
|
let image_size = iced::Size::new(video_width as f32, video_height as f32);
|
||||||
iced::Size::new(size.height * (width / height), size.height)
|
let raw_size = limits.resolve(self.width, self.height, image_size);
|
||||||
} else {
|
let full_size = self.content_fit.fit(image_size, raw_size);
|
||||||
iced::Size::new(size.width, size.width * (height / width))
|
let final_size = iced::Size {
|
||||||
|
width: match self.width {
|
||||||
|
iced::Length::Shrink => f32::min(raw_size.width, full_size.width),
|
||||||
|
_ => raw_size.width,
|
||||||
|
},
|
||||||
|
height: match self.height {
|
||||||
|
iced::Length::Shrink => f32::min(raw_size.height, full_size.height),
|
||||||
|
_ => raw_size.height,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
layout::Node::new(size)
|
layout::Node::new(final_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue