From: Reynaldo H. Verdejo Pinochet Date: Mon, 10 Sep 2012 23:00:57 +0000 (-0300) Subject: eglglessink: Move eglgles Sink to ext/ X-Git-Tag: 1.19.3~507^2~14695 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=85250eb9e20141ab8ad6e0660a8c493a6d80278a;p=platform%2Fupstream%2Fgstreamer.git eglglessink: Move eglgles Sink to ext/ --- diff --git a/ext/eglgles/Android.mk b/ext/eglgles/Android.mk new file mode 100644 index 0000000..6f8db38 --- /dev/null +++ b/ext/eglgles/Android.mk @@ -0,0 +1,71 @@ +LOCAL_PATH := $(call my-dir) + +# ------------------------------------- +# gsteglglessink library +# +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm + +gsteglglessink_FILES := gsteglglessink.c + +LOCAL_SRC_FILES := $(gsteglglessink_FILES) +LOCAL_C_INCLUDES = $(LOCAL_PATH) \ + $(LOCAL_PATH)/include + +ifneq ($(NDK_BUILD), true) +LOCAL_C_INCLUDES += $(TOP)/frameworks/base +else +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../android_headers +endif + +LOCAL_CFLAGS += -DHAVE_CONFIG_H +LOCAL_CFLAGS += -Wall -Wdeclaration-after-statement -g -O2 +LOCAL_CFLAGS += -DANDROID_USE_GSTREAMER \ + $(shell $(PKG_CONFIG) gstreamer-plugins-bad-0.10 --cflags) \ + $(shell $(PKG_CONFIG) gstreamer-audio-0.10 --cflags) + +ifeq ($(USE_AUDIO_PURE_CODEC),true) +LOCAL_CFLAGS += -DAUDIO_PURE_CODEC +endif + +LOCAL_SHARED_LIBRARIES += libdl +LOCAL_SHARED_LIBRARIES += \ + libgstreamer-0.10 \ + libgstbase-0.10 \ + libglib-2.0 \ + libgthread-2.0 \ + libgmodule-2.0 \ + libgobject-2.0 \ + libgstvideo-0.10 \ + libgstinterfaces-0.10 + +ifneq ($(NDK_BUILD), true) +LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib -llog +else +LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib -llog -lmedia -lutils +endif + +LOCAL_LDFLAGS += -lEGL -lGLESv2 + +LOCAL_SHARED_LIBRARIES += \ + libutils \ + libcutils \ + libui \ + libhardware \ + libandroid_runtime \ + libmedia + + +LOCAL_MODULE:= libgsteglglessink +LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/gstreamer-0.10 + +# +# define LOCAL_PRELINK_MODULE to false to not use pre-link map +# +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_TAGS := eng debug + +include $(BUILD_SHARED_LIBRARY) + +#endif # USE_HARDWARE_MM == true diff --git a/ext/eglgles/Makefile.am b/ext/eglgles/Makefile.am new file mode 100644 index 0000000..ff0adda --- /dev/null +++ b/ext/eglgles/Makefile.am @@ -0,0 +1,18 @@ +plugin_LTLIBRARIES = libgsteglglessink.la + +libgsteglglessink_la_SOURCES = gsteglglessink.c video_platform_wrapper.c + +libgsteglglessink_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(DROIDSINKS_CFLAGS) \ + $(X11_CFLAGS) + +libgsteglglessink_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) $(DROIDSINKS_LIBS) $(X11_LIBS) \ + -lgstvideo-$(GST_MAJORMINOR) \ + -lgstinterfaces-$(GST_MAJORMINOR) + +libgsteglglessink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgsteglglessink_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gsteglglessink.h video_platform_wrapper.h diff --git a/ext/eglgles/gstvidroidsink.c b/ext/eglgles/gstvidroidsink.c new file mode 100644 index 0000000..5a58f13 --- /dev/null +++ b/ext/eglgles/gstvidroidsink.c @@ -0,0 +1,1640 @@ +/* + * GStreamer EGL/GLES Sink + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-eglglessink + * + * This is a vout sink using EGL/GLES. + * + * + * Rationale on OpenGL ES version + * + * This Sink uses GLESv2 + * + * + * + * + * Example launch line + * |[ + * gst-launch -v -m videotestsrc ! eglglessink + * ]| + * + * + * + * Example launch line with forced slow path rendering + * + * The sink will chose a buffer copy-over slow rendering path even + * if needed EGL/GLES extensions to use a fast rendering path are + * available. + * + * |[ + * gst-launch -v -m videotestsrc ! eglglessink force_rendering_slow=TRUE + * ]| + * + * + * + * Example launch line with internal window creation disabled + * + * The sink will wait for a window handle through it's xOverlay interface + * even if internal window creation is supported by the platform and + * implemented. + * + * |[ + * gst-launch -v -m videotestsrc ! eglglessink force_rendering_slow=TRUE + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "video_platform_wrapper.h" + +#include "gsteglglessink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_eglglessink_debug); +#define GST_CAT_DEFAULT gst_eglglessink_debug + +/* XXX: These should be defined per model someway + * but the Galaxy Nexus's were taken as a reference + * for now on: + */ +#define EGLGLESSINK_MAX_FRAME_WIDTH 1280 +#define EGLGLESSINK_MAX_FRAME_HEIGHT 720 + +/* These are only needed for the fast rendering path */ +#ifdef EGL_KHR_image +static PFNEGLCREATEIMAGEKHRPROC my_eglCreateImageKHR; +static PFNEGLDESTROYIMAGEKHRPROC my_eglDestroyImageKHR; + +#ifdef EGL_KHR_lock_surface +static PFNEGLLOCKSURFACEKHRPROC my_eglLockSurfaceKHR; +static PFNEGLUNLOCKSURFACEKHRPROC my_eglUnlockSurfaceKHR; + +static EGLint lock_attribs[] = { + EGL_MAP_PRESERVE_PIXELS_KHR, EGL_TRUE, + EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR | EGL_WRITE_SURFACE_BIT_KHR, + EGL_NONE +}; + +#ifdef GL_OES_EGL_image +static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC my_glEGLImageTargetTexture2DOES; +#define EGL_FAST_RENDERING_POSSIBLE 1 +#endif +#endif +#endif + +static const char *vert_prog = { + "attribute vec3 position;" + "varying vec2 opos;" + "void main(void)" + "{" + " opos = vec2((position.x + 1.0)/2.0, ((-1.0 * position.y) + 1.0)/2.0);" + " gl_Position = vec4(position, 1.0);" "}" +}; + +static const char *frag_prog = { + "varying vec2 opos;" + "uniform sampler2D tex;" + "void main(void)" + "{" + " vec4 t = texture2D(tex, opos);" " gl_FragColor = vec4(t.xyz, 0.5);" "}" +}; + +/* Input capabilities. + * + * OpenGL ES Standard does not mandate YUV support + * + * XXX: Extend RGB support to a set. Maybe implement YUV too. + */ +static GstStaticPadTemplate gst_eglglessink_sink_template_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB_16)); + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_SILENT, + PROP_CAN_CREATE_WINDOW, + PROP_DEFAULT_HEIGHT, + PROP_DEFAULT_WIDTH, + PROP_FORCE_RENDERING_SLOW +}; + +/* XXX: Harcoded for now */ +static EGLint eglglessink_RGB16_config[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_NONE +}; + +static void gst_eglglessink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_eglglessink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static GstFlowReturn gst_eglglessink_show_frame (GstVideoSink * vsink, + GstBuffer * buf); +static gboolean gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps); +static gboolean gst_eglglessink_start (GstBaseSink * sink); +static gboolean gst_eglglessink_stop (GstBaseSink * sink); +static GstFlowReturn gst_eglglessink_buffer_alloc (GstBaseSink * sink, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); + +/* XOverlay interface cruft */ +static gboolean gst_eglglessink_interface_supported + (GstImplementsInterface * iface, GType type); +static void gst_eglglessink_implements_init + (GstImplementsInterfaceClass * klass); +static void gst_eglglessink_xoverlay_init (GstXOverlayClass * iface); +static void gst_eglglessink_init_interfaces (GType type); + +/* Actual XOverlay interface funcs */ +static void gst_eglglessink_expose (GstXOverlay * overlay); +static void gst_eglglessink_set_window_handle (GstXOverlay * overlay, + guintptr id); + +/* Custom Buffer funcs */ +static void gst_eglglesbuffer_destroy (GstEglGlesBuffer * eglglessink); +static void gst_eglglesbuffer_init (GstEglGlesBuffer * eglglessink, + gpointer g_class); +static GType gst_eglglesbuffer_get_type (void); +static gint gst_eglglessink_get_compat_format_from_caps + (GstEglGlesSink * eglglessink, GstCaps * caps); +static void gst_eglglesbuffer_finalize (GstEglGlesBuffer * eglglessink); +static void gst_eglglesbuffer_class_init (gpointer g_class, + gpointer class_data); +static void gst_eglglesbuffer_free (GstEglGlesBuffer * eglglesbuffer); +static GstEglGlesBuffer *gst_eglglesbuffer_new (GstEglGlesSink * eglglessink, + GstCaps * caps); +static EGLint *gst_eglglesbuffer_create_native (EGLNativeWindowType win, + EGLConfig config, EGLNativeDisplayType display, const EGLint * egl_attribs); + +/* Utility */ +static EGLNativeWindowType gst_eglglessink_create_window (GstEglGlesSink * + eglglessink, gint width, gint height); +static gboolean gst_eglglessink_init_egl_display (GstEglGlesSink * eglglessink); +static gboolean gst_eglglessink_init_egl_surface (GstEglGlesSink * eglglessink); +static void gst_eglglessink_init_egl_exts (GstEglGlesSink * eglglessink); +static void gst_eglglessink_render_and_display (GstEglGlesSink * sink, + GstBuffer * buf); +static inline gboolean got_gl_error (const char *wtf); + +static GstBufferClass *gsteglglessink_buffer_parent_class = NULL; +#define GST_TYPE_EGLGLESBUFFER (gst_eglglesbuffer_get_type()) +#define GST_IS_EGLGLESBUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_EGLGLESBUFFER)) +#define GST_EGLGLESBUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_EGLGLESBUFFER, GstEglGlesBuffer)) +#define GST_EGLGLESBUFFER_CAST(obj) ((GstEglGlesBuffer *)(obj)) + + +GST_BOILERPLATE_FULL (GstEglGlesSink, gst_eglglessink, GstVideoSink, + GST_TYPE_VIDEO_SINK, gst_eglglessink_init_interfaces) + +/* Custom Buffer Funcs */ +/* XXX: Drafted implementation */ + static EGLint *gst_eglglesbuffer_create_native (EGLNativeWindowType win, + EGLConfig config, EGLNativeDisplayType display, const EGLint * egl_attribs) +{ + EGLNativePixmapType pix; + EGLSurface pix_surface; + EGLint *buffer = NULL; + + /* XXX: Need to figure out how to create an egl_native_pixmap_t to + * feed to eglCreatePixmapSurface. An option on android: create an + * android_native_buffer_t to pass straight to eglCreateImageKHR. + */ + + pix_surface = eglCreatePixmapSurface (display, config, pix, egl_attribs); + + if (pix_surface == EGL_NO_SURFACE) { + GST_CAT_ERROR (GST_CAT_DEFAULT, "Unable to create pixmap surface"); + goto EGL_ERROR; + } + + if (my_eglLockSurfaceKHR (display, pix_surface, lock_attribs) == EGL_FALSE) { + GST_CAT_ERROR (GST_CAT_DEFAULT, "Unable to lock surface"); + goto EGL_ERROR; + } + + if (eglQuerySurface (display, pix_surface, EGL_BITMAP_POINTER_KHR, buffer) + == EGL_FALSE) { + GST_CAT_ERROR (GST_CAT_DEFAULT, + "Unable to query surface for bitmap pointer"); + goto EGL_ERROR_LOCKED; + } + + return buffer; + +EGL_ERROR_LOCKED: + my_eglUnlockSurfaceKHR (display, pix_surface); +EGL_ERROR: + GST_CAT_ERROR (GST_CAT_DEFAULT, "EGL call returned error %x", eglGetError ()); + if (!eglDestroySurface (display, pix_surface)) { + GST_CAT_ERROR (GST_CAT_DEFAULT, "Couldn't destroy surface"); + GST_CAT_ERROR (GST_CAT_DEFAULT, "EGL call returned error %x", + eglGetError ()); + } + return NULL; +} + +static GstEglGlesBuffer * +gst_eglglesbuffer_new (GstEglGlesSink * eglglessink, GstCaps * caps) +{ + GstEglGlesBuffer *eglglesbuffer = NULL; + GstStructure *structure = NULL; + + g_return_val_if_fail (GST_IS_EGLGLESSINK (eglglessink), NULL); + g_return_val_if_fail (caps, NULL); + + eglglesbuffer = + (GstEglGlesBuffer *) gst_mini_object_new (GST_TYPE_EGLGLESBUFFER); + GST_DEBUG_OBJECT (eglglesbuffer, "Creating new GstEglGlesBuffer"); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "width", &eglglesbuffer->width) || + !gst_structure_get_int (structure, "height", &eglglesbuffer->height)) { + GST_WARNING ("Failed getting geometry from caps %" GST_PTR_FORMAT, caps); + } + + GST_LOG_OBJECT (eglglessink, "creating %dx%d", eglglesbuffer->width, + eglglesbuffer->height); + + eglglesbuffer->format = + gst_eglglessink_get_compat_format_from_caps (eglglessink, caps); + + if (eglglesbuffer->format == GST_EGLGLESSINK_IMAGE_NOFMT) { + GST_WARNING_OBJECT (eglglessink, + "Failed to get format from caps %" GST_PTR_FORMAT, caps); + GST_ERROR_OBJECT (eglglessink, + "Invalid input caps. Failed to create %dx%d buffer", + eglglesbuffer->width, eglglesbuffer->height); + goto BEACH_UNLOCKED; + } + + eglglesbuffer->eglglessink = gst_object_ref (eglglessink); + + eglglesbuffer->image = gst_eglglesbuffer_create_native + (eglglessink->window, eglglessink->config, eglglessink->display, NULL); + if (!eglglesbuffer->image) { + GST_ERROR_OBJECT (eglglessink, + "Failed to create native %sx%d image buffer", eglglesbuffer->width, + eglglesbuffer->height); + goto BEACH_UNLOCKED; + } + + GST_BUFFER_DATA (eglglesbuffer) = (guchar *) eglglesbuffer->image; + GST_BUFFER_SIZE (eglglesbuffer) = eglglesbuffer->size; + + return eglglesbuffer; + +BEACH_UNLOCKED: + gst_eglglesbuffer_free (eglglesbuffer); + eglglesbuffer = NULL; + return NULL; +} + +static void +gst_eglglesbuffer_destroy (GstEglGlesBuffer * eglglesbuffer) +{ + + GstEglGlesSink *eglglessink; + + GST_DEBUG_OBJECT (eglglesbuffer, "Destroying buffer"); + + eglglessink = eglglesbuffer->eglglessink; + if (G_UNLIKELY (eglglessink == NULL)) + goto NO_SINK; + + g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink)); + + GST_OBJECT_LOCK (eglglessink); + GST_DEBUG_OBJECT (eglglessink, "Destroying image"); + + if (eglglesbuffer->image) { + if (GST_BUFFER_DATA (eglglesbuffer)) { + g_free (GST_BUFFER_DATA (eglglesbuffer)); + } + eglglesbuffer->image = NULL; + /* XXX: Unallocate EGL/GL especific resources asociated with this + * Image here + */ + } + + GST_OBJECT_UNLOCK (eglglessink); + eglglesbuffer->eglglessink = NULL; + gst_object_unref (eglglessink); + + GST_MINI_OBJECT_CLASS (gsteglglessink_buffer_parent_class)->finalize + (GST_MINI_OBJECT (eglglesbuffer)); + + return; + +NO_SINK: + GST_WARNING ("No sink found"); + return; +} + +/* XXX: Missing implementation. + * This function will have the code for maintaing the pool. readding or + * destroying the buffers on size or runing/status change. Right now all + * it does is to call _destroy. + * for a proper implementation take a look at xvimagesink's image buffer + * destroy func. + */ +static void +gst_eglglesbuffer_finalize (GstEglGlesBuffer * eglglesbuffer) +{ + GstEglGlesSink *eglglessink; + + eglglessink = eglglesbuffer->eglglessink; + if (G_UNLIKELY (eglglessink == NULL)) + goto NO_SINK; + + g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink)); + + gst_eglglesbuffer_destroy (eglglesbuffer); + + return; + +NO_SINK: + GST_WARNING ("No sink found"); + return; +} + +static void +gst_eglglesbuffer_free (GstEglGlesBuffer * eglglesbuffer) +{ + /* Make sure it is not recycled. This is meaningless without + * a pool but was left here as a reference + */ + eglglesbuffer->width = -1; + eglglesbuffer->height = -1; + gst_buffer_unref (GST_BUFFER (eglglesbuffer)); +} + +static void +gst_eglglesbuffer_init (GstEglGlesBuffer * eglglesbuffer, gpointer g_class) +{ + eglglesbuffer->width = 0; + eglglesbuffer->height = 0; + eglglesbuffer->size = 0; + eglglesbuffer->image = NULL; + eglglesbuffer->format = GST_EGLGLESSINK_IMAGE_NOFMT; +} + +static void +gst_eglglesbuffer_class_init (gpointer g_class, gpointer class_data) +{ + GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); + + gsteglglessink_buffer_parent_class = g_type_class_peek_parent (g_class); + + mini_object_class->finalize = (GstMiniObjectFinalizeFunction) + gst_eglglesbuffer_finalize; +} + +static GType +gst_eglglesbuffer_get_type (void) +{ + static GType _gst_eglglessink_buffer_type; + + if (G_UNLIKELY (_gst_eglglessink_buffer_type == 0)) { + static const GTypeInfo eglglessink_buffer_info = { + sizeof (GstBufferClass), + NULL, + NULL, + gst_eglglesbuffer_class_init, + NULL, + NULL, + sizeof (GstEglGlesBuffer), + 0, + (GInstanceInitFunc) gst_eglglesbuffer_init, + NULL + }; + _gst_eglglessink_buffer_type = g_type_register_static (GST_TYPE_BUFFER, + "GstEglGlesBuffer", &eglglessink_buffer_info, 0); + } + return _gst_eglglessink_buffer_type; +} + + +/* This function is sort of meaningless right now as we + * Only Support one image format / caps but was left here + * as a reference for future improvements. + */ +static gint +gst_eglglessink_get_compat_format_from_caps (GstEglGlesSink * eglglessink, + GstCaps * caps) +{ + + GList *list = NULL; + GstEglGlesImageFmt *format; + + g_return_val_if_fail (GST_IS_EGLGLESSINK (eglglessink), 0); + + list = eglglessink->supported_fmts; + + /* Traverse the list trying to find a compatible format */ + while (list) { + format = list->data; + GST_DEBUG_OBJECT (eglglessink, "Checking compatibility between listed %" + GST_PTR_FORMAT " and %" GST_PTR_FORMAT, format->caps, caps); + if (format) { + if (gst_caps_can_intersect (caps, format->caps)) { + return format->fmt; + } + } + list = g_list_next (list); + } + + return GST_EGLGLESSINK_IMAGE_NOFMT; +} + +static GstCaps * +gst_eglglessink_different_size_suggestion (GstEglGlesSink * eglglessink, + GstCaps * caps) +{ + GstCaps *intersection; + GstCaps *new_caps; + GstStructure *s; + gint width, height; + gint par_n = 1, par_d = 1; + gint dar_n, dar_d; + gint w, h; + + new_caps = gst_caps_copy (caps); + + s = gst_caps_get_structure (new_caps, 0); + + gst_structure_get_int (s, "width", &width); + gst_structure_get_int (s, "height", &height); + gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d); + + gst_structure_remove_field (s, "width"); + gst_structure_remove_field (s, "height"); + gst_structure_remove_field (s, "pixel-aspect-ratio"); + + intersection = gst_caps_intersect (eglglessink->current_caps, new_caps); + gst_caps_unref (new_caps); + + if (gst_caps_is_empty (intersection)) + return intersection; + + s = gst_caps_get_structure (intersection, 0); + + gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d); + + /* XXX: xvimagesink supports all PARs not sure about our eglglessink + * though, need to review this afterwards. + */ + + gst_structure_fixate_field_nearest_int (s, "width", width); + gst_structure_fixate_field_nearest_int (s, "height", height); + gst_structure_get_int (s, "width", &w); + gst_structure_get_int (s, "height", &h); + + gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d); + gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d, + NULL); + + return intersection; +} + +static GstFlowReturn +gst_eglglessink_buffer_alloc (GstBaseSink * bsink, guint64 offset, + guint size, GstCaps * caps, GstBuffer ** buf) +{ + + GstEglGlesSink *eglglessink; + GstFlowReturn ret = GST_FLOW_OK; + GstEglGlesBuffer *eglglesbuffer = NULL; + GstCaps *intersection = NULL; + GstStructure *structure = NULL; + GstVideoFormat image_format; + gint width, height; + + eglglessink = GST_EGLGLESSINK (bsink); + + /* No custom alloc for the slow rendering path */ + if (eglglessink->rendering_path == GST_EGLGLESSINK_RENDER_SLOW) { + GST_INFO_OBJECT (eglglessink, "No custom alloc for slow rendering path"); + *buf = NULL; + return GST_FLOW_OK; + } + + if (G_UNLIKELY (!caps)) + goto NO_CAPS; + + if (G_LIKELY (gst_caps_is_equal (caps, eglglessink->current_caps))) { + GST_LOG_OBJECT (eglglessink, + "Buffer alloc for same last_caps, reusing caps"); + intersection = gst_caps_ref (caps); + image_format = eglglessink->format; + width = GST_VIDEO_SINK_WIDTH (eglglessink); + height = GST_VIDEO_SINK_HEIGHT (eglglessink); + + goto REUSE_LAST_CAPS; + } + + GST_DEBUG_OBJECT (eglglessink, "Buffer alloc requested size %d with caps %" + GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size, + caps, eglglessink->current_caps); + + /* Check the caps against our current caps */ + intersection = gst_caps_intersect (eglglessink->current_caps, caps); + + GST_DEBUG_OBJECT (eglglessink, "Intersection in buffer alloc returned %" + GST_PTR_FORMAT, intersection); + + if (gst_caps_is_empty (intersection)) { + GstCaps *new_caps; + + gst_caps_unref (intersection); + + /* So we don't support this kind of buffer, let's define one we'd like */ + new_caps = gst_caps_copy (caps); + + structure = gst_caps_get_structure (new_caps, 0); + if (!gst_structure_has_field (structure, "width") || + !gst_structure_has_field (structure, "height")) { + gst_caps_unref (new_caps); + goto INVALID; + } + + /* Try different dimensions */ + intersection = + gst_eglglessink_different_size_suggestion (eglglessink, new_caps); + + /* YUV not implemented yet */ + if (gst_caps_is_empty (intersection)) { + + gst_structure_set_name (structure, "video/x-raw-rgb"); + + /* Remove format specific fields */ + gst_structure_remove_field (structure, "format"); + gst_structure_remove_field (structure, "endianness"); + gst_structure_remove_field (structure, "depth"); + gst_structure_remove_field (structure, "bpp"); + gst_structure_remove_field (structure, "red_mask"); + gst_structure_remove_field (structure, "green_mask"); + gst_structure_remove_field (structure, "blue_mask"); + gst_structure_remove_field (structure, "alpha_mask"); + + /* Reuse intersection with current_caps */ + intersection = gst_caps_intersect (eglglessink->current_caps, new_caps); + } + + /* Try with different dimensions */ + if (gst_caps_is_empty (intersection)) + intersection = + gst_eglglessink_different_size_suggestion (eglglessink, new_caps); + + /* Clean this copy */ + gst_caps_unref (new_caps); + + if (gst_caps_is_empty (intersection)) + goto INCOMPATIBLE; + } + + /* Ensure the returned caps are fixed */ + gst_caps_truncate (intersection); + + GST_DEBUG_OBJECT (eglglessink, "Allocating a buffer with caps %" + GST_PTR_FORMAT, intersection); + if (gst_caps_is_equal (intersection, caps)) { + /* Things work better if we return a buffer with the same caps ptr + * as was asked for when we can */ + gst_caps_replace (&intersection, caps); + } + + /* Get image format from caps */ + image_format = gst_eglglessink_get_compat_format_from_caps (eglglessink, + intersection); + + if (image_format == GST_EGLGLESSINK_IMAGE_NOFMT) + GST_WARNING_OBJECT (eglglessink, "Can't get a compatible format from caps"); + + /* Get geometry from caps */ + structure = gst_caps_get_structure (intersection, 0); + if (!gst_structure_get_int (structure, "width", &width) || + !gst_structure_get_int (structure, "height", &height) || + image_format == GST_EGLGLESSINK_IMAGE_NOFMT) + goto INVALID_CAPS; + +REUSE_LAST_CAPS: + + GST_DEBUG_OBJECT (eglglessink, "Creating eglglesbuffer"); + eglglesbuffer = gst_eglglesbuffer_new (eglglessink, intersection); + + if (eglglesbuffer) { + /* Make sure the buffer is cleared of any previously used flags */ + GST_MINI_OBJECT_CAST (eglglesbuffer)->flags = 0; + gst_buffer_set_caps (GST_BUFFER_CAST (eglglesbuffer), intersection); + } + + *buf = GST_BUFFER_CAST (eglglesbuffer); + +BEACH: + if (intersection) { + gst_caps_unref (intersection); + } + + return ret; + + /* ERRORS */ +INVALID: + { + GST_DEBUG_OBJECT (eglglessink, "No width/height on caps!?"); + ret = GST_FLOW_WRONG_STATE; + goto BEACH; + } +INCOMPATIBLE: + { + GST_WARNING_OBJECT (eglglessink, "We were requested a buffer with " + "caps %" GST_PTR_FORMAT ", but our current caps %" GST_PTR_FORMAT + " are completely incompatible!", caps, eglglessink->current_caps); + ret = GST_FLOW_NOT_NEGOTIATED; + goto BEACH; + } +INVALID_CAPS: + { + GST_WARNING_OBJECT (eglglessink, "Invalid caps for buffer allocation %" + GST_PTR_FORMAT, intersection); + ret = GST_FLOW_NOT_NEGOTIATED; + goto BEACH; + } +NO_CAPS: + { + GST_WARNING_OBJECT (eglglessink, "Have no caps, doing fallback allocation"); + *buf = NULL; + ret = GST_FLOW_OK; + goto BEACH; + } +} + +gboolean +gst_eglglessink_start (GstBaseSink * sink) +{ + gboolean ret; + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (sink); + GstEglGlesImageFmt *format; + + eglglessink->flow_lock = g_mutex_new (); + g_mutex_lock (eglglessink->flow_lock); + + ret = gst_eglglessink_init_egl_display (eglglessink); + + if (!ret) { + GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL display"); + goto HANDLE_ERROR; + } + + ret = platform_wrapper_init (); + + if (!ret) { + GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL platform wrapper"); + goto HANDLE_ERROR; + } + + /* Init supported caps list (Right now we just harcode the only one we support) + * XXX: Not sure this is the right place to do it. + */ + format = g_new0 (GstEglGlesImageFmt, 1); + if (format) { + format->fmt = GST_EGLGLESSINK_IMAGE_RGB565; + format->caps = gst_caps_copy (gst_pad_get_pad_template_caps + (GST_VIDEO_SINK_PAD (eglglessink))); + eglglessink->supported_fmts = g_list_append + (eglglessink->supported_fmts, format); + } + + g_mutex_unlock (eglglessink->flow_lock); + + return TRUE; + +HANDLE_ERROR: + g_mutex_unlock (eglglessink->flow_lock); + return FALSE; +} + +/* XXX: Should implement */ +gboolean +gst_eglglessink_stop (GstBaseSink * sink) +{ + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (sink); + + platform_destroy_native_window (eglglessink->display, eglglessink->window); + g_mutex_free (eglglessink->flow_lock); + eglglessink->flow_lock = NULL; + + return TRUE; +} + +static void +gst_eglglessink_xoverlay_init (GstXOverlayClass * iface) +{ + iface->set_window_handle = gst_eglglessink_set_window_handle; + iface->expose = gst_eglglessink_expose; +} + +static gboolean +gst_eglglessink_interface_supported (GstImplementsInterface * iface, GType type) +{ + return (type == GST_TYPE_X_OVERLAY); +} + +static void +gst_eglglessink_implements_init (GstImplementsInterfaceClass * klass) +{ + klass->supported = gst_eglglessink_interface_supported; +} + +static inline gboolean +got_gl_error (const char *wtf) +{ + GLuint error = GL_NO_ERROR; + + if ((error = glGetError ()) != GL_NO_ERROR) { + GST_CAT_ERROR (GST_CAT_DEFAULT, "GL ERROR: %s returned %x", wtf, error); + return TRUE; + } + + return FALSE; +} + +static EGLNativeWindowType +gst_eglglessink_create_window (GstEglGlesSink * eglglessink, gint width, + gint height) +{ + EGLNativeWindowType window = 0; + + if (!eglglessink->can_create_window) { + GST_ERROR_OBJECT (eglglessink, "This sink can't create a window by itself"); + return window; + } else + GST_INFO_OBJECT (eglglessink, "Attempting internal window creation"); + + if (!width && !height) { /* Create a default size window */ + width = eglglessink->window_default_width; + height = eglglessink->window_default_height; + } + + window = platform_create_native_window (width, height); + if (!window) { + GST_ERROR_OBJECT (eglglessink, "Could not create window"); + return window; + } + gst_x_overlay_got_window_handle (GST_X_OVERLAY (eglglessink), window); + return window; +} + +/* XXX: Should implement (redisplay) + * We need at least the last buffer stored for this to work + */ +static void +gst_eglglessink_expose (GstXOverlay * overlay) +{ + GstEglGlesSink *eglglessink; + eglglessink = GST_EGLGLESSINK (overlay); + GST_DEBUG_OBJECT (eglglessink, "Expose catched, redisplay"); + + /* Logic would be to get _render_and_display() to use + * last seen buffer to render from when NULL it's + * passed on */ + g_mutex_lock (eglglessink->flow_lock); + gst_eglglessink_render_and_display (eglglessink, NULL); + g_mutex_unlock (eglglessink->flow_lock); + + return; +} + +/* Checks available egl/gles extensions and chooses + * a suitable rendering path from GstVidroidSinkRenderingPath + * accordingly. This function can only be called after an + * EGL context has been made current. + */ +static void +gst_eglglessink_init_egl_exts (GstEglGlesSink * eglglessink) +{ + const char *eglexts; + unsigned const char *glexts; + + eglexts = eglQueryString (eglglessink->display, EGL_EXTENSIONS); + glexts = glGetString (GL_EXTENSIONS); + + GST_DEBUG_OBJECT (eglglessink, "Available EGL extensions: %s\n", eglexts); + GST_DEBUG_OBJECT (eglglessink, "Available GLES extensions: %s\n", glexts); + +#ifdef EGL_FAST_RENDERING_POSSIBLE + /* OK Fast rendering should be possible from the declared + * extensions on the eglexts/glexts.h headers + */ + + /* Check for support from claimed EGL/GLES extensions */ + + if (!strstr (eglexts, "EGL_KHR_image")) + goto KHR_IMAGE_NA; + if (!strstr (eglexts, "EGL_KHR_lock_surface")) + goto SURFACE_LOCK_NA; + if (!strstr (glexts, "GL_OES_EGL_image")) + goto TEXTURE_2DOES_NA; + + /* Check for actual extension proc addresses */ + + my_eglCreateImageKHR = + (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress ("eglCreateImageKHR"); + my_eglDestroyImageKHR = + (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress ("eglDestroyImageKHR"); + + if (!my_eglCreateImageKHR || !my_eglDestroyImageKHR) { + KHR_IMAGE_NA: + GST_INFO_OBJECT (eglglessink, "Extension missing: EGL_KHR_image"); + goto MISSING_EXTS; + } + + my_eglLockSurfaceKHR = + (PFNEGLLOCKSURFACEKHRPROC) eglGetProcAddress ("eglLockSurfaceKHR"); + my_eglUnlockSurfaceKHR = + (PFNEGLUNLOCKSURFACEKHRPROC) eglGetProcAddress ("eglUnlockSurfaceKHR"); + + if (!my_eglLockSurfaceKHR || !my_eglUnlockSurfaceKHR) { + SURFACE_LOCK_NA: + GST_INFO_OBJECT (eglglessink, "Extension missing: EGL_KHR_lock_surface"); + goto MISSING_EXTS; + } + + my_glEGLImageTargetTexture2DOES = + (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress + ("glEGLImageTargetTexture2DOES"); + + if (!my_glEGLImageTargetTexture2DOES) { + TEXTURE_2DOES_NA: + GST_INFO_OBJECT (eglglessink, "Extension missing: GL_OES_EGL_image"); + goto MISSING_EXTS; + } + + if (!eglglessink->force_rendering_slow) { + GST_INFO_OBJECT (eglglessink, + "Have needed extensions for fast rendering path"); + } else { + GST_WARNING_OBJECT (eglglessink, + "Extension check passed but slow rendering path being forced"); + goto SLOW_PATH_SELECTED; + } + + /* Extension check passed. Enable fast rendering path */ + eglglessink->rendering_path = GST_EGLGLESSINK_RENDER_FAST; + GST_INFO_OBJECT (eglglessink, "Using fast rendering path"); + return; +#endif + +MISSING_EXTS: + GST_WARNING_OBJECT (eglglessink, + "Extensions missing. Can't use fast rendering path"); +SLOW_PATH_SELECTED: + eglglessink->rendering_path = GST_EGLGLESSINK_RENDER_SLOW; + GST_INFO_OBJECT (eglglessink, "Using slow rendering path"); + return; +} + +static gboolean +gst_eglglessink_init_egl_surface (GstEglGlesSink * eglglessink) +{ + GLint test; + GLuint verthandle, fraghandle, prog; + + GST_DEBUG_OBJECT (eglglessink, "Enter EGL surface setup"); + + g_mutex_lock (eglglessink->flow_lock); + + eglglessink->surface = eglCreateWindowSurface (eglglessink->display, + eglglessink->config, eglglessink->window, NULL); + + if (eglglessink->surface == EGL_NO_SURFACE) { + GST_ERROR_OBJECT (eglglessink, "Can't create surface, eglCreateSurface"); + goto HANDLE_EGL_ERROR_LOCKED; + } + + if (!eglMakeCurrent (eglglessink->display, eglglessink->surface, + eglglessink->surface, eglglessink->context)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't bind surface/context, " + "eglMakeCurrent"); + goto HANDLE_EGL_ERROR_LOCKED; + } + + /* We have a surface! */ + eglglessink->have_surface = TRUE; + g_mutex_unlock (eglglessink->flow_lock); + + /* Init vertex and fragment progs. + * XXX: Need to be runtime conditional or ifdefed + */ + + verthandle = glCreateShader (GL_VERTEX_SHADER); + GST_DEBUG_OBJECT (eglglessink, "sending %s to handle %d", vert_prog, + verthandle); + glShaderSource (verthandle, 1, &vert_prog, NULL); + if (got_gl_error ("glShaderSource vertex")) + goto HANDLE_ERROR; + + glCompileShader (verthandle); + if (got_gl_error ("glCompileShader vertex")) + goto HANDLE_ERROR; + + glGetShaderiv (verthandle, GL_COMPILE_STATUS, &test); + if (test != GL_FALSE) + GST_DEBUG_OBJECT (eglglessink, "Successfully compiled vertex program"); + + fraghandle = glCreateShader (GL_FRAGMENT_SHADER); + glShaderSource (fraghandle, 1, &frag_prog, NULL); + if (got_gl_error ("glShaderSource fragment")) + goto HANDLE_ERROR; + + glCompileShader (fraghandle); + if (got_gl_error ("glCompileShader fragment")) + goto HANDLE_ERROR; + + glGetShaderiv (fraghandle, GL_COMPILE_STATUS, &test); + if (test != GL_FALSE) + GST_DEBUG_OBJECT (eglglessink, "Successfully compiled fragment program"); + + prog = glCreateProgram (); + if (got_gl_error ("glCreateProgram")) + goto HANDLE_ERROR; + glAttachShader (prog, verthandle); + if (got_gl_error ("glAttachShader vertices")) + goto HANDLE_ERROR; + glAttachShader (prog, fraghandle); + if (got_gl_error ("glAttachShader fragments")) + goto HANDLE_ERROR; + glLinkProgram (prog); + glGetProgramiv (prog, GL_LINK_STATUS, &test); + if (test != GL_FALSE) + GST_DEBUG_OBJECT (eglglessink, "GLES: Successfully linked program"); + + glUseProgram (prog); + if (got_gl_error ("glUseProgram")) + goto HANDLE_ERROR; + + /* Generate and bind texture */ + if (!eglglessink->have_texture) { + GST_INFO_OBJECT (eglglessink, "Doing initial texture setup"); + + g_mutex_lock (eglglessink->flow_lock); + + glGenTextures (1, &eglglessink->texture[0]); + if (got_gl_error ("glGenTextures")) + goto HANDLE_ERROR_LOCKED; + + glBindTexture (GL_TEXTURE_2D, eglglessink->texture[0]); + if (got_gl_error ("glBindTexture")) + goto HANDLE_ERROR_LOCKED; + + eglglessink->have_texture = TRUE; + g_mutex_unlock (eglglessink->flow_lock); + } + + return TRUE; + + /* Errors */ +HANDLE_EGL_ERROR_LOCKED: + GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ()); +HANDLE_ERROR_LOCKED: + g_mutex_unlock (eglglessink->flow_lock); +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Couldn't setup EGL surface"); + return FALSE; +} + +static gboolean +gst_eglglessink_init_egl_display (GstEglGlesSink * eglglessink) +{ + GLint egl_configs; + EGLint egl_major, egl_minor; + + EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + GST_DEBUG_OBJECT (eglglessink, "Enter EGL initial configuration"); + + eglglessink->display = eglGetDisplay (EGL_DEFAULT_DISPLAY); + if (eglglessink->display == EGL_NO_DISPLAY) { + GST_ERROR_OBJECT (eglglessink, "Could not get EGL display connection"); + goto HANDLE_ERROR; /* No EGL error is set by eglGetDisplay() */ + } + + if (eglInitialize (eglglessink->display, &egl_major, &egl_minor) + == EGL_FALSE) { + GST_ERROR_OBJECT (eglglessink, "Could not init EGL display connection"); + goto HANDLE_EGL_ERROR; + } + + /* Check against required EGL version */ + if (egl_major < GST_EGLGLESSINK_EGL_MIN_VERSION) { + GST_ERROR_OBJECT (eglglessink, "EGL v%d\n needed, but you only have v%d.%d", + GST_EGLGLESSINK_EGL_MIN_VERSION, egl_major, egl_minor); + goto HANDLE_ERROR; + } + + GST_INFO_OBJECT (eglglessink, "System reports supported EGL version v%d.%d", + egl_major, egl_minor); + + if (!eglChooseConfig (eglglessink->display, eglglessink_RGB16_config, + &eglglessink->config, 1, &egl_configs)) { + GST_ERROR_OBJECT (eglglessink, "eglChooseConfig failed"); + goto HANDLE_EGL_ERROR; + } + + eglBindAPI (EGL_OPENGL_ES_API); + + eglglessink->context = eglCreateContext (eglglessink->display, + eglglessink->config, EGL_NO_CONTEXT, con_attribs); + + if (eglglessink->context == EGL_NO_CONTEXT) { + GST_ERROR_OBJECT (eglglessink, "Error getting context, eglCreateContext"); + goto HANDLE_EGL_ERROR; + } + + GST_DEBUG_OBJECT (eglglessink, "EGL Context: %x", eglglessink->context); + + return TRUE; + + /* Errors */ +HANDLE_EGL_ERROR: + GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ()); +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Couldn't setup window/surface from handle"); + return FALSE; +} + +/* XXX: Never actually tested */ +static void +gst_eglglessink_set_window_handle (GstXOverlay * overlay, guintptr id) +{ + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay); + + g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink)); + GST_DEBUG_OBJECT (eglglessink, "We got a window handle!"); + + if (!id) { + /* We are being requested to create our own window. + * 0x0 fires default size creation + */ + GST_WARNING_OBJECT (eglglessink, "OH NOES they want a new window"); + g_mutex_lock (eglglessink->flow_lock); + eglglessink->window = gst_eglglessink_create_window (eglglessink, 0, 0); + if (!eglglessink->window) { + GST_ERROR_OBJECT (eglglessink, "Got a NULL window"); + goto HANDLE_ERROR_LOCKED; + } + } else if (eglglessink->window == id) { /* Already used window */ + GST_WARNING_OBJECT (eglglessink, + "We've got the same %x window handle again", id); + GST_INFO_OBJECT (eglglessink, "Skipping surface setup"); + goto HANDLE_ERROR; + } else { + g_mutex_lock (eglglessink->flow_lock); + eglglessink->window = (EGLNativeWindowType) id; + } + + /* OK, we have a new window */ + eglglessink->have_window = TRUE; + g_mutex_unlock (eglglessink->flow_lock); + + if (!gst_eglglessink_init_egl_surface (eglglessink)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface!"); + goto HANDLE_ERROR; + } + + /* Init extensions */ + gst_eglglessink_init_egl_exts (eglglessink); + + return; + + /* Errors */ +HANDLE_ERROR_LOCKED: + g_mutex_unlock (eglglessink->flow_lock); +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Couldn't setup window/surface from handle"); + return; +} + +/* Rendering and display */ +static void +gst_eglglessink_render_and_display (GstEglGlesSink * eglglessink, + GstBuffer * buf) +{ + gint w, h; + + if (!buf) { + GST_ERROR_OBJECT (eglglessink, "Null buffer, no past queue implemented"); + goto HANDLE_ERROR; + } + + w = GST_VIDEO_SINK_WIDTH (eglglessink); + h = GST_VIDEO_SINK_HEIGHT (eglglessink); + GST_DEBUG_OBJECT (eglglessink, + "Got good buffer %x. Sink geometry is %dx%d size %d", buf, w, h, + GST_BUFFER_SIZE (buf)); + + /* Make sure we stay on our context to avoid threading nightmares */ + if (!eglMakeCurrent (eglglessink->display, eglglessink->surface, + eglglessink->surface, eglglessink->context)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't bind surface/context, " + "eglMakeCurrent"); + goto HANDLE_ERROR; + } + + /* XXX: This should actually happen each time + * frame/window dimension changes. + * Also might want to find a way to pass buffer's + * width and height values when non power of two + * and no npot extension available. + */ + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, GST_BUFFER_DATA (buf)); + if (got_gl_error ("glTexImage2D")) + goto HANDLE_ERROR; + + /* resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (got_gl_error ("glTexParameteri")) + goto HANDLE_ERROR; + + /* XXX: VBO stuff this actually makes more sense on the setcaps stub? + * The way it is right now makes this happen only for the first buffer + * though so I guess it should work */ + g_mutex_lock (eglglessink->flow_lock); + if (!eglglessink->have_vbo) { + GST_DEBUG_OBJECT (eglglessink, "Doing initial VBO setup"); + + eglglessink->coordarray[0].x = -1; + eglglessink->coordarray[0].y = 1; + eglglessink->coordarray[0].z = 0; + + eglglessink->coordarray[1].x = 1; + eglglessink->coordarray[1].y = 1; + eglglessink->coordarray[1].z = 0; + + eglglessink->coordarray[2].x = 1; + eglglessink->coordarray[2].y = -1; + eglglessink->coordarray[2].z = 0; + + eglglessink->coordarray[3].x = -1; + eglglessink->coordarray[3].y = -1; + eglglessink->coordarray[3].z = 0; + + eglglessink->indexarray[0] = 1; + eglglessink->indexarray[1] = 2; + eglglessink->indexarray[2] = 0; + eglglessink->indexarray[3] = 3; + + glGenBuffers (1, &eglglessink->vdata); + glGenBuffers (1, &eglglessink->idata); + if (got_gl_error ("glGenBuffers")) + goto HANDLE_ERROR_LOCKED; + + glBindBuffer (GL_ARRAY_BUFFER, eglglessink->vdata); + if (got_gl_error ("glBindBuffer vdata")) + goto HANDLE_ERROR_LOCKED; + + glBufferData (GL_ARRAY_BUFFER, sizeof (eglglessink->coordarray), + eglglessink->coordarray, GL_STATIC_DRAW); + if (got_gl_error ("glBufferData vdata")) + goto HANDLE_ERROR_LOCKED; + + glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, 0); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR_LOCKED; + glEnableVertexAttribArray (0); + + glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, eglglessink->idata); + if (got_gl_error ("glBindBuffer idata")) + goto HANDLE_ERROR_LOCKED; + + glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (eglglessink->indexarray), + eglglessink->indexarray, GL_STATIC_DRAW); + if (got_gl_error ("glBufferData idata")) + goto HANDLE_ERROR_LOCKED; + + glViewport (0, 0, w, h); + + eglglessink->have_vbo = TRUE; + } + g_mutex_unlock (eglglessink->flow_lock); + + glClearColor (1.0, 0.0, 0.0, 0.0); + glClear (GL_COLOR_BUFFER_BIT); + glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); + if (got_gl_error ("glDrawElements")) + goto HANDLE_ERROR; + + eglSwapBuffers (eglglessink->display, eglglessink->surface); + + return; + +/* + EGLImageKHR img = EGL_NO_IMAGE_KHR; + EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, + EGL_FALSE, EGL_NONE, EGL_NONE + }; + + if (!buf) { + GST_ERROR_OBJECT (eglglessink, "Null buffer, no past queue implemented"); + goto HANDLE_ERROR; + } + + img = my_eglCreateImageKHR (eglglessink->display, EGL_NO_CONTEXT, + EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer) GST_BUFFER_DATA (buf), attrs); + + if (img == EGL_NO_IMAGE_KHR) { + GST_ERROR_OBJECT (eglglessink, "my_eglCreateImageKHR failed"); + goto HANDLE_EGL_ERROR; + } + + my_glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, img); + +HANDLE_EGL_ERROR: + GST_ERROR_OBJECT (eglglessink, "EGL call returned error %x", eglGetError ()); +*/ +HANDLE_ERROR_LOCKED: + g_mutex_unlock (eglglessink->flow_lock); +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame"); +} + +static GstFlowReturn +gst_eglglessink_show_frame (GstVideoSink * vsink, GstBuffer * buf) +{ + GstEglGlesSink *eglglessink; + + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + + eglglessink = GST_EGLGLESSINK (vsink); + GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf); + + if (!eglglessink->have_window) { + GST_ERROR_OBJECT (eglglessink, "I don't have a window to render to"); + return GST_FLOW_ERROR; + } + + if (!eglglessink->have_surface) { + GST_ERROR_OBJECT (eglglessink, "I don't have a surface to render to"); + return GST_FLOW_ERROR; + } +#ifndef EGL_ANDROID_image_native_buffer + GST_WARNING_OBJECT (eglglessink, "EGL_ANDROID_image_native_buffer not " + "available"); +#endif + + gst_eglglessink_render_and_display (eglglessink, buf); + + return GST_FLOW_OK; +} + +static gboolean +gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps) +{ + GstEglGlesSink *eglglessink; + gboolean ret = TRUE; + gint width, height; + + eglglessink = GST_EGLGLESSINK (bsink); + + GST_DEBUG_OBJECT (eglglessink, + "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %" + GST_PTR_FORMAT, eglglessink->current_caps, caps); + + if (!(ret = gst_video_format_parse_caps (caps, &eglglessink->format, &width, + &height))) { + GST_ERROR_OBJECT (eglglessink, "Got weird and/or incomplete caps"); + goto HANDLE_ERROR; + } + + if (eglglessink->format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT (eglglessink, "Got unknown video format caps"); + goto HANDLE_ERROR; + } + + if (gst_eglglessink_get_compat_format_from_caps (eglglessink, caps) == + GST_EGLGLESSINK_IMAGE_NOFMT) { + GST_ERROR_OBJECT (eglglessink, "Unsupported format"); + goto HANDLE_ERROR; + } + + /* XXX: Renegotiation not implemented yet */ + if (eglglessink->current_caps) { + GST_ERROR_OBJECT (eglglessink, "Caps already set. Won't do it again"); + if (gst_caps_is_always_compatible (caps, eglglessink->current_caps)) { + GST_INFO_OBJECT (eglglessink, "Caps are compatible anyway"); + goto SUCCEED; + } else { + GST_INFO_OBJECT (eglglessink, + "Caps %" GST_PTR_FORMAT "Not always compatible with current-caps %" + GST_PTR_FORMAT, caps, eglglessink->current_caps); + GST_WARNING_OBJECT (eglglessink, "Renegotiation not implemented"); + goto HANDLE_ERROR; + } + } + + /* OK, got caps and had none. Ask application to give us a window */ + if (!eglglessink->have_window) { + gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (eglglessink)); + } + + g_mutex_lock (eglglessink->flow_lock); + GST_VIDEO_SINK_WIDTH (eglglessink) = width; + GST_VIDEO_SINK_HEIGHT (eglglessink) = height; + + if (!eglglessink->have_window) { + /* Window creation for no x11/mesa hasn't been implemented yet */ + GST_INFO_OBJECT (eglglessink, + "No window. Will attempt internal window creation"); + if (!(eglglessink->window = gst_eglglessink_create_window (eglglessink, + width, height))) { + GST_ERROR_OBJECT (eglglessink, "Internal window creation failed!"); + goto HANDLE_ERROR_LOCKED; + } + } + + eglglessink->have_window = TRUE; + eglglessink->current_caps = gst_caps_ref (caps); + g_mutex_unlock (eglglessink->flow_lock); + + if (!gst_eglglessink_init_egl_surface (eglglessink)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface from window"); + goto HANDLE_ERROR; + } + + gst_eglglessink_init_egl_exts (eglglessink); + +SUCCEED: + GST_INFO_OBJECT (eglglessink, "Setcaps succeed"); + return TRUE; + +/* Errors */ +HANDLE_ERROR_LOCKED: + g_mutex_unlock (eglglessink->flow_lock); +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Setcaps failed"); + return FALSE; +} + +static void +gst_eglglessink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstEglGlesSink *eglglessink; + + g_return_if_fail (GST_IS_EGLGLESSINK (object)); + + eglglessink = GST_EGLGLESSINK (object); + + switch (prop_id) { + case PROP_SILENT: + eglglessink->silent = g_value_get_boolean (value); + break; + case PROP_CAN_CREATE_WINDOW: + eglglessink->can_create_window = g_value_get_boolean (value); + break; + case PROP_DEFAULT_HEIGHT: + eglglessink->window_default_height = g_value_get_int (value); + break; + case PROP_DEFAULT_WIDTH: + eglglessink->window_default_width = g_value_get_int (value); + break; + case PROP_FORCE_RENDERING_SLOW: + eglglessink->force_rendering_slow = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_eglglessink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstEglGlesSink *eglglessink; + + g_return_if_fail (GST_IS_EGLGLESSINK (object)); + + eglglessink = GST_EGLGLESSINK (object); + + switch (prop_id) { + case PROP_SILENT: + g_value_set_boolean (value, eglglessink->silent); + break; + case PROP_CAN_CREATE_WINDOW: + g_value_set_boolean (value, eglglessink->can_create_window); + break; + case PROP_DEFAULT_HEIGHT: + g_value_set_int (value, eglglessink->window_default_height); + break; + case PROP_DEFAULT_WIDTH: + g_value_set_int (value, eglglessink->window_default_width); + break; + case PROP_FORCE_RENDERING_SLOW: + g_value_set_boolean (value, eglglessink->force_rendering_slow); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_eglglessink_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_set_details_simple (element_class, + "EGL/GLES vout Sink", + "Sink/Video", + "An EGL/GLES Video Output Sink Implementing the XOverlay interface", + "Reynaldo H. Verdejo Pinochet "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_eglglessink_sink_template_factory)); +} + +/* initialize the eglglessink's class */ +static void +gst_eglglessink_class_init (GstEglGlesSinkClass * klass) +{ + GObjectClass *gobject_class; + GstBaseSinkClass *gstbasesink_class; + GstVideoSinkClass *gstvideosink_class; + + gobject_class = (GObjectClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstvideosink_class = (GstVideoSinkClass *) klass; + + gobject_class->set_property = gst_eglglessink_set_property; + gobject_class->get_property = gst_eglglessink_get_property; + + gstbasesink_class->start = gst_eglglessink_start; + gstbasesink_class->stop = gst_eglglessink_stop; + gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_setcaps); + gstbasesink_class->buffer_alloc = GST_DEBUG_FUNCPTR + (gst_eglglessink_buffer_alloc); + + gstvideosink_class->show_frame = + GST_DEBUG_FUNCPTR (gst_eglglessink_show_frame); + + g_object_class_install_property (gobject_class, PROP_SILENT, + g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?", + FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_CAN_CREATE_WINDOW, + g_param_spec_boolean ("can_create_window", "CanCreateWindow", + "Attempt to create a window?", TRUE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_FORCE_RENDERING_SLOW, + g_param_spec_boolean ("force_rendering_slow", "ForceRenderingSlow", + "Force slow rendering path?", FALSE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_DEFAULT_WIDTH, + g_param_spec_int ("window_default_width", "DefaultWidth", + "Default width for self created windows", 0, + EGLGLESSINK_MAX_FRAME_WIDTH, EGLGLESSINK_MAX_FRAME_WIDTH, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_DEFAULT_HEIGHT, + g_param_spec_int ("window_default_height", "CanCreateWindow", + "Default height for self created windows", 0, + EGLGLESSINK_MAX_FRAME_HEIGHT, EGLGLESSINK_MAX_FRAME_HEIGHT, + G_PARAM_READWRITE)); +} + + +static void +gst_eglglessink_init (GstEglGlesSink * eglglessink, + GstEglGlesSinkClass * gclass) +{ + /* Init defaults */ + eglglessink->have_window = FALSE; + eglglessink->have_surface = FALSE; + eglglessink->have_vbo = FALSE; + eglglessink->have_texture = FALSE; + eglglessink->running = FALSE; /* XXX: unused */ + eglglessink->can_create_window = TRUE; + eglglessink->force_rendering_slow = FALSE; +} + +/* Interface initializations. Used here for initializing the XOverlay + * Interface. + */ +static void +gst_eglglessink_init_interfaces (GType type) +{ + static const GInterfaceInfo implements_info = { + (GInterfaceInitFunc) gst_eglglessink_implements_init, NULL, NULL + }; + + static const GInterfaceInfo xoverlay_info = { + (GInterfaceInitFunc) gst_eglglessink_xoverlay_init, NULL, NULL + }; + + g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, + &implements_info); + g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info); + +} + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +static gboolean +eglglessink_plugin_init (GstPlugin * plugin) +{ + /* debug category for fltering log messages */ + GST_DEBUG_CATEGORY_INIT (gst_eglglessink_debug, "eglglessink", + 0, "Simple EGL/GLES Sink"); + + return gst_element_register (plugin, "eglglessink", GST_RANK_NONE, + GST_TYPE_EGLGLESSINK); +} + +/* PACKAGE: this is usually set by autotools depending on some _INIT macro + * in configure.ac and then written into and defined in config.h, but we can + * just set it ourselves here in case someone doesn't use autotools to + * compile this code. GST_PLUGIN_DEFINE needs PACKAGE to be defined. + */ +#ifndef PACKAGE +#define PACKAGE "EGL/GLES Sink" +#endif + +#ifndef VERSION +#define VERSION "0.911" +#endif + +/* gstreamer looks for this structure to register eglglessinks */ +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "eglglessink", + "EGL/GLES sink", + eglglessink_plugin_init, + VERSION, "LGPL", "GStreamer", "http://gstreamer.net/") diff --git a/ext/eglgles/gstvidroidsink.h b/ext/eglgles/gstvidroidsink.h new file mode 100644 index 0000000..0f28411 --- /dev/null +++ b/ext/eglgles/gstvidroidsink.h @@ -0,0 +1,167 @@ +/* + * GStreamer EGL/GLES Sink + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_EGLGLESSINK_H__ +#define __GST_EGLGLESSINK_H__ + +#include + +#include +#include +#include +#include + +G_BEGIN_DECLS +#define GST_TYPE_EGLGLESSINK \ + (gst_eglglessink_get_type()) +#define GST_EGLGLESSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_EGLGLESSINK,GstEglGlesSink)) +#define GST_EGLGLESSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_EGLGLESSINK,GstEglGlesSinkClass)) +#define GST_IS_EGLGLESSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_EGLGLESSINK)) +#define GST_IS_EGLGLESSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_EGLGLESSINK)) +/* XXX: Harcoded format. Should be runtime built latter on. */ +#define GST_EGLGLESSINK_IMAGE_RGB565 1 +#define GST_EGLGLESSINK_IMAGE_NOFMT -1 + +#define GST_EGLGLESSINK_EGL_MIN_VERSION 1 + +typedef struct _GstEglGlesBuffer GstEglGlesBuffer; +typedef struct _GstEglGlesBufferClass GstEglGlesBufferClass; + +typedef struct _GstEglGlesSink GstEglGlesSink; +typedef struct _GstEglGlesSinkClass GstEglGlesSinkClass; + +typedef struct _GstEglGlesImageFmt GstEglGlesImageFmt; + +/* Should be extended when new rendering methods + * get implemented. + */ +typedef enum { + GST_EGLGLESSINK_RENDER_SLOW, + GST_EGLGLESSINK_RENDER_FAST +} GstVidroidSinkRenderingPath; + +typedef struct _coord +{ + float x; + float y; + float z; +} coord; + +struct _GstEglGlesImageFmt +{ + gint fmt; + GstCaps *caps; +}; + +/* XXX: Maybe use GstVideoRectangle for the image data? */ +struct _GstEglGlesBuffer +{ + GstBuffer buffer; + GstEglGlesSink *eglglessink; + + EGLint *image; + gint format; + + gint width, height; + size_t size; +}; + +struct _GstEglGlesSink +{ + GstVideoSink videosink; + GstVideoFormat format; + GstCaps *current_caps; + GstPad *sink; + + /* XXX: The supported format list should likely be part + * of a local EGL/GLES context and built at runtime from + * the platform supported fmts. Right now we just add one + * format/caps at init. + */ + GList *supported_fmts; + + GMutex *flow_lock; + + EGLConfig config; + EGLContext context; + EGLDisplay display; + EGLNativeWindowType window; + EGLSurface surface; + + GLuint texture[1]; + + gboolean have_window; + gboolean have_surface;; + gboolean have_vbo; + gboolean have_texture; + gboolean running; + + GstVidroidSinkRenderingPath rendering_path; + + /* shader vars */ + coord coordarray[4]; + unsigned short indexarray[4]; + unsigned int vdata, idata; + + /* props */ + gboolean silent; + gboolean can_create_window; + gboolean force_rendering_slow; + gint window_default_width; + gint window_default_height; +}; + +struct _GstEglGlesSinkClass +{ + GstVideoSinkClass parent_class; +}; + +GType gst_eglglessink_get_type (void); + +G_END_DECLS +#endif /* __GST_EGLGLESSINK_H__ */ diff --git a/ext/eglgles/video_platform_wrapper.c b/ext/eglgles/video_platform_wrapper.c new file mode 100644 index 0000000..9ec582c --- /dev/null +++ b/ext/eglgles/video_platform_wrapper.c @@ -0,0 +1,142 @@ +/* + * GStreamer Android Video Platform Wrapper + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define EGL_EGLEXT_PROTOTYPES + +#include +#include +#include + +#include +#include "video_platform_wrapper.h" + +#ifndef __BIONIC__ +#include +#endif + +GST_DEBUG_CATEGORY_STATIC (eglgles_platform_wrapper); +#define GST_CAT_DEFAULT eglgles_platform_wrapper + +/* XXX: Likely to be removed */ +gboolean +platform_wrapper_init (void) +{ + GST_DEBUG_CATEGORY_INIT (eglgles_platform_wrapper, + "EglGles Platform Wrapper", 0, + "Platform dependent native-window utility routines for EglGles"); + return TRUE; +} + +#ifndef __BIONIC__ +EGLNativeWindowType +platform_create_native_window (gint width, gint height) +{ + Display *d; + Window w; + //XEvent e; + int s; + + d = XOpenDisplay (NULL); + if (d == NULL) { + GST_CAT_ERROR (GST_CAT_DEFAULT, "Can't open X11 display"); + return (EGLNativeWindowType) 0; + } + + s = DefaultScreen (d); + w = XCreateSimpleWindow (d, RootWindow (d, s), 10, 10, width, height, 1, + BlackPixel (d, s), WhitePixel (d, s)); + XStoreName (d, w, "eglglessink"); + XMapWindow (d, w); + XFlush (d); + return (EGLNativeWindowType) w; +} + +gboolean +platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType window) +{ + /* XXX: Should proly catch BadWindow */ + XDestroyWindow (display, window); + return TRUE; +} + +/* XXX: Missing implementation */ +EGLint * +platform_crate_native_image_buffer (EGLNativeWindowType win, EGLConfig config, + EGLNativeDisplayType display, const EGLint * egl_attribs) +{ + return NULL; +} + +#else +/* Android does not support the creation of an egl window surface + * from native code. Hence, we just return NULL here for the time + * being. Function is left for reference as implementing it should + * help us suport other EGL platforms. + */ +EGLNativeWindowType +platform_create_native_window (gint width, gint height) +{ + /* XXX: There was one example on AOSP that was using something + * along the lines of window = android_createDisplaySurface(); + * but wasn't working properly. + */ + + GST_CAT_ERROR (GST_CAT_DEFAULT, "Android: Can't create native window"); + return (EGLNativeWindowType) 0; +} + +gboolean +platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType window) +{ + GST_CAT_ERROR (GST_CAT_DEFAULT, "Android: Can't destroy native window"); + return TRUE; +} + +#endif diff --git a/ext/eglgles/video_platform_wrapper.h b/ext/eglgles/video_platform_wrapper.h new file mode 100644 index 0000000..db27c57 --- /dev/null +++ b/ext/eglgles/video_platform_wrapper.h @@ -0,0 +1,64 @@ +/* + * GStreamer Android Video Platform Wrapper + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * General idea is to have all platform dependent code here for easy + * tweaking and isolation from the main routines + */ + +#ifndef __GST_ANDROID_VIDEO_PLATFORM_WRAPPER__ +#define __GST_ANDROID_VIDEO_PLATFORM_WRAPPER__ + +#include +#include + +gboolean platform_wrapper_init (void); +EGLNativeWindowType platform_create_native_window (gint width, gint height); +gboolean platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType w); +EGLint *platform_crate_native_image_buffer (EGLNativeWindowType win, + EGLConfig config, EGLNativeDisplayType display, const EGLint * egl_attribs); + + +#endif