testegl: port to gstgl API

- append a glfilter just before fakesink
  So that we get gltexture or eglimages

- propagate our EGLDisplay to the pipeline
  see GST_QUERY_CONTEXT

- share our EGLContext with the iternal gl context
  of the pipeline, see GST_QUERY_ALLOCATION

- use GstVideoGLTextureUploadMeta to upload
  the incoming gltexture or eglimage to our gl texture

TODO: convert from GLESv1 to GLESv2

See https://bugzilla.gnome.org/show_bug.cgi?id=728940
This commit is contained in:
Julien Isorce 2014-04-25 13:21:59 +01:00
parent 4593f434a0
commit f1c76ef921
2 changed files with 116 additions and 609 deletions

View file

@ -13,11 +13,11 @@ testegl_LDADD = \
-lgstvideo-@GST_API_VERSION@ \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
$(GST_EGL_LIBS)
$(GST_GL_LIBS)
testegl_CFLAGS = \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(GST_EGL_CFLAGS)
$(GST_GL_CFLAGS)

View file

@ -5,6 +5,7 @@ Copyright (C) 2013, Fluendo S.A.
@author: Josep Torra <josep@fluendo.com>
Copyright (C) 2013, Video Experts Group LLC.
@author: Ilya Smelykh <ilya@videoexpertsgroup.com>
Copyright (C) 2014 Julien Isorce <julien.isorce@collabora.co.uk>
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -47,7 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <gst/gst.h>
#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL) && defined (__GNUC__)
#if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
#ifndef __VCCOREVER__
#define __VCCOREVER__ 0x04000000
#endif
@ -56,29 +57,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma GCC optimize ("gnu89-inline")
#endif
#include "bcm_host.h"
#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL) && defined (__GNUC__)
#pragma GCC reset_options
#pragma GCC diagnostic pop
#endif
#include <GLES/gl.h>
#include <GLES/glext.h>
#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL) && defined (__GNUC__)
#ifndef __VCCOREVER__
#define __VCCOREVER__ 0x04000000
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"
#pragma GCC optimize ("gnu89-inline")
#include <gst/gl/gl.h>
#include <gst/gl/egl/gstgldisplay_egl.h>
#if defined (USE_OMX_TARGET_RPI)
#include <bcm_host.h>
#endif
#define EGL_EGLEXT_PROTOTYPES
#include <gst/egl/egl.h>
#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL) && defined (__GNUC__)
#if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__)
#pragma GCC reset_options
#pragma GCC diagnostic pop
#endif
@ -93,7 +82,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define TRACE_VC_MEMORY_ENABLED 0
#if TRACE_VC_MEMORY_ENABLED
#if defined (USE_OMX_TARGET_RPI) && TRACE_VC_MEMORY_ENABLED
#define TRACE_VC_MEMORY(str) \
fprintf (stderr, "\n\n" str "\n"); \
system ("vcdbg reloc >&2")
@ -135,8 +124,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
typedef struct
{
#if defined (USE_OMX_TARGET_RPI)
DISPMANX_DISPLAY_HANDLE_T dispman_display;
DISPMANX_ELEMENT_HANDLE_T dispman_element;
#endif
uint32_t screen_width;
uint32_t screen_height;
@ -165,17 +156,16 @@ typedef struct
/* GStreamer related resources */
GstElement *pipeline;
GstElement *vsink;
GstEGLDisplay *gst_display;
GstGLDisplayEGL *gst_display;
/* Interthread comunication */
GAsyncQueue *queue;
GMutex *queue_lock;
GCond *cond;
GMutex queue_lock;
GCond cond;
gboolean flushing;
GstMiniObject *popped_obj;
GstBuffer *current_buffer;
GstBufferPool *pool;
/* GLib mainloop */
GMainLoop *main_loop;
GstBuffer *last_buffer;
@ -190,37 +180,6 @@ typedef struct
guint64 dropped;
} APP_STATE_T;
typedef struct
{
GThread *thread;
EGLImageKHR image;
GLuint texture;
APP_STATE_T *state;
} GstEGLGLESImageData;
/* EGLImage memory, buffer pool, etc */
typedef struct
{
GstVideoBufferPool parent;
APP_STATE_T *state;
GstAllocator *allocator;
GstAllocationParams params;
GstVideoInfo info;
gboolean add_metavideo;
gboolean want_eglimage;
GstEGLDisplay *display;
} GstCustomEGLImageBufferPool;
typedef GstVideoBufferPoolClass GstCustomEGLImageBufferPoolClass;
#define GST_CUSTOM_EGL_IMAGE_BUFFER_POOL(p) ((GstCustomEGLImageBufferPool*)(p))
GType gst_custom_egl_image_buffer_pool_get_type (void);
G_DEFINE_TYPE (GstCustomEGLImageBufferPool, gst_custom_egl_image_buffer_pool,
GST_TYPE_VIDEO_BUFFER_POOL);
static void init_ogl (APP_STATE_T * state);
static void init_model_proj (APP_STATE_T * state);
static void reset_model (APP_STATE_T * state);
@ -230,8 +189,6 @@ static void redraw_scene (APP_STATE_T * state);
static void update_model (APP_STATE_T * state);
static void init_textures (APP_STATE_T * state);
static APP_STATE_T _state, *state = &_state;
static GstBufferPool *gst_custom_egl_image_buffer_pool_new (APP_STATE_T * state,
GstEGLDisplay * display);
static gboolean queue_object (APP_STATE_T * state, GstMiniObject * obj,
gboolean synchronous);
@ -254,371 +211,6 @@ typedef enum
GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
} GstPlayFlags;
static 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 0x%04x", wtf, error);
return TRUE;
}
return FALSE;
}
static gboolean
got_egl_error (const char *wtf)
{
EGLint error;
if ((error = eglGetError ()) != EGL_SUCCESS) {
GST_CAT_DEBUG (GST_CAT_DEFAULT, "EGL ERROR: %s returned 0x%04x", wtf,
error);
return TRUE;
}
return FALSE;
}
static void
image_data_free (GstEGLGLESImageData * data)
{
if (data->thread == g_thread_self ()) {
eglDestroyImageKHR (state->display, data->image);
glDeleteTextures (1, &data->texture);
} else {
GstQuery *query;
GstStructure *s;
s = gst_structure_new ("eglglessink-deallocate-eglimage",
"EGLImage", G_TYPE_POINTER, data->image,
"GLTexture", G_TYPE_POINTER, data->texture, NULL);
query = gst_query_new_custom (GST_QUERY_CUSTOM, s);
queue_object (state, GST_MINI_OBJECT_CAST (query), FALSE);
}
g_slice_free (GstEGLGLESImageData, data);
}
static GstBuffer *
gst_egl_allocate_eglimage (APP_STATE_T * ctx,
GstAllocator * allocator, GstVideoFormat format, gint width, gint height)
{
GstEGLGLESImageData *data = NULL;
GstBuffer *buffer;
GstVideoInfo info;
gint i;
gint stride[3];
gsize offset[3];
GstMemory *mem[3] = { NULL, NULL, NULL };
guint n_mem;
GstMemoryFlags flags = 0;
memset (stride, 0, sizeof (stride));
memset (offset, 0, sizeof (offset));
if (!gst_egl_image_memory_is_mappable ())
flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
/* See https://bugzilla.gnome.org/show_bug.cgi?id=695203 */
flags |= GST_MEMORY_FLAG_NO_SHARE;
gst_video_info_set_format (&info, format, width, height);
GST_DEBUG ("Allocating EGL Image format %s width %d height %d",
gst_video_format_to_string (format), width, height);
switch (format) {
case GST_VIDEO_FORMAT_RGBA:{
gsize size;
mem[0] =
gst_egl_image_allocator_alloc (allocator, ctx->gst_display,
GST_VIDEO_GL_TEXTURE_TYPE_RGBA, GST_VIDEO_INFO_WIDTH (&info),
GST_VIDEO_INFO_HEIGHT (&info), &size);
if (mem[0]) {
stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
n_mem = 1;
GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
} else {
data = g_slice_new0 (GstEGLGLESImageData);
data->thread = g_thread_self ();
data->state = ctx;
stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 4);
size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
glGenTextures (1, &data->texture);
if (got_gl_error ("glGenTextures"))
goto mem_error;
glBindTexture (GL_TEXTURE_2D, data->texture);
if (got_gl_error ("glBindTexture"))
goto mem_error;
/* Set 2D resizing params */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* If these are not set the texture image unit will return
* * (R, G, B, A) = black on glTexImage2D for non-POT width/height
* * frames. For a deeper explanation take a look at the OpenGL ES
* * documentation for glTexParameter */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (got_gl_error ("glTexParameteri"))
goto mem_error;
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
GST_VIDEO_INFO_WIDTH (&info),
GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (got_gl_error ("glTexImage2D"))
goto mem_error;
data->image =
eglCreateImageKHR (gst_egl_display_get (ctx->gst_display),
ctx->context, EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer) (guintptr) data->texture, NULL);
if (got_egl_error ("eglCreateImageKHR"))
goto mem_error;
mem[0] =
gst_egl_image_allocator_wrap (allocator, ctx->gst_display,
data->image, GST_VIDEO_GL_TEXTURE_TYPE_RGBA, flags, size, data,
(GDestroyNotify) image_data_free);
n_mem = 1;
}
}
break;
default:
goto mem_error;
break;
}
buffer = gst_buffer_new ();
gst_buffer_add_video_meta_full (buffer, 0, format, width, height,
GST_VIDEO_INFO_N_PLANES (&info), offset, stride);
/* n_mem could be reused for planar colorspaces, for now its == 1 for RGBA */
for (i = 0; i < n_mem; i++)
gst_buffer_append_memory (buffer, mem[i]);
return buffer;
mem_error:
{
GST_ERROR ("Failed to create EGLImage");
if (data)
image_data_free (data);
if (mem[0])
gst_memory_unref (mem[0]);
return NULL;
}
}
static const gchar **
gst_custom_egl_image_buffer_pool_get_options (GstBufferPool * bpool)
{
static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL
};
return options;
}
static gboolean
gst_custom_egl_image_buffer_pool_set_config (GstBufferPool * bpool,
GstStructure * config)
{
GstCustomEGLImageBufferPool *pool = GST_CUSTOM_EGL_IMAGE_BUFFER_POOL (bpool);
GstCaps *caps;
GstVideoInfo info;
if (pool->allocator)
gst_object_unref (pool->allocator);
pool->allocator = NULL;
if (!GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->set_config (bpool,
config))
return FALSE;
if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
|| !caps)
return FALSE;
if (!gst_video_info_from_caps (&info, caps))
return FALSE;
if (!gst_buffer_pool_config_get_allocator (config, &pool->allocator,
&pool->params))
return FALSE;
if (pool->allocator)
gst_object_ref (pool->allocator);
pool->add_metavideo =
gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
pool->want_eglimage = (pool->allocator
&& g_strcmp0 (pool->allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0);
pool->info = info;
return TRUE;
}
static GstFlowReturn
gst_custom_egl_image_buffer_pool_alloc_buffer (GstBufferPool * bpool,
GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
{
GstCustomEGLImageBufferPool *pool = GST_CUSTOM_EGL_IMAGE_BUFFER_POOL (bpool);
*buffer = NULL;
if (!pool->add_metavideo || !pool->want_eglimage)
return
GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
buffer, params);
if (!pool->allocator)
return GST_FLOW_NOT_NEGOTIATED;
switch (pool->info.finfo->format) {
case GST_VIDEO_FORMAT_RGBA:{
GstFlowReturn ret;
GstQuery *query;
GstStructure *s;
const GValue *v;
s = gst_structure_new ("eglglessink-allocate-eglimage",
"format", GST_TYPE_VIDEO_FORMAT, pool->info.finfo->format,
"width", G_TYPE_INT, pool->info.width,
"height", G_TYPE_INT, pool->info.height, NULL);
query = gst_query_new_custom (GST_QUERY_CUSTOM, s);
ret = queue_object (state, GST_MINI_OBJECT_CAST (query), TRUE);
if (ret != TRUE || !gst_structure_has_field (s, "buffer")) {
GST_WARNING ("Fallback memory allocation");
gst_query_unref (query);
return
GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->alloc_buffer
(bpool, buffer, params);
}
v = gst_structure_get_value (s, "buffer");
*buffer = GST_BUFFER_CAST (g_value_get_pointer (v));
gst_query_unref (query);
if (!*buffer) {
GST_WARNING ("Fallback memory allocation");
return
GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->alloc_buffer
(bpool, buffer, params);
}
return GST_FLOW_OK;
break;
}
default:
return
GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool,
buffer, params);
break;
}
return GST_FLOW_ERROR;
}
static GstFlowReturn
gst_custom_egl_image_buffer_pool_acquire_buffer (GstBufferPool * bpool,
GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
{
GstFlowReturn ret;
GstCustomEGLImageBufferPool *pool;
ret =
GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
buffer, params);
if (ret != GST_FLOW_OK || !*buffer)
return ret;
pool = GST_CUSTOM_EGL_IMAGE_BUFFER_POOL (bpool);
/* XXX: Don't return the memory we just rendered, glEGLImageTargetTexture2DOES()
* keeps the EGLImage unmappable until the next one is uploaded
*/
if (*buffer && *buffer == pool->state->current_buffer) {
GstBuffer *oldbuf = *buffer;
ret =
GST_BUFFER_POOL_CLASS
(gst_custom_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool,
buffer, params);
gst_object_replace ((GstObject **) & oldbuf->pool, (GstObject *) pool);
gst_buffer_unref (oldbuf);
}
return ret;
}
static void
gst_custom_egl_image_buffer_pool_finalize (GObject * object)
{
GstCustomEGLImageBufferPool *pool = GST_CUSTOM_EGL_IMAGE_BUFFER_POOL (object);
if (pool->allocator)
gst_object_unref (pool->allocator);
pool->allocator = NULL;
if (pool->display)
gst_egl_display_unref (pool->display);
pool->display = NULL;
G_OBJECT_CLASS (gst_custom_egl_image_buffer_pool_parent_class)->finalize
(object);
}
static void
gst_custom_egl_image_buffer_pool_class_init (GstCustomEGLImageBufferPoolClass *
klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
gobject_class->finalize = gst_custom_egl_image_buffer_pool_finalize;
gstbufferpool_class->get_options =
gst_custom_egl_image_buffer_pool_get_options;
gstbufferpool_class->set_config = gst_custom_egl_image_buffer_pool_set_config;
gstbufferpool_class->alloc_buffer =
gst_custom_egl_image_buffer_pool_alloc_buffer;
gstbufferpool_class->acquire_buffer =
gst_custom_egl_image_buffer_pool_acquire_buffer;
}
static void
gst_custom_egl_image_buffer_pool_init (GstCustomEGLImageBufferPool * pool)
{
}
static GstBufferPool *
gst_custom_egl_image_buffer_pool_new (APP_STATE_T * state,
GstEGLDisplay * display)
{
GstCustomEGLImageBufferPool *pool;
pool = g_object_new (gst_custom_egl_image_buffer_pool_get_type (), NULL);
pool->display = gst_egl_display_ref (state->gst_display);
pool->state = state;
return (GstBufferPool *) pool;
}
/***********************************************************
* Name: init_ogl
*
@ -636,13 +228,17 @@ init_ogl (APP_STATE_T * state)
int32_t success = 0;
EGLBoolean result;
EGLint num_config;
EGLNativeWindowType window_handle = (EGLNativeWindowType) 0;
#if defined (USE_OMX_TARGET_RPI)
static EGL_DISPMANX_WINDOW_T nativewindow;
DISPMANX_UPDATE_HANDLE_T dispman_update;
VC_RECT_T dst_rect;
VC_RECT_T src_rect;
#endif
//FIXME
static const EGLint attribute_list[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
@ -664,6 +260,7 @@ init_ogl (APP_STATE_T * state)
result = eglInitialize (state->display, NULL, NULL);
assert (EGL_FALSE != result);
#if defined (USE_OMX_TARGET_RPI)
/* get an appropriate EGL frame buffer configuration
* this uses a BRCM extension that gets the closest match, rather
* than standard which returns anything that matches. */
@ -671,12 +268,17 @@ init_ogl (APP_STATE_T * state)
eglSaneChooseConfigBRCM (state->display, attribute_list, &config, 1,
&num_config);
assert (EGL_FALSE != result);
#else
result =
eglChooseConfig (state->display, attribute_list, &config, 1, &num_config);
#endif
/* create an EGL rendering context */
state->context =
eglCreateContext (state->display, config, EGL_NO_CONTEXT, NULL);
assert (state->context != EGL_NO_CONTEXT);
#if defined (USE_OMX_TARGET_RPI)
/* create an EGL window surface */
success = graphics_get_display_size (0 /* LCD */ , &state->screen_width,
&state->screen_height);
@ -706,8 +308,11 @@ init_ogl (APP_STATE_T * state)
nativewindow.height = state->screen_height;
vc_dispmanx_update_submit_sync (dispman_update);
window_handle = &nativewindow;
#endif
state->surface =
eglCreateWindowSurface (state->display, config, &nativewindow, NULL);
eglCreateWindowSurface (state->display, config, window_handle, NULL);
assert (state->surface != EGL_NO_SURFACE);
/* connect the context to the surface */
@ -976,22 +581,20 @@ render_scene (APP_STATE_T * state)
static void
update_image (APP_STATE_T * state, GstBuffer * buffer)
{
GstMemory *mem = NULL;
GstVideoGLTextureUploadMeta *meta = NULL;
if (state->current_buffer) {
gst_buffer_unref (state->current_buffer);
TRACE_VC_MEMORY_ONCE_FOR_ID ("before GstVideoGLTextureUploadMeta", gid0);
if ((meta = gst_buffer_get_video_gl_texture_upload_meta (buffer))) {
if (meta->n_textures == 1) {
guint ids[4] = { state->tex, 0, 0, 0 };
if (!gst_video_gl_texture_upload_meta_upload (meta, ids)) {
GST_WARNING ("failed to upload eglimage to texture");
}
}
}
state->current_buffer = gst_buffer_ref (buffer);
mem = gst_buffer_peek_memory (buffer, 0);
TRACE_VC_MEMORY_ONCE_FOR_ID ("before glEGLImageTargetTexture2DOES", gid0);
glBindTexture (GL_TEXTURE_2D, state->tex);
glEGLImageTargetTexture2DOES (GL_TEXTURE_2D,
gst_egl_image_memory_get_image (mem));
TRACE_VC_MEMORY_ONCE_FOR_ID ("after glEGLImageTargetTexture2DOES", gid1);
TRACE_VC_MEMORY_ONCE_FOR_ID ("after GstVideoGLTextureUploadMeta", gid1);
}
static void
@ -999,8 +602,8 @@ init_intercom (APP_STATE_T * state)
{
state->queue =
g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
state->queue_lock = g_mutex_new ();
state->cond = g_cond_new ();
g_mutex_init (&state->queue_lock);
g_cond_init (&state->cond);
}
static void
@ -1011,22 +614,13 @@ terminate_intercom (APP_STATE_T * state)
g_async_queue_unref (state->queue);
}
if (state->queue_lock) {
g_mutex_free (state->queue_lock);
}
if (state->cond) {
g_cond_free (state->cond);
}
g_mutex_clear (&state->queue_lock);
g_cond_clear (&state->cond);
}
static void
flush_internal (APP_STATE_T * state)
{
if (state->current_buffer) {
gst_buffer_unref (state->current_buffer);
}
state->current_buffer = NULL;
}
static void
@ -1034,18 +628,18 @@ flush_start (APP_STATE_T * state)
{
GstMiniObject *object = NULL;
g_mutex_lock (state->queue_lock);
g_mutex_lock (&state->queue_lock);
state->flushing = TRUE;
g_cond_broadcast (state->cond);
g_mutex_unlock (state->queue_lock);
g_cond_broadcast (&state->cond);
g_mutex_unlock (&state->queue_lock);
while ((object = g_async_queue_try_pop (state->queue))) {
gst_mini_object_unref (object);
}
g_mutex_lock (state->queue_lock);
g_mutex_lock (&state->queue_lock);
flush_internal (state);
state->popped_obj = NULL;
g_mutex_unlock (state->queue_lock);
g_mutex_unlock (&state->queue_lock);
}
static void
@ -1053,14 +647,14 @@ flush_stop (APP_STATE_T * state)
{
GstMiniObject *object = NULL;
g_mutex_lock (state->queue_lock);
g_mutex_lock (&state->queue_lock);
while ((object = GST_MINI_OBJECT_CAST (g_async_queue_try_pop (state->queue)))) {
gst_mini_object_unref (object);
}
flush_internal (state);
state->popped_obj = NULL;
state->flushing = FALSE;
g_mutex_unlock (state->queue_lock);
g_mutex_unlock (&state->queue_lock);
}
static void
@ -1118,9 +712,9 @@ handle_queued_objects (APP_STATE_T * state)
{
GstMiniObject *object = NULL;
g_mutex_lock (state->queue_lock);
g_mutex_lock (&state->queue_lock);
if (state->flushing) {
g_cond_broadcast (state->cond);
g_cond_broadcast (&state->cond);
goto beach;
} else if (g_async_queue_length (state->queue) == 0) {
goto beach;
@ -1139,38 +733,8 @@ handle_queued_objects (APP_STATE_T * state)
GstQuery *query = GST_QUERY_CAST (object);
GstStructure *s = (GstStructure *) gst_query_get_structure (query);
if (gst_structure_has_name (s, "eglglessink-allocate-eglimage")) {
GstBuffer *buffer;
GstVideoFormat format;
gint width, height;
GValue v = { 0, };
if (!gst_structure_get_enum (s, "format", GST_TYPE_VIDEO_FORMAT,
(gint *) & format)
|| !gst_structure_get_int (s, "width", &width)
|| !gst_structure_get_int (s, "height", &height)) {
if (gst_structure_has_name (s, "not-used")) {
g_assert_not_reached ();
}
buffer =
gst_egl_allocate_eglimage (state,
GST_CUSTOM_EGL_IMAGE_BUFFER_POOL (state->pool)->allocator, format,
width, height);
g_value_init (&v, G_TYPE_POINTER);
g_value_set_pointer (&v, buffer);
gst_structure_set_value (s, "buffer", &v);
g_value_unset (&v);
} else if (gst_structure_has_name (s, "eglglessink-deallocate-eglimage")) {
gpointer _image, _texture;
EGLImageKHR image;
GLuint texture;
gst_structure_get (s, "EGLImage", G_TYPE_POINTER, &_image, "GLTexture",
G_TYPE_POINTER, &_texture, NULL);
image = (EGLImageKHR) _image;
texture = (GLuint) _texture;
eglDestroyImageKHR (state->display, image);
glDeleteTextures (1, &texture);
} else {
g_assert_not_reached ();
}
@ -1193,11 +757,11 @@ handle_queued_objects (APP_STATE_T * state)
if (object) {
state->popped_obj = object;
g_cond_broadcast (state->cond);
g_cond_broadcast (&state->cond);
}
beach:
g_mutex_unlock (state->queue_lock);
g_mutex_unlock (&state->queue_lock);
return FALSE;
}
@ -1207,7 +771,7 @@ queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous)
{
gboolean res = TRUE;
g_mutex_lock (state->queue_lock);
g_mutex_lock (&state->queue_lock);
if (state->flushing) {
gst_mini_object_unref (obj);
res = FALSE;
@ -1219,12 +783,12 @@ queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous)
if (synchronous) {
/* Waiting for object to be handled */
do {
g_cond_wait (state->cond, state->queue_lock);
g_cond_wait (&state->cond, &state->queue_lock);
} while (!state->flushing && state->popped_obj != obj);
}
beach:
g_mutex_unlock (state->queue_lock);
g_mutex_unlock (&state->queue_lock);
return res;
}
@ -1273,117 +837,38 @@ query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
APP_STATE_T *state = (APP_STATE_T *) user_data;
GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
GstStructure *external_gl_context_desc = NULL;
gchar *platform = NULL;
gchar *gl_apis = NULL;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_ALLOCATION:
{
GstBufferPool *pool;
GstStructure *config;
GstCaps *caps;
GstVideoInfo info;
gboolean need_pool;
guint size;
GstAllocator *allocator;
GstAllocationParams params;
platform = gst_gl_platform_to_string (GST_GL_PLATFORM_EGL);
gl_apis = gst_gl_api_to_string (GST_GL_API_GLES1);
gst_allocation_params_init (&params);
gst_query_parse_allocation (query, &caps, &need_pool);
if (!caps) {
GST_ERROR ("allocation query without caps");
return GST_PAD_PROBE_OK;
}
if (!gst_video_info_from_caps (&info, caps)) {
GST_ERROR ("allocation query with invalid caps");
return GST_PAD_PROBE_OK;
}
g_mutex_lock (state->queue_lock);
pool = state->pool ? gst_object_ref (state->pool) : NULL;
g_mutex_unlock (state->queue_lock);
if (pool) {
GstCaps *pcaps;
/* we had a pool, check caps */
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
GST_DEBUG ("check existing pool caps %" GST_PTR_FORMAT
" with new caps %" GST_PTR_FORMAT, pcaps, caps);
if (!gst_caps_is_equal (caps, pcaps)) {
GST_DEBUG ("pool has different caps");
/* different caps, we can't use this pool */
gst_object_unref (pool);
pool = NULL;
}
gst_structure_free (config);
}
GST_DEBUG ("pool %p", pool);
if (pool == NULL && need_pool) {
GstVideoInfo info;
if (!gst_video_info_from_caps (&info, caps)) {
GST_ERROR ("allocation query has invalid caps %"
GST_PTR_FORMAT, caps);
return GST_PAD_PROBE_OK;
}
GST_DEBUG ("create new pool");
state->pool = pool =
gst_custom_egl_image_buffer_pool_new (state, state->display);
GST_DEBUG ("done create new pool %p", pool);
/* the normal size of a frame */
size = info.size;
config = gst_buffer_pool_get_config (pool);
/* we need at least 2 buffer because we hold on to the last one */
gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
gst_buffer_pool_config_set_allocator (config, NULL, &params);
if (!gst_buffer_pool_set_config (pool, config)) {
gst_object_unref (pool);
GST_ERROR ("failed to set pool configuration");
return GST_PAD_PROBE_OK;
}
}
if (pool) {
/* we need at least 2 buffer because we hold on to the last one */
gst_query_add_allocation_pool (query, pool, size, 2, 0);
gst_object_unref (pool);
}
/* First the default allocator */
if (!gst_egl_image_memory_is_mappable ()) {
allocator = gst_allocator_find (NULL);
gst_query_add_allocation_param (query, allocator, &params);
gst_object_unref (allocator);
}
allocator = gst_egl_image_allocator_obtain ();
GST_WARNING ("Allocator obtained %p", allocator);
if (!gst_egl_image_memory_is_mappable ())
params.flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
gst_query_add_allocation_param (query, allocator, &params);
gst_object_unref (allocator);
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
external_gl_context_desc =
gst_structure_new ("GstVideoGLTextureUploadMeta",
"gst.gl.context.handle", G_TYPE_POINTER, state->context,
"gst.gl.context.type", G_TYPE_STRING, platform,
"gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL);
gst_query_add_allocation_meta (query,
GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL);
GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE,
NULL /*external_gl_context_desc */ );
gst_structure_free (external_gl_context_desc);
g_free (gl_apis);
g_free (platform);
GST_DEBUG ("done alocation");
return GST_PAD_PROBE_OK;
break;
}
case GST_QUERY_DRAIN:
case GST_QUERY_CONTEXT:
{
flush_internal (state);
return gst_gl_handle_context_query (state->pipeline, query,
(GstGLDisplay **) & state->gst_display);
break;
}
default:
@ -1396,9 +881,12 @@ query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
static gboolean
init_playbin_player (APP_STATE_T * state, const gchar * uri)
{
GstElement *vsink;
vsink = gst_element_factory_make ("fakesink", "vsink");
GstPad *pad = NULL;
GstPad *ghostpad = NULL;
GstElement *vbin = gst_bin_new ("vbin");
/* FIXME replace it by glcolorscale */
GstElement *glfilter = gst_element_factory_make ("gleffects", "glfilter");
GstElement *vsink = gst_element_factory_make ("fakesink", "vsink");
g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE,
"enable-last-sample", FALSE,
"max-lateness", 20 * GST_MSECOND, "signal-handoffs", TRUE, NULL);
@ -1406,18 +894,29 @@ init_playbin_player (APP_STATE_T * state, const gchar * uri)
g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state);
g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state);
gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"),
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state, NULL);
gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"),
GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state, NULL);
gst_bin_add_many (GST_BIN (vbin), glfilter, vsink, NULL);
pad = gst_element_get_static_pad (glfilter, "sink");
ghostpad = gst_ghost_pad_new ("sink", pad);
gst_object_unref (pad);
gst_element_add_pad (vbin, ghostpad);
gst_pad_add_probe (ghostpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb,
state, NULL);
gst_pad_add_probe (ghostpad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb,
state, NULL);
gst_element_link (glfilter, vsink);
// FIXME unref ghostpad ?
/* Instantiate and configure playbin */
state->pipeline = gst_element_factory_make ("playbin", "player");
g_object_set (state->pipeline, "uri", uri,
"video-sink", vsink, "flags",
"video-sink", vbin, "flags",
GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_AUDIO, NULL);
state->vsink = gst_object_ref (vsink);
state->vsink = gst_object_ref (vsink); // FIXME vbin ?
return TRUE;
}
@ -1427,6 +926,8 @@ init_parse_launch_player (APP_STATE_T * state, const gchar * spipeline)
GstElement *vsink;
GError *error = NULL;
// FIXME handle parse
state->pipeline = gst_parse_launch (spipeline, &error);
if (!state->pipeline) {
@ -1650,7 +1151,9 @@ qos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state)
static void
close_ogl (void)
{
#if defined (USE_OMX_TARGET_RPI)
DISPMANX_UPDATE_HANDLE_T dispman_update;
#endif
/* clear screen */
glClear (GL_COLOR_BUFFER_BIT);
@ -1661,12 +1164,14 @@ close_ogl (void)
EGL_NO_CONTEXT);
eglDestroySurface (state->display, state->surface);
eglDestroyContext (state->display, state->context);
gst_egl_display_unref (state->gst_display);
gst_object_unref (state->gst_display);
#if defined (USE_OMX_TARGET_RPI)
dispman_update = vc_dispmanx_update_start (0);
vc_dispmanx_element_remove (dispman_update, state->dispman_element);
vc_dispmanx_update_submit_sync (dispman_update);
vc_dispmanx_display_close (state->dispman_display);
#endif
}
//==============================================================================
@ -1676,7 +1181,7 @@ open_ogl (void)
{
TRACE_VC_MEMORY ("state 0");
#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
#if defined (USE_OMX_TARGET_RPI)
bcm_host_init ();
TRACE_VC_MEMORY ("after bcm_host_init");
#endif
@ -1685,8 +1190,8 @@ open_ogl (void)
init_ogl (state);
TRACE_VC_MEMORY ("after init_ogl");
/* Wrap the EGL display */
state->gst_display = gst_egl_display_new (state->display, NULL);
/* Wrap the EGLDisplay to GstGLDisplayEGL */
state->gst_display = gst_gl_display_egl_new_with_egl_display (state->display);
/* Setup the model world */
init_model_proj (state);
@ -1730,9 +1235,11 @@ main (int argc, char **argv)
state->animate = TRUE;
state->current_buffer = NULL;
#if !GLIB_CHECK_VERSION (2, 31, 0)
/* must initialise the threading system before using any other GLib funtion */
if (!g_thread_supported ())
g_thread_init (NULL);
#endif
ctx = g_option_context_new ("[ADDITIONAL ARGUMENTS]");
g_option_context_add_main_entries (ctx, options, NULL);