mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-11-25 10:30:59 +00:00
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:
parent
2a7137e6dc
commit
198847cb54
5 changed files with 132 additions and 135 deletions
11
TODO.md
11
TODO.md
|
@ -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
|
||||||
|
|
138
src/app.rs
138
src/app.rs
|
@ -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: >k::Application) -> anyhow::Result<GPSApp, Box<dyn error::Error>> {
|
fn new(application: >k::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)?;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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(®istry);
|
let plugins = gst::Registry::plugin_list(®istry);
|
||||||
|
@ -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(®istry, element_name, gst::ElementFactory::static_type())
|
||||||
®istry,
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue