mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 07:42:23 +02:00
Merge pull request #530 from Rafostar/pipeline-preview-launcher
Ability to show GStreamer pipeline
This commit is contained in:
11
meson.build
11
meson.build
@@ -89,6 +89,12 @@ libadwaita_dep = dependency('libadwaita-1',
|
||||
peas_dep = dependency('libpeas-2',
|
||||
required: false,
|
||||
)
|
||||
cgraph_dep = dependency('libcgraph',
|
||||
required: false,
|
||||
)
|
||||
gvc_dep = dependency('libgvc',
|
||||
required: false,
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
libm = cc.find_library('m', required: false)
|
||||
@@ -160,3 +166,8 @@ if build_clapper
|
||||
summary(name, clapper_available_features.contains(name) ? 'Yes' : 'No', section: 'Features')
|
||||
endforeach
|
||||
endif
|
||||
if build_clapperapp
|
||||
foreach name : clapperapp_possible_functionalities
|
||||
summary(name, clapperapp_available_functionalities.contains(name) ? 'Yes' : 'No', section: 'Functionalities')
|
||||
endforeach
|
||||
endif
|
||||
|
@@ -41,6 +41,11 @@ option('enhancers-loader',
|
||||
value: 'enabled',
|
||||
description: 'Ability to load libpeas based plugins that enhance capabilities'
|
||||
)
|
||||
option('pipeline-preview',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Ability to preview GStreamer pipeline in clapper-app'
|
||||
)
|
||||
|
||||
# Features
|
||||
option('discoverer',
|
||||
|
@@ -66,6 +66,7 @@
|
||||
"testing/dav1d.json",
|
||||
"testing/gstreamer.json",
|
||||
"testing/gst-plugins-rs.json",
|
||||
"testing/graphviz.json",
|
||||
{
|
||||
"name": "clapper",
|
||||
"buildsystem": "meson",
|
||||
|
@@ -51,6 +51,7 @@
|
||||
"flathub/lib/libmicrodns.json",
|
||||
"flathub/lib/libpeas.json",
|
||||
"flathub/gstreamer-1.0/gstreamer.json",
|
||||
"testing/graphviz.json",
|
||||
{
|
||||
"name": "clapper",
|
||||
"buildsystem": "meson",
|
||||
|
38
pkgs/flatpak/testing/graphviz.json
Normal file
38
pkgs/flatpak/testing/graphviz.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "graphviz",
|
||||
"buildsystem": "autotools",
|
||||
"config-opts": [
|
||||
"--without-x",
|
||||
"--without-gtk",
|
||||
"--without-gtkgl",
|
||||
"--without-gtkglext",
|
||||
"--without-gdk",
|
||||
"--without-gdk-pixbuf",
|
||||
"--without-qt",
|
||||
"--disable-static",
|
||||
"--disable-sharp",
|
||||
"--disable-go",
|
||||
"--disable-guile",
|
||||
"--disable-java",
|
||||
"--disable-javascript",
|
||||
"--disable-lua",
|
||||
"--disable-perl",
|
||||
"--disable-php",
|
||||
"--disable-python",
|
||||
"--disable-r",
|
||||
"--disable-ruby",
|
||||
"--disable-tcl"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin",
|
||||
"/share/man",
|
||||
"/share/graphviz"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/12.2.1/graphviz-12.2.1.tar.gz",
|
||||
"sha256": "242bc18942eebda6db4039f108f387ec97856fc91ba47f21e89341c34b554df8"
|
||||
}
|
||||
]
|
||||
}
|
@@ -41,6 +41,7 @@ struct _ClapperAppApplication
|
||||
GtkApplication parent;
|
||||
|
||||
GSettings *settings;
|
||||
GCancellable *cancellable;
|
||||
|
||||
gboolean need_init_state;
|
||||
};
|
||||
@@ -61,6 +62,12 @@ struct ClapperPluginData
|
||||
struct ClapperPluginFeatureData features[10];
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ClapperAppApplication *app;
|
||||
guint id;
|
||||
} ClapperAppWindowData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *action;
|
||||
@@ -317,6 +324,19 @@ _assemble_initial_state (GtkWindow *window)
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
_show_error_dialog (GError *error, GtkWindow *parent)
|
||||
{
|
||||
AdwDialog *dialog;
|
||||
|
||||
dialog = adw_alert_dialog_new ("Error", error->message);
|
||||
adw_alert_dialog_add_response (ADW_ALERT_DIALOG (dialog), "close", _("Close"));
|
||||
adw_alert_dialog_set_default_response (ADW_ALERT_DIALOG (dialog), "close");
|
||||
adw_alert_dialog_set_close_response (ADW_ALERT_DIALOG (dialog), "close");
|
||||
|
||||
adw_dialog_present (dialog, GTK_WIDGET (parent));
|
||||
}
|
||||
|
||||
static void
|
||||
add_files (GSimpleAction *action, GVariant *param, gpointer user_data)
|
||||
{
|
||||
@@ -395,6 +415,102 @@ show_info (GSimpleAction *action, GVariant *param, gpointer user_data)
|
||||
gtk_window_present (GTK_WINDOW (info_window));
|
||||
}
|
||||
|
||||
static void
|
||||
_launch_pipeline_cb (GtkFileLauncher *launcher,
|
||||
GAsyncResult *res, ClapperAppWindowData *win_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gtk_file_launcher_launch_finish (launcher, res, &error)) {
|
||||
if (error->domain != GTK_DIALOG_ERROR || error->code != GTK_DIALOG_ERROR_DISMISSED) {
|
||||
GtkWindow *window;
|
||||
|
||||
GST_ERROR ("Could not launch pipeline preview, reason: %s",
|
||||
GST_STR_NULL (error->message));
|
||||
|
||||
if ((window = gtk_application_get_window_by_id (
|
||||
GTK_APPLICATION (win_data->app), win_data->id)))
|
||||
_show_error_dialog (error, window);
|
||||
}
|
||||
g_error_free (error);
|
||||
}
|
||||
g_free (win_data);
|
||||
}
|
||||
|
||||
static void
|
||||
_show_pipeline_cb (GObject *source G_GNUC_UNUSED,
|
||||
GAsyncResult *res, ClapperAppWindowData *win_data)
|
||||
{
|
||||
GTask *task = G_TASK (res);
|
||||
GtkWindow *window;
|
||||
GFile *svg_file;
|
||||
GError *error = NULL;
|
||||
|
||||
svg_file = (GFile *) g_task_propagate_pointer (task, &error);
|
||||
window = gtk_application_get_window_by_id (
|
||||
GTK_APPLICATION (win_data->app), win_data->id);
|
||||
|
||||
if (error) {
|
||||
if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED) {
|
||||
GST_ERROR ("Could not create pipeline graph file, reason: %s",
|
||||
GST_STR_NULL (error->message));
|
||||
if (window)
|
||||
_show_error_dialog (error, window);
|
||||
}
|
||||
g_error_free (error);
|
||||
g_free (win_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (window) {
|
||||
GtkFileLauncher *launcher = gtk_file_launcher_new (svg_file);
|
||||
|
||||
#if GTK_CHECK_VERSION(4,12,0)
|
||||
gtk_file_launcher_set_always_ask (launcher, TRUE);
|
||||
#endif
|
||||
|
||||
gtk_file_launcher_launch (launcher, window, NULL,
|
||||
(GAsyncReadyCallback) _launch_pipeline_cb, win_data);
|
||||
g_object_unref (launcher);
|
||||
} else {
|
||||
g_free (win_data);
|
||||
}
|
||||
|
||||
g_object_unref (svg_file);
|
||||
}
|
||||
|
||||
static void
|
||||
show_pipeline (GSimpleAction *action, GVariant *param, gpointer user_data)
|
||||
{
|
||||
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (user_data);
|
||||
GtkApplication *gtk_app = GTK_APPLICATION (self);
|
||||
GtkWindow *window;
|
||||
ClapperAppWindowData *win_data;
|
||||
|
||||
window = gtk_application_get_active_window (gtk_app);
|
||||
|
||||
while (window && !CLAPPER_APP_IS_WINDOW (window))
|
||||
window = gtk_window_get_transient_for (window);
|
||||
|
||||
if (G_UNLIKELY (window == NULL))
|
||||
return;
|
||||
|
||||
if (self->cancellable) {
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_object_unref (self->cancellable);
|
||||
}
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
win_data = g_new (ClapperAppWindowData, 1);
|
||||
win_data->app = self;
|
||||
win_data->id = gtk_application_window_get_id (GTK_APPLICATION_WINDOW (window));
|
||||
|
||||
clapper_app_utils_create_pipeline_svg_file_async (
|
||||
clapper_app_window_get_player (CLAPPER_APP_WINDOW (window)),
|
||||
self->cancellable, (GAsyncReadyCallback) _show_pipeline_cb, win_data);
|
||||
}
|
||||
|
||||
static void
|
||||
show_about (GSimpleAction *action, GVariant *param, gpointer user_data)
|
||||
{
|
||||
@@ -714,6 +830,7 @@ clapper_app_application_constructed (GObject *object)
|
||||
{ "clear-queue", clear_queue, NULL, NULL, NULL },
|
||||
{ "new-window", new_window, NULL, NULL, NULL },
|
||||
{ "info", show_info, NULL, NULL, NULL },
|
||||
{ "pipeline", show_pipeline, NULL, NULL, NULL },
|
||||
{ "preferences", show_preferences, NULL, NULL, NULL },
|
||||
{ "about", show_about, NULL, NULL, NULL },
|
||||
};
|
||||
@@ -722,6 +839,7 @@ clapper_app_application_constructed (GObject *object)
|
||||
{ "app.add-uri", { "<Control>u", NULL, NULL }},
|
||||
{ "app.new-window", { "<Control>n", NULL, NULL }},
|
||||
{ "app.info", { "<Control>i", NULL, NULL }},
|
||||
{ "app.pipeline", { "<Control><Shift>p", NULL, NULL }},
|
||||
{ "app.preferences", { "<Control>comma", NULL, NULL }},
|
||||
{ "app.about", { "F1", NULL, NULL }},
|
||||
{ "win.toggle-fullscreen", { "F11", "f", NULL }},
|
||||
@@ -756,6 +874,19 @@ clapper_app_application_constructed (GObject *object)
|
||||
G_OBJECT_CLASS (parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_app_application_dispose (GObject *object)
|
||||
{
|
||||
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (object);
|
||||
|
||||
if (self->cancellable) {
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_app_application_finalize (GObject *object)
|
||||
{
|
||||
@@ -779,6 +910,7 @@ clapper_app_application_class_init (ClapperAppApplicationClass *klass)
|
||||
"Clapper App Application");
|
||||
|
||||
gobject_class->constructed = clapper_app_application_constructed;
|
||||
gobject_class->dispose = clapper_app_application_dispose;
|
||||
gobject_class->finalize = clapper_app_application_finalize;
|
||||
|
||||
gtk_application_class->window_removed = clapper_app_application_window_removed;
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
@@ -36,6 +36,8 @@ struct _ClapperAppInfoWindow
|
||||
GtkWidget *astreams_list;
|
||||
GtkWidget *sstreams_list;
|
||||
|
||||
GtkWidget *pipeline_button;
|
||||
|
||||
ClapperPlayer *player;
|
||||
};
|
||||
|
||||
@@ -174,6 +176,10 @@ clapper_app_info_window_init (ClapperAppInfoWindow *self)
|
||||
gtk_widget_remove_css_class (self->vstreams_list, "view");
|
||||
gtk_widget_remove_css_class (self->astreams_list, "view");
|
||||
gtk_widget_remove_css_class (self->sstreams_list, "view");
|
||||
|
||||
#ifdef HAVE_GRAPHVIZ
|
||||
gtk_widget_set_visible (self->pipeline_button, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -256,6 +262,7 @@ clapper_app_info_window_class_init (ClapperAppInfoWindowClass *klass)
|
||||
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, vstreams_list);
|
||||
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, astreams_list);
|
||||
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, sstreams_list);
|
||||
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, pipeline_button);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, media_duration_closure);
|
||||
gtk_widget_class_bind_template_callback (widget_class, playback_element_name_closure);
|
||||
|
@@ -23,6 +23,11 @@
|
||||
#include "clapper-app-utils.h"
|
||||
#include "clapper-app-media-item-box.h"
|
||||
|
||||
#ifdef HAVE_GRAPHVIZ
|
||||
#include <graphviz/cgraph.h>
|
||||
#include <graphviz/gvc.h>
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
#ifdef HAVE_WIN_PROCESS_THREADS_API
|
||||
@@ -527,3 +532,178 @@ clapper_app_utils_make_element (const gchar *string)
|
||||
|
||||
return gst_element_factory_make (string, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* _get_tmp_dir:
|
||||
* @subdir: (nullable): an optional subdirectory
|
||||
*
|
||||
* Returns: (transfer full): a newly constructed #GFile
|
||||
*/
|
||||
static inline GFile *
|
||||
_get_tmp_dir (const gchar *subdir)
|
||||
{
|
||||
/* XXX: System tmp directory does not work within containers such as Flatpak
|
||||
* for our usage with file launcher, so make our own temp in app data dir */
|
||||
return g_file_new_build_filename (
|
||||
g_get_user_data_dir (), CLAPPER_APP_ID, "tmp", subdir, NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GRAPHVIZ
|
||||
static GFile *
|
||||
_create_tmp_subdir (const gchar *subdir, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
GFile *tmp_dir;
|
||||
GError *my_error = NULL;
|
||||
|
||||
tmp_dir = _get_tmp_dir (subdir);
|
||||
|
||||
if (!g_file_make_directory_with_parents (tmp_dir, cancellable, &my_error)) {
|
||||
if (my_error->domain != G_IO_ERROR || my_error->code != G_IO_ERROR_EXISTS) {
|
||||
*error = g_error_copy (my_error);
|
||||
g_clear_object (&tmp_dir); // return NULL
|
||||
}
|
||||
g_error_free (my_error);
|
||||
}
|
||||
|
||||
return tmp_dir;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
_create_pipeline_svg_file_in_thread (GTask *task, GObject *source G_GNUC_UNUSED,
|
||||
ClapperPlayer *player, GCancellable *cancellable)
|
||||
{
|
||||
GFile *tmp_file = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
#ifdef HAVE_GRAPHVIZ
|
||||
GFile *tmp_subdir;
|
||||
Agraph_t *graph;
|
||||
GVC_t *gvc;
|
||||
gchar *path, *template = NULL, *dot_data = NULL, *img_data = NULL;
|
||||
gint fd;
|
||||
guint size = 0;
|
||||
|
||||
if (!(tmp_subdir = _create_tmp_subdir ("pipelines", cancellable, &error)))
|
||||
goto finish;
|
||||
|
||||
path = g_file_get_path (tmp_subdir);
|
||||
g_object_unref (tmp_subdir);
|
||||
|
||||
template = g_build_filename (path, "pipeline-XXXXXX.svg", NULL);
|
||||
g_free (path);
|
||||
|
||||
fd = g_mkstemp (template); // Modifies template to actual filename
|
||||
|
||||
if (G_UNLIKELY (fd == -1)) {
|
||||
g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Could not open temp file for writing");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
dot_data = clapper_player_make_pipeline_graph (player, GST_DEBUG_GRAPH_SHOW_ALL);
|
||||
|
||||
if (g_cancellable_is_cancelled (cancellable))
|
||||
goto close_and_finish;
|
||||
|
||||
graph = agmemread (dot_data);
|
||||
|
||||
gvc = gvContext ();
|
||||
gvLayout (gvc, graph, "dot");
|
||||
gvRenderData (gvc, graph, "svg", &img_data, &size);
|
||||
|
||||
agclose (graph);
|
||||
gvFreeContext (gvc);
|
||||
|
||||
if (g_cancellable_is_cancelled (cancellable))
|
||||
goto close_and_finish;
|
||||
|
||||
if (write (fd, img_data, size) != -1) {
|
||||
tmp_file = g_file_new_for_path (template);
|
||||
} else {
|
||||
g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Could not write data to temp file");
|
||||
}
|
||||
|
||||
close_and_finish:
|
||||
/* Always close the file IO */
|
||||
if (G_UNLIKELY (close (fd) == -1))
|
||||
GST_ERROR ("Could not close temp file!");
|
||||
|
||||
finish:
|
||||
g_free (template);
|
||||
g_free (dot_data);
|
||||
g_free (img_data);
|
||||
#else
|
||||
g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Cannot create graph file when compiled without Graphviz");
|
||||
#endif
|
||||
|
||||
if (tmp_file)
|
||||
g_task_return_pointer (task, tmp_file, (GDestroyNotify) g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
clapper_app_utils_create_pipeline_svg_file_async (ClapperPlayer *player,
|
||||
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (NULL, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, gst_object_ref (player), (GDestroyNotify) gst_object_unref);
|
||||
g_task_run_in_thread (task, (GTaskThreadFunc) _create_pipeline_svg_file_in_thread);
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_delete_dir_recursive (GFile *dir, GError **error)
|
||||
{
|
||||
GFileEnumerator *dir_enum;
|
||||
|
||||
if ((dir_enum = g_file_enumerate_children (dir,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error))) {
|
||||
while (TRUE) {
|
||||
GFileInfo *info = NULL;
|
||||
GFile *child = NULL;
|
||||
|
||||
if (!g_file_enumerator_iterate (dir_enum, &info,
|
||||
&child, NULL, error) || !info)
|
||||
break;
|
||||
|
||||
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
|
||||
if (!_delete_dir_recursive (child, error))
|
||||
break;
|
||||
} else if (!g_file_delete (child, NULL, error)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (dir_enum);
|
||||
}
|
||||
|
||||
if (*error != NULL)
|
||||
return FALSE;
|
||||
|
||||
return g_file_delete (dir, NULL, error);
|
||||
}
|
||||
|
||||
void
|
||||
clapper_app_utils_delete_tmp_dir (void)
|
||||
{
|
||||
GFile *tmp_dir = _get_tmp_dir (NULL);
|
||||
GError *error = NULL;
|
||||
|
||||
if (!_delete_dir_recursive (tmp_dir, &error)) {
|
||||
if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_NOT_FOUND) {
|
||||
GST_ERROR ("Could not remove temp dir, reason: %s",
|
||||
GST_STR_NULL (error->message));
|
||||
}
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_object_unref (tmp_dir);
|
||||
}
|
||||
|
@@ -84,4 +84,10 @@ void clapper_app_utils_iterate_plugin_feature_ranks (GSettings *settings, Clappe
|
||||
G_GNUC_INTERNAL
|
||||
GstElement * clapper_app_utils_make_element (const gchar *string);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_app_utils_create_pipeline_svg_file_async (ClapperPlayer *player, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_app_utils_delete_tmp_dir (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
@@ -74,5 +74,7 @@ main (gint argc, gchar **argv)
|
||||
clapper_app_utils_win_hi_res_clock_stop (resolution);
|
||||
#endif
|
||||
|
||||
clapper_app_utils_delete_tmp_dir ();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@@ -94,6 +94,23 @@ clapperapp_c_args = [
|
||||
'-DGST_USE_UNSTABLE_API',
|
||||
]
|
||||
|
||||
clapperapp_possible_functionalities = [
|
||||
'pipeline-preview',
|
||||
]
|
||||
clapperapp_available_functionalities = []
|
||||
|
||||
pp_option = get_option('pipeline-preview')
|
||||
|
||||
if not pp_option.disabled()
|
||||
if cgraph_dep.found() and gvc_dep.found()
|
||||
clapperapp_c_args += ['-DHAVE_GRAPHVIZ']
|
||||
clapperapp_deps += [cgraph_dep, gvc_dep]
|
||||
clapperapp_available_functionalities += 'pipeline-preview'
|
||||
elif pp_option.enabled()
|
||||
error('pipeline-preview option was enabled, but required dependencies were not found')
|
||||
endif
|
||||
endif
|
||||
|
||||
is_windows = ['windows'].contains(host_machine.system())
|
||||
|
||||
if is_windows
|
||||
|
@@ -26,6 +26,12 @@
|
||||
<property name="accelerator"><Ctrl>question</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkShortcutsShortcut">
|
||||
<property name="title" translatable="yes">Show pipeline</property>
|
||||
<property name="accelerator"><Ctrl><Shift>p</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkShortcutsShortcut">
|
||||
<property name="title" translatable="yes">Toggle fullscreen</property>
|
||||
|
@@ -232,6 +232,17 @@
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pipeline_button">
|
||||
<property name="halign">center</property>
|
||||
<property name="label" translatable="yes">Show Pipeline</property>
|
||||
<property name="action-name">app.pipeline</property>
|
||||
<property name="visible">false</property>
|
||||
<style>
|
||||
<class name="pill"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@@ -2187,6 +2187,28 @@ clapper_player_add_feature (ClapperPlayer *self, ClapperFeature *feature)
|
||||
clapper_features_manager_add_feature (self->features_manager, feature, GST_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_player_make_pipeline_graph:
|
||||
* @player: a #ClapperPlayer
|
||||
* @details: a #GstDebugGraphDetails level
|
||||
*
|
||||
* Make current #GStreamer pipeline graph in `graphviz` dot format.
|
||||
*
|
||||
* Applications can use tools like `graphviz` to display returned
|
||||
* data or just save it to a file as-is for the user to do it manually.
|
||||
*
|
||||
* Returns: (transfer full): current pipeline description in dot format.
|
||||
*
|
||||
* Since: 0.10
|
||||
*/
|
||||
gchar *
|
||||
clapper_player_make_pipeline_graph (ClapperPlayer *self, GstDebugGraphDetails details)
|
||||
{
|
||||
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), NULL);
|
||||
|
||||
return gst_debug_bin_to_dot_data (GST_BIN (self->playbin), details);
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_player_thread_start (ClapperThreadedObject *threaded_object)
|
||||
{
|
||||
|
@@ -204,4 +204,7 @@ void clapper_player_seek_custom (ClapperPlayer *player, gdouble position, Clappe
|
||||
CLAPPER_API
|
||||
void clapper_player_add_feature (ClapperPlayer *player, ClapperFeature *feature);
|
||||
|
||||
CLAPPER_API
|
||||
gchar * clapper_player_make_pipeline_graph (ClapperPlayer *player, GstDebugGraphDetails details);
|
||||
|
||||
G_END_DECLS
|
||||
|
Reference in New Issue
Block a user