2 * GStreamer EGL/GLES Sink
3 * Copyright (C) 2012 Collabora Ltd.
4 * @author: Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>
5 * @author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
25 * Alternatively, the contents of this file may be used under the
26 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
27 * which case the following provisions apply instead of the ones
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Library General Public
32 * License as published by the Free Software Foundation; either
33 * version 2 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Library General Public License for more details.
40 * You should have received a copy of the GNU Library General Public
41 * License along with this library; if not, write to the
42 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
43 * Boston, MA 02110-1301, USA.
47 * SECTION:element-eglglessink
49 * EglGlesSink renders video frames on a EGL surface it sets up
50 * from a window it either creates (on X11) or gets a handle to
51 * through it's xOverlay interface. All the display/surface logic
52 * in this sink uses EGL to interact with the native window system.
53 * The rendering logic, in turn, uses OpenGL ES v2.
55 * This sink has been tested to work on X11/Mesa and on Android
56 * (From Gingerbread on to Jelly Bean) and while it's currently
57 * using an slow copy-over rendering path it has proven to be fast
58 * enough on the devices we have tried it on.
61 * <title>Supported EGL/OpenGL ES versions</title>
63 * This Sink uses EGLv1 and GLESv2
68 * <title>Example launch line</title>
70 * gst-launch -v -m videotestsrc ! eglglessink
75 * <title>Example launch line with internal window creation disabled</title>
77 * By setting the can_create_window property to FALSE you can force the
78 * sink to wait for a window handle through it's xOverlay interface even
79 * if internal window creation is supported by the platform. Window creation
80 * is only supported in X11 right now but it should be trivial to add support
81 * for different platforms.
84 * gst-launch -v -m videotestsrc ! eglglessink can_create_window=FALSE
89 * <title>Scaling</title>
91 * The sink will try it's best to consider the incoming frame's and display's
92 * pixel aspect ratio and fill the corresponding surface without altering the
93 * decoded frame's geometry when scaling. You can disable this logic by setting
94 * the force_aspect_ratio property to FALSE, in which case the sink will just
95 * fill the entire surface it has access to regardles of the PAR/DAR relationship.
98 * Querying the display aspect ratio is only supported with EGL versions >= 1.2.
99 * The sink will just assume the DAR to be 1/1 if it can't get access to this
103 * Here is an example launch line with the PAR/DAR aware scaling disabled:
106 * gst-launch -v -m videotestsrc ! eglglessink force_aspect_ratio=FALSE
115 #define EGL_EGLEXT_PROTOTYPES
116 #define GL_GLEXT_PROTOTYPES
120 #include <gst/video/video.h>
121 #include <gst/video/video-frame.h>
122 #include <gst/video/gstvideosink.h>
123 #include <gst/video/gstvideometa.h>
124 #include <gst/video/gstvideopool.h>
125 #include <gst/video/videooverlay.h>
127 #include "gstegladaptation.h"
130 #include <bcm_host.h>
133 #include "gsteglglessink.h"
135 GST_DEBUG_CATEGORY_STATIC (gst_eglglessink_debug);
136 #define GST_CAT_DEFAULT gst_eglglessink_debug
138 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
140 /* Input capabilities. */
141 static GstStaticPadTemplate gst_eglglessink_sink_template_factory =
142 GST_STATIC_PAD_TEMPLATE ("sink",
145 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
146 (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE,
147 "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
148 "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
149 "RGB, BGR, RGB16 }") ";"
150 GST_VIDEO_CAPS_MAKE_WITH_FEATURES
151 (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
152 "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
153 "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
154 "RGB, BGR, RGB16 }") ";" GST_VIDEO_CAPS_MAKE ("{ "
155 "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
156 "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
157 "RGB, BGR, RGB16 }")));
159 /* Filter signals and args */
170 PROP_FORCE_ASPECT_RATIO,
173 static void gst_eglglessink_finalize (GObject * object);
174 static void gst_eglglessink_get_property (GObject * object, guint prop_id,
175 GValue * value, GParamSpec * pspec);
176 static void gst_eglglessink_set_property (GObject * object, guint prop_id,
177 const GValue * value, GParamSpec * pspec);
178 static GstStateChangeReturn gst_eglglessink_change_state (GstElement * element,
179 GstStateChange transition);
180 static void gst_eglglessink_set_context (GstElement * element,
181 GstContext * context);
182 static GstFlowReturn gst_eglglessink_prepare (GstBaseSink * bsink,
184 static GstFlowReturn gst_eglglessink_show_frame (GstVideoSink * vsink,
186 static gboolean gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps);
187 static GstCaps *gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter);
188 static gboolean gst_eglglessink_propose_allocation (GstBaseSink * bsink,
190 static gboolean gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query);
191 static gboolean gst_eglglessink_event (GstBaseSink * bsink, GstEvent * event);
193 /* VideoOverlay interface cruft */
194 static void gst_eglglessink_videooverlay_init (GstVideoOverlayInterface *
197 /* Actual VideoOverlay interface funcs */
198 static void gst_eglglessink_expose (GstVideoOverlay * overlay);
199 static void gst_eglglessink_set_window_handle (GstVideoOverlay * overlay,
201 static void gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay,
202 gint x, gint y, gint width, gint height);
205 static gboolean gst_eglglessink_create_window (GstEglGlesSink *
206 eglglessink, gint width, gint height);
207 static gboolean gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink,
210 gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps);
211 static GstFlowReturn gst_eglglessink_upload (GstEglGlesSink * sink,
213 static GstFlowReturn gst_eglglessink_render (GstEglGlesSink * sink);
214 static GstFlowReturn gst_eglglessink_queue_object (GstEglGlesSink * sink,
215 GstMiniObject * obj);
216 static inline gboolean egl_init (GstEglGlesSink * eglglessink);
217 static GstBufferPool *gst_egl_image_buffer_pool_new (GstEglGlesSink *
218 eglglessink, GstEGLDisplay * display);
220 /* EGLImage memory, buffer pool, etc */
223 GstVideoBufferPool parent;
225 GstEglGlesSink *sink;
226 GstAllocator *allocator;
227 GstAllocationParams params;
229 gboolean add_metavideo;
230 gboolean want_eglimage;
231 GstEGLDisplay *display;
232 } GstEGLImageBufferPool;
234 typedef GstVideoBufferPoolClass GstEGLImageBufferPoolClass;
236 #define GST_EGL_IMAGE_BUFFER_POOL(p) ((GstEGLImageBufferPool*)(p))
238 GType gst_egl_image_buffer_pool_get_type (void);
240 G_DEFINE_TYPE (GstEGLImageBufferPool, gst_egl_image_buffer_pool,
241 GST_TYPE_VIDEO_BUFFER_POOL);
243 static const gchar **
244 gst_egl_image_buffer_pool_get_options (GstBufferPool * bpool)
246 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL
253 gst_egl_image_buffer_pool_set_config (GstBufferPool * bpool,
254 GstStructure * config)
256 GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
261 gst_object_unref (pool->allocator);
262 pool->allocator = NULL;
264 if (!GST_BUFFER_POOL_CLASS
265 (gst_egl_image_buffer_pool_parent_class)->set_config (bpool, config))
268 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
272 if (!gst_video_info_from_caps (&info, caps))
275 if (!gst_buffer_pool_config_get_allocator (config, &pool->allocator,
279 gst_object_ref (pool->allocator);
281 pool->add_metavideo =
282 gst_buffer_pool_config_has_option (config,
283 GST_BUFFER_POOL_OPTION_VIDEO_META);
285 pool->want_eglimage = (pool->allocator
286 && g_strcmp0 (pool->allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0);
294 gst_egl_image_buffer_pool_alloc_buffer (GstBufferPool * bpool,
295 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
297 GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
301 if (!pool->add_metavideo || !pool->want_eglimage)
303 GST_BUFFER_POOL_CLASS
304 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
307 if (!pool->allocator)
308 return GST_FLOW_NOT_NEGOTIATED;
310 switch (pool->info.finfo->format) {
311 case GST_VIDEO_FORMAT_RGB:
312 case GST_VIDEO_FORMAT_BGR:
313 case GST_VIDEO_FORMAT_RGB16:
314 case GST_VIDEO_FORMAT_NV12:
315 case GST_VIDEO_FORMAT_NV21:
316 case GST_VIDEO_FORMAT_RGBA:
317 case GST_VIDEO_FORMAT_BGRA:
318 case GST_VIDEO_FORMAT_ARGB:
319 case GST_VIDEO_FORMAT_ABGR:
320 case GST_VIDEO_FORMAT_RGBx:
321 case GST_VIDEO_FORMAT_BGRx:
322 case GST_VIDEO_FORMAT_xRGB:
323 case GST_VIDEO_FORMAT_xBGR:
324 case GST_VIDEO_FORMAT_AYUV:
325 case GST_VIDEO_FORMAT_YV12:
326 case GST_VIDEO_FORMAT_I420:
327 case GST_VIDEO_FORMAT_Y444:
328 case GST_VIDEO_FORMAT_Y42B:
329 case GST_VIDEO_FORMAT_Y41B:{
335 s = gst_structure_new ("eglglessink-allocate-eglimage",
336 "format", GST_TYPE_VIDEO_FORMAT, pool->info.finfo->format,
337 "width", G_TYPE_INT, pool->info.width,
338 "height", G_TYPE_INT, pool->info.height, NULL);
339 query = gst_query_new_custom (GST_QUERY_CUSTOM, s);
342 gst_eglglessink_queue_object (pool->sink,
343 GST_MINI_OBJECT_CAST (query));
345 if (ret != GST_FLOW_OK || !gst_structure_has_field (s, "buffer")) {
346 GST_WARNING ("Fallback memory allocation");
347 gst_query_unref (query);
349 GST_BUFFER_POOL_CLASS
350 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
354 v = gst_structure_get_value (s, "buffer");
355 *buffer = GST_BUFFER_CAST (g_value_get_pointer (v));
356 gst_query_unref (query);
359 GST_WARNING ("Fallback memory allocation");
361 GST_BUFFER_POOL_CLASS
362 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
371 GST_BUFFER_POOL_CLASS
372 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
377 return GST_FLOW_ERROR;
381 gst_egl_image_buffer_pool_acquire_buffer (GstBufferPool * bpool,
382 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
385 GstEGLImageBufferPool *pool;
388 GST_BUFFER_POOL_CLASS
389 (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
391 if (ret != GST_FLOW_OK || !*buffer)
394 pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
396 /* XXX: Don't return the memory we just rendered, glEGLImageTargetTexture2DOES()
397 * keeps the EGLImage unmappable until the next one is uploaded
399 if (*buffer && *buffer == pool->sink->last_buffer) {
400 GstBuffer *oldbuf = *buffer;
403 GST_BUFFER_POOL_CLASS
404 (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
406 gst_object_replace ((GstObject **) & oldbuf->pool, (GstObject *) pool);
407 gst_buffer_unref (oldbuf);
414 gst_egl_image_buffer_pool_finalize (GObject * object)
416 GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (object);
419 gst_object_unref (pool->allocator);
420 pool->allocator = NULL;
423 gst_object_unref (pool->sink);
427 gst_egl_display_unref (pool->display);
428 pool->display = NULL;
430 G_OBJECT_CLASS (gst_egl_image_buffer_pool_parent_class)->finalize (object);
434 gst_egl_image_buffer_pool_class_init (GstEGLImageBufferPoolClass * klass)
436 GObjectClass *gobject_class = (GObjectClass *) klass;
437 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
439 gobject_class->finalize = gst_egl_image_buffer_pool_finalize;
440 gstbufferpool_class->get_options = gst_egl_image_buffer_pool_get_options;
441 gstbufferpool_class->set_config = gst_egl_image_buffer_pool_set_config;
442 gstbufferpool_class->alloc_buffer = gst_egl_image_buffer_pool_alloc_buffer;
443 gstbufferpool_class->acquire_buffer =
444 gst_egl_image_buffer_pool_acquire_buffer;
448 gst_egl_image_buffer_pool_init (GstEGLImageBufferPool * pool)
452 #define parent_class gst_eglglessink_parent_class
453 G_DEFINE_TYPE_WITH_CODE (GstEglGlesSink, gst_eglglessink, GST_TYPE_VIDEO_SINK,
454 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
455 gst_eglglessink_videooverlay_init));
457 static inline gboolean
458 egl_init (GstEglGlesSink * eglglessink)
462 if (!gst_egl_adaptation_init_egl_display (eglglessink->egl_context)) {
463 GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL display");
468 gst_egl_adaptation_fill_supported_fbuffer_configs
469 (eglglessink->egl_context);
471 GST_ERROR_OBJECT (eglglessink, "Display support NONE of our configs");
474 GST_OBJECT_LOCK (eglglessink);
475 gst_caps_replace (&eglglessink->sinkcaps, caps);
476 GST_OBJECT_UNLOCK (eglglessink);
477 gst_caps_unref (caps);
480 eglglessink->egl_started = TRUE;
485 GST_ERROR_OBJECT (eglglessink, "Failed to perform EGL init");
490 render_thread_func (GstEglGlesSink * eglglessink)
494 GstDataQueueItem *item = NULL;
495 GstFlowReturn last_flow = GST_FLOW_OK;
497 g_value_init (&val, GST_TYPE_G_THREAD);
498 g_value_set_boxed (&val, g_thread_self ());
499 message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink),
500 GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (eglglessink));
501 gst_message_set_stream_status_object (message, &val);
502 GST_DEBUG_OBJECT (eglglessink, "posting ENTER stream status");
503 gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message);
504 g_value_unset (&val);
506 eglBindAPI (EGL_OPENGL_ES_API);
508 while (gst_data_queue_pop (eglglessink->queue, &item)) {
509 GstMiniObject *object = item->object;
511 GST_DEBUG_OBJECT (eglglessink, "Handling object %" GST_PTR_FORMAT, object);
513 if (GST_IS_CAPS (object)) {
514 GstCaps *caps = GST_CAPS_CAST (object);
516 if (caps != eglglessink->configured_caps) {
517 if (!gst_eglglessink_configure_caps (eglglessink, caps)) {
518 last_flow = GST_FLOW_NOT_NEGOTIATED;
521 } else if (GST_IS_QUERY (object)) {
522 GstQuery *query = GST_QUERY_CAST (object);
523 GstStructure *s = (GstStructure *) gst_query_get_structure (query);
525 if (gst_structure_has_name (s, "eglglessink-allocate-eglimage")) {
527 GstVideoFormat format;
531 if (!gst_structure_get_enum (s, "format", GST_TYPE_VIDEO_FORMAT,
533 || !gst_structure_get_int (s, "width", &width)
534 || !gst_structure_get_int (s, "height", &height)) {
535 g_assert_not_reached ();
539 gst_egl_adaptation_allocate_eglimage (eglglessink->egl_context,
540 GST_EGL_IMAGE_BUFFER_POOL (eglglessink->pool)->allocator, format,
542 g_value_init (&v, G_TYPE_POINTER);
543 g_value_set_pointer (&v, buffer);
544 gst_structure_set_value (s, "buffer", &v);
547 g_assert_not_reached ();
549 last_flow = GST_FLOW_OK;
550 } else if (GST_IS_BUFFER (object)) {
551 GstBuffer *buf = GST_BUFFER_CAST (item->object);
553 if (eglglessink->configured_caps) {
554 last_flow = gst_eglglessink_upload (eglglessink, buf);
556 last_flow = GST_FLOW_OK;
557 GST_DEBUG_OBJECT (eglglessink,
558 "No caps configured yet, not drawing anything");
560 } else if (!object) {
561 if (eglglessink->configured_caps) {
562 last_flow = gst_eglglessink_render (eglglessink);
564 last_flow = GST_FLOW_OK;
565 GST_DEBUG_OBJECT (eglglessink,
566 "No caps configured yet, not drawing anything");
569 g_assert_not_reached ();
572 item->destroy (item);
573 g_mutex_lock (&eglglessink->render_lock);
574 eglglessink->last_flow = last_flow;
575 eglglessink->dequeued_object = object;
576 g_cond_broadcast (&eglglessink->render_cond);
577 g_mutex_unlock (&eglglessink->render_lock);
579 if (last_flow != GST_FLOW_OK)
581 GST_DEBUG_OBJECT (eglglessink, "Successfully handled object");
584 if (last_flow == GST_FLOW_OK) {
585 g_mutex_lock (&eglglessink->render_lock);
586 eglglessink->last_flow = GST_FLOW_FLUSHING;
587 eglglessink->dequeued_object = NULL;
588 g_cond_broadcast (&eglglessink->render_cond);
589 g_mutex_unlock (&eglglessink->render_lock);
592 GST_DEBUG_OBJECT (eglglessink, "Shutting down thread");
594 /* EGL/GLES cleanup */
595 gst_egl_adaptation_cleanup (eglglessink->egl_context);
597 if (eglglessink->configured_caps) {
598 gst_caps_unref (eglglessink->configured_caps);
599 eglglessink->configured_caps = NULL;
602 g_value_init (&val, GST_TYPE_G_THREAD);
603 g_value_set_boxed (&val, g_thread_self ());
604 message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink),
605 GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (eglglessink));
606 gst_message_set_stream_status_object (message, &val);
607 GST_DEBUG_OBJECT (eglglessink, "posting LEAVE stream status");
608 gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message);
609 g_value_unset (&val);
615 gst_eglglessink_start (GstEglGlesSink * eglglessink)
617 GError *error = NULL;
619 GST_DEBUG_OBJECT (eglglessink, "Starting");
621 if (!eglglessink->egl_started) {
622 GST_ERROR_OBJECT (eglglessink, "EGL uninitialized. Bailing out");
626 /* Ask for a window to render to */
627 if (!eglglessink->have_window)
628 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (eglglessink));
630 if (!eglglessink->have_window && !eglglessink->create_window) {
631 GST_ERROR_OBJECT (eglglessink, "Window handle unavailable and we "
632 "were instructed not to create an internal one. Bailing out.");
636 eglglessink->last_flow = GST_FLOW_OK;
637 eglglessink->display_region.w = 0;
638 eglglessink->display_region.h = 0;
640 gst_data_queue_set_flushing (eglglessink->queue, FALSE);
642 #if !GLIB_CHECK_VERSION (2, 31, 0)
643 eglglessink->thread =
644 g_thread_create ((GThreadFunc) render_thread_func, eglglessink, TRUE,
647 eglglessink->thread = g_thread_try_new ("eglglessink-render",
648 (GThreadFunc) render_thread_func, eglglessink, &error);
651 if (!eglglessink->thread || error != NULL)
654 GST_DEBUG_OBJECT (eglglessink, "Started");
659 GST_ERROR_OBJECT (eglglessink, "Couldn't start");
660 g_clear_error (&error);
665 gst_eglglessink_stop (GstEglGlesSink * eglglessink)
667 GST_DEBUG_OBJECT (eglglessink, "Stopping");
669 gst_data_queue_set_flushing (eglglessink->queue, TRUE);
670 g_mutex_lock (&eglglessink->render_lock);
671 g_cond_broadcast (&eglglessink->render_cond);
672 g_mutex_unlock (&eglglessink->render_lock);
674 if (eglglessink->thread) {
675 g_thread_join (eglglessink->thread);
676 eglglessink->thread = NULL;
678 eglglessink->last_flow = GST_FLOW_FLUSHING;
680 gst_buffer_replace (&eglglessink->last_buffer, NULL);
682 if (eglglessink->using_own_window) {
683 gst_egl_adaptation_destroy_native_window (eglglessink->egl_context,
684 &eglglessink->own_window_data);
685 eglglessink->have_window = FALSE;
687 eglglessink->egl_context->used_window = 0;
688 if (eglglessink->current_caps) {
689 gst_caps_unref (eglglessink->current_caps);
690 eglglessink->current_caps = NULL;
693 GST_DEBUG_OBJECT (eglglessink, "Stopped");
699 gst_eglglessink_videooverlay_init (GstVideoOverlayInterface * iface)
701 iface->set_window_handle = gst_eglglessink_set_window_handle;
702 iface->expose = gst_eglglessink_expose;
703 iface->set_render_rectangle = gst_eglglessink_set_render_rectangle;
707 gst_eglglessink_create_window (GstEglGlesSink * eglglessink, gint width,
710 gboolean window_created = FALSE;
712 if (!eglglessink->create_window) {
713 GST_ERROR_OBJECT (eglglessink, "This sink can't create a window by itself");
716 GST_INFO_OBJECT (eglglessink, "Attempting internal window creation");
719 gst_egl_adaptation_create_native_window (eglglessink->egl_context, width,
720 height, &eglglessink->own_window_data);
721 if (!window_created) {
722 GST_ERROR_OBJECT (eglglessink, "Could not create window");
724 return window_created;
728 gst_eglglessink_expose (GstVideoOverlay * overlay)
730 GstEglGlesSink *eglglessink;
733 eglglessink = GST_EGLGLESSINK (overlay);
734 GST_DEBUG_OBJECT (eglglessink, "Expose catched, redisplay");
736 /* Render from last seen buffer */
737 ret = gst_eglglessink_queue_object (eglglessink, NULL);
738 if (ret == GST_FLOW_ERROR)
739 GST_ERROR_OBJECT (eglglessink, "Redisplay failed");
743 gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink, gboolean reset)
745 gdouble render_width, render_height;
746 gdouble texture_width, texture_height;
747 gdouble x1, x2, y1, y2;
748 gdouble tx1, tx2, ty1, ty2;
750 GST_INFO_OBJECT (eglglessink, "VBO setup. have_vbo:%d, should reset %d",
751 eglglessink->egl_context->have_vbo, reset);
753 if (eglglessink->egl_context->have_vbo && reset) {
754 glDeleteBuffers (1, &eglglessink->egl_context->position_buffer);
755 glDeleteBuffers (1, &eglglessink->egl_context->index_buffer);
756 eglglessink->egl_context->have_vbo = FALSE;
759 render_width = eglglessink->render_region.w;
760 render_height = eglglessink->render_region.h;
762 texture_width = eglglessink->configured_info.width;
763 texture_height = eglglessink->configured_info.height;
765 GST_DEBUG_OBJECT (eglglessink, "Performing VBO setup");
767 x1 = (eglglessink->display_region.x / render_width) * 2.0 - 1;
768 y1 = (eglglessink->display_region.y / render_height) * 2.0 - 1;
769 x2 = ((eglglessink->display_region.x +
770 eglglessink->display_region.w) / render_width) * 2.0 - 1;
771 y2 = ((eglglessink->display_region.y +
772 eglglessink->display_region.h) / render_height) * 2.0 - 1;
774 tx1 = (eglglessink->crop.x / texture_width);
775 tx2 = ((eglglessink->crop.x + eglglessink->crop.w) / texture_width);
776 ty1 = (eglglessink->crop.y / texture_height);
777 ty2 = ((eglglessink->crop.y + eglglessink->crop.h) / texture_height);
779 /* X-normal, Y-normal orientation */
780 eglglessink->egl_context->position_array[0].x = x2;
781 eglglessink->egl_context->position_array[0].y = y2;
782 eglglessink->egl_context->position_array[0].z = 0;
783 eglglessink->egl_context->position_array[0].a = tx2;
784 eglglessink->egl_context->position_array[0].b = ty1;
786 eglglessink->egl_context->position_array[1].x = x2;
787 eglglessink->egl_context->position_array[1].y = y1;
788 eglglessink->egl_context->position_array[1].z = 0;
789 eglglessink->egl_context->position_array[1].a = tx2;
790 eglglessink->egl_context->position_array[1].b = ty2;
792 eglglessink->egl_context->position_array[2].x = x1;
793 eglglessink->egl_context->position_array[2].y = y2;
794 eglglessink->egl_context->position_array[2].z = 0;
795 eglglessink->egl_context->position_array[2].a = tx1;
796 eglglessink->egl_context->position_array[2].b = ty1;
798 eglglessink->egl_context->position_array[3].x = x1;
799 eglglessink->egl_context->position_array[3].y = y1;
800 eglglessink->egl_context->position_array[3].z = 0;
801 eglglessink->egl_context->position_array[3].a = tx1;
802 eglglessink->egl_context->position_array[3].b = ty2;
804 /* X-normal, Y-flip orientation */
805 eglglessink->egl_context->position_array[4 + 0].x = x2;
806 eglglessink->egl_context->position_array[4 + 0].y = y2;
807 eglglessink->egl_context->position_array[4 + 0].z = 0;
808 eglglessink->egl_context->position_array[4 + 0].a = tx2;
809 eglglessink->egl_context->position_array[4 + 0].b = ty2;
811 eglglessink->egl_context->position_array[4 + 1].x = x2;
812 eglglessink->egl_context->position_array[4 + 1].y = y1;
813 eglglessink->egl_context->position_array[4 + 1].z = 0;
814 eglglessink->egl_context->position_array[4 + 1].a = tx2;
815 eglglessink->egl_context->position_array[4 + 1].b = ty1;
817 eglglessink->egl_context->position_array[4 + 2].x = x1;
818 eglglessink->egl_context->position_array[4 + 2].y = y2;
819 eglglessink->egl_context->position_array[4 + 2].z = 0;
820 eglglessink->egl_context->position_array[4 + 2].a = tx1;
821 eglglessink->egl_context->position_array[4 + 2].b = ty2;
823 eglglessink->egl_context->position_array[4 + 3].x = x1;
824 eglglessink->egl_context->position_array[4 + 3].y = y1;
825 eglglessink->egl_context->position_array[4 + 3].z = 0;
826 eglglessink->egl_context->position_array[4 + 3].a = tx1;
827 eglglessink->egl_context->position_array[4 + 3].b = ty1;
830 if (eglglessink->display_region.x == 0) {
831 /* Borders top/bottom */
833 eglglessink->egl_context->position_array[8 + 0].x = 1;
834 eglglessink->egl_context->position_array[8 + 0].y = 1;
835 eglglessink->egl_context->position_array[8 + 0].z = 0;
837 eglglessink->egl_context->position_array[8 + 1].x = x2;
838 eglglessink->egl_context->position_array[8 + 1].y = y2;
839 eglglessink->egl_context->position_array[8 + 1].z = 0;
841 eglglessink->egl_context->position_array[8 + 2].x = -1;
842 eglglessink->egl_context->position_array[8 + 2].y = 1;
843 eglglessink->egl_context->position_array[8 + 2].z = 0;
845 eglglessink->egl_context->position_array[8 + 3].x = x1;
846 eglglessink->egl_context->position_array[8 + 3].y = y2;
847 eglglessink->egl_context->position_array[8 + 3].z = 0;
849 eglglessink->egl_context->position_array[12 + 0].x = 1;
850 eglglessink->egl_context->position_array[12 + 0].y = y1;
851 eglglessink->egl_context->position_array[12 + 0].z = 0;
853 eglglessink->egl_context->position_array[12 + 1].x = 1;
854 eglglessink->egl_context->position_array[12 + 1].y = -1;
855 eglglessink->egl_context->position_array[12 + 1].z = 0;
857 eglglessink->egl_context->position_array[12 + 2].x = x1;
858 eglglessink->egl_context->position_array[12 + 2].y = y1;
859 eglglessink->egl_context->position_array[12 + 2].z = 0;
861 eglglessink->egl_context->position_array[12 + 3].x = -1;
862 eglglessink->egl_context->position_array[12 + 3].y = -1;
863 eglglessink->egl_context->position_array[12 + 3].z = 0;
865 /* Borders left/right */
867 eglglessink->egl_context->position_array[8 + 0].x = x1;
868 eglglessink->egl_context->position_array[8 + 0].y = 1;
869 eglglessink->egl_context->position_array[8 + 0].z = 0;
871 eglglessink->egl_context->position_array[8 + 1].x = x1;
872 eglglessink->egl_context->position_array[8 + 1].y = -1;
873 eglglessink->egl_context->position_array[8 + 1].z = 0;
875 eglglessink->egl_context->position_array[8 + 2].x = -1;
876 eglglessink->egl_context->position_array[8 + 2].y = 1;
877 eglglessink->egl_context->position_array[8 + 2].z = 0;
879 eglglessink->egl_context->position_array[8 + 3].x = -1;
880 eglglessink->egl_context->position_array[8 + 3].y = -1;
881 eglglessink->egl_context->position_array[8 + 3].z = 0;
883 eglglessink->egl_context->position_array[12 + 0].x = 1;
884 eglglessink->egl_context->position_array[12 + 0].y = 1;
885 eglglessink->egl_context->position_array[12 + 0].z = 0;
887 eglglessink->egl_context->position_array[12 + 1].x = 1;
888 eglglessink->egl_context->position_array[12 + 1].y = -1;
889 eglglessink->egl_context->position_array[12 + 1].z = 0;
891 eglglessink->egl_context->position_array[12 + 2].x = x2;
892 eglglessink->egl_context->position_array[12 + 2].y = y2;
893 eglglessink->egl_context->position_array[12 + 2].z = 0;
895 eglglessink->egl_context->position_array[12 + 3].x = x2;
896 eglglessink->egl_context->position_array[12 + 3].y = -1;
897 eglglessink->egl_context->position_array[12 + 3].z = 0;
900 eglglessink->egl_context->index_array[0] = 0;
901 eglglessink->egl_context->index_array[1] = 1;
902 eglglessink->egl_context->index_array[2] = 2;
903 eglglessink->egl_context->index_array[3] = 3;
905 glGenBuffers (1, &eglglessink->egl_context->position_buffer);
906 glGenBuffers (1, &eglglessink->egl_context->index_buffer);
907 if (got_gl_error ("glGenBuffers"))
908 goto HANDLE_ERROR_LOCKED;
910 glBindBuffer (GL_ARRAY_BUFFER, eglglessink->egl_context->position_buffer);
911 if (got_gl_error ("glBindBuffer position_buffer"))
912 goto HANDLE_ERROR_LOCKED;
914 glBufferData (GL_ARRAY_BUFFER,
915 sizeof (eglglessink->egl_context->position_array),
916 eglglessink->egl_context->position_array, GL_STATIC_DRAW);
917 if (got_gl_error ("glBufferData position_buffer"))
918 goto HANDLE_ERROR_LOCKED;
920 glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
921 eglglessink->egl_context->index_buffer);
922 if (got_gl_error ("glBindBuffer index_buffer"))
923 goto HANDLE_ERROR_LOCKED;
925 glBufferData (GL_ELEMENT_ARRAY_BUFFER,
926 sizeof (eglglessink->egl_context->index_array),
927 eglglessink->egl_context->index_array, GL_STATIC_DRAW);
928 if (got_gl_error ("glBufferData index_buffer"))
929 goto HANDLE_ERROR_LOCKED;
931 eglglessink->egl_context->have_vbo = TRUE;
932 GST_DEBUG_OBJECT (eglglessink, "VBO setup done");
937 GST_ERROR_OBJECT (eglglessink, "Unable to perform VBO setup");
942 gst_eglglessink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
944 GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
946 g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
947 GST_DEBUG_OBJECT (eglglessink, "We got a window handle: %p", (gpointer) id);
949 /* OK, we have a new window */
950 GST_OBJECT_LOCK (eglglessink);
951 eglglessink->egl_context->window = (EGLNativeWindowType) id;
952 eglglessink->have_window = ((gpointer) id != NULL);
953 GST_OBJECT_UNLOCK (eglglessink);
959 gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
960 gint width, gint height)
962 GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
964 g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
966 GST_OBJECT_LOCK (eglglessink);
967 eglglessink->render_region.x = x;
968 eglglessink->render_region.y = y;
969 eglglessink->render_region.w = width;
970 eglglessink->render_region.h = height;
971 eglglessink->render_region_changed = TRUE;
972 eglglessink->render_region_user = (width != -1 && height != -1);
973 GST_OBJECT_UNLOCK (eglglessink);
979 queue_item_destroy (GstDataQueueItem * item)
981 if (item->object && !GST_IS_QUERY (item->object))
982 gst_mini_object_unref (item->object);
983 g_slice_free (GstDataQueueItem, item);
987 gst_eglglessink_queue_object (GstEglGlesSink * eglglessink, GstMiniObject * obj)
989 GstDataQueueItem *item;
990 GstFlowReturn last_flow;
992 g_mutex_lock (&eglglessink->render_lock);
993 last_flow = eglglessink->last_flow;
994 g_mutex_unlock (&eglglessink->render_lock);
996 if (last_flow != GST_FLOW_OK)
999 item = g_slice_new0 (GstDataQueueItem);
1002 item->object = NULL;
1003 else if (GST_IS_QUERY (obj))
1006 item->object = gst_mini_object_ref (obj);
1008 item->duration = GST_CLOCK_TIME_NONE;
1009 item->visible = TRUE;
1010 item->destroy = (GDestroyNotify) queue_item_destroy;
1012 GST_DEBUG_OBJECT (eglglessink, "Queueing object %" GST_PTR_FORMAT, obj);
1014 g_mutex_lock (&eglglessink->render_lock);
1015 if (!gst_data_queue_push (eglglessink->queue, item)) {
1016 item->destroy (item);
1017 g_mutex_unlock (&eglglessink->render_lock);
1018 GST_DEBUG_OBJECT (eglglessink, "Flushing");
1019 return GST_FLOW_FLUSHING;
1022 GST_DEBUG_OBJECT (eglglessink, "Waiting for object to be handled");
1024 g_cond_wait (&eglglessink->render_cond, &eglglessink->render_lock);
1025 } while (eglglessink->dequeued_object != obj
1026 && eglglessink->last_flow != GST_FLOW_FLUSHING);
1027 GST_DEBUG_OBJECT (eglglessink, "Object handled: %s",
1028 gst_flow_get_name (eglglessink->last_flow));
1029 last_flow = eglglessink->last_flow;
1030 g_mutex_unlock (&eglglessink->render_lock);
1032 return (obj ? last_flow : GST_FLOW_OK);
1036 gst_eglglessink_crop_changed (GstEglGlesSink * eglglessink,
1037 GstVideoCropMeta * crop)
1040 return (crop->x != eglglessink->crop.x ||
1041 crop->y != eglglessink->crop.y ||
1042 crop->width != eglglessink->crop.w ||
1043 crop->height != eglglessink->crop.h);
1046 return (eglglessink->crop.x != 0 || eglglessink->crop.y != 0 ||
1047 eglglessink->crop.w != eglglessink->configured_info.width ||
1048 eglglessink->crop.h != eglglessink->configured_info.height);
1052 gst_eglglessink_fill_texture (GstEglGlesSink * eglglessink, GstBuffer * buf)
1054 GstVideoFrame vframe;
1057 memset (&vframe, 0, sizeof (vframe));
1059 if (!gst_video_frame_map (&vframe, &eglglessink->configured_info, buf,
1061 GST_ERROR_OBJECT (eglglessink, "Couldn't map frame");
1065 w = GST_VIDEO_FRAME_WIDTH (&vframe);
1066 h = GST_VIDEO_FRAME_HEIGHT (&vframe);
1068 GST_DEBUG_OBJECT (eglglessink,
1069 "Got buffer %p: %dx%d size %" G_GSIZE_FORMAT, buf, w, h,
1070 gst_buffer_get_size (buf));
1072 switch (eglglessink->configured_info.finfo->format) {
1073 case GST_VIDEO_FORMAT_BGR:
1074 case GST_VIDEO_FORMAT_RGB:{
1079 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1080 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1082 glActiveTexture (GL_TEXTURE0);
1084 if (GST_ROUND_UP_8 (c_w * 3) == stride) {
1085 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1086 } else if (GST_ROUND_UP_4 (c_w * 3) == stride) {
1087 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1088 } else if (GST_ROUND_UP_2 (c_w * 3) == stride) {
1089 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1090 } else if (c_w * 3 == stride) {
1091 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1093 stride_width = stride;
1095 if (GST_ROUND_UP_8 (stride_width * 3) == stride) {
1096 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1097 } else if (GST_ROUND_UP_4 (stride_width * 3) == stride) {
1098 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1099 } else if (GST_ROUND_UP_2 (stride_width * 3) == stride) {
1100 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1101 } else if (stride_width * 3 == stride) {
1102 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1104 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1108 if (got_gl_error ("glPixelStorei"))
1111 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1113 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1114 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB,
1115 GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1118 case GST_VIDEO_FORMAT_RGB16:{
1123 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1124 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1126 glActiveTexture (GL_TEXTURE0);
1128 if (GST_ROUND_UP_8 (c_w * 2) == stride) {
1129 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1130 } else if (GST_ROUND_UP_4 (c_w * 2) == stride) {
1131 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1132 } else if (c_w * 2 == stride) {
1133 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1135 stride_width = stride;
1137 if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1138 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1139 } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) {
1140 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1141 } else if (stride_width * 2 == stride) {
1142 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1144 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1148 if (got_gl_error ("glPixelStorei"))
1151 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1153 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1154 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB,
1155 GL_UNSIGNED_SHORT_5_6_5, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1158 case GST_VIDEO_FORMAT_RGBA:
1159 case GST_VIDEO_FORMAT_BGRA:
1160 case GST_VIDEO_FORMAT_ARGB:
1161 case GST_VIDEO_FORMAT_ABGR:
1162 case GST_VIDEO_FORMAT_RGBx:
1163 case GST_VIDEO_FORMAT_BGRx:
1164 case GST_VIDEO_FORMAT_xRGB:
1165 case GST_VIDEO_FORMAT_xBGR:{
1170 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1171 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1173 glActiveTexture (GL_TEXTURE0);
1175 if (GST_ROUND_UP_8 (c_w * 4) == stride) {
1176 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1177 } else if (c_w * 4 == stride) {
1178 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1180 stride_width = stride;
1182 if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1183 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1184 } else if (stride_width * 4 == stride) {
1185 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1187 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1191 if (got_gl_error ("glPixelStorei"))
1194 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1196 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1197 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0,
1198 GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1201 case GST_VIDEO_FORMAT_AYUV:{
1206 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1207 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1209 glActiveTexture (GL_TEXTURE0);
1211 if (GST_ROUND_UP_8 (c_w * 4) == stride) {
1212 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1213 } else if (c_w * 4 == stride) {
1214 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1216 stride_width = stride;
1218 if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1219 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1220 } else if (stride_width * 4 == stride) {
1221 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1223 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1227 if (got_gl_error ("glPixelStorei"))
1230 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1232 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1233 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0,
1234 GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1237 case GST_VIDEO_FORMAT_Y444:
1238 case GST_VIDEO_FORMAT_I420:
1239 case GST_VIDEO_FORMAT_YV12:
1240 case GST_VIDEO_FORMAT_Y42B:
1241 case GST_VIDEO_FORMAT_Y41B:{
1246 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1247 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0);
1249 glActiveTexture (GL_TEXTURE0);
1251 if (GST_ROUND_UP_8 (c_w) == stride) {
1252 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1253 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1254 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1255 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1256 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1257 } else if (c_w == stride) {
1258 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1260 stride_width = stride;
1262 if (GST_ROUND_UP_8 (stride_width) == stride) {
1263 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1264 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1265 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1266 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1267 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1268 } else if (stride_width == stride) {
1269 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1271 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1275 if (got_gl_error ("glPixelStorei"))
1278 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1280 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1281 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1283 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0),
1284 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1285 GST_VIDEO_FRAME_COMP_DATA (&vframe, 0));
1288 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1289 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1);
1291 glActiveTexture (GL_TEXTURE1);
1293 if (GST_ROUND_UP_8 (c_w) == stride) {
1294 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1295 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1296 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1297 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1298 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1299 } else if (c_w == stride) {
1300 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1302 stride_width = stride;
1304 if (GST_ROUND_UP_8 (stride_width) == stride) {
1305 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1306 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1307 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1308 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1309 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1310 } else if (stride_width == stride) {
1311 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1313 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1317 if (got_gl_error ("glPixelStorei"))
1320 eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w);
1322 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]);
1323 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1325 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1),
1326 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1327 GST_VIDEO_FRAME_COMP_DATA (&vframe, 1));
1330 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 2);
1331 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 2);
1333 glActiveTexture (GL_TEXTURE2);
1335 if (GST_ROUND_UP_8 (c_w) == stride) {
1336 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1337 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1338 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1339 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1340 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1341 } else if (c_w == stride) {
1342 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1344 stride_width = stride;
1346 if (GST_ROUND_UP_8 (stride_width) == stride) {
1347 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1348 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1349 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1350 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1351 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1352 } else if (stride_width == stride) {
1353 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1355 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1359 if (got_gl_error ("glPixelStorei"))
1362 eglglessink->stride[2] = ((gdouble) stride_width) / ((gdouble) c_w);
1364 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[2]);
1365 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1367 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 2),
1368 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1369 GST_VIDEO_FRAME_COMP_DATA (&vframe, 2));
1372 case GST_VIDEO_FORMAT_NV12:
1373 case GST_VIDEO_FORMAT_NV21:{
1378 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1379 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0);
1381 glActiveTexture (GL_TEXTURE0);
1383 if (GST_ROUND_UP_8 (c_w) == stride) {
1384 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1385 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1386 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1387 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1388 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1389 } else if (c_w == stride) {
1390 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1392 stride_width = stride;
1394 if (GST_ROUND_UP_8 (stride_width) == stride) {
1395 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1396 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1397 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1398 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1399 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1400 } else if (stride_width == stride) {
1401 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1403 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1407 if (got_gl_error ("glPixelStorei"))
1410 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1412 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1413 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1415 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0),
1416 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1417 GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1420 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1421 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1);
1423 glActiveTexture (GL_TEXTURE1);
1425 if (GST_ROUND_UP_8 (c_w * 2) == stride) {
1426 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1427 } else if (GST_ROUND_UP_4 (c_w * 2) == stride) {
1428 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1429 } else if (c_w * 2 == stride) {
1430 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1432 stride_width = stride / 2;
1434 if (GST_ROUND_UP_8 (stride_width * 2) == stride) {
1435 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1436 } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) {
1437 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1438 } else if (stride_width * 2 == stride) {
1439 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1441 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1445 if (got_gl_error ("glPixelStorei"))
1448 eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w);
1450 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]);
1451 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
1453 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1),
1454 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
1455 GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1));
1459 g_assert_not_reached ();
1463 if (got_gl_error ("glTexImage2D"))
1466 gst_video_frame_unmap (&vframe);
1473 gst_video_frame_unmap (&vframe);
1478 /* Rendering and display */
1479 static GstFlowReturn
1480 gst_eglglessink_upload (GstEglGlesSink * eglglessink, GstBuffer * buf)
1482 GstVideoCropMeta *crop = NULL;
1485 GST_DEBUG_OBJECT (eglglessink, "Rendering previous buffer again");
1488 GstVideoGLTextureUploadMeta *upload_meta;
1490 crop = gst_buffer_get_video_crop_meta (buf);
1492 upload_meta = gst_buffer_get_video_gl_texture_upload_meta (buf);
1494 if (gst_eglglessink_crop_changed (eglglessink, crop)) {
1496 eglglessink->crop.x = crop->x;
1497 eglglessink->crop.y = crop->y;
1498 eglglessink->crop.w = crop->width;
1499 eglglessink->crop.h = crop->height;
1501 eglglessink->crop.x = 0;
1502 eglglessink->crop.y = 0;
1503 eglglessink->crop.w = eglglessink->configured_info.width;
1504 eglglessink->crop.h = eglglessink->configured_info.height;
1506 eglglessink->crop_changed = TRUE;
1512 if (upload_meta->n_textures != eglglessink->egl_context->n_textures)
1515 for (i = 0; i < eglglessink->egl_context->n_textures; i++) {
1517 glActiveTexture (GL_TEXTURE0);
1519 glActiveTexture (GL_TEXTURE1);
1521 glActiveTexture (GL_TEXTURE2);
1523 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]);
1526 if (!gst_video_gl_texture_upload_meta_upload (upload_meta,
1527 eglglessink->egl_context->texture))
1530 eglglessink->orientation = upload_meta->texture_orientation;
1531 eglglessink->stride[0] = 1;
1532 eglglessink->stride[1] = 1;
1533 eglglessink->stride[2] = 1;
1534 } else if (gst_buffer_n_memory (buf) >= 1 &&
1535 (mem = gst_buffer_peek_memory (buf, 0))
1536 && gst_is_egl_image_memory (mem)) {
1539 n = gst_buffer_n_memory (buf);
1541 for (i = 0; i < n; i++) {
1542 mem = gst_buffer_peek_memory (buf, i);
1544 g_assert (gst_is_egl_image_memory (mem));
1547 glActiveTexture (GL_TEXTURE0);
1549 glActiveTexture (GL_TEXTURE1);
1551 glActiveTexture (GL_TEXTURE2);
1553 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]);
1554 glEGLImageTargetTexture2DOES (GL_TEXTURE_2D,
1555 gst_egl_image_memory_get_image (mem));
1556 if (got_gl_error ("glEGLImageTargetTexture2DOES"))
1558 eglglessink->orientation = gst_egl_image_memory_get_orientation (mem);
1559 if (eglglessink->orientation !=
1560 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL
1561 && eglglessink->orientation !=
1562 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) {
1563 GST_ERROR_OBJECT (eglglessink, "Unsupported EGLImage orientation");
1564 return GST_FLOW_ERROR;
1567 gst_buffer_replace (&eglglessink->last_buffer, buf);
1568 eglglessink->stride[0] = 1;
1569 eglglessink->stride[1] = 1;
1570 eglglessink->stride[2] = 1;
1572 eglglessink->orientation =
1573 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL;
1574 if (!gst_eglglessink_fill_texture (eglglessink, buf))
1583 GST_ERROR_OBJECT (eglglessink, "Failed to upload texture");
1584 return GST_FLOW_ERROR;
1588 static GstFlowReturn
1589 gst_eglglessink_render (GstEglGlesSink * eglglessink)
1594 /* If no one has set a display rectangle on us initialize
1595 * a sane default. According to the docs on the xOverlay
1596 * interface we are supposed to fill the overlay 100%. We
1597 * do this trying to take PAR/DAR into account unless the
1598 * calling party explicitly ask us not to by setting
1599 * force_aspect_ratio to FALSE.
1601 if (gst_egl_adaptation_update_surface_dimensions (eglglessink->egl_context) ||
1602 eglglessink->render_region_changed ||
1603 !eglglessink->display_region.w || !eglglessink->display_region.h ||
1604 eglglessink->crop_changed) {
1605 GST_OBJECT_LOCK (eglglessink);
1607 if (!eglglessink->render_region_user) {
1608 eglglessink->render_region.x = 0;
1609 eglglessink->render_region.y = 0;
1610 eglglessink->render_region.w = eglglessink->egl_context->surface_width;
1611 eglglessink->render_region.h = eglglessink->egl_context->surface_height;
1613 eglglessink->render_region_changed = FALSE;
1614 eglglessink->crop_changed = FALSE;
1616 if (!eglglessink->force_aspect_ratio) {
1617 eglglessink->display_region.x = 0;
1618 eglglessink->display_region.y = 0;
1619 eglglessink->display_region.w = eglglessink->render_region.w;
1620 eglglessink->display_region.h = eglglessink->render_region.h;
1622 GstVideoRectangle frame;
1627 if (!gst_video_calculate_display_ratio (&dar_n, &dar_d,
1628 eglglessink->crop.w, eglglessink->crop.h,
1629 eglglessink->configured_info.par_n,
1630 eglglessink->configured_info.par_d,
1631 eglglessink->egl_context->pixel_aspect_ratio_n,
1632 eglglessink->egl_context->pixel_aspect_ratio_d)) {
1633 GST_WARNING_OBJECT (eglglessink, "Could not compute resulting DAR");
1634 frame.w = eglglessink->crop.w;
1635 frame.h = eglglessink->crop.h;
1637 /* Find suitable matching new size acording to dar & par
1638 * rationale for prefering leaving the height untouched
1639 * comes from interlacing considerations.
1640 * XXX: Move this to gstutils?
1642 if (eglglessink->crop.h % dar_d == 0) {
1644 gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d);
1645 frame.h = eglglessink->crop.h;
1646 } else if (eglglessink->crop.w % dar_n == 0) {
1648 gst_util_uint64_scale_int (eglglessink->crop.w, dar_d, dar_n);
1649 frame.w = eglglessink->crop.w;
1651 /* Neither width nor height can be precisely scaled.
1652 * Prefer to leave height untouched. See comment above.
1655 gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d);
1656 frame.h = eglglessink->crop.h;
1660 gst_video_sink_center_rect (frame, eglglessink->render_region,
1661 &eglglessink->display_region, TRUE);
1664 glViewport (eglglessink->render_region.x,
1665 eglglessink->egl_context->surface_height -
1666 eglglessink->render_region.y -
1667 eglglessink->render_region.h,
1668 eglglessink->render_region.w, eglglessink->render_region.h);
1670 /* Clear the surface once if its content is preserved */
1671 if (eglglessink->egl_context->buffer_preserved) {
1672 glClearColor (0.0, 0.0, 0.0, 1.0);
1673 glClear (GL_COLOR_BUFFER_BIT);
1676 if (!gst_eglglessink_setup_vbo (eglglessink, FALSE)) {
1677 GST_OBJECT_UNLOCK (eglglessink);
1678 GST_ERROR_OBJECT (eglglessink, "VBO setup failed");
1681 GST_OBJECT_UNLOCK (eglglessink);
1684 if (!eglglessink->egl_context->buffer_preserved) {
1685 /* Draw black borders */
1686 GST_DEBUG_OBJECT (eglglessink, "Drawing black border 1");
1687 glUseProgram (eglglessink->egl_context->glslprogram[1]);
1689 glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3,
1690 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (8 * sizeof (coord5)));
1691 if (got_gl_error ("glVertexAttribPointer"))
1694 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1695 if (got_gl_error ("glDrawElements"))
1698 GST_DEBUG_OBJECT (eglglessink, "Drawing black border 2");
1700 glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3,
1701 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (12 * sizeof (coord5)));
1702 if (got_gl_error ("glVertexAttribPointer"))
1705 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1706 if (got_gl_error ("glDrawElements"))
1710 /* Draw video frame */
1711 GST_DEBUG_OBJECT (eglglessink, "Drawing video frame");
1713 glUseProgram (eglglessink->egl_context->glslprogram[0]);
1715 glUniform2f (eglglessink->egl_context->tex_scale_loc[0][0],
1716 eglglessink->stride[0], 1);
1717 glUniform2f (eglglessink->egl_context->tex_scale_loc[0][1],
1718 eglglessink->stride[1], 1);
1719 glUniform2f (eglglessink->egl_context->tex_scale_loc[0][2],
1720 eglglessink->stride[2], 1);
1722 for (i = 0; i < eglglessink->egl_context->n_textures; i++) {
1723 glUniform1i (eglglessink->egl_context->tex_loc[0][i], i);
1724 if (got_gl_error ("glUniform1i"))
1728 if (eglglessink->orientation ==
1729 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) {
1730 glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3,
1731 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (0 * sizeof (coord5)));
1732 if (got_gl_error ("glVertexAttribPointer"))
1735 glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2,
1736 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (3 * sizeof (gfloat)));
1737 if (got_gl_error ("glVertexAttribPointer"))
1739 } else if (eglglessink->orientation ==
1740 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) {
1741 glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3,
1742 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (4 * sizeof (coord5)));
1743 if (got_gl_error ("glVertexAttribPointer"))
1746 glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2,
1747 GL_FLOAT, GL_FALSE, sizeof (coord5),
1748 (gpointer) (4 * sizeof (coord5) + 3 * sizeof (gfloat)));
1749 if (got_gl_error ("glVertexAttribPointer"))
1752 g_assert_not_reached ();
1755 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1756 if (got_gl_error ("glDrawElements"))
1759 if (!gst_egl_adaptation_context_swap_buffers (eglglessink->egl_context)) {
1764 GST_DEBUG_OBJECT (eglglessink, "Succesfully rendered 1 frame");
1768 GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame");
1770 return GST_FLOW_ERROR;
1773 static GstFlowReturn
1774 gst_eglglessink_prepare (GstBaseSink * bsink, GstBuffer * buf)
1776 GstEglGlesSink *eglglessink;
1778 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1780 eglglessink = GST_EGLGLESSINK (bsink);
1781 GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
1783 return gst_eglglessink_queue_object (eglglessink, GST_MINI_OBJECT_CAST (buf));
1786 static GstFlowReturn
1787 gst_eglglessink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1789 GstEglGlesSink *eglglessink;
1791 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1793 eglglessink = GST_EGLGLESSINK (vsink);
1794 GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
1796 return gst_eglglessink_queue_object (eglglessink, NULL);
1800 gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1802 GstEglGlesSink *eglglessink;
1803 GstCaps *ret = NULL;
1805 eglglessink = GST_EGLGLESSINK (bsink);
1807 GST_OBJECT_LOCK (eglglessink);
1808 if (eglglessink->sinkcaps) {
1809 ret = gst_caps_ref (eglglessink->sinkcaps);
1812 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
1815 GST_OBJECT_UNLOCK (eglglessink);
1819 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1821 gst_caps_unref (ret);
1829 gst_eglglessink_event (GstBaseSink * bsink, GstEvent * event)
1831 GstEglGlesSink *eglglessink;
1833 eglglessink = GST_EGLGLESSINK (bsink);
1835 switch (GST_EVENT_TYPE (event)) {
1836 case GST_EVENT_CONTEXT:{
1837 GstContext *context;
1838 GstEGLDisplay *display;
1840 gst_event_parse_context (event, &context);
1842 if (gst_context_get_egl_display (context, &display)) {
1843 GST_OBJECT_LOCK (eglglessink);
1844 if (eglglessink->egl_context->set_display)
1845 gst_egl_display_unref (eglglessink->egl_context->set_display);
1846 eglglessink->egl_context->set_display = display;
1847 GST_OBJECT_UNLOCK (eglglessink);
1850 gst_context_unref (context);
1852 return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->event (bsink,
1857 return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->event (bsink,
1864 gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query)
1866 GstEglGlesSink *eglglessink;
1868 eglglessink = GST_EGLGLESSINK (bsink);
1870 switch (GST_QUERY_TYPE (query)) {
1871 case GST_QUERY_CONTEXT:{
1874 GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink, query);
1876 n = gst_query_get_n_context_types (query);
1877 for (i = 0; i < n; i++) {
1878 const gchar *context_type = NULL;
1880 gst_query_parse_nth_context_type (query, i, &context_type);
1881 if (g_strcmp0 (context_type, GST_EGL_DISPLAY_CONTEXT_TYPE) == 0) {
1882 GstContext *context, *old_context;
1884 gst_query_parse_context (query, &old_context);
1886 context = gst_context_copy (old_context);
1888 context = gst_context_new ();
1890 gst_context_set_egl_display (context,
1891 eglglessink->egl_context->display);
1892 gst_query_set_context (query, context);
1893 gst_context_unref (context);
1902 return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink,
1909 gst_eglglessink_set_context (GstElement * element, GstContext * context)
1911 GstEglGlesSink *eglglessink;
1912 GstEGLDisplay *display = NULL;
1914 eglglessink = GST_EGLGLESSINK (element);
1916 if (gst_context_get_egl_display (context, &display)) {
1917 GST_OBJECT_LOCK (eglglessink);
1918 if (eglglessink->egl_context->set_display)
1919 gst_egl_display_unref (eglglessink->egl_context->set_display);
1920 eglglessink->egl_context->set_display = display;
1921 GST_OBJECT_UNLOCK (eglglessink);
1924 GST_OBJECT_LOCK (eglglessink);
1925 context = gst_context_copy (context);
1926 gst_context_set_egl_display (context, eglglessink->egl_context->display);
1927 GST_OBJECT_UNLOCK (eglglessink);
1929 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1930 gst_context_unref (context);
1934 gst_eglglessink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1936 GstEglGlesSink *eglglessink;
1937 GstBufferPool *pool;
1938 GstStructure *config;
1943 GstAllocator *allocator;
1944 GstAllocationParams params;
1946 eglglessink = GST_EGLGLESSINK (bsink);
1948 gst_allocation_params_init (¶ms);
1950 gst_query_parse_allocation (query, &caps, &need_pool);
1952 GST_ERROR_OBJECT (eglglessink, "allocation query without caps");
1956 if (!gst_video_info_from_caps (&info, caps)) {
1957 GST_ERROR_OBJECT (eglglessink, "allocation query with invalid caps");
1961 GST_OBJECT_LOCK (eglglessink);
1962 pool = eglglessink->pool ? gst_object_ref (eglglessink->pool) : NULL;
1963 GST_OBJECT_UNLOCK (eglglessink);
1968 /* we had a pool, check caps */
1969 GST_DEBUG_OBJECT (eglglessink, "check existing pool caps");
1970 config = gst_buffer_pool_get_config (pool);
1971 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1973 if (!gst_caps_is_equal (caps, pcaps)) {
1974 GST_DEBUG_OBJECT (eglglessink, "pool has different caps");
1975 /* different caps, we can't use this pool */
1976 gst_object_unref (pool);
1979 gst_structure_free (config);
1982 if (pool == NULL && need_pool) {
1985 if (!gst_video_info_from_caps (&info, caps)) {
1986 GST_ERROR_OBJECT (eglglessink, "allocation query has invalid caps %"
1987 GST_PTR_FORMAT, caps);
1991 GST_DEBUG_OBJECT (eglglessink, "create new pool");
1993 gst_egl_image_buffer_pool_new (eglglessink,
1994 eglglessink->egl_context->display);
1996 /* the normal size of a frame */
1999 config = gst_buffer_pool_get_config (pool);
2000 /* we need at least 2 buffer because we hold on to the last one */
2001 gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
2002 gst_buffer_pool_config_set_allocator (config, NULL, ¶ms);
2003 if (!gst_buffer_pool_set_config (pool, config)) {
2004 gst_object_unref (pool);
2005 GST_ERROR_OBJECT (eglglessink, "failed to set pool configuration");
2011 /* we need at least 2 buffer because we hold on to the last one */
2012 gst_query_add_allocation_pool (query, pool, size, 2, 0);
2013 gst_object_unref (pool);
2016 /* First the default allocator */
2017 if (!gst_egl_image_memory_is_mappable ()) {
2018 allocator = gst_allocator_find (NULL);
2019 gst_query_add_allocation_param (query, allocator, ¶ms);
2020 gst_object_unref (allocator);
2023 allocator = gst_egl_image_allocator_obtain ();
2024 if (!gst_egl_image_memory_is_mappable ())
2025 params.flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
2026 gst_query_add_allocation_param (query, allocator, ¶ms);
2027 gst_object_unref (allocator);
2029 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2030 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2031 gst_query_add_allocation_meta (query,
2032 GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL);
2038 gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps)
2040 gboolean ret = TRUE;
2043 gst_video_info_init (&info);
2044 if (!(ret = gst_video_info_from_caps (&info, caps))) {
2045 GST_ERROR_OBJECT (eglglessink, "Couldn't parse caps");
2049 eglglessink->configured_info = info;
2050 GST_VIDEO_SINK_WIDTH (eglglessink) = info.width;
2051 GST_VIDEO_SINK_HEIGHT (eglglessink) = info.height;
2053 if (eglglessink->configured_caps) {
2054 GST_DEBUG_OBJECT (eglglessink, "Caps were already set");
2055 if (gst_caps_can_intersect (caps, eglglessink->configured_caps)) {
2056 GST_DEBUG_OBJECT (eglglessink, "Caps are compatible anyway");
2060 GST_DEBUG_OBJECT (eglglessink, "Caps are not compatible, reconfiguring");
2062 /* EGL/GLES cleanup */
2063 gst_egl_adaptation_cleanup (eglglessink->egl_context);
2065 gst_caps_unref (eglglessink->configured_caps);
2066 eglglessink->configured_caps = NULL;
2069 if (!gst_egl_adaptation_choose_config (eglglessink->egl_context)) {
2070 GST_ERROR_OBJECT (eglglessink, "Couldn't choose EGL config");
2074 gst_caps_replace (&eglglessink->configured_caps, caps);
2076 /* By now the application should have set a window
2077 * if it meant to do so
2079 GST_OBJECT_LOCK (eglglessink);
2080 if (!eglglessink->have_window) {
2082 GST_INFO_OBJECT (eglglessink,
2083 "No window. Will attempt internal window creation");
2084 if (!gst_eglglessink_create_window (eglglessink, info.width, info.height)) {
2085 GST_ERROR_OBJECT (eglglessink, "Internal window creation failed!");
2086 GST_OBJECT_UNLOCK (eglglessink);
2089 eglglessink->using_own_window = TRUE;
2090 eglglessink->have_window = TRUE;
2092 GST_DEBUG_OBJECT (eglglessink, "Using window handle %p",
2093 (gpointer) eglglessink->egl_context->window);
2094 eglglessink->egl_context->used_window = eglglessink->egl_context->window;
2095 GST_OBJECT_UNLOCK (eglglessink);
2096 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (eglglessink),
2097 (guintptr) eglglessink->egl_context->used_window);
2099 if (!eglglessink->egl_context->have_surface) {
2100 if (!gst_egl_adaptation_init_egl_surface (eglglessink->egl_context,
2101 eglglessink->configured_info.finfo->format)) {
2102 GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface from window");
2107 gst_egl_adaptation_init_egl_exts (eglglessink->egl_context);
2110 GST_INFO_OBJECT (eglglessink, "Configured caps successfully");
2114 GST_ERROR_OBJECT (eglglessink, "Configuring caps failed");
2119 gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2121 GstEglGlesSink *eglglessink;
2123 GstBufferPool *newpool, *oldpool;
2124 GstStructure *config;
2125 GstAllocationParams params = { 0, };
2127 eglglessink = GST_EGLGLESSINK (bsink);
2129 GST_DEBUG_OBJECT (eglglessink,
2130 "Current caps %" GST_PTR_FORMAT ", setting caps %"
2131 GST_PTR_FORMAT, eglglessink->current_caps, caps);
2133 if (gst_eglglessink_queue_object (eglglessink,
2134 GST_MINI_OBJECT_CAST (caps)) != GST_FLOW_OK) {
2135 GST_ERROR_OBJECT (eglglessink, "Failed to configure caps");
2139 if (!gst_video_info_from_caps (&info, caps)) {
2140 GST_ERROR_OBJECT (eglglessink, "Invalid caps %" GST_PTR_FORMAT, caps);
2145 gst_egl_image_buffer_pool_new (eglglessink,
2146 eglglessink->egl_context->display);
2147 config = gst_buffer_pool_get_config (newpool);
2148 /* we need at least 2 buffer because we hold on to the last one */
2149 gst_buffer_pool_config_set_params (config, caps, info.size, 2, 0);
2150 gst_buffer_pool_config_set_allocator (config, NULL, ¶ms);
2151 if (!gst_buffer_pool_set_config (newpool, config)) {
2152 gst_object_unref (newpool);
2153 GST_ERROR_OBJECT (eglglessink, "Failed to set buffer pool configuration");
2157 GST_OBJECT_LOCK (eglglessink);
2158 oldpool = eglglessink->pool;
2159 eglglessink->pool = newpool;
2160 GST_OBJECT_UNLOCK (eglglessink);
2163 gst_object_unref (oldpool);
2165 gst_caps_replace (&eglglessink->current_caps, caps);
2171 gst_eglglessink_open (GstEglGlesSink * eglglessink)
2173 if (!egl_init (eglglessink)) {
2181 gst_eglglessink_close (GstEglGlesSink * eglglessink)
2183 if (eglglessink->egl_context->display) {
2184 gst_egl_display_unref (eglglessink->egl_context->display);
2185 eglglessink->egl_context->display = NULL;
2188 gst_caps_unref (eglglessink->sinkcaps);
2189 eglglessink->sinkcaps = NULL;
2190 eglglessink->egl_started = FALSE;
2192 GST_OBJECT_LOCK (eglglessink);
2193 if (eglglessink->pool)
2194 gst_object_unref (eglglessink->pool);
2195 eglglessink->pool = NULL;
2196 GST_OBJECT_UNLOCK (eglglessink);
2201 static GstStateChangeReturn
2202 gst_eglglessink_change_state (GstElement * element, GstStateChange transition)
2204 GstEglGlesSink *eglglessink;
2205 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2207 eglglessink = GST_EGLGLESSINK (element);
2209 switch (transition) {
2210 case GST_STATE_CHANGE_NULL_TO_READY:
2211 if (!gst_eglglessink_open (eglglessink)) {
2212 ret = GST_STATE_CHANGE_FAILURE;
2216 case GST_STATE_CHANGE_READY_TO_PAUSED:
2217 if (!gst_eglglessink_start (eglglessink)) {
2218 ret = GST_STATE_CHANGE_FAILURE;
2226 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2227 if (ret == GST_STATE_CHANGE_FAILURE)
2230 switch (transition) {
2231 case GST_STATE_CHANGE_READY_TO_NULL:
2232 if (!gst_eglglessink_close (eglglessink)) {
2233 ret = GST_STATE_CHANGE_FAILURE;
2237 case GST_STATE_CHANGE_PAUSED_TO_READY:
2238 if (!gst_eglglessink_stop (eglglessink)) {
2239 ret = GST_STATE_CHANGE_FAILURE;
2252 gst_eglglessink_finalize (GObject * object)
2254 GstEglGlesSink *eglglessink;
2256 g_return_if_fail (GST_IS_EGLGLESSINK (object));
2258 eglglessink = GST_EGLGLESSINK (object);
2260 gst_egl_adaptation_context_free (eglglessink->egl_context);
2262 if (eglglessink->queue)
2263 g_object_unref (eglglessink->queue);
2264 eglglessink->queue = NULL;
2266 g_cond_clear (&eglglessink->render_cond);
2267 g_mutex_clear (&eglglessink->render_lock);
2269 G_OBJECT_CLASS (parent_class)->finalize (object);
2273 gst_eglglessink_set_property (GObject * object, guint prop_id,
2274 const GValue * value, GParamSpec * pspec)
2276 GstEglGlesSink *eglglessink;
2278 g_return_if_fail (GST_IS_EGLGLESSINK (object));
2280 eglglessink = GST_EGLGLESSINK (object);
2283 case PROP_CREATE_WINDOW:
2284 eglglessink->create_window = g_value_get_boolean (value);
2286 case PROP_FORCE_ASPECT_RATIO:
2287 eglglessink->force_aspect_ratio = g_value_get_boolean (value);
2290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2296 gst_eglglessink_get_property (GObject * object, guint prop_id,
2297 GValue * value, GParamSpec * pspec)
2299 GstEglGlesSink *eglglessink;
2301 g_return_if_fail (GST_IS_EGLGLESSINK (object));
2303 eglglessink = GST_EGLGLESSINK (object);
2306 case PROP_CREATE_WINDOW:
2307 g_value_set_boolean (value, eglglessink->create_window);
2309 case PROP_FORCE_ASPECT_RATIO:
2310 g_value_set_boolean (value, eglglessink->force_aspect_ratio);
2313 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2318 /* initialize the eglglessink's class */
2320 gst_eglglessink_class_init (GstEglGlesSinkClass * klass)
2322 GObjectClass *gobject_class;
2323 GstElementClass *gstelement_class;
2324 GstBaseSinkClass *gstbasesink_class;
2325 GstVideoSinkClass *gstvideosink_class;
2327 gobject_class = (GObjectClass *) klass;
2328 gstelement_class = (GstElementClass *) klass;
2329 gstbasesink_class = (GstBaseSinkClass *) klass;
2330 gstvideosink_class = (GstVideoSinkClass *) klass;
2332 gobject_class->set_property = gst_eglglessink_set_property;
2333 gobject_class->get_property = gst_eglglessink_get_property;
2334 gobject_class->finalize = gst_eglglessink_finalize;
2336 gstelement_class->change_state = gst_eglglessink_change_state;
2337 gstelement_class->set_context = gst_eglglessink_set_context;
2339 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_setcaps);
2340 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_getcaps);
2341 gstbasesink_class->propose_allocation =
2342 GST_DEBUG_FUNCPTR (gst_eglglessink_propose_allocation);
2343 gstbasesink_class->prepare = GST_DEBUG_FUNCPTR (gst_eglglessink_prepare);
2344 gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_eglglessink_query);
2345 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_eglglessink_event);
2347 gstvideosink_class->show_frame =
2348 GST_DEBUG_FUNCPTR (gst_eglglessink_show_frame);
2350 g_object_class_install_property (gobject_class, PROP_CREATE_WINDOW,
2351 g_param_spec_boolean ("create-window", "Create Window",
2352 "If set to true, the sink will attempt to create it's own window to "
2353 "render to if none is provided. This is currently only supported "
2354 "when the sink is used under X11",
2355 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2356 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2357 g_param_spec_boolean ("force-aspect-ratio",
2358 "Respect aspect ratio when scaling",
2359 "If set to true, the sink will attempt to preserve the incoming "
2360 "frame's geometry while scaling, taking both the storage's and "
2361 "display's pixel aspect ratio into account",
2362 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2364 gst_element_class_set_static_metadata (gstelement_class,
2365 "EGL/GLES vout Sink",
2367 "An EGL/GLES Video Output Sink Implementing the VideoOverlay interface",
2368 "Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>, "
2369 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
2371 gst_element_class_add_pad_template (gstelement_class,
2372 gst_static_pad_template_get (&gst_eglglessink_sink_template_factory));
2376 queue_check_full_func (GstDataQueue * queue, guint visible, guint bytes,
2377 guint64 time, gpointer checkdata)
2379 return visible != 0;
2383 gst_eglglessink_init (GstEglGlesSink * eglglessink)
2385 eglglessink->egl_context =
2386 gst_egl_adaptation_context_new (GST_ELEMENT_CAST (eglglessink));
2391 eglglessink->have_window = FALSE;
2392 eglglessink->egl_context->have_surface = FALSE;
2393 eglglessink->egl_context->have_vbo = FALSE;
2394 eglglessink->egl_context->have_texture = FALSE;
2395 eglglessink->egl_started = FALSE;
2396 eglglessink->using_own_window = FALSE;
2399 eglglessink->create_window = TRUE;
2400 eglglessink->force_aspect_ratio = TRUE;
2402 g_mutex_init (&eglglessink->render_lock);
2403 g_cond_init (&eglglessink->render_cond);
2404 eglglessink->queue =
2405 gst_data_queue_new (queue_check_full_func, NULL, NULL, NULL);
2406 eglglessink->last_flow = GST_FLOW_FLUSHING;
2408 eglglessink->render_region.x = 0;
2409 eglglessink->render_region.y = 0;
2410 eglglessink->render_region.w = -1;
2411 eglglessink->render_region.h = -1;
2412 eglglessink->render_region_changed = TRUE;
2413 eglglessink->render_region_user = FALSE;
2416 static GstBufferPool *
2417 gst_egl_image_buffer_pool_new (GstEglGlesSink *
2418 eglglessink, GstEGLDisplay * display)
2420 GstEGLImageBufferPool *pool;
2422 pool = g_object_new (gst_egl_image_buffer_pool_get_type (), NULL);
2423 pool->display = gst_egl_display_ref (display);
2424 pool->sink = gst_object_ref (eglglessink);
2426 return (GstBufferPool *) pool;
2429 /* entry point to initialize the plug-in
2430 * initialize the plug-in itself
2431 * register the element factories and other features
2434 eglglessink_plugin_init (GstPlugin * plugin)
2436 /* debug category for fltering log messages */
2437 GST_DEBUG_CATEGORY_INIT (gst_eglglessink_debug, "eglglessink",
2438 0, "Simple EGL/GLES Sink");
2440 gst_egl_adaption_init ();
2443 GST_DEBUG ("Initialize BCM host");
2447 return gst_element_register (plugin, "eglglessink", GST_RANK_SECONDARY,
2448 GST_TYPE_EGLGLESSINK);
2451 /* gstreamer looks for this structure to register eglglessinks */
2452 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2456 eglglessink_plugin_init,
2457 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)