omxvideodec: Add support for egl_render on RPi

This commit is contained in:
Sebastian Dröge 2013-02-25 11:55:04 +01:00
parent d0a5a9a9bf
commit 8a1bb1b4a3
6 changed files with 714 additions and 99 deletions

View file

@ -145,6 +145,7 @@ AG_GST_CHECK_GST_CONTROLLER($GST_API_VERSION, [$GST_REQ], yes)
AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no)
AG_GST_CHECK_GST_PLUGINS_BASE($GST_API_VERSION, [$GST_REQ], yes)
AM_CONDITIONAL(HAVE_GST_CHECK, test "x$HAVE_GST_CHECK" = "xyes")
PKG_CHECK_MODULES([GST_PLUGINS_BAD], [gstreamer-plugins-bad-1.0])
dnl Check for documentation xrefs
GLIB_PREFIX="`$PKG_CONFIG --variable=prefix glib-2.0`"

View file

@ -53,10 +53,13 @@ endif
libgstomx_la_CFLAGS = \
-DGST_USE_UNSTABLE_API=1 \
$(OMX_INCLUDEPATH) \
$(GST_PLUGINS_BAD_CFLAGS) \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS)
libgstomx_la_LIBADD = \
$(GST_PLUGINS_BAD_LIBS) \
-lgstegl-@GST_API_VERSION@ \
$(GST_PLUGINS_BASE_LIBS) \
-lgstaudio-@GST_API_VERSION@ \
-lgstpbutils-@GST_API_VERSION@ \

View file

