diff --git a/Cargo.lock b/Cargo.lock index c7ab7cf..d18209c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,7 +50,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" dependencies = [ "android-properties", - "bitflags 2.4.2", + "bitflags 2.6.0", "cc", "cesu8", "jni", @@ -162,9 +162,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block" @@ -214,7 +214,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -235,7 +235,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "log", "polling", "rustix", @@ -542,7 +542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" dependencies = [ "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -557,7 +557,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libloading 0.8.1", "winapi", ] @@ -589,7 +589,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "bytemuck", "drm-ffi", "drm-fourcc", @@ -781,7 +781,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -856,7 +856,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -956,7 +956,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab9e86540b5d8402e905ad4ce7d6aa544092131ab564f3102175af176b90a053" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "futures-channel", "futures-core", "futures-executor", @@ -982,7 +982,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -1045,7 +1045,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "gpu-alloc-types", ] @@ -1055,7 +1055,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", ] [[package]] @@ -1077,7 +1077,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "gpu-descriptor-types", "hashbrown", ] @@ -1088,7 +1088,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", ] [[package]] @@ -1219,7 +1219,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "com", "libc", "libloading 0.8.1", @@ -1278,7 +1278,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55f9226618af7e8155fc8a233e4d84dc1ec44438705f972e35d5d50e62f937df" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "glam", "log", "num-traits", @@ -1321,7 +1321,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0660df444c64a13f72999c55a324f6d3ea020167bb1af8ef43fe2bff168d8c27" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "bytemuck", "cosmic-text", "half", @@ -1424,9 +1424,11 @@ dependencies = [ "glib", "gstreamer", "gstreamer-app", + "gstreamer-base", "iced", "iced_native", "iced_wgpu", + "log", "thiserror", "url", ] @@ -1437,7 +1439,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa5d3ce370c7044ddc9535c8f87057f1008434a495be6b9f46b2666c3e9638f7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "bytemuck", "futures", "glam", @@ -1677,7 +1679,7 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", "redox_syscall 0.4.1", ] @@ -1730,9 +1732,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1758,7 +1760,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "block", "core-graphics-types", "foreign-types", @@ -1796,7 +1798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8878eb410fc90853da3908aebfe61d73d26d4437ef850b70050461f939509899" dependencies = [ "bit-set", - "bitflags 2.4.2", + "bitflags 2.6.0", "codespan-reporting", "hexf-parse", "indexmap", @@ -1815,7 +1817,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "jni-sys", "log", "ndk-sys", @@ -1896,7 +1898,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -2037,7 +2039,7 @@ checksum = "e05d1c929301fee6830dafa764341118829b2535c216b0571e3821ecac5c885b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -2130,7 +2132,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -2204,9 +2206,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2357,7 +2359,7 @@ version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.13", @@ -2438,7 +2440,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -2492,7 +2494,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -2577,7 +2579,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", ] [[package]] @@ -2622,9 +2624,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -2685,7 +2687,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -2825,7 +2827,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] @@ -2988,7 +2990,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -3022,7 +3024,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3068,7 +3070,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "rustix", "wayland-backend", "wayland-scanner", @@ -3080,7 +3082,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "cursor-icon", "wayland-backend", ] @@ -3102,7 +3104,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -3114,7 +3116,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -3127,7 +3129,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -3216,7 +3218,7 @@ checksum = "6b15e451d4060ada0d99a64df44e4d590213496da7c4f245572d51071e8e30ed" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.4.2", + "bitflags 2.6.0", "cfg_aliases 0.1.1", "codespan-reporting", "indexmap", @@ -3244,7 +3246,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.4.2", + "bitflags 2.6.0", "block", "cfg_aliases 0.1.1", "core-graphics-types", @@ -3284,7 +3286,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "895fcbeb772bfb049eb80b2d6e47f6c9af235284e9703c96fc0218a42ffd5af2" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "js-sys", "web-sys", ] @@ -3566,7 +3568,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.4.2", + "bitflags 2.6.0", "bytemuck", "calloop", "cfg_aliases 0.1.1", @@ -3667,7 +3669,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "dlib", "log", "once_cell", @@ -3721,7 +3723,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.72", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e868ac2..19871fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,9 @@ iced_native = "0.10" iced_wgpu = "0.12" gstreamer = "0.22" gstreamer-app = "0.22" # appsink +gstreamer-base = "0.22" # basesrc glib = "0.19" # gobject traits and error type +log = "0.4" thiserror = "1" url = "2" # media uri diff --git a/examples/minimal.rs b/examples/minimal.rs index 30a2d1d..b4ec891 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -39,7 +39,6 @@ impl Sandbox for App { .unwrap(), ) .unwrap(), - false, ) .unwrap(); App { diff --git a/src/lib.rs b/src/lib.rs index 305e212..a25fad8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,8 @@ pub enum Error { Bool(#[from] glib::BoolError), #[error("failed to get the gstreamer bus")] Bus, + #[error("failed to get AppSink element with name='{0}' from gstreamer pipeline")] + AppSink(String), #[error("{0}")] StateChange(#[from] gst::StateChangeError), #[error("failed to cast gstreamer element")] diff --git a/src/video.rs b/src/video.rs index f14c971..ab35749 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,7 +1,9 @@ use crate::Error; use gst::prelude::*; +use gst_base::prelude::*; use gstreamer as gst; use gstreamer_app as gst_app; +use gstreamer_base as gst_base; use iced::widget::image as img; use std::cell::RefCell; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -44,7 +46,7 @@ pub(crate) struct Internal { pub(crate) id: u64, pub(crate) bus: gst::Bus, - pub(crate) source: gst::Bin, + pub(crate) source: gst::Pipeline, pub(crate) width: i32, pub(crate) height: i32, @@ -110,35 +112,45 @@ impl Drop for Video { impl Video { /// Create a new video player from a given video which loads from `uri`. - /// - /// If `live` is set then no duration is queried (as this will result in an error and is non-sensical for live streams). - /// Set `live` if the streaming source is indefinite (e.g. a live stream). - /// Note that this will cause the duration to be zero. - pub fn new(uri: &url::Url, live: bool) -> Result { + /// Note that live sourced will report the duration to be zero. + pub fn new(uri: &url::Url) -> Result { + let pipeline = format!("uridecodebin uri=\"{}\" ! videoconvert ! videoscale ! appsink name=iced_video caps=video/x-raw,format=RGBA,pixel-aspect-ratio=1/1", uri.as_str()); + Self::from_pipeline(pipeline) + } + + pub fn from_pipeline>(pipeline: S) -> Result { static NEXT_ID: AtomicU64 = AtomicU64::new(0); let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); gst::init()?; - 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::().unwrap(); - - let video_sink: gst::Element = source.property("video-sink"); - let pad = video_sink.pads().get(0).cloned().unwrap(); - let pad = pad.dynamic_cast::().unwrap(); - let bin = pad - .parent_element() - .unwrap() - .downcast::() + let pipeline = gst::parse::launch(pipeline.as_ref())? + .downcast::() + .map_err(|_| Error::Cast)?; + let mut live = false; + pipeline + .iterate_sources() + .foreach(|elem| { + if let Ok(src) = elem.downcast::() { + if src.is_live() { + live = true; + } + } + }) .unwrap(); - let app_sink = bin.by_name("app_sink").unwrap(); - let app_sink = app_sink.downcast::().unwrap(); + let app_sink_name = "iced_video"; + let app_sink = pipeline + .by_name(app_sink_name) + .and_then(|elem| elem.downcast::().ok()) + .ok_or(Error::AppSink(app_sink_name.to_string()))?; - source.set_state(gst::State::Playing)?; + let pad = app_sink.pads().first().cloned().unwrap(); + + pipeline.set_state(gst::State::Playing)?; // wait for up to 5 seconds until the decoder gets the source capabilities - source.state(gst::ClockTime::from_seconds(5)).0?; + pipeline.state(gst::ClockTime::from_seconds(5)).0?; // extract resolution and framerate // TODO(jazzfool): maybe we want to extract some other information too? @@ -152,7 +164,7 @@ impl Video { let duration = if !live { std::time::Duration::from_nanos( - source + pipeline .query_duration::() .ok_or(Error::Duration)? .nseconds(), @@ -194,8 +206,8 @@ impl Video { Ok(Video(RefCell::new(Internal { id, - bus: source.bus().unwrap(), - source, + bus: pipeline.bus().unwrap(), + source: pipeline, width, height, @@ -231,14 +243,14 @@ impl Video { /// /// This uses a linear scale, for example `0.5` is perceived as half as loud. pub fn set_volume(&mut self, volume: f64) { - self.0.borrow().source.set_property("volume", &volume); + self.0.borrow().source.set_property("volume", volume); } /// Set if the audio is muted or not, without changing the volume. pub fn set_muted(&mut self, muted: bool) { let mut inner = self.0.borrow_mut(); inner.muted = muted; - inner.source.set_property("mute", &muted); + inner.source.set_property("mute", muted); } /// Get if the audio is muted or not. @@ -292,7 +304,6 @@ impl Video { .query_position::() .map_or(0, |pos| pos.nseconds()), ) - .into() } /// Get the media duration. diff --git a/src/video_player.rs b/src/video_player.rs index 61e30c1..ba582b8 100644 --- a/src/video_player.rs +++ b/src/video_player.rs @@ -5,6 +5,7 @@ use iced::{ Element, }; use iced_wgpu::primitive::pipeline::Renderer as PrimitiveRenderer; +use log::error; use std::{marker::PhantomData, sync::atomic::Ordering}; use std::{sync::Arc, time::Duration}; @@ -16,6 +17,7 @@ where video: &'a Video, on_end_of_stream: Option, on_new_frame: Option, + on_error: Option Message + 'a>>, _phantom: PhantomData<(Theme, Renderer)>, } @@ -29,6 +31,7 @@ where video, on_end_of_stream: None, on_new_frame: None, + on_error: None, _phantom: Default::default(), } } @@ -48,6 +51,16 @@ where ..self } } + + pub fn on_error(self, on_error: F) -> Self + where + F: 'a + Fn(&glib::Error) -> Message, + { + VideoPlayer { + on_error: Some(Box::new(on_error)), + ..self + } + } } impl<'a, Message, Theme, Renderer> Widget @@ -134,7 +147,12 @@ where for msg in inner.bus.iter() { match msg.view() { - gst::MessageView::Error(err) => panic!("{:#?}", err), + gst::MessageView::Error(err) => { + error!("bus returned an error: {err}"); + if let Some(ref on_error) = self.on_error { + shell.publish(on_error(&err.error())) + }; + } gst::MessageView::Eos(_eos) => { if let Some(on_end_of_stream) = self.on_end_of_stream.clone() { shell.publish(on_end_of_stream); @@ -152,7 +170,7 @@ where // Don't run eos_pause if restart_stream is true; fixes "pausing" after restarting a stream if restart_stream { if let Err(err) = inner.restart_stream() { - eprintln!("cannot restart stream (can't seek): {:#?}", err); + error!("cannot restart stream (can't seek): {err:#?}") } } else if eos_pause { inner.is_eos = true;