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>
126 #include <gst/egl/egl.h>
129 #include <EGL/eglext.h>
130 #include <GLES2/gl2.h>
131 #include <GLES2/gl2ext.h>
134 #include <bcm_host.h>
137 #include "gstegladaptation.h"
138 #include "gsteglglessink.h"
140 GST_DEBUG_CATEGORY_STATIC (gst_eglglessink_debug);
141 #define GST_CAT_DEFAULT gst_eglglessink_debug
143 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
145 /* Input capabilities. */
146 static GstStaticPadTemplate gst_eglglessink_sink_template_factory =
147 GST_STATIC_PAD_TEMPLATE ("sink",
150 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
151 (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE,
152 "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
153 "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
154 "RGB, BGR, RGB16 }") ";"
155 GST_VIDEO_CAPS_MAKE_WITH_FEATURES
156 (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
157 "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
158 "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
159 "RGB, BGR, RGB16 }") ";" GST_VIDEO_CAPS_MAKE ("{ "
160 "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, "
161 "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, "
162 "RGB, BGR, RGB16 }")));
164 /* Filter signals and args */
175 PROP_FORCE_ASPECT_RATIO,
178 static void gst_eglglessink_finalize (GObject * object);
179 static void gst_eglglessink_get_property (GObject * object, guint prop_id,
180 GValue * value, GParamSpec * pspec);
181 static void gst_eglglessink_set_property (GObject * object, guint prop_id,
182 const GValue * value, GParamSpec * pspec);
183 static GstStateChangeReturn gst_eglglessink_change_state (GstElement * element,
184 GstStateChange transition);
185 static void gst_eglglessink_set_context (GstElement * element,
186 GstContext * context);
187 static GstFlowReturn gst_eglglessink_prepare (GstBaseSink * bsink,
189 static GstFlowReturn gst_eglglessink_show_frame (GstVideoSink * vsink,
191 static gboolean gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps);
192 static GstCaps *gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter);
193 static gboolean gst_eglglessink_propose_allocation (GstBaseSink * bsink,
195 static gboolean gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query);
196 static gboolean gst_eglglessink_event (GstBaseSink * bsink, GstEvent * event);
198 /* VideoOverlay interface cruft */
199 static void gst_eglglessink_videooverlay_init (GstVideoOverlayInterface *
202 /* Actual VideoOverlay interface funcs */
203 static void gst_eglglessink_expose (GstVideoOverlay * overlay);
204 static void gst_eglglessink_set_window_handle (GstVideoOverlay * overlay,
206 static void gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay,
207 gint x, gint y, gint width, gint height);
210 static gboolean gst_eglglessink_create_window (GstEglGlesSink *
211 eglglessink, gint width, gint height);
212 static gboolean gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink,
215 gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps);
216 static GstFlowReturn gst_eglglessink_upload (GstEglGlesSink * sink,
218 static GstFlowReturn gst_eglglessink_render (GstEglGlesSink * sink);
219 static GstFlowReturn gst_eglglessink_queue_object (GstEglGlesSink * sink,
220 GstMiniObject * obj);
221 static inline gboolean egl_init (GstEglGlesSink * eglglessink);
222 static GstBufferPool *gst_egl_image_buffer_pool_new (GstEglGlesSink *
223 eglglessink, GstEGLDisplay * display);
225 /* EGLImage memory, buffer pool, etc */
228 GstVideoBufferPool parent;
230 GstEglGlesSink *sink;
231 GstAllocator *allocator;
232 GstAllocationParams params;
234 gboolean add_metavideo;
235 gboolean want_eglimage;
236 GstEGLDisplay *display;
237 } GstEGLImageBufferPool;
239 typedef GstVideoBufferPoolClass GstEGLImageBufferPoolClass;
241 #define GST_EGL_IMAGE_BUFFER_POOL(p) ((GstEGLImageBufferPool*)(p))
243 GType gst_egl_image_buffer_pool_get_type (void);
245 G_DEFINE_TYPE (GstEGLImageBufferPool, gst_egl_image_buffer_pool,
246 GST_TYPE_VIDEO_BUFFER_POOL);
248 static const gchar **
249 gst_egl_image_buffer_pool_get_options (GstBufferPool * bpool)
251 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL
258 gst_egl_image_buffer_pool_set_config (GstBufferPool * bpool,
259 GstStructure * config)
261 GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
266 gst_object_unref (pool->allocator);
267 pool->allocator = NULL;
269 if (!GST_BUFFER_POOL_CLASS
270 (gst_egl_image_buffer_pool_parent_class)->set_config (bpool, config))
273 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
277 if (!gst_video_info_from_caps (&info, caps))
280 if (!gst_buffer_pool_config_get_allocator (config, &pool->allocator,
284 gst_object_ref (pool->allocator);
286 pool->add_metavideo =
287 gst_buffer_pool_config_has_option (config,
288 GST_BUFFER_POOL_OPTION_VIDEO_META);
290 pool->want_eglimage = (pool->allocator
291 && g_strcmp0 (pool->allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0);
299 gst_egl_image_buffer_pool_alloc_buffer (GstBufferPool * bpool,
300 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
302 GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
306 if (!pool->add_metavideo || !pool->want_eglimage)
308 GST_BUFFER_POOL_CLASS
309 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
312 if (!pool->allocator)
313 return GST_FLOW_NOT_NEGOTIATED;
315 switch (pool->info.finfo->format) {
316 case GST_VIDEO_FORMAT_RGB:
317 case GST_VIDEO_FORMAT_BGR:
318 case GST_VIDEO_FORMAT_RGB16:
319 case GST_VIDEO_FORMAT_NV12:
320 case GST_VIDEO_FORMAT_NV21:
321 case GST_VIDEO_FORMAT_RGBA:
322 case GST_VIDEO_FORMAT_BGRA:
323 case GST_VIDEO_FORMAT_ARGB:
324 case GST_VIDEO_FORMAT_ABGR:
325 case GST_VIDEO_FORMAT_RGBx:
326 case GST_VIDEO_FORMAT_BGRx:
327 case GST_VIDEO_FORMAT_xRGB:
328 case GST_VIDEO_FORMAT_xBGR:
329 case GST_VIDEO_FORMAT_AYUV:
330 case GST_VIDEO_FORMAT_YV12:
331 case GST_VIDEO_FORMAT_I420:
332 case GST_VIDEO_FORMAT_Y444:
333 case GST_VIDEO_FORMAT_Y42B:
334 case GST_VIDEO_FORMAT_Y41B:{
340 s = gst_structure_new ("eglglessink-allocate-eglimage",
341 "format", GST_TYPE_VIDEO_FORMAT, pool->info.finfo->format,
342 "width", G_TYPE_INT, pool->info.width,
343 "height", G_TYPE_INT, pool->info.height, NULL);
344 query = gst_query_new_custom (GST_QUERY_CUSTOM, s);
347 gst_eglglessink_queue_object (pool->sink,
348 GST_MINI_OBJECT_CAST (query));
350 if (ret != GST_FLOW_OK || !gst_structure_has_field (s, "buffer")) {
351 GST_WARNING ("Fallback memory allocation");
352 gst_query_unref (query);
354 GST_BUFFER_POOL_CLASS
355 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
359 v = gst_structure_get_value (s, "buffer");
360 *buffer = GST_BUFFER_CAST (g_value_get_pointer (v));
361 gst_query_unref (query);
364 GST_WARNING ("Fallback memory allocation");
366 GST_BUFFER_POOL_CLASS
367 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
376 GST_BUFFER_POOL_CLASS
377 (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
382 return GST_FLOW_ERROR;
386 gst_egl_image_buffer_pool_acquire_buffer (GstBufferPool * bpool,
387 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
390 GstEGLImageBufferPool *pool;
393 GST_BUFFER_POOL_CLASS
394 (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
396 if (ret != GST_FLOW_OK || !*buffer)
399 pool = GST_EGL_IMAGE_BUFFER_POOL (bpool);
401 /* XXX: Don't return the memory we just rendered, glEGLImageTargetTexture2DOES()
402 * keeps the EGLImage unmappable until the next one is uploaded
404 if (*buffer && *buffer == pool->sink->last_buffer) {
405 GstBuffer *oldbuf = *buffer;
408 GST_BUFFER_POOL_CLASS
409 (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
411 gst_object_replace ((GstObject **) & oldbuf->pool, (GstObject *) pool);
412 gst_buffer_unref (oldbuf);
419 gst_egl_image_buffer_pool_finalize (GObject * object)
421 GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (object);
424 gst_object_unref (pool->allocator);
425 pool->allocator = NULL;
428 gst_object_unref (pool->sink);
432 gst_egl_display_unref (pool->display);
433 pool->display = NULL;
435 G_OBJECT_CLASS (gst_egl_image_buffer_pool_parent_class)->finalize (object);
439 gst_egl_image_buffer_pool_class_init (GstEGLImageBufferPoolClass * klass)
441 GObjectClass *gobject_class = (GObjectClass *) klass;
442 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
444 gobject_class->finalize = gst_egl_image_buffer_pool_finalize;
445 gstbufferpool_class->get_options = gst_egl_image_buffer_pool_get_options;
446 gstbufferpool_class->set_config = gst_egl_image_buffer_pool_set_config;
447 gstbufferpool_class->alloc_buffer = gst_egl_image_buffer_pool_alloc_buffer;
448 gstbufferpool_class->acquire_buffer =
449 gst_egl_image_buffer_pool_acquire_buffer;
453 gst_egl_image_buffer_pool_init (GstEGLImageBufferPool * pool)
457 #define parent_class gst_eglglessink_parent_class
458 G_DEFINE_TYPE_WITH_CODE (GstEglGlesSink, gst_eglglessink, GST_TYPE_VIDEO_SINK,
459 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
460 gst_eglglessink_videooverlay_init));
462 static inline gboolean
463 egl_init (GstEglGlesSink * eglglessink)
467 if (!gst_egl_adaptation_init_egl_display (eglglessink->egl_context)) {
468 GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL display");
473 gst_egl_adaptation_fill_supported_fbuffer_configs
474 (eglglessink->egl_context);
476 GST_ERROR_OBJECT (eglglessink, "Display support NONE of our configs");
479 GST_OBJECT_LOCK (eglglessink);
480 gst_caps_replace (&eglglessink->sinkcaps, caps);
481 GST_OBJECT_UNLOCK (eglglessink);
482 gst_caps_unref (caps);
485 eglglessink->egl_started = TRUE;
490 GST_ERROR_OBJECT (eglglessink, "Failed to perform EGL init");
495 render_thread_func (GstEglGlesSink * eglglessink)
499 GstDataQueueItem *item = NULL;
500 GstFlowReturn last_flow = GST_FLOW_OK;
502 g_value_init (&val, GST_TYPE_G_THREAD);
503 g_value_set_boxed (&val, g_thread_self ());
504 message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink),
505 GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (eglglessink));
506 gst_message_set_stream_status_object (message, &val);
507 GST_DEBUG_OBJECT (eglglessink, "posting ENTER stream status");
508 gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message);
509 g_value_unset (&val);
511 eglBindAPI (EGL_OPENGL_ES_API);
513 while (gst_data_queue_pop (eglglessink->queue, &item)) {
514 GstMiniObject *object = item->object;
516 GST_DEBUG_OBJECT (eglglessink, "Handling object %" GST_PTR_FORMAT, object);
518 if (GST_IS_CAPS (object)) {
519 GstCaps *caps = GST_CAPS_CAST (object);
521 if (caps != eglglessink->configured_caps) {
522 if (!gst_eglglessink_configure_caps (eglglessink, caps)) {
523 last_flow = GST_FLOW_NOT_NEGOTIATED;
526 } else if (GST_IS_QUERY (object)) {
527 GstQuery *query = GST_QUERY_CAST (object);
528 GstStructure *s = (GstStructure *) gst_query_get_structure (query);
530 if (gst_structure_has_name (s, "eglglessink-allocate-eglimage")) {
532 GstVideoFormat format;
536 if (!gst_structure_get_enum (s, "format", GST_TYPE_VIDEO_FORMAT,
538 || !gst_structure_get_int (s, "width", &width)
539 || !gst_structure_get_int (s, "height", &height)) {
540 g_assert_not_reached ();
544 gst_egl_adaptation_allocate_eglimage (eglglessink->egl_context,
545 GST_EGL_IMAGE_BUFFER_POOL (eglglessink->pool)->allocator, format,
547 g_value_init (&v, G_TYPE_POINTER);
548 g_value_set_pointer (&v, buffer);
549 gst_structure_set_value (s, "buffer", &v);
552 g_assert_not_reached ();
554 last_flow = GST_FLOW_OK;
555 } else if (GST_IS_BUFFER (object)) {
556 GstBuffer *buf = GST_BUFFER_CAST (item->object);
558 if (eglglessink->configured_caps) {
559 last_flow = gst_eglglessink_upload (eglglessink, buf);
561 last_flow = GST_FLOW_OK;
562 GST_DEBUG_OBJECT (eglglessink,
563 "No caps configured yet, not drawing anything");
565 } else if (!object) {
566 if (eglglessink->configured_caps) {
567 last_flow = gst_eglglessink_render (eglglessink);
569 last_flow = GST_FLOW_OK;
570 GST_DEBUG_OBJECT (eglglessink,
571 "No caps configured yet, not drawing anything");
574 g_assert_not_reached ();
577 item->destroy (item);
578 g_mutex_lock (&eglglessink->render_lock);
579 eglglessink->last_flow = last_flow;
580 eglglessink->dequeued_object = object;
581 g_cond_broadcast (&eglglessink->render_cond);
582 g_mutex_unlock (&eglglessink->render_lock);
584 if (last_flow != GST_FLOW_OK)
586 GST_DEBUG_OBJECT (eglglessink, "Successfully handled object");
589 if (last_flow == GST_FLOW_OK) {
590 g_mutex_lock (&eglglessink->render_lock);
591 eglglessink->last_flow = GST_FLOW_FLUSHING;
592 eglglessink->dequeued_object = NULL;
593 g_cond_broadcast (&eglglessink->render_cond);
594 g_mutex_unlock (&eglglessink->render_lock);
597 GST_DEBUG_OBJECT (eglglessink, "Shutting down thread");
599 /* EGL/GLES cleanup */
600 gst_egl_adaptation_cleanup (eglglessink->egl_context);
602 if (eglglessink->configured_caps) {
603 gst_caps_unref (eglglessink->configured_caps);
604 eglglessink->configured_caps = NULL;
607 g_value_init (&val, GST_TYPE_G_THREAD);
608 g_value_set_boxed (&val, g_thread_self ());
609 message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink),
610 GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (eglglessink));
611 gst_message_set_stream_status_object (message, &val);
612 GST_DEBUG_OBJECT (eglglessink, "posting LEAVE stream status");
613 gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message);
614 g_value_unset (&val);
620 gst_eglglessink_start (GstEglGlesSink * eglglessink)
622 GError *error = NULL;
624 GST_DEBUG_OBJECT (eglglessink, "Starting");
626 if (!eglglessink->egl_started) {
627 GST_ERROR_OBJECT (eglglessink, "EGL uninitialized. Bailing out");
631 /* Ask for a window to render to */
632 if (!eglglessink->have_window)
633 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (eglglessink));
635 if (!eglglessink->have_window && !eglglessink->create_window) {
636 GST_ERROR_OBJECT (eglglessink, "Window handle unavailable and we "
637 "were instructed not to create an internal one. Bailing out.");
641 eglglessink->last_flow = GST_FLOW_OK;
642 eglglessink->display_region.w = 0;
643 eglglessink->display_region.h = 0;
645 gst_data_queue_set_flushing (eglglessink->queue, FALSE);
647 #if !GLIB_CHECK_VERSION (2, 31, 0)
648 eglglessink->thread =
649 g_thread_create ((GThreadFunc) render_thread_func, eglglessink, TRUE,
652 eglglessink->thread = g_thread_try_new ("eglglessink-render",
653 (GThreadFunc) render_thread_func, eglglessink, &error);
656 if (!eglglessink->thread || error != NULL)
659 GST_DEBUG_OBJECT (eglglessink, "Started");
664 GST_ERROR_OBJECT (eglglessink, "Couldn't start");
665 g_clear_error (&error);
670 gst_eglglessink_stop (GstEglGlesSink * eglglessink)
672 GST_DEBUG_OBJECT (eglglessink, "Stopping");
674 gst_data_queue_set_flushing (eglglessink->queue, TRUE);
675 g_mutex_lock (&eglglessink->render_lock);
676 g_cond_broadcast (&eglglessink->render_cond);
677 g_mutex_unlock (&eglglessink->render_lock);
679 if (eglglessink->thread) {
680 g_thread_join (eglglessink->thread);
681 eglglessink->thread = NULL;
683 eglglessink->last_flow = GST_FLOW_FLUSHING;
685 gst_buffer_replace (&eglglessink->last_buffer, NULL);
687 if (eglglessink->using_own_window) {
688 gst_egl_adaptation_destroy_native_window (eglglessink->egl_context,
689 &eglglessink->own_window_data);
690 eglglessink->have_window = FALSE;
692 eglglessink->egl_context->used_window = 0;
693 if (eglglessink->current_caps) {
694 gst_caps_unref (eglglessink->current_caps);
695 eglglessink->current_caps = NULL;
698 GST_DEBUG_OBJECT (eglglessink, "Stopped");
704 gst_eglglessink_videooverlay_init (GstVideoOverlayInterface * iface)
706 iface->set_window_handle = gst_eglglessink_set_window_handle;
707 iface->expose = gst_eglglessink_expose;
708 iface->set_render_rectangle = gst_eglglessink_set_render_rectangle;
712 gst_eglglessink_create_window (GstEglGlesSink * eglglessink, gint width,
715 EGLNativeWindowType window = 0;
716 gboolean window_created = FALSE;
718 if (!eglglessink->create_window) {
719 GST_ERROR_OBJECT (eglglessink, "This sink can't create a window by itself");
722 GST_INFO_OBJECT (eglglessink, "Attempting internal window creation");
725 gst_egl_adaptation_create_native_window (eglglessink->egl_context, width,
726 height, &eglglessink->own_window_data);
727 if (!window_created) {
728 GST_ERROR_OBJECT (eglglessink, "Could not create window");
730 return window_created;
734 gst_eglglessink_expose (GstVideoOverlay * overlay)
736 GstEglGlesSink *eglglessink;
739 eglglessink = GST_EGLGLESSINK (overlay);
740 GST_DEBUG_OBJECT (eglglessink, "Expose catched, redisplay");
742 /* Render from last seen buffer */
743 ret = gst_eglglessink_queue_object (eglglessink, NULL);
744 if (ret == GST_FLOW_ERROR)
745 GST_ERROR_OBJECT (eglglessink, "Redisplay failed");
749 gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink, gboolean reset)
751 gdouble render_width, render_height;
752 gdouble texture_width, texture_height;
753 gdouble x1, x2, y1, y2;
754 gdouble tx1, tx2, ty1, ty2;
756 GST_INFO_OBJECT (eglglessink, "VBO setup. have_vbo:%d, should reset %d",
757 eglglessink->egl_context->have_vbo, reset);
759 if (eglglessink->egl_context->have_vbo && reset) {
760 glDeleteBuffers (1, &eglglessink->egl_context->position_buffer);
761 glDeleteBuffers (1, &eglglessink->egl_context->index_buffer);
762 eglglessink->egl_context->have_vbo = FALSE;
765 render_width = eglglessink->render_region.w;
766 render_height = eglglessink->render_region.h;
768 texture_width = eglglessink->configured_info.width;
769 texture_height = eglglessink->configured_info.height;
771 GST_DEBUG_OBJECT (eglglessink, "Performing VBO setup");
773 x1 = (eglglessink->display_region.x / render_width) * 2.0 - 1;
774 y1 = (eglglessink->display_region.y / render_height) * 2.0 - 1;
775 x2 = ((eglglessink->display_region.x +
776 eglglessink->display_region.w) / render_width) * 2.0 - 1;
777 y2 = ((eglglessink->display_region.y +
778 eglglessink->display_region.h) / render_height) * 2.0 - 1;
780 tx1 = (eglglessink->crop.x / texture_width);
781 tx2 = ((eglglessink->crop.x + eglglessink->crop.w) / texture_width);
782 ty1 = (eglglessink->crop.y / texture_height);
783 ty2 = ((eglglessink->crop.y + eglglessink->crop.h) / texture_height);
785 /* X-normal, Y-normal orientation */
786 eglglessink->egl_context->position_array[0].x = x2;
787 eglglessink->egl_context->position_array[0].y = y2;
788 eglglessink->egl_context->position_array[0].z = 0;
789 eglglessink->egl_context->position_array[0].a = tx2;
790 eglglessink->egl_context->position_array[0].b = ty1;
792 eglglessink->egl_context->position_array[1].x = x2;
793 eglglessink->egl_context->position_array[1].y = y1;
794 eglglessink->egl_context->position_array[1].z = 0;
795 eglglessink->egl_context->position_array[1].a = tx2;
796 eglglessink->egl_context->position_array[1].b = ty2;
798 eglglessink->egl_context->position_array[2].x = x1;
799 eglglessink->egl_context->position_array[2].y = y2;
800 eglglessink->egl_context->position_array[2].z = 0;
801 eglglessink->egl_context->position_array[2].a = tx1;
802 eglglessink->egl_context->position_array[2].b = ty1;
804 eglglessink->egl_context->position_array[3].x = x1;
805 eglglessink->egl_context->position_array[3].y = y1;
806 eglglessink->egl_context->position_array[3].z = 0;
807 eglglessink->egl_context->position_array[3].a = tx1;
808 eglglessink->egl_context->position_array[3].b = ty2;
810 /* X-normal, Y-flip orientation */
811 eglglessink->egl_context->position_array[4 + 0].x = x2;
812 eglglessink->egl_context->position_array[4 + 0].y = y2;
813 eglglessink->egl_context->position_array[4 + 0].z = 0;
814 eglglessink->egl_context->position_array[4 + 0].a = tx2;
815 eglglessink->egl_context->position_array[4 + 0].b = ty2;
817 eglglessink->egl_context->position_array[4 + 1].x = x2;
818 eglglessink->egl_context->position_array[4 + 1].y = y1;
819 eglglessink->egl_context->position_array[4 + 1].z = 0;
820 eglglessink->egl_context->position_array[4 + 1].a = tx2;
821 eglglessink->egl_context->position_array[4 + 1].b = ty1;
823 eglglessink->egl_context->position_array[4 + 2].x = x1;
824 eglglessink->egl_context->position_array[4 + 2].y = y2;
825 eglglessink->egl_context->position_array[4 + 2].z = 0;
826 eglglessink->egl_context->position_array[4 + 2].a = tx1;
827 eglglessink->egl_context->position_array[4 + 2].b = ty2;
829 eglglessink->egl_context->position_array[4 + 3].x = x1;
830 eglglessink->egl_context->position_array[4 + 3].y = y1;
831 eglglessink->egl_context->position_array[4 + 3].z = 0;
832 eglglessink->egl_context->position_array[4 + 3].a = tx1;
833 eglglessink->egl_context->position_array[4 + 3].b = ty1;
836 if (eglglessink->display_region.x == 0) {
837 /* Borders top/bottom */
839 eglglessink->egl_context->position_array[8 + 0].x = 1;
840 eglglessink->egl_context->position_array[8 + 0].y = 1;
841 eglglessink->egl_context->position_array[8 + 0].z = 0;
843 eglglessink->egl_context->position_array[8 + 1].x = x2;
844 eglglessink->egl_context->position_array[8 + 1].y = y2;
845 eglglessink->egl_context->position_array[8 + 1].z = 0;
847 eglglessink->egl_context->position_array[8 + 2].x = -1;
848 eglglessink->egl_context->position_array[8 + 2].y = 1;
849 eglglessink->egl_context->position_array[8 + 2].z = 0;
851 eglglessink->egl_context->position_array[8 + 3].x = x1;
852 eglglessink->egl_context->position_array[8 + 3].y = y2;
853 eglglessink->egl_context->position_array[8 + 3].z = 0;
855 eglglessink->egl_context->position_array[12 + 0].x = 1;
856 eglglessink->egl_context->position_array[12 + 0].y = y1;
857 eglglessink->egl_context->position_array[12 + 0].z = 0;
859 eglglessink->egl_context->position_array[12 + 1].x = 1;
860 eglglessink->egl_context->position_array[12 + 1].y = -1;
861 eglglessink->egl_context->position_array[12 + 1].z = 0;
863 eglglessink->egl_context->position_array[12 + 2].x = x1;
864 eglglessink->egl_context->position_array[12 + 2].y = y1;
865 eglglessink->egl_context->position_array[12 + 2].z = 0;
867 eglglessink->egl_context->position_array[12 + 3].x = -1;
868 eglglessink->egl_context->position_array[12 + 3].y = -1;
869 eglglessink->egl_context->position_array[12 + 3].z = 0;
871 /* Borders left/right */
873 eglglessink->egl_context->position_array[8 + 0].x = x1;
874 eglglessink->egl_context->position_array[8 + 0].y = 1;
875 eglglessink->egl_context->position_array[8 + 0].z = 0;
877 eglglessink->egl_context->position_array[8 + 1].x = x1;
878 eglglessink->egl_context->position_array[8 + 1].y = -1;
879 eglglessink->egl_context->position_array[8 + 1].z = 0;
881 eglglessink->egl_context->position_array[8 + 2].x = -1;
882 eglglessink->egl_context->position_array[8 + 2].y = 1;
883 eglglessink->egl_context->position_array[8 + 2].z = 0;
885 eglglessink->egl_context->position_array[8 + 3].x = -1;
886 eglglessink->egl_context->position_array[8 + 3].y = -1;
887 eglglessink->egl_context->position_array[8 + 3].z = 0;
889 eglglessink->egl_context->position_array[12 + 0].x = 1;
890 eglglessink->egl_context->position_array[12 + 0].y = 1;
891 eglglessink->egl_context->position_array[12 + 0].z = 0;
893 eglglessink->egl_context->position_array[12 + 1].x = 1;
894 eglglessink->egl_context->position_array[12 + 1].y = -1;
895 eglglessink->egl_context->position_array[12 + 1].z = 0;
897 eglglessink->egl_context->position_array[12 + 2].x = x2;
898 eglglessink->egl_context->position_array[12 + 2].y = y2;
899 eglglessink->egl_context->position_array[12 + 2].z = 0;
901 eglglessink->egl_context->position_array[12 + 3].x = x2;
902 eglglessink->egl_context->position_array[12 + 3].y = -1;
903 eglglessink->egl_context->position_array[12 + 3].z = 0;
906 eglglessink->egl_context->index_array[0] = 0;
907 eglglessink->egl_context->index_array[1] = 1;
908 eglglessink->egl_context->index_array[2] = 2;
909 eglglessink->egl_context->index_array[3] = 3;
911 glGenBuffers (1, &eglglessink->egl_context->position_buffer);
912 glGenBuffers (1, &eglglessink->egl_context->index_buffer);
913 if (got_gl_error ("glGenBuffers"))
914 goto HANDLE_ERROR_LOCKED;
916 glBindBuffer (GL_ARRAY_BUFFER, eglglessink->egl_context->position_buffer);
917 if (got_gl_error ("glBindBuffer position_buffer"))
918 goto HANDLE_ERROR_LOCKED;
920 glBufferData (GL_ARRAY_BUFFER,
921 sizeof (eglglessink->egl_context->position_array),
922 eglglessink->egl_context->position_array, GL_STATIC_DRAW);
923 if (got_gl_error ("glBufferData position_buffer"))
924 goto HANDLE_ERROR_LOCKED;
926 glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
927 eglglessink->egl_context->index_buffer);
928 if (got_gl_error ("glBindBuffer index_buffer"))
929 goto HANDLE_ERROR_LOCKED;
931 glBufferData (GL_ELEMENT_ARRAY_BUFFER,
932 sizeof (eglglessink->egl_context->index_array),
933 eglglessink->egl_context->index_array, GL_STATIC_DRAW);
934 if (got_gl_error ("glBufferData index_buffer"))
935 goto HANDLE_ERROR_LOCKED;
937 eglglessink->egl_context->have_vbo = TRUE;
938 GST_DEBUG_OBJECT (eglglessink, "VBO setup done");
943 GST_ERROR_OBJECT (eglglessink, "Unable to perform VBO setup");
948 gst_eglglessink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
950 GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
952 g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
953 GST_DEBUG_OBJECT (eglglessink, "We got a window handle: %p", (gpointer) id);
955 /* OK, we have a new window */
956 GST_OBJECT_LOCK (eglglessink);
957 eglglessink->egl_context->window = (EGLNativeWindowType) id;
958 eglglessink->have_window = ((gpointer) id != NULL);
959 GST_OBJECT_UNLOCK (eglglessink);
965 gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
966 gint width, gint height)
968 GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay);
970 g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink));
972 GST_OBJECT_LOCK (eglglessink);
973 eglglessink->render_region.x = x;
974 eglglessink->render_region.y = y;
975 eglglessink->render_region.w = width;
976 eglglessink->render_region.h = height;
977 eglglessink->render_region_changed = TRUE;
978 eglglessink->render_region_user = (width != -1 && height != -1);
979 GST_OBJECT_UNLOCK (eglglessink);
985 queue_item_destroy (GstDataQueueItem * item)
987 if (item->object && !GST_IS_QUERY (item->object))
988 gst_mini_object_unref (item->object);
989 g_slice_free (GstDataQueueItem, item);
993 gst_eglglessink_queue_object (GstEglGlesSink * eglglessink, GstMiniObject * obj)
995 GstDataQueueItem *item;
996 GstFlowReturn last_flow;
998 g_mutex_lock (&eglglessink->render_lock);
999 last_flow = eglglessink->last_flow;
1000 g_mutex_unlock (&eglglessink->render_lock);
1002 if (last_flow != GST_FLOW_OK)
1005 item = g_slice_new0 (GstDataQueueItem);
1008 item->object = NULL;
1009 else if (GST_IS_QUERY (obj))
1012 item->object = gst_mini_object_ref (obj);
1014 item->duration = GST_CLOCK_TIME_NONE;
1015 item->visible = TRUE;
1016 item->destroy = (GDestroyNotify) queue_item_destroy;
1018 GST_DEBUG_OBJECT (eglglessink, "Queueing object %" GST_PTR_FORMAT, obj);
1020 g_mutex_lock (&eglglessink->render_lock);
1021 if (!gst_data_queue_push (eglglessink->queue, item)) {
1022 item->destroy (item);
1023 g_mutex_unlock (&eglglessink->render_lock);
1024 GST_DEBUG_OBJECT (eglglessink, "Flushing");
1025 return GST_FLOW_FLUSHING;
1028 GST_DEBUG_OBJECT (eglglessink, "Waiting for object to be handled");
1030 g_cond_wait (&eglglessink->render_cond, &eglglessink->render_lock);
1031 } while (eglglessink->dequeued_object != obj
1032 && eglglessink->last_flow != GST_FLOW_FLUSHING);
1033 GST_DEBUG_OBJECT (eglglessink, "Object handled: %s",
1034 gst_flow_get_name (eglglessink->last_flow));
1035 last_flow = eglglessink->last_flow;
1036 g_mutex_unlock (&eglglessink->render_lock);
1038 return (obj ? last_flow : GST_FLOW_OK);
1042 gst_eglglessink_crop_changed (GstEglGlesSink * eglglessink,
1043 GstVideoCropMeta * crop)
1046 return (crop->x != eglglessink->crop.x ||
1047 crop->y != eglglessink->crop.y ||
1048 crop->width != eglglessink->crop.w ||
1049 crop->height != eglglessink->crop.h);
1052 return (eglglessink->crop.x != 0 || eglglessink->crop.y != 0 ||
1053 eglglessink->crop.w != eglglessink->configured_info.width ||
1054 eglglessink->crop.h != eglglessink->configured_info.height);
1058 gst_eglglessink_fill_texture (GstEglGlesSink * eglglessink, GstBuffer * buf)
1060 GstVideoFrame vframe;
1063 memset (&vframe, 0, sizeof (vframe));
1065 if (!gst_video_frame_map (&vframe, &eglglessink->configured_info, buf,
1067 GST_ERROR_OBJECT (eglglessink, "Couldn't map frame");
1071 w = GST_VIDEO_FRAME_WIDTH (&vframe);
1072 h = GST_VIDEO_FRAME_HEIGHT (&vframe);
1074 GST_DEBUG_OBJECT (eglglessink,
1075 "Got buffer %p: %dx%d size %" G_GSIZE_FORMAT, buf, w, h,
1076 gst_buffer_get_size (buf));
1078 switch (eglglessink->configured_info.finfo->format) {
1079 case GST_VIDEO_FORMAT_BGR:
1080 case GST_VIDEO_FORMAT_RGB:{
1085 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1086 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1088 glActiveTexture (GL_TEXTURE0);
1090 if (GST_ROUND_UP_8 (c_w * 3) == stride) {
1091 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1092 } else if (GST_ROUND_UP_4 (c_w * 3) == stride) {
1093 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1094 } else if (GST_ROUND_UP_2 (c_w * 3) == stride) {
1095 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1096 } else if (c_w * 3 == stride) {
1097 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1099 stride_width = stride;
1101 if (GST_ROUND_UP_8 (stride_width * 3) == stride) {
1102 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1103 } else if (GST_ROUND_UP_4 (stride_width * 3) == stride) {
1104 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1105 } else if (GST_ROUND_UP_2 (stride_width * 3) == stride) {
1106 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1107 } else if (stride_width * 3 == stride) {
1108 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1110 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1114 if (got_gl_error ("glPixelStorei"))
1117 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1119 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1120 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB,
1121 GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1124 case GST_VIDEO_FORMAT_RGB16:{
1129 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1130 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1132 glActiveTexture (GL_TEXTURE0);
1134 if (GST_ROUND_UP_8 (c_w * 2) == stride) {
1135 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1136 } else if (GST_ROUND_UP_4 (c_w * 2) == stride) {
1137 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1138 } else if (c_w * 2 == stride) {
1139 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1141 stride_width = stride;
1143 if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1144 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1145 } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) {
1146 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1147 } else if (stride_width * 2 == stride) {
1148 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1150 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1154 if (got_gl_error ("glPixelStorei"))
1157 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1159 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1160 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB,
1161 GL_UNSIGNED_SHORT_5_6_5, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1164 case GST_VIDEO_FORMAT_RGBA:
1165 case GST_VIDEO_FORMAT_BGRA:
1166 case GST_VIDEO_FORMAT_ARGB:
1167 case GST_VIDEO_FORMAT_ABGR:
1168 case GST_VIDEO_FORMAT_RGBx:
1169 case GST_VIDEO_FORMAT_BGRx:
1170 case GST_VIDEO_FORMAT_xRGB:
1171 case GST_VIDEO_FORMAT_xBGR:{
1176 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1177 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1179 glActiveTexture (GL_TEXTURE0);
1181 if (GST_ROUND_UP_8 (c_w * 4) == stride) {
1182 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1183 } else if (c_w * 4 == stride) {
1184 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1186 stride_width = stride;
1188 if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1189 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1190 } else if (stride_width * 4 == stride) {
1191 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1193 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1197 if (got_gl_error ("glPixelStorei"))
1200 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1202 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1203 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0,
1204 GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1207 case GST_VIDEO_FORMAT_AYUV:{
1212 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1213 stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe);
1215 glActiveTexture (GL_TEXTURE0);
1217 if (GST_ROUND_UP_8 (c_w * 4) == stride) {
1218 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1219 } else if (c_w * 4 == stride) {
1220 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1222 stride_width = stride;
1224 if (GST_ROUND_UP_8 (stride_width * 4) == stride) {
1225 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1226 } else if (stride_width * 4 == stride) {
1227 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1229 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1233 if (got_gl_error ("glPixelStorei"))
1236 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1238 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1239 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0,
1240 GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1243 case GST_VIDEO_FORMAT_Y444:
1244 case GST_VIDEO_FORMAT_I420:
1245 case GST_VIDEO_FORMAT_YV12:
1246 case GST_VIDEO_FORMAT_Y42B:
1247 case GST_VIDEO_FORMAT_Y41B:{
1252 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1253 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0);
1255 glActiveTexture (GL_TEXTURE0);
1257 if (GST_ROUND_UP_8 (c_w) == stride) {
1258 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1259 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1260 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1261 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1262 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1263 } else if (c_w == stride) {
1264 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1266 stride_width = stride;
1268 if (GST_ROUND_UP_8 (stride_width) == stride) {
1269 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1270 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1271 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1272 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1273 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1274 } else if (stride_width == stride) {
1275 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1277 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1281 if (got_gl_error ("glPixelStorei"))
1284 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1286 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1287 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1289 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0),
1290 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1291 GST_VIDEO_FRAME_COMP_DATA (&vframe, 0));
1294 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1295 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1);
1297 glActiveTexture (GL_TEXTURE1);
1299 if (GST_ROUND_UP_8 (c_w) == stride) {
1300 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1301 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1302 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1303 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1304 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1305 } else if (c_w == stride) {
1306 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1308 stride_width = stride;
1310 if (GST_ROUND_UP_8 (stride_width) == stride) {
1311 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1312 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1313 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1314 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1315 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1316 } else if (stride_width == stride) {
1317 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1319 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1323 if (got_gl_error ("glPixelStorei"))
1326 eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w);
1328 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]);
1329 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1331 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1),
1332 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1333 GST_VIDEO_FRAME_COMP_DATA (&vframe, 1));
1336 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 2);
1337 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 2);
1339 glActiveTexture (GL_TEXTURE2);
1341 if (GST_ROUND_UP_8 (c_w) == stride) {
1342 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1343 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1344 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1345 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1346 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1347 } else if (c_w == stride) {
1348 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1350 stride_width = stride;
1352 if (GST_ROUND_UP_8 (stride_width) == stride) {
1353 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1354 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1355 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1356 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1357 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1358 } else if (stride_width == stride) {
1359 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1361 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1365 if (got_gl_error ("glPixelStorei"))
1368 eglglessink->stride[2] = ((gdouble) stride_width) / ((gdouble) c_w);
1370 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[2]);
1371 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1373 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 2),
1374 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1375 GST_VIDEO_FRAME_COMP_DATA (&vframe, 2));
1378 case GST_VIDEO_FORMAT_NV12:
1379 case GST_VIDEO_FORMAT_NV21:{
1384 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1385 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0);
1387 glActiveTexture (GL_TEXTURE0);
1389 if (GST_ROUND_UP_8 (c_w) == stride) {
1390 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1391 } else if (GST_ROUND_UP_4 (c_w) == stride) {
1392 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1393 } else if (GST_ROUND_UP_2 (c_w) == stride) {
1394 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1395 } else if (c_w == stride) {
1396 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1398 stride_width = stride;
1400 if (GST_ROUND_UP_8 (stride_width) == stride) {
1401 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1402 } else if (GST_ROUND_UP_4 (stride_width) == stride) {
1403 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1404 } else if (GST_ROUND_UP_2 (stride_width) == stride) {
1405 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1406 } else if (stride_width == stride) {
1407 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1409 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1413 if (got_gl_error ("glPixelStorei"))
1416 eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w);
1418 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]);
1419 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
1421 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0),
1422 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
1423 GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0));
1426 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1427 stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1);
1429 glActiveTexture (GL_TEXTURE1);
1431 if (GST_ROUND_UP_8 (c_w * 2) == stride) {
1432 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1433 } else if (GST_ROUND_UP_4 (c_w * 2) == stride) {
1434 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1435 } else if (c_w * 2 == stride) {
1436 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1438 stride_width = stride / 2;
1440 if (GST_ROUND_UP_8 (stride_width * 2) == stride) {
1441 glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
1442 } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) {
1443 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1444 } else if (stride_width * 2 == stride) {
1445 glPixelStorei (GL_UNPACK_ALIGNMENT, 2);
1447 GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride);
1451 if (got_gl_error ("glPixelStorei"))
1454 eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w);
1456 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]);
1457 glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
1459 GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1),
1460 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
1461 GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1));
1465 g_assert_not_reached ();
1469 if (got_gl_error ("glTexImage2D"))
1472 gst_video_frame_unmap (&vframe);
1479 gst_video_frame_unmap (&vframe);
1484 /* Rendering and display */
1485 static GstFlowReturn
1486 gst_eglglessink_upload (GstEglGlesSink * eglglessink, GstBuffer * buf)
1488 GstVideoCropMeta *crop = NULL;
1491 GST_DEBUG_OBJECT (eglglessink, "Rendering previous buffer again");
1494 GstVideoGLTextureUploadMeta *upload_meta;
1496 crop = gst_buffer_get_video_crop_meta (buf);
1498 upload_meta = gst_buffer_get_video_gl_texture_upload_meta (buf);
1500 if (gst_eglglessink_crop_changed (eglglessink, crop)) {
1502 eglglessink->crop.x = crop->x;
1503 eglglessink->crop.y = crop->y;
1504 eglglessink->crop.w = crop->width;
1505 eglglessink->crop.h = crop->height;
1507 eglglessink->crop.x = 0;
1508 eglglessink->crop.y = 0;
1509 eglglessink->crop.w = eglglessink->configured_info.width;
1510 eglglessink->crop.h = eglglessink->configured_info.height;
1512 eglglessink->crop_changed = TRUE;
1518 if (upload_meta->n_textures != eglglessink->egl_context->n_textures)
1521 for (i = 0; i < eglglessink->egl_context->n_textures; i++) {
1523 glActiveTexture (GL_TEXTURE0);
1525 glActiveTexture (GL_TEXTURE1);
1527 glActiveTexture (GL_TEXTURE2);
1529 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]);
1532 if (!gst_video_gl_texture_upload_meta_upload (upload_meta,
1533 eglglessink->egl_context->texture))
1536 eglglessink->orientation = upload_meta->texture_orientation;
1537 eglglessink->stride[0] = 1;
1538 eglglessink->stride[1] = 1;
1539 eglglessink->stride[2] = 1;
1540 } else if (gst_buffer_n_memory (buf) >= 1 &&
1541 (mem = gst_buffer_peek_memory (buf, 0))
1542 && gst_is_egl_image_memory (mem)) {
1545 n = gst_buffer_n_memory (buf);
1547 for (i = 0; i < n; i++) {
1548 mem = gst_buffer_peek_memory (buf, i);
1550 g_assert (gst_is_egl_image_memory (mem));
1553 glActiveTexture (GL_TEXTURE0);
1555 glActiveTexture (GL_TEXTURE1);
1557 glActiveTexture (GL_TEXTURE2);
1559 glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]);
1560 glEGLImageTargetTexture2DOES (GL_TEXTURE_2D,
1561 gst_egl_image_memory_get_image (mem));
1562 if (got_gl_error ("glEGLImageTargetTexture2DOES"))
1564 eglglessink->orientation = gst_egl_image_memory_get_orientation (mem);
1565 if (eglglessink->orientation !=
1566 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL
1567 && eglglessink->orientation !=
1568 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) {
1569 GST_ERROR_OBJECT (eglglessink, "Unsupported EGLImage orientation");
1570 return GST_FLOW_ERROR;
1573 gst_buffer_replace (&eglglessink->last_buffer, buf);
1574 eglglessink->stride[0] = 1;
1575 eglglessink->stride[1] = 1;
1576 eglglessink->stride[2] = 1;
1578 eglglessink->orientation =
1579 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL;
1580 if (!gst_eglglessink_fill_texture (eglglessink, buf))
1589 GST_ERROR_OBJECT (eglglessink, "Failed to upload texture");
1590 return GST_FLOW_ERROR;
1594 static GstFlowReturn
1595 gst_eglglessink_render (GstEglGlesSink * eglglessink)
1600 /* If no one has set a display rectangle on us initialize
1601 * a sane default. According to the docs on the xOverlay
1602 * interface we are supposed to fill the overlay 100%. We
1603 * do this trying to take PAR/DAR into account unless the
1604 * calling party explicitly ask us not to by setting
1605 * force_aspect_ratio to FALSE.
1607 if (gst_egl_adaptation_update_surface_dimensions (eglglessink->egl_context) ||
1608 eglglessink->render_region_changed ||
1609 !eglglessink->display_region.w || !eglglessink->display_region.h ||
1610 eglglessink->crop_changed) {
1611 GST_OBJECT_LOCK (eglglessink);
1613 if (!eglglessink->render_region_user) {
1614 eglglessink->render_region.x = 0;
1615 eglglessink->render_region.y = 0;
1616 eglglessink->render_region.w = eglglessink->egl_context->surface_width;
1617 eglglessink->render_region.h = eglglessink->egl_context->surface_height;
1619 eglglessink->render_region_changed = FALSE;
1620 eglglessink->crop_changed = FALSE;
1622 if (!eglglessink->force_aspect_ratio) {
1623 eglglessink->display_region.x = 0;
1624 eglglessink->display_region.y = 0;
1625 eglglessink->display_region.w = eglglessink->render_region.w;
1626 eglglessink->display_region.h = eglglessink->render_region.h;
1628 GstVideoRectangle frame;
1633 if (!gst_video_calculate_display_ratio (&dar_n, &dar_d,
1634 eglglessink->crop.w, eglglessink->crop.h,
1635 eglglessink->configured_info.par_n,
1636 eglglessink->configured_info.par_d,
1637 eglglessink->egl_context->pixel_aspect_ratio_n,
1638 eglglessink->egl_context->pixel_aspect_ratio_d)) {
1639 GST_WARNING_OBJECT (eglglessink, "Could not compute resulting DAR");
1640 frame.w = eglglessink->crop.w;
1641 frame.h = eglglessink->crop.h;
1643 /* Find suitable matching new size acording to dar & par
1644 * rationale for prefering leaving the height untouched
1645 * comes from interlacing considerations.
1646 * XXX: Move this to gstutils?
1648 if (eglglessink->crop.h % dar_d == 0) {
1650 gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d);
1651 frame.h = eglglessink->crop.h;
1652 } else if (eglglessink->crop.w % dar_n == 0) {
1654 gst_util_uint64_scale_int (eglglessink->crop.w, dar_d, dar_n);
1655 frame.w = eglglessink->crop.w;
1657 /* Neither width nor height can be precisely scaled.
1658 * Prefer to leave height untouched. See comment above.
1661 gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d);
1662 frame.h = eglglessink->crop.h;
1666 gst_video_sink_center_rect (frame, eglglessink->render_region,
1667 &eglglessink->display_region, TRUE);
1670 glViewport (eglglessink->render_region.x,
1671 eglglessink->egl_context->surface_height -
1672 eglglessink->render_region.y -
1673 eglglessink->render_region.h,
1674 eglglessink->render_region.w, eglglessink->render_region.h);
1676 /* Clear the surface once if its content is preserved */
1677 if (eglglessink->egl_context->buffer_preserved) {
1678 glClearColor (0.0, 0.0, 0.0, 1.0);
1679 glClear (GL_COLOR_BUFFER_BIT);
1682 if (!gst_eglglessink_setup_vbo (eglglessink, FALSE)) {
1683 GST_OBJECT_UNLOCK (eglglessink);
1684 GST_ERROR_OBJECT (eglglessink, "VBO setup failed");
1687 GST_OBJECT_UNLOCK (eglglessink);
1690 if (!eglglessink->egl_context->buffer_preserved) {
1691 /* Draw black borders */
1692 GST_DEBUG_OBJECT (eglglessink, "Drawing black border 1");
1693 glUseProgram (eglglessink->egl_context->glslprogram[1]);
1695 glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3,
1696 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (8 * sizeof (coord5)));
1697 if (got_gl_error ("glVertexAttribPointer"))
1700 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1701 if (got_gl_error ("glDrawElements"))
1704 GST_DEBUG_OBJECT (eglglessink, "Drawing black border 2");
1706 glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3,
1707 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (12 * sizeof (coord5)));
1708 if (got_gl_error ("glVertexAttribPointer"))
1711 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1712 if (got_gl_error ("glDrawElements"))
1716 /* Draw video frame */
1717 GST_DEBUG_OBJECT (eglglessink, "Drawing video frame");
1719 glUseProgram (eglglessink->egl_context->glslprogram[0]);
1721 glUniform2f (eglglessink->egl_context->tex_scale_loc[0][0],
1722 eglglessink->stride[0], 1);
1723 glUniform2f (eglglessink->egl_context->tex_scale_loc[0][1],
1724 eglglessink->stride[1], 1);
1725 glUniform2f (eglglessink->egl_context->tex_scale_loc[0][2],
1726 eglglessink->stride[2], 1);
1728 for (i = 0; i < eglglessink->egl_context->n_textures; i++) {
1729 glUniform1i (eglglessink->egl_context->tex_loc[0][i], i);
1730 if (got_gl_error ("glUniform1i"))
1734 if (eglglessink->orientation ==
1735 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) {
1736 glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3,
1737 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (0 * sizeof (coord5)));
1738 if (got_gl_error ("glVertexAttribPointer"))
1741 glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2,
1742 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (3 * sizeof (gfloat)));
1743 if (got_gl_error ("glVertexAttribPointer"))
1745 } else if (eglglessink->orientation ==
1746 GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) {
1747 glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3,
1748 GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (4 * sizeof (coord5)));
1749 if (got_gl_error ("glVertexAttribPointer"))
1752 glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2,
1753 GL_FLOAT, GL_FALSE, sizeof (coord5),
1754 (gpointer) (4 * sizeof (coord5) + 3 * sizeof (gfloat)));
1755 if (got_gl_error ("glVertexAttribPointer"))
1758 g_assert_not_reached ();
1761 glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
1762 if (got_gl_error ("glDrawElements"))
1765 if (!gst_egl_adaptation_context_swap_buffers (eglglessink->egl_context)) {
1770 GST_DEBUG_OBJECT (eglglessink, "Succesfully rendered 1 frame");
1774 GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame");
1776 return GST_FLOW_ERROR;
1779 static GstFlowReturn
1780 gst_eglglessink_prepare (GstBaseSink * bsink, GstBuffer * buf)
1782 GstEglGlesSink *eglglessink;
1784 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1786 eglglessink = GST_EGLGLESSINK (bsink);
1787 GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
1789 return gst_eglglessink_queue_object (eglglessink, GST_MINI_OBJECT_CAST (buf));
1792 static GstFlowReturn
1793 gst_eglglessink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1795 GstEglGlesSink *eglglessink;
1797 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1799 eglglessink = GST_EGLGLESSINK (vsink);
1800 GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf);
1802 return gst_eglglessink_queue_object (eglglessink, NULL);
1806 gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1808 GstEglGlesSink *eglglessink;
1809 GstCaps *ret = NULL;
1811 eglglessink = GST_EGLGLESSINK (bsink);
1813 GST_OBJECT_LOCK (eglglessink);
1814 if (eglglessink->sinkcaps) {
1815 ret = gst_caps_ref (eglglessink->sinkcaps);
1818 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
1821 GST_OBJECT_UNLOCK (eglglessink);
1825 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1827 gst_caps_unref (ret);
1835 gst_eglglessink_event (GstBaseSink * bsink, GstEvent * event)
1837 GstEglGlesSink *eglglessink;
1839 eglglessink = GST_EGLGLESSINK (bsink);
1841 switch (GST_EVENT_TYPE (event)) {
1842 case GST_EVENT_CONTEXT:{
1843 GstContext *context;
1844 GstEGLDisplay *display;
1846 gst_event_parse_context (event, &context);
1848 if (gst_context_get_egl_display (context, &display)) {
1849 GST_OBJECT_LOCK (eglglessink);
1850 if (eglglessink->egl_context->set_display)
1851 gst_egl_display_unref (eglglessink->egl_context->set_display);
1852 eglglessink->egl_context->set_display = display;
1853 GST_OBJECT_UNLOCK (eglglessink);
1856 gst_context_unref (context);
1858 return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->event (bsink,
1863 return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->event (bsink,
1870 gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query)
1872 GstEglGlesSink *eglglessink;
1874 eglglessink = GST_EGLGLESSINK (bsink);
1876 switch (GST_QUERY_TYPE (query)) {
1877 case GST_QUERY_CONTEXT:{
1880 GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink, query);
1882 n = gst_query_get_n_context_types (query);
1883 for (i = 0; i < n; i++) {
1884 const gchar *context_type = NULL;
1886 gst_query_parse_nth_context_type (query, i, &context_type);
1887 if (g_strcmp0 (context_type, GST_EGL_DISPLAY_CONTEXT_TYPE) == 0) {
1888 GstContext *context, *old_context;
1890 gst_query_parse_context (query, &old_context);
1892 context = gst_context_copy (old_context);
1894 context = gst_context_new ();
1896 gst_context_set_egl_display (context,
1897 eglglessink->egl_context->display);
1898 gst_query_set_context (query, context);
1899 gst_context_unref (context);
1908 return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink,
1915 gst_eglglessink_set_context (GstElement * element, GstContext * context)
1917 GstEglGlesSink *eglglessink;
1918 GstEGLDisplay *display = NULL;
1920 eglglessink = GST_EGLGLESSINK (element);
1922 if (gst_context_get_egl_display (context, &display)) {
1923 GST_OBJECT_LOCK (eglglessink);
1924 if (eglglessink->egl_context->set_display)
1925 gst_egl_display_unref (eglglessink->egl_context->set_display);
1926 eglglessink->egl_context->set_display = display;
1927 GST_OBJECT_UNLOCK (eglglessink);
1930 GST_OBJECT_LOCK (eglglessink);
1931 context = gst_context_copy (context);
1932 gst_context_set_egl_display (context, eglglessink->egl_context->display);
1933 GST_OBJECT_UNLOCK (eglglessink);
1935 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1936 gst_context_unref (context);
1940 gst_eglglessink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1942 GstEglGlesSink *eglglessink;
1943 GstBufferPool *pool;
1944 GstStructure *config;
1949 GstAllocator *allocator;
1950 GstAllocationParams params;
1952 eglglessink = GST_EGLGLESSINK (bsink);
1954 gst_allocation_params_init (¶ms);
1956 gst_query_parse_allocation (query, &caps, &need_pool);
1958 GST_ERROR_OBJECT (eglglessink, "allocation query without caps");
1962 if (!gst_video_info_from_caps (&info, caps)) {
1963 GST_ERROR_OBJECT (eglglessink, "allocation query with invalid caps");
1967 GST_OBJECT_LOCK (eglglessink);
1968 pool = eglglessink->pool ? gst_object_ref (eglglessink->pool) : NULL;
1969 GST_OBJECT_UNLOCK (eglglessink);
1974 /* we had a pool, check caps */
1975 GST_DEBUG_OBJECT (eglglessink, "check existing pool caps");
1976 config = gst_buffer_pool_get_config (pool);
1977 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1979 if (!gst_caps_is_equal (caps, pcaps)) {
1980 GST_DEBUG_OBJECT (eglglessink, "pool has different caps");
1981 /* different caps, we can't use this pool */
1982 gst_object_unref (pool);
1985 gst_structure_free (config);
1988 if (pool == NULL && need_pool) {
1991 if (!gst_video_info_from_caps (&info, caps)) {
1992 GST_ERROR_OBJECT (eglglessink, "allocation query has invalid caps %"
1993 GST_PTR_FORMAT, caps);
1997 GST_DEBUG_OBJECT (eglglessink, "create new pool");
1999 gst_egl_image_buffer_pool_new (eglglessink,
2000 eglglessink->egl_context->display);
2002 /* the normal size of a frame */
2005 config = gst_buffer_pool_get_config (pool);
2006 /* we need at least 2 buffer because we hold on to the last one */
2007 gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
2008 gst_buffer_pool_config_set_allocator (config, NULL, ¶ms);
2009 if (!gst_buffer_pool_set_config (pool, config)) {
2010 gst_object_unref (pool);
2011 GST_ERROR_OBJECT (eglglessink, "failed to set pool configuration");
2017 /* we need at least 2 buffer because we hold on to the last one */
2018 gst_query_add_allocation_pool (query, pool, size, 2, 0);
2019 gst_object_unref (pool);
2022 /* First the default allocator */
2023 if (!gst_egl_image_memory_is_mappable ()) {
2024 allocator = gst_allocator_find (NULL);
2025 gst_query_add_allocation_param (query, allocator, ¶ms);
2026 gst_object_unref (allocator);
2029 allocator = gst_egl_image_allocator_obtain ();
2030 if (!gst_egl_image_memory_is_mappable ())
2031 params.flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
2032 gst_query_add_allocation_param (query, allocator, ¶ms);
2033 gst_object_unref (allocator);
2035 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2036 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2037 gst_query_add_allocation_meta (query,
2038 GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL);
2044 gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps)
2046 gboolean ret = TRUE;
2049 gst_video_info_init (&info);
2050 if (!(ret = gst_video_info_from_caps (&info, caps))) {
2051 GST_ERROR_OBJECT (eglglessink, "Couldn't parse caps");
2055 eglglessink->configured_info = info;
2056 GST_VIDEO_SINK_WIDTH (eglglessink) = info.width;
2057 GST_VIDEO_SINK_HEIGHT (eglglessink) = info.height;
2059 if (eglglessink->configured_caps) {
2060 GST_DEBUG_OBJECT (eglglessink, "Caps were already set");
2061 if (gst_caps_can_intersect (caps, eglglessink->configured_caps)) {
2062 GST_DEBUG_OBJECT (eglglessink, "Caps are compatible anyway");
2066 GST_DEBUG_OBJECT (eglglessink, "Caps are not compatible, reconfiguring");
2068 /* EGL/GLES cleanup */
2069 gst_egl_adaptation_cleanup (eglglessink->egl_context);
2071 gst_caps_unref (eglglessink->configured_caps);
2072 eglglessink->configured_caps = NULL;
2075 if (!gst_egl_adaptation_choose_config (eglglessink->egl_context)) {
2076 GST_ERROR_OBJECT (eglglessink, "Couldn't choose EGL config");
2080 gst_caps_replace (&eglglessink->configured_caps, caps);
2082 /* By now the application should have set a window
2083 * if it meant to do so
2085 GST_OBJECT_LOCK (eglglessink);
2086 if (!eglglessink->have_window) {
2088 GST_INFO_OBJECT (eglglessink,
2089 "No window. Will attempt internal window creation");
2090 if (!gst_eglglessink_create_window (eglglessink, info.width, info.height)) {
2091 GST_ERROR_OBJECT (eglglessink, "Internal window creation failed!");
2092 GST_OBJECT_UNLOCK (eglglessink);
2095 eglglessink->using_own_window = TRUE;
2096 eglglessink->have_window = TRUE;
2098 GST_DEBUG_OBJECT (eglglessink, "Using window handle %p",
2099 (gpointer) eglglessink->egl_context->window);
2100 eglglessink->egl_context->used_window = eglglessink->egl_context->window;
2101 GST_OBJECT_UNLOCK (eglglessink);
2102 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (eglglessink),
2103 (guintptr) eglglessink->egl_context->used_window);
2105 if (!eglglessink->egl_context->have_surface) {
2106 if (!gst_egl_adaptation_init_egl_surface (eglglessink->egl_context,
2107 eglglessink->configured_info.finfo->format)) {
2108 GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface from window");
2113 gst_egl_adaptation_init_egl_exts (eglglessink->egl_context);
2116 GST_INFO_OBJECT (eglglessink, "Configured caps successfully");
2120 GST_ERROR_OBJECT (eglglessink, "Configuring caps failed");
2125 gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2127 GstEglGlesSink *eglglessink;
2129 GstBufferPool *newpool, *oldpool;
2130 GstStructure *config;
2131 GstAllocationParams params = { 0, };
2133 eglglessink = GST_EGLGLESSINK (bsink);
2135 GST_DEBUG_OBJECT (eglglessink,
2136 "Current caps %" GST_PTR_FORMAT ", setting caps %"
2137 GST_PTR_FORMAT, eglglessink->current_caps, caps);
2139 if (gst_eglglessink_queue_object (eglglessink,
2140 GST_MINI_OBJECT_CAST (caps)) != GST_FLOW_OK) {
2141 GST_ERROR_OBJECT (eglglessink, "Failed to configure caps");
2145 if (!gst_video_info_from_caps (&info, caps)) {
2146 GST_ERROR_OBJECT (eglglessink, "Invalid caps %" GST_PTR_FORMAT, caps);
2151 gst_egl_image_buffer_pool_new (eglglessink,
2152 eglglessink->egl_context->display);
2153 config = gst_buffer_pool_get_config (newpool);
2154 /* we need at least 2 buffer because we hold on to the last one */
2155 gst_buffer_pool_config_set_params (config, caps, info.size, 2, 0);
2156 gst_buffer_pool_config_set_allocator (config, NULL, ¶ms);
2157 if (!gst_buffer_pool_set_config (newpool, config)) {
2158 gst_object_unref (newpool);
2159 GST_ERROR_OBJECT (eglglessink, "Failed to set buffer pool configuration");
2163 GST_OBJECT_LOCK (eglglessink);
2164 oldpool = eglglessink->pool;
2165 eglglessink->pool = newpool;
2166 GST_OBJECT_UNLOCK (eglglessink);
2169 gst_object_unref (oldpool);
2171 gst_caps_replace (&eglglessink->current_caps, caps);
2177 gst_eglglessink_open (GstEglGlesSink * eglglessink)
2179 if (!egl_init (eglglessink)) {
2187 gst_eglglessink_close (GstEglGlesSink * eglglessink)
2189 if (eglglessink->egl_context->display) {
2190 gst_egl_display_unref (eglglessink->egl_context->display);
2191 eglglessink->egl_context->display = NULL;
2194 gst_caps_unref (eglglessink->sinkcaps);
2195 eglglessink->sinkcaps = NULL;
2196 eglglessink->egl_started = FALSE;
2198 GST_OBJECT_LOCK (eglglessink);
2199 if (eglglessink->pool)
2200 gst_object_unref (eglglessink->pool);
2201 eglglessink->pool = NULL;
2202 GST_OBJECT_UNLOCK (eglglessink);
2207 static GstStateChangeReturn
2208 gst_eglglessink_change_state (GstElement * element, GstStateChange transition)
2210 GstEglGlesSink *eglglessink;
2211 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2213 eglglessink = GST_EGLGLESSINK (element);
2215 switch (transition) {
2216 case GST_STATE_CHANGE_NULL_TO_READY:
2217 if (!gst_eglglessink_open (eglglessink)) {
2218 ret = GST_STATE_CHANGE_FAILURE;
2222 case GST_STATE_CHANGE_READY_TO_PAUSED:
2223 if (!gst_eglglessink_start (eglglessink)) {
2224 ret = GST_STATE_CHANGE_FAILURE;
2232 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2233 if (ret == GST_STATE_CHANGE_FAILURE)
2236 switch (transition) {
2237 case GST_STATE_CHANGE_READY_TO_NULL:
2238 if (!gst_eglglessink_close (eglglessink)) {
2239 ret = GST_STATE_CHANGE_FAILURE;
2243 case GST_STATE_CHANGE_PAUSED_TO_READY:
2244 if (!gst_eglglessink_stop (eglglessink)) {
2245 ret = GST_STATE_CHANGE_FAILURE;
2258 gst_eglglessink_finalize (GObject * object)
2260 GstEglGlesSink *eglglessink;
2262 g_return_if_fail (GST_IS_EGLGLESSINK (object));
2264 eglglessink = GST_EGLGLESSINK (object);
2266 gst_egl_adaptation_context_free (eglglessink->egl_context);
2268 if (eglglessink->queue)
2269 g_object_unref (eglglessink->queue);
2270 eglglessink->queue = NULL;
2272 g_cond_clear (&eglglessink->render_cond);
2273 g_mutex_clear (&eglglessink->render_lock);
2275 G_OBJECT_CLASS (parent_class)->finalize (object);
2279 gst_eglglessink_set_property (GObject * object, guint prop_id,
2280 const GValue * value, GParamSpec * pspec)
2282 GstEglGlesSink *eglglessink;
2284 g_return_if_fail (GST_IS_EGLGLESSINK (object));
2286 eglglessink = GST_EGLGLESSINK (object);
2289 case PROP_CREATE_WINDOW:
2290 eglglessink->create_window = g_value_get_boolean (value);
2292 case PROP_FORCE_ASPECT_RATIO:
2293 eglglessink->force_aspect_ratio = g_value_get_boolean (value);
2296 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2302 gst_eglglessink_get_property (GObject * object, guint prop_id,
2303 GValue * value, GParamSpec * pspec)
2305 GstEglGlesSink *eglglessink;
2307 g_return_if_fail (GST_IS_EGLGLESSINK (object));
2309 eglglessink = GST_EGLGLESSINK (object);
2312 case PROP_CREATE_WINDOW:
2313 g_value_set_boolean (value, eglglessink->create_window);
2315 case PROP_FORCE_ASPECT_RATIO:
2316 g_value_set_boolean (value, eglglessink->force_aspect_ratio);
2319 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2324 /* initialize the eglglessink's class */
2326 gst_eglglessink_class_init (GstEglGlesSinkClass * klass)
2328 GObjectClass *gobject_class;
2329 GstElementClass *gstelement_class;
2330 GstBaseSinkClass *gstbasesink_class;
2331 GstVideoSinkClass *gstvideosink_class;
2333 gobject_class = (GObjectClass *) klass;
2334 gstelement_class = (GstElementClass *) klass;
2335 gstbasesink_class = (GstBaseSinkClass *) klass;
2336 gstvideosink_class = (GstVideoSinkClass *) klass;
2338 gobject_class->set_property = gst_eglglessink_set_property;
2339 gobject_class->get_property = gst_eglglessink_get_property;
2340 gobject_class->finalize = gst_eglglessink_finalize;
2342 gstelement_class->change_state = gst_eglglessink_change_state;
2343 gstelement_class->set_context = gst_eglglessink_set_context;
2345 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_setcaps);
2346 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_getcaps);
2347 gstbasesink_class->propose_allocation =
2348 GST_DEBUG_FUNCPTR (gst_eglglessink_propose_allocation);
2349 gstbasesink_class->prepare = GST_DEBUG_FUNCPTR (gst_eglglessink_prepare);
2350 gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_eglglessink_query);
2351 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_eglglessink_event);
2353 gstvideosink_class->show_frame =
2354 GST_DEBUG_FUNCPTR (gst_eglglessink_show_frame);
2356 g_object_class_install_property (gobject_class, PROP_CREATE_WINDOW,
2357 g_param_spec_boolean ("create-window", "Create Window",
2358 "If set to true, the sink will attempt to create it's own window to "
2359 "render to if none is provided. This is currently only supported "
2360 "when the sink is used under X11",
2361 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2362 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2363 g_param_spec_boolean ("force-aspect-ratio",
2364 "Respect aspect ratio when scaling",
2365 "If set to true, the sink will attempt to preserve the incoming "
2366 "frame's geometry while scaling, taking both the storage's and "
2367 "display's pixel aspect ratio into account",
2368 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2370 gst_element_class_set_static_metadata (gstelement_class,
2371 "EGL/GLES vout Sink",
2373 "An EGL/GLES Video Output Sink Implementing the VideoOverlay interface",
2374 "Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>, "
2375 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
2377 gst_element_class_add_pad_template (gstelement_class,
2378 gst_static_pad_template_get (&gst_eglglessink_sink_template_factory));
2382 queue_check_full_func (GstDataQueue * queue, guint visible, guint bytes,
2383 guint64 time, gpointer checkdata)
2385 return visible != 0;
2389 gst_eglglessink_init (GstEglGlesSink * eglglessink)
2391 eglglessink->egl_context =
2392 gst_egl_adaptation_context_new (GST_ELEMENT_CAST (eglglessink));
2397 eglglessink->have_window = FALSE;
2398 eglglessink->egl_context->have_surface = FALSE;
2399 eglglessink->egl_context->have_vbo = FALSE;
2400 eglglessink->egl_context->have_texture = FALSE;
2401 eglglessink->egl_started = FALSE;
2402 eglglessink->using_own_window = FALSE;
2405 eglglessink->create_window = TRUE;
2406 eglglessink->force_aspect_ratio = TRUE;
2408 g_mutex_init (&eglglessink->render_lock);
2409 g_cond_init (&eglglessink->render_cond);
2410 eglglessink->queue =
2411 gst_data_queue_new (queue_check_full_func, NULL, NULL, NULL);
2412 eglglessink->last_flow = GST_FLOW_FLUSHING;
2414 eglglessink->render_region.x = 0;
2415 eglglessink->render_region.y = 0;
2416 eglglessink->render_region.w = -1;
2417 eglglessink->render_region.h = -1;
2418 eglglessink->render_region_changed = TRUE;
2419 eglglessink->render_region_user = FALSE;
2422 static GstBufferPool *
2423 gst_egl_image_buffer_pool_new (GstEglGlesSink *
2424 eglglessink, GstEGLDisplay * display)
2426 GstEGLImageBufferPool *pool;
2428 pool = g_object_new (gst_egl_image_buffer_pool_get_type (), NULL);
2429 pool->display = gst_egl_display_ref (display);
2430 pool->sink = gst_object_ref (eglglessink);
2432 return (GstBufferPool *) pool;
2435 /* entry point to initialize the plug-in
2436 * initialize the plug-in itself
2437 * register the element factories and other features
2440 eglglessink_plugin_init (GstPlugin * plugin)
2442 /* debug category for fltering log messages */
2443 GST_DEBUG_CATEGORY_INIT (gst_eglglessink_debug, "eglglessink",
2444 0, "Simple EGL/GLES Sink");
2446 gst_egl_adaption_init ();
2449 GST_DEBUG ("Initialize BCM host");
2453 return gst_element_register (plugin, "eglglessink", GST_RANK_PRIMARY,
2454 GST_TYPE_EGLGLESSINK);
2457 /* gstreamer looks for this structure to register eglglessinks */
2458 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2462 eglglessink_plugin_init,
2463 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)