diff options
Diffstat (limited to 'gstreamer-properties/gstreamer-properties.c')
-rw-r--r-- | gstreamer-properties/gstreamer-properties.c | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/gstreamer-properties/gstreamer-properties.c b/gstreamer-properties/gstreamer-properties.c new file mode 100644 index 0000000..4e9f0c9 --- /dev/null +++ b/gstreamer-properties/gstreamer-properties.c @@ -0,0 +1,695 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* gstreamer-properties.c + * Copyright (C) 2002 Jan Schmidt + * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net> + * + * Written by: Jan Schmidt <[email protected]> + * + * 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 2, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <locale.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> +#include <gst/gst.h> +#include <gst/interfaces/propertyprobe.h> +#include "gstreamer-properties-structs.h" +#include "pipeline-tests.h" + +#define WID(s) gtk_builder_get_object (builder, s) +static GtkBuilder *builder; +static GtkDialog *main_window; +static MateConfClient *mateconf_client; /* NULL */ + +static gchar pipeline_editor_property[] = "gstp-editor"; + +static gchar * +gst_properties_mateconf_get_full_key (const gchar * key) +{ + return g_strdup_printf ("/system/gstreamer/%d.%d/%s", + GST_VERSION_MAJOR, GST_VERSION_MINOR, key); +} + +gchar * +gst_properties_mateconf_get_string (const gchar * key) +{ + GError *error = NULL; + gchar *value = NULL; + gchar *full_key; + + full_key = gst_properties_mateconf_get_full_key (key); + + value = mateconf_client_get_string (mateconf_client, full_key, &error); + g_free (full_key); + + if (error) { + g_warning ("%s() error: %s", G_STRFUNC, error->message); + g_error_free (error); + return NULL; + } + + return value; +} + +void +gst_properties_mateconf_set_string (const gchar * key, const gchar * value) +{ + GError *error = NULL; + gchar *full_key; + + full_key = gst_properties_mateconf_get_full_key (key); + + mateconf_client_set_string (mateconf_client, full_key, value, &error); + g_free (full_key); + + if (error) { + g_warning ("%s() error: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +static void +dialog_response (GtkDialog * widget, gint response_id, GtkBuilder * dialog) +{ + GError *error = NULL; + + if (response_id == GTK_RESPONSE_HELP) + gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), + "ghelp:gstreamer-properties", + gtk_get_current_event_time (), + &error); + else + gtk_main_quit (); + + if (error) { + g_warning ("%s() error: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +static void +test_button_clicked (GtkButton * button, gpointer user_data) +{ + GSTPPipelineEditor *editor = (GSTPPipelineEditor *) (user_data); + GSTPPipelineDescription *pipeline_desc = + editor->pipeline_desc + editor->cur_pipeline_index; + if (pipeline_desc->is_custom) { + GtkEntry *entry = editor->entry; + + pipeline_desc->pipeline = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + } + + user_test_pipeline (builder, GTK_WINDOW (main_window), pipeline_desc); + + if (pipeline_desc->is_custom) { + g_free (pipeline_desc->pipeline); + pipeline_desc->pipeline = NULL; + } +} + +static void +pipeline_devicemenu_changed (GtkComboBox *devicemenu, gpointer user_data) +{ + GSTPPipelineEditor *editor = (GSTPPipelineEditor *) (user_data); + GSTPPipelineDescription *pipeline_desc = NULL; + GtkTreeIter iter; + GtkTreeModel *model = gtk_combo_box_get_model (devicemenu); + gchar *devicename = NULL; + gboolean active; + + /* Determine which option changed, retrieve the pipeline desc, + * and call update_from_option */ + + if (model == NULL) + return; + + active = gtk_combo_box_get_active_iter (devicemenu, &iter); + + if (!active) + return; + + gtk_tree_model_get (model, &iter, 1, &pipeline_desc, 2, &devicename, -1); + + if (pipeline_desc == NULL) + return; + + pipeline_desc->device = devicename; + + if (pipeline_desc->is_custom == FALSE) { + gchar *pipeline = gst_pipeline_string_from_desc (pipeline_desc); + + if (pipeline) + gtk_entry_set_text (editor->entry, pipeline); + gtk_widget_set_sensitive (GTK_WIDGET (editor->entry), FALSE); + + /* Update MateConf */ + gst_properties_mateconf_set_string (editor->mateconf_key, pipeline); + } else { + gtk_widget_set_sensitive (GTK_WIDGET (editor->entry), TRUE); + } +} + +static void +update_device_menu (GSTPPipelineEditor * editor, + GSTPPipelineDescription * pipeline_desc) +{ + /* Lots of gstreamer stuff */ + GstElementFactory *factory; + GstElement *element; + GstPropertyProbe *probe; + const GParamSpec *pspec; + GObjectClass *klass; + const gchar *longname; + + if (editor->devicemenu == NULL) { + GtkCellRenderer *cellrenderer = gtk_cell_renderer_text_new (); + + editor->devicemenu = GTK_COMBO_BOX (WID (editor->devicemenu_name)); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editor->devicemenu), + cellrenderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (editor->devicemenu), + cellrenderer, "text", 0); + + g_object_set_data (G_OBJECT (editor->devicemenu), pipeline_editor_property, + (gpointer) (editor)); + g_signal_connect (G_OBJECT (editor->devicemenu), "changed", + (GCallback) pipeline_devicemenu_changed, (gpointer) (editor)); + } + + if (editor->devicemenu) { + gchar *insensitive_label = g_strdup(_("None")); + gboolean devices_added = FALSE; + gboolean preselect = FALSE; + GtkTreeIter preselection; + /* Use a pointer for the devicename (col 3), the capplet seems to avoid + * string allocation/deallocation this way */ + GtkListStore *store = gtk_list_store_new (3, G_TYPE_STRING, + G_TYPE_POINTER, G_TYPE_POINTER); + + gtk_widget_set_sensitive (GTK_WIDGET (editor->devicemenu), FALSE); + + gtk_combo_box_set_model (editor->devicemenu, NULL); + + if (pipeline_desc->is_custom == FALSE) { + + /* first see if we can actually create a device here */ + factory = gst_element_factory_find (pipeline_desc->pipeline); + if (!factory) { + GST_WARNING ("Failed to find factory for pipeline '%s'", + pipeline_desc->pipeline); + // return; + } + else { + element = gst_element_factory_create (factory, "test"); + longname = gst_element_factory_get_longname (factory); + if (!element) { + GST_WARNING ("Failed to create instance of factory '%s' (%s)", + longname, GST_PLUGIN_FEATURE (factory)->name); + //return; + } + else { + klass = G_OBJECT_GET_CLASS (element); + + /* do we have a "device" property? */ + if (!g_object_class_find_property (klass, "device") || + !GST_IS_PROPERTY_PROBE (element) || + !(probe = GST_PROPERTY_PROBE (element)) || + !(pspec = gst_property_probe_get_property (probe, "device"))) { + GST_DEBUG ("Found source '%s' (%s) - no device", + longname, GST_PLUGIN_FEATURE (factory)->name); + g_free (insensitive_label); + /* Element does not support setting devices */ + insensitive_label = g_strdup(_("Unsupported")); + } else { + gint n; + gchar *name; + GValueArray *array; + + /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */ + if (g_object_class_find_property (klass, "autoprobe")) { + g_object_set (G_OBJECT (element), "autoprobe", FALSE, NULL); + if (g_object_class_find_property (klass, "autoprobe-fps")) { + g_object_set (G_OBJECT (element), "autoprobe-fps", FALSE, NULL); + } + } + + array = gst_property_probe_probe_and_get_values (probe, pspec); + if (array != NULL) { + GtkTreeIter iter; + + /* default device item, so we can let the element handle it */ + if (array->n_values > 0) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, _("Default"), + 1, (gpointer) pipeline_desc, + 2, NULL, -1); + + devices_added = TRUE; + + // Preselect this to simulate GtkOptionMenu behavior + preselect = TRUE; + preselection = iter; + + gtk_widget_set_sensitive (GTK_WIDGET (editor->devicemenu), TRUE); + } + + for (n = 0; n < array->n_values; n++) { + GValue *device; + // GstCaps *caps; + + device = g_value_array_get_nth (array, n); + g_object_set_property (G_OBJECT (element), "device", device); + + if (gst_element_set_state (element, GST_STATE_READY) != + GST_STATE_CHANGE_SUCCESS) { + GST_WARNING + ("Found source '%s' (%s) - device %s failed to open", longname, + GST_PLUGIN_FEATURE (factory)->name, + g_value_get_string (device)); + continue; + } + + g_object_get (G_OBJECT (element), "device-name", &name, NULL); + // caps = gst_pad_get_caps (gst_element_get_pad (element, "src")); + + if (name == NULL) + name = _("Unknown"); + + GST_DEBUG ("Found source '%s' (%s) - device %s '%s'", + longname, GST_PLUGIN_FEATURE (factory)->name, + g_value_get_string (device), name); + + gst_element_set_state (element, GST_STATE_NULL); + + /* Add device to devicemenu */ + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, name, + 1, (gpointer) pipeline_desc, + 2, (gpointer) g_value_get_string (device), + -1); + + devices_added = TRUE; + if (pipeline_desc->device != NULL && + !strcmp (pipeline_desc->device, g_value_get_string(device))) + { + preselect = TRUE; + preselection = iter; + } + } + } + } + gst_object_unref (GST_OBJECT (element)); + } + gst_object_unref (GST_OBJECT (factory)); + } + } + + /* No devices to choose -> "None" */ + if (!devices_added) { + GtkTreeIter iter; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, insensitive_label, + 1, (gpointer) pipeline_desc, + 2, NULL, -1); + preselect = TRUE; + preselection = iter; + } + + gtk_combo_box_set_model (editor->devicemenu, GTK_TREE_MODEL (store)); + + if (preselect) { + gtk_combo_box_set_active_iter (editor->devicemenu, &preselection); + } + + g_free (insensitive_label); + } +} + +static void +update_from_option (GSTPPipelineEditor * editor, + GSTPPipelineDescription * pipeline_desc) +{ + /* optionmenu changed, update the edit box, + * and the appropriate MateConf key */ + /* FIXME g_return_if_fail(editor); */ + /* g_return_if_fail(pipeline_desc); */ + + editor->cur_pipeline_index = pipeline_desc->index; + + /* Update device list */ + update_device_menu(editor, pipeline_desc); + + if (pipeline_desc->is_custom == FALSE) { + gchar *pipeline = gst_pipeline_string_from_desc(pipeline_desc); + if (pipeline) + gtk_entry_set_text (editor->entry, pipeline); + gtk_widget_set_sensitive (GTK_WIDGET (editor->entry), FALSE); + + /* Update MateConf */ + gst_properties_mateconf_set_string (editor->mateconf_key, pipeline); + } else { + gtk_widget_set_sensitive (GTK_WIDGET (editor->entry), TRUE); + } +} + +static gboolean +set_menuitem_by_pipeline (GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + GSTPPipelineEditor *editor = (GSTPPipelineEditor *) (data); + GSTPPipelineDescription *pipeline_desc; + + gtk_tree_model_get (model, iter, 1, &pipeline_desc, -1); + if (pipeline_desc == (editor->pipeline_desc + editor->cur_pipeline_index)) { + gtk_combo_box_set_active_iter (editor->optionmenu, iter); + return TRUE; + } + + return FALSE; +} + +static void +update_from_mateconf (GSTPPipelineEditor * editor, const gchar * pipeline_str) +{ + /* Iterate over the pipelines in the editor, and locate the one + matching this pipeline_str. If none, then use 'Custom' entry */ + int i = 0; + gint custom_desc = -1; + + /* g_return_if_fail (editor != NULL); */ + gchar **pipeline_nodes = g_strsplit (pipeline_str, " ", -1); + gchar *pipeline_device = NULL; + if (pipeline_nodes == NULL) { + pipeline_nodes[0] = (gchar*)pipeline_str; + pipeline_device = pipeline_nodes[1]; + } + + editor->cur_pipeline_index = -1; + for (i = 0; i < editor->n_pipeline_desc; i++) { + GSTPPipelineDescription *pipeline_desc = editor->pipeline_desc + i; + + if (pipeline_desc->is_custom == TRUE) { + custom_desc = i; + } else if (!strcmp (gst_pipeline_string_from_desc (pipeline_desc), pipeline_str)) { + editor->cur_pipeline_index = i; + break; + } else if (!strcmp (gst_pipeline_string_from_desc (pipeline_desc), pipeline_nodes[0])) { + editor->cur_pipeline_index = i; + pipeline_desc->device = gst_pipeline_string_get_property_value(pipeline_str, "device"); + break; + } + } + + if (editor->cur_pipeline_index < 0) { + editor->cur_pipeline_index = custom_desc; + if (custom_desc >= 0) { + gtk_entry_set_text (editor->entry, pipeline_str); + if (pipeline_str == NULL || *pipeline_str == '\0') + gtk_widget_set_sensitive (GTK_WIDGET (editor->test_button), FALSE); + } + } + + if (editor->cur_pipeline_index >= 0) { + GtkTreeModel *model = gtk_combo_box_get_model (editor->optionmenu); + + gtk_tree_model_foreach (model, set_menuitem_by_pipeline, editor); + update_from_option (editor, + editor->pipeline_desc + editor->cur_pipeline_index); + } + + g_strfreev(pipeline_nodes); +} + +static void +pipeline_option_changed (GtkComboBox *optionmenu, gpointer user_data) +{ + GtkTreeIter iter; + GtkTreeModel *model = gtk_combo_box_get_model (optionmenu); + gboolean active; + GSTPPipelineEditor *editor = (GSTPPipelineEditor *) (user_data); + GSTPPipelineDescription *pipeline_desc = NULL; + + /* Determine which option changed, retrieve the pipeline desc, + * and call update_from_option */ + active = gtk_combo_box_get_active_iter (optionmenu, &iter); + g_return_if_fail (active == TRUE); + gtk_tree_model_get (model, &iter, 1, &pipeline_desc, -1); + + update_from_option (editor, pipeline_desc); +} + +static void +entry_changed (GtkEditable * editable, gpointer user_data) +{ + GSTPPipelineEditor *editor = (GSTPPipelineEditor *) (user_data); + const gchar *new_text = gtk_entry_get_text (GTK_ENTRY (editable)); + + if (new_text == NULL || *new_text == '\0') { + /* disable test button */ + gtk_widget_set_sensitive (GTK_WIDGET (editor->test_button), FALSE); + } else { + gtk_widget_set_sensitive (GTK_WIDGET (editor->test_button), TRUE); + } + /* Update MateConf */ + gst_properties_mateconf_set_string (editor->mateconf_key, new_text); +} + +static gboolean +element_available (const gchar * pipeline) +{ + gboolean res = FALSE; + gchar *p, *first_space; + + if (pipeline == NULL || *pipeline == '\0') + return FALSE; + + p = g_strdup (pipeline); + + g_strstrip (p); + + /* skip the check and pretend all is fine if it's something that does + * not look like an element name (e.g. parentheses to signify a bin) */ + if (!g_ascii_isalpha (*p)) { + g_free (p); + return TRUE; + } + + /* just the element name, no arguments */ + first_space = strchr (p, ' '); + if (first_space != NULL) + *first_space = '\0'; + + /* check if element is available */ + res = gst_default_registry_check_feature_version (p, GST_VERSION_MAJOR, + GST_VERSION_MINOR, 0); + + g_free (p); + return res; +} + +static GtkComboBox * +create_pipeline_menu (GtkBuilder * dialog, GSTPPipelineEditor * editor) +{ + GtkComboBox *option = NULL; + gint i; + GSTPPipelineDescription *pipeline_desc = editor->pipeline_desc; + + + option = GTK_COMBO_BOX (WID (editor->optionmenu_name)); + if (option) { + GtkListStore *list_store = gtk_list_store_new (2, G_TYPE_STRING, + G_TYPE_POINTER); + GtkCellRenderer *cellrenderer = gtk_cell_renderer_text_new (); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (option), cellrenderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (option), cellrenderer, + "text", 0); + for (i = 0; i < editor->n_pipeline_desc; i++) { + GtkTreeIter iter; + + if (element_available (pipeline_desc[i].pipeline)) { + GstElement *pipeline; + GError *error = NULL; + + pipeline = gst_parse_launch (pipeline_desc[i].pipeline, &error); + if (pipeline != NULL) { + gst_object_unref (pipeline); + } + if (error != NULL) { + g_error_free (error); + continue; + } + } else if (pipeline_desc[i].pipeline != NULL) { + /* FIXME: maybe we should show those in the + * combo box, but make them insensitive? Or is + * that more confusing than helpful for users? */ + g_message ("Skipping unavailable plugin '%s'", + pipeline_desc[i].pipeline); + continue; + } else { + /* This is probably the 'Custom' pipeline */ + } + + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, + 0, gettext (pipeline_desc[i].name), + 1, (gpointer) & pipeline_desc[i], -1); + pipeline_desc[i].index = i; + } + + gtk_combo_box_set_model (option, GTK_TREE_MODEL (list_store)); + } + + return option; +} + +static void +init_pipeline_editor (GtkBuilder * dialog, GSTPPipelineEditor * editor) +{ + gchar *mateconf_init_pipe = NULL; + + /* g_return_if_fail(editor != NULL); */ + + editor->optionmenu = create_pipeline_menu (dialog, editor); + editor->entry = GTK_ENTRY (WID (editor->entry_name)); + editor->test_button = GTK_BUTTON (WID (editor->test_button_name)); + + /* g_return_if_fail (editor->entry && editor->optionmenu && editor->test_button); */ + if (!(editor->entry && editor->optionmenu && editor->test_button)) + return; + + g_object_set_data (G_OBJECT (editor->optionmenu), pipeline_editor_property, + (gpointer) (editor)); + g_signal_connect (G_OBJECT (editor->optionmenu), "changed", + (GCallback) pipeline_option_changed, (gpointer) (editor)); + g_object_set_data (G_OBJECT (editor->entry), pipeline_editor_property, + (gpointer) (editor)); + g_signal_connect (G_OBJECT (editor->entry), "changed", + (GCallback) entry_changed, (gpointer) (editor)); + g_object_set_data (G_OBJECT (editor->test_button), pipeline_editor_property, + (gpointer) (editor)); + g_signal_connect (G_OBJECT (editor->test_button), "clicked", + (GCallback) test_button_clicked, (gpointer) (editor)); + + mateconf_init_pipe = gst_properties_mateconf_get_string (editor->mateconf_key); + + if (mateconf_init_pipe) { + update_from_mateconf (editor, mateconf_init_pipe); + g_free (mateconf_init_pipe); + } +} + +static void +create_dialog (void) +{ + int i = 0; + + for (i = 0; i < pipeline_editors_count; i++) { + init_pipeline_editor (builder, pipeline_editors + i); + } + + main_window = GTK_DIALOG (WID ("gst_properties_dialog")); + if (!main_window) { + /* Fatal error */ + GtkWidget *dialog; + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Failure instantiating main window")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + return; + } + + g_signal_connect (G_OBJECT (main_window), + "response", (GCallback) dialog_response, builder); + gtk_window_set_icon_name (GTK_WINDOW (main_window), "gstreamer-properties"); + gtk_widget_show (GTK_WIDGET (main_window)); +} + +int +main (int argc, char **argv) +{ + GOptionContext *ctx; + GError *error = NULL; + + g_thread_init (NULL); + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + ctx = g_option_context_new ("gstreamer-properties"); + g_option_context_add_group (ctx, gtk_get_option_group (TRUE)); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + + if (! g_option_context_parse (ctx, &argc, &argv, &error)) { + g_printerr ("option parsing failed: %s\n", error->message); + g_error_free (error); + return EXIT_FAILURE; + } + + builder = gtk_builder_new (); + + /* FIXME: hardcode uninstalled path here */ + if (g_file_test ("gstreamer-properties.ui", G_FILE_TEST_EXISTS) == TRUE) { + gtk_builder_add_from_file (builder, "gstreamer-properties.ui", &error); + } else if (g_file_test (GSTPROPS_UIDIR "/gstreamer-properties.ui", + G_FILE_TEST_EXISTS) == TRUE) { + gtk_builder_add_from_file (builder, GSTPROPS_UIDIR "/gstreamer-properties.ui", &error); + } + + mateconf_client = mateconf_client_get_default (); + + if (error) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Failed to load UI file; please check your installation.")); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_clear_error (&error); + + exit (1); + } + + create_dialog (); + + if (main_window) + gtk_main (); + + g_object_unref (mateconf_client); + + return 0; +} |