eglglessink: Add support for allocating EGLImages on Mali

This allows to decoders and other upstream elements to do
zero-copy to the sink.
This commit is contained in:
Sebastian Dröge 2012-12-28 12:27:43 +01:00
parent 21d9fe45e4
commit 4df3e56c9a
4 changed files with 1983 additions and 516 deletions

File diff suppressed because it is too large Load diff

View file

@ -56,6 +56,8 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include "video_platform_wrapper.h"
G_BEGIN_DECLS
#define GST_TYPE_EGLGLESSINK \
(gst_eglglessink_get_type())
@ -124,9 +126,9 @@ struct _GstEglGlesRenderContext
EGLNativeWindowType window, used_window;
EGLSurface surface;
gboolean buffer_preserved;
GLuint fragshader[3]; /* frame, border, frame-platform */
GLuint vertshader[3]; /* frame, border, frame-platform */
GLuint glslprogram[3]; /* frame, border, frame-platform */
GLuint fragshader[2]; /* frame, border */
GLuint vertshader[2]; /* frame, border */
GLuint glslprogram[2]; /* frame, border */
GLuint texture[3]; /* RGB/Y, U/UV, V */
EGLint surface_width;
EGLint surface_height;
@ -135,13 +137,19 @@ struct _GstEglGlesRenderContext
gint n_textures;
/* shader vars */
GLuint position_loc[3]; /* frame, border, frame-platform */
GLuint texpos_loc[2]; /* frame, frame-platform */
GLuint tex_scale_loc[2][3]; /* [frame, frame-platform] RGB/Y, U/UV, V */
GLuint tex_loc[2][3]; /* [frame, frame-platform] RGB/Y, U/UV, V */
GLuint position_loc[2]; /* frame, border */
GLuint texpos_loc[2]; /* frame */
GLuint tex_scale_loc[1][3]; /* [frame] RGB/Y, U/UV, V */
GLuint tex_loc[1][3]; /* [frame] RGB/Y, U/UV, V */
coord5 position_array[12]; /* 4 x Frame, 4 x Border1, 4 x Border2 */
unsigned short index_array[4];
unsigned int position_buffer, index_buffer;
gboolean can_map_eglimage;
GstMemoryMapFunction eglimage_map;
GstMemoryUnmapFunction eglimage_unmap;
PlatformMapVideo eglimage_video_map;
PlatformUnmapVideo eglimage_video_unmap;
};
/*
@ -201,6 +209,7 @@ struct _GstEglGlesSink
GstCaps *current_caps, *configured_caps;
GstVideoInfo configured_info;
gfloat stride[3];
GstBufferPool *pool;
GstEglGlesImageFmt *selected_fmt;
GstEglGlesRenderContext eglglesctx;

View file

@ -47,10 +47,13 @@
#endif
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <string.h>
#include <gst/gst.h>
#include "video_platform_wrapper.h"
@ -70,6 +73,7 @@ platform_wrapper_init (void)
#ifdef USE_EGL_X11
#include <X11/Xlib.h>
#include <gst/video/gstvideopool.h>
typedef struct
{
@ -118,10 +122,45 @@ platform_destroy_native_window (EGLNativeDisplayType display,
*window_data = NULL;
return TRUE;
}
gboolean
platform_can_map_eglimage (GstMemoryMapFunction * map,
GstMemoryUnmapFunction * unmap, PlatformMapVideo * video_map,
PlatformUnmapVideo * video_unmap)
{
return FALSE;
}
gboolean
platform_has_custom_eglimage_alloc (void)
{
return FALSE;
}
gboolean
platform_alloc_eglimage (EGLDisplay display, EGLContext context, GLint format,
GLint type, gint width, gint height, GLuint tex_id, EGLImageKHR * image,
gpointer * image_platform_data)
{
g_assert_not_reached ();
return FALSE;
}
void
platform_free_eglimage (EGLDisplay display, EGLContext context, GLuint tex_id,
EGLImageKHR * image, gpointer * image_platform_data)
{
g_assert_not_reached ();
}
#endif
#ifdef USE_EGL_MALI_FB
#include <EGL/fbdev_window.h>
#include <mali_egl_image.h>
#include <ump/ump.h>
#include <ump/ump_ref_drv.h>
#include <gst/video/video.h>
EGLNativeWindowType
platform_create_native_window (gint width, gint height, gpointer * window_data)
@ -142,9 +181,438 @@ platform_destroy_native_window (EGLNativeDisplayType display,
return TRUE;
}
static gpointer
eglimage_map (GstMemory * gmem, gsize maxsize, GstMapFlags flags)
{
GstEGLImageMemory *mem;
gint i;
g_return_val_if_fail (strcmp (gmem->allocator->mem_type,
GST_EGL_IMAGE_MEMORY_NAME) == 0, FALSE);
mem = GST_EGL_IMAGE_MEMORY (gmem);
g_mutex_lock (&mem->lock);
for (i = 0; i < mem->n_textures; i++) {
if (mem->memory_refcount[i]) {
/* Only multiple READ maps are allowed */
if ((mem->memory_flags[i] & GST_MAP_WRITE)) {
g_mutex_unlock (&mem->lock);
return NULL;
}
}
}
if (!mem->mapped_memory_refcount) {
EGLint attribs[] = {
MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y,
MALI_EGL_IMAGE_ACCESS_MODE, MALI_EGL_IMAGE_ACCESS_READ_ONLY,
EGL_NONE
};
GstVideoInfo info;
mali_egl_image *mali_egl_image;
guint8 *plane_memory, *p;
gint stride, h;
gint j;
gst_video_info_set_format (&info, mem->format, mem->width, mem->height);
mem->mapped_memory = g_malloc (mem->parent.size);
for (i = 0; i < mem->n_textures; i++) {
mali_egl_image = mali_egl_image_lock_ptr (mem->image[i]);
if (!mali_egl_image) {
g_free (mem->mapped_memory);
GST_ERROR ("Failed to lock Mali EGL image: 0x%04x",
mali_egl_image_get_error ());
g_mutex_unlock (&mem->lock);
return NULL;
}
plane_memory = mali_egl_image_map_buffer (mali_egl_image, attribs);
if (!plane_memory) {
mali_egl_image_unlock_ptr (mem->image[i]);
g_free (mem->mapped_memory);
GST_ERROR ("Failed to lock Mali map image: 0x%04x",
mali_egl_image_get_error ());
g_mutex_unlock (&mem->lock);
return NULL;
}
p = ((guint8 *) mem->mapped_memory) + mem->offset[i];
stride = mem->stride[i];
h = GST_VIDEO_INFO_COMP_HEIGHT (&info, i);
for (j = 0; j < h; j++) {
memcpy (p, plane_memory, stride);
p += mem->stride[i];
plane_memory += mem->stride[i];
}
mali_egl_image_unmap_buffer (mem->image[i], attribs);
mali_egl_image_unlock_ptr (mem->image[i]);
}
} else {
/* Only multiple READ maps are allowed */
if ((mem->mapped_memory_flags & GST_MAP_WRITE)) {
g_mutex_unlock (&mem->lock);
return NULL;
}
}
mem->mapped_memory_refcount++;
g_mutex_unlock (&mem->lock);
return mem->mapped_memory;
}
static void
eglimage_unmap (GstMemory * gmem)
{
GstEGLImageMemory *mem;
gint i;
g_return_if_fail (strcmp (gmem->allocator->mem_type,
GST_EGL_IMAGE_MEMORY_NAME) == 0);
mem = GST_EGL_IMAGE_MEMORY (gmem);
g_return_if_fail (mem->mapped_memory);
g_mutex_lock (&mem->lock);
mem->mapped_memory_refcount--;
if (mem->mapped_memory_refcount > 0) {
g_mutex_unlock (&mem->lock);
return;
}
/* Write back */
if ((mem->mapped_memory_flags & GST_MAP_WRITE)) {
EGLint attribs[] = {
MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y,
MALI_EGL_IMAGE_ACCESS_MODE, MALI_EGL_IMAGE_ACCESS_WRITE_ONLY,
EGL_NONE
};
GstVideoInfo info;
mali_egl_image *mali_egl_image;
guint8 *plane_memory, *p;
gint stride, h;
gint j;
gst_video_info_set_format (&info, mem->format, mem->width, mem->height);
for (i = 0; i < mem->n_textures; i++) {
mali_egl_image = mali_egl_image_lock_ptr (mem->image[i]);
if (!mali_egl_image) {
g_free (mem->mapped_memory);
GST_ERROR ("Failed to lock Mali EGL image: 0x%04x",
mali_egl_image_get_error ());
g_mutex_unlock (&mem->lock);
return;
}
plane_memory = mali_egl_image_map_buffer (mali_egl_image, attribs);
if (!plane_memory) {
mali_egl_image_unlock_ptr (mem->image[i]);
g_free (mem->mapped_memory);
GST_ERROR ("Failed to lock Mali map image: 0x%04x",
mali_egl_image_get_error ());
g_mutex_unlock (&mem->lock);
return;
}
p = ((guint8 *) mem->mapped_memory) + mem->offset[i];
stride = mem->stride[i];
h = GST_VIDEO_INFO_COMP_HEIGHT (&info, i);
for (j = 0; j < h; j++) {
memcpy (plane_memory, p, stride);
p += mem->stride[i];
plane_memory += mem->stride[i];
}
mali_egl_image_unmap_buffer (mem->image[i], attribs);
mali_egl_image_unlock_ptr (mem->image[i]);
}
}
g_free (mem->mapped_memory);
g_mutex_unlock (&mem->lock);
}
static gboolean
eglimage_video_map (GstVideoMeta * meta, guint plane,
GstMapInfo * info, gpointer * data, gint * stride, GstMapFlags flags)
{
GstMemory *gmem;
GstEGLImageMemory *mem;
GstVideoInfo vinfo;
g_return_val_if_fail (gst_buffer_n_memory (meta->buffer) == 1, FALSE);
gmem = gst_buffer_peek_memory (meta->buffer, 0);
g_return_val_if_fail (strcmp (gmem->allocator->mem_type,
GST_EGL_IMAGE_MEMORY_NAME) == 0, FALSE);
mem = GST_EGL_IMAGE_MEMORY (gmem);
g_mutex_lock (&mem->lock);
if (mem->format == GST_VIDEO_FORMAT_YV12) {
if (plane == 1)
plane = 2;
else if (plane == 2)
plane = 1;
}
if (mem->mapped_memory_refcount) {
/* Only multiple READ maps are allowed */
if ((mem->mapped_memory_flags & GST_MAP_WRITE)) {
g_mutex_unlock (&mem->lock);
return FALSE;
}
}
if (!mem->memory_refcount[plane]) {
EGLint attribs[] = {
MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y,
MALI_EGL_IMAGE_ACCESS_MODE, MALI_EGL_IMAGE_ACCESS_READ_WRITE,
EGL_NONE
};
if ((flags & GST_MAP_READ) && (flags & GST_MAP_WRITE))
attribs[3] = MALI_EGL_IMAGE_ACCESS_READ_WRITE;
else if ((flags & GST_MAP_READ))
attribs[3] = MALI_EGL_IMAGE_ACCESS_READ_ONLY;
else if ((flags & GST_MAP_WRITE))
attribs[3] = MALI_EGL_IMAGE_ACCESS_WRITE_ONLY;
mem->memory_platform_data[plane] =
mali_egl_image_lock_ptr (mem->image[plane]);
if (!mem->memory_platform_data[plane]) {
GST_ERROR ("Failed to lock Mali EGL image: 0x%04x",
mali_egl_image_get_error ());
goto map_error;
}
mem->memory[plane] =
mali_egl_image_map_buffer (mem->memory_platform_data[plane], attribs);
if (!mem->memory[plane])
goto map_error;
mem->memory_flags[plane] = flags;
} else {
/* Only multiple READ maps are allowed */
if ((mem->memory_flags[plane] & GST_MAP_WRITE)) {
g_mutex_unlock (&mem->lock);
return FALSE;
}
}
mem->memory_refcount[plane]++;
gst_video_info_set_format (&vinfo, mem->format, mem->width, mem->height);
*data = mem->memory[plane];
*stride = mem->stride[plane];
g_mutex_unlock (&mem->lock);
return TRUE;
map_error:
{
EGLint attribs[] = {
MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y,
EGL_NONE
};
GST_ERROR ("Failed to map Mali EGL image: 0x%04x",
mali_egl_image_get_error ());
if (mem->memory_platform_data[plane]) {
mali_egl_image_unmap_buffer (mem->image[plane], attribs);
mali_egl_image_unlock_ptr (mem->image[plane]);
}
mem->memory[plane] = NULL;
mem->memory_platform_data[plane] = NULL;
g_mutex_unlock (&mem->lock);
return FALSE;
}
}
static gboolean
eglimage_video_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
{
GstMemory *gmem;
GstEGLImageMemory *mem;
EGLint attribs[] = {
MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y,
EGL_NONE
};
g_return_val_if_fail (gst_buffer_n_memory (meta->buffer) == 1, FALSE);
gmem = gst_buffer_peek_memory (meta->buffer, 0);
g_return_val_if_fail (strcmp (gmem->allocator->mem_type,
GST_EGL_IMAGE_MEMORY_NAME) == 0, FALSE);
mem = GST_EGL_IMAGE_MEMORY (gmem);
g_mutex_lock (&mem->lock);
if (mem->format == GST_VIDEO_FORMAT_YV12) {
if (plane == 1)
plane = 2;
else if (plane == 2)
plane = 1;
}
if (!mem->memory_refcount[plane]) {
g_mutex_unlock (&mem->lock);
g_return_val_if_reached (FALSE);
}
mem->memory_refcount[plane]--;
if (mem->memory_refcount[plane] > 0) {
g_mutex_unlock (&mem->lock);
return TRUE;
}
/* Unmaps automatically */
if (mem->memory_platform_data[plane]) {
mali_egl_image_unmap_buffer (mem->image[plane], attribs);
mali_egl_image_unlock_ptr (mem->image[plane]);
}
mem->memory[plane] = NULL;
mem->memory_platform_data[plane] = NULL;
g_mutex_unlock (&mem->lock);
return TRUE;
}
gboolean
platform_can_map_eglimage (GstMemoryMapFunction * map,
GstMemoryUnmapFunction * unmap, PlatformMapVideo * video_map,
PlatformUnmapVideo * video_unmap)
{
*map = eglimage_map;
*unmap = eglimage_unmap;
*video_map = eglimage_video_map;
*video_unmap = eglimage_video_unmap;
return TRUE;
}
gboolean
platform_has_custom_eglimage_alloc (void)
{
return TRUE;
}
gboolean
platform_alloc_eglimage (EGLDisplay display, EGLContext context, GLint format,
GLint type, gint width, gint height, GLuint tex_id, EGLImageKHR * image,
gpointer * image_platform_data)
{
fbdev_pixmap pixmap;
pixmap.flags = FBDEV_PIXMAP_SUPPORTS_UMP;
pixmap.width = width;
pixmap.height = height;
switch (format) {
case GL_LUMINANCE:
g_return_val_if_fail (type == GL_UNSIGNED_BYTE, FALSE);
pixmap.red_size = 0;
pixmap.green_size = 0;
pixmap.blue_size = 0;
pixmap.alpha_size = 0;
pixmap.luminance_size = 8;
break;
case GL_LUMINANCE_ALPHA:
g_return_val_if_fail (type == GL_UNSIGNED_BYTE, FALSE);
pixmap.red_size = 0;
pixmap.green_size = 0;
pixmap.blue_size = 0;
pixmap.alpha_size = 8;
pixmap.luminance_size = 8;
break;
case GL_RGB:
if (type == GL_UNSIGNED_BYTE) {
pixmap.red_size = 8;
pixmap.green_size = 8;
pixmap.blue_size = 8;
pixmap.alpha_size = 0;
pixmap.luminance_size = 0;
} else if (type == GL_UNSIGNED_SHORT_5_6_5) {
pixmap.red_size = 5;
pixmap.green_size = 6;
pixmap.blue_size = 5;
pixmap.alpha_size = 0;
pixmap.luminance_size = 0;
} else {
g_return_val_if_reached (FALSE);
}
break;
case GL_RGBA:
g_return_val_if_fail (type == GL_UNSIGNED_BYTE, FALSE);
pixmap.red_size = 8;
pixmap.green_size = 8;
pixmap.blue_size = 8;
pixmap.alpha_size = 8;
pixmap.luminance_size = 0;
break;
default:
g_assert_not_reached ();
return FALSE;
}
pixmap.buffer_size =
pixmap.red_size + pixmap.green_size + pixmap.blue_size +
pixmap.alpha_size + pixmap.luminance_size;
pixmap.bytes_per_pixel = pixmap.buffer_size / 8;
pixmap.format = 0;
if (ump_open () != UMP_OK) {
GST_ERROR ("Failed to open UMP");
return FALSE;
}
pixmap.data =
ump_ref_drv_allocate (GST_ROUND_UP_4 (pixmap.width) * pixmap.height *
pixmap.bytes_per_pixel, UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR);
if (pixmap.data == UMP_INVALID_MEMORY_HANDLE) {
GST_ERROR ("Failed to allocate pixmap data via UMP");
ump_close ();
return FALSE;
}
*image_platform_data = g_slice_dup (fbdev_pixmap, &pixmap);
*image =
eglCreateImageKHR (display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
(EGLClientBuffer) * image_platform_data, NULL);
if (!image) {
GST_ERROR ("Failed to create EGLImage for pixmap");
ump_reference_release ((ump_handle) pixmap.data);
ump_close ();
g_slice_free (fbdev_pixmap, *image_platform_data);
return FALSE;
}
return TRUE;
}
void
platform_free_eglimage (EGLDisplay display, EGLContext context, GLuint tex_id,
EGLImageKHR * image, gpointer * image_platform_data)
{
fbdev_pixmap *pixmap = *image_platform_data;
eglDestroyImageKHR (display, *image);
ump_reference_release ((ump_handle) pixmap->data);
ump_close ();
g_slice_free (fbdev_pixmap, *image_platform_data);
}
#endif
#if !defined(USE_EGL_X11) && !defined(USE_EGL_MALI_FB)
#include <gst/video/gstvideopool.h>
/* Dummy functions for creating a native Window */
EGLNativeWindowType
platform_create_native_window (gint width, gint height, gpointer * window_data)
@ -161,4 +629,33 @@ platform_destroy_native_window (EGLNativeDisplayType display,
return TRUE;
}
gboolean
platform_can_map_eglimage (GstMemoryMapFunction * map,
GstMemoryUnmapFunction * unmap, PlatformMapVideo * video_map,
PlatformUnmapVideo * video_unmap)
{
return FALSE;
}
gboolean
platform_has_custom_eglimage_alloc (void)
{
return FALSE;
}
gboolean
platform_alloc_eglimage (EGLDisplay display, EGLContext context, GLint format,
GLint type, gint width, gint height, GLuint tex_id, EGLImageKHR * image,
gpointer * image_platform_data)
{
g_assert_not_reached ();
return FALSE;
}
void
platform_free_eglimage (EGLDisplay display, EGLContext context, GLuint tex_id,
EGLImageKHR * image, gpointer * image_platform_data)
{
g_assert_not_reached ();
}
#endif

View file

@ -51,6 +51,8 @@
#define __GST_ANDROID_VIDEO_PLATFORM_WRAPPER__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideometa.h>
#include <EGL/egl.h>
gboolean platform_wrapper_init (void);
@ -58,5 +60,44 @@ EGLNativeWindowType platform_create_native_window (gint width, gint height, gpoi
gboolean platform_destroy_native_window (EGLNativeDisplayType display,
EGLNativeWindowType w, gpointer * window_data);
GstBufferPool *platform_create_buffer_pool (EGLDisplay display);
#define GST_EGL_IMAGE_MEMORY_NAME "GstEGLImage"
typedef struct
{
GstMemory parent;
GstVideoFormat format;
gint width, height;
GMutex lock;
/* Always in order RGB/Y, UV/U, V */
EGLImageKHR image[3];
gpointer image_platform_data[3];
GLuint texture[3];
gint stride[3];
gsize offset[3];
guint n_textures;
gpointer memory[3];
gpointer memory_platform_data[3];
gint memory_refcount[3];
GstMapFlags memory_flags[3];
gpointer mapped_memory;
gint mapped_memory_refcount;
GstMapFlags mapped_memory_flags;
} GstEGLImageMemory;
#define GST_EGL_IMAGE_MEMORY(m) ((GstEGLImageMemory*)(m))
typedef gboolean (*PlatformMapVideo) (GstVideoMeta *meta, guint plane, GstMapInfo *info, gpointer *data, gint * stride, GstMapFlags flags);
typedef gboolean (*PlatformUnmapVideo) (GstVideoMeta *meta, guint plane, GstMapInfo *info);
gboolean platform_can_map_eglimage (GstMemoryMapFunction *map, GstMemoryUnmapFunction *unmap, PlatformMapVideo *video_map, PlatformUnmapVideo *video_unmap);
gboolean platform_has_custom_eglimage_alloc (void);
gboolean platform_alloc_eglimage (EGLDisplay display, EGLContext context, GLint format, GLint type, gint width, gint height, GLuint tex_id, EGLImageKHR *image, gpointer *image_platform_data);
void platform_free_eglimage (EGLDisplay display, EGLContext context, GLuint tex_id, EGLImageKHR *image, gpointer *image_platform_data);
#endif