omx: add API to implement dynamic buffers support

OMX 1.2.0 introduced a third way to manage buffers by allowing
components to only allocate buffers header during their initialization
and change their pBuffer pointer at runtime.
This new feature can save us a copy between GStreamer and OMX for each
input buffer.

This patch adds API to allocate and use such buffers.

https://bugzilla.gnome.org/show_bug.cgi?id=787093
This commit is contained in:
Guillaume Desmottes 2017-07-20 16:31:54 +02:00 committed by Julien Isorce
parent 47c341de21
commit da07a647b8
2 changed files with 166 additions and 0 deletions

View file

@ -609,6 +609,26 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent,
return OMX_ErrorNone; return OMX_ErrorNone;
} }
static void
gst_omx_buffer_unmap (GstOMXBuffer * buffer)
{
g_return_if_fail (buffer != NULL);
if (buffer->input_frame_mapped) {
g_assert (!buffer->input_mem);
g_assert (!buffer->input_buffer);
gst_video_frame_unmap (&buffer->input_frame);
buffer->input_frame_mapped = FALSE;
} else if (buffer->input_mem) {
g_assert (!buffer->input_buffer);
gst_memory_unmap (buffer->input_mem, &buffer->map);
g_clear_pointer (&buffer->input_mem, gst_memory_unref);
} else if (buffer->input_buffer) {
gst_buffer_unmap (buffer->input_buffer, &buffer->map);
g_clear_pointer (&buffer->input_buffer, gst_buffer_unref);
}
}
static OMX_ERRORTYPE static OMX_ERRORTYPE
EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE * pBuffer) OMX_BUFFERHEADERTYPE * pBuffer)
@ -630,6 +650,9 @@ EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
return OMX_ErrorBadParameter; return OMX_ErrorBadParameter;
} }
/* Release and unmap the parent buffer, if any */
gst_omx_buffer_unmap (buf);
comp = buf->port->comp; comp = buf->port->comp;
msg = g_slice_new (GstOMXMessage); msg = g_slice_new (GstOMXMessage);
@ -1751,6 +1774,7 @@ gst_omx_port_allocate_buffers (GstOMXPort * port)
g_mutex_lock (&port->comp->lock); g_mutex_lock (&port->comp->lock);
err = gst_omx_port_allocate_buffers_unlocked (port, NULL, NULL, -1); err = gst_omx_port_allocate_buffers_unlocked (port, NULL, NULL, -1);
port->allocation = GST_OMX_BUFFER_ALLOCATION_ALLOCATE_BUFFER;
g_mutex_unlock (&port->comp->lock); g_mutex_unlock (&port->comp->lock);
return err; return err;
@ -1768,11 +1792,130 @@ gst_omx_port_use_buffers (GstOMXPort * port, const GList * buffers)
g_mutex_lock (&port->comp->lock); g_mutex_lock (&port->comp->lock);
n = g_list_length ((GList *) buffers); n = g_list_length ((GList *) buffers);
err = gst_omx_port_allocate_buffers_unlocked (port, buffers, NULL, n); err = gst_omx_port_allocate_buffers_unlocked (port, buffers, NULL, n);
port->allocation = GST_OMX_BUFFER_ALLOCATION_USE_BUFFER;
g_mutex_unlock (&port->comp->lock); g_mutex_unlock (&port->comp->lock);
return err; return err;
} }
gboolean
gst_omx_is_dynamic_allocation_supported (void)
{
/* The Zynqultrascaleplus stack implements OMX 1.1.0 but supports the dynamic
* allocation mode from 1.2.0 as an extension. */
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
return TRUE;
#endif
#if OMX_VERSION_MINOR == 2
return TRUE;
#else
return FALSE;
#endif
}
/* OMX 1.2.0 introduced a dynamic allocation mode where only buffer headers are
* being allocated during component's initialization. The actual buffers are
* allocated upstream and passed to OMX by setting the pBuffer dynamically
* for each input buffer.
*
* This function takes care of allocating the buffer headers. Element should
* then use one of the gst_omx_buffer_map_*() method to update buffer's pBuffer
* pointers for each incoming buffer.
*
* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_use_dynamic_buffers (GstOMXPort * port)
{
OMX_ERRORTYPE err;
GList *buffers = NULL;
guint i, n;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
n = port->port_def.nBufferCountActual;
for (i = 0; i < port->port_def.nBufferCountActual; i++)
/* Pass NULL to UseBuffer() as the buffer is dynamic and so its payload
* will be set each time before being passed to OMX. */
buffers = g_list_prepend (buffers, GUINT_TO_POINTER (NULL));
g_mutex_lock (&port->comp->lock);
err = gst_omx_port_allocate_buffers_unlocked (port, buffers, NULL, n);
port->allocation = GST_OMX_BUFFER_ALLOCATION_USE_BUFFER_DYNAMIC;
g_mutex_unlock (&port->comp->lock);
return err;
}
/* gst_omx_buffer_map_* methods are used in dynamic buffer mode to map
* a frame/memory/buffer and update @buffer so its pBuffer points to the
* mapped data. It also ensures that the input will stay alive until
* gst_omx_buffer_unmap() is called.
* This is used in OMX 1.2.0 dynamic allocation mode so an OMX component can
* safely process @buffer's content without having to copy it.
* The input will be automatically unmapped when @buffer is released by OMX.
*/
gboolean
gst_omx_buffer_map_frame (GstOMXBuffer * buffer, GstBuffer * input,
GstVideoInfo * info)
{
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
if (!gst_video_frame_map (&buffer->input_frame, info, input, GST_MAP_READ))
return FALSE;
buffer->input_frame_mapped = TRUE;
buffer->omx_buf->pBuffer =
GST_VIDEO_FRAME_PLANE_DATA (&buffer->input_frame, 0);
buffer->omx_buf->nAllocLen = gst_buffer_get_size (input);
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
gboolean
gst_omx_buffer_map_memory (GstOMXBuffer * buffer, GstMemory * mem)
{
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (mem != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
if (!gst_memory_map (mem, &buffer->map, GST_MAP_READ))
return FALSE;
buffer->input_mem = gst_memory_ref (mem);
buffer->omx_buf->pBuffer = buffer->map.data;
buffer->omx_buf->nAllocLen = buffer->map.size;
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
gboolean
gst_omx_buffer_map_buffer (GstOMXBuffer * buffer, GstBuffer * input)
{
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (input != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
if (!gst_buffer_map (input, &buffer->map, GST_MAP_READ))
return FALSE;
buffer->input_buffer = gst_buffer_ref (input);
buffer->omx_buf->pBuffer = buffer->map.data;
buffer->omx_buf->nAllocLen = buffer->map.size;
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
/* NOTE: Uses comp->lock and comp->messages_lock */ /* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE OMX_ERRORTYPE
gst_omx_port_use_eglimages (GstOMXPort * port, const GList * images) gst_omx_port_use_eglimages (GstOMXPort * port, const GList * images)

View file

@ -25,6 +25,7 @@
#include <gmodule.h> #include <gmodule.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/video/video.h>
#include <string.h> #include <string.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -220,6 +221,13 @@ typedef enum {
GST_OMX_COMPONENT_TYPE_FILTER GST_OMX_COMPONENT_TYPE_FILTER
} GstOmxComponentType; } GstOmxComponentType;
/* How the port's buffers are allocated */
typedef enum {
GST_OMX_BUFFER_ALLOCATION_ALLOCATE_BUFFER,
GST_OMX_BUFFER_ALLOCATION_USE_BUFFER,
GST_OMX_BUFFER_ALLOCATION_USE_BUFFER_DYNAMIC, /* Only supported by OMX 1.2.0 */
} GstOMXBufferAllocation;
struct _GstOMXMessage { struct _GstOMXMessage {
GstOMXMessageType type; GstOMXMessageType type;
@ -267,6 +275,7 @@ struct _GstOMXPort {
gboolean enabled_pending; /* TRUE after OMX_Command{En,Dis}able */ gboolean enabled_pending; /* TRUE after OMX_Command{En,Dis}able */
gboolean disabled_pending; /* was done until it took effect */ gboolean disabled_pending; /* was done until it took effect */
gboolean eos; /* TRUE after a buffer with EOS flag was received */ gboolean eos; /* TRUE after a buffer with EOS flag was received */
GstOMXBufferAllocation allocation;
/* Increased whenever the settings of these port change. /* Increased whenever the settings of these port change.
* If settings_cookie != configured_settings_cookie * If settings_cookie != configured_settings_cookie
@ -323,6 +332,14 @@ struct _GstOMXBuffer {
/* TRUE if this is an EGLImage */ /* TRUE if this is an EGLImage */
gboolean eglimage; gboolean eglimage;
/* Used in dynamic buffer mode to keep track of the mapped content while it's
* being processed by the OMX component. */
GstVideoFrame input_frame;
gboolean input_frame_mapped; /* TRUE if input_frame is valid */
GstMemory *input_mem;
GstBuffer *input_buffer;
GstMapInfo map;
}; };
struct _GstOMXClassData { struct _GstOMXClassData {
@ -397,6 +414,12 @@ OMX_ERRORTYPE gst_omx_port_set_enabled (GstOMXPort * port, gboolean enabled)
OMX_ERRORTYPE gst_omx_port_wait_enabled (GstOMXPort * port, GstClockTime timeout); OMX_ERRORTYPE gst_omx_port_wait_enabled (GstOMXPort * port, GstClockTime timeout);
gboolean gst_omx_port_is_enabled (GstOMXPort * port); gboolean gst_omx_port_is_enabled (GstOMXPort * port);
/* OMX 1.2.0 dynamic allocation mode */
gboolean gst_omx_is_dynamic_allocation_supported (void);
OMX_ERRORTYPE gst_omx_port_use_dynamic_buffers (GstOMXPort * port);
gboolean gst_omx_buffer_map_frame (GstOMXBuffer * buffer, GstBuffer * input, GstVideoInfo * info);
gboolean gst_omx_buffer_map_memory (GstOMXBuffer * buffer, GstMemory * mem);
gboolean gst_omx_buffer_map_buffer (GstOMXBuffer * buffer, GstBuffer * input);
void gst_omx_set_default_role (GstOMXClassData *class_data, const gchar *default_role); void gst_omx_set_default_role (GstOMXClassData *class_data, const gchar *default_role);