add thumbnail support
This commit is contained in:
parent
76de72cdca
commit
09286fbb0f
2 changed files with 50 additions and 8 deletions
|
@ -14,6 +14,7 @@ Features:
|
||||||
- Audio support.
|
- Audio support.
|
||||||
- Programmatic control.
|
- Programmatic control.
|
||||||
- Small (around 300 lines).
|
- Small (around 300 lines).
|
||||||
|
- Capture thumbnails from a set of timestamps.
|
||||||
|
|
||||||
Limitations (hopefully to be fixed):
|
Limitations (hopefully to be fixed):
|
||||||
- GStreamer hardware acceleration not working? (leads to choppy playback in some scenarios).
|
- GStreamer hardware acceleration not working? (leads to choppy playback in some scenarios).
|
||||||
|
|
57
src/lib.rs
57
src/lib.rs
|
@ -1,13 +1,14 @@
|
||||||
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, Image, Subscription};
|
use iced::{image as img, Command, Image, Subscription};
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Position in the media.
|
/// Position in the media.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Position {
|
pub enum Position {
|
||||||
/// Position based on time.
|
/// Position based on time.
|
||||||
///
|
///
|
||||||
|
@ -58,11 +59,14 @@ pub enum Error {
|
||||||
Caps,
|
Caps,
|
||||||
#[error("failed to query media duration or position")]
|
#[error("failed to query media duration or position")]
|
||||||
Duration,
|
Duration,
|
||||||
|
#[error("failed to sync with playback")]
|
||||||
|
Sync,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum VideoPlayerMessage {
|
pub enum VideoPlayerMessage {
|
||||||
NextFrame,
|
NextFrame,
|
||||||
|
EndOfPlayback,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Video player which handles multimedia playback.
|
/// Video player which handles multimedia playback.
|
||||||
|
@ -76,6 +80,7 @@ pub struct VideoPlayer {
|
||||||
duration: std::time::Duration,
|
duration: std::time::Duration,
|
||||||
|
|
||||||
frame: Arc<Mutex<Option<img::Handle>>>,
|
frame: Arc<Mutex<Option<img::Handle>>>,
|
||||||
|
wait: mpsc::Receiver<()>,
|
||||||
paused: bool,
|
paused: bool,
|
||||||
muted: bool,
|
muted: bool,
|
||||||
}
|
}
|
||||||
|
@ -116,6 +121,8 @@ impl VideoPlayer {
|
||||||
let frame = Arc::new(Mutex::new(None));
|
let frame = Arc::new(Mutex::new(None));
|
||||||
let frame_ref = Arc::clone(&frame);
|
let frame_ref = Arc::clone(&frame);
|
||||||
|
|
||||||
|
let (notify, wait) = mpsc::channel();
|
||||||
|
|
||||||
app_sink.set_callbacks(
|
app_sink.set_callbacks(
|
||||||
gst_app::AppSinkCallbacks::builder()
|
gst_app::AppSinkCallbacks::builder()
|
||||||
.new_sample(move |sink| {
|
.new_sample(move |sink| {
|
||||||
|
@ -143,6 +150,8 @@ impl VideoPlayer {
|
||||||
map.as_slice().to_owned(),
|
map.as_slice().to_owned(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
notify.send(()).map_err(|_| gst::FlowError::Error)?;
|
||||||
|
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
})
|
})
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -192,6 +201,7 @@ impl VideoPlayer {
|
||||||
duration,
|
duration,
|
||||||
|
|
||||||
frame,
|
frame,
|
||||||
|
wait,
|
||||||
paused: false,
|
paused: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
})
|
})
|
||||||
|
@ -227,10 +237,10 @@ impl VideoPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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, paused: bool) {
|
||||||
self.paused = pause;
|
self.paused = paused;
|
||||||
self.source
|
self.source
|
||||||
.set_state(if pause {
|
.set_state(if paused {
|
||||||
gst::State::Paused
|
gst::State::Paused
|
||||||
} else {
|
} else {
|
||||||
gst::State::Playing
|
gst::State::Playing
|
||||||
|
@ -266,15 +276,46 @@ impl VideoPlayer {
|
||||||
self.duration
|
self.duration
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: VideoPlayerMessage) {
|
/// Generates a list of thumbnails based on a set of positions in the media.
|
||||||
|
///
|
||||||
|
/// Slow; only needs to be called once for each instance.
|
||||||
|
/// It's best to call this at the very start of playback, otherwise the position may shift.
|
||||||
|
pub fn thumbnails(&mut self, positions: &[Position]) -> Result<Vec<img::Handle>, Error> {
|
||||||
|
let paused = self.paused;
|
||||||
|
let pos = self.position().ok_or(Error::Duration)?;
|
||||||
|
self.set_paused(false);
|
||||||
|
let out = positions
|
||||||
|
.iter()
|
||||||
|
.map(|&pos| {
|
||||||
|
self.seek(pos)?;
|
||||||
|
self.wait.recv().map_err(|_| Error::Sync)?;
|
||||||
|
Ok(self.frame_image())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
self.set_paused(paused);
|
||||||
|
self.seek(pos)?;
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, message: VideoPlayerMessage) -> Command<VideoPlayerMessage> {
|
||||||
match message {
|
match message {
|
||||||
VideoPlayerMessage::NextFrame => {
|
VideoPlayerMessage::NextFrame => {
|
||||||
|
let mut cmd = Command::none();
|
||||||
for msg in self.bus.iter() {
|
for msg in self.bus.iter() {
|
||||||
if let gst::MessageView::Error(err) = msg.view() {
|
match msg.view() {
|
||||||
panic!("{:#?}", err);
|
gst::MessageView::Error(err) => panic!("{:#?}", err),
|
||||||
|
gst::MessageView::Eos(_eos) => {
|
||||||
|
cmd = Command::batch(vec![
|
||||||
|
cmd,
|
||||||
|
Command::perform(async {}, |_| VideoPlayerMessage::EndOfPlayback),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cmd
|
||||||
}
|
}
|
||||||
|
_ => Command::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue