summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2017-07-20 16:31:54 +0200
committerJulien Isorce <jisorce@oblong.com>2017-12-14 09:00:02 +0000
commitda07a647b8ee0dbbf59f667e778c7b6b51a5cd7e (patch)
tree654296f6fbc0fcf7812ee43669fef9ad6f3f25a9
parent47c341de217d3160a2cd0ef3ba97964b283d0248 (diff)
downloadgst-omx-da07a647b8ee0dbbf59f667e778c7b6b51a5cd7e.tar.gz
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
-rw-r--r--omx/gstomx.c143
-rw-r--r--omx/gstomx.h23
2 files changed, 166 insertions, 0 deletions
diff --git a/omx/gstomx.c b/omx/gstomx.c
index fc17ac8..8754599 100644
--- a/omx/gstomx.c
+++ b/omx/gstomx.c
@@ -609,6 +609,26 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent,
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
EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE * pBuffer)
@@ -630,6 +650,9 @@ EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
return OMX_ErrorBadParameter;
}
+ /* Release and unmap the parent buffer, if any */
+ gst_omx_buffer_unmap (buf);
+
comp = buf->port->comp;
msg = g_slice_new (GstOMXMessage);
@@ -1751,6 +1774,7 @@ gst_omx_port_allocate_buffers (GstOMXPort * port)
g_mutex_lock (&port->comp->lock);
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);
return err;
@@ -1768,11 +1792,130 @@ gst_omx_port_use_buffers (GstOMXPort * port, const GList * buffers)
g_mutex_lock (&port->comp->lock);
n = g_list_length ((GList *) buffers);
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);
+
+ 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 */
OMX_ERRORTYPE
gst_omx_port_use_eglimages (GstOMXPort * port, const GList * images)
diff --git a/omx/gstomx.h b/omx/gstomx.h
index b6cb57b..e197c96 100644
--- a/omx/gstomx.h
+++ b/omx/gstomx.h
@@ -25,6 +25,7 @@
#include <gmodule.h>
#include <gst/gst.h>
+#include <gst/video/video.h>
#include <string.h>
#ifdef HAVE_CONFIG_H
@@ -220,6 +221,13 @@ typedef enum {
GST_OMX_COMPONENT_TYPE_FILTER
} 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 {
GstOMXMessageType type;
@@ -267,6 +275,7 @@ struct _GstOMXPort {
gboolean enabled_pending; /* TRUE after OMX_Command{En,Dis}able */
gboolean disabled_pending; /* was done until it took effect */
gboolean eos; /* TRUE after a buffer with EOS flag was received */
+ GstOMXBufferAllocation allocation;
/* Increased whenever the settings of these port change.
* If settings_cookie != configured_settings_cookie
@@ -323,6 +332,14 @@ struct _GstOMXBuffer {
/* TRUE if this is an 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 {
@@ -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);
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);