1016 lines
31 KiB
Diff
1016 lines
31 KiB
Diff
From 5a4ef8928f587f702e92dbeb75280232719c5df4 Mon Sep 17 00:00:00 2001
|
|
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
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 <gst/gst.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdk.h>
|
|
|
|
-#include <stdio.h>
|
|
-#include <stdlib.h>
|
|
-#include <string.h>
|
|
-
|
|
-#ifdef GDK_WINDOWING_WAYLAND
|
|
-#include <gdk/gdkwayland.h>
|
|
-#else
|
|
-#error "Wayland is not supported in GTK+"
|
|
-#endif
|
|
-
|
|
-#include <gst/video/videooverlay.h>
|
|
-#include <wayland/wayland.h> // 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
|
|
|