gstreamer/subprojects/gst-plugins-bad/sys/qsv/gstqsvallocator.cpp
Seungha Yang 5e66dde1b2 qsv: Add VP9 decoder
Recent Intel GPU supports 12bits VP9 decoding but only VP9
profile2 with 10bits is defined by DXVA spec.
Thus, we need this vendor specific decoder element

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3290>
2022-10-31 19:30:47 +00:00

836 lines
23 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;
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:
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;
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)) {
GST_ERROR_OBJECT (self, "Unexpected call");
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) {
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;
}