summaryrefslogtreecommitdiff
path: root/omx/gstomx.c
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 /omx/gstomx.c
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
Diffstat (limited to 'omx/gstomx.c')
-rw-r--r--omx/gstomx.c143
1 files changed, 143 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)