mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-12-18 22:16:33 +00:00
app: port support update
Connect the port to the element pad capabilities. Can now create a port only if the element supports the request pad Can only delete a port which is a "sometimes" port/pad. Introduce GPS module.
This commit is contained in:
parent
4bae12c011
commit
0f0f9d6fc0
11 changed files with 422 additions and 263 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -287,9 +287,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gdk4"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1023950739cd692fbf559507cad031bf2b0369d68466121f8a1316bc5ae29f0"
|
||||
checksum = "228384db1fd6539f51152c97e59fbf29b6293988ee89bc915ebff229eb5cda58"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs",
|
||||
|
@ -303,9 +303,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gdk4-sys"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "401b6be19db1e0ad2978fde7b46989b82f7595affa09cb56fe6f1302bc4f19e3"
|
||||
checksum = "48a39e34abe35ee2cf54a1e29dd983accecd113ad30bdead5050418fa92f2a1b"
|
||||
dependencies = [
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
|
@ -510,9 +510,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gsk4"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd7c79a864b8c4606d39fad3fd872688ea20ba8e833a9f38e905a458a29a8c"
|
||||
checksum = "cc7de6ca62027aea1d3ab5d4a6bb2db24a296f56473d8a9a4d7ee33042745ff3"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs",
|
||||
|
@ -526,9 +526,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gsk4-sys"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d25ee96f7d1bdb6fa9945425aea95ce60efedf60c994eceb671fd93ad11d541"
|
||||
checksum = "e31d21d7ce02ba261bb24c50c4ab238a10b41a2c97c32afffae29471b7cca69b"
|
||||
dependencies = [
|
||||
"cairo-sys-rs",
|
||||
"gdk4-sys",
|
||||
|
@ -594,9 +594,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gtk4"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6fda2b49a25eefc1116f15d06585d333c21fc3ce499ab0a86641fac2fbf40ac"
|
||||
checksum = "8622c50f55cbe83e78335cf95850ccefa99e8eac3335ef7d17473312402d7e89"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs",
|
||||
|
@ -617,9 +617,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gtk4-macros"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b17860e493ada8a40577f0e92bce8249a97c831e39d7811ef21de6281d67f86"
|
||||
checksum = "c666e9d310431da43cb9d24995b247e5e24d11972d19bcd8b40e1663d25d0f51"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"proc-macro-crate",
|
||||
|
@ -631,9 +631,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gtk4-sys"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc1047ea7a0ff2947fbfb703eecfcca6b58912171037a4c55b1577015f079d5"
|
||||
checksum = "1544614a8d44f38719a2fdfd608d644d5f385beab33e5852688f48a983a2b583"
|
||||
dependencies = [
|
||||
"cairo-sys-rs",
|
||||
"gdk-pixbuf-sys",
|
||||
|
|
7
TODO.md
7
TODO.md
|
@ -27,12 +27,13 @@
|
|||
### Graphview
|
||||
|
||||
- [ ] create a crate for graphview/node/port
|
||||
- [ ] Remove a port from a node
|
||||
- [x] Remove a port from a node if possible
|
||||
- [ ] Implement graphview unit test
|
||||
- [x] add a css class for pad (presence always or sometimes)
|
||||
|
||||
### app
|
||||
|
||||
- [ ] check that a node accept to create a port on request (input/output)
|
||||
- [x] check that a node accept to create a port on request (input/output)
|
||||
- [ ] Control the connection between element
|
||||
- [x] unable to connect in and in out and out
|
||||
- [ ] unable to connect element with incompatible caps.
|
||||
|
@ -45,7 +46,7 @@
|
|||
- [ ] Implement a command line parser to graph
|
||||
- [ ] Render the parse launch line in a message box
|
||||
- [ ] Prevent to create a pad in an element without the template
|
||||
- [ ] Check the pîpeline live
|
||||
- [ ] Check the pipeline validity
|
||||
- [ ] Implement pipeline unit test
|
||||
- [x] Save node position in XML
|
||||
- [x] Autosave the graph
|
||||
|
|
107
src/app.rs
107
src/app.rs
|
@ -35,13 +35,13 @@ use std::ops;
|
|||
use std::rc::{Rc, Weak};
|
||||
|
||||
use crate::about;
|
||||
use crate::gps::{ElementInfo, PadInfo, Pipeline, PipelineState};
|
||||
use crate::logger;
|
||||
use crate::pipeline::{Pipeline, PipelineState};
|
||||
use crate::plugindialogs;
|
||||
use crate::settings::Settings;
|
||||
use crate::{GPS_DEBUG, GPS_ERROR, GPS_TRACE, GPS_WARN};
|
||||
|
||||
use crate::graphmanager::{GraphView, Node, PortDirection};
|
||||
use crate::graphmanager::{GraphView, Node, PortDirection, PortPresence};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GPSAppInner {
|
||||
|
@ -192,7 +192,7 @@ impl GPSApp {
|
|||
|
||||
application.add_action(&gio::SimpleAction::new("graph.add-plugin", None));
|
||||
|
||||
application.add_action(&gio::SimpleAction::new("port.delete-link", None));
|
||||
application.add_action(&gio::SimpleAction::new("port.delete", None));
|
||||
|
||||
application.add_action(&gio::SimpleAction::new("node.add-to-favorite", None));
|
||||
application.add_action(&gio::SimpleAction::new("node.delete", None));
|
||||
|
@ -224,6 +224,28 @@ impl GPSApp {
|
|||
pop_menu
|
||||
}
|
||||
|
||||
fn app_menu_action(&self, action_name: &str) -> SimpleAction {
|
||||
let application = gio::Application::default()
|
||||
.expect("No default application")
|
||||
.downcast::<gtk::Application>()
|
||||
.expect("Unable to downcast default application");
|
||||
|
||||
application
|
||||
.lookup_action(action_name)
|
||||
.unwrap_or_else(|| panic!("Unable to find action {}", action_name))
|
||||
.dynamic_cast::<SimpleAction>()
|
||||
.expect("Unable to dynamic cast to SimpleAction")
|
||||
}
|
||||
|
||||
fn disconnect_app_menu_action(&self, action_name: &str) {
|
||||
let action = self.app_menu_action(action_name);
|
||||
|
||||
if let Some(signal_handler_id) = self.menu_signal_handlers.borrow_mut().remove(action_name)
|
||||
{
|
||||
action.disconnect(signal_handler_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_app_menu_action<
|
||||
F: Fn(&SimpleAction, std::option::Option<&glib::Variant>) + 'static,
|
||||
>(
|
||||
|
@ -231,20 +253,8 @@ impl GPSApp {
|
|||
action_name: &str,
|
||||
f: F,
|
||||
) {
|
||||
let application = gio::Application::default()
|
||||
.expect("No default application")
|
||||
.downcast::<gtk::Application>()
|
||||
.expect("Unable to downcast default application");
|
||||
let action = application
|
||||
.lookup_action(action_name)
|
||||
.unwrap_or_else(|| panic!("Unable to find action {}", action_name))
|
||||
.dynamic_cast::<SimpleAction>()
|
||||
.expect("Unable to dynamic cast to SimpleAction");
|
||||
|
||||
if let Some(signal_handler_id) = self.menu_signal_handlers.borrow_mut().remove(action_name)
|
||||
{
|
||||
action.disconnect(signal_handler_id);
|
||||
}
|
||||
let action = self.app_menu_action(action_name);
|
||||
self.disconnect_app_menu_action(action_name);
|
||||
let signal_handler_id = action.connect_activate(f);
|
||||
self.menu_signal_handlers
|
||||
.borrow_mut()
|
||||
|
@ -459,7 +469,7 @@ impl GPSApp {
|
|||
}
|
||||
|
||||
pub fn display_plugin_list(app: &GPSApp) {
|
||||
let elements = Pipeline::elements_list().expect("Unable to obtain element's list");
|
||||
let elements = ElementInfo::elements_list().expect("Unable to obtain element's list");
|
||||
plugindialogs::display_plugin_list(app, &elements);
|
||||
}
|
||||
|
||||
|
@ -660,9 +670,18 @@ impl GPSApp {
|
|||
.object("port_menu")
|
||||
.expect("Couldn't get menu model for port");
|
||||
pop_menu.set_menu_model(Some(&menu));
|
||||
app.connect_app_menu_action("port.delete-link", move |_, _| {
|
||||
GPS_DEBUG!("port.delete-link port {} node {}", port_id, node_id);
|
||||
});
|
||||
|
||||
if app.graphview.borrow().can_remove_port(node_id, port_id) {
|
||||
let app_weak = app.downgrade();
|
||||
app.connect_app_menu_action("port.delete", move |_, _| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
GPS_DEBUG!("port.delete-link port {} node {}", port_id, node_id);
|
||||
app.graphview.borrow().remove_port(node_id, port_id);
|
||||
});
|
||||
} else {
|
||||
app.disconnect_app_menu_action("port.delete");
|
||||
}
|
||||
|
||||
pop_menu.show();
|
||||
None
|
||||
},
|
||||
|
@ -679,6 +698,7 @@ impl GPSApp {
|
|||
let app = upgrade_weak!(app_weak, None);
|
||||
|
||||
let node_id = values[1].get::<u32>().expect("node id args[1]");
|
||||
let node = app.graphview.borrow().node(node_id).expect("Unable to find node with this ID");
|
||||
let point = values[2].get::<graphene::Point>().expect("point in args[2]");
|
||||
|
||||
let pop_menu = app.app_pop_menu_at_position(&*app.graphview.borrow(), point.to_vec2().x() as f64, point.to_vec2().y() as f64);
|
||||
|
@ -708,28 +728,33 @@ impl GPSApp {
|
|||
}
|
||||
);
|
||||
|
||||
let app_weak = app.downgrade();
|
||||
app.connect_app_menu_action("node.request-pad-input",
|
||||
move |_,_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
GPS_DEBUG!("node.request-pad-input {}", node_id);
|
||||
let mut node = app.graphview.borrow().node(node_id).unwrap();
|
||||
let port_id = app.graphview.borrow().next_port_id();
|
||||
node.add_port(port_id, "in", PortDirection::Input);
|
||||
}
|
||||
);
|
||||
|
||||
if ElementInfo::element_supports_new_pad_request(&node.name(), PortDirection::Input) {
|
||||
let app_weak = app.downgrade();
|
||||
app.connect_app_menu_action("node.request-pad-input",
|
||||
move |_,_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
GPS_DEBUG!("node.request-pad-input {}", node_id);
|
||||
let port_id = app.graphview.borrow().next_port_id();
|
||||
app.graphview.borrow().add_port(node_id, port_id, "in", PortDirection::Input, PortPresence::Sometimes);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
app.disconnect_app_menu_action("node.request-pad-input");
|
||||
}
|
||||
if ElementInfo::element_supports_new_pad_request(&node.name(), PortDirection::Output) {
|
||||
let app_weak = app.downgrade();
|
||||
app.connect_app_menu_action("node.request-pad-output",
|
||||
move |_,_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
GPS_DEBUG!("node.request-pad-output {}", node_id);
|
||||
let mut node = app.graphview.borrow_mut().node(node_id).unwrap();
|
||||
let port_id = app.graphview.borrow_mut().next_port_id();
|
||||
node.add_port(port_id, "out", PortDirection::Output);
|
||||
app.graphview.borrow().add_port(node_id, port_id, "out", PortDirection::Output, PortPresence::Sometimes);
|
||||
|
||||
}
|
||||
);
|
||||
} else {
|
||||
app.disconnect_app_menu_action("node.request-pad-output");
|
||||
}
|
||||
|
||||
let app_weak = app.downgrade();
|
||||
app.connect_app_menu_action("node.properties",
|
||||
|
@ -771,8 +796,8 @@ impl GPSApp {
|
|||
pub fn add_new_element(&self, element_name: &str) {
|
||||
let graph_view = self.graphview.borrow();
|
||||
let node_id = graph_view.next_node_id();
|
||||
let pads = Pipeline::pads(element_name, false);
|
||||
if Pipeline::element_is_uri_src_handler(element_name) {
|
||||
let (inputs, outputs) = PadInfo::pads(element_name, false);
|
||||
if ElementInfo::element_is_uri_src_handler(element_name) {
|
||||
GPSApp::get_file_from_dialog(self, false, move |app, filename| {
|
||||
GPS_DEBUG!("Open file {}", filename);
|
||||
let node = app.graphview.borrow().node(node_id).unwrap();
|
||||
|
@ -783,9 +808,13 @@ impl GPSApp {
|
|||
}
|
||||
graph_view.add_node_with_port(
|
||||
node_id,
|
||||
Node::new(node_id, element_name, Pipeline::element_type(element_name)),
|
||||
pads.0,
|
||||
pads.1,
|
||||
Node::new(
|
||||
node_id,
|
||||
element_name,
|
||||
ElementInfo::element_type(element_name),
|
||||
),
|
||||
inputs.len() as u32,
|
||||
outputs.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
<menu id="port_menu">
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes" comments="port menu entry delete the link">_Delete link</attribute>
|
||||
<attribute name="action">app.port.delete-link</attribute>
|
||||
<attribute name="label" translatable="yes" comments="port menu entry delete the link">_Delete</attribute>
|
||||
<attribute name="action">app.port.delete</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
|
|
222
src/gps/element.rs
Normal file
222
src/gps/element.rs
Normal file
|
@ -0,0 +1,222 @@
|
|||
// element.rs
|
||||
//
|
||||
// Copyright 2022 Stéphane Cerveau <scerveau@collabora.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::gps::PadInfo;
|
||||
use crate::graphmanager::{NodeType, PortDirection, PortPresence};
|
||||
use crate::logger;
|
||||
use crate::GPS_INFO;
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gstreamer as gst;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct ElementInfo {
|
||||
pub name: Option<String>,
|
||||
plugin_name: Option<String>,
|
||||
rank: i32,
|
||||
}
|
||||
|
||||
impl Default for ElementInfo {
|
||||
fn default() -> ElementInfo {
|
||||
ElementInfo {
|
||||
name: None,
|
||||
plugin_name: None,
|
||||
rank: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ElementInfo {
|
||||
pub fn elements_list() -> anyhow::Result<Vec<ElementInfo>> {
|
||||
let registry = gst::Registry::get();
|
||||
let mut elements: Vec<ElementInfo> = Vec::new();
|
||||
let plugins = gst::Registry::plugin_list(®istry);
|
||||
for plugin in plugins {
|
||||
let plugin_name = gst::Plugin::plugin_name(&plugin);
|
||||
let features = gst::Registry::feature_list_by_plugin(®istry, &plugin_name);
|
||||
for feature in features {
|
||||
let mut element = ElementInfo::default();
|
||||
if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
|
||||
let feature = factory.upcast::<gst::PluginFeature>();
|
||||
|
||||
element.name = Some(gst::PluginFeature::name(&feature).as_str().to_owned());
|
||||
element.plugin_name =
|
||||
Some(gst::Plugin::plugin_name(&plugin).as_str().to_owned());
|
||||
elements.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
elements.sort();
|
||||
Ok(elements)
|
||||
}
|
||||
|
||||
pub fn element_feature(element_name: &str) -> Option<gst::PluginFeature> {
|
||||
let registry = gst::Registry::get();
|
||||
gst::Registry::find_feature(®istry, element_name, gst::ElementFactory::static_type())
|
||||
}
|
||||
|
||||
pub fn element_description(element_name: &str) -> anyhow::Result<String> {
|
||||
let mut desc = String::from("");
|
||||
let feature = ElementInfo::element_feature(element_name)
|
||||
.ok_or_else(|| glib::bool_error!("Failed get element feature"))?;
|
||||
|
||||
if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
|
||||
desc.push_str("<b>Factory details:</b>\n");
|
||||
desc.push_str("<b>Name:</b>");
|
||||
desc.push_str(&factory.name());
|
||||
desc.push('\n');
|
||||
|
||||
let element_keys = factory.metadata_keys();
|
||||
for key in element_keys {
|
||||
let val = factory.metadata(&key);
|
||||
if let Some(val) = val {
|
||||
desc.push_str("<b>");
|
||||
desc.push_str(&key);
|
||||
desc.push_str("</b>:");
|
||||
desc.push_str(>k::glib::markup_escape_text(&val).to_string());
|
||||
desc.push('\n');
|
||||
}
|
||||
}
|
||||
let feature = factory.upcast::<gst::PluginFeature>();
|
||||
let plugin = gst::PluginFeature::plugin(&feature);
|
||||
if let Some(plugin) = plugin {
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Plugin details:</b>");
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Name:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(gst::Plugin::plugin_name(&plugin).as_str());
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Description:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(>k::glib::markup_escape_text(&plugin.description()).to_string());
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Filename:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(
|
||||
>k::glib::markup_escape_text(
|
||||
&plugin.filename().unwrap().as_path().display().to_string(),
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Version:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(>k::glib::markup_escape_text(&plugin.version()).to_string());
|
||||
desc.push('\n');
|
||||
}
|
||||
}
|
||||
Ok(desc)
|
||||
}
|
||||
|
||||
pub fn element_type(element_name: &str) -> NodeType {
|
||||
let (inputs, outputs) = PadInfo::pads(element_name, true);
|
||||
let mut element_type = NodeType::Source;
|
||||
if !inputs.is_empty() {
|
||||
if !outputs.is_empty() {
|
||||
element_type = NodeType::Transform;
|
||||
} else {
|
||||
element_type = NodeType::Sink;
|
||||
}
|
||||
} else if !outputs.is_empty() {
|
||||
element_type = NodeType::Source;
|
||||
}
|
||||
|
||||
element_type
|
||||
}
|
||||
|
||||
fn value_as_str(v: &glib::Value) -> Option<String> {
|
||||
match v.type_() {
|
||||
glib::Type::I8 => Some(str_some_value!(v, i8).to_string()),
|
||||
glib::Type::U8 => Some(str_some_value!(v, u8).to_string()),
|
||||
glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()),
|
||||
glib::Type::I32 => Some(str_some_value!(v, i32).to_string()),
|
||||
glib::Type::U32 => Some(str_some_value!(v, u32).to_string()),
|
||||
glib::Type::I64 => Some(str_some_value!(v, i64).to_string()),
|
||||
glib::Type::U64 => Some(str_some_value!(v, u64).to_string()),
|
||||
glib::Type::F32 => Some(str_some_value!(v, f32).to_string()),
|
||||
glib::Type::F64 => Some(str_some_value!(v, f64).to_string()),
|
||||
glib::Type::STRING => str_opt_value!(v, String),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_properties(element_name: &str) -> anyhow::Result<HashMap<String, String>> {
|
||||
let mut properties_list = HashMap::new();
|
||||
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Unable to get the factory from the feature");
|
||||
let element = factory.create(None)?;
|
||||
let params = element.class().list_properties();
|
||||
|
||||
for param in params {
|
||||
GPS_INFO!("Property_name {}", param.name());
|
||||
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||
{
|
||||
let value = ElementInfo::value_as_str(&element.property(param.name()).unwrap())
|
||||
.unwrap_or_else(|| String::from(""));
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else if let Some(value) = ElementInfo::value_as_str(param.default_value()) {
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else {
|
||||
GPS_INFO!("Unable to add property_name {}", param.name());
|
||||
}
|
||||
}
|
||||
Ok(properties_list)
|
||||
}
|
||||
|
||||
pub fn element_is_uri_src_handler(element_name: &str) -> bool {
|
||||
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Unable to get the factory from the feature");
|
||||
let element = factory
|
||||
.create(None)
|
||||
.expect("Unable to create an element from the feature");
|
||||
match element.dynamic_cast::<gst::URIHandler>() {
|
||||
Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src,
|
||||
Err(_e) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_supports_new_pad_request(element_name: &str, direction: PortDirection) -> bool {
|
||||
let (inputs, outputs) = PadInfo::pads(element_name, true);
|
||||
if direction == PortDirection::Input {
|
||||
for input in inputs {
|
||||
if input.presence() == PortPresence::Sometimes {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if direction == PortDirection::Output {
|
||||
for output in outputs {
|
||||
if output.presence() == PortPresence::Sometimes {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GPS_ERROR!("Port direction unknown");
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
7
src/gps/mod.rs
Normal file
7
src/gps/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod element;
|
||||
mod pad;
|
||||
mod pipeline;
|
||||
|
||||
pub use element::ElementInfo;
|
||||
pub use pad::PadInfo;
|
||||
pub use pipeline::{Pipeline, PipelineState};
|
97
src/gps/pad.rs
Normal file
97
src/gps/pad.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
// pad.rs
|
||||
//
|
||||
// Copyright 2022 Stéphane Cerveau <scerveau@collabora.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::logger;
|
||||
use crate::GPS_INFO;
|
||||
|
||||
use crate::gps::ElementInfo;
|
||||
use crate::graphmanager::{PortDirection, PortPresence};
|
||||
|
||||
use gst::prelude::*;
|
||||
use gstreamer as gst;
|
||||
|
||||
#[derive(Debug, PartialOrd, PartialEq)]
|
||||
pub struct PadInfo {
|
||||
name: Option<String>,
|
||||
element_name: Option<String>,
|
||||
direction: PortDirection,
|
||||
presence: PortPresence,
|
||||
}
|
||||
|
||||
impl Default for PadInfo {
|
||||
fn default() -> PadInfo {
|
||||
PadInfo {
|
||||
name: None,
|
||||
element_name: None,
|
||||
direction: PortDirection::Unknown,
|
||||
presence: PortPresence::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PadInfo {
|
||||
pub fn presence(&self) -> PortPresence {
|
||||
self.presence
|
||||
}
|
||||
|
||||
fn pad_to_port_presence(presence: gst::PadPresence) -> PortPresence {
|
||||
match presence {
|
||||
gst::PadPresence::Always => PortPresence::Always,
|
||||
gst::PadPresence::Sometimes => PortPresence::Sometimes,
|
||||
gst::PadPresence::Request => PortPresence::Sometimes,
|
||||
_ => PortPresence::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pads(element_name: &str, include_on_request: bool) -> (Vec<PadInfo>, Vec<PadInfo>) {
|
||||
let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature");
|
||||
let mut input = vec![];
|
||||
let mut output = vec![];
|
||||
|
||||
if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
|
||||
if factory.num_pad_templates() > 0 {
|
||||
let pads = factory.static_pad_templates();
|
||||
for pad in pads {
|
||||
GPS_INFO!("Found a pad name {}", pad.name_template());
|
||||
if pad.presence() == gst::PadPresence::Always
|
||||
|| (include_on_request
|
||||
&& (pad.presence() == gst::PadPresence::Request
|
||||
|| pad.presence() == gst::PadPresence::Sometimes))
|
||||
{
|
||||
if pad.direction() == gst::PadDirection::Src {
|
||||
output.push(PadInfo {
|
||||
name: Some(pad.name_template().to_string()),
|
||||
element_name: Some(element_name.to_string()),
|
||||
direction: PortDirection::Output,
|
||||
presence: PadInfo::pad_to_port_presence(pad.presence()),
|
||||
});
|
||||
} else if pad.direction() == gst::PadDirection::Sink {
|
||||
input.push(PadInfo {
|
||||
name: Some(pad.name_template().to_string()),
|
||||
element_name: Some(element_name.to_string()),
|
||||
direction: PortDirection::Input,
|
||||
presence: PadInfo::pad_to_port_presence(pad.presence()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(input, output)
|
||||
}
|
||||
}
|
|
@ -30,23 +30,6 @@ use std::fmt;
|
|||
use std::ops;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct ElementInfo {
|
||||
pub name: Option<String>,
|
||||
plugin_name: Option<String>,
|
||||
rank: i32,
|
||||
}
|
||||
|
||||
impl Default for ElementInfo {
|
||||
fn default() -> ElementInfo {
|
||||
ElementInfo {
|
||||
name: None,
|
||||
plugin_name: None,
|
||||
rank: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PipelineState {
|
||||
Playing,
|
||||
|
@ -197,188 +180,6 @@ impl Pipeline {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn elements_list() -> anyhow::Result<Vec<ElementInfo>> {
|
||||
let registry = gst::Registry::get();
|
||||
let mut elements: Vec<ElementInfo> = Vec::new();
|
||||
let plugins = gst::Registry::plugin_list(®istry);
|
||||
for plugin in plugins {
|
||||
let plugin_name = gst::Plugin::plugin_name(&plugin);
|
||||
let features = gst::Registry::feature_list_by_plugin(®istry, &plugin_name);
|
||||
for feature in features {
|
||||
let mut element = ElementInfo::default();
|
||||
if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
|
||||
let feature = factory.upcast::<gst::PluginFeature>();
|
||||
|
||||
element.name = Some(gst::PluginFeature::name(&feature).as_str().to_owned());
|
||||
element.plugin_name =
|
||||
Some(gst::Plugin::plugin_name(&plugin).as_str().to_owned());
|
||||
elements.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
elements.sort();
|
||||
Ok(elements)
|
||||
}
|
||||
|
||||
fn element_feature(element_name: &str) -> Option<gst::PluginFeature> {
|
||||
let registry = gst::Registry::get();
|
||||
gst::Registry::find_feature(®istry, element_name, gst::ElementFactory::static_type())
|
||||
}
|
||||
|
||||
pub fn element_description(element_name: &str) -> anyhow::Result<String> {
|
||||
let mut desc = String::from("");
|
||||
let feature = Pipeline::element_feature(element_name)
|
||||
.ok_or_else(|| glib::bool_error!("Failed get element feature"))?;
|
||||
|
||||
if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
|
||||
desc.push_str("<b>Factory details:</b>\n");
|
||||
desc.push_str("<b>Name:</b>");
|
||||
desc.push_str(&factory.name());
|
||||
desc.push('\n');
|
||||
|
||||
let element_keys = factory.metadata_keys();
|
||||
for key in element_keys {
|
||||
let val = factory.metadata(&key);
|
||||
if let Some(val) = val {
|
||||
desc.push_str("<b>");
|
||||
desc.push_str(&key);
|
||||
desc.push_str("</b>:");
|
||||
desc.push_str(>k::glib::markup_escape_text(&val).to_string());
|
||||
desc.push('\n');
|
||||
}
|
||||
}
|
||||
let feature = factory.upcast::<gst::PluginFeature>();
|
||||
let plugin = gst::PluginFeature::plugin(&feature);
|
||||
if let Some(plugin) = plugin {
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Plugin details:</b>");
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Name:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(gst::Plugin::plugin_name(&plugin).as_str());
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Description:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(>k::glib::markup_escape_text(&plugin.description()).to_string());
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Filename:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(
|
||||
>k::glib::markup_escape_text(
|
||||
&plugin.filename().unwrap().as_path().display().to_string(),
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
desc.push('\n');
|
||||
desc.push_str("<b>Version:");
|
||||
desc.push_str("</b>");
|
||||
desc.push_str(>k::glib::markup_escape_text(&plugin.version()).to_string());
|
||||
desc.push('\n');
|
||||
}
|
||||
}
|
||||
Ok(desc)
|
||||
}
|
||||
|
||||
pub fn pads(element_name: &str, include_on_request: bool) -> (u32, u32) {
|
||||
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||
let mut input = 0;
|
||||
let mut output = 0;
|
||||
|
||||
if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
|
||||
if factory.num_pad_templates() > 0 {
|
||||
let pads = factory.static_pad_templates();
|
||||
for pad in pads {
|
||||
if pad.presence() == gst::PadPresence::Always
|
||||
|| (include_on_request
|
||||
&& (pad.presence() == gst::PadPresence::Request
|
||||
|| pad.presence() == gst::PadPresence::Sometimes))
|
||||
{
|
||||
if pad.direction() == gst::PadDirection::Src {
|
||||
output += 1;
|
||||
} else if pad.direction() == gst::PadDirection::Sink {
|
||||
input += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(input, output)
|
||||
}
|
||||
|
||||
pub fn element_type(element_name: &str) -> NodeType {
|
||||
let pads = Pipeline::pads(element_name, true);
|
||||
let mut element_type = NodeType::Source;
|
||||
if pads.0 > 0 {
|
||||
if pads.1 > 0 {
|
||||
element_type = NodeType::Transform;
|
||||
} else {
|
||||
element_type = NodeType::Sink;
|
||||
}
|
||||
} else if pads.1 > 0 {
|
||||
element_type = NodeType::Source;
|
||||
}
|
||||
|
||||
element_type
|
||||
}
|
||||
|
||||
fn value_as_str(v: &glib::Value) -> Option<String> {
|
||||
match v.type_() {
|
||||
glib::Type::I8 => Some(str_some_value!(v, i8).to_string()),
|
||||
glib::Type::U8 => Some(str_some_value!(v, u8).to_string()),
|
||||
glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()),
|
||||
glib::Type::I32 => Some(str_some_value!(v, i32).to_string()),
|
||||
glib::Type::U32 => Some(str_some_value!(v, u32).to_string()),
|
||||
glib::Type::I64 => Some(str_some_value!(v, i64).to_string()),
|
||||
glib::Type::U64 => Some(str_some_value!(v, u64).to_string()),
|
||||
glib::Type::F32 => Some(str_some_value!(v, f32).to_string()),
|
||||
glib::Type::F64 => Some(str_some_value!(v, f64).to_string()),
|
||||
glib::Type::STRING => str_opt_value!(v, String),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_properties(element_name: &str) -> anyhow::Result<HashMap<String, String>> {
|
||||
let mut properties_list = HashMap::new();
|
||||
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Unable to get the factory from the feature");
|
||||
let element = factory.create(None)?;
|
||||
let params = element.class().list_properties();
|
||||
|
||||
for param in params {
|
||||
GPS_INFO!("Property_name {}", param.name());
|
||||
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||
{
|
||||
let value = Pipeline::value_as_str(&element.property(param.name()).unwrap())
|
||||
.unwrap_or_else(|| String::from(""));
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else if let Some(value) = Pipeline::value_as_str(param.default_value()) {
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else {
|
||||
GPS_INFO!("Unable to add property_name {}", param.name());
|
||||
}
|
||||
}
|
||||
Ok(properties_list)
|
||||
}
|
||||
|
||||
pub fn element_is_uri_src_handler(element_name: &str) -> bool {
|
||||
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Unable to get the factory from the feature");
|
||||
let element = factory
|
||||
.create(None)
|
||||
.expect("Unable to create an element from the feature");
|
||||
match element.dynamic_cast::<gst::URIHandler>() {
|
||||
Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src,
|
||||
Err(_e) => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Render graph methods
|
||||
fn process_gst_node(
|
||||
&self,
|
|
@ -26,7 +26,7 @@ mod config;
|
|||
mod graphmanager;
|
||||
#[macro_use]
|
||||
mod logger;
|
||||
mod pipeline;
|
||||
mod gps;
|
||||
mod plugindialogs;
|
||||
mod settings;
|
||||
use gtk::prelude::*;
|
||||
|
|
|
@ -28,7 +28,10 @@ rust_sources = files(
|
|||
'logger.rs',
|
||||
'macros.rs',
|
||||
'main.rs',
|
||||
'pipeline.rs',
|
||||
'gps/pipeline.rs',
|
||||
'gps/element.rs',
|
||||
'gps/pad.rs',
|
||||
'gps/mod.rs',
|
||||
'plugindialogs.rs',
|
||||
'settings.rs',
|
||||
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use crate::app::GPSApp;
|
||||
use crate::gps::ElementInfo;
|
||||
use crate::logger;
|
||||
use crate::pipeline::ElementInfo;
|
||||
use crate::pipeline::Pipeline;
|
||||
use gtk::glib;
|
||||
use gtk::prelude::*;
|
||||
use gtk::TextBuffer;
|
||||
|
@ -93,7 +92,7 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
|
|||
if let Some((model, iter)) = selection.selected() {
|
||||
let element_name = model
|
||||
.get::<String>(&iter, 1);
|
||||
let description = Pipeline::element_description(&element_name).expect("Unable to get element description from GStreamer");
|
||||
let description = ElementInfo::element_description(&element_name).expect("Unable to get element description from GStreamer");
|
||||
text_buffer.set_text("");
|
||||
text_buffer.insert_markup(&mut text_buffer.end_iter(), &description);
|
||||
}
|
||||
|
@ -133,7 +132,7 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
|
|||
.expect("Couldn't get box-plugin-properties");
|
||||
let update_properties: Rc<RefCell<HashMap<String, String>>> =
|
||||
Rc::new(RefCell::new(HashMap::new()));
|
||||
let properties = Pipeline::element_properties(element_name).unwrap();
|
||||
let properties = ElementInfo::element_properties(element_name).unwrap();
|
||||
for (name, value) in properties {
|
||||
let entry_box = Box::new(gtk::Orientation::Horizontal, 6);
|
||||
let label = Label::new(Some(&name));
|
||||
|
|
Loading…
Reference in a new issue