app: code cleanup

Use simplified anyhow result
Rewrite the expect/error messages.
Refactor the start pipeline API
Rename the treeview ids
This commit is contained in:
Stéphane Cerveau 2022-01-11 14:38:18 +01:00
parent 2a7137e6dc
commit 198847cb54
5 changed files with 132 additions and 135 deletions

11
TODO.md
View file

@ -25,6 +25,15 @@ TODO:
- [x] Connect the logs to the window - [x] Connect the logs to the window
- [] Create a window for the video output - [] Create a window for the video output
- [] Add multiple graphviews with tabs. - [] Add multiple graphviews with tabs.
- [] Property window in the main window
- [] Connect the GPS status to GST status
- [] Implement graph dot render/load
- [] Implement a command line parser to graph
- [] Unable to create a pad in an element without the template
- [] Remove a pad from the graph
- [] Implement graphview unit test
- [] Implement pipeline unit test
- [] Save node position in XML
## bugs ## bugs
@ -35,5 +44,5 @@ TODO:
## Code cleanup ## Code cleanup
[] remove useless code from graphview [] remove useless code from graphview
[] Move render to a specific module [X] Move render to a specific module
[x] Move GST render to a specific module [x] Move GST render to a specific module

View file

@ -16,6 +16,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use glib::SignalHandlerId; use glib::SignalHandlerId;
use glib::Value; use glib::Value;
use gtk::gdk::Rectangle; use gtk::gdk::Rectangle;
@ -29,8 +30,8 @@ use gtk::{gio, gio::SimpleAction, glib, graphene};
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::{error, ops};
use crate::about; use crate::about;
use crate::logger; use crate::logger;
@ -78,26 +79,29 @@ impl GPSAppWeak {
} }
impl GPSApp { impl GPSApp {
fn new(application: &gtk::Application) -> anyhow::Result<GPSApp, Box<dyn error::Error>> { fn new(application: &gtk::Application) -> anyhow::Result<GPSApp> {
let glade_src = include_str!("gps.ui"); let glade_src = include_str!("gps.ui");
let builder = Builder::from_string(glade_src); let builder = Builder::from_string(glade_src);
let window: ApplicationWindow = builder.object("mainwindow").expect("Couldn't get window"); let window: ApplicationWindow = builder
.object("mainwindow")
.expect("Couldn't get the main window");
window.set_application(Some(application)); window.set_application(Some(application));
window.set_title(Some("GstPipelineStudio")); window.set_title(Some("GstPipelineStudio"));
let settings = Settings::load_settings(); let settings = Settings::load_settings();
window.set_size_request(settings.app_width, settings.app_height); window.set_size_request(settings.app_width, settings.app_height);
let paned: Paned = builder let paned: Paned = builder
.object("graph_logs-paned") .object("graph_logs-paned")
.expect("Couldn't get window"); .expect("Couldn't get graph_logs-paned");
paned.set_position(settings.app_graph_logs_paned_pos); paned.set_position(settings.app_graph_logs_paned_pos);
let paned: Paned = builder let paned: Paned = builder
.object("graph_favorites-paned") .object("graph_favorites-paned")
.expect("Couldn't get window"); .expect("Couldn't get graph_favorites-paned");
paned.set_position(settings.app_graph_favorites_paned_pos); paned.set_position(settings.app_graph_favorites_paned_pos);
if settings.app_maximized { if settings.app_maximized {
window.maximize(); window.maximize();
} }
let pipeline = Pipeline::new().expect("Unable to initialize the pipeline"); let pipeline = Pipeline::new().expect("Unable to initialize GStreamer subsystem");
let app = GPSApp(Rc::new(GPSAppInner { let app = GPSApp(Rc::new(GPSAppInner {
window, window,
graphview: RefCell::new(GraphView::new()), graphview: RefCell::new(GraphView::new()),
@ -113,11 +117,8 @@ impl GPSApp {
// Create application and error out if that fails for whatever reason // Create application and error out if that fails for whatever reason
let app = match GPSApp::new(application) { let app = match GPSApp::new(application) {
Ok(app) => app, Ok(app) => app,
Err(_err) => { Err(err) => {
/* utils::show_error_dialog( println!("Error creating application: {}", err);
true,
format!("Error creating application: {}", err).as_str(),
); */
return; return;
} }
}; };
@ -125,7 +126,6 @@ impl GPSApp {
// When the application is activated show the UI. This happens when the first process is // When the application is activated show the UI. This happens when the first process is
// started, and in the first process whenever a second process is started // started, and in the first process whenever a second process is started
let app_weak = app.downgrade(); let app_weak = app.downgrade();
application.connect_activate(glib::clone!(@weak application => move |_| { application.connect_activate(glib::clone!(@weak application => move |_| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
app.build_ui(&application); app.build_ui(&application);
@ -140,7 +140,7 @@ impl GPSApp {
let window: ApplicationWindow = app let window: ApplicationWindow = app
.builder .builder
.object("mainwindow") .object("mainwindow")
.expect("Couldn't get window"); .expect("Couldn't get the main window");
let mut settings = Settings::load_settings(); let mut settings = Settings::load_settings();
settings.app_maximized = window.is_maximized(); settings.app_maximized = window.is_maximized();
settings.app_width = window.width(); settings.app_width = window.width();
@ -148,19 +148,19 @@ impl GPSApp {
let paned: Paned = app let paned: Paned = app
.builder .builder
.object("graph_logs-paned") .object("graph_logs-paned")
.expect("Couldn't get window"); .expect("Couldn't get graph_logs-paned");
settings.app_graph_logs_paned_pos = paned.position(); settings.app_graph_logs_paned_pos = paned.position();
let paned: Paned = app let paned: Paned = app
.builder .builder
.object("graph_favorites-paned") .object("graph_favorites-paned")
.expect("Couldn't get window"); .expect("Couldn't get graph_favorites-paned");
settings.app_graph_favorites_paned_pos = paned.position(); settings.app_graph_favorites_paned_pos = paned.position();
Settings::save_settings(&settings); Settings::save_settings(&settings);
let pop_menu: PopoverMenu = app let pop_menu: PopoverMenu = app
.builder .builder
.object("app_pop_menu") .object("app_pop_menu")
.expect("Couldn't get pop over menu for app"); .expect("Couldn't get app_pop_menu");
pop_menu.unparent(); pop_menu.unparent();
app.drop(); app.drop();
@ -203,12 +203,12 @@ impl GPSApp {
let mainwindow: ApplicationWindow = self let mainwindow: ApplicationWindow = self
.builder .builder
.object("mainwindow") .object("mainwindow")
.expect("Couldn't get mainwindow"); .expect("Couldn't get the main window");
let pop_menu: PopoverMenu = self let pop_menu: PopoverMenu = self
.builder .builder
.object("app_pop_menu") .object("app_pop_menu")
.expect("Couldn't get popover menu"); .expect("Couldn't get app_pop_menu");
if let Some((x, y)) = widget.translate_coordinates(&mainwindow, x, y) { if let Some((x, y)) = widget.translate_coordinates(&mainwindow, x, y) {
let point = graphene::Point::new(x as f32, y as f32); let point = graphene::Point::new(x as f32, y as f32);
@ -232,12 +232,12 @@ impl GPSApp {
let application = gio::Application::default() let application = gio::Application::default()
.expect("No default application") .expect("No default application")
.downcast::<gtk::Application>() .downcast::<gtk::Application>()
.expect("Default application has wrong type"); .expect("Unable to downcast default application");
let action = application let action = application
.lookup_action(action_name) .lookup_action(action_name)
.expect("Unable to find action") .unwrap_or_else(|| panic!("Unable to find action {}", action_name))
.dynamic_cast::<SimpleAction>() .dynamic_cast::<SimpleAction>()
.expect("Unable to cast to SimpleAction"); .expect("Unable to dynamic cast to SimpleAction");
if let Some(signal_handler_id) = self.menu_signal_handlers.borrow_mut().remove(action_name) if let Some(signal_handler_id) = self.menu_signal_handlers.borrow_mut().remove(action_name)
{ {
@ -270,7 +270,7 @@ impl GPSApp {
let window: ApplicationWindow = app let window: ApplicationWindow = app
.builder .builder
.object("mainwindow") .object("mainwindow")
.expect("Couldn't get window"); .expect("Couldn't get main window");
let file_chooser: FileChooserDialog = FileChooserDialog::new( let file_chooser: FileChooserDialog = FileChooserDialog::new(
Some(message), Some(message),
Some(&window), Some(&window),
@ -289,7 +289,7 @@ impl GPSApp {
file.path() file.path()
.expect("Couldn't get file path") .expect("Couldn't get file path")
.to_str() .to_str()
.expect("unable to convert to string"), .expect("Unable to convert to string"),
); );
f(app, filename); f(app, filename);
} }
@ -336,8 +336,8 @@ impl GPSApp {
fn setup_logger_list(&self) { fn setup_logger_list(&self) {
let logger_list: TreeView = self let logger_list: TreeView = self
.builder .builder
.object("logger_list") .object("treeview-logger")
.expect("Couldn't get window"); .expect("Couldn't get treeview-logger");
let column = TreeViewColumn::new(); let column = TreeViewColumn::new();
let cell = CellRendererText::new(); let cell = CellRendererText::new();
column.pack_start(&cell, true); column.pack_start(&cell, true);
@ -358,8 +358,8 @@ impl GPSApp {
fn add_to_logger_list(&self, log_entry: String) { fn add_to_logger_list(&self, log_entry: String) {
let logger_list: TreeView = self let logger_list: TreeView = self
.builder .builder
.object("logger_list") .object("treeview-logger")
.expect("Couldn't get window"); .expect("Couldn't get treeview-logger");
if let Some(model) = logger_list.model() { if let Some(model) = logger_list.model() {
let list_store = model let list_store = model
.dynamic_cast::<ListStore>() .dynamic_cast::<ListStore>()
@ -382,8 +382,8 @@ impl GPSApp {
fn setup_favorite_list(&self, application: &Application) { fn setup_favorite_list(&self, application: &Application) {
let favorite_list: TreeView = self let favorite_list: TreeView = self
.builder .builder
.object("favorites_list") .object("treeview-favorites")
.expect("Couldn't get window"); .expect("Couldn't get treeview-favorites");
let column = TreeViewColumn::new(); let column = TreeViewColumn::new();
let cell = CellRendererText::new(); let cell = CellRendererText::new();
@ -402,7 +402,7 @@ impl GPSApp {
.get(&iter, 0) .get(&iter, 0)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Treeview selection, column 1");
GPS_DEBUG!("{}", element_name); GPS_DEBUG!("{} selected", element_name);
app.add_new_element(&element_name); app.add_new_element(&element_name);
} }
}); });
@ -418,14 +418,14 @@ impl GPSApp {
let element_name = model let element_name = model
.get(&iter, 0) .get(&iter, 0)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Treeview selection, column 0");
GPS_DEBUG!("{}", element_name); GPS_DEBUG!("Element {} selected", element_name);
let pop_menu = app.app_pop_menu_at_position(&favorite_list, x, y); let pop_menu = app.app_pop_menu_at_position(&favorite_list, x, y);
let menu: gio::MenuModel = app let menu: gio::MenuModel = app
.builder .builder
.object("fav_menu") .object("fav_menu")
.expect("Couldn't get menu model for graph"); .expect("Couldn't get fav_menu model");
pop_menu.set_menu_model(Some(&menu)); pop_menu.set_menu_model(Some(&menu));
let app_weak = app.downgrade(); let app_weak = app.downgrade();
@ -451,8 +451,8 @@ impl GPSApp {
if !favorites.contains(&element_name) { if !favorites.contains(&element_name) {
let favorite_list: TreeView = self let favorite_list: TreeView = self
.builder .builder
.object("favorites_list") .object("treeview-favorites")
.expect("Couldn't get window"); .expect("Couldn't get treeview-favorites");
if let Some(model) = favorite_list.model() { if let Some(model) = favorite_list.model() {
let list_store = model let list_store = model
.dynamic_cast::<ListStore>() .dynamic_cast::<ListStore>()
@ -472,7 +472,7 @@ impl GPSApp {
let drawing_area_window: Viewport = self let drawing_area_window: Viewport = self
.builder .builder
.object("drawing_area") .object("drawing_area")
.expect("Couldn't get window"); .expect("Couldn't get drawing_area");
drawing_area_window.set_child(Some(&*self.graphview.borrow())); drawing_area_window.set_child(Some(&*self.graphview.borrow()));
@ -482,7 +482,7 @@ impl GPSApp {
let status_bar: Statusbar = self let status_bar: Statusbar = self
.builder .builder
.object("status_bar") .object("status_bar")
.expect("Couldn't get window"); .expect("Couldn't get status_bar");
status_bar.push(status_bar.context_id("Description"), "GPS is ready"); status_bar.push(status_bar.context_id("Description"), "GPS is ready");
self.setup_app_actions(application); self.setup_app_actions(application);
@ -490,7 +490,7 @@ impl GPSApp {
let pop_menu: PopoverMenu = self let pop_menu: PopoverMenu = self
.builder .builder
.object("app_pop_menu") .object("app_pop_menu")
.expect("Couldn't get pop over menu for app"); .expect("Couldn't get app_pop_menu");
pop_menu.set_parent(window); pop_menu.set_parent(window);
let app_weak = self.downgrade(); let app_weak = self.downgrade();
@ -543,48 +543,26 @@ impl GPSApp {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
GPSApp::display_plugin_list(&app); GPSApp::display_plugin_list(&app);
}); });
let add_button: Button = self
.builder
.object("button-play")
.expect("Couldn't get app_button");
let app_weak = self.downgrade();
add_button.connect_clicked(glib::clone!(@weak window => move |_| {
// entry.set_text("Clicked!");
let app = upgrade_weak!(app_weak);
let graph_view = app.graphview.borrow();
let pipeline = app.pipeline.borrow();
if pipeline.state() == PipelineState::Stopped {
if let Err(err) = pipeline.create_pipeline(&pipeline.render_gst_launch(&graph_view)) {
GPS_ERROR!("Unable to start a pipeline: {}", err);
}
pipeline.set_state(PipelineState::Playing).expect("Unable to change state");
} else if pipeline.state() == PipelineState::Paused {
pipeline.set_state(PipelineState::Playing).expect("Unable to change state");
} else {
pipeline.set_state(PipelineState::Paused).expect("Unable to change state");
}
}));
let add_button: Button = self
.builder
.object("button-pause")
.expect("Couldn't get app_button");
let app_weak = self.downgrade(); let app_weak = self.downgrade();
add_button.connect_clicked(glib::clone!(@weak window => move |_| { self.connect_button_action("button-play", move |_| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
let graph_view = app.graphview.borrow(); let graph_view = app.graphview.borrow();
let pipeline = app.pipeline.borrow(); let _ = app
if pipeline.state() == PipelineState::Stopped { .pipeline
if let Err(err) = pipeline.create_pipeline(&pipeline.render_gst_launch(&graph_view)) { .borrow()
GPS_ERROR!("Unable to start a pipeline: {}", err); .start_pipeline(&graph_view, PipelineState::Playing);
} });
pipeline.set_state(PipelineState::Paused).expect("Unable to change state");
} else if pipeline.state() == PipelineState::Paused { let app_weak = self.downgrade();
pipeline.set_state(PipelineState::Playing).expect("Unable to change state"); self.connect_button_action("button-pause", move |_| {
} else { let app = upgrade_weak!(app_weak);
pipeline.set_state(PipelineState::Paused).expect("Unable to change state"); let graph_view = app.graphview.borrow();
} let _ = app
})); .pipeline
.borrow()
.start_pipeline(&graph_view, PipelineState::Paused);
});
let app_weak = self.downgrade(); let app_weak = self.downgrade();
self.connect_button_action("button-stop", move |_| { self.connect_button_action("button-stop", move |_| {
@ -592,6 +570,7 @@ impl GPSApp {
let pipeline = app.pipeline.borrow(); let pipeline = app.pipeline.borrow();
let _ = pipeline.set_state(PipelineState::Stopped); let _ = pipeline.set_state(PipelineState::Stopped);
}); });
let app_weak = self.downgrade(); let app_weak = self.downgrade();
self.connect_button_action("button-clear", move |_| { self.connect_button_action("button-clear", move |_| {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
@ -614,7 +593,7 @@ impl GPSApp {
let menu: gio::MenuModel = app let menu: gio::MenuModel = app
.builder .builder
.object("graph_menu") .object("graph_menu")
.expect("Couldn't get menu model for graph"); .expect("Couldn't graph_menu");
pop_menu.set_menu_model(Some(&menu)); pop_menu.set_menu_model(Some(&menu));
let app_weak = app.downgrade(); let app_weak = app.downgrade();
@ -624,7 +603,6 @@ impl GPSApp {
GPSApp::display_plugin_list(&app); GPSApp::display_plugin_list(&app);
} }
); );
pop_menu.show(); pop_menu.show();
None None
}), }),
@ -795,13 +773,13 @@ impl GPSApp {
graph_view.remove_all_nodes(); graph_view.remove_all_nodes();
} }
fn save_graph(&self, filename: &str) -> anyhow::Result<(), Box<dyn error::Error>> { fn save_graph(&self, filename: &str) -> anyhow::Result<()> {
let graph_view = self.graphview.borrow_mut(); let graph_view = self.graphview.borrow_mut();
graph_view.render_xml(filename)?; graph_view.render_xml(filename)?;
Ok(()) Ok(())
} }
fn load_graph(&self, filename: &str) -> anyhow::Result<(), Box<dyn error::Error>> { fn load_graph(&self, filename: &str) -> anyhow::Result<()> {
self.clear_graph(); self.clear_graph();
let graph_view = self.graphview.borrow_mut(); let graph_view = self.graphview.borrow_mut();
graph_view.load_xml(filename)?; graph_view.load_xml(filename)?;

View file

@ -139,7 +139,7 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkButton" id="apply-plugin-properties"> <object class="GtkButton" id="button-apply-plugin-properties">
<property name="halign">end</property> <property name="halign">end</property>
<property name="hexpand">1</property> <property name="hexpand">1</property>
<property name="receives-default">1</property> <property name="receives-default">1</property>
@ -254,7 +254,7 @@
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="vexpand">True</property> <property name="vexpand">True</property>
<property name="child"> <property name="child">
<object class="GtkTreeView" id="favorites_list"> <object class="GtkTreeView" id="treeview-favorites">
</object> </object>
</property> </property>
</object> </object>
@ -264,7 +264,7 @@
<child> <child>
<object class="GtkScrolledWindow"> <object class="GtkScrolledWindow">
<property name="child"> <property name="child">
<object class="GtkTreeView" id="logger_list"/> <object class="GtkTreeView" id="treeview-logger"/>
</property> </property>
</object> </object>
</child> </child>

View file

@ -25,7 +25,6 @@ use gst::prelude::*;
use gstreamer as gst; use gstreamer as gst;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::error;
use std::fmt; use std::fmt;
use std::ops; use std::ops;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
@ -88,7 +87,7 @@ pub struct PipelineInner {
} }
impl Pipeline { impl Pipeline {
pub fn new() -> Result<Self, Box<dyn error::Error>> { pub fn new() -> anyhow::Result<Self> {
let pipeline = Pipeline(Rc::new(PipelineInner { let pipeline = Pipeline(Rc::new(PipelineInner {
pipeline: RefCell::new(None), pipeline: RefCell::new(None),
current_state: Cell::new(PipelineState::Stopped), current_state: Cell::new(PipelineState::Stopped),
@ -97,36 +96,58 @@ impl Pipeline {
Ok(pipeline) Ok(pipeline)
} }
pub fn create_pipeline(&self, description: &str) -> Result<(), Box<dyn error::Error>> { pub fn create_pipeline(&self, description: &str) -> anyhow::Result<()> {
GPS_INFO!("Creating pipeline {}", description); GPS_INFO!("Creating pipeline {}", description);
/* create playbin */ // Create pipeline from the description
let pipeline = gst::parse_launch(&description.to_string())?; let pipeline = gst::parse_launch(&description.to_string())?;
if let Ok(pipeline) = pipeline.downcast::<gst::Pipeline>() { if let Ok(pipeline) = pipeline.downcast::<gst::Pipeline>() {
//pipeline.set_property_message_forward(true);
let bus = pipeline.bus().expect("Pipeline had no bus"); let bus = pipeline.bus().expect("Pipeline had no bus");
let pipeline_weak = self.downgrade(); let pipeline_weak = self.downgrade();
bus.add_watch_local(move |_bus, msg| { bus.add_watch_local(move |_bus, msg| {
let pipeline = upgrade_weak!(pipeline_weak, glib::Continue(false)); let pipeline = upgrade_weak!(pipeline_weak, glib::Continue(false));
pipeline.on_pipeline_message(msg); pipeline.on_pipeline_message(msg);
glib::Continue(true) glib::Continue(true)
})?; })?;
*self.pipeline.borrow_mut() = Some(pipeline); *self.pipeline.borrow_mut() = Some(pipeline);
/* start playing */ /* start playing */
} else { } else {
GPS_ERROR!("Couldn't downcast pipeline") GPS_ERROR!("Can not create a proper pipeline from gstreamer parse_launch");
} }
Ok(()) Ok(())
} }
pub fn set_state(&self, state: PipelineState) -> Result<(), Box<dyn error::Error>> { pub fn start_pipeline(
&self,
graphview: &GraphView,
new_state: PipelineState,
) -> anyhow::Result<PipelineState> {
if self.state() == PipelineState::Stopped {
self.create_pipeline(&self.render_gst_launch(graphview))
.map_err(|err| {
GPS_ERROR!("Unable to start a pipeline: {}", err);
})
.unwrap();
self.set_state(new_state)
.map_err(|_| GPS_ERROR!("Unable to change state"))
.unwrap();
} else if self.state() == PipelineState::Paused {
self.set_state(PipelineState::Playing)
.map_err(|_| GPS_ERROR!("Unable to change state"))
.unwrap();
} else {
self.set_state(PipelineState::Paused)
.map_err(|_| GPS_ERROR!("Unable to change state"))
.unwrap();
}
Ok(self.state())
}
pub fn set_state(&self, new_state: PipelineState) -> anyhow::Result<PipelineState> {
if let Some(pipeline) = self.pipeline.borrow().to_owned() { if let Some(pipeline) = self.pipeline.borrow().to_owned() {
match state { match new_state {
PipelineState::Playing => pipeline.set_state(gst::State::Playing)?, PipelineState::Playing => pipeline.set_state(gst::State::Playing)?,
PipelineState::Paused => pipeline.set_state(gst::State::Paused)?, PipelineState::Paused => pipeline.set_state(gst::State::Paused)?,
PipelineState::Stopped => { PipelineState::Stopped => {
@ -134,9 +155,9 @@ impl Pipeline {
gst::StateChangeSuccess::Success gst::StateChangeSuccess::Success
} }
}; };
self.current_state.set(state); self.current_state.set(new_state);
} }
Ok(()) Ok(new_state)
} }
pub fn state(&self) -> PipelineState { pub fn state(&self) -> PipelineState {
@ -181,7 +202,7 @@ impl Pipeline {
}; };
} }
pub fn elements_list() -> Result<Vec<ElementInfo>, Box<dyn error::Error>> { pub fn elements_list() -> anyhow::Result<Vec<ElementInfo>> {
let registry = gst::Registry::get(); let registry = gst::Registry::get();
let mut elements: Vec<ElementInfo> = Vec::new(); let mut elements: Vec<ElementInfo> = Vec::new();
let plugins = gst::Registry::plugin_list(&registry); let plugins = gst::Registry::plugin_list(&registry);
@ -204,24 +225,15 @@ impl Pipeline {
Ok(elements) Ok(elements)
} }
pub fn element_feature( fn element_feature(element_name: &str) -> Option<gst::PluginFeature> {
element_name: &str,
) -> anyhow::Result<gst::PluginFeature, Box<dyn error::Error>> {
let registry = gst::Registry::get(); let registry = gst::Registry::get();
let feature = gst::Registry::find_feature( gst::Registry::find_feature(&registry, element_name, gst::ElementFactory::static_type())
&registry,
element_name,
gst::ElementFactory::static_type(),
)
.expect("Unable to find the element name");
Ok(feature)
} }
pub fn element_description( pub fn element_description(element_name: &str) -> anyhow::Result<String> {
element_name: &str,
) -> anyhow::Result<String, Box<dyn error::Error>> {
let mut desc = String::from(""); let mut desc = String::from("");
let feature = Pipeline::element_feature(element_name)?; 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>() { if let Ok(factory) = feature.downcast::<gst::ElementFactory>() {
desc.push_str("<b>Factory details:</b>\n"); desc.push_str("<b>Factory details:</b>\n");
@ -330,15 +342,13 @@ impl Pipeline {
} }
} }
pub fn element_properties( pub fn element_properties(element_name: &str) -> anyhow::Result<HashMap<String, String>> {
element_name: &str,
) -> anyhow::Result<HashMap<String, String>, Box<dyn error::Error>> {
let mut properties_list = HashMap::new(); let mut properties_list = HashMap::new();
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature"); let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
let factory = feature let factory = feature
.downcast::<gst::ElementFactory>() .downcast::<gst::ElementFactory>()
.expect("Factory not found"); .expect("Unable to get the factory from the feature");
let element = factory.create(None)?; let element = factory.create(None)?;
let params = element.class().list_properties(); let params = element.class().list_properties();
@ -364,8 +374,10 @@ impl Pipeline {
let factory = feature let factory = feature
.downcast::<gst::ElementFactory>() .downcast::<gst::ElementFactory>()
.expect("Factory not found"); .expect("Unable to get the factory from the feature");
let element = factory.create(None).expect("Unable to get factory"); let element = factory
.create(None)
.expect("Unable to create an element from the feature");
match element.dynamic_cast::<gst::URIHandler>() { match element.dynamic_cast::<gst::URIHandler>() {
Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src, Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src,
Err(_e) => false, Err(_e) => false,
@ -373,7 +385,7 @@ impl Pipeline {
} }
// Render graph methods // Render graph methods
pub fn process_node( fn process_gst_node(
&self, &self,
graphview: &GraphView, graphview: &GraphView,
node: &Node, node: &Node,
@ -401,7 +413,7 @@ impl Pipeline {
description.push_str(&format!("{}. ", node.unique_name())); description.push_str(&format!("{}. ", node.unique_name()));
} else { } else {
description = description =
self.process_node(graphview, &node, elements, description.clone()); self.process_gst_node(graphview, &node, elements, description.clone());
} }
} }
} }
@ -415,7 +427,7 @@ impl Pipeline {
let mut description = String::from(""); let mut description = String::from("");
for source_node in source_nodes { for source_node in source_nodes {
description = description =
self.process_node(graphview, &source_node, &mut elements, description.clone()); self.process_gst_node(graphview, &source_node, &mut elements, description.clone());
} }
description description
} }

View file

@ -61,7 +61,7 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
let dialog: Dialog = app let dialog: Dialog = app
.builder .builder
.object("dialog-plugin-list") .object("dialog-plugin-list")
.expect("Couldn't get window"); .expect("Couldn't get the dialog-plugin-list window");
if app.plugin_list_initialized.get().is_none() { if app.plugin_list_initialized.get().is_none() {
dialog.set_title(Some("Plugin list")); dialog.set_title(Some("Plugin list"));
@ -70,13 +70,13 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
let text_view: TextView = app let text_view: TextView = app
.builder .builder
.object("textview-plugin-list") .object("textview-plugin-list")
.expect("Couldn't get window"); .expect("Couldn't get textview-plugin-list window");
let text_buffer: TextBuffer = text_view.buffer(); let text_buffer: TextBuffer = text_view.buffer();
let tree: TreeView = app let tree: TreeView = app
.builder .builder
.object("treeview-plugin-list") .object("treeview-plugin-list")
.expect("Couldn't get window"); .expect("Couldn't get treeview-plugin-list window");
if tree.n_columns() < 2 { if tree.n_columns() < 2 {
append_column(&tree, 0); append_column(&tree, 0);
append_column(&tree, 1); append_column(&tree, 1);
@ -94,8 +94,8 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
let element_name = model let element_name = model
.get(&iter, 1) .get(&iter, 1)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Unable to get the treeview selection, column 1");
let description = Pipeline::element_description(&element_name).expect("Unable to get element list from GStreamer"); let description = Pipeline::element_description(&element_name).expect("Unable to get element description from GStreamer");
text_buffer.set_text(""); text_buffer.set_text("");
text_buffer.insert_markup(&mut text_buffer.end_iter(), &description); text_buffer.insert_markup(&mut text_buffer.end_iter(), &description);
} }
@ -110,14 +110,12 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
let element_name = model let element_name = model
.get(&iter, 1) .get(&iter, 1)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Unable to get the treeview selection, column 1");
app.add_new_element(&element_name); app.add_new_element(&element_name);
} }
}), }),
); );
app.plugin_list_initialized app.plugin_list_initialized.set(true).unwrap();
.set(true)
.expect("Should never happen");
} }
dialog.show(); dialog.show();
@ -127,7 +125,7 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
let dialog: Dialog = app let dialog: Dialog = app
.builder .builder
.object("dialog-plugin-properties") .object("dialog-plugin-properties")
.expect("Couldn't get window"); .expect("Couldn't get dialog-plugin-properties");
dialog.set_title(Some(&format!("{} properties", element_name))); dialog.set_title(Some(&format!("{} properties", element_name)));
dialog.set_default_size(640, 480); dialog.set_default_size(640, 480);
@ -136,7 +134,7 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
let properties_box: Box = app let properties_box: Box = app
.builder .builder
.object("box-plugin-properties") .object("box-plugin-properties")
.expect("Couldn't get window"); .expect("Couldn't get box-plugin-properties");
let update_properties: Rc<RefCell<HashMap<String, String>>> = let update_properties: Rc<RefCell<HashMap<String, String>>> =
Rc::new(RefCell::new(HashMap::new())); Rc::new(RefCell::new(HashMap::new()));
let properties = Pipeline::element_properties(element_name).unwrap(); let properties = Pipeline::element_properties(element_name).unwrap();
@ -163,8 +161,8 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32)
} }
let properties_apply_btn: Button = app let properties_apply_btn: Button = app
.builder .builder
.object("apply-plugin-properties") .object("button-apply-plugin-properties")
.expect("Couldn't get window"); .expect("Couldn't get button-apply-plugin-properties");
let app_weak = app.downgrade(); let app_weak = app.downgrade();
properties_apply_btn.connect_clicked( properties_apply_btn.connect_clicked(