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:
George Kiagiadakis 2014-02-28 11:48:30 +02:00
parent 68133361ec
commit e56f305274
5 changed files with 224 additions and 46 deletions

View file

@ -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

View file

@ -60,6 +60,10 @@ struct _GstWaylandSink
gint video_height;
gchar *display_name;
gboolean drawing_frozen;
gboolean negotiated;
GCond render_cond;
};
struct _GstWaylandSinkClass

View file

@ -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)
{

View file

@ -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);

View file

@ -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) \