/* GStreamer * Copyright (C) 2023 Igalia, S.L. * Author: Víctor Jáquez * * 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 #include #include #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; GstVideoInfo in_info = *importer->in_info; 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]; gsize plane_size[GST_VIDEO_MAX_PLANES]; GstVideoAlignment align = { 0, }; /* 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 (&in_info); meta = gst_buffer_get_video_meta (inbuf); /* Update video info importerd on video meta */ if (meta) { GST_VIDEO_INFO_WIDTH (&in_info) = meta->width; GST_VIDEO_INFO_HEIGHT (&in_info) = meta->height; g_assert (n_planes == meta->n_planes); for (i = 0; i < n_planes; i++) { GST_VIDEO_INFO_PLANE_OFFSET (&in_info, i) = meta->offset[i]; GST_VIDEO_INFO_PLANE_STRIDE (&in_info, i) = meta->stride[i]; } } if (!gst_video_info_align_full (&in_info, &align, plane_size)) return FALSE; /* 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 (&in_info, i), plane_size[i], &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_from_string (GST_CAPS_FEATURE_MEMORY_VA)); return TRUE; }