summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Isorce <julien.isorce@collabora.co.uk>2014-01-20 16:55:54 +0000
committerJosep Torra <n770galaxy@gmail.com>2014-03-10 15:58:45 +0100
commit50a954824a8a7770b53961efff06886d3d6815a5 (patch)
tree589f4a23a7f8d510f7dbe615d988a8ffbc02a628
parentb86b62818dbce5625258a9918a1d43f953755289 (diff)
omxvideodec: integrate resize component on RPIresize
It enables the use of ximagesink on RPI. Or any video sink that does not use GL on RPI. Total CPU load at 20% for a 640x360 video. Total CPU load at 60% for 1280x720 video. Mainly because it does colorspace conversion. It allows to support more downstream elements (or make them useable because it drastically reduces CPU load) Ex: Allow to output RGB16 if downstream does not support I420 and if "video_decode" can only output I420. Only available on RPI (passing --with-omx-target=rpi to the configure script) because I only tested on RPI and also because I'm sure if the resize element is available on other platform. And for now it's instanciated using its name directly: "OMX.broadcom.resize" But you does not necessarly need to have GST_EGL as it's the case to use "OMX.broadcom.egl_render" First, omxvideodec tries "egl_render" (if egl available) then if it fails it directly tries "video_decode" as useall but then if it fails again it tries "resize" with a compatible downstream colorformat. When trying the resize component it try to do OMX_UseBuffer (usefull to use downstream XImage create using XShmCreateImage) and if it fails to actually use those downstream buffers then it fallbacks to OMX_AllocateBuffer. Ex: Allow to have zero-copy with omxh264dec ! ximagesink (OMX_UseBuffer) but omxh264dec ! tee ! ximagesink will do OMX_AllocateBuffer because tee does not forward the allocation query.
-rw-r--r--omx/gstomxvideodec.c1151
-rw-r--r--omx/gstomxvideodec.h11
2 files changed, 1070 insertions, 92 deletions
diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c
index fcacb1c..d30ba64 100644
--- a/omx/gstomxvideodec.c
+++ b/omx/gstomxvideodec.c
@@ -54,6 +54,8 @@
GST_DEBUG_CATEGORY_STATIC (gst_omx_video_dec_debug_category);
#define GST_CAT_DEFAULT gst_omx_video_dec_debug_category
+static gboolean gst_omx_video_dec_negotiate (GstOMXVideoDec * self);
+
typedef struct _GstOMXMemory GstOMXMemory;
typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator;
typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass;
@@ -307,36 +309,59 @@ gst_omx_buffer_pool_get_options (GstBufferPool * bpool)
{ GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
static const gchar *options[] = { NULL };
GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
+ gboolean use_resizer = FALSE;
+ gboolean use_raw_video_options = FALSE;
GST_OBJECT_LOCK (pool);
- if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
- && pool->port->port_def.format.video.eCompressionFormat ==
- OMX_VIDEO_CodingUnused) {
- GST_OBJECT_UNLOCK (pool);
- return raw_video_options;
+
+#if defined (USE_OMX_TARGET_RPI)
+ use_resizer = GST_OMX_VIDEO_DEC (pool->element)->use_resizer;
+#endif
+
+ if (use_resizer) { /* image */
+ if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainImage
+ && pool->port->port_def.format.image.eCompressionFormat ==
+ OMX_IMAGE_CodingUnused) {
+ use_raw_video_options = TRUE;
+ }
+ } else { /* video */
+ if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
+ && pool->port->port_def.format.video.eCompressionFormat ==
+ OMX_VIDEO_CodingUnused) {
+ use_raw_video_options = TRUE;
+ }
}
GST_OBJECT_UNLOCK (pool);
- return options;
+ return (use_raw_video_options ? raw_video_options : options);
}
static gboolean
gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
{
GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
- GstCaps *caps;
+ gboolean use_resizer = FALSE;
+ GstCaps *caps = NULL;
GST_OBJECT_LOCK (pool);
+#if defined (USE_OMX_TARGET_RPI)
+ use_resizer = GST_OMX_VIDEO_DEC (pool->element)->use_resizer;
+#endif
+
if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
goto wrong_config;
if (caps == NULL)
goto no_caps;
- if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
- && pool->port->port_def.format.video.eCompressionFormat ==
- OMX_VIDEO_CodingUnused) {
+ if ((!use_resizer && pool->port
+ && pool->port->port_def.eDomain == OMX_PortDomainVideo
+ && pool->port->port_def.format.video.eCompressionFormat ==
+ OMX_VIDEO_CodingUnused) || (use_resizer && pool->port
+ && pool->port->port_def.eDomain == OMX_PortDomainImage
+ && pool->port->port_def.format.image.eCompressionFormat ==
+ OMX_IMAGE_CodingUnused)) {
GstVideoInfo info;
/* now parse the caps from the config */
@@ -725,6 +750,13 @@ gst_omx_video_dec_init (GstOMXVideoDec * self)
{
gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
+#ifdef USE_OMX_TARGET_RPI
+ /* initially just try to use the decoder component alone */
+ self->try_resizer = FALSE;
+ self->use_resizer = FALSE;
+ self->resizer_padding_bottom = 0;
+#endif
+
g_mutex_init (&self->drain_lock);
g_cond_init (&self->drain_cond);
}
@@ -784,7 +816,8 @@ gst_omx_video_dec_open (GstVideoDecoder * decoder)
GST_DEBUG_OBJECT (self, "Opened decoder");
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
GST_DEBUG_OBJECT (self, "Opening EGL renderer");
self->egl_render =
gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name,
@@ -831,6 +864,52 @@ gst_omx_video_dec_open (GstVideoDecoder * decoder)
GST_DEBUG_OBJECT (self, "Opened EGL renderer");
#endif
+ GST_DEBUG_OBJECT (self, "Opening resize component");
+ self->resizer =
+ gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name,
+ "OMX.broadcom.resize", NULL, klass->cdata.hacks);
+
+ if (!self->resizer)
+ return FALSE;
+
+ if (gst_omx_component_get_state (self->resizer,
+ GST_CLOCK_TIME_NONE) != OMX_StateLoaded)
+ return FALSE;
+
+ {
+ OMX_PORT_PARAM_TYPE param;
+ OMX_ERRORTYPE err;
+
+ GST_OMX_INIT_STRUCT (&param);
+
+ /* image param */
+ err =
+ gst_omx_component_get_parameter (self->resizer,
+ OMX_IndexParamImageInit, &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->res_in_port = gst_omx_component_add_port (self->resizer, in_port_index);
+ self->res_out_port =
+ gst_omx_component_add_port (self->resizer, out_port_index);
+
+ if (!self->res_in_port || !self->res_out_port)
+ return FALSE;
+
+ GST_DEBUG_OBJECT (self, "Opened resize component");
+#endif
+
return TRUE;
}
@@ -841,7 +920,8 @@ gst_omx_video_dec_shutdown (GstOMXVideoDec * self)
GST_DEBUG_OBJECT (self, "Shutting down decoder");
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
state = gst_omx_component_get_state (self->egl_render, 0);
if (state > OMX_StateLoaded || state == OMX_StateInvalid) {
if (state > OMX_StateIdle) {
@@ -866,6 +946,30 @@ gst_omx_video_dec_shutdown (GstOMXVideoDec * self)
/* Otherwise we didn't use EGL and just fall back to
* shutting down the decoder */
#endif
+ state = gst_omx_component_get_state (self->resizer, 0);
+ if (state > OMX_StateLoaded || state == OMX_StateInvalid) {
+ if (state > OMX_StateIdle) {
+ gst_omx_component_set_state (self->resizer, OMX_StateIdle);
+ gst_omx_component_set_state (self->dec, OMX_StateIdle);
+ gst_omx_component_get_state (self->resizer, 5 * GST_SECOND);
+ gst_omx_component_get_state (self->dec, 1 * GST_SECOND);
+ }
+ gst_omx_component_set_state (self->resizer, 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->resizer, self->res_in_port);
+ if (state > OMX_StateLoaded) {
+ gst_omx_component_get_state (self->resizer, 5 * GST_SECOND);
+ gst_omx_component_get_state (self->dec, 1 * GST_SECOND);
+ }
+ }
+
+ /* Otherwise we didn't use RESIZER 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) {
@@ -899,13 +1003,20 @@ gst_omx_video_dec_close (GstVideoDecoder * decoder)
gst_omx_component_free (self->dec);
self->dec = NULL;
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
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->res_in_port = NULL;
+ self->res_out_port = NULL;
+ if (self->resizer)
+ gst_omx_component_free (self->resizer);
+ self->resizer = NULL;
+#endif
self->started = FALSE;
@@ -950,12 +1061,18 @@ 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);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
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
+ if (self->res_in_port)
+ gst_omx_port_set_flushing (self->res_in_port, 5 * GST_SECOND, TRUE);
+ if (self->res_out_port)
+ gst_omx_port_set_flushing (self->res_out_port, 5 * GST_SECOND, TRUE);
+#endif
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
@@ -1181,23 +1298,97 @@ done:
return ret;
}
+#if defined (USE_OMX_TARGET_RPI)
+static OMX_ERRORTYPE
+gst_omx_video_dec_set_resizer_params (GstOMXVideoDec * self,
+ const GstVideoInfo * info)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+ OMX_PARAM_RESIZETYPE param;
+
+ g_return_val_if_fail (self, OMX_ErrorUndefined);
+ g_return_val_if_fail (self->res_out_port, OMX_ErrorUndefined);
+
+ GST_OMX_INIT_STRUCT (&param);
+
+ param.nPortIndex = self->res_out_port->index;
+ param.eMode = OMX_RESIZE_BOX;
+ param.nMaxWidth = GST_VIDEO_INFO_WIDTH (info);
+ param.nMaxHeight = GST_VIDEO_INFO_HEIGHT (info);
+ param.nMaxBytes = 0;
+ param.bAllowUpscaling = TRUE;
+ param.bPreserveAspectRatio = FALSE;
+
+ err =
+ gst_omx_component_set_parameter (self->resizer, OMX_IndexParamResize,
+ &param);
+
+ if (err != OMX_ErrorNone) {
+ GST_INFO_OBJECT (self, "Failed to set resize params: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ return err;
+ }
+
+ return OMX_ErrorNone;
+}
+
+static OMX_ERRORTYPE
+gst_omx_video_dec_set_resize_out_port (GstOMXVideoDec * self,
+ const GstVideoInfo * info)
+{
+ GstOMXPort *port = NULL;
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ g_return_val_if_fail (self, OMX_ErrorUndefined);
+ g_return_val_if_fail (info, OMX_ErrorUndefined);
+ g_return_val_if_fail (self->res_out_port, OMX_ErrorUndefined);
+
+ port = self->res_out_port;
+
+ port->port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
+ port->port_def.format.image.eColorFormat = self->resize_color;
+ port->port_def.format.image.nFrameWidth = GST_VIDEO_INFO_WIDTH (info);
+ port->port_def.format.image.nFrameHeight =
+ GST_VIDEO_INFO_HEIGHT (info) + self->resizer_padding_bottom;
+ port->port_def.format.image.nStride = GST_VIDEO_INFO_PLANE_STRIDE (info, 0);
+ port->port_def.format.image.nSliceHeight = 0;
+ port->port_def.format.image.bFlagErrorConcealment = OMX_FALSE;
+
+ err = gst_omx_port_update_port_definition (port, &port->port_def);
+ if (err != OMX_ErrorNone) {
+ GST_INFO_OBJECT (self, "Failed to set resizer output port: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ return err;
+ }
+
+ err = gst_omx_video_dec_set_resizer_params (self, info);
+
+ return err;
+}
+#endif
+
static OMX_ERRORTYPE
gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
{
OMX_ERRORTYPE err = OMX_ErrorNone;
- GstOMXPort *port;
+ GstOMXPort *port = self->dec_out_port;
GstBufferPool *pool;
GstStructure *config;
gboolean eglimage = FALSE, add_videometa = FALSE;
+ gboolean do_resize = FALSE;
+ gboolean try_allocate = FALSE;
GstCaps *caps = NULL;
guint min = 0, max = 0;
GstVideoCodecState *state =
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self));
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- port = self->eglimage ? self->egl_out_port : self->dec_out_port;
-#else
- port = self->dec_out_port;
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage)
+ port = self->egl_out_port;
+#endif
+ if (self->use_resizer)
+ port = self->res_out_port;
#endif
pool = gst_video_decoder_get_buffer_pool (GST_VIDEO_DECODER (self));
@@ -1223,9 +1414,12 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
GST_BUFFER_POOL_OPTION_VIDEO_META);
gst_structure_free (config);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
eglimage = self->eglimage && (allocator
&& g_strcmp0 (allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0);
+#endif
+ do_resize = self->use_resizer;
#else
/* TODO: Implement something that works for other targets too */
eglimage = FALSE;
@@ -1241,7 +1435,8 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
GST_DEBUG_OBJECT (self, "No pool available, not negotiated yet");
}
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
/* Will retry without EGLImage */
if (self->eglimage && !eglimage) {
GST_DEBUG_OBJECT (self,
@@ -1250,12 +1445,27 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
goto done;
}
#endif
+ if (self->use_resizer && !do_resize) {
+ GST_DEBUG_OBJECT (self,
+ "Wanted to use RESIZER 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);
-
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+ if (caps) {
+#if defined (USE_OMX_TARGET_RPI)
+ if (self->use_resizer)
+ self->out_port_pool =
+ gst_omx_buffer_pool_new (GST_ELEMENT_CAST (self), self->resizer,
+ port);
+ else
+#endif
+ self->out_port_pool =
+ gst_omx_buffer_pool_new (GST_ELEMENT_CAST (self), self->dec, port);
+ }
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (eglimage) {
GList *buffers = NULL;
GList *images = NULL;
@@ -1391,12 +1601,193 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
}
}
}
-#endif
+#endif /* HAVE_GST_EGL */
- /* If not using EGLImage or trying to use EGLImage failed */
- if (!eglimage) {
+ if (do_resize) {
+ GList *buffers = NULL;
+ GList *images = NULL;
+ gint i;
+ GstBufferPoolAcquireParams params = { 0, };
+
+ GST_DEBUG_OBJECT (self, "Trying to resize and use %d buffers", min);
+
+ for (i = 0; i < min; i++) {
+ GstBuffer *buffer = NULL;
+ GstMemory *mem = NULL;
+ GstMapInfo info;
+
+ 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))) {
+ /* buffer does not match minimal requirement to try OMX_UseBuffer */
+ GST_INFO_OBJECT (self, "Failed to allocated %d-th buffer %s", i,
+ mem->allocator->mem_type);
+ g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
+ g_list_free (images);
+ buffers = NULL;
+ images = NULL;
+ err = OMX_ErrorUndefined;
+ try_allocate = TRUE;
+ goto do_resize_done;
+ break;
+ } else {
+ /* if downstream pool is 1 n_mem then always try to use buffers
+ * and retry without using them if it fails */
+ buffers = g_list_append (buffers, buffer);
+
+ gst_buffer_map (buffer, &info, GST_MAP_WRITE);
+ images = g_list_append (images, info.data);
+ gst_buffer_unmap (buffer, &info);
+ }
+ }
+
+ GST_DEBUG_OBJECT (self, "Allocated %d buffers successfully", min);
+
+ /* Everything went fine? */
+ if (do_resize) {
+ GstVideoInfo *info = &state->info;
+
+ err = gst_omx_video_dec_set_resize_out_port (self, info);
+
+ if (err != OMX_ErrorNone) {
+ GST_INFO_OBJECT (self,
+ "Failed to configure output 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);
+ 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 %u 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);
+ 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);
+ goto done;
+ }
+ }
+
+ /* buffers match minimal requirements then
+ * now try to actually use them */
+ err = gst_omx_port_use_buffers (port, images);
+ g_list_free (images);
+
+ if (err != OMX_ErrorNone) {
+ GST_INFO_OBJECT (self,
+ "Failed to OMX_UseBuffer on port: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref);
+ try_allocate = TRUE;
+ goto do_resize_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);
+ 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 */
+ }
+ }
+ }
+
+/* useful when resize component coud not or failed to do OMX_UseBuffer
+ * but it still has a chance to do OMX_AllocateBuffer */
+do_resize_done:
+#endif /* USE_OMX_TARGET_RPI */
+
+ /* if no eglimage and no resizing then always try to allocate
+ * Note that in the resizing case we may fallback to try
+ * to allocate (see above) */
+ if (!eglimage && !do_resize)
+ try_allocate = TRUE;
+
+ /* If not using EGLImage or resize cannot use buffers */
+ if (try_allocate) {
gboolean was_enabled = TRUE;
+#if defined (USE_OMX_TARGET_RPI)
+ GstVideoInfo *info = &state->info;
+
+ if (gst_omx_port_is_enabled (port)) {
+ /* it means that gst_omx_port_use_buffers failed */
+ err = gst_omx_port_set_enabled (port, FALSE);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self,
+ "Failed to disable resize output port %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ goto done;
+ }
+
+ err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self,
+ "Failed to released buffers on resize output port %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ goto done;
+ }
+
+ err = gst_omx_port_deallocate_buffers (port);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self,
+ "Failed to deallocate buffers on resize output port %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ goto done;
+ }
+
+ err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self,
+ "Failed to wait disable for resize output port %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ goto done;
+ }
+ }
+
+ if (do_resize) {
+ err = gst_omx_video_dec_set_resize_out_port (self, info);
+ if (err != OMX_ErrorNone) {
+ GST_INFO_OBJECT (self,
+ "Failed to configure resize output port: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ goto done;
+ }
+ }
+#endif
+
if (min != port->port_def.nBufferCountActual) {
err = gst_omx_port_update_port_definition (port, NULL);
if (err == OMX_ErrorNone) {
@@ -1469,21 +1860,23 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self)
goto done;
}
}
-
-
}
err = OMX_ErrorNone;
if (caps) {
+ guint bufsize = self->dec_out_port->port_def.nBufferSize;;
config = gst_buffer_pool_get_config (self->out_port_pool);
if (add_videometa)
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
- gst_buffer_pool_config_set_params (config, caps,
- self->dec_out_port->port_def.nBufferSize, min, max);
+#if defined (USE_OMX_TARGET_RPI)
+ if (self->use_resizer)
+ bufsize = self->res_out_port->port_def.nBufferSize;
+#endif
+ gst_buffer_pool_config_set_params (config, caps, bufsize, min, max);
if (!gst_buffer_pool_set_config (self->out_port_pool, config)) {
GST_INFO_OBJECT (self, "Failed to set config on internal pool");
@@ -1525,6 +1918,7 @@ static OMX_ERRORTYPE
gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self)
{
OMX_ERRORTYPE err;
+ GstOMXPort *port = self->dec_out_port;
if (self->out_port_pool) {
gst_buffer_pool_set_active (self->out_port_pool, FALSE);
@@ -1535,14 +1929,17 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self)
gst_object_unref (self->out_port_pool);
self->out_port_pool = NULL;
}
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- 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);
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage)
+ port = self->egl_out_port;
+#endif
+ if (self->use_resizer)
+ port = self->res_out_port;
#endif
+ err = gst_omx_port_deallocate_buffers (port);
+
return err;
}
@@ -1557,7 +1954,8 @@ gst_omx_video_dec_reconfigure_output_port (GstOMXVideoDec * self)
/* At this point the decoder output port is disabled */
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
{
OMX_STATETYPE egl_state;
@@ -1719,6 +2117,202 @@ gst_omx_video_dec_reconfigure_output_port (GstOMXVideoDec * self)
self->eglimage = FALSE;
}
#endif
+
+ if (self->try_resizer) {
+ OMX_STATETYPE res_state;
+
+ if (self->use_resizer) {
+ /* Nothing to do here */
+ err = OMX_ErrorNone;
+ port = self->res_out_port;
+ goto enable_port;
+ } else {
+ GstStructure *config = NULL;
+ GstBufferPool *pool = NULL;
+ GstVideoInfo videoinfo;
+ gst_video_info_init (&videoinfo);
+
+ /* Set up resize component */
+ self->use_resizer = TRUE;
+
+ /* use decoder output port because resizer may choose a random output size
+ * and round up height to 16 because resize component only support that */
+ gst_omx_port_get_port_definition (self->dec_out_port, &port_def);
+
+ GST_VIDEO_DECODER_STREAM_LOCK (self);
+
+ if (self->resize_width <= 0 || self->resize_height <= 0) {
+ self->resize_width = port_def.format.video.nFrameWidth;
+ self->resize_height = port_def.format.video.nFrameHeight;
+ }
+
+ state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
+ self->resize_format, self->resize_width,
+ self->resize_height, 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 resizer");
+ GST_VIDEO_DECODER_STREAM_UNLOCK (self);
+ goto no_res;
+ }
+
+ gst_video_codec_state_unref (state);
+ state = NULL;
+
+ pool = gst_video_decoder_get_buffer_pool (GST_VIDEO_DECODER (self));
+ config = gst_buffer_pool_get_config (pool);
+ gst_object_unref (pool);
+
+ /* resizer only support height been multiple of 16 */
+ if (!gst_buffer_pool_config_has_option (config,
+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)
+ && self->resize_height != GST_ROUND_UP_16 (self->resize_height)) {
+ GST_DEBUG_OBJECT (self, "negotiated pool do not support alignment");
+
+ self->resizer_padding_bottom = 0;
+
+ /* use GST_ROUND_UP_16 this time */
+ state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
+ self->resize_format, self->resize_width,
+ GST_ROUND_UP_16 (self->resize_height), self->input_state);
+
+ if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
+ gst_video_codec_state_unref (state);
+ GST_ERROR_OBJECT (self, "Failed to re-negotiate resizer");
+ GST_VIDEO_DECODER_STREAM_UNLOCK (self);
+ gst_structure_free (config);
+ goto no_res;
+ }
+
+ gst_video_codec_state_unref (state);
+ }
+ gst_structure_free (config);
+
+ /* Now link it all together */
+
+ GST_VIDEO_DECODER_STREAM_UNLOCK (self);
+ err = gst_omx_port_set_enabled (self->res_in_port, FALSE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_wait_enabled (self->res_in_port, 1 * GST_SECOND);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_set_enabled (self->res_out_port, FALSE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_wait_enabled (self->res_out_port, 1 * GST_SECOND);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err =
+ gst_omx_component_setup_tunnel (self->dec, self->dec_out_port,
+ self->resizer, self->res_in_port);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_set_enabled (self->res_in_port, TRUE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_component_set_state (self->resizer, OMX_StateIdle);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+ err = gst_omx_port_wait_enabled (self->res_in_port, 1 * GST_SECOND);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ if (gst_omx_component_get_state (self->resizer,
+ GST_CLOCK_TIME_NONE) != OMX_StateIdle)
+ goto no_res;
+
+ err = gst_omx_video_dec_allocate_output_buffers (self);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ if (gst_omx_component_set_state (self->resizer,
+ OMX_StateExecuting) != OMX_ErrorNone)
+ goto no_res;
+
+ if (gst_omx_component_get_state (self->resizer,
+ GST_CLOCK_TIME_NONE) != OMX_StateExecuting)
+ goto no_res;
+
+ err =
+ gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err =
+ gst_omx_port_set_flushing (self->res_in_port, 5 * GST_SECOND, FALSE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err =
+ gst_omx_port_set_flushing (self->res_out_port, 5 * GST_SECOND, FALSE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_populate (self->res_out_port);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_set_enabled (self->dec_out_port, TRUE);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_wait_enabled (self->dec_out_port, 1 * GST_SECOND);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_mark_reconfigured (self->dec_out_port);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ err = gst_omx_port_mark_reconfigured (self->res_out_port);
+ if (err != OMX_ErrorNone)
+ goto no_res;
+
+ goto done;
+ }
+
+ no_res:
+
+ gst_omx_port_set_enabled (self->dec_out_port, FALSE);
+ gst_omx_port_wait_enabled (self->dec_out_port, 1 * GST_SECOND);
+
+ gst_omx_port_set_enabled (self->res_out_port, FALSE);
+ gst_omx_port_wait_enabled (self->res_out_port, 1 * GST_SECOND);
+
+ gst_omx_port_set_enabled (self->res_in_port, FALSE);
+ gst_omx_port_wait_enabled (self->res_in_port, 1 * GST_SECOND);
+
+ res_state = gst_omx_component_get_state (self->resizer, 0);
+ if (res_state > OMX_StateLoaded || res_state == OMX_StateInvalid) {
+ if (res_state > OMX_StateIdle) {
+ gst_omx_component_set_state (self->resizer, OMX_StateIdle);
+ gst_omx_component_get_state (self->resizer, 5 * GST_SECOND);
+ }
+ gst_omx_component_set_state (self->resizer, OMX_StateLoaded);
+
+ gst_omx_video_dec_deallocate_output_buffers (self);
+ gst_omx_component_close_tunnel (self->dec, self->dec_out_port,
+ self->resizer, self->res_in_port);
+
+ if (res_state > OMX_StateLoaded) {
+ gst_omx_component_get_state (self->resizer, 5 * GST_SECOND);
+ }
+ }
+
+ /* After this resizer should be deactivated
+ * and the decoder's output port disabled */
+ self->use_resizer = FALSE;
+ }
+#endif
+
port = self->dec_out_port;
/* Update caps */
@@ -1769,7 +2363,7 @@ gst_omx_video_dec_reconfigure_output_port (GstOMXVideoDec * self)
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
enable_port:
#endif
err = gst_omx_video_dec_allocate_output_buffers (self);
@@ -1792,7 +2386,7 @@ done:
static void
gst_omx_video_dec_loop (GstOMXVideoDec * self)
{
- GstOMXPort *port;
+ GstOMXPort *port = self->dec_out_port;
GstOMXBuffer *buf = NULL;
GstVideoCodecFrame *frame;
GstFlowReturn flow_ret = GST_FLOW_OK;
@@ -1800,10 +2394,13 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
GstClockTimeDiff deadline;
OMX_ERRORTYPE err;
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- port = self->eglimage ? self->egl_out_port : self->dec_out_port;
-#else
- port = self->dec_out_port;
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage)
+ port = self->egl_out_port;
+#endif
+ if (self->use_resizer)
+ port = self->res_out_port;
#endif
acq_return = gst_omx_port_acquire_buffer (port, &buf);
@@ -2200,18 +2797,34 @@ 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);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- 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);
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage) {
+ 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);
+ } else
+#endif
+ if (self->use_resizer) {
+ gst_omx_port_set_flushing (self->res_in_port, 5 * GST_SECOND, TRUE);
+ gst_omx_port_set_flushing (self->res_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);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- if (gst_omx_component_get_state (self->egl_render, 0) > OMX_StateIdle)
- gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage) {
+ if (gst_omx_component_get_state (self->egl_render, 0) > OMX_StateIdle)
+ gst_omx_component_set_state (self->egl_render, OMX_StateIdle);
+ } else
+#endif
+ if (self->use_resizer) {
+ if (gst_omx_component_get_state (self->resizer, 0) > OMX_StateIdle)
+ gst_omx_component_set_state (self->resizer, OMX_StateIdle);
+ }
#endif
self->downstream_flow_ret = GST_FLOW_FLUSHING;
@@ -2224,8 +2837,14 @@ gst_omx_video_dec_stop (GstVideoDecoder * decoder)
g_mutex_unlock (&self->drain_lock);
gst_omx_component_get_state (self->dec, 5 * GST_SECOND);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- gst_omx_component_get_state (self->egl_render, 1 * GST_SECOND);
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage)
+ gst_omx_component_get_state (self->egl_render, 1 * GST_SECOND);
+ else
+#endif
+ if (self->use_resizer)
+ gst_omx_component_get_state (self->resizer, 1 * GST_SECOND);
#endif
gst_buffer_replace (&self->codec_data, NULL);
@@ -2251,8 +2870,124 @@ video_negotiation_map_free (VideoNegotiationMap * m)
g_slice_free (VideoNegotiationMap, m);
}
+#ifdef USE_OMX_TARGET_RPI
static GList *
-gst_omx_video_dec_get_supported_colorformats (GstOMXVideoDec * self)
+gst_omx_video_dec_get_supported_image_colorformats (GstOMXVideoDec * self)
+{
+ GstOMXComponent *comp;
+ GstOMXPort *port;
+ OMX_IMAGE_PARAM_PORTFORMATTYPE param;
+ OMX_ERRORTYPE err;
+ GList *negotiation_map = NULL;
+ gint old_index;
+ VideoNegotiationMap *m;
+
+ port = self->res_out_port;
+ comp = self->resizer;
+
+ GST_OMX_INIT_STRUCT (&param);
+ param.nPortIndex = port->index;
+ param.nIndex = 0;
+
+ old_index = -1;
+ do {
+ err =
+ gst_omx_component_get_parameter (comp,
+ OMX_IndexParamImagePortFormat, &param);
+
+ /* FIXME: Workaround for Bellagio that simply always
+ * returns the same value regardless of nIndex and
+ * never returns OMX_ErrorNoMore
+ */
+ if (old_index == param.nIndex)
+ break;
+
+ if (err == OMX_ErrorNone || err == OMX_ErrorNoMore) {
+ switch (param.eColorFormat) {
+ case OMX_COLOR_FormatYUV420Planar:
+ case OMX_COLOR_FormatYUV420PackedPlanar:
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_I420;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports I420 (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ case OMX_COLOR_FormatYUV420SemiPlanar:
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_NV12;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports NV12 (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ case OMX_COLOR_Format16bitRGB565:
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_RGB16;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports RGB16 (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ case OMX_COLOR_Format16bitBGR565:
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_BGR16;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports BGR16 (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ case OMX_COLOR_Format32bitARGB8888:
+ /* RPI hack */
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_BGRx;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_BGRA;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports ARGB (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ case OMX_COLOR_Format32bitABGR8888:
+ /* RPI hack */
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_RGBx;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_RGBA;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports ABGR (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ case OMX_COLOR_Format32bitBGRA8888:
+ m = g_slice_new (VideoNegotiationMap);
+ m->format = GST_VIDEO_FORMAT_BGRA;
+ m->type = param.eColorFormat;
+ negotiation_map = g_list_append (negotiation_map, m);
+ GST_DEBUG_OBJECT (self, "Component supports BGRA (%d) at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ default:
+ GST_DEBUG_OBJECT (self,
+ "Component supports unsupported color format %d at index %u",
+ param.eColorFormat, (guint) param.nIndex);
+ break;
+ }
+ }
+ old_index = param.nIndex++;
+ } while (err == OMX_ErrorNone);
+
+ return negotiation_map;
+}
+#endif
+
+
+static GList *
+gst_omx_video_dec_get_supported_video_colorformats (GstOMXVideoDec * self)
{
GstOMXComponent *comp;
GstOMXPort *port;
@@ -2322,14 +3057,23 @@ gst_omx_video_dec_get_supported_colorformats (GstOMXVideoDec * self)
static gboolean
gst_omx_video_dec_negotiate (GstOMXVideoDec * self)
{
- OMX_VIDEO_PARAM_PORTFORMATTYPE param;
+ OMX_VIDEO_PARAM_PORTFORMATTYPE param_video;
OMX_ERRORTYPE err;
GstCaps *comp_supported_caps;
- GList *negotiation_map = NULL, *l;
+ GList *video_negotiation_map = NULL;
+ GList *l = NULL;
GstCaps *templ_caps, *intersection;
GstVideoFormat format;
GstStructure *s;
const gchar *format_str;
+ gboolean has_fixed_size = TRUE;
+ gint width = 0;
+ gint height = 0;
+
+#ifdef USE_OMX_TARGET_RPI
+ GList *image_negotiation_map = NULL;
+ OMX_IMAGE_PARAM_PORTFORMATTYPE param_image;
+#endif
GST_DEBUG_OBJECT (self, "Trying to negotiate a video format with downstream");
@@ -2341,9 +3085,11 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self)
GST_DEBUG_OBJECT (self, "Allowed downstream caps: %" GST_PTR_FORMAT,
intersection);
- negotiation_map = gst_omx_video_dec_get_supported_colorformats (self);
+ video_negotiation_map =
+ gst_omx_video_dec_get_supported_video_colorformats (self);
+
comp_supported_caps = gst_caps_new_empty ();
- for (l = negotiation_map; l; l = l->next) {
+ for (l = video_negotiation_map; l; l = l->next) {
VideoNegotiationMap *map = l->data;
gst_caps_append_structure (comp_supported_caps,
@@ -2352,26 +3098,72 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self)
gst_video_format_to_string (map->format), NULL));
}
+#ifdef USE_OMX_TARGET_RPI
+ image_negotiation_map =
+ gst_omx_video_dec_get_supported_image_colorformats (self);
+
+ comp_supported_caps = gst_caps_new_empty ();
+ for (l = image_negotiation_map; l; l = l->next) {
+ VideoNegotiationMap *map = l->data;
+
+ gst_caps_append_structure (comp_supported_caps,
+ gst_structure_new ("video/x-raw",
+ "format", G_TYPE_STRING,
+ gst_video_format_to_string (map->format), NULL));
+ }
+#endif
+
+ /* respect prefered caps */
if (!gst_caps_is_empty (comp_supported_caps)) {
- GstCaps *tmp;
+ gint n = gst_caps_get_size (intersection);
+ gint i = 0;
+ GstCaps *tmp = NULL;
+
+ /* caps i is prefered over caps i+1 */
+ for (i = 0; i < n; ++i) {
+ GstCaps *caps_i = gst_caps_copy_nth (intersection, i);
+ tmp = gst_caps_intersect (comp_supported_caps, caps_i);
+ gst_caps_unref (caps_i);
+
+ if (!gst_caps_is_empty (tmp)) {
+ /* downstream caps_i is supported */
+ break;
+ }
+
+ gst_caps_unref (tmp);
+ tmp = NULL;
+ }
- tmp = gst_caps_intersect (comp_supported_caps, intersection);
gst_caps_unref (intersection);
- intersection = tmp;
+ intersection = tmp ? tmp : gst_caps_new_empty ();
}
gst_caps_unref (comp_supported_caps);
if (gst_caps_is_empty (intersection)) {
gst_caps_unref (intersection);
- GST_ERROR_OBJECT (self, "Empty caps");
- g_list_free_full (negotiation_map,
+ g_list_free_full (video_negotiation_map,
(GDestroyNotify) video_negotiation_map_free);
+#ifdef USE_OMX_TARGET_RPI
+ g_list_free_full (image_negotiation_map,
+ (GDestroyNotify) video_negotiation_map_free);
+#endif
+
+ GST_ERROR_OBJECT (self, "Empty caps");
+
return FALSE;
}
intersection = gst_caps_truncate (intersection);
+
+ /* retrieve fixed width and/or height set by downstream */
+ s = gst_caps_get_structure (intersection, 0);
+ has_fixed_size = gst_structure_get_int (s, "width", &width);
+ has_fixed_size &= gst_structure_get_int (s, "height", &height);
+
intersection = gst_caps_fixate (intersection);
+ GST_DEBUG_OBJECT (self, "fixated caps: %" GST_PTR_FORMAT, intersection);
+
s = gst_caps_get_structure (intersection, 0);
format_str = gst_structure_get_string (s, "format");
if (!format_str ||
@@ -2380,39 +3172,98 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self)
GST_VIDEO_FORMAT_UNKNOWN) {
GST_ERROR_OBJECT (self, "Invalid caps: %" GST_PTR_FORMAT, intersection);
gst_caps_unref (intersection);
- g_list_free_full (negotiation_map,
+ g_list_free_full (video_negotiation_map,
(GDestroyNotify) video_negotiation_map_free);
+#ifdef USE_OMX_TARGET_RPI
+ g_list_free_full (image_negotiation_map,
+ (GDestroyNotify) video_negotiation_map_free);
+#endif
+ gst_caps_unref (intersection);
return FALSE;
}
gst_caps_unref (intersection);
- GST_OMX_INIT_STRUCT (&param);
- param.nPortIndex = self->dec_out_port->index;
-
- for (l = negotiation_map; l; l = l->next) {
+ GST_OMX_INIT_STRUCT (&param_video);
+ param_video.nPortIndex = self->dec_out_port->index;
+ for (l = video_negotiation_map; l; l = l->next) {
VideoNegotiationMap *m = l->data;
if (m->format == format) {
- param.eColorFormat = m->type;
- break;
+ if (!has_fixed_size) {
+ param_video.eColorFormat = m->type;
+ GST_DEBUG_OBJECT (self, "Negotiating color format %s (%d)", format_str,
+ param_video.eColorFormat);
+ break;
+ } else {
+ GST_DEBUG_OBJECT (self, "Had the right format but not the size");
+ }
}
}
- GST_DEBUG_OBJECT (self, "Negotiating color format %s (%d)", format_str,
- param.eColorFormat);
+#ifdef USE_OMX_TARGET_RPI
+ /* we prefer to use the video_decode component alone
+ * if possible that why we tried its negotiation map
+ * first. Try the resize negotiation map if not found. */
+ if (!l) {
+ GST_OMX_INIT_STRUCT (&param_image);
+ param_image.nPortIndex = self->res_out_port->index;
+ for (l = image_negotiation_map; l; l = l->next) {
+ VideoNegotiationMap *m = l->data;
+
+ if (m->format == format) {
+ param_image.eColorFormat = m->type;
+ self->try_resizer = TRUE;
+ GST_DEBUG_OBJECT (self, "Negotiating color format %s (%d)", format_str,
+ param_image.eColorFormat);
+ break;
+ }
+ }
+ }
+#endif
/* We must find something here */
g_assert (l != NULL);
- g_list_free_full (negotiation_map,
+ g_list_free_full (video_negotiation_map,
(GDestroyNotify) video_negotiation_map_free);
+#ifdef USE_OMX_TARGET_RPI
+ g_list_free_full (image_negotiation_map,
+ (GDestroyNotify) video_negotiation_map_free);
+#endif
- err =
- gst_omx_component_set_parameter (self->dec,
- OMX_IndexParamVideoPortFormat, &param);
- if (err != OMX_ErrorNone) {
- GST_ERROR_OBJECT (self, "Failed to set video port format: %s (0x%08x)",
- gst_omx_error_to_string (err), err);
+#ifdef USE_OMX_TARGET_RPI
+ if (self->try_resizer) {
+ err =
+ gst_omx_component_set_parameter (self->resizer,
+ OMX_IndexParamImagePortFormat, &param_image);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self, "Failed to set image port format: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ }
+
+ self->resize_color = param_image.eColorFormat;
+ self->resize_format = format;
+ if (has_fixed_size) {
+ self->resize_width = width;
+ self->resize_height = height;
+ } else {
+ /* get width height from decoder later */
+ self->resize_width = 0;
+ self->resize_height = 0;
+ }
+ } else {
+#endif
+ err =
+ gst_omx_component_set_parameter (self->dec,
+ OMX_IndexParamVideoPortFormat, &param_video);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self, "Failed to set video port format: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ }
+#ifdef USE_OMX_TARGET_RPI
}
+#endif
+
+ gst_caps_unref (intersection);
return (err == OMX_ErrorNone);
}
@@ -2466,11 +3317,14 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder,
}
if (needs_disable && is_format_change) {
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
- GstOMXPort *out_port =
- self->eglimage ? self->egl_out_port : self->dec_out_port;
-#else
GstOMXPort *out_port = self->dec_out_port;
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
+ if (self->eglimage)
+ out_port = self->egl_out_port;
+#endif
+ if (self->use_resizer)
+ out_port = self->res_out_port;
#endif
GST_DEBUG_OBJECT (self, "Need to disable and drain decoder");
@@ -2495,12 +3349,20 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder,
return FALSE;
needs_disable = FALSE;
} else {
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (self->eglimage) {
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);
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);
+ } else
+#endif
+ if (self->use_resizer) {
+ 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);
+ gst_omx_port_set_flushing (self->res_in_port, 5 * GST_SECOND, TRUE);
+ gst_omx_port_set_flushing (self->res_out_port, 5 * GST_SECOND, TRUE);
}
#endif
@@ -2524,7 +3386,8 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder,
if (gst_omx_port_wait_enabled (out_port, 1 * GST_SECOND) != OMX_ErrorNone)
return FALSE;
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (self->eglimage) {
OMX_STATETYPE egl_state;
@@ -2554,6 +3417,37 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder,
gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE);
}
self->eglimage = FALSE;
+ } else
+#endif
+ if (self->use_resizer) {
+ OMX_STATETYPE res_state;
+
+ res_state = gst_omx_component_get_state (self->resizer, 0);
+ if (res_state > OMX_StateLoaded || res_state == OMX_StateInvalid) {
+
+ if (res_state > OMX_StateIdle) {
+ gst_omx_component_set_state (self->resizer, OMX_StateIdle);
+ gst_omx_component_set_state (self->dec, OMX_StateIdle);
+ res_state = gst_omx_component_get_state (self->resizer,
+ 5 * GST_SECOND);
+ gst_omx_component_get_state (self->dec, 1 * GST_SECOND);
+ }
+ gst_omx_component_set_state (self->resizer, OMX_StateLoaded);
+ gst_omx_component_set_state (self->dec, OMX_StateLoaded);
+
+ gst_omx_component_close_tunnel (self->dec, self->dec_out_port,
+ self->resizer, self->res_in_port);
+
+ if (res_state > OMX_StateLoaded) {
+ gst_omx_component_get_state (self->resizer, 5 * GST_SECOND);
+ }
+
+ gst_omx_component_set_state (self->dec, OMX_StateIdle);
+
+ gst_omx_component_set_state (self->dec, OMX_StateExecuting);
+ gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE);
+ }
+ self->use_resizer = FALSE;
}
#endif
}
@@ -2681,12 +3575,20 @@ gst_omx_video_dec_flush (GstVideoDecoder * decoder)
gst_omx_component_set_state (self->dec, OMX_StatePause);
gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE);
}
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (self->eglimage) {
if (gst_omx_component_get_state (self->egl_render, 0) == OMX_StateExecuting) {
gst_omx_component_set_state (self->egl_render, OMX_StatePause);
gst_omx_component_get_state (self->egl_render, GST_CLOCK_TIME_NONE);
}
+ } else
+#endif
+ if (self->use_resizer) {
+ if (gst_omx_component_get_state (self->resizer, 0) == OMX_StateExecuting) {
+ gst_omx_component_set_state (self->resizer, OMX_StatePause);
+ gst_omx_component_get_state (self->resizer, GST_CLOCK_TIME_NONE);
+ }
}
#endif
@@ -2695,20 +3597,32 @@ gst_omx_video_dec_flush (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);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (self->eglimage) {
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);
+ } else
+#endif
+ if (self->use_resizer) {
+ gst_omx_port_set_flushing (self->res_in_port, 5 * GST_SECOND, TRUE);
+ gst_omx_port_set_flushing (self->res_out_port, 5 * GST_SECOND, TRUE);
}
#endif
/* 3) Resume components */
gst_omx_component_set_state (self->dec, OMX_StateExecuting);
gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (self->eglimage) {
gst_omx_component_set_state (self->egl_render, OMX_StateExecuting);
gst_omx_component_get_state (self->egl_render, GST_CLOCK_TIME_NONE);
+ } else
+#endif
+ if (self->use_resizer) {
+ gst_omx_component_set_state (self->resizer, OMX_StateExecuting);
+ gst_omx_component_get_state (self->resizer, GST_CLOCK_TIME_NONE);
}
#endif
@@ -2716,12 +3630,20 @@ gst_omx_video_dec_flush (GstVideoDecoder * decoder)
gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE);
gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE);
-#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
+#if defined (USE_OMX_TARGET_RPI)
+#if defined (HAVE_GST_EGL)
if (self->eglimage) {
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);
err = gst_omx_port_populate (self->egl_out_port);
gst_omx_port_mark_reconfigured (self->egl_out_port);
+ } else
+#endif
+ if (self->use_resizer) {
+ gst_omx_port_set_flushing (self->res_in_port, 5 * GST_SECOND, FALSE);
+ gst_omx_port_set_flushing (self->res_out_port, 5 * GST_SECOND, FALSE);
+ err = gst_omx_port_populate (self->res_out_port);
+ gst_omx_port_mark_reconfigured (self->res_out_port);
} else {
err = gst_omx_port_populate (self->dec_out_port);
}
@@ -3127,8 +4049,9 @@ gst_omx_video_dec_drain (GstOMXVideoDec * self, gboolean is_eos)
static gboolean
gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
{
- GstBufferPool *pool;
- GstStructure *config;
+ GstBufferPool *pool = NULL;
+ GstStructure *config = NULL;
+ GstOMXVideoDec *self = GST_OMX_VIDEO_DEC (bdec);
#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_EGL)
{
@@ -3169,9 +4092,53 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
config = gst_buffer_pool_get_config (pool);
if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
+
+#if defined (USE_OMX_TARGET_RPI)
+ GstCaps *caps = NULL;
+ GstVideoInfo info;
+ GstVideoAlignment align;
+
+ gst_video_info_init (&info);
+ gst_video_alignment_reset (&align);
+#endif
+
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+#if defined (USE_OMX_TARGET_RPI)
+ /* resizer always output multiple of 16 */
+ if (self->use_resizer) {
+ gst_query_parse_allocation (query, &caps, NULL);
+ if (caps && gst_video_info_from_caps (&info, caps)) {
+ gst_buffer_pool_config_add_option (config,
+ GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+
+ if (GST_ROUND_UP_16 (self->dec_out_port->port_def.format.video.
+ nFrameHeight) >
+ self->dec_out_port->port_def.format.video.nFrameHeight) {
+ /* minimun padding for resizer to work if it actually does scaling */
+ gint min_diff = 8;
+ if ((GST_ROUND_UP_16 (GST_VIDEO_INFO_HEIGHT (&info)) -
+ GST_VIDEO_INFO_HEIGHT (&info)) < min_diff)
+ self->resizer_padding_bottom =
+ GST_ROUND_UP_16 (GST_VIDEO_INFO_HEIGHT (&info) + min_diff) -
+ GST_VIDEO_INFO_HEIGHT (&info);
+ else
+ self->resizer_padding_bottom =
+ GST_ROUND_UP_16 (GST_VIDEO_INFO_HEIGHT (&info)) -
+ GST_VIDEO_INFO_HEIGHT (&info);
+ }
+
+ GST_DEBUG_OBJECT (self, "resizer bottom padding: %d",
+ self->resizer_padding_bottom);
+
+ align.padding_bottom = self->resizer_padding_bottom;
+ gst_buffer_pool_config_set_video_alignment (config, &align);
+ }
+ }
+#endif
}
+
gst_buffer_pool_set_config (pool, config);
gst_object_unref (pool);
diff --git a/omx/gstomxvideodec.h b/omx/gstomxvideodec.h
index 3978865..f192c59 100644
--- a/omx/gstomxvideodec.h
+++ b/omx/gstomxvideodec.h
@@ -79,10 +79,21 @@ struct _GstOMXVideoDec
GstFlowReturn downstream_flow_ret;
#ifdef USE_OMX_TARGET_RPI
+#if defined (HAVE_GST_EGL)
GstOMXComponent *egl_render;
GstOMXPort *egl_in_port, *egl_out_port;
gboolean eglimage;
#endif
+ GstOMXComponent *resizer;
+ GstOMXPort *res_in_port, *res_out_port;
+ gboolean try_resizer;
+ gboolean use_resizer;
+ OMX_COLOR_FORMATTYPE resize_color;
+ GstVideoFormat resize_format;
+ gint resize_width;
+ gint resize_height;
+ gint resizer_padding_bottom;
+#endif
};
struct _GstOMXVideoDecClass