Merge pull request #183 from Dewb/feat/thumb-orientation
Correctly orient videos in thumbnail mode
This commit is contained in:
commit
fa1637fe51
3 changed files with 87 additions and 68 deletions
71
src/main.rs
71
src/main.rs
|
|
@ -18,7 +18,7 @@ use cosmic::{
|
|||
};
|
||||
use iced_video_player::{
|
||||
gst::{self, prelude::*},
|
||||
gst_app, gst_pbutils, Video, VideoPlayer,
|
||||
gst_pbutils, Video, VideoPlayer,
|
||||
};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
|
|
@ -46,6 +46,7 @@ mod menu;
|
|||
mod mpris;
|
||||
mod project;
|
||||
mod thumbnail;
|
||||
mod video;
|
||||
|
||||
static CONTROLS_TIMEOUT: Duration = Duration::new(2, 0);
|
||||
|
||||
|
|
@ -360,71 +361,9 @@ impl App {
|
|||
self.flags.config_state.recent_files.truncate(10);
|
||||
self.save_config_state();
|
||||
|
||||
//TODO: this code came from iced_video_player::Video::new and has been modified to stop the pipeline on error
|
||||
//TODO: remove unwraps and enable playback of files with only audio.
|
||||
let video = {
|
||||
gst::init().unwrap();
|
||||
|
||||
let pipeline = format!(
|
||||
"playbin uri=\"{}\" video-sink=\"videoscale ! videoconvert ! videoflip method=automatic ! appsink name=iced_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1\"",
|
||||
url.as_str()
|
||||
);
|
||||
let pipeline = gst::parse::launch(pipeline.as_ref())
|
||||
.unwrap()
|
||||
.downcast::<gst::Pipeline>()
|
||||
.map_err(|_| iced_video_player::Error::Cast)
|
||||
.unwrap();
|
||||
pipeline.connect("element-setup", false, |vals| {
|
||||
let Ok(elem) = vals[1].get::<gst::Element>() else {
|
||||
return None;
|
||||
};
|
||||
if let Some(factory) = elem.factory() {
|
||||
if factory.name() == "souphttpsrc" {
|
||||
elem.set_property("user-agent", "Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0");
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
let video_sink: gst::Element = pipeline.property("video-sink");
|
||||
let pad = video_sink.pads().first().cloned().unwrap();
|
||||
let pad = pad.dynamic_cast::<gst::GhostPad>().unwrap();
|
||||
let bin = pad
|
||||
.parent_element()
|
||||
.unwrap()
|
||||
.downcast::<gst::Bin>()
|
||||
.unwrap();
|
||||
let video_sink = bin.by_name("iced_video").unwrap();
|
||||
let video_sink = video_sink.downcast::<gst_app::AppSink>().unwrap();
|
||||
|
||||
match Video::from_gst_pipeline(pipeline.clone(), video_sink, None) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
log::warn!("failed to open {}: {err}", url);
|
||||
// Handle codecs required before the file can play
|
||||
let mut commands = Vec::new();
|
||||
while let Some(msg) = pipeline
|
||||
.bus()
|
||||
.unwrap()
|
||||
.pop_filtered(&[gst::MessageType::Element])
|
||||
{
|
||||
match msg.view() {
|
||||
gst::MessageView::Element(element) => {
|
||||
if gst_pbutils::MissingPluginMessage::is(&element) {
|
||||
commands.push(Command::perform(
|
||||
async { message::app(Message::MissingPlugin(msg)) },
|
||||
|x| x,
|
||||
));
|
||||
// Do one codec install at a time
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
pipeline.set_state(gst::State::Null).unwrap();
|
||||
return Command::batch(commands);
|
||||
}
|
||||
}
|
||||
let video = match video::new_video(&url) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => return err
|
||||
};
|
||||
|
||||
self.duration = video.duration().as_secs_f64();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use cosmic::iced_core::image::Data;
|
||||
use iced_video_player::{Position, Video};
|
||||
use iced_video_player::{Position};
|
||||
use image::{DynamicImage, ImageFormat, RgbaImage};
|
||||
use std::{error::Error, num::NonZero, path::Path, time::Duration};
|
||||
use url::Url;
|
||||
|
||||
use super::video;
|
||||
|
||||
pub fn main(
|
||||
input: &Url,
|
||||
output: &Path,
|
||||
|
|
@ -11,7 +13,11 @@ pub fn main(
|
|||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut image = {
|
||||
let thumbnails = {
|
||||
let mut video = Video::new(input)?;
|
||||
let mut video = match video::new_video(input) {
|
||||
Ok(ok) => ok,
|
||||
Err(_err) => return Err(Into::into(format!("missing required plugin")))
|
||||
};
|
||||
|
||||
let duration = video.duration();
|
||||
//TODO: how best to decide time?
|
||||
let position = if duration.as_secs_f64() < 20.0 {
|
||||
|
|
|
|||
74
src/video.rs
Normal file
74
src/video.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
use iced_video_player::{
|
||||
gst::{self, prelude::*},
|
||||
gst_app, gst_pbutils, Video,
|
||||
};
|
||||
|
||||
use cosmic::app::{Command, message};
|
||||
|
||||
pub fn new_video(url: &url::Url) -> Result<Video, cosmic::Command<cosmic::app::Message<super::Message>>> {
|
||||
//TODO: this code came from iced_video_player::Video::new and has been modified to stop the pipeline on error
|
||||
//TODO: remove unwraps and enable playback of files with only audio.
|
||||
gst::init().unwrap();
|
||||
|
||||
let pipeline = format!(
|
||||
"playbin uri=\"{}\" video-sink=\"videoscale ! videoconvert ! videoflip method=automatic ! appsink name=iced_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1\"",
|
||||
url.as_str()
|
||||
);
|
||||
let pipeline = gst::parse::launch(pipeline.as_ref())
|
||||
.unwrap()
|
||||
.downcast::<gst::Pipeline>()
|
||||
.map_err(|_| iced_video_player::Error::Cast)
|
||||
.unwrap();
|
||||
pipeline.connect("element-setup", false, |vals| {
|
||||
let Ok(elem) = vals[1].get::<gst::Element>() else {
|
||||
return None;
|
||||
};
|
||||
if let Some(factory) = elem.factory() {
|
||||
if factory.name() == "souphttpsrc" {
|
||||
elem.set_property("user-agent", "Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0");
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
let video_sink: gst::Element = pipeline.property("video-sink");
|
||||
let pad = video_sink.pads().first().cloned().unwrap();
|
||||
let pad = pad.dynamic_cast::<gst::GhostPad>().unwrap();
|
||||
let bin = pad
|
||||
.parent_element()
|
||||
.unwrap()
|
||||
.downcast::<gst::Bin>()
|
||||
.unwrap();
|
||||
let video_sink = bin.by_name("iced_video").unwrap();
|
||||
let video_sink = video_sink.downcast::<gst_app::AppSink>().unwrap();
|
||||
|
||||
match Video::from_gst_pipeline(pipeline.clone(), video_sink, None) {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(err) => {
|
||||
log::warn!("failed to open {}: {err}", url);
|
||||
// Handle codecs required before the file can play
|
||||
let mut commands = Vec::new();
|
||||
while let Some(msg) = pipeline
|
||||
.bus()
|
||||
.unwrap()
|
||||
.pop_filtered(&[gst::MessageType::Element])
|
||||
{
|
||||
match msg.view() {
|
||||
gst::MessageView::Element(element) => {
|
||||
if gst_pbutils::MissingPluginMessage::is(&element) {
|
||||
commands.push(Command::perform(
|
||||
async { message::app(super::Message::MissingPlugin(msg)) },
|
||||
|x| x,
|
||||
));
|
||||
// Do one codec install at a time
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
pipeline.set_state(gst::State::Null).unwrap();
|
||||
Err(Command::batch(commands))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue