diff options
author | Julien Isorce <julien.isorce@collabora.co.uk> | 2014-01-20 16:55:54 +0000 |
---|---|---|
committer | Josep Torra <n770galaxy@gmail.com> | 2014-03-10 15:58:45 +0100 |
commit | 50a954824a8a7770b53961efff06886d3d6815a5 (patch) | |
tree | 589f4a23a7f8d510f7dbe615d988a8ffbc02a628 | |
parent | b86b62818dbce5625258a9918a1d43f953755289 (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.c | 1151 | ||||
-rw-r--r-- | omx/gstomxvideodec.h | 11 |
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 (¶m); + + /* image param */ + err = + gst_omx_component_get_parameter (self->resizer, + OMX_IndexParamImageInit, ¶m); + 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 (¶m); + + 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, + ¶m); + + 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, ¶ms) != 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 (¶m); + param.nPortIndex = port->index; + param.nIndex = 0; + + old_index = -1; + do { + err = + gst_omx_component_get_parameter (comp, + OMX_IndexParamImagePortFormat, ¶m); + + /* 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 (¶m); - param.nPortIndex = self->dec_out_port->index; - - for (l = negotiation_map; l; l = l->next) { + GST_OMX_INIT_STRUCT (¶m_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 (¶m_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, ¶m); - 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, ¶m_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, ¶m_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 |