gstreamer/subprojects/gst-plugins-bad/sys/va/gstvabase.c

216 lines
6.1 KiB
C

/* GStreamer
* Copyright (C) 2023 Igalia, S.L.
* Author: Víctor Jáquez <vjaquez@igalia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstvabase.h"
#include <gst/video/video.h>
#include <gst/va/gstvavideoformat.h>
#include <gst/va/vasurfaceimage.h>
#define GST_CAT_DEFAULT (importer->debug_category)
/* big bad mutex to exclusive access to shared stream buffers, such as
* DMABuf after a tee */
static GRecMutex GST_VA_SHARED_LOCK = { 0, };
static gboolean
_try_import_dmabuf_unlocked (GstVaBufferImporter * importer, GstBuffer * inbuf)
{
GstVideoMeta *meta;
GstVideoInfoDmaDrm drm_info = *importer->in_drm_info;
GstMemory *mems[GST_VIDEO_MAX_PLANES];
guint i, n_planes, usage_hint;
gsize offset[GST_VIDEO_MAX_PLANES];
uintptr_t fd[GST_VIDEO_MAX_PLANES];
/* This will eliminate most non-dmabuf out there */
if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
return FALSE;
n_planes = GST_VIDEO_INFO_N_PLANES (&drm_info.vinfo);
meta = gst_buffer_get_video_meta (inbuf);
/* Update video info importerd on video meta */
if (meta) {
GST_VIDEO_INFO_WIDTH (&drm_info.vinfo) = meta->width;
GST_VIDEO_INFO_HEIGHT (&drm_info.vinfo) = meta->height;
g_assert (n_planes == meta->n_planes);
for (i = 0; i < n_planes; i++) {
GST_VIDEO_INFO_PLANE_OFFSET (&drm_info.vinfo, i) = meta->offset[i];
GST_VIDEO_INFO_PLANE_STRIDE (&drm_info.vinfo, i) = meta->stride[i];
}
}
/* Find and validate all memories */
for (i = 0; i < n_planes; i++) {
guint length;
guint mem_idx;
gsize mem_skip;
if (!gst_buffer_find_memory (inbuf,
GST_VIDEO_INFO_PLANE_OFFSET (&drm_info.vinfo, i), 1, &mem_idx,
&length, &mem_skip))
return FALSE;
/* We can't have more then one dmabuf per plane */
if (length != 1)
return FALSE;
mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
/* And all memory found must be dmabuf */
if (!gst_is_dmabuf_memory (mems[i]))
return FALSE;
offset[i] = mems[i]->offset + mem_skip;
fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
}
usage_hint = va_get_surface_usage_hint (importer->display,
importer->entrypoint, GST_PAD_SINK, TRUE);
/* Now create a VASurfaceID for the buffer */
return gst_va_dmabuf_memories_setup (importer->display, &drm_info, mems, fd,
offset, usage_hint);
}
static gboolean
_try_import_buffer (GstVaBufferImporter * importer, GstBuffer * inbuf)
{
VASurfaceID surface;
gboolean ret;
surface = gst_va_buffer_get_surface (inbuf);
if (surface != VA_INVALID_ID &&
(gst_va_buffer_peek_display (inbuf) == importer->display))
return TRUE;
g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
ret = _try_import_dmabuf_unlocked (importer, inbuf);
g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
return ret;
}
GstFlowReturn
gst_va_buffer_importer_import (GstVaBufferImporter * importer,
GstBuffer * inbuf, GstBuffer ** outbuf)
{
GstBuffer *buffer = NULL;
GstBufferPool *pool;
GstFlowReturn ret;
GstVideoFrame in_frame, out_frame;
gboolean imported, copied;
imported = _try_import_buffer (importer, inbuf);
if (imported) {
*outbuf = gst_buffer_ref (inbuf);
return GST_FLOW_OK;
}
/* input buffer doesn't come from a vapool, thus it is required to
* have a pool, grab from it a new buffer and copy the input
* buffer to the new one */
if (!(pool = importer->get_sinkpad_pool (importer->element,
importer->pool_data)))
return GST_FLOW_ERROR;
ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
if (ret != GST_FLOW_OK)
return ret;
GST_LOG_OBJECT (importer->element, "copying input frame");
if (!gst_video_frame_map (&in_frame, importer->in_info, inbuf, GST_MAP_READ))
goto invalid_buffer;
if (!gst_video_frame_map (&out_frame, importer->sinkpad_info, buffer,
GST_MAP_WRITE)) {
gst_video_frame_unmap (&in_frame);
goto invalid_buffer;
}
copied = gst_video_frame_copy (&out_frame, &in_frame);
gst_video_frame_unmap (&out_frame);
gst_video_frame_unmap (&in_frame);
if (!copied)
goto invalid_buffer;
if (!gst_buffer_copy_into (buffer, inbuf, GST_BUFFER_COPY_FLAGS |
GST_BUFFER_COPY_TIMESTAMPS, 0, -1)) {
GST_WARNING_OBJECT (importer->element,
"Couldn't import buffer flags and timestamps");
}
*outbuf = buffer;
return GST_FLOW_OK;
invalid_buffer:
{
GST_ELEMENT_WARNING (importer->element, STREAM, FORMAT, (NULL),
("invalid video buffer received"));
if (buffer)
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
}
gboolean
gst_va_base_convert_caps_to_va (GstCaps * caps)
{
g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
/* For DMA buffer, we can only import linear buffers. Replace the drm-format
* into format field. */
if (gst_video_is_dma_drm_caps (caps)) {
GstVideoInfoDmaDrm dma_info;
GstVideoInfo info;
if (!gst_video_info_dma_drm_from_caps (&dma_info, caps))
return FALSE;
if (dma_info.drm_modifier != DRM_FORMAT_MOD_LINEAR)
return FALSE;
if (!gst_va_dma_drm_info_to_video_info (&dma_info, &info))
return FALSE;
gst_caps_set_simple (caps, "format", G_TYPE_STRING,
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&info)), NULL);
gst_structure_remove_field (gst_caps_get_structure (caps, 0), "drm-format");
}
gst_caps_set_features_simple (caps,
gst_caps_features_new_single_static_str (GST_CAPS_FEATURE_MEMORY_VA));
return TRUE;
}