@ -1066,14 +1066,10 @@ gst_omx_component_setup_tunnel (GstOMXComponent * comp1, GstOMXPort * port1,
OMX_ERRORTYPE err;
g_return_val_if_fail (comp1 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (comp1->state == OMX_StateLoaded
|| !port1->port_def.bEnabled, OMX_ErrorUndefined);
g_return_val_if_fail (port1 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port1->port_def.eDir == OMX_DirOutput,
OMX_ErrorUndefined);
g_return_val_if_fail (comp2 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (comp2->state == OMX_StateLoaded
|| !port2->port_def.bEnabled, OMX_ErrorUndefined);
g_return_val_if_fail (port2 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port2->port_def.eDir == OMX_DirInput,
OMX_ErrorUndefined);
@ -1111,14 +1107,10 @@ gst_omx_component_close_tunnel (GstOMXComponent * comp1, GstOMXPort * port1,
OMX_ERRORTYPE err;
g_return_val_if_fail (comp1 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (comp1->state == OMX_StateLoaded
|| !port1->port_def.bEnabled, OMX_ErrorUndefined);
g_return_val_if_fail (port1 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port1->port_def.eDir == OMX_DirOutput,
OMX_ErrorUndefined);
g_return_val_if_fail (comp2 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (comp2->state == OMX_StateLoaded
|| !port2->port_def.bEnabled, OMX_ErrorUndefined);
g_return_val_if_fail (port2 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port2->port_def.eDir == OMX_DirInput,
OMX_ErrorUndefined);

View file

@ -48,6 +48,10 @@
#include <OMX_Core.h>
#include <OMX_Component.h>
#ifdef USE_OMX_TARGET_RPI
#include <OMX_Broadcom.h>
#endif
#ifdef GST_OMX_STRUCT_PACKING
#pragma pack()
#endif

View file

@ -27,6 +27,7 @@
#include <gst/gst.h>
#include <gst/video/gstvideometa.h>
#include <gst/video/gstvideopool.h>
#include <gst/egl/egl.h>
#include <string.h>
#include "gstomxvideodec.h"
@ -776,6 +777,53 @@ gst_omx_video_dec_open (GstVideoDecoder * decoder)
GST_DEBUG_OBJECT (self, "Opened decoder");
#ifdef USE_OMX_TARGET_RPI
GST_DEBUG_OBJECT (self, "Opening EGL renderer");
self->egl_render =
gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name,
"OMX.broadcom.egl_render", NULL, klass->cdata.hacks);
if (!self->egl_render)
return FALSE;
if (gst_omx_component_get_state (self->egl_render,
GST_CLOCK_TIME_NONE) != OMX_StateLoaded)
return FALSE;
{
OMX_PORT_PARAM_TYPE param;
OMX_ERRORTYPE err;
GST_OMX_INIT_STRUCT (&param);
err =
gst_omx_component_get_parameter (self->egl_render,
OMX_IndexParamVideoInit, &param);
if (err != OMX_ErrorNone) {
GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)",
gst_omx_error_to_string (err), err);
/* Fallback */
in_port_index = 0;
out_port_index = 1;
} else {
GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", param.nPorts,
param.nStartPortNumber);
in_port_index = param.nStartPortNumber + 0;
out_port_index = param.nStartPortNumber + 1;
}
}
self->egl_in_port =
gst_omx_component_add_port (self->egl_render, in_port_index);
self->egl_out_port =
gst_omx_component_add_port (self->egl_render, out_port_index);
if (!self->egl_in_port || !self->egl_out_port)
return FALSE;
GST_DEBUG_OBJECT (self, "Opened EGL renderer");
#endif
return TRUE;
}
@ -786,6 +834,32 @@ gst_omx_video_dec_shutdown (GstOMXVideoDec * self)
GST_DEBUG_OBJECT (self, "Shutting down decoder");
#ifdef USE_OMX_TARGET_RPI
state = gst_omx_component_get_state (self->egl_render, 0);
if (state > OMX_StateLoaded || state == OMX_StateInvalid) {
if (state > OMX_StateIdle) {
gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
gst_omx_component_set_state (self->dec, OMX_StateIdle);
gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND);
gst_omx_component_get_state (self->dec, 1 * GST_SECOND);
}
gst_omx_component_set_state (self->egl_render, OMX_StateLoaded);
gst_omx_component_set_state (self->dec, OMX_StateLoaded);
gst_omx_port_deallocate_buffers (self->dec_in_port);
gst_omx_video_dec_deallocate_output_buffers (self);
gst_omx_component_close_tunnel (self->dec, self->dec_out_port,
self->egl_render, self->egl_in_port);
if (state > OMX_StateLoaded) {
gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND);
gst_omx_component_get_state (self->dec, 1 * GST_SECOND);
}
}
/* Otherwise we didn't use EGL and just fall back to
* shutting down the decoder */
#endif
state = gst_omx_component_get_state (self->dec, 0);
if (state > OMX_StateLoaded || state == OMX_StateInvalid) {
if (state > OMX_StateIdle) {
@ -818,6 +892,14 @@ gst_omx_video_dec_close (GstVideoDecoder * decoder)
gst_omx_component_free (self->dec);
self->dec = NULL;
#ifdef USE_OMX_TARGET_RPI
self->egl_in_port = NULL;
self->egl_out_port = NULL;
if (self->egl_render)
gst_omx_component_free (self->egl_render);
self->egl_render = NULL;
#endif
self->started = FALSE;
GST_DEBUG_OBJECT (self, "Closed decoder");
@ -861,6 +943,12 @@ gst_omx_video_dec_change_state (GstElement * element, GstStateChange transition)
gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE);
if (self->dec_out_port)
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
#ifdef USE_OMX_TARGET_RPI
if (self->egl_in_port)
gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE);
if (self->egl_out_port)
gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE);
#endif
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
@ -948,7 +1036,7 @@ _find_nearest_frame (GstOMXVideoDec * self, GstOMXBuffer * buf)
}
}
if (best_id) {
if (FALSE && best_id) {
for (l = frames; l && l != best_l; l = l->next) {
GstVideoCodecFrame *tmp = l->data;
BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp);
@ -978,7 +1066,7 @@ _find_nearest_frame (GstOMXVideoDec * self, GstOMXBuffer * buf)
}
}
if (finish_frames) {
if (FALSE && finish_frames) {
g_warning ("Too old frames, bug in decoder -- please file a bug");
for (l = finish_frames; l; l = l->next) {
gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), l->data);
@ -1155,13 +1243,14 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
GstVideoCodecState *state =
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self));
#ifdef USE_OMX_TARGET_RPI
port = self->eglimage ? self->egl_out_port : self->dec_out_port;
#else
port = self->dec_out_port;
#endif
pool = gst_video_decoder_get_buffer_pool (GST_VIDEO_DECODER (self));
/* FIXME: Enable this once there's a way to request downstream to
* release all our buffers, e.g.
* http://cgit.freedesktop.org/~wtay/gstreamer/log/?h=release-pool */
if (FALSE && pool) {
if (pool) {
GstAllocator *allocator;
config = gst_buffer_pool_get_config (pool);
@ -1182,8 +1271,13 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
add_videometa = gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
/* TODO: Implement something here */
#ifdef USE_OMX_TARGET_RPI
eglimage = self->eglimage && (allocator
&& g_strcmp0 (allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0);
#else
/* TODO: Implement something that works for other targets too */
eglimage = FALSE;
#endif
caps = caps ? gst_caps_ref (caps) : NULL;
GST_DEBUG_OBJECT (self, "Trying to use pool %p with caps %" GST_PTR_FORMAT
@ -1195,11 +1289,157 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
GST_DEBUG_OBJECT (self, "No pool available, not negotiated yet");
}
#ifdef USE_OMX_TARGET_RPI
/* Will retry without EGLImage */
if (self->eglimage && !eglimage) {
GST_DEBUG_OBJECT (self,
"Wanted to use EGLImage but downstream doesn't support it");
err = OMX_ErrorUndefined;
goto done;
}
#endif
if (caps)
self->out_port_pool =
gst_omx_buffer_pool_new (GST_ELEMENT_CAST (self), self->dec, port);
/* TODO: Implement EGLImage handling and usage of other downstream buffers */
#ifdef USE_OMX_TARGET_RPI
if (eglimage) {
GList *buffers = NULL;
GList *images = NULL;
gint i;
GstBufferPoolAcquireParams params = { 0, };
GstEGLDisplay *display = NULL;
GST_DEBUG_OBJECT (self, "Trying to allocate %d EGLImages", min);
for (i = 0; i < min; i++) {
GstBuffer *buffer;
GstMemory *mem;
if (gst_buffer_pool_acquire_buffer (pool, &buffer, &params) != GST_FLOW_OK
|| gst_buffer_n_memory (buffer) != 1
|| !(mem = gst_buffer_peek_memory (buffer, 0))
|| g_strcmp0 (mem->allocator->mem_type,
GST_EGL_IMAGE_MEMORY_TYPE) != 0) {
GST_INFO_OBJECT (self, "Failed to allocated %d-th EGLImage", i);
g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
g_list_free (images);
buffers = NULL;
images = NULL;
if (display)
gst_egl_display_unref (display);
display = NULL;
/* TODO: For non-RPi targets we want to use the normal memory code below */
/* Retry without EGLImage */
err = OMX_ErrorUndefined;
goto done;
}
buffers = g_list_append (buffers, buffer);
gst_egl_image_memory_set_orientation (mem,
GST_EGL_IMAGE_ORIENTATION_X_NORMAL_Y_FLIP);
images = g_list_append (images, gst_egl_image_memory_get_image (mem));
if (!display)
display = gst_egl_image_memory_get_display (mem);
}
GST_DEBUG_OBJECT (self, "Allocated %d EGLImages successfully", min);
/* Everything went fine? */
if (eglimage) {
GST_DEBUG_OBJECT (self, "Setting EGLDisplay");
self->egl_out_port->port_def.format.video.pNativeWindow =
gst_egl_display_get (display);
err =
gst_omx_port_update_port_definition (self->egl_out_port,
&self->egl_out_port->port_def);
if (display)
gst_egl_display_unref (display);
display = NULL;
if (err != OMX_ErrorNone) {
GST_INFO_OBJECT (self,
"Failed to set EGLDisplay on port: %s (0x%08x)",
gst_omx_error_to_string (err), err);
g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
g_list_free (images);
/* TODO: For non-RPi targets we want to use the normal memory code below */
/* Retry without EGLImage */
goto done;
} else {
GList *l;
if (min != port->port_def.nBufferCountActual) {
err = gst_omx_port_update_port_definition (port, NULL);
if (err == OMX_ErrorNone) {
port->port_def.nBufferCountActual = min;
err = gst_omx_port_update_port_definition (port, &port->port_def);
}
if (err != OMX_ErrorNone) {
GST_INFO_OBJECT (self,
"Failed to configure %n output buffers: %s (0x%08x)", min,
gst_omx_error_to_string (err), err);
g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
g_list_free (images);
/* TODO: For non-RPi targets we want to use the normal memory code below */
/* Retry without EGLImage */
goto done;
}
}
if (!gst_omx_port_is_enabled (port)) {
err = gst_omx_port_set_enabled (port, TRUE);
if (err != OMX_ErrorNone) {
GST_INFO_OBJECT (self,
"Failed to enable port: %s (0x%08x)",
gst_omx_error_to_string (err), err);
g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
g_list_free (images);
/* TODO: For non-RPi targets we want to use the normal memory code below */
/* Retry without EGLImage */
goto done;
}
}
err = gst_omx_port_use_eglimages (port, images);
g_list_free (images);
if (err != OMX_ErrorNone) {
GST_INFO_OBJECT (self,
"Failed to pass EGLImages to port: %s (0x%08x)",
gst_omx_error_to_string (err), err);
g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
/* TODO: For non-RPi targets we want to use the normal memory code below */
/* Retry without EGLImage */
goto done;
}
err = gst_omx_port_wait_enabled (port, 2 * GST_SECOND);
if (err != OMX_ErrorNone) {
GST_INFO_OBJECT (self,
"Failed to wait until port is enabled: %s (0x%08x)",
gst_omx_error_to_string (err), err);
g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
/* TODO: For non-RPi targets we want to use the normal memory code below */
/* Retry without EGLImage */
goto done;
}
GST_DEBUG_OBJECT (self, "Populating internal buffer pool");
GST_OMX_BUFFER_POOL (self->out_port_pool)->other_pool =
GST_BUFFER_POOL (gst_object_ref (pool));
for (l = buffers; l; l = l->next) {
g_ptr_array_add (GST_OMX_BUFFER_POOL (self->out_port_pool)->buffers,
l->data);
}
g_list_free (buffers);
/* All good and done, set caps below */
}
}
}
#endif
/* If not using EGLImage or trying to use EGLImage failed */
if (!eglimage) {
@ -1336,11 +1576,259 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self)
if (self->out_port_pool) {
gst_buffer_pool_set_active (self->out_port_pool, FALSE);
gst_buffer_pool_wait_released (self->out_port_pool);
GST_OMX_BUFFER_POOL (self->out_port_pool)->deactivated = TRUE;
gst_object_unref (self->out_port_pool);
self->out_port_pool = NULL;
}
#ifdef USE_OMX_TARGET_RPI
err =
gst_omx_port_deallocate_buffers (self->
eglimage ? self->egl_out_port : self->dec_out_port);
#else
err = gst_omx_port_deallocate_buffers (self->dec_out_port);
#endif
return err;
}
static OMX_ERRORTYPE
gst_omx_video_dec_reconfigure_output_port (GstOMXVideoDec * self)
{
GstOMXPort *port;
OMX_ERRORTYPE err;
GstVideoCodecState *state;
OMX_PARAM_PORTDEFINITIONTYPE port_def;
GstVideoFormat format;
/* At this point the decoder output port is disabled */
#ifdef USE_OMX_TARGET_RPI
{
OMX_STATETYPE egl_state;
if (self->eglimage) {
/* Nothing to do here, we could however fall back to non-EGLImage in theory */
err = OMX_ErrorNone;
goto enable_port;
} else {
/* Set up egl_render */
self->eglimage = TRUE;
gst_omx_port_get_port_definition (self->dec_out_port, &port_def);
GST_VIDEO_DECODER_STREAM_LOCK (self);
state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
GST_VIDEO_FORMAT_RGBA, port_def.format.video.nFrameWidth,
port_def.format.video.nFrameHeight, self->input_state);
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
gst_video_codec_state_unref (state);
GST_ERROR_OBJECT (self, "Failed to negotiate RGBA for EGLImage");
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
goto no_egl;
}
gst_video_codec_state_unref (state);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
/* Now link it all together */
err = gst_omx_port_set_enabled (self->egl_in_port, FALSE);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_wait_enabled (self->egl_in_port, 1 * GST_SECOND);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_set_enabled (self->egl_out_port, FALSE);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_wait_enabled (self->egl_out_port, 1 * GST_SECOND);
if (err != OMX_ErrorNone)
goto no_egl;
{
#define OMX_IndexParamBrcmVideoEGLRenderDiscardMode 0x7f0000dc
OMX_CONFIG_PORTBOOLEANTYPE discardMode;
memset (&discardMode, 0, sizeof (discardMode));
discardMode.nSize = sizeof (discardMode);
discardMode.nPortIndex = 220;
discardMode.nVersion.nVersion = OMX_VERSION;
discardMode.bEnabled = OMX_FALSE;
if (gst_omx_component_set_parameter (self->egl_render,
OMX_IndexParamBrcmVideoEGLRenderDiscardMode,
&discardMode) != OMX_ErrorNone)
goto no_egl;
#undef OMX_IndexParamBrcmVideoEGLRenderDiscardMode
}
err =
gst_omx_component_setup_tunnel (self->dec, self->dec_out_port,
self->egl_render, self->egl_in_port);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_set_enabled (self->egl_in_port, TRUE);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_wait_enabled (self->egl_in_port, 1 * GST_SECOND);
if (err != OMX_ErrorNone)
goto no_egl;
if (gst_omx_component_get_state (self->egl_render,
GST_CLOCK_TIME_NONE) != OMX_StateIdle)
goto no_egl;
err = gst_omx_video_dec_allocate_output_buffers (self);
if (err != OMX_ErrorNone)
goto no_egl;
if (gst_omx_component_set_state (self->egl_render,
OMX_StateExecuting) != OMX_ErrorNone)
goto no_egl;
if (gst_omx_component_get_state (self->egl_render,
GST_CLOCK_TIME_NONE) != OMX_StateExecuting)
goto no_egl;
err =
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE);
if (err != OMX_ErrorNone)
goto no_egl;
err =
gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, FALSE);
if (err != OMX_ErrorNone)
goto no_egl;
err =
gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, FALSE);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_populate (self->egl_out_port);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_set_enabled (self->dec_out_port, TRUE);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_wait_enabled (self->dec_out_port, 1 * GST_SECOND);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_mark_reconfigured (self->dec_out_port);
if (err != OMX_ErrorNone)
goto no_egl;
err = gst_omx_port_mark_reconfigured (self->egl_out_port);
if (err != OMX_ErrorNone)
goto no_egl;
goto done;
}
no_egl:
gst_omx_port_set_enabled (self->dec_out_port, FALSE);
gst_omx_port_wait_enabled (self->dec_out_port, 1 * GST_SECOND);
egl_state = gst_omx_component_get_state (self->egl_render, 0);
if (egl_state > OMX_StateLoaded || egl_state == OMX_StateInvalid) {
if (egl_state > OMX_StateIdle) {
gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND);
}
gst_omx_component_set_state (self->egl_render, OMX_StateLoaded);
gst_omx_video_dec_deallocate_output_buffers (self);
gst_omx_component_close_tunnel (self->dec, self->dec_out_port,
self->egl_render, self->egl_in_port);
if (egl_state > OMX_StateLoaded) {
gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND);
}
}
/* After this egl_render should be deactivated
* and the decoder's output port disabled */
self->eglimage = FALSE;
}
#endif
port = self->dec_out_port;
/* Update caps */
GST_VIDEO_DECODER_STREAM_LOCK (self);
gst_omx_port_get_port_definition (port, &port_def);
g_assert (port_def.format.video.eCompressionFormat == OMX_VIDEO_CodingUnused);
switch (port_def.format.video.eColorFormat) {
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420PackedPlanar:
GST_DEBUG_OBJECT (self, "Output is I420 (%d)",
port_def.format.video.eColorFormat);
format = GST_VIDEO_FORMAT_I420;
break;
case OMX_COLOR_FormatYUV420SemiPlanar:
GST_DEBUG_OBJECT (self, "Output is NV12 (%d)",
port_def.format.video.eColorFormat);
format = GST_VIDEO_FORMAT_NV12;
break;
default:
GST_ERROR_OBJECT (self, "Unsupported color format: %d",
port_def.format.video.eColorFormat);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
err = OMX_ErrorUndefined;
goto done;
break;
}
GST_DEBUG_OBJECT (self,
"Setting output state: format %s, width %d, height %d",
gst_video_format_to_string (format),
port_def.format.video.nFrameWidth, port_def.format.video.nFrameHeight);
state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
format, port_def.format.video.nFrameWidth,
port_def.format.video.nFrameHeight, self->input_state);
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
gst_video_codec_state_unref (state);
GST_ERROR_OBJECT (self, "Failed to negotiate");
err = OMX_ErrorUndefined;
goto done;
}
gst_video_codec_state_unref (state);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
#ifdef USE_OMX_TARGET_RPI
enable_port:
#endif
err = gst_omx_video_dec_allocate_output_buffers (self);
if (err != OMX_ErrorNone)
goto done;
err = gst_omx_port_populate (port);
if (err != OMX_ErrorNone)
goto done;
err = gst_omx_port_mark_reconfigured (port);
if (err != OMX_ErrorNone)
goto done;
done:
return err;
}
@ -1348,7 +1836,7 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self)
static void
gst_omx_video_dec_loop (GstOMXVideoDec * self)
{
GstOMXPort *port = self->dec_out_port;
GstOMXPort *port;
GstOMXBuffer *buf = NULL;
GstVideoCodecFrame *frame;
GstFlowReturn flow_ret = GST_FLOW_OK;
@ -1356,6 +1844,12 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
GstClockTimeDiff deadline;
OMX_ERRORTYPE err;
#ifdef USE_OMX_TARGET_RPI
port = self->eglimage ? self->egl_out_port : self->dec_out_port;
#else
port = self->dec_out_port;
#endif
acq_return = gst_omx_port_acquire_buffer (port, &buf);
if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) {
goto component_error;
@ -1393,68 +1887,63 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
goto reconfigure_error;
}
GST_VIDEO_DECODER_STREAM_LOCK (self);
if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) {
/* We have the possibility to reconfigure everything now */
err = gst_omx_video_dec_reconfigure_output_port (self);
if (err != OMX_ErrorNone)
goto reconfigure_error;
} else {
/* Just update caps */
GST_VIDEO_DECODER_STREAM_LOCK (self);
gst_omx_port_get_port_definition (port, &port_def);
g_assert (port_def.format.video.eCompressionFormat ==
OMX_VIDEO_CodingUnused);
gst_omx_port_get_port_definition (port, &port_def);
g_assert (port_def.format.video.eCompressionFormat ==
OMX_VIDEO_CodingUnused);
switch (port_def.format.video.eColorFormat) {
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420PackedPlanar:
GST_DEBUG_OBJECT (self, "Output is I420 (%d)",
port_def.format.video.eColorFormat);
format = GST_VIDEO_FORMAT_I420;
break;
case OMX_COLOR_FormatYUV420SemiPlanar:
GST_DEBUG_OBJECT (self, "Output is NV12 (%d)",
port_def.format.video.eColorFormat);
format = GST_VIDEO_FORMAT_NV12;
break;
default:
GST_ERROR_OBJECT (self, "Unsupported color format: %d",
port_def.format.video.eColorFormat);
switch (port_def.format.video.eColorFormat) {
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420PackedPlanar:
GST_DEBUG_OBJECT (self, "Output is I420 (%d)",
port_def.format.video.eColorFormat);
format = GST_VIDEO_FORMAT_I420;
break;
case OMX_COLOR_FormatYUV420SemiPlanar:
GST_DEBUG_OBJECT (self, "Output is NV12 (%d)",
port_def.format.video.eColorFormat);
format = GST_VIDEO_FORMAT_NV12;
break;
default:
GST_ERROR_OBJECT (self, "Unsupported color format: %d",
port_def.format.video.eColorFormat);
if (buf)
gst_omx_port_release_buffer (self->dec_out_port, buf);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
goto caps_failed;
break;
}
GST_DEBUG_OBJECT (self,
"Setting output state: format %s, width %d, height %d",
gst_video_format_to_string (format),
port_def.format.video.nFrameWidth,
port_def.format.video.nFrameHeight);
state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
format, port_def.format.video.nFrameWidth,
port_def.format.video.nFrameHeight, self->input_state);
/* Take framerate and pixel-aspect-ratio from sinkpad caps */
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
if (buf)
gst_omx_port_release_buffer (self->dec_out_port, buf);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
gst_video_codec_state_unref (state);
goto caps_failed;
break;
}
}
GST_DEBUG_OBJECT (self,
"Setting output state: format %s, width %d, height %d",
gst_video_format_to_string (format),
port_def.format.video.nFrameWidth, port_def.format.video.nFrameHeight);
state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
format, port_def.format.video.nFrameWidth,
port_def.format.video.nFrameHeight, self->input_state);
/* Take framerate and pixel-aspect-ratio from sinkpad caps */
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
if (buf)
gst_omx_port_release_buffer (self->dec_out_port, buf);
gst_video_codec_state_unref (state);
goto caps_failed;
}
gst_video_codec_state_unref (state);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) {
err = gst_omx_video_dec_allocate_output_buffers (self);
if (err != OMX_ErrorNone)
goto reconfigure_error;
err = gst_omx_port_populate (port);
if (err != OMX_ErrorNone)
goto reconfigure_error;
err = gst_omx_port_mark_reconfigured (port);
if (err != OMX_ErrorNone)
goto reconfigure_error;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
}
/* Now get a buffer */
@ -1489,7 +1978,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
GST_TIME_ARGS (-deadline));
flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame);
frame = NULL;
} else if (!frame && buf->omx_buf->nFilledLen > 0) {
} else if (!frame && (buf->omx_buf->nFilledLen > 0 || buf->eglimage)) {
GstBuffer *outbuf;
/* This sometimes happens at EOS or if the input is not properly framed,
@ -1532,7 +2021,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
}
flow_ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (self), outbuf);
} else if (buf->omx_buf->nFilledLen > 0) {
} else if (buf->omx_buf->nFilledLen > 0 || buf->eglimage) {
if (self->out_port_pool) {
gint i, n;
GstBufferPoolAcquireParams params = { 0, };
@ -1748,10 +2237,19 @@ gst_omx_video_dec_stop (GstVideoDecoder * decoder)
gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE);
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
#ifdef USE_OMX_TARGET_RPI
gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE);
gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE);
#endif
gst_pad_stop_task (GST_VIDEO_DECODER_SRC_PAD (decoder));
if (gst_omx_component_get_state (self->dec, 0) > OMX_StateIdle)
gst_omx_component_set_state (self->dec, OMX_StateIdle);
#ifdef USE_OMX_TARGET_RPI
if (gst_omx_component_get_state (self->egl_render, 0) > OMX_StateIdle)
gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
#endif
self->downstream_flow_ret = GST_FLOW_FLUSHING;
self->started = FALSE;
@ -1763,6 +2261,9 @@ gst_omx_video_dec_stop (GstVideoDecoder * decoder)
g_mutex_unlock (&self->drain_lock);
gst_omx_component_get_state (self->dec, 5 * GST_SECOND);
#ifdef USE_OMX_TARGET_RPI
gst_omx_component_get_state (self->egl_render, 1 * GST_SECOND);
#endif
gst_buffer_replace (&self->codec_data, NULL);
@ -1790,12 +2291,17 @@ video_negotiation_map_free (VideoNegotiationMap * m)
static GList *
gst_omx_video_dec_get_supported_colorformats (GstOMXVideoDec * self)
{
GstOMXPort *port = self->dec_out_port;
GstOMXComponent *comp;
GstOMXPort *port;
GstVideoCodecState *state = self->input_state;
OMX_VIDEO_PARAM_PORTFORMATTYPE param;
OMX_ERRORTYPE err;
GList *negotiation_map = NULL;
gint old_index;
VideoNegotiationMap *m;
port = self->dec_out_port;
comp = self->dec;
GST_OMX_INIT_STRUCT (&param);
param.nPortIndex = port->index;
@ -1807,10 +2313,8 @@ gst_omx_video_dec_get_supported_colorformats (GstOMXVideoDec * self)
old_index = -1;
do {
VideoNegotiationMap *m;
err =
gst_omx_component_get_parameter (self->dec,
gst_omx_component_get_parameter (comp,
OMX_IndexParamVideoPortFormat, &param);
/* FIXME: Workaround for Bellagio that simply always
@ -1997,10 +2501,17 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder,
}
if (needs_disable && is_format_change) {
#ifdef USE_OMX_TARGET_RPI
GstOMXPort *out_port =
self->eglimage ? self->egl_out_port : self->dec_out_port;
#else
GstOMXPort *out_port = self->dec_out_port;
#endif
GST_DEBUG_OBJECT (self, "Need to disable and drain decoder");
gst_omx_video_dec_drain (self, FALSE);
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
gst_omx_port_set_flushing (out_port, 5 * GST_SECOND, TRUE);
/* Wait until the srcpad loop is finished,
* unlock GST_VIDEO_DECODER_STREAM_LOCK to prevent deadlocks
@ -2019,26 +2530,82 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder,
return FALSE;
needs_disable = FALSE;
} else {
if (gst_omx_port_set_enabled (self->dec_in_port, FALSE) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_set_enabled (self->dec_out_port, FALSE) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_buffers_released (self->dec_in_port,
5 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_buffers_released (self->dec_out_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_deallocate_buffers (self->dec_in_port) != OMX_ErrorNone)
return FALSE;
if (gst_omx_video_dec_deallocate_output_buffers (self) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_enabled (self->dec_in_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_enabled (self->dec_out_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
#ifdef USE_OMX_TARGET_RPI
if (self->eglimage) {
OMX_STATETYPE egl_state;
if (gst_omx_port_set_enabled (self->dec_in_port,
FALSE) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_set_enabled (self->dec_out_port,
FALSE) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_buffers_released (self->dec_in_port,
5 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_buffers_released (self->dec_out_port,
5 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_deallocate_buffers (self->dec_in_port) !=
OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_enabled (self->dec_in_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_enabled (self->dec_out_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
egl_state = gst_omx_component_get_state (self->egl_render, 0);
if (egl_state > OMX_StateLoaded || egl_state == OMX_StateInvalid) {
if (egl_state > OMX_StateIdle) {
gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND);
}
gst_omx_component_set_state (self->egl_render, OMX_StateLoaded);
gst_omx_video_dec_deallocate_output_buffers (self);
gst_omx_component_close_tunnel (self->dec, self->dec_out_port,
self->egl_render, self->egl_in_port);
if (egl_state > OMX_StateLoaded) {
gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND);
}
}
self->eglimage = FALSE;
} else {
#else
{
if (gst_omx_port_set_enabled (self->dec_in_port,
FALSE) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_set_enabled (out_port, FALSE) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_buffers_released (self->dec_in_port,
5 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_buffers_released (out_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_deallocate_buffers (self->dec_in_port) !=
OMX_ErrorNone)
return FALSE;
if (gst_omx_video_dec_deallocate_output_buffers (self) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_enabled (self->dec_in_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
if (gst_omx_port_wait_enabled (out_port,
1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
#endif
}
}
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
@ -2154,6 +2721,11 @@ gst_omx_video_dec_reset (GstVideoDecoder * decoder, gboolean hard)
gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE);
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
#ifdef USE_OMX_TARGET_RPI
gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE);
gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE);
#endif
/* Wait until the srcpad loop is finished,
* unlock GST_VIDEO_DECODER_STREAM_LOCK to prevent deadlocks
* caused by using this lock from inside the loop function */
@ -2166,6 +2738,11 @@ gst_omx_video_dec_reset (GstVideoDecoder * decoder, gboolean hard)
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE);
gst_omx_port_populate (self->dec_out_port);
#ifdef USE_OMX_TARGET_RPI
gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, FALSE);
gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, FALSE);
#endif
/* Start the srcpad loop again */
self->last_upstream_ts = 0;
self->eos = FALSE;
@ -2567,6 +3144,35 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
GstBufferPool *pool;
GstStructure *config;
#ifdef USE_OMX_TARGET_RPI
{
GstCaps *caps;
gint i, n;
GstVideoInfo info;
gst_query_parse_allocation (query, &caps, NULL);
if (caps && gst_video_info_from_caps (&info, caps)
&& info.finfo->format == GST_VIDEO_FORMAT_RGBA) {
/* Prefer an EGLImage allocator if available and we want to use it */
n = gst_query_get_n_allocation_params (query);
for (i = 0; i < n; i++) {
GstAllocator *allocator;
GstAllocationParams params;
gst_query_parse_nth_allocation_param (query, i, &allocator, &params);
if (allocator
&& g_strcmp0 (allocator->mem_type,
GST_EGL_IMAGE_MEMORY_TYPE) == 0) {
gst_query_set_nth_allocation_param (query, 0, allocator, &params);
while (gst_query_get_n_allocation_params (query) > 1)
gst_query_remove_nth_allocation_param (query, 1);
break;
}
}
}
}
#endif
if (!GST_VIDEO_DECODER_CLASS
(gst_omx_video_dec_parent_class)->decide_allocation (bdec, query))
return FALSE;

View file

@ -21,6 +21,10 @@
#ifndef __GST_OMX_VIDEO_DEC_H__
#define __GST_OMX_VIDEO_DEC_H__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideodecoder.h>
@ -52,7 +56,7 @@ struct _GstOMXVideoDec
/* < protected > */
GstOMXComponent *dec;
GstOMXPort *dec_in_port, *dec_out_port;
GstBufferPool *in_port_pool, *out_port_pool;
/* < private > */
@ -74,6 +78,11 @@ struct _GstOMXVideoDec
gboolean eos;
GstFlowReturn downstream_flow_ret;
#ifdef USE_OMX_TARGET_RPI
GstOMXComponent *egl_render;
GstOMXPort *egl_in_port, *egl_out_port;
gboolean eglimage;
#endif
};
struct _GstOMXVideoDecClass