mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 11:10:37 +00:00
1853dd56a4
Because DXVA does not define JPEG decoding, we need this vendor specific API for Windows Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3316>
848 lines
24 KiB
C++
848 lines
24 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2021 Seungha Yang <seungha@centricular.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 "gstqsvallocator.h"
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_qsv_allocator_debug);
|
|
#define GST_CAT_DEFAULT gst_qsv_allocator_debug
|
|
|
|
/* Both d3d11 and va use (GST_MAP_FLAG_LAST << 1) value
|
|
* for GPU access */
|
|
#define GST_MAP_QSV (GST_MAP_FLAG_LAST << 1)
|
|
|
|
struct _GstQsvFrame
|
|
{
|
|
GstMiniObject parent;
|
|
|
|
GstQsvAllocator *allocator;
|
|
|
|
GMutex lock;
|
|
|
|
guint map_count;
|
|
GstBuffer *buffer;
|
|
GstVideoInfo info;
|
|
GstVideoFrame frame;
|
|
GstQsvMemoryType mem_type;
|
|
GstMapFlags map_flags;
|
|
};
|
|
|
|
GST_DEFINE_MINI_OBJECT_TYPE (GstQsvFrame, gst_qsv_frame);
|
|
|
|
static void
|
|
_gst_qsv_frame_free (GstQsvFrame * frame)
|
|
{
|
|
g_mutex_clear (&frame->lock);
|
|
gst_clear_buffer (&frame->buffer);
|
|
gst_clear_object (&frame->allocator);
|
|
g_free (frame);
|
|
}
|
|
|
|
static GstQsvFrame *
|
|
gst_qsv_frame_new (void)
|
|
{
|
|
GstQsvFrame *self;
|
|
|
|
self = g_new0 (GstQsvFrame, 1);
|
|
g_mutex_init (&self->lock);
|
|
|
|
gst_mini_object_init (GST_MINI_OBJECT_CAST (self), 0,
|
|
GST_TYPE_QSV_FRAME, nullptr, nullptr,
|
|
(GstMiniObjectFreeFunction) _gst_qsv_frame_free);
|
|
|
|
return self;
|
|
}
|
|
|
|
GstBuffer *
|
|
gst_qsv_frame_peek_buffer (GstQsvFrame * frame)
|
|
{
|
|
g_return_val_if_fail (GST_IS_QSV_FRAME (frame), nullptr);
|
|
|
|
return frame->buffer;
|
|
}
|
|
|
|
gboolean
|
|
gst_qsv_frame_set_buffer (GstQsvFrame * frame, GstBuffer * buffer)
|
|
{
|
|
g_return_val_if_fail (GST_IS_QSV_FRAME (frame), FALSE);
|
|
|
|
g_mutex_lock (&frame->lock);
|
|
if (frame->buffer == buffer) {
|
|
g_mutex_unlock (&frame->lock);
|
|
return TRUE;
|
|
}
|
|
|
|
if (frame->map_count > 0) {
|
|
GST_ERROR ("frame is locked");
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gst_clear_buffer (&frame->buffer);
|
|
frame->buffer = buffer;
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct _GstQsvAllocatorPrivate
|
|
{
|
|
GstAtomicQueue *queue;
|
|
|
|
mfxFrameAllocator allocator;
|
|
mfxFrameAllocResponse response;
|
|
guint16 extra_alloc_size;
|
|
gboolean dummy_alloc;
|
|
};
|
|
|
|
#define gst_qsv_allocator_parent_class parent_class
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstQsvAllocator,
|
|
gst_qsv_allocator, GST_TYPE_OBJECT);
|
|
|
|
static void gst_qsv_allocator_finalize (GObject * object);
|
|
static mfxStatus gst_qsv_allocator_alloc (mfxHDL pthis,
|
|
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response);
|
|
static mfxStatus gst_qsv_allocator_lock (mfxHDL pthis, mfxMemId mid,
|
|
mfxFrameData * ptr);
|
|
static mfxStatus gst_qsv_allocator_unlock (mfxHDL pthis, mfxMemId mid,
|
|
mfxFrameData * ptr);
|
|
static mfxStatus gst_qsv_allocator_get_hdl (mfxHDL pthis, mfxMemId mid,
|
|
mfxHDL * handle);
|
|
static mfxStatus gst_qsv_allocator_free (mfxHDL pthis,
|
|
mfxFrameAllocResponse * response);
|
|
static GstBuffer *gst_qsv_allocator_download_default (GstQsvAllocator * self,
|
|
const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
|
|
GstBufferPool * pool);
|
|
|
|
static void
|
|
gst_qsv_allocator_class_init (GstQsvAllocatorClass * klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gst_qsv_allocator_finalize;
|
|
|
|
klass->download = GST_DEBUG_FUNCPTR (gst_qsv_allocator_download_default);
|
|
}
|
|
|
|
static void
|
|
gst_qsv_allocator_init (GstQsvAllocator * self)
|
|
{
|
|
GstQsvAllocatorPrivate *priv;
|
|
|
|
priv = self->priv = (GstQsvAllocatorPrivate *)
|
|
gst_qsv_allocator_get_instance_private (self);
|
|
|
|
priv->queue = gst_atomic_queue_new (16);
|
|
|
|
priv->allocator.pthis = self;
|
|
priv->allocator.Alloc = gst_qsv_allocator_alloc;
|
|
priv->allocator.Lock = gst_qsv_allocator_lock;
|
|
priv->allocator.Unlock = gst_qsv_allocator_unlock;
|
|
priv->allocator.GetHDL = gst_qsv_allocator_get_hdl;
|
|
priv->allocator.Free = gst_qsv_allocator_free;
|
|
}
|
|
|
|
static void
|
|
gst_qsv_allocator_finalize (GObject * object)
|
|
{
|
|
GstQsvAllocator *self = GST_QSV_ALLOCATOR (object);
|
|
GstQsvAllocatorPrivate *priv = self->priv;
|
|
GstQsvFrame *frame;
|
|
|
|
GST_DEBUG_OBJECT (object, "finalize");
|
|
|
|
while ((frame = (GstQsvFrame *) gst_atomic_queue_pop (priv->queue)))
|
|
gst_qsv_frame_unref (frame);
|
|
|
|
gst_atomic_queue_unref (priv->queue);
|
|
gst_qsv_allocator_free ((mfxHDL) self, &priv->response);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static mfxStatus
|
|
gst_qsv_allocator_alloc_default (GstQsvAllocator * self, gboolean dummy_alloc,
|
|
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
|
|
{
|
|
GstQsvFrame **mids = nullptr;
|
|
GstVideoInfo info;
|
|
GstVideoAlignment align;
|
|
GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
|
|
GstBufferPool *pool;
|
|
GstCaps *caps;
|
|
GstStructure *config;
|
|
|
|
/* Something unexpected and went wrong */
|
|
if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) == 0) {
|
|
GST_ERROR_OBJECT (self,
|
|
"MFX is requesting system memory, type 0x%x", request->Type);
|
|
return MFX_ERR_UNSUPPORTED;
|
|
}
|
|
|
|
switch (request->Info.FourCC) {
|
|
case MFX_FOURCC_NV12:
|
|
format = GST_VIDEO_FORMAT_NV12;
|
|
break;
|
|
case MFX_FOURCC_P010:
|
|
format = GST_VIDEO_FORMAT_P010_10LE;
|
|
break;
|
|
case MFX_FOURCC_P016:
|
|
format = GST_VIDEO_FORMAT_P016_LE;
|
|
break;
|
|
case MFX_FOURCC_AYUV:
|
|
format = GST_VIDEO_FORMAT_VUYA;
|
|
break;
|
|
case MFX_FOURCC_Y410:
|
|
format = GST_VIDEO_FORMAT_Y410;
|
|
break;
|
|
case MFX_FOURCC_YUY2:
|
|
format = GST_VIDEO_FORMAT_YUY2;
|
|
break;
|
|
case MFX_FOURCC_RGB4:
|
|
format = GST_VIDEO_FORMAT_BGRA;
|
|
break;
|
|
default:
|
|
/* TODO: add more formats */
|
|
break;
|
|
}
|
|
|
|
if (format == GST_VIDEO_FORMAT_UNKNOWN) {
|
|
GST_ERROR_OBJECT (self, "Unknown MFX format fourcc %" GST_FOURCC_FORMAT,
|
|
GST_FOURCC_ARGS (request->Info.FourCC));
|
|
|
|
return MFX_ERR_UNSUPPORTED;
|
|
}
|
|
|
|
mids = g_new0 (GstQsvFrame *, request->NumFrameSuggested);
|
|
response->NumFrameActual = request->NumFrameSuggested;
|
|
|
|
gst_video_info_set_format (&info,
|
|
format, request->Info.CropW, request->Info.CropH);
|
|
|
|
if (dummy_alloc) {
|
|
for (guint i = 0; i < request->NumFrameSuggested; i++) {
|
|
mids[i] = gst_qsv_allocator_acquire_frame (self,
|
|
GST_QSV_SYSTEM_MEMORY, &info, nullptr, nullptr);
|
|
}
|
|
|
|
response->mids = (mfxMemId *) mids;
|
|
|
|
return MFX_ERR_NONE;
|
|
}
|
|
|
|
caps = gst_video_info_to_caps (&info);
|
|
if (!caps) {
|
|
GST_ERROR_OBJECT (self, "Failed to convert video-info to caps");
|
|
return MFX_ERR_UNSUPPORTED;
|
|
}
|
|
|
|
gst_video_alignment_reset (&align);
|
|
align.padding_right = request->Info.Width - request->Info.CropW;
|
|
align.padding_bottom = request->Info.Height - request->Info.CropH;
|
|
|
|
pool = gst_video_buffer_pool_new ();
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
gst_buffer_pool_config_add_option (config,
|
|
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
|
|
gst_buffer_pool_config_set_video_alignment (config, &align);
|
|
gst_buffer_pool_config_set_params (config, caps, GST_VIDEO_INFO_SIZE (&info),
|
|
0, 0);
|
|
gst_caps_unref (caps);
|
|
gst_buffer_pool_set_config (pool, config);
|
|
gst_buffer_pool_set_active (pool, TRUE);
|
|
|
|
for (guint i = 0; i < request->NumFrameSuggested; i++) {
|
|
GstBuffer *buffer;
|
|
|
|
if (gst_buffer_pool_acquire_buffer (pool, &buffer, nullptr) != GST_FLOW_OK) {
|
|
GST_ERROR_OBJECT (self, "Failed to allocate texture buffer");
|
|
gst_buffer_pool_set_active (pool, FALSE);
|
|
gst_object_unref (pool);
|
|
goto error;
|
|
}
|
|
|
|
mids[i] = gst_qsv_allocator_acquire_frame (self,
|
|
GST_QSV_SYSTEM_MEMORY, &info, buffer, nullptr);
|
|
}
|
|
|
|
gst_buffer_pool_set_active (pool, FALSE);
|
|
gst_object_unref (pool);
|
|
|
|
response->mids = (mfxMemId *) mids;
|
|
|
|
return MFX_ERR_NONE;
|
|
|
|
error:
|
|
if (mids) {
|
|
for (guint i = 0; i < response->NumFrameActual; i++)
|
|
gst_clear_qsv_frame (&mids[i]);
|
|
|
|
g_free (mids);
|
|
}
|
|
|
|
response->NumFrameActual = 0;
|
|
|
|
return MFX_ERR_MEMORY_ALLOC;
|
|
}
|
|
|
|
static gboolean
|
|
gst_qsv_allocator_copy_cached_response (GstQsvAllocator * self,
|
|
mfxFrameAllocResponse * dst, mfxFrameAllocResponse * src)
|
|
{
|
|
GstQsvFrame **mids;
|
|
|
|
if (src->NumFrameActual == 0)
|
|
return FALSE;
|
|
|
|
mids = g_new0 (GstQsvFrame *, src->NumFrameActual);
|
|
|
|
for (guint i = 0; i < src->NumFrameActual; i++) {
|
|
GstQsvFrame *frame = (GstQsvFrame *) src->mids[i];
|
|
|
|
mids[i] = gst_qsv_frame_ref (frame);
|
|
}
|
|
|
|
dst->NumFrameActual = src->NumFrameActual;
|
|
dst->mids = (mfxMemId *) mids;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static mfxStatus
|
|
gst_qsv_allocator_alloc (mfxHDL pthis,
|
|
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
|
|
{
|
|
GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
|
|
GstQsvAllocatorPrivate *priv = self->priv;
|
|
GstQsvAllocatorClass *klass;
|
|
mfxStatus status;
|
|
mfxFrameAllocRequest req = *request;
|
|
gboolean dummy_alloc = priv->dummy_alloc;
|
|
|
|
GST_INFO_OBJECT (self, "Alloc, Request Type: 0x%x, %dx%d (%dx%d)",
|
|
req.Type, req.Info.Width, req.Info.Height,
|
|
req.Info.CropW, req.Info.CropH);
|
|
|
|
/* Apply extra_alloc_size only for GST internal use case */
|
|
if ((request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) != 0)
|
|
req.NumFrameSuggested += priv->extra_alloc_size;
|
|
|
|
if (req.Info.CropW == 0 || req.Info.CropH == 0) {
|
|
req.Info.CropW = req.Info.Width;
|
|
req.Info.CropH = req.Info.Height;
|
|
}
|
|
|
|
if (request->Info.FourCC == MFX_FOURCC_P8 ||
|
|
(request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) == 0) {
|
|
dummy_alloc = FALSE;
|
|
}
|
|
|
|
GST_INFO_OBJECT (self, "Dummy alloc %d", dummy_alloc);
|
|
|
|
if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) != 0) {
|
|
status = gst_qsv_allocator_alloc_default (self,
|
|
dummy_alloc, &req, response);
|
|
} else {
|
|
klass = GST_QSV_ALLOCATOR_GET_CLASS (self);
|
|
g_assert (klass->alloc);
|
|
|
|
status = klass->alloc (self, dummy_alloc, &req, response);
|
|
}
|
|
|
|
if (status != MFX_ERR_NONE)
|
|
return status;
|
|
|
|
/* Cache this respons so that this can be accessible from GST side */
|
|
if (dummy_alloc) {
|
|
gst_qsv_allocator_free ((mfxHDL) self, &priv->response);
|
|
gst_qsv_allocator_copy_cached_response (self, &priv->response, response);
|
|
}
|
|
|
|
return MFX_ERR_NONE;
|
|
}
|
|
|
|
static mfxStatus
|
|
gst_qsv_allocator_lock (mfxHDL pthis, mfxMemId mid, mfxFrameData * ptr)
|
|
{
|
|
GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
|
|
GstQsvFrame *frame = (GstQsvFrame *) mid;
|
|
guint stride;
|
|
|
|
GST_TRACE_OBJECT (self, "Lock mfxMemId %p", mid);
|
|
|
|
g_mutex_lock (&frame->lock);
|
|
if (!frame->buffer) {
|
|
GST_ERROR_OBJECT (self, "MemId %p doesn't hold buffer", mid);
|
|
g_mutex_unlock (&frame->lock);
|
|
return MFX_ERR_LOCK_MEMORY;
|
|
}
|
|
|
|
if (frame->map_count == 0) {
|
|
guint map_flags = (guint) frame->map_flags;
|
|
map_flags &= ~((guint) GST_MAP_QSV);
|
|
|
|
gst_video_frame_map (&frame->frame, &frame->info, frame->buffer,
|
|
(GstMapFlags) map_flags);
|
|
}
|
|
|
|
frame->map_count++;
|
|
stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame->frame, 0);
|
|
|
|
/* FIXME: check and handle other formats */
|
|
switch (GST_VIDEO_INFO_FORMAT (&frame->info)) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_P010_10LE:
|
|
case GST_VIDEO_FORMAT_P016_LE:
|
|
ptr->Pitch = (mfxU16) stride;
|
|
ptr->Y = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
|
|
ptr->UV = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 1);
|
|
break;
|
|
case GST_VIDEO_FORMAT_VUYA:
|
|
ptr->PitchHigh = (mfxU16) (stride / (1 << 16));
|
|
ptr->PitchLow = (mfxU16) (stride % (1 << 16));
|
|
ptr->V = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
|
|
ptr->U = ptr->V + 1;
|
|
ptr->Y = ptr->V + 2;
|
|
ptr->A = ptr->V + 3;
|
|
break;
|
|
case GST_VIDEO_FORMAT_Y410:
|
|
ptr->PitchHigh = (mfxU16) (stride / (1 << 16));
|
|
ptr->PitchLow = (mfxU16) (stride % (1 << 16));
|
|
ptr->Y410 = (mfxY410 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
ptr->Pitch = (mfxU16) stride;
|
|
ptr->B = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
|
|
ptr->G = ptr->B + 1;
|
|
ptr->R = ptr->B + 2;
|
|
ptr->A = ptr->B + 3;
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
ptr->Pitch = (mfxU16) stride;
|
|
ptr->R = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
|
|
ptr->G = ptr->R + 1;
|
|
ptr->B = ptr->R + 2;
|
|
ptr->A = ptr->R + 3;
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
ptr->Pitch = (mfxU16) stride;
|
|
ptr->Y = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
|
|
ptr->U = ptr->Y + 1;
|
|
ptr->V = ptr->Y + 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
return MFX_ERR_NONE;
|
|
}
|
|
|
|
static mfxStatus
|
|
gst_qsv_allocator_unlock (mfxHDL pthis, mfxMemId mid, mfxFrameData * ptr)
|
|
{
|
|
GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
|
|
GstQsvFrame *frame = (GstQsvFrame *) mid;
|
|
|
|
GST_TRACE_OBJECT (self, "Unlock mfxMemId %p", mid);
|
|
|
|
g_mutex_lock (&frame->lock);
|
|
|
|
if (frame->map_count > 0) {
|
|
frame->map_count--;
|
|
|
|
if (frame->map_count == 0)
|
|
gst_video_frame_unmap (&frame->frame);
|
|
} else {
|
|
GST_WARNING_OBJECT (self, "Unlock request for non-locked memory");
|
|
}
|
|
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
/* SDK will not re-lock unless we clear data pointer here. It happens
|
|
* on Linux with BGRA JPEG encoding */
|
|
ptr->R = nullptr;
|
|
ptr->G = nullptr;
|
|
ptr->B = nullptr;
|
|
ptr->A = nullptr;
|
|
|
|
return MFX_ERR_NONE;
|
|
}
|
|
|
|
static mfxStatus
|
|
gst_qsv_allocator_get_hdl (mfxHDL pthis, mfxMemId mid, mfxHDL * handle)
|
|
{
|
|
GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
|
|
GstQsvFrame *frame = GST_QSV_FRAME_CAST (mid);
|
|
GstMapInfo map_info;
|
|
|
|
if (!GST_QSV_MEM_TYPE_IS_VIDEO (frame->mem_type))
|
|
return MFX_ERR_UNSUPPORTED;
|
|
|
|
g_mutex_lock (&frame->lock);
|
|
if (!frame->buffer) {
|
|
GST_ERROR_OBJECT (self, "MemId %p doesn't hold buffer", mid);
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
return MFX_ERR_UNSUPPORTED;
|
|
}
|
|
|
|
g_assert ((frame->map_flags & GST_MAP_QSV) != 0);
|
|
if (!gst_buffer_map (frame->buffer, &map_info, frame->map_flags)) {
|
|
GST_ERROR_OBJECT (self, "Failed to map buffer");
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
return MFX_ERR_UNSUPPORTED;
|
|
}
|
|
|
|
GST_TRACE_OBJECT (self, "Get handle for mfxMemId %p", mid);
|
|
|
|
#ifdef G_OS_WIN32
|
|
mfxHDLPair *pair = (mfxHDLPair *) handle;
|
|
pair->first = (mfxHDL) map_info.data;
|
|
|
|
/* GstD3D11 will fill user_data[0] with subresource index */
|
|
pair->second = (mfxHDL) map_info.user_data[0];
|
|
#else
|
|
*handle = (mfxHDL) map_info.data;
|
|
#endif
|
|
|
|
/* XXX: Ideally we should unmap only when this surface is unlocked... */
|
|
gst_buffer_unmap (frame->buffer, &map_info);
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
return MFX_ERR_NONE;
|
|
}
|
|
|
|
static mfxStatus
|
|
gst_qsv_allocator_free (mfxHDL pthis, mfxFrameAllocResponse * response)
|
|
{
|
|
GstQsvFrame **frames = (GstQsvFrame **) response->mids;
|
|
|
|
for (guint i = 0; i < response->NumFrameActual; i++)
|
|
gst_clear_qsv_frame (&frames[i]);
|
|
|
|
g_clear_pointer (&response->mids, g_free);
|
|
response->NumFrameActual = 0;
|
|
|
|
return MFX_ERR_NONE;
|
|
}
|
|
|
|
static void
|
|
gst_qsv_frame_release (GstQsvFrame * frame)
|
|
{
|
|
GstQsvAllocator *allocator = frame->allocator;
|
|
|
|
g_mutex_lock (&frame->lock);
|
|
if (frame->map_count > 0) {
|
|
GST_WARNING_OBJECT (allocator, "Releasing mapped frame %p", frame);
|
|
gst_video_frame_unmap (&frame->frame);
|
|
}
|
|
frame->map_count = 0;
|
|
gst_clear_buffer (&frame->buffer);
|
|
g_mutex_unlock (&frame->lock);
|
|
|
|
GST_MINI_OBJECT_CAST (frame)->dispose = nullptr;
|
|
frame->allocator = nullptr;
|
|
|
|
GST_TRACE_OBJECT (allocator, "Moving frame %p back to pool", frame);
|
|
|
|
gst_atomic_queue_push (allocator->priv->queue, frame);
|
|
gst_object_unref (allocator);
|
|
}
|
|
|
|
static gboolean
|
|
gst_qsv_frame_dispose (GstQsvFrame * frame)
|
|
{
|
|
g_assert (frame->allocator);
|
|
|
|
gst_qsv_frame_ref (frame);
|
|
gst_qsv_frame_release (frame);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GstBuffer *
|
|
gst_qsv_allocator_upload_default (GstQsvAllocator * allocator,
|
|
const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
|
|
{
|
|
GstBuffer *dst_buf;
|
|
GstFlowReturn flow_ret;
|
|
GstVideoFrame src_frame, dst_frame;
|
|
|
|
flow_ret = gst_buffer_pool_acquire_buffer (pool, &dst_buf, nullptr);
|
|
if (flow_ret != GST_FLOW_OK) {
|
|
GST_WARNING ("Failed to acquire buffer from pool, return %s",
|
|
gst_flow_get_name (flow_ret));
|
|
return nullptr;
|
|
}
|
|
|
|
gst_video_frame_map (&src_frame, info, buffer, GST_MAP_READ);
|
|
gst_video_frame_map (&dst_frame, info, dst_buf, GST_MAP_WRITE);
|
|
|
|
if (GST_VIDEO_FRAME_WIDTH (&src_frame) == GST_VIDEO_FRAME_WIDTH (&dst_frame)
|
|
&& GST_VIDEO_FRAME_HEIGHT (&src_frame) ==
|
|
GST_VIDEO_FRAME_HEIGHT (&dst_frame)) {
|
|
gst_video_frame_unmap (&src_frame);
|
|
gst_video_frame_unmap (&dst_frame);
|
|
|
|
gst_buffer_unref (dst_buf);
|
|
return gst_buffer_ref (buffer);
|
|
}
|
|
|
|
for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&src_frame); i++) {
|
|
guint src_width_in_bytes, src_height;
|
|
guint dst_width_in_bytes, dst_height;
|
|
guint width_in_bytes, height;
|
|
guint src_stride, dst_stride;
|
|
guint8 *src_data, *dst_data;
|
|
|
|
src_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&src_frame, i) *
|
|
GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
|
|
src_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
|
|
src_stride = GST_VIDEO_FRAME_COMP_STRIDE (&src_frame, i);
|
|
|
|
dst_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&dst_frame, i) *
|
|
GST_VIDEO_FRAME_COMP_PSTRIDE (&dst_frame, i);
|
|
dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&dst_frame, i);
|
|
dst_stride = GST_VIDEO_FRAME_COMP_STRIDE (&dst_frame, i);
|
|
|
|
width_in_bytes = MIN (src_width_in_bytes, dst_width_in_bytes);
|
|
height = MIN (src_height, dst_height);
|
|
|
|
src_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&src_frame, i);
|
|
dst_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&dst_frame, i);
|
|
|
|
for (guint j = 0; j < height; j++) {
|
|
memcpy (dst_data, src_data, width_in_bytes);
|
|
dst_data += dst_stride;
|
|
src_data += src_stride;
|
|
}
|
|
}
|
|
|
|
gst_video_frame_unmap (&dst_frame);
|
|
gst_video_frame_unmap (&src_frame);
|
|
|
|
return dst_buf;
|
|
}
|
|
|
|
/**
|
|
* gst_qsv_allocator_acquire_frame:
|
|
* @allocator: a #GstQsvAllocator
|
|
* @mem_type: a memory type
|
|
* @info: a #GstVideoInfo
|
|
* @buffer: (nullable) (transfer full): a #GstBuffer
|
|
* @pool: (nullable): a #GstBufferPool
|
|
*
|
|
* Uploads @buffer to video memory if required, and wraps GstBuffer using
|
|
* #GstQsvFrame object so that QSV API can access native memory handle
|
|
* via mfxFrameAllocator interface.
|
|
*
|
|
* Returns: a #GstQsvFrame object
|
|
*/
|
|
GstQsvFrame *
|
|
gst_qsv_allocator_acquire_frame (GstQsvAllocator * allocator,
|
|
GstQsvMemoryType mem_type, const GstVideoInfo * info, GstBuffer * buffer,
|
|
GstBufferPool * pool)
|
|
{
|
|
GstQsvAllocatorPrivate *priv;
|
|
GstQsvFrame *frame;
|
|
guint32 map_flags = 0;
|
|
|
|
g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
|
|
|
|
if (GST_QSV_MEM_TYPE_IS_SYSTEM (mem_type) &&
|
|
GST_QSV_MEM_TYPE_IS_VIDEO (mem_type)) {
|
|
GST_ERROR_OBJECT (allocator, "Invalid memory type");
|
|
return nullptr;
|
|
}
|
|
|
|
if (GST_QSV_MEM_TYPE_IS_VIDEO (mem_type)) {
|
|
map_flags = GST_MAP_QSV;
|
|
|
|
if ((mem_type & GST_QSV_ENCODER_IN_MEMORY) != 0) {
|
|
map_flags |= GST_MAP_READ;
|
|
} else if ((mem_type & GST_QSV_DECODER_OUT_MEMORY) != 0 ||
|
|
(mem_type & GST_QSV_PROCESS_TARGET) != 0) {
|
|
map_flags |= GST_MAP_WRITE;
|
|
} else {
|
|
GST_ERROR_OBJECT (allocator,
|
|
"Unknown read/write access for video memory");
|
|
return nullptr;
|
|
}
|
|
} else if ((mem_type & GST_QSV_ENCODER_IN_MEMORY) != 0) {
|
|
map_flags = GST_MAP_READ;
|
|
} else {
|
|
map_flags = GST_MAP_READWRITE;
|
|
}
|
|
|
|
priv = allocator->priv;
|
|
frame = (GstQsvFrame *) gst_atomic_queue_pop (priv->queue);
|
|
|
|
if (!frame)
|
|
frame = gst_qsv_frame_new ();
|
|
|
|
frame->mem_type = mem_type;
|
|
frame->map_flags = (GstMapFlags) map_flags;
|
|
frame->info = *info;
|
|
|
|
if (!pool) {
|
|
frame->buffer = buffer;
|
|
} else if (buffer) {
|
|
GstBuffer *upload_buf;
|
|
|
|
frame->allocator = (GstQsvAllocator *) gst_object_ref (allocator);
|
|
GST_MINI_OBJECT_CAST (frame)->dispose =
|
|
(GstMiniObjectDisposeFunction) gst_qsv_frame_dispose;
|
|
|
|
if (GST_QSV_MEM_TYPE_IS_SYSTEM (mem_type)) {
|
|
upload_buf = gst_qsv_allocator_upload_default (allocator, info, buffer,
|
|
pool);
|
|
} else {
|
|
GstQsvAllocatorClass *klass;
|
|
|
|
klass = GST_QSV_ALLOCATOR_GET_CLASS (allocator);
|
|
g_assert (klass->upload);
|
|
|
|
upload_buf = klass->upload (allocator, info, buffer, pool);
|
|
}
|
|
|
|
gst_buffer_unref (buffer);
|
|
|
|
if (!upload_buf) {
|
|
GST_WARNING_OBJECT (allocator, "Failed to upload buffer");
|
|
gst_qsv_frame_unref (frame);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
frame->buffer = upload_buf;
|
|
}
|
|
|
|
return frame;
|
|
}
|
|
|
|
static GstBuffer *
|
|
gst_qsv_allocator_download_default (GstQsvAllocator * self,
|
|
const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
|
|
GstBufferPool * pool)
|
|
{
|
|
GstBuffer *buffer = nullptr;
|
|
GstFlowReturn ret;
|
|
GstVideoFrame dst_frame;
|
|
mfxStatus status;
|
|
mfxFrameData dummy;
|
|
gboolean copy_ret;
|
|
|
|
GST_TRACE_OBJECT (self, "Download");
|
|
|
|
if (!force_copy)
|
|
return gst_buffer_ref (frame->buffer);
|
|
|
|
ret = gst_buffer_pool_acquire_buffer (pool, &buffer, nullptr);
|
|
if (ret != GST_FLOW_OK) {
|
|
GST_WARNING_OBJECT (self, "Failed to acquire buffer");
|
|
return nullptr;
|
|
}
|
|
|
|
/* Use gst_qsv_allocator_lock() instead of gst_video_frame_map() to avoid
|
|
* redundant map if it's already locked by driver, already locked by driver
|
|
* sounds unsafe situaltion though */
|
|
status = gst_qsv_allocator_lock ((mfxHDL) self, (mfxMemId) frame, &dummy);
|
|
if (status != MFX_ERR_NONE) {
|
|
gst_buffer_unref (buffer);
|
|
GST_ERROR_OBJECT (self, "Failed to lock frame");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!gst_video_frame_map (&dst_frame, info, buffer, GST_MAP_WRITE)) {
|
|
gst_qsv_allocator_unlock ((mfxHDL) self, (mfxMemId) frame, &dummy);
|
|
gst_buffer_unref (buffer);
|
|
GST_ERROR_OBJECT (self, "Failed to map output buffer");
|
|
return nullptr;
|
|
}
|
|
|
|
copy_ret = gst_video_frame_copy (&dst_frame, &frame->frame);
|
|
gst_qsv_allocator_unlock ((mfxHDL) self, (mfxMemId) frame, &dummy);
|
|
gst_video_frame_unmap (&dst_frame);
|
|
|
|
if (!copy_ret) {
|
|
GST_ERROR_OBJECT (self, "Failed to copy frame");
|
|
gst_buffer_unref (buffer);
|
|
return nullptr;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
GstBuffer *
|
|
gst_qsv_allocator_download_frame (GstQsvAllocator * allocator,
|
|
gboolean force_copy, GstQsvFrame * frame, const GstVideoInfo * pool_info,
|
|
GstBufferPool * pool)
|
|
{
|
|
GstQsvAllocatorClass *klass;
|
|
|
|
g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
|
|
g_return_val_if_fail (GST_IS_QSV_FRAME (frame), nullptr);
|
|
g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), nullptr);
|
|
|
|
if (GST_QSV_MEM_TYPE_IS_SYSTEM (frame->mem_type)) {
|
|
return gst_qsv_allocator_download_default (allocator, pool_info,
|
|
force_copy, frame, pool);
|
|
}
|
|
|
|
klass = GST_QSV_ALLOCATOR_GET_CLASS (allocator);
|
|
g_assert (klass->download);
|
|
|
|
return klass->download (allocator, pool_info, force_copy, frame, pool);
|
|
}
|
|
|
|
mfxFrameAllocator *
|
|
gst_qsv_allocator_get_allocator_handle (GstQsvAllocator * allocator)
|
|
{
|
|
g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
|
|
|
|
return &allocator->priv->allocator;
|
|
}
|
|
|
|
gboolean
|
|
gst_qsv_allocator_get_cached_response (GstQsvAllocator * allocator,
|
|
mfxFrameAllocResponse * response)
|
|
{
|
|
g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), FALSE);
|
|
|
|
return gst_qsv_allocator_copy_cached_response (allocator,
|
|
response, &allocator->priv->response);
|
|
}
|
|
|
|
void
|
|
gst_qsv_allocator_set_options (GstQsvAllocator * allocator,
|
|
guint16 extra_alloc_size, gboolean dummy_alloc)
|
|
{
|
|
g_return_if_fail (GST_IS_QSV_ALLOCATOR (allocator));
|
|
|
|
allocator->priv->extra_alloc_size = extra_alloc_size;
|
|
allocator->priv->dummy_alloc = dummy_alloc;
|
|
}
|