static GstFlowReturn gst_eglglessink_render (GstEglGlesSink * eglglessink) { guint dar_n, dar_d; gint i; gint w, h; w = GST_VIDEO_SINK_WIDTH (eglglessink); h = GST_VIDEO_SINK_HEIGHT (eglglessink); /* If no one has set a display rectangle on us initialize * a sane default. According to the docs on the xOverlay * interface we are supposed to fill the overlay 100%. We * do this trying to take PAR/DAR into account unless the * calling party explicitly ask us not to by setting * force_aspect_ratio to FALSE. */ if (gst_egl_adaptation_update_surface_dimensions (eglglessink->egl_context) || eglglessink->render_region_changed || !eglglessink->display_region.w || !eglglessink->display_region.h || eglglessink->size_changed) { GST_OBJECT_LOCK (eglglessink); if (!eglglessink->render_region_user) { eglglessink->render_region.x = 0; eglglessink->render_region.y = 0; eglglessink->render_region.w = eglglessink->egl_context->surface_width; eglglessink->render_region.h = eglglessink->egl_context->surface_height; } eglglessink->render_region_changed = FALSE; eglglessink->size_changed = FALSE; if (!eglglessink->force_aspect_ratio) { eglglessink->display_region.x = 0; eglglessink->display_region.y = 0; eglglessink->display_region.w = eglglessink->render_region.w; eglglessink->display_region.h = eglglessink->render_region.h; } else { GstVideoRectangle frame; frame.x = 0; frame.y = 0; if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, w, h, eglglessink->par_n, eglglessink->par_d, eglglessink->egl_context->pixel_aspect_ratio_n, eglglessink->egl_context->pixel_aspect_ratio_d)) { GST_WARNING_OBJECT (eglglessink, "Could not compute resulting DAR"); frame.w = w; frame.h = h; } else { /* Find suitable matching new size acording to dar & par * rationale for prefering leaving the height untouched * comes from interlacing considerations. * XXX: Move this to gstutils? */ if (h % dar_d == 0) { frame.w = gst_util_uint64_scale_int (h, dar_n, dar_d); frame.h = h; } else if (w % dar_n == 0) { frame.h = gst_util_uint64_scale_int (w, dar_d, dar_n); frame.w = w; } else { /* Neither width nor height can be precisely scaled. * Prefer to leave height untouched. See comment above. */ frame.w = gst_util_uint64_scale_int (h, dar_n, dar_d); frame.h = h; } } gst_video_sink_center_rect (frame, eglglessink->render_region, &eglglessink->display_region, TRUE); } glViewport (eglglessink->render_region.x, eglglessink->egl_context->surface_height - eglglessink->render_region.y - eglglessink->render_region.h, eglglessink->render_region.w, eglglessink->render_region.h); /* Clear the surface once if its content is preserved */ if (eglglessink->egl_context->buffer_preserved) { glClearColor (0.0, 0.0, 0.0, 1.0); glClear (GL_COLOR_BUFFER_BIT); } if (!gst_eglglessink_setup_vbo (eglglessink, FALSE)) { GST_OBJECT_UNLOCK (eglglessink); GST_ERROR_OBJECT (eglglessink, "VBO setup failed"); goto HANDLE_ERROR; } GST_OBJECT_UNLOCK (eglglessink); } if (!eglglessink->egl_context->buffer_preserved) { /* Draw black borders */ GST_DEBUG_OBJECT (eglglessink, "Drawing black border 1"); glUseProgram (eglglessink->egl_context->glslprogram[1]); glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3, GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (4 * sizeof (coord5))); if (got_gl_error ("glVertexAttribPointer")) goto HANDLE_ERROR; glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); if (got_gl_error ("glDrawElements")) goto HANDLE_ERROR; GST_DEBUG_OBJECT (eglglessink, "Drawing black border 2"); glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3, GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (8 * sizeof (coord5))); if (got_gl_error ("glVertexAttribPointer")) goto HANDLE_ERROR; glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); if (got_gl_error ("glDrawElements")) goto HANDLE_ERROR; } /* Draw video frame */ GST_DEBUG_OBJECT (eglglessink, "Drawing video frame"); glUseProgram (eglglessink->egl_context->glslprogram[0]); for (i = 0; i < eglglessink->egl_context->n_textures; i++) { glUniform1i (eglglessink->egl_context->tex_loc[0][i], i); if (got_gl_error ("glUniform1i")) goto HANDLE_ERROR; } glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3, GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (0 * sizeof (coord5))); if (got_gl_error ("glVertexAttribPointer")) goto HANDLE_ERROR; glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2, GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (3 * sizeof (gfloat))); if (got_gl_error ("glVertexAttribPointer")) goto HANDLE_ERROR; glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); if (got_gl_error ("glDrawElements")) goto HANDLE_ERROR; if (!gst_egl_adaptation_swap_buffers (eglglessink->egl_context)) { goto HANDLE_ERROR; } GST_DEBUG_OBJECT (eglglessink, "Succesfully rendered 1 frame"); return GST_FLOW_OK; HANDLE_ERROR: GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame"); return GST_FLOW_ERROR; }
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; EGLImageKHR image; 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); 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; 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, image, GST_VIDEO_GL_TEXTURE_TYPE_RGBA, flags, size, data, NULL); 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) gst_egl_gles_image_data_free (data); if (mem[0]) gst_memory_unref (mem[0]); return NULL; } }
static gboolean gst_eglglessink_fill_texture (GstEglGlesSink * eglglessink, GstBuffer * buf) { gint w, h; w = GST_VIDEO_SINK_WIDTH (eglglessink); h = GST_VIDEO_SINK_HEIGHT (eglglessink); GST_DEBUG_OBJECT (eglglessink, "Got good buffer %p. Sink geometry is %dx%d size %d", buf, w, h, buf ? GST_BUFFER_SIZE (buf) : -1); switch (eglglessink->format) { case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_ABGR: case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_BGRx: case GST_VIDEO_FORMAT_xRGB: case GST_VIDEO_FORMAT_xBGR: glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf)); break; case GST_VIDEO_FORMAT_AYUV: glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf)); break; case GST_VIDEO_FORMAT_Y444: case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y41B:{ gint coffset, cw, ch; coffset = gst_video_format_get_component_offset (eglglessink->format, 0, w, h); cw = gst_video_format_get_component_width (eglglessink->format, 0, w); ch = gst_video_format_get_component_height (eglglessink->format, 0, h); glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, cw, ch, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset); coffset = gst_video_format_get_component_offset (eglglessink->format, 1, w, h); cw = gst_video_format_get_component_width (eglglessink->format, 1, w); ch = gst_video_format_get_component_height (eglglessink->format, 1, h); glActiveTexture (GL_TEXTURE1); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]); glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, cw, ch, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset); coffset = gst_video_format_get_component_offset (eglglessink->format, 2, w, h); cw = gst_video_format_get_component_width (eglglessink->format, 2, w); ch = gst_video_format_get_component_height (eglglessink->format, 2, h); glActiveTexture (GL_TEXTURE2); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[2]); glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, cw, ch, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset); break; } case GST_VIDEO_FORMAT_YUY2: case GST_VIDEO_FORMAT_YVYU: case GST_VIDEO_FORMAT_UYVY: glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf)); glActiveTexture (GL_TEXTURE1); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, GST_ROUND_UP_2 (w) / 2, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf)); break; case GST_VIDEO_FORMAT_NV12: case GST_VIDEO_FORMAT_NV21:{ gint coffset, cw, ch; coffset = gst_video_format_get_component_offset (eglglessink->format, 0, w, h); cw = gst_video_format_get_component_width (eglglessink->format, 0, w); ch = gst_video_format_get_component_height (eglglessink->format, 0, h); glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, cw, ch, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset); coffset = gst_video_format_get_component_offset (eglglessink->format, (eglglessink->format == GST_VIDEO_FORMAT_NV12 ? 1 : 2), w, h); cw = gst_video_format_get_component_width (eglglessink->format, 1, w); ch = gst_video_format_get_component_height (eglglessink->format, 1, h); glActiveTexture (GL_TEXTURE1); glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]); glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, cw, ch, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GST_BUFFER_DATA (buf) + coffset); break; } default: g_assert_not_reached (); break; } if (got_gl_error ("glTexImage2D")) goto HANDLE_ERROR; return TRUE; HANDLE_ERROR: return FALSE; }
static gboolean gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink, gboolean reset) { gdouble render_width, render_height; gdouble x1, x2, y1, y2; GST_INFO_OBJECT (eglglessink, "VBO setup. have_vbo:%d, should reset %d", eglglessink->egl_context->have_vbo, reset); if (eglglessink->egl_context->have_vbo && reset) { glDeleteBuffers (1, &eglglessink->egl_context->position_buffer); glDeleteBuffers (1, &eglglessink->egl_context->index_buffer); eglglessink->egl_context->have_vbo = FALSE; } render_width = eglglessink->render_region.w; render_height = eglglessink->render_region.h; GST_DEBUG_OBJECT (eglglessink, "Performing VBO setup"); x1 = (eglglessink->display_region.x / render_width) * 2.0 - 1; y1 = (eglglessink->display_region.y / render_height) * 2.0 - 1; x2 = ((eglglessink->display_region.x + eglglessink->display_region.w) / render_width) * 2.0 - 1; y2 = ((eglglessink->display_region.y + eglglessink->display_region.h) / render_height) * 2.0 - 1; eglglessink->egl_context->position_array[0].x = x2; eglglessink->egl_context->position_array[0].y = y2; eglglessink->egl_context->position_array[0].z = 0; eglglessink->egl_context->position_array[0].a = 1; eglglessink->egl_context->position_array[0].b = 0; eglglessink->egl_context->position_array[1].x = x2; eglglessink->egl_context->position_array[1].y = y1; eglglessink->egl_context->position_array[1].z = 0; eglglessink->egl_context->position_array[1].a = 1; eglglessink->egl_context->position_array[1].b = 1; eglglessink->egl_context->position_array[2].x = x1; eglglessink->egl_context->position_array[2].y = y2; eglglessink->egl_context->position_array[2].z = 0; eglglessink->egl_context->position_array[2].a = 0; eglglessink->egl_context->position_array[2].b = 0; eglglessink->egl_context->position_array[3].x = x1; eglglessink->egl_context->position_array[3].y = y1; eglglessink->egl_context->position_array[3].z = 0; eglglessink->egl_context->position_array[3].a = 0; eglglessink->egl_context->position_array[3].b = 1; if (eglglessink->display_region.x == 0) { /* Borders top/bottom */ eglglessink->egl_context->position_array[4 + 0].x = 1; eglglessink->egl_context->position_array[4 + 0].y = 1; eglglessink->egl_context->position_array[4 + 0].z = 0; eglglessink->egl_context->position_array[4 + 1].x = x2; eglglessink->egl_context->position_array[4 + 1].y = y2; eglglessink->egl_context->position_array[4 + 1].z = 0; eglglessink->egl_context->position_array[4 + 2].x = -1; eglglessink->egl_context->position_array[4 + 2].y = 1; eglglessink->egl_context->position_array[4 + 2].z = 0; eglglessink->egl_context->position_array[4 + 3].x = x1; eglglessink->egl_context->position_array[4 + 3].y = y2; eglglessink->egl_context->position_array[4 + 3].z = 0; eglglessink->egl_context->position_array[8 + 0].x = 1; eglglessink->egl_context->position_array[8 + 0].y = y1; eglglessink->egl_context->position_array[8 + 0].z = 0; eglglessink->egl_context->position_array[8 + 1].x = 1; eglglessink->egl_context->position_array[8 + 1].y = -1; eglglessink->egl_context->position_array[8 + 1].z = 0; eglglessink->egl_context->position_array[8 + 2].x = x1; eglglessink->egl_context->position_array[8 + 2].y = y1; eglglessink->egl_context->position_array[8 + 2].z = 0; eglglessink->egl_context->position_array[8 + 3].x = -1; eglglessink->egl_context->position_array[8 + 3].y = -1; eglglessink->egl_context->position_array[8 + 3].z = 0; } else { /* Borders left/right */ eglglessink->egl_context->position_array[4 + 0].x = x1; eglglessink->egl_context->position_array[4 + 0].y = 1; eglglessink->egl_context->position_array[4 + 0].z = 0; eglglessink->egl_context->position_array[4 + 1].x = x1; eglglessink->egl_context->position_array[4 + 1].y = -1; eglglessink->egl_context->position_array[4 + 1].z = 0; eglglessink->egl_context->position_array[4 + 2].x = -1; eglglessink->egl_context->position_array[4 + 2].y = 1; eglglessink->egl_context->position_array[4 + 2].z = 0; eglglessink->egl_context->position_array[4 + 3].x = -1; eglglessink->egl_context->position_array[4 + 3].y = -1; eglglessink->egl_context->position_array[4 + 3].z = 0; eglglessink->egl_context->position_array[8 + 0].x = 1; eglglessink->egl_context->position_array[8 + 0].y = 1; eglglessink->egl_context->position_array[8 + 0].z = 0; eglglessink->egl_context->position_array[8 + 1].x = 1; eglglessink->egl_context->position_array[8 + 1].y = -1; eglglessink->egl_context->position_array[8 + 1].z = 0; eglglessink->egl_context->position_array[8 + 2].x = x2; eglglessink->egl_context->position_array[8 + 2].y = y2; eglglessink->egl_context->position_array[8 + 2].z = 0; eglglessink->egl_context->position_array[8 + 3].x = x2; eglglessink->egl_context->position_array[8 + 3].y = -1; eglglessink->egl_context->position_array[8 + 3].z = 0; } eglglessink->egl_context->index_array[0] = 0; eglglessink->egl_context->index_array[1] = 1; eglglessink->egl_context->index_array[2] = 2; eglglessink->egl_context->index_array[3] = 3; glGenBuffers (1, &eglglessink->egl_context->position_buffer); glGenBuffers (1, &eglglessink->egl_context->index_buffer); if (got_gl_error ("glGenBuffers")) goto HANDLE_ERROR_LOCKED; glBindBuffer (GL_ARRAY_BUFFER, eglglessink->egl_context->position_buffer); if (got_gl_error ("glBindBuffer position_buffer")) goto HANDLE_ERROR_LOCKED; glBufferData (GL_ARRAY_BUFFER, sizeof (eglglessink->egl_context->position_array), eglglessink->egl_context->position_array, GL_STATIC_DRAW); if (got_gl_error ("glBufferData position_buffer")) goto HANDLE_ERROR_LOCKED; glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, eglglessink->egl_context->index_buffer); if (got_gl_error ("glBindBuffer index_buffer")) goto HANDLE_ERROR_LOCKED; glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (eglglessink->egl_context->index_array), eglglessink->egl_context->index_array, GL_STATIC_DRAW); if (got_gl_error ("glBufferData index_buffer")) goto HANDLE_ERROR_LOCKED; eglglessink->egl_context->have_vbo = TRUE; GST_DEBUG_OBJECT (eglglessink, "VBO setup done"); return TRUE; HANDLE_ERROR_LOCKED: GST_ERROR_OBJECT (eglglessink, "Unable to perform VBO setup"); return FALSE; }
gboolean gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx, GstVideoFormat format) { GLboolean ret; const gchar *texnames[3] = { NULL, }; gchar *frag_prog = NULL; gboolean free_frag_prog = FALSE; gint i; GST_DEBUG_OBJECT (ctx->element, "Enter EGL surface setup"); if (!gst_egl_adaptation_create_surface (ctx)) { goto HANDLE_ERROR_LOCKED; } gst_egl_adaptation_query_buffer_preserved (ctx); if (!gst_egl_adaptation_make_current (ctx, TRUE)) goto HANDLE_ERROR_LOCKED; gst_egl_adaptation_query_par (ctx); /* Save surface dims */ gst_egl_adaptation_update_surface_dimensions (ctx); /* We have a surface! */ ctx->have_surface = TRUE; /* Init vertex and fragment GLSL shaders. * Note: Shader compiler support is optional but we currently rely on it. */ glGetBooleanv (GL_SHADER_COMPILER, &ret); if (ret == GL_FALSE) { GST_ERROR_OBJECT (ctx->element, "Shader compiler support is unavailable!"); goto HANDLE_ERROR; } /* Build shader program for video texture rendering */ switch (format) { case GST_VIDEO_FORMAT_AYUV: frag_prog = (gchar *) frag_AYUV_prog; free_frag_prog = FALSE; ctx->n_textures = 1; texnames[0] = "tex"; break; case GST_VIDEO_FORMAT_Y444: case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y41B: frag_prog = (gchar *) frag_PLANAR_YUV_prog; free_frag_prog = FALSE; ctx->n_textures = 3; texnames[0] = "Ytex"; texnames[1] = "Utex"; texnames[2] = "Vtex"; break; case GST_VIDEO_FORMAT_YUY2: frag_prog = g_strdup_printf (frag_YUY2_YVYU_UYVY_prog, 'r', 'g', 'a'); free_frag_prog = TRUE; ctx->n_textures = 2; texnames[0] = "Ytex"; texnames[1] = "UVtex"; break; case GST_VIDEO_FORMAT_YVYU: frag_prog = g_strdup_printf (frag_YUY2_YVYU_UYVY_prog, 'r', 'a', 'g'); free_frag_prog = TRUE; ctx->n_textures = 2; texnames[0] = "Ytex"; texnames[1] = "UVtex"; break; case GST_VIDEO_FORMAT_UYVY: frag_prog = g_strdup_printf (frag_YUY2_YVYU_UYVY_prog, 'a', 'r', 'b'); free_frag_prog = TRUE; ctx->n_textures = 2; texnames[0] = "Ytex"; texnames[1] = "UVtex"; break; case GST_VIDEO_FORMAT_NV12: frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'r', 'a'); free_frag_prog = TRUE; ctx->n_textures = 2; texnames[0] = "Ytex"; texnames[1] = "UVtex"; break; case GST_VIDEO_FORMAT_NV21: frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'a', 'r'); free_frag_prog = TRUE; ctx->n_textures = 2; texnames[0] = "Ytex"; texnames[1] = "UVtex"; break; case GST_VIDEO_FORMAT_BGR: case GST_VIDEO_FORMAT_BGRx: case GST_VIDEO_FORMAT_BGRA: frag_prog = g_strdup_printf (frag_REORDER_prog, 'b', 'g', 'r'); free_frag_prog = TRUE; ctx->n_textures = 1; texnames[0] = "tex"; break; case GST_VIDEO_FORMAT_xRGB: case GST_VIDEO_FORMAT_ARGB: frag_prog = g_strdup_printf (frag_REORDER_prog, 'g', 'b', 'a'); free_frag_prog = TRUE; ctx->n_textures = 1; texnames[0] = "tex"; break; case GST_VIDEO_FORMAT_xBGR: case GST_VIDEO_FORMAT_ABGR: frag_prog = g_strdup_printf (frag_REORDER_prog, 'a', 'b', 'g'); free_frag_prog = TRUE; ctx->n_textures = 1; texnames[0] = "tex"; break; case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_RGB16: frag_prog = (gchar *) frag_COPY_prog; free_frag_prog = FALSE; ctx->n_textures = 1; texnames[0] = "tex"; break; default: g_assert_not_reached (); break; } if (!create_shader_program (ctx, &ctx->glslprogram[0], &ctx->vertshader[0], &ctx->fragshader[0], vert_COPY_prog, frag_prog)) { if (free_frag_prog) g_free (frag_prog); frag_prog = NULL; goto HANDLE_ERROR; } if (free_frag_prog) g_free (frag_prog); frag_prog = NULL; ctx->position_loc[0] = glGetAttribLocation (ctx->glslprogram[0], "position"); ctx->texpos_loc[0] = glGetAttribLocation (ctx->glslprogram[0], "texpos"); glEnableVertexAttribArray (ctx->position_loc[0]); if (got_gl_error ("glEnableVertexAttribArray")) goto HANDLE_ERROR; glEnableVertexAttribArray (ctx->texpos_loc[0]); if (got_gl_error ("glEnableVertexAttribArray")) goto HANDLE_ERROR; for (i = 0; i < ctx->n_textures; i++) { ctx->tex_loc[0][i] = glGetUniformLocation (ctx->glslprogram[0], texnames[i]); } if (!ctx->buffer_preserved) { /* Build shader program for black borders */ if (!create_shader_program (ctx, &ctx->glslprogram[1], &ctx->vertshader[1], &ctx->fragshader[1], vert_COPY_prog_no_tex, frag_BLACK_prog)) goto HANDLE_ERROR; ctx->position_loc[1] = glGetAttribLocation (ctx->glslprogram[1], "position"); glEnableVertexAttribArray (ctx->position_loc[1]); if (got_gl_error ("glEnableVertexAttribArray")) goto HANDLE_ERROR; } /* Generate textures */ if (!ctx->have_texture) { GST_INFO_OBJECT (ctx->element, "Performing initial texture setup"); glGenTextures (ctx->n_textures, ctx->texture); if (got_gl_error ("glGenTextures")) goto HANDLE_ERROR_LOCKED; for (i = 0; i < ctx->n_textures; i++) { glBindTexture (GL_TEXTURE_2D, ctx->texture[i]); if (got_gl_error ("glBindTexture")) goto HANDLE_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 HANDLE_ERROR_LOCKED; } ctx->have_texture = TRUE; } glUseProgram (0); return TRUE; /* Errors */ HANDLE_ERROR_LOCKED: HANDLE_ERROR: GST_ERROR_OBJECT (ctx->element, "Couldn't setup EGL surface"); return FALSE; }
static gboolean create_shader_program (GstEglAdaptationContext * ctx, GLuint * prog, GLuint * vert, GLuint * frag, const gchar * vert_text, const gchar * frag_text) { GLint test; GLchar *info_log; /* Build shader program for video texture rendering */ *vert = glCreateShader (GL_VERTEX_SHADER); GST_DEBUG_OBJECT (ctx->element, "Sending %s to handle %d", vert_text, *vert); glShaderSource (*vert, 1, &vert_text, NULL); if (got_gl_error ("glShaderSource vertex")) goto HANDLE_ERROR; glCompileShader (*vert); if (got_gl_error ("glCompileShader vertex")) goto HANDLE_ERROR; glGetShaderiv (*vert, GL_COMPILE_STATUS, &test); if (test != GL_FALSE) GST_DEBUG_OBJECT (ctx->element, "Successfully compiled vertex shader"); else { GST_ERROR_OBJECT (ctx->element, "Couldn't compile vertex shader"); glGetShaderiv (*vert, GL_INFO_LOG_LENGTH, &test); info_log = g_new0 (GLchar, test); glGetShaderInfoLog (*vert, test, NULL, info_log); GST_INFO_OBJECT (ctx->element, "Compilation info log:\n%s", info_log); g_free (info_log); goto HANDLE_ERROR; } *frag = glCreateShader (GL_FRAGMENT_SHADER); GST_DEBUG_OBJECT (ctx->element, "Sending %s to handle %d", frag_text, *frag); glShaderSource (*frag, 1, &frag_text, NULL); if (got_gl_error ("glShaderSource fragment")) goto HANDLE_ERROR; glCompileShader (*frag); if (got_gl_error ("glCompileShader fragment")) goto HANDLE_ERROR; glGetShaderiv (*frag, GL_COMPILE_STATUS, &test); if (test != GL_FALSE) GST_DEBUG_OBJECT (ctx->element, "Successfully compiled fragment shader"); else { GST_ERROR_OBJECT (ctx->element, "Couldn't compile fragment shader"); glGetShaderiv (*frag, GL_INFO_LOG_LENGTH, &test); info_log = g_new0 (GLchar, test); glGetShaderInfoLog (*frag, test, NULL, info_log); GST_INFO_OBJECT (ctx->element, "Compilation info log:\n%s", info_log); g_free (info_log); goto HANDLE_ERROR; } *prog = glCreateProgram (); if (got_gl_error ("glCreateProgram")) goto HANDLE_ERROR; glAttachShader (*prog, *vert); if (got_gl_error ("glAttachShader vertices")) goto HANDLE_ERROR; glAttachShader (*prog, *frag); if (got_gl_error ("glAttachShader fragments")) goto HANDLE_ERROR; glLinkProgram (*prog); glGetProgramiv (*prog, GL_LINK_STATUS, &test); if (test != GL_FALSE) { GST_DEBUG_OBJECT (ctx->element, "GLES: Successfully linked program"); } else { GST_ERROR_OBJECT (ctx->element, "Couldn't link program"); goto HANDLE_ERROR; } return TRUE; HANDLE_ERROR: { if (*frag && *prog) glDetachShader (*prog, *frag); if (*vert && *prog) glDetachShader (*prog, *vert); if (*prog) glDeleteProgram (*prog); if (*frag) glDeleteShader (*frag); if (*vert) glDeleteShader (*vert); *prog = 0; *frag = 0; *vert = 0; return FALSE; } }
GstBuffer * gst_egl_image_allocator_alloc_eglimage (GstAllocator * allocator, GstEGLDisplay * display, EGLContext eglcontext, 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); switch (format) { case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_BGR:{ gsize size; EGLImageKHR image; mem[0] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_RGB, 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); stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 3); 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_RGB, GST_VIDEO_INFO_WIDTH (&info), GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); if (got_gl_error ("glTexImage2D")) goto mem_error; image = eglCreateImageKHR (gst_egl_display_get (display), eglcontext, 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, display, image, GST_VIDEO_GL_TEXTURE_TYPE_RGB, flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free); n_mem = 1; } break; } case GST_VIDEO_FORMAT_RGB16:{ EGLImageKHR image; gsize size; mem[0] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_RGB, 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); stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 2); 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_RGB, GST_VIDEO_INFO_WIDTH (&info), GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); if (got_gl_error ("glTexImage2D")) goto mem_error; image = eglCreateImageKHR (gst_egl_display_get (display), eglcontext, 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, display, image, GST_VIDEO_GL_TEXTURE_TYPE_RGB, flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free); n_mem = 1; } break; } case GST_VIDEO_FORMAT_NV12: case GST_VIDEO_FORMAT_NV21:{ EGLImageKHR image; gsize size[2]; mem[0] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, 0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]); mem[1] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA, GST_VIDEO_INFO_COMP_WIDTH (&info, 1), GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]); if (mem[0] && mem[1]) { stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info); offset[1] = size[0]; stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info); n_mem = 2; GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE); } else { if (mem[0]) gst_memory_unref (mem[0]); if (mem[1]) gst_memory_unref (mem[1]); mem[0] = mem[1] = NULL; stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0)); stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1) * 2); offset[1] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0); size[0] = offset[1]; size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1); for (i = 0; i < 2; i++) { data = g_slice_new0 (GstEGLGLESImageData); 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; if (i == 0) glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, i), GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); else glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, GST_VIDEO_INFO_COMP_WIDTH (&info, i), GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); if (got_gl_error ("glTexImage2D")) goto mem_error; image = eglCreateImageKHR (gst_egl_display_get (display), eglcontext, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer) (guintptr) data->texture, NULL); if (got_egl_error ("eglCreateImageKHR")) goto mem_error; mem[i] = gst_egl_image_allocator_wrap (allocator, display, image, (i == 0 ? GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE : GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA), flags, size[i], data, (GDestroyNotify) gst_egl_gles_image_data_free); } n_mem = 2; } break; } case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_Y444: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y41B:{ EGLImageKHR image; gsize size[3]; mem[0] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, 0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]); mem[1] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, 1), GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]); mem[2] = gst_egl_image_allocator_alloc (allocator, display, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, 2), GST_VIDEO_INFO_COMP_HEIGHT (&info, 2), &size[2]); if (mem[0] && mem[1] && mem[2]) { stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info); offset[1] = size[0]; stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info); offset[2] = size[1]; stride[2] = size[2] / GST_VIDEO_INFO_HEIGHT (&info); n_mem = 3; GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE); GST_MINI_OBJECT_FLAG_SET (mem[2], GST_MEMORY_FLAG_NO_SHARE); } else { if (mem[0]) gst_memory_unref (mem[0]); if (mem[1]) gst_memory_unref (mem[1]); if (mem[2]) gst_memory_unref (mem[2]); mem[0] = mem[1] = mem[2] = NULL; stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0)); stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1)); stride[2] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 2)); size[0] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0); size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1); size[2] = stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 2); offset[0] = 0; offset[1] = size[0]; offset[2] = offset[1] + size[1]; for (i = 0; i < 3; i++) { data = g_slice_new0 (GstEGLGLESImageData); 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_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, i), GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); if (got_gl_error ("glTexImage2D")) goto mem_error; image = eglCreateImageKHR (gst_egl_display_get (display), eglcontext, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer) (guintptr) data->texture, NULL); if (got_egl_error ("eglCreateImageKHR")) goto mem_error; mem[i] = gst_egl_image_allocator_wrap (allocator, display, image, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, flags, size[i], data, (GDestroyNotify) gst_egl_gles_image_data_free); } n_mem = 3; } break; } case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_ARGB: case GST_VIDEO_FORMAT_ABGR: case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_BGRx: case GST_VIDEO_FORMAT_xRGB: case GST_VIDEO_FORMAT_xBGR: case GST_VIDEO_FORMAT_AYUV:{ gsize size; EGLImageKHR image; mem[0] = gst_egl_image_allocator_alloc (allocator, 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); 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; image = eglCreateImageKHR (gst_egl_display_get (display), eglcontext, 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, display, image, GST_VIDEO_GL_TEXTURE_TYPE_RGBA, flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free); n_mem = 1; } break; } default: g_assert_not_reached (); break; } buffer = gst_buffer_new (); gst_buffer_add_video_meta_full (buffer, 0, format, width, height, GST_VIDEO_INFO_N_PLANES (&info), offset, stride); for (i = 0; i < n_mem; i++) gst_buffer_append_memory (buffer, mem[i]); return buffer; mem_error: { GST_ERROR_OBJECT (GST_CAT_DEFAULT, "Failed to create EGLImage"); if (data) gst_egl_gles_image_data_free (data); if (mem[0]) gst_memory_unref (mem[0]); if (mem[1]) gst_memory_unref (mem[1]); if (mem[2]) gst_memory_unref (mem[2]); return NULL; } }