From 5a4ef8928f587f702e92dbeb75280232719c5df4 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Mon, 13 Dec 2021 17:23:38 +0200 Subject: [PATCH 1/7] touch-player: cleanup and port to gtkwaylandsink --- main.c | 754 +++++++--------------------------- 1 file changed, 144 insertions(+), 610 deletions(-) diff --git a/main.c b/main.c index f1861de..0f254e0 100644 --- a/main.c +++ b/main.c @@ -6,40 +6,19 @@ * * SPDX-License-Identufier: GPL-2.0+ * - * NOTE: inspirated from https://github.com/GStreamer/gst-plugins-bad/tree/master/tests/examples/waylandsink + * NOTE: inspired from https://github.com/GStreamer/gst-plugins-bad/tree/master/tests/examples/waylandsink */ #include #include #include -#include -#include -#include - -#ifdef GDK_WINDOWING_WAYLAND -#include -#else -#error "Wayland is not supported in GTK+" -#endif - -#include -#include // for gst/wayland/wayland.h - -gboolean local_kb_set_key_handler(gpointer user_data); - static gchar *graph = NULL; static gchar *shader_file = NULL; static gboolean nofullscreen = FALSE; -static guint32 last_touch_tap = 0; -static guint32 last_pointer_tap = 0; -static gint window_width = 480; -static gint window_height = 272; - -static GMainLoop *loop; static GOptionEntry entries[] = { - {"No Fullscreen", 'F', 0, G_OPTION_ARG_NONE, &nofullscreen, + {"nofullscreen", 'F', 0, G_OPTION_ARG_NONE, &nofullscreen, "Do not put video on fullscreeen", NULL}, {"width", 'w', 0, G_OPTION_ARG_INT, &window_width, "Windows Width", NULL}, {"height", 'h', 0, G_OPTION_ARG_INT, &window_height, "Windows Height", NULL}, @@ -52,17 +31,17 @@ static GOptionEntry entries[] = { typedef struct { GtkWidget *window_widget; - GtkWidget *video_widget; - GtkWidget *box_widget; GstElement *pipeline; - GstVideoOverlay *overlay; + + GMainLoop *loop; + guint io_watch_id; gchar **argv; gint current_uri; /* index for argv */ - gboolean to_start; - gint video_width; - gint video_height; + + guint32 last_touch_tap; + guint32 last_pointer_tap; } DemoApp; static void @@ -75,295 +54,6 @@ on_about_to_finish (GstElement * playbin, DemoApp * d) g_object_set (playbin, "uri", d->argv[d->current_uri], NULL); } -// --------------------------------------------------- -// --------------------------------------------------- -struct _gtk_wayland_for_event { - DemoApp *d; - - struct wl_display *wl_display; - struct wl_registry *wl_registry; - struct wl_seat *wl_seat; - struct wl_pointer *wl_pointer; - struct wl_touch *wl_touch; -}; -struct _gtk_wayland_for_event wayland_support; - -static void -pointer_handle_enter(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface, - wl_fixed_t sx, wl_fixed_t sy) -{ -} - -static void -pointer_handle_leave(void *data, struct wl_pointer *pointer, - uint32_t serial, struct wl_surface *surface) -{ -} - -static void -pointer_handle_motion(void *data, struct wl_pointer *pointer, - uint32_t time, wl_fixed_t sx, wl_fixed_t sy) -{ -} - -static void -pointer_handle_button(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, uint32_t time, uint32_t button, - uint32_t state) -{ - struct _gtk_wayland_for_event* this = (struct _gtk_wayland_for_event*) data; - guint32 diff; - GstState actual_state; - - //g_print("Pointer button %d \n", state); - - if (WL_POINTER_BUTTON_STATE_RELEASED == state) return; - - if (last_pointer_tap == 0) { - last_pointer_tap = time; - //g_print("--> SIMPLE TAP\n"); - gst_element_get_state(this->d->pipeline, &actual_state, NULL, -1); - if (actual_state == GST_STATE_PAUSED) - gst_element_set_state (this->d->pipeline, GST_STATE_PLAYING); - else - gst_element_set_state (this->d->pipeline, GST_STATE_PAUSED); - } else { - diff = time - last_pointer_tap; - if (last_pointer_tap != 0) { - last_pointer_tap = time; - if (diff < 600) { - //g_print("--> DOUBLE TAP\n"); - gst_element_set_state (this->d->pipeline, GST_STATE_NULL); - g_main_loop_quit (loop); - exit(1); - last_pointer_tap = 0; - } else { - gst_element_get_state(this->d->pipeline, &actual_state, NULL, -1); - if (actual_state == GST_STATE_PAUSED) - gst_element_set_state (this->d->pipeline, GST_STATE_PLAYING); - else - gst_element_set_state (this->d->pipeline, GST_STATE_PAUSED); - //g_print("--> SIMPLE TAP\n"); - } - //g_print("--> BEGIN diff = %d\n", diff); - } - } -} - -static void -pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) -{ -} - -static const struct wl_pointer_listener pointer_listener = { - pointer_handle_enter, - pointer_handle_leave, - pointer_handle_motion, - pointer_handle_button, - pointer_handle_axis, -}; -static void -touch_handle_down(void *data, struct wl_touch *wl_touch, - uint32_t serial, uint32_t time, struct wl_surface *surface, - int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) -{ - struct _gtk_wayland_for_event* this = (struct _gtk_wayland_for_event*) data; - guint32 diff; - GstState actual_state; - - g_print("TOUCH down\n"); - if (last_touch_tap == 0) { - last_touch_tap = time; - gst_element_get_state(this->d->pipeline, &actual_state, NULL, -1); - if (actual_state == GST_STATE_PAUSED) - gst_element_set_state (this->d->pipeline, GST_STATE_PLAYING); - else - gst_element_set_state (this->d->pipeline, GST_STATE_PAUSED); - } else { - diff = time - last_touch_tap; - if (last_touch_tap != 0) { - last_touch_tap = time; - if (diff < 600) { - //g_print("--> DOUBLE TAP\n"); - gst_element_set_state (this->d->pipeline, GST_STATE_NULL); - g_main_loop_quit (loop); - exit(1); //force to quit application - } else { - gst_element_get_state(this->d->pipeline, &actual_state, NULL, -1); - if (actual_state == GST_STATE_PAUSED) - gst_element_set_state (this->d->pipeline, GST_STATE_PLAYING); - else - gst_element_set_state (this->d->pipeline, GST_STATE_PAUSED); - //g_print("--> SIMPLE TAP\n"); - } - //g_print("--> BEGIN diff = %d\n", diff); - } - } -} - -static void -touch_handle_up(void *data, struct wl_touch *wl_touch, - uint32_t serial, uint32_t time, int32_t id) -{ -} - -static void -touch_handle_motion(void *data, struct wl_touch *wl_touch, - uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) -{ -} - -static void -touch_handle_frame(void *data, struct wl_touch *wl_touch) -{ -} -static void -touch_handle_cancel(void *data, struct wl_touch *wl_touch) -{ -} -static const struct wl_touch_listener touch_listener = { - touch_handle_down, - touch_handle_up, - touch_handle_motion, - touch_handle_frame, - touch_handle_cancel, -}; -static void -seat_handle_capabilities(void *data, struct wl_seat *seat, - enum wl_seat_capability caps) -{ - struct _gtk_wayland_for_event* this = (struct _gtk_wayland_for_event*) data; - - //g_print("seat_handle_capabilities %d\n", caps); - - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !this->wl_pointer) { - this->wl_pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(this->wl_pointer, &pointer_listener, this); - } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && this->wl_pointer) { - wl_pointer_destroy(this->wl_pointer); - this->wl_pointer = NULL; - } - - if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !this->wl_touch) { - this->wl_touch = wl_seat_get_touch(seat); - wl_touch_set_user_data(this->wl_touch, this); - wl_touch_add_listener(this->wl_touch, &touch_listener, this); - } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && this->wl_touch) { - wl_touch_destroy(this->wl_touch); - this->wl_touch = NULL; - } -} - -static const struct wl_seat_listener seat_listener = -{ - seat_handle_capabilities, -}; -static void -global_registry_handler(void *data, struct wl_registry *registry, uint32_t id, - const char *interface, uint32_t version) -{ - struct _gtk_wayland_for_event* this = (struct _gtk_wayland_for_event*) data; - - //g_print("registry event for %s id, %d data %p\n", interface, id, data); - if (strcmp(interface, "wl_seat") == 0) { - this->wl_seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); - wl_seat_add_listener(this->wl_seat, &seat_listener, this); - } -} - -static void -global_registry_remover(void *data, struct wl_registry *registry, uint32_t id) -{ - //g_print("registry lost for %d\n", id); -} - -static const struct wl_registry_listener registry_listener = { - global_registry_handler, - global_registry_remover -}; - -void init_wl_event(GtkWidget *widget, DemoApp *d) { - GdkDisplay *display; - - wayland_support.d = d; - - display = gtk_widget_get_display (widget); - wayland_support.wl_display = gdk_wayland_display_get_wl_display (display); - if (wayland_support.wl_display == NULL) - return; - wayland_support.wl_registry = wl_display_get_registry(wayland_support.wl_display); - wl_registry_add_listener(wayland_support.wl_registry, ®istry_listener, &wayland_support); - wl_display_dispatch(wayland_support.wl_display); - wl_display_roundtrip(wayland_support.wl_display); -} -// --------------------------------------------------- -// --------------------------------------------------- -struct _gst_caps_video_size { - gint width; - gint height; -}; - -static gboolean print_field (GQuark field, const GValue * value, gpointer pfx) { - gchar *str = gst_value_serialize (value); - const gchar *sfield = g_quark_to_string (field); - struct _gst_caps_video_size *size = pfx; - //g_print("--%s: %s\n", sfield, str); - - if (!strncmp(sfield, "width", 5) && (G_VALUE_TYPE(value) == G_TYPE_INT)) { - size->width = g_value_get_int (value); - } else if (!strncmp(sfield, "height", 6) && (G_VALUE_TYPE(value) == G_TYPE_INT)) { - size->height = g_value_get_int (value); - } - g_free (str); - return TRUE; -} - -static gboolean msg_cb(GstBus * bus, GstMessage * message, gpointer data) -{ - g_print ("*******message: %s\n", GST_MESSAGE_TYPE_NAME (message)); - return TRUE; -} -static void -msg_structure_change (DemoApp *d) -{ - const GstStructure *s; - gint width, height; - gint i; - GstElement *sink; - GstPad *pad = NULL; - GstCaps *caps = NULL; - struct _gst_caps_video_size video_size; - video_size.width = video_size.height = 0; - - sink = gst_bin_get_by_name(GST_BIN(d->pipeline), "waylandsink0"); - pad = gst_element_get_static_pad (GST_ELEMENT(sink), "sink"); - - caps = gst_pad_get_current_caps (GST_PAD(pad)); - if (!caps) - caps = gst_pad_query_caps (GST_PAD(pad), NULL); - - for (i = 0; i < gst_caps_get_size(caps); i++) { - GstStructure *structure = gst_caps_get_structure(caps, i); - g_print ("******%s\n", gst_structure_get_name (structure)); - gst_structure_foreach (structure, print_field, &video_size); - } - if (nofullscreen) { - if( (200 >= video_size.width) || (200 >= video_size.height) ) { - video_size.width = 640; - video_size.height = 480; - } - gtk_widget_set_size_request (d->window_widget, video_size.width, video_size.height); - } - g_print("-----------main:set size to %d %d \n", video_size.width, video_size.height ); - d->video_width = video_size.width; - d->video_height = video_size.height; - - gst_caps_unref (caps); - gst_object_unref (pad); -} - static void msg_state_changed (GstBus * bus, GstMessage * message, gpointer user_data) { @@ -380,13 +70,13 @@ msg_state_changed (GstBus * bus, GstMessage * message, gpointer user_data) switch (new){ case GST_STATE_VOID_PENDING: - g_print("new state: GST_STATE_VOID_PENDING\n");break; + g_print("new state: GST_STATE_VOID_PENDING\n"); + break; case GST_STATE_NULL: - g_print("new state: GST_STATE_NULL\n");break; + g_print("new state: GST_STATE_NULL\n"); + break; case GST_STATE_READY: g_print("new state: GST_STATE_READY\n"); - if (d->to_start) - gst_element_set_state (d->pipeline, GST_STATE_PLAYING); break; case GST_STATE_PAUSED: g_print("new state: GST_STATE_PAUSED\n"); @@ -400,8 +90,8 @@ msg_state_changed (GstBus * bus, GstMessage * message, gpointer user_data) } } -int -gstreamer_bus_callback (struct _GstBus * bus, GstMessage * message, void *data) +static gboolean +gstreamer_bus_callback (GstBus * bus, GstMessage * message, void *data) { DemoApp *d = data; @@ -421,22 +111,18 @@ gstreamer_bus_callback (struct _GstBus * bus, GstMessage * message, void *data) g_print ("Debug details: %s\n", debug); g_free (debug); } - gst_element_set_state (d->pipeline, GST_STATE_NULL); + g_main_loop_quit (d->loop); break; } case GST_MESSAGE_STATE_CHANGED: - g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message)); msg_state_changed (bus, message, data); break; case GST_MESSAGE_EOS: /* end-of-stream */ - g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message)); g_print ("EOS\n"); - gst_element_set_state (d->pipeline, GST_STATE_NULL); - g_main_loop_quit (loop); - exit(1); + g_main_loop_quit (d->loop); break; default: @@ -446,34 +132,31 @@ gstreamer_bus_callback (struct _GstBus * bus, GstMessage * message, void *data) return TRUE; } - - static gboolean -button_notify_event_cb (GtkWidget *widget, - GdkEventButton *event, - gpointer data) +button_notify_event_cb (GtkWidget * widget, GdkEventButton * event, + gpointer data) { DemoApp *d = data; guint32 diff; GstState actual_state; + g_print("--> %s\n", __FUNCTION__); + if (event->button == GDK_BUTTON_PRIMARY) { - if (last_touch_tap == 0) { - last_touch_tap = event->time; + if (d->last_pointer_tap == 0) { + d->last_pointer_tap = event->time; gst_element_get_state(d->pipeline, &actual_state, NULL, -1); if (actual_state == GST_STATE_PAUSED) gst_element_set_state (d->pipeline, GST_STATE_PLAYING); else gst_element_set_state (d->pipeline, GST_STATE_PAUSED); } else { - diff = event->time - last_touch_tap; - if (last_touch_tap != 0) { - last_touch_tap = event->time; + diff = event->time - d->last_pointer_tap; + if (d->last_pointer_tap != 0) { + d->last_pointer_tap = event->time; if (diff < 600) { //g_print("--> DOUBLE TAP\n"); - gst_element_set_state (d->pipeline, GST_STATE_NULL); - g_main_loop_quit (loop); - exit(1); //force to quit application + g_main_loop_quit (d->loop); } else { gst_element_get_state(d->pipeline, &actual_state, NULL, -1); if (actual_state == GST_STATE_PAUSED) @@ -492,9 +175,7 @@ button_notify_event_cb (GtkWidget *widget, } static gboolean -touch_notify_event_cb (GtkWidget *widget, - GdkEvent *event, - gpointer data) +touch_notify_event_cb (GtkWidget * widget, GdkEvent * event, gpointer data) { DemoApp *d = data; guint32 diff; @@ -503,22 +184,20 @@ touch_notify_event_cb (GtkWidget *widget, g_print("--> %s\n", __FUNCTION__); switch(event->touch.type) { case GDK_TOUCH_BEGIN: - if (last_touch_tap == 0) { - last_touch_tap = event->touch.time; + if (d->last_touch_tap == 0) { + d->last_touch_tap = event->touch.time; gst_element_get_state(d->pipeline, &actual_state, NULL, -1); if (actual_state == GST_STATE_PAUSED) gst_element_set_state (d->pipeline, GST_STATE_PLAYING); else gst_element_set_state (d->pipeline, GST_STATE_PAUSED); } else { - diff = event->touch.time - last_touch_tap; - if (last_touch_tap != 0) { - last_touch_tap = event->touch.time; + diff = event->touch.time - d->last_touch_tap; + if (d->last_touch_tap != 0) { + d->last_touch_tap = event->touch.time; if (diff < 600) { g_print("--> DOUBLE TAP\n"); - gst_element_set_state (d->pipeline, GST_STATE_NULL); - g_main_loop_quit (loop); - exit(1); //force to quit application + g_main_loop_quit (d->loop); } else { gst_element_get_state(d->pipeline, &actual_state, NULL, -1); if (actual_state == GST_STATE_PAUSED) @@ -548,112 +227,21 @@ touch_notify_event_cb (GtkWidget *widget, return TRUE; } -static GstBusSyncReply -bus_sync_handler (GstBus * bus, GstMessage * message, gpointer user_data) -{ - DemoApp *d = user_data; - - if (gst_is_wayland_display_handle_need_context_message (message)) { - GstContext *context; - GdkDisplay *display; - struct wl_display *display_handle; - - msg_structure_change(d); - - display = gtk_widget_get_display (d->video_widget); - display_handle = gdk_wayland_display_get_wl_display (display); - context = gst_wayland_display_handle_context_new (display_handle); - gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (message)), context); - - goto drop; - } else if (gst_is_video_overlay_prepare_window_handle_message (message)) { - GtkAllocation allocation; - GdkWindow *window; - struct wl_surface *window_handle; - - /* GST_MESSAGE_SRC (message) will be the overlay object that we have to - * use. This may be waylandsink, but it may also be playbin. In the latter - * case, we must make sure to use playbin instead of waylandsink, because - * playbin resets the window handle and render_rectangle after restarting - * playback and the actual window size is lost */ - d->overlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)); - - msg_structure_change(d); - - gtk_widget_get_allocation (d->video_widget, &allocation); - window = gtk_widget_get_window (d->video_widget); - window_handle = gdk_wayland_window_get_wl_surface (window); - - init_wl_event(d->video_widget, d); - - g_print ("setting window handle and size (%d x %d)\n", - allocation.width, allocation.height); - - gst_video_overlay_set_window_handle (d->overlay, (guintptr) window_handle); - gst_video_overlay_set_render_rectangle (d->overlay, allocation.x, - allocation.y, allocation.width, allocation.height); - - if (d->to_start) { - gst_element_set_state (d->pipeline, GST_STATE_PLAYING); - } - - goto drop; - } - - return GST_BUS_PASS; - -drop: - gst_message_unref (message); - return GST_BUS_DROP; -} - -/* We use the "draw" callback to change the size of the sink - * because the "configure-event" is only sent to top-level widgets. */ -static gboolean -video_widget_draw_cb (GtkWidget * widget, cairo_t * cr, gpointer user_data) -{ - DemoApp *d = user_data; - GtkAllocation allocation; - - gtk_widget_get_allocation (widget, &allocation); - - g_print ("draw_cb x %d, y %d, w %d, h %d\n", - allocation.x, allocation.y, allocation.width, allocation.height); - - if (d->overlay) { - -// gst_video_overlay_set_render_rectangle (d->overlay, allocation.x, -// allocation.y, allocation.width, allocation.height); - if (nofullscreen) { - int x, y; - x = allocation.x + (allocation.width - d->video_width)/2; - y = allocation.y + (allocation.height - d->video_height)/2; - gst_video_overlay_set_render_rectangle (d->overlay, x, - y, d->video_width, d->video_height); - } - else - gst_video_overlay_set_render_rectangle (d->overlay, allocation.x, - allocation.y, allocation.width, allocation.height); - } - - /* There is no need to call gst_video_overlay_expose(). - * The wayland compositor can always re-draw the window - * based on its last contents if necessary */ - - return FALSE; -} - static void build_window (DemoApp * d) { - GtkWidget *box; + GtkCssProvider* provider; + GstElement *sink; + GtkWidget *video_widget; /* windows */ d->window_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW(d->window_widget), "GStreamer Wayland GTK "); - gtk_window_set_default_size (GTK_WINDOW (d->window_widget), window_width, window_height); - gtk_window_set_transient_for (GTK_WINDOW(d->window_widget), NULL); - g_signal_connect (GTK_WINDOW(d->window_widget), "destroy", G_CALLBACK (g_main_loop_quit), loop); + g_signal_connect (d->window_widget, "destroy", + G_CALLBACK (gtk_widget_destroyed), &d->window_widget); + g_signal_connect_swapped (d->window_widget, "destroy", + G_CALLBACK (g_main_loop_quit), d->loop); + if (!nofullscreen) gtk_window_fullscreen(GTK_WINDOW(d->window_widget)); //else { @@ -661,12 +249,11 @@ build_window (DemoApp * d) //} /* styling background color to black */ - GtkCssProvider* provider = gtk_css_provider_new(); - - char *data = "#transparent_bg,GtkDrawingArea {\n" + const char *data = "#transparent_bg,GtkDrawingArea {\n" " background-color: rgba (88%, 88%, 88%, 1.0);\n" "}"; + provider = gtk_css_provider_new(); gtk_css_provider_load_from_data(provider, data, -1, NULL); gtk_style_context_add_provider(gtk_widget_get_style_context(d->window_widget), GTK_STYLE_PROVIDER(provider), @@ -675,19 +262,25 @@ build_window (DemoApp * d) gtk_widget_set_name(d->window_widget, "transparent_bg"); - - d->video_widget = gtk_event_box_new (); - gtk_widget_set_support_multidevice (d->video_widget, TRUE); - gtk_widget_set_app_paintable (d->video_widget, TRUE); - gtk_event_box_set_above_child (GTK_EVENT_BOX(d->video_widget), TRUE); - gtk_widget_set_vexpand (d->video_widget, TRUE); - g_signal_connect (d->video_widget, "draw", - G_CALLBACK (video_widget_draw_cb), d); - - - gtk_container_add(GTK_CONTAINER (d->window_widget), d->video_widget); - gtk_widget_show (d->video_widget); + sink = gst_bin_get_by_name (GST_BIN (d->pipeline), "gtkwsink"); + if (!sink && !g_strcmp0 (G_OBJECT_TYPE_NAME (d->pipeline), "GstPlayBin")) + g_object_get (d->pipeline, "video-sink", &sink, NULL); + g_assert (sink); + g_assert (!g_strcmp0 (G_OBJECT_TYPE_NAME (sink), "GstGtkWaylandSink")); + + g_object_get (sink, "widget", &video_widget, NULL); + gtk_widget_set_support_multidevice (video_widget, TRUE); + gtk_widget_set_vexpand (video_widget, TRUE); + g_signal_connect (video_widget, "touch-event", + G_CALLBACK (touch_notify_event_cb), d); + g_signal_connect (video_widget, "button-press-event", + G_CALLBACK (button_notify_event_cb), d); + + gtk_container_add(GTK_CONTAINER (d->window_widget), video_widget); gtk_widget_show_all (d->window_widget); + + g_object_unref (video_widget); + gst_object_unref (sink); } static void @@ -699,7 +292,6 @@ print_keyboard_help (void) g_print ("\n"); } -static gulong io_watch_id; static void keyboard_cb (const gchar key_input, gpointer user_data) { @@ -709,25 +301,25 @@ keyboard_cb (const gchar key_input, gpointer user_data) /* only want to switch/case on single char, not first char of string */ if (key_input != '\0') key = g_ascii_tolower (key_input); + switch (key) { case 'h': print_keyboard_help (); - break; + break; case 'p': { GstState actual_state; gst_element_get_state(d->pipeline, &actual_state, NULL, -1); - if (actual_state == GST_STATE_PAUSED) - gst_element_set_state (d->pipeline, GST_STATE_PLAYING); - else + if (actual_state == GST_STATE_PLAYING) gst_element_set_state (d->pipeline, GST_STATE_PAUSED); + else + gst_element_set_state (d->pipeline, GST_STATE_PLAYING); + break; } - break; case 'q': - gst_element_set_state (d->pipeline, GST_STATE_NULL); - local_kb_set_key_handler (NULL); - g_main_loop_quit(loop); - exit(1); //force to quit application + g_main_loop_quit(d->loop); + break; + default: break; } } @@ -739,7 +331,6 @@ io_callback (GIOChannel * io, GIOCondition condition, gpointer data) GError *error = NULL; switch (g_io_channel_read_chars (io, &in, 1, NULL, &error)) { - case G_IO_STATUS_NORMAL: keyboard_cb(in, data); return TRUE; @@ -759,27 +350,31 @@ io_callback (GIOChannel * io, GIOCondition condition, gpointer data) return FALSE; } -gboolean -local_kb_set_key_handler(gpointer user_data) + +static void +local_kb_set_key_handler (DemoApp *d, gboolean enable) { GIOChannel *io; - if (io_watch_id > 0) { - g_source_remove (io_watch_id); - io_watch_id = 0; + if (d->io_watch_id > 0) { + g_source_remove (d->io_watch_id); + d->io_watch_id = 0; + } + if (enable) { + io = g_io_channel_unix_new (STDIN_FILENO); + d->io_watch_id = g_io_add_watch (io, G_IO_IN, io_callback, d); + g_io_channel_unref (io); } - io = g_io_channel_unix_new (STDIN_FILENO); - io_watch_id = g_io_add_watch (io, G_IO_IN, io_callback, user_data); - g_io_channel_unref (io); - return TRUE; } + int main (int argc, char **argv) { - DemoApp *d; + DemoApp app = {NULL}, *d = &app; GOptionContext *context; GstBus *bus; GError *error = NULL; - guint bus_watch_id; + guint bus_watch_id = 0; + int ret = 0; gtk_init (&argc, &argv); gst_init (&argc, &argv); @@ -787,22 +382,18 @@ main (int argc, char **argv) context = g_option_context_new ("- waylandsink gtk demo"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { - g_printerr ("option parsing failed: %s\n", error->message); - return 1; + g_option_context_free (context); + goto out; } - - d = g_slice_new0 (DemoApp); - build_window (d); + g_option_context_free (context); if (argc > 1) { d->argv = argv; d->current_uri = 1; - d->to_start = TRUE; - if (!nofullscreen) - d->pipeline = gst_parse_launch ("playbin video-sink='waylandsink fullscreen=true'", NULL); - else - d->pipeline = gst_parse_launch ("playbin video-sink='waylandsink'", NULL); + d->pipeline = gst_parse_launch ("playbin video-sink=gtkwaylandsink", &error); + if (error) + goto out; g_object_set (d->pipeline, "uri", argv[d->current_uri], NULL); @@ -811,146 +402,89 @@ main (int argc, char **argv) G_CALLBACK (on_about_to_finish), d); } else { if (graph != NULL) { - d->to_start = TRUE; - if (strstr(graph, "waylandsink") != NULL) { + if (strstr(graph, "gtkwaylandsink") != NULL) { d->pipeline = gst_parse_launch (graph, NULL); } else { - g_print("ERROR: grap does not contains waylandsink !!!\n"); - g_free(graph); - return 1; + g_print("ERROR: graph does not contain gtkwaylandsink !!!\n"); + ret = 1; + goto out; } } else if (shader_file != NULL) { - gchar *shader_graph; - gchar fragment_content[8096]; - FILE *fp; - size_t nread, data_read, len=0; - char *line; + gchar *fragment_content; + GFile *file; GstElement *customshader; - d->to_start = TRUE; - shader_graph = g_strdup_printf("v4l2src ! video/x-raw, format=YUY2, width=320, height=240, framerate=(fraction)15/1 ! videorate ! video/x-raw,framerate=(fraction)5/1 ! queue ! videoconvert ! video/x-raw,format=RGBA ! queue ! glupload ! queue ! glshader name=customshader ! queue ! gldownload ! queue ! videoconvert ! queue ! waylandsink sync=false fullscreen=true"); - d->pipeline = gst_parse_launch (shader_graph, NULL); + d->pipeline = gst_parse_launch ("v4l2src ! " + "video/x-raw, format=YUY2, width=320, height=240, framerate=(fraction)15/1 ! " + "videorate ! video/x-raw,framerate=(fraction)5/1 ! " + "queue ! videoconvert ! video/x-raw,format=RGBA ! " + "queue ! glupload ! queue ! glshader name=customshader ! queue ! gldownload ! " + "queue ! videoconvert ! " + "queue ! gtkwaylandsink name=gtkwsink", &error); + if (error) + goto out; + + file = g_file_new_for_path (shader_file); + g_file_load_contents (file, NULL, &fragment_content, NULL, NULL, &error); + g_object_unref (file); + if (error) + goto out; customshader = gst_bin_get_by_name(GST_BIN(d->pipeline), "customshader"); g_assert(customshader); - fp = fopen(shader_file, "r"); - if (fp == NULL) { - g_print("ERROR: file cannot be openned, please specify absolute path!!\n"); - g_free(shader_file); - return 1; - } else { - data_read = 0; - bzero(fragment_content, sizeof fragment_content); - while ((nread = getline(&line, &len, fp) ) != -1) { - snprintf(fragment_content + data_read, 8096 - data_read, "%s", line); - data_read += nread; - } - free(line); - fclose(fp); - //g_print("content: \n%s\n", fragment_content); - } - if (customshader) { - //g_print("set fragment\n"); - g_object_set(customshader, "fragment", fragment_content, NULL); - } + //g_print("set fragment, content: \n%s\n", fragment_content); + g_object_set(customshader, "fragment", fragment_content, NULL); + gst_object_unref (customshader); + g_free (fragment_content); } else { d->pipeline = gst_parse_launch ("videotestsrc pattern=18 " - "background-color=0x000062FF ! waylandsink fullscreen=true", NULL); + "background-color=0x000062FF ! gtkwaylandsink name=gtkwsink", + &error); + if (error) + goto out; } } + d->loop = g_main_loop_new (NULL, FALSE); + build_window (d); + bus = gst_pipeline_get_bus (GST_PIPELINE (d->pipeline)); bus_watch_id = gst_bus_add_watch (bus, gstreamer_bus_callback, d); - gst_bus_set_sync_handler (bus, bus_sync_handler, d, NULL); gst_object_unref (bus); - if (nofullscreen) { - GstState state; - GstElement *sink; + local_kb_set_key_handler (d, TRUE); + g_print ("Press 'h' to see a list of keyboard shortcuts.\n"); - gint i; - struct _gst_caps_video_size video_size; - video_size.width = video_size.height = 0; + gst_element_set_state (d->pipeline, GST_STATE_PLAYING); - gst_element_set_state (d->pipeline, GST_STATE_PAUSED); - gst_element_seek_simple (d->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH, GST_SECOND * 1); - gst_element_get_state (d->pipeline, &state, NULL, GST_SECOND * 5); + g_main_loop_run (d->loop); - switch (state){ - case GST_STATE_VOID_PENDING: - g_print("state: GST_STATE_VOID_PENDING\n");break; - case GST_STATE_NULL: - g_print("state: GST_STATE_NULL\n");break; - case GST_STATE_READY: - g_print("state: GST_STATE_READY\n"); - break; - case GST_STATE_PAUSED: - g_print("state: GST_STATE_PAUSED\n"); - break; - case GST_STATE_PLAYING: - g_print("state: GST_STATE_PLAYING\n"); - break; - default: - break; - } + local_kb_set_key_handler (d, FALSE); - gst_element_seek_simple (d->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH, 0); - gst_element_set_state (d->pipeline, GST_STATE_NULL); - gst_element_set_state (d->pipeline, GST_STATE_PAUSED); - } + gst_element_set_state (d->pipeline, GST_STATE_NULL); - if (local_kb_set_key_handler (d)) { - g_print ("Press 'h' to see a list of keyboard shortcuts.\n"); - } else { - g_print ("Interactive keyboard handling in terminal not available.\n"); +out: + if (error) { + g_printerr ("ERROR: %s\n", error->message); + ret = 1; } + g_clear_error (&error); - gst_element_set_state (d->pipeline, GST_STATE_PLAYING); - { - GstState actual_state; + if (bus_watch_id) + g_source_remove (bus_watch_id); - while (TRUE) { - gst_element_get_state(d->pipeline, &actual_state, NULL, 2); - switch (actual_state){ - case GST_STATE_VOID_PENDING: - g_print("main state: GST_STATE_VOID_PENDING\n"); - break; - case GST_STATE_NULL: - g_print("main state: GST_STATE_NULL\n"); - break; - case GST_STATE_READY: - g_print("main state: GST_STATE_READY\n"); - break; - case GST_STATE_PAUSED: - g_print("main state: GST_STATE_PAUSED\n"); - break; - case GST_STATE_PLAYING: - g_print("main state: GST_STATE_PLAYING\n"); - break; - default: - break; - } - if (actual_state == GST_STATE_PLAYING) - break; - - sleep(1); - } + gst_clear_object (&d->pipeline); + if (d->window_widget) { + g_print ("destroy window\n"); + g_signal_handlers_disconnect_by_data (d->window_widget, d->loop); + gtk_widget_destroy (d->window_widget); } - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - - local_kb_set_key_handler (NULL); - - gst_element_set_state (d->pipeline, GST_STATE_NULL); - gst_object_unref (d->pipeline); - g_object_unref (d->window_widget); - g_source_remove (bus_watch_id); - g_slice_free (DemoApp, d); - g_main_loop_unref (loop); + g_clear_pointer (&d->loop, g_main_loop_unref); g_free(graph); + g_free(shader_file); - return 0; + return ret; } -- 2.25.1