mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 03:00:35 +00:00
waylandsink: implement the GstVideoOverlay & GstWaylandVideo interfaces
This is the initial implementation, without the GstVideoOverlay.expose() method. It only implements using an external (sub)surface and resizing it with GstWaylandVideo.
This commit is contained in:
parent
68133361ec
commit
e56f305274
5 changed files with 224 additions and 46 deletions
|
@ -81,10 +81,13 @@ static void gst_wayland_sink_get_property (GObject * object,
|
|||
static void gst_wayland_sink_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_wayland_sink_finalize (GObject * object);
|
||||
|
||||
static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
|
||||
GstCaps * filter);
|
||||
static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
|
||||
static gboolean gst_wayland_sink_start (GstBaseSink * bsink);
|
||||
static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink,
|
||||
GstBuffer * buffer);
|
||||
static gboolean
|
||||
|
@ -137,9 +140,11 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
|
|||
"Output to wayland surface",
|
||||
"Sreerenj Balachandran <sreerenj.balachandran@intel.com>");
|
||||
|
||||
gstelement_class->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
|
||||
|
||||
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
|
||||
gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
|
||||
gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_wayland_sink_start);
|
||||
gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
|
||||
gstbasesink_class->propose_allocation =
|
||||
GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
|
||||
|
@ -154,9 +159,7 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
|
|||
static void
|
||||
gst_wayland_sink_init (GstWaylandSink * sink)
|
||||
{
|
||||
sink->display = NULL;
|
||||
sink->window = NULL;
|
||||
sink->pool = NULL;
|
||||
g_cond_init (&sink->render_cond);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -208,18 +211,67 @@ gst_wayland_sink_finalize (GObject * object)
|
|||
if (sink->display_name)
|
||||
g_free (sink->display_name);
|
||||
|
||||
g_cond_clear (&sink->render_cond);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstWaylandSink *sink = GST_WAYLAND_SINK (element);
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
if (!sink->window)
|
||||
gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
return ret;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
if (gst_wl_window_is_toplevel (sink->window)) {
|
||||
g_clear_object (&sink->window);
|
||||
g_clear_object (&sink->display);
|
||||
g_clear_object (&sink->pool);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
|
||||
{
|
||||
GstWaylandSink *sink;
|
||||
GstCaps *caps;
|
||||
gint width, height;
|
||||
|
||||
sink = GST_WAYLAND_SINK (bsink);
|
||||
|
||||
caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
|
||||
|
||||
/* If we don't have a window yet, it will
|
||||
* be created using the upstream size.
|
||||
* Otherwise, we should tell upstream exactly
|
||||
* what size we want. We don't resize ourselves. */
|
||||
if (sink->window) {
|
||||
caps = gst_caps_make_writable (caps);
|
||||
gst_wl_window_get_size (sink->window, &width, &height);
|
||||
gst_caps_set_simple (caps, "width", G_TYPE_INT, width,
|
||||
"height", G_TYPE_INT, height, NULL);
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
GstCaps *intersection;
|
||||
|
||||
|
@ -237,6 +289,7 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
GstWaylandSink *sink;
|
||||
GstBufferPool *newpool;
|
||||
GstVideoInfo info;
|
||||
GError *error = NULL;
|
||||
enum wl_shm_format format;
|
||||
GArray *formats;
|
||||
gint i;
|
||||
|
@ -244,8 +297,9 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
static GstAllocationParams params = { 0, 0, 0, 15, };
|
||||
|
||||
sink = GST_WAYLAND_SINK (bsink);
|
||||
GST_OBJECT_LOCK (sink);
|
||||
|
||||
GST_LOG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
|
||||
GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
|
||||
|
||||
/* extract info from caps */
|
||||
if (!gst_video_info_from_caps (&info, caps))
|
||||
|
@ -255,6 +309,25 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
if (format == -1)
|
||||
goto invalid_format;
|
||||
|
||||
/* store the video size */
|
||||
sink->video_width = info.width;
|
||||
sink->video_height = info.height;
|
||||
|
||||
/* create the output window if needed */
|
||||
if (!sink->window || sink->video_width != sink->window->width ||
|
||||
sink->video_height != sink->window->height) {
|
||||
|
||||
if (!sink->display)
|
||||
sink->display = gst_wl_display_new (sink->display_name, &error);
|
||||
|
||||
if (sink->display == NULL)
|
||||
goto display_failed;
|
||||
|
||||
g_clear_object (&sink->window);
|
||||
sink->window = gst_wl_window_new_toplevel (sink->display, sink->video_width,
|
||||
sink->video_height);
|
||||
}
|
||||
|
||||
/* verify we support the requested format */
|
||||
formats = sink->display->formats;
|
||||
for (i = 0; i < formats->len; i++) {
|
||||
|
@ -265,17 +338,10 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
if (i >= formats->len)
|
||||
goto unsupported_format;
|
||||
|
||||
/* store the video size */
|
||||
sink->video_width = info.width;
|
||||
sink->video_height = info.height;
|
||||
|
||||
/* create a new pool for the new configuration */
|
||||
newpool = gst_wayland_buffer_pool_new (sink->display);
|
||||
|
||||
if (!newpool) {
|
||||
GST_DEBUG_OBJECT (sink, "Failed to create new pool");
|
||||
return FALSE;
|
||||
}
|
||||
if (!newpool)
|
||||
goto pool_failed;
|
||||
|
||||
structure = gst_buffer_pool_get_config (newpool);
|
||||
gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
|
||||
|
@ -286,49 +352,47 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||
gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
|
||||
gst_object_unref (newpool);
|
||||
|
||||
sink->negotiated = TRUE;
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
return TRUE;
|
||||
|
||||
invalid_format:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink,
|
||||
"Could not locate image format from caps %" GST_PTR_FORMAT, caps);
|
||||
return FALSE;
|
||||
goto failure;
|
||||
}
|
||||
display_failed:
|
||||
{
|
||||
GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
|
||||
("Could not initialise Wayland output"),
|
||||
("Failed to create GstWlDisplay: '%s'", error->message));
|
||||
g_error_free (error);
|
||||
goto failure;
|
||||
}
|
||||
unsupported_format:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
|
||||
gst_wayland_format_to_string (format));
|
||||
return FALSE;
|
||||
goto failure;
|
||||
}
|
||||
pool_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (sink, "Failed to create new pool");
|
||||
goto failure;
|
||||
}
|
||||
config_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (bsink, "failed setting config");
|
||||
goto failure;
|
||||
}
|
||||
failure:
|
||||
{
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_wayland_sink_start (GstBaseSink * bsink)
|
||||
{
|
||||
GstWaylandSink *sink = (GstWaylandSink *) bsink;
|
||||
gboolean result = TRUE;
|
||||
GError *error = NULL;
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "start");
|
||||
|
||||
if (!sink->display)
|
||||
sink->display = gst_wl_display_new (sink->display_name, &error);
|
||||
|
||||
if (sink->display == NULL) {
|
||||
GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
|
||||
("Could not initialise Wayland output"),
|
||||
("Failed to create GstWlDisplay: '%s'", error->message));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
||||
{
|
||||
|
@ -430,14 +494,17 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
GstVideoRectangle src, dst, res;
|
||||
GstBuffer *to_render;
|
||||
GstWlMeta *meta;
|
||||
GstFlowReturn ret;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
struct wl_surface *surface;
|
||||
struct wl_callback *callback;
|
||||
|
||||
GST_OBJECT_LOCK (sink);
|
||||
|
||||
GST_LOG_OBJECT (sink, "render buffer %p", buffer);
|
||||
if (!sink->window)
|
||||
sink->window = gst_wl_window_new_toplevel (sink->display, sink->video_width,
|
||||
sink->video_height);
|
||||
|
||||
/* surface is resizing - drop buffers until finished */
|
||||
if (sink->drawing_frozen || !sink->negotiated)
|
||||
goto done;
|
||||
|
||||
meta = gst_buffer_get_wl_meta (buffer);
|
||||
|
||||
|
@ -481,26 +548,35 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
wl_surface_commit (surface);
|
||||
wl_display_flush (sink->display->display);
|
||||
|
||||
/* notify _resume_rendering() in case it's waiting */
|
||||
g_cond_broadcast (&sink->render_cond);
|
||||
|
||||
if (buffer != to_render)
|
||||
gst_buffer_unref (to_render);
|
||||
return GST_FLOW_OK;
|
||||
goto done;
|
||||
|
||||
no_buffer:
|
||||
{
|
||||
GST_WARNING_OBJECT (sink, "could not create image");
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
no_pool:
|
||||
{
|
||||
GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
|
||||
("Internal error: can't allocate images"),
|
||||
("We don't have a bufferpool negotiated"));
|
||||
return GST_FLOW_ERROR;
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto done;
|
||||
}
|
||||
activate_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
{
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -516,7 +592,45 @@ static void
|
|||
gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
|
||||
{
|
||||
GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
|
||||
GstWaylandWindowHandle *whandle = (GstWaylandWindowHandle *) handle;
|
||||
GError *error = NULL;
|
||||
GstPad *peer;
|
||||
|
||||
g_return_if_fail (sink != NULL);
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
|
||||
(void *) handle);
|
||||
|
||||
if (GST_STATE (sink) > GST_STATE_READY)
|
||||
gst_wayland_sink_pause_rendering (GST_WAYLAND_VIDEO (sink));
|
||||
|
||||
g_clear_object (&sink->window);
|
||||
g_clear_object (&sink->display);
|
||||
|
||||
if (handle) {
|
||||
sink->display =
|
||||
gst_wl_display_new_existing (whandle->display, FALSE, &error);
|
||||
if (error) {
|
||||
GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
|
||||
("Could not set window handle"),
|
||||
("Failed to use the external wayland display: '%s'", error->message));
|
||||
g_error_free (error);
|
||||
} else {
|
||||
sink->window = gst_wl_window_new_from_surface (sink->display,
|
||||
whandle->surface, whandle->width, whandle->height);
|
||||
}
|
||||
} else {
|
||||
/* reconfigure to force creation of new window in set_caps */
|
||||
sink->negotiated = FALSE;
|
||||
peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
||||
if (peer) {
|
||||
gst_pad_send_event (peer, gst_event_new_reconfigure ());
|
||||
gst_object_unref (peer);
|
||||
}
|
||||
}
|
||||
|
||||
if (GST_STATE (sink) > GST_STATE_READY)
|
||||
gst_wayland_sink_resume_rendering (GST_WAYLAND_VIDEO (sink));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -538,7 +652,32 @@ static void
|
|||
gst_wayland_sink_set_surface_size (GstWaylandVideo * video, gint w, gint h)
|
||||
{
|
||||
GstWaylandSink *sink = GST_WAYLAND_SINK (video);
|
||||
GstPad *peer;
|
||||
|
||||
g_return_if_fail (sink != NULL);
|
||||
g_return_if_fail (sink->window != NULL);
|
||||
|
||||
GST_OBJECT_LOCK (sink);
|
||||
if (!sink->window) {
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
GST_WARNING_OBJECT (sink,
|
||||
"set_surface_size called without window, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
gst_wl_window_set_size (sink->window, w, h);
|
||||
sink->negotiated = FALSE;
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
|
||||
/* upstream must change video size because we can't resize ourselves.
|
||||
* This can be removed when we move to wl_viewport */
|
||||
if (GST_STATE (sink) > GST_STATE_READY) {
|
||||
peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
||||
if (peer) {
|
||||
gst_pad_send_event (peer, gst_event_new_reconfigure ());
|
||||
gst_object_unref (peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -546,6 +685,10 @@ gst_wayland_sink_pause_rendering (GstWaylandVideo * video)
|
|||
{
|
||||
GstWaylandSink *sink = GST_WAYLAND_SINK (video);
|
||||
g_return_if_fail (sink != NULL);
|
||||
|
||||
GST_OBJECT_LOCK (sink);
|
||||
sink->drawing_frozen = TRUE;
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -553,6 +696,11 @@ gst_wayland_sink_resume_rendering (GstWaylandVideo * video)
|
|||
{
|
||||
GstWaylandSink *sink = GST_WAYLAND_SINK (video);
|
||||
g_return_if_fail (sink != NULL);
|
||||
|
||||
GST_OBJECT_LOCK (sink);
|
||||
sink->drawing_frozen = FALSE;
|
||||
g_cond_wait (&sink->render_cond, GST_OBJECT_GET_LOCK (sink));
|
||||
GST_OBJECT_UNLOCK (sink);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
|
@ -60,6 +60,10 @@ struct _GstWaylandSink
|
|||
gint video_height;
|
||||
|
||||
gchar *display_name;
|
||||
|
||||
gboolean drawing_frozen;
|
||||
gboolean negotiated;
|
||||
GCond render_cond;
|
||||
};
|
||||
|
||||
struct _GstWaylandSinkClass
|
||||
|
|
|
@ -152,6 +152,14 @@ gst_wl_window_get_wl_surface (GstWlWindow * window)
|
|||
return window->surface;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_wl_window_is_toplevel (GstWlWindow * window)
|
||||
{
|
||||
g_return_val_if_fail (window != NULL, FALSE);
|
||||
|
||||
return (window->shell_surface != NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_wl_window_get_size (GstWlWindow * window, gint * w, gint * h)
|
||||
{
|
||||
|
|
|
@ -58,6 +58,7 @@ GstWlWindow *gst_wl_window_new_from_surface (GstWlDisplay * display,
|
|||
|
||||
GstWlDisplay *gst_wl_window_get_display (GstWlWindow * window);
|
||||
struct wl_surface *gst_wl_window_get_wl_surface (GstWlWindow * window);
|
||||
gboolean gst_wl_window_is_toplevel (GstWlWindow *window);
|
||||
|
||||
void gst_wl_window_get_size (GstWlWindow * window, gint * w, gint * h);
|
||||
void gst_wl_window_set_size (GstWlWindow * window, gint w, gint h);
|
||||
|
|
|
@ -22,9 +22,26 @@
|
|||
#define __GST_WAYLAND_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstWaylandWindowHandle:
|
||||
*
|
||||
* Window handle structure to pass to the GstVideoOverlay set_window_handle
|
||||
* method.
|
||||
*/
|
||||
typedef struct _GstWaylandWindowHandle GstWaylandWindowHandle;
|
||||
|
||||
struct _GstWaylandWindowHandle {
|
||||
struct wl_display *display;
|
||||
struct wl_surface *surface;
|
||||
gint width;
|
||||
gint height;
|
||||
};
|
||||
|
||||
|
||||
#define GST_TYPE_WAYLAND_VIDEO \
|
||||
(gst_wayland_video_get_type ())
|
||||
#define GST_WAYLAND_VIDEO(obj) \
|
||||
|
|
Loading…
Reference in a new issue