From b84483057b8b01ee7093b315f122bdae6180596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Cerveau?= Date: Fri, 28 Jan 2022 15:24:30 +0100 Subject: [PATCH] pipeline: add gtk4paintablesink Add a way to draw whenever possible the video render into the preview box in UI --- Cargo.lock | 84 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 +++-- src/app.rs | 17 +++++++-- src/common.rs | 1 - src/gps/element.rs | 38 ++++++++++++++++++-- src/gps/pad.rs | 1 - src/gps/pipeline.rs | 27 +++++++++++++-- 7 files changed, 166 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74743e2..cddc797 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -476,6 +476,30 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gst-plugin-gtk4" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f70501fa85dfdbeebecea35d747791351d04266f5c2c4aa23014d9fe74132290" +dependencies = [ + "fragile", + "gst-plugin-version-helper", + "gstreamer", + "gstreamer-base", + "gstreamer-video", + "gtk4", + "once_cell", +] + +[[package]] +name = "gst-plugin-version-helper" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6a4dd1cb931cc6b49af354a68f21b3aee46b5b07370215d942f3a71542123f" +dependencies = [ + "chrono", +] + [[package]] name = "gst_pipeline_studio" version = "0.1.0" @@ -484,6 +508,7 @@ dependencies = [ "futures-channel", "futures-executor", "gettext-rs", + "gst-plugin-gtk4", "gstreamer", "gtk4", "log 0.4.14", @@ -520,6 +545,33 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gstreamer-base" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224f35f36582407caf58ded74854526beeecc23d0cf64b8d1c3e00584ed6863f" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "glib", + "gstreamer", + "gstreamer-base-sys", + "libc", +] + +[[package]] +name = "gstreamer-base-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a083493c3c340e71fa7c66eebda016e9fafc03eb1b4804cf9b2bad61994b078e" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + [[package]] name = "gstreamer-sys" version = "0.18.0" @@ -532,6 +584,38 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gstreamer-video" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410c72d885a67aeb7dbfa49c347e6c85d60f54e1cdaf6aadf8b5364892451261" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "fragile", + "futures-channel", + "glib", + "gstreamer", + "gstreamer-base", + "gstreamer-video-sys", + "libc", + "once_cell", +] + +[[package]] +name = "gstreamer-video-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255c487bf6dd145e23558eaf1c92ef0946ee1999d22bdadc1e492b463609c4b6" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + [[package]] name = "gtk4" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index 7b6df8d..5c192f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ edition = "2018" gtk = { version = "0.4.1", package = "gtk4" } anyhow = "1" gettext-rs = {version = "0.7", features = ["gettext-system"]} -gstreamer = "0.18.1" +gst = { package = "gstreamer", version = "0.18.1" } +gst-plugin-gtk4 = { version = "0.1.0", optional=true } log = "0.4.11" once_cell = "1.7.2" xml-rs = "0.8.4" @@ -20,4 +21,8 @@ simplelog = "0.11.2" futures-channel = "0.3" [dev-dependencies] -futures-executor = "0.3" \ No newline at end of file +futures-executor = "0.3" + +[features] +default = ["gtk4-plugin"] +gtk4-plugin = ["gst-plugin-gtk4"] \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 357fc12..76ccd87 100644 --- a/src/app.rs +++ b/src/app.rs @@ -19,7 +19,7 @@ use glib::SignalHandlerId; use glib::Value; -use gtk::gdk::Rectangle; +use gtk::gdk; use gtk::prelude::*; use gtk::{gio, gio::SimpleAction, glib, graphene}; use gtk::{ @@ -257,7 +257,7 @@ impl GPSApp { if let Some((x, y)) = widget.translate_coordinates(&mainwindow, x, y) { let point = graphene::Point::new(x as f32, y as f32); - pop_menu.set_pointing_to(Some(&Rectangle::new( + pop_menu.set_pointing_to(Some(&gdk::Rectangle::new( point.to_vec2().x() as i32, point.to_vec2().y() as i32, 0, @@ -363,6 +363,19 @@ impl GPSApp { status_bar.push(status_bar.context_id("Description"), &state.to_string()); } + pub fn set_app_preview(&self, paintable: &gdk::Paintable) { + let picture = gtk::Picture::new(); + picture.set_paintable(Some(paintable)); + let box_preview: gtk::Box = self + .builder + .object("box-preview") + .expect("Couldn't get box_preview"); + while let Some(child) = box_preview.first_child() { + box_preview.remove(&child); + } + box_preview.append(&picture); + } + pub fn build_ui(&self, application: &Application) { let drawing_area_window: Viewport = self .builder diff --git a/src/common.rs b/src/common.rs index 46660cf..6994572 100644 --- a/src/common.rs +++ b/src/common.rs @@ -18,7 +18,6 @@ // SPDX-License-Identifier: GPL-3.0-only use anyhow::Result; -use gstreamer as gst; pub fn init() -> Result<()> { unsafe { diff --git a/src/gps/element.rs b/src/gps/element.rs index 6361f0a..06d3d97 100644 --- a/src/gps/element.rs +++ b/src/gps/element.rs @@ -24,7 +24,6 @@ use crate::GPS_INFO; use gst::glib; use gst::prelude::*; -use gstreamer as gst; use std::collections::HashMap; #[derive(Debug, Default, Eq, Ord, PartialEq, PartialOrd)] @@ -62,6 +61,13 @@ impl ElementInfo { gst::Registry::find_feature(®istry, element_name, gst::ElementFactory::static_type()) } + pub fn element_update_rank(element_name: &str, rank: gst::Rank) { + let feature: Option = ElementInfo::element_feature(element_name); + if let Some(feature) = feature { + feature.set_rank(rank); + } + } + pub fn element_description(element_name: &str) -> anyhow::Result { let mut desc = String::from(""); let feature = ElementInfo::element_feature(element_name) @@ -102,7 +108,12 @@ impl ElementInfo { desc.push_str(""); desc.push_str( >k::glib::markup_escape_text( - &plugin.filename().unwrap().as_path().display().to_string(), + &plugin + .filename() + .unwrap_or_default() + .as_path() + .display() + .to_string(), ) .to_string(), ); @@ -208,4 +219,27 @@ impl ElementInfo { } None } + + pub fn search_fo_element(bin: &gst::Bin, element_name: &str) -> Option { + let mut iter = bin.iterate_elements(); + let element = loop { + match iter.next() { + Ok(Some(element)) => { + if element.is::() { + let bin = element.dynamic_cast::().unwrap(); + break ElementInfo::search_fo_element(&bin, element_name); + } else { + GPS_INFO!("Found factory: {}", element.factory().unwrap().name()); + if element.factory().unwrap().name() == element_name { + GPS_INFO!("Found {}", element_name); + break Some(element); + } + } + } + Err(gst::IteratorError::Resync) => iter.resync(), + _ => break None, + } + }; + element + } } diff --git a/src/gps/pad.rs b/src/gps/pad.rs index a18883b..5f5d6e3 100644 --- a/src/gps/pad.rs +++ b/src/gps/pad.rs @@ -24,7 +24,6 @@ use crate::gps::ElementInfo; use crate::graphmanager::{PortDirection, PortPresence}; use gst::prelude::*; -use gstreamer as gst; #[derive(Debug, PartialOrd, PartialEq)] pub struct PadInfo { diff --git a/src/gps/pipeline.rs b/src/gps/pipeline.rs index 52e44f7..9b6c526 100644 --- a/src/gps/pipeline.rs +++ b/src/gps/pipeline.rs @@ -19,12 +19,14 @@ use crate::app::{AppState, GPSApp, GPSAppWeak}; use crate::graphmanager::{GraphView, Node, NodeType, PortDirection, PropertyExt}; + +use crate::gps::ElementInfo; use crate::logger; use crate::GPS_INFO; use gst::glib; use gst::prelude::*; -use gstreamer as gst; +use gtk::gdk; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::fmt; @@ -80,6 +82,11 @@ impl Pipeline { pipeline: RefCell::new(None), current_state: Cell::new(PipelineState::Stopped), })); + #[cfg(feature = "gtk4-plugin")] + { + gstgtk4::plugin_register_static().expect("Failed to register gstgtk4 plugin"); + ElementInfo::element_update_rank("gtk4paintablesink", gst::Rank::Primary); + } Ok(pipeline) } @@ -88,7 +95,7 @@ impl Pipeline { *self.app.borrow_mut() = Some(app.upgrade().unwrap()); } - pub fn create_pipeline(&self, description: &str) -> anyhow::Result { + pub fn create_pipeline(&self, description: &str) -> anyhow::Result { GPS_INFO!("Creating pipeline {}", description); // Create pipeline from the description @@ -104,6 +111,18 @@ impl Pipeline { Ok(pipeline.unwrap()) } + pub fn check_for_gtk4sink(&self, pipeline: &gst::Pipeline) { + let bin = pipeline.clone().dynamic_cast::().unwrap(); + let gtksink = ElementInfo::search_fo_element(&bin, "gtk4paintablesink"); + if let Some(gtksink) = gtksink { + let paintable = gtksink.property::("paintable"); + self.app + .borrow() + .as_ref() + .expect("App should be available") + .set_app_preview(&paintable); + } + } pub fn start_pipeline( &self, graphview: &GraphView, @@ -116,6 +135,7 @@ impl Pipeline { GPS_ERROR!("Unable to start a pipeline: {}", err); err })?; + let bus = pipeline.bus().expect("Pipeline had no bus"); let pipeline_weak = self.downgrade(); bus.add_watch_local(move |_bus, msg| { @@ -123,7 +143,8 @@ impl Pipeline { pipeline.on_pipeline_message(msg); glib::Continue(true) })?; - + pipeline.set_state(gst::State::Ready)?; + self.check_for_gtk4sink(&pipeline); *self.pipeline.borrow_mut() = Some(pipeline); }