From f4be2299b9d950d53ed4656bfbd1339955c6b278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Cerveau?= Date: Mon, 18 Sep 2023 12:30:39 +0200 Subject: [PATCH] graphbook: introduce element_factory_exists This method helps to tell if a factory exists when loading a graph. If the factory does not exists, use light mode to display it and prevent some menu item and change its description. --- ChangeLog.md | 4 ++ TODO.md | 2 - src/gps/element.rs | 114 +++++++++++++++++++++++++++------------------ src/gps/pad.rs | 60 ++++++++++++------------ src/graphbook.rs | 92 ++++++++++++++++++++---------------- 5 files changed, 155 insertions(+), 117 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 32025cd..e15cfe0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -100,3 +100,7 @@ ### app - [x] Add multiple graphviews with tabs. - [x] handle the caps setter element + +## 0.3.2 +### app +- [x] check that element exists before creating it on file load. \ No newline at end of file diff --git a/TODO.md b/TODO.md index a1a2805..c3fcc6a 100644 --- a/TODO.md +++ b/TODO.md @@ -15,7 +15,6 @@ - [ ] unable to connect element with incompatible caps. - [ ] Implement graph dot render/load - - [ ] Add probes on each pad to monitor the pipeline - [ ] Render a media file - [ ] Offer compatible element to a pad (autorender) @@ -32,7 +31,6 @@ ## bugs -- [ ] check that element exists before creating it on file load. - [ ] Combo box is not well selected if the value is not linear such as flags. See flags in playbin - [ ] opening a graph file can lead a different behavior in the pipeline. See videomixer graph where the zorder on pads is not correctly set to right one. diff --git a/src/gps/element.rs b/src/gps/element.rs index e8d4503..0dd5349 100644 --- a/src/gps/element.rs +++ b/src/gps/element.rs @@ -46,6 +46,19 @@ impl ElementInfo { Ok(elements) } + pub fn element_factory_exists(element_name: &str) -> bool { + match ElementInfo::element_feature(element_name) { + Some(_feature) => { + GPS_DEBUG!("Found element factory name {}", element_name); + true + } + None => { + GPS_ERROR!("Unable to find element factory name {}", element_name); + false + } + } + } + pub fn element_feature(element_name: &str) -> Option { let registry = gst::Registry::get(); gst::Registry::find_feature(®istry, element_name, gst::ElementFactory::static_type()) @@ -60,59 +73,68 @@ impl ElementInfo { pub fn element_description(element_name: &str) -> anyhow::Result { let mut desc = String::from(""); - let feature = ElementInfo::element_feature(element_name) - .ok_or_else(|| glib::bool_error!("Failed get element feature"))?; - let rank = feature.rank(); - if let Ok(factory) = feature.downcast::() { + if !ElementInfo::element_factory_exists(element_name) { desc.push_str("Factory details:\n"); - desc.push_str("Rank:"); - let _ = write!(desc, "{rank:?}",); - desc.push('\n'); desc.push_str("Name:"); - desc.push_str(&factory.name()); + desc.push_str(element_name); desc.push('\n'); + desc.push('\n'); + desc.push_str("Factory unavailable."); + } else { + let feature = ElementInfo::element_feature(element_name) + .ok_or_else(|| glib::bool_error!("Failed get element feature"))?; + let rank = feature.rank(); + if let Ok(factory) = feature.downcast::() { + desc.push_str("Factory details:\n"); + desc.push_str("Rank:"); + let _ = write!(desc, "{rank:?}",); + desc.push('\n'); + desc.push_str("Name:"); + 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(""); - desc.push_str(&key); - desc.push_str(":"); - desc.push_str(>k::glib::markup_escape_text(val)); + let element_keys = factory.metadata_keys(); + for key in element_keys { + let val = factory.metadata(&key); + if let Some(val) = val { + desc.push_str(""); + desc.push_str(&key); + desc.push_str(":"); + desc.push_str(>k::glib::markup_escape_text(val)); + desc.push('\n'); + } + } + let feature = factory.upcast::(); + let plugin = gst::PluginFeature::plugin(&feature); + if let Some(plugin) = plugin { + desc.push('\n'); + desc.push_str("Plugin details:"); + desc.push('\n'); + desc.push_str("Name:"); + desc.push_str(""); + desc.push_str(gst::Plugin::plugin_name(&plugin).as_str()); + desc.push('\n'); + desc.push_str("Description:"); + desc.push_str(""); + desc.push_str(>k::glib::markup_escape_text(&plugin.description())); + desc.push('\n'); + desc.push_str("Filename:"); + desc.push_str(""); + desc.push_str(>k::glib::markup_escape_text( + &plugin + .filename() + .unwrap_or_default() + .as_path() + .display() + .to_string(), + )); + desc.push('\n'); + desc.push_str("Version:"); + desc.push_str(""); + desc.push_str(>k::glib::markup_escape_text(&plugin.version())); desc.push('\n'); } } - let feature = factory.upcast::(); - let plugin = gst::PluginFeature::plugin(&feature); - if let Some(plugin) = plugin { - desc.push('\n'); - desc.push_str("Plugin details:"); - desc.push('\n'); - desc.push_str("Name:"); - desc.push_str(""); - desc.push_str(gst::Plugin::plugin_name(&plugin).as_str()); - desc.push('\n'); - desc.push_str("Description:"); - desc.push_str(""); - desc.push_str(>k::glib::markup_escape_text(&plugin.description())); - desc.push('\n'); - desc.push_str("Filename:"); - desc.push_str(""); - desc.push_str(>k::glib::markup_escape_text( - &plugin - .filename() - .unwrap_or_default() - .as_path() - .display() - .to_string(), - )); - desc.push('\n'); - desc.push_str("Version:"); - desc.push_str(""); - desc.push_str(>k::glib::markup_escape_text(&plugin.version())); - desc.push('\n'); - } } Ok(desc) } diff --git a/src/gps/pad.rs b/src/gps/pad.rs index fe85b78..d9fc1d1 100644 --- a/src/gps/pad.rs +++ b/src/gps/pad.rs @@ -51,41 +51,43 @@ impl PadInfo { } pub fn pads(element_name: &str, include_on_request: bool) -> (Vec, Vec) { - 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::() { - if factory.num_pad_templates() > 0 { - let pads = factory.static_pad_templates(); - for pad in pads { - GPS_TRACE!("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()), - caps: Some(pad.caps().to_string()), - }); - } 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()), - caps: Some(pad.caps().to_string()), - }); + if let Some(feature) = ElementInfo::element_feature(element_name) { + if let Ok(factory) = feature.downcast::() { + if factory.num_pad_templates() > 0 { + let pads = factory.static_pad_templates(); + for pad in pads { + GPS_TRACE!("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()), + caps: Some(pad.caps().to_string()), + }); + } 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()), + caps: Some(pad.caps().to_string()), + }); + } } } } } + (input, output) + } else { + (input, output) } - (input, output) } } diff --git a/src/graphbook.rs b/src/graphbook.rs index 6c5998a..186b795 100644 --- a/src/graphbook.rs +++ b/src/graphbook.rs @@ -242,6 +242,10 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) { if let Some(node) = current_graphtab(&app).graphview().node(node_id) { let description = GPS::ElementInfo::element_description(&node.name()).ok(); node.set_tooltip_markup(description.as_deref()); + if !GPS::ElementInfo::element_factory_exists(&node.name()) { + node.set_light(true); + node.set_tooltip_markup(description.as_deref()); + } for port in node.all_ports(GM::PortDirection::All) { let caps = PropertyExt::property(&port,"_caps"); GPS_DEBUG!("caps={} for port id {}", caps.clone().unwrap_or_else(|| "caps unknown".to_string()), port.id()); @@ -382,6 +386,8 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) { glib::clone!(@weak graphbook => @default-return None, move |values: &[Value]| { let app = upgrade_weak!(app_weak, None); let node_id = values[1].get::().expect("node id args[1]"); + let node = current_graphtab(&app).graphview().node(node_id).unwrap(); + let element_exists = GPS::ElementInfo::element_factory_exists(&node.name()); let point = values[2].get::().expect("point in args[2]"); let pop_menu = app.app_pop_menu_at_position(&*current_graphtab(&app).graphview(), point.to_vec2().x() as f64, point.to_vec2().y() as f64); let menu: gio::MenuModel = app @@ -390,16 +396,6 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) { .expect("Couldn't get menu model for node"); pop_menu.set_menu_model(Some(&menu)); - let app_weak = app.downgrade(); - app.connect_app_menu_action("node.add-to-favorite", - move |_,_| { - let app = upgrade_weak!(app_weak); - GPS_DEBUG!("node.add-to-favorite id: {}", node_id); - if let Some(node) = current_graphtab(&app).graphview().node(node_id) { - GPSUI::elements::add_to_favorite_list(&app, node.name()); - }; - } - ); let app_weak = app.downgrade(); app.connect_app_menu_action("node.delete", @@ -409,42 +405,56 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) { current_graphtab(&app).graphview().remove_node(node_id); } ); - let node = app.node(node_id); - if let Some(input) = GPS::ElementInfo::element_supports_new_pad_request(&node.name(), GM::PortDirection::Input) { + if element_exists { let app_weak = app.downgrade(); - app.connect_app_menu_action("node.request-pad-input", + app.connect_app_menu_action("node.add-to-favorite", move |_,_| { let app = upgrade_weak!(app_weak); - GPS_DEBUG!("node.request-pad-input id: {}", node_id); - app.create_port_with_caps(node_id, GM::PortDirection::Input, GM::PortPresence::Sometimes, input.caps().to_string()); + GPS_DEBUG!("node.add-to-favorite id: {}", node_id); + if let Some(node) = current_graphtab(&app).graphview().node(node_id) { + GPSUI::elements::add_to_favorite_list(&app, node.name()); + }; } ); - } else { - app.disconnect_app_menu_action("node.request-pad-input"); - } - let node = app.node(node_id); - if let Some(output) = GPS::ElementInfo::element_supports_new_pad_request(&node.name(), GM::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 id: {}", node_id); - app.create_port_with_caps(node_id, GM::PortDirection::Output, GM::PortPresence::Sometimes, output.caps().to_string()); - } - ); - } else { - app.disconnect_app_menu_action("node.request-pad-output"); - } - let app_weak = app.downgrade(); - app.connect_app_menu_action("node.properties", - move |_,_| { - let app = upgrade_weak!(app_weak); - GPS_DEBUG!("node.properties id {}", node_id); - let node = current_graphtab(&app).graphview().node(node_id).unwrap(); - GPSUI::properties::display_plugin_properties(&app, &node.name(), node_id); + + let node = app.node(node_id); + if let Some(input) = GPS::ElementInfo::element_supports_new_pad_request(&node.name(), GM::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 id: {}", node_id); + app.create_port_with_caps(node_id, GM::PortDirection::Input, GM::PortPresence::Sometimes, input.caps().to_string()); + } + ); + } else { + app.disconnect_app_menu_action("node.request-pad-input"); } - ); + let node = app.node(node_id); + if let Some(output) = GPS::ElementInfo::element_supports_new_pad_request(&node.name(), GM::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 id: {}", node_id); + app.create_port_with_caps(node_id, GM::PortDirection::Output, GM::PortPresence::Sometimes, output.caps().to_string()); + } + ); + } else { + app.disconnect_app_menu_action("node.request-pad-output"); + } + + let app_weak = app.downgrade(); + app.connect_app_menu_action("node.properties", + move |_,_| { + let app = upgrade_weak!(app_weak); + GPS_DEBUG!("node.properties id {}", node_id); + let node = current_graphtab(&app).graphview().node(node_id).unwrap(); + GPSUI::properties::display_plugin_properties(&app, &node.name(), node_id); + } + ); + } pop_menu.show(); None }), @@ -459,7 +469,9 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) { let node_id = values[1].get::().expect("node id args[1]"); GPS_TRACE!("Node double clicked id={}", node_id); let node = current_graphtab(&app).graphview().node(node_id).unwrap(); - GPSUI::properties::display_plugin_properties(&app, &node.name(), node_id); + if GPS::ElementInfo::element_factory_exists(&node.name()) { + GPSUI::properties::display_plugin_properties(&app, &node.name(), node_id); + } None }), );