qsv: Introduce H.264 Intel Quick Sync Video Encoder

A new implementation of Intel Quick Sync Video plugin.
This plugin supports both Windows and Linux but optimization for
VA/DMABuf is not implemented yet.

This new plugin has some notable differences compared with existing
MSDK plugin.

* Encoder will expose formats which can be natively supported
without internal conversion. This will make encoder
control/negotiation flow much simpler and cleaner than
that of MSDK plugin.

* This plugin includes QSV specific library loading helper,
called dispatcher, with QSV SDK headers as a part of this plugin.
So, there will be no more SDK version dependent #ifdef in the code
and also there will be no more build-time MSDK/oneVPL SDK
dependency.

* Memory allocator interop between GStreamer and QSV is re-designed
and decoupled. Instead of implementing QSV specific allocator/bufferpool,
this plugin will make use of generic GStreamer memory
allocator/bufferpool (e.g., GstD3D11Allocator and GstD3D11BufferPool).
Specifically, GstQsvAllocator object will help interop between
GstMemory and mfxFrameAllocator memory abstraction layers.

Note that because of the design decision, VA/DMABuf support is not made
as a part of this initial commit. We can add the optimization for Linux
later once GstVA library exposes allocator/bufferpool implementation as
an API like GstD3D11.

* Initial encoder implementation supports interop with GstD3D11
infrastructure, including zero-copy encoding with upstream D3D11 element.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1408>
This commit is contained in:
Seungha Yang 2022-02-06 21:34:43 +09:00 committed by GStreamer Marge Bot
parent c80132e4a3
commit 64ed6075b7
17 changed files with 5352 additions and 0 deletions

View file

@ -140,6 +140,7 @@ option('openni2', type : 'feature', value : 'auto', description : 'OpenNI2 libra
option('opensles', type : 'feature', value : 'auto', description : 'OpenSL ES audio source/sink plugin')
option('opus', type : 'feature', value : 'auto', description : 'OPUS audio parser plugin')
option('qroverlay', type : 'feature', value : 'auto', description : 'Element to set random data on a qroverlay')
option('qsv', type : 'feature', value : 'auto', description : 'Intel Quick Sync Video plugin')
option('resindvd', type : 'feature', value : 'auto', description : 'Resin DVD playback plugin (GPL - only built if gpl option is also enabled!)')
option('rsvg', type : 'feature', value : 'auto', description : 'SVG overlayer and image decoder plugin')
option('rtmp', type : 'feature', value : 'auto', description : 'RTMP video network source and sink plugin')
@ -191,6 +192,10 @@ option('sctp-internal-usrsctp', type: 'feature', value : 'enabled',
option('mfx_api', type : 'combo', choices : ['MSDK', 'oneVPL', 'auto'], value : 'auto',
description : 'Select MFX API to build against')
# QSV plugin options
option('mfx-modules-dir', type: 'string', value : '',
description : 'libmfx runtime module dir, linux only')
# License-related feature options
option('gpl', type: 'feature', value: 'disabled', yield: true,
description: 'Allow build plugins that have (A)GPL-licensed dependencies')

View file

@ -18,6 +18,7 @@ subdir('mediafoundation')
subdir('msdk')
subdir('nvcodec')
subdir('opensles')
subdir('qsv')
subdir('shm')
subdir('tinyalsa')
subdir('uvch264')

View file

@ -0,0 +1,506 @@
/* 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;
/* For direct GPU access */
GstMapInfo map_info;
};
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;
}
struct _GstQsvAllocatorPrivate
{
GstAtomicQueue *queue;
mfxFrameAllocator allocator;
};
#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 void
gst_qsv_allocator_class_init (GstQsvAllocatorClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gst_qsv_allocator_finalize;
}
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);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static mfxStatus
gst_qsv_allocator_alloc_default (GstQsvAllocator * self,
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
{
GstQsvFrame **mids = nullptr;
GstVideoInfo info;
GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
GST_TRACE_OBJECT (self, "Alloc");
/* 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;
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.Width, request->Info.Height);
for (guint i = 0; i < request->NumFrameSuggested; i++) {
GstBuffer *buffer;
buffer = gst_buffer_new_and_alloc (info.size);
mids[i] = gst_qsv_allocator_acquire_frame (self,
GST_QSV_SYSTEM_MEMORY, &info, buffer, nullptr);
gst_buffer_unref (buffer);
}
response->mids = (mfxMemId *) mids;
return MFX_ERR_NONE;
}
static mfxStatus
gst_qsv_allocator_alloc (mfxHDL pthis,
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
{
GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
GstQsvAllocatorClass *klass;
if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) != 0)
return gst_qsv_allocator_alloc_default (self, request, response);
klass = GST_QSV_ALLOCATOR_GET_CLASS (self);
g_assert (klass->alloc);
return klass->alloc (self, request, response);
}
static mfxStatus
gst_qsv_allocator_lock (mfxHDL pthis, mfxMemId mid, mfxFrameData * ptr)
{
GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
GstQsvFrame *frame = (GstQsvFrame *) mid;
GST_TRACE_OBJECT (self, "Lock mfxMemId %p", mid);
g_mutex_lock (&frame->lock);
if (frame->map_count == 0) {
gst_video_frame_map (&frame->frame, &frame->info, frame->buffer,
GST_MAP_READ);
}
frame->map_count++;
ptr->Pitch = (mfxU16) GST_VIDEO_FRAME_PLANE_STRIDE (&frame->frame, 0);
ptr->Y = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
/* FIXME: check and handle other formats */
if (GST_VIDEO_INFO_FORMAT (&frame->info) == GST_VIDEO_FORMAT_NV12)
ptr->UV = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 1);
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);
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);
if (frame->mem_type != GST_QSV_VIDEO_MEMORY) {
GST_ERROR_OBJECT (self, "Unexpected call");
return MFX_ERR_UNSUPPORTED;
}
if (!frame->map_info.data) {
GST_ERROR_OBJECT (self, "No mapped data");
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) frame->map_info.data;
/* GstD3D11 will fill user_data[0] with subresource index */
pair->second = (mfxHDL) frame->map_info.user_data[0];
#else
*handle = (mfxHDL) frame->map_info.data;
#endif
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);
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;
g_mutex_unlock (&frame->lock);
if (frame->mem_type == GST_QSV_VIDEO_MEMORY && frame->map_info.data)
gst_buffer_unmap (frame->buffer, &frame->map_info);
memset (&frame->map_info, 0, sizeof (GstMapInfo));
gst_clear_buffer (&frame->buffer);
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: (transfer none): 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;
g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
priv = allocator->priv;
frame = (GstQsvFrame *) gst_atomic_queue_pop (priv->queue);
if (!frame)
frame = gst_qsv_frame_new ();
frame->mem_type = mem_type;
frame->allocator = (GstQsvAllocator *) gst_object_ref (allocator);
GST_MINI_OBJECT_CAST (frame)->dispose =
(GstMiniObjectDisposeFunction) gst_qsv_frame_dispose;
if (!pool) {
frame->buffer = gst_buffer_ref (buffer);
frame->info = *info;
} else {
GstBuffer *upload_buf;
if (mem_type == GST_QSV_SYSTEM_MEMORY) {
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);
}
if (!upload_buf) {
GST_WARNING_OBJECT (allocator, "Failed to upload buffer");
gst_qsv_frame_unref (frame);
return nullptr;
}
frame->buffer = upload_buf;
frame->info = *info;
}
if (mem_type == GST_QSV_VIDEO_MEMORY) {
/* TODO: we need to know context whether this memory is for
* output (e.g., decoder or vpp), but we have only encoder
* implementation at the moment, so GST_MAP_READ should be fine */
if (!gst_buffer_map (frame->buffer, &frame->map_info,
(GstMapFlags) (GST_MAP_READ | GST_MAP_QSV))) {
GST_ERROR_OBJECT (allocator, "Failed to map video buffer");
gst_qsv_frame_unref (frame);
return nullptr;
}
}
return frame;
}
mfxFrameAllocator *
gst_qsv_allocator_get_allocator_handle (GstQsvAllocator * allocator)
{
g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
return &allocator->priv->allocator;
}

View file

@ -0,0 +1,106 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
#include <mfx.h>
G_BEGIN_DECLS
#define GST_TYPE_QSV_FRAME (gst_qsv_frame_get_type())
#define GST_IS_QSV_FRAME(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_QSV_FRAME))
#define GST_QSV_FRAME_CAST(obj) ((GstQsvFrame *) obj)
#define GST_TYPE_QSV_ALLOCATOR (gst_qsv_allocator_get_type())
#define GST_QSV_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_QSV_ALLOCATOR, GstQsvAllocator))
#define GST_QSV_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_QSV_ALLOCATOR, GstQsvAllocatorClass))
#define GST_IS_QSV_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_QSV_ALLOCATOR))
#define GST_IS_QSV_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_QSV_ALLOCATOR))
#define GST_QSV_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_QSV_ALLOCATOR, GstQsvAllocatorClass))
#define GST_QSV_ALLOCATOR_CAST(obj) ((GstQsvAllocator *)obj)
typedef struct _GstQsvFrame GstQsvFrame;
typedef struct _GstQsvAllocator GstQsvAllocator;
typedef struct _GstQsvAllocatorClass GstQsvAllocatorClass;
typedef struct _GstQsvAllocatorPrivate GstQsvAllocatorPrivate;
GType gst_qsv_frame_get_type (void);
GstBuffer * gst_qsv_frame_peek_buffer (GstQsvFrame * frame);
static inline GstQsvFrame *
gst_qsv_frame_ref (GstQsvFrame * frame)
{
return (GstQsvFrame *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (frame));
}
static inline void
gst_qsv_frame_unref (GstQsvFrame * frame)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (frame));
}
static inline void
gst_clear_qsv_frame (GstQsvFrame ** frame)
{
gst_clear_mini_object ((GstMiniObject **) frame);
}
typedef enum
{
GST_QSV_SYSTEM_MEMORY,
GST_QSV_VIDEO_MEMORY,
} GstQsvMemoryType;
struct _GstQsvAllocator
{
GstObject parent;
GstQsvAllocatorPrivate *priv;
};
struct _GstQsvAllocatorClass
{
GstObjectClass parent_class;
mfxStatus (*alloc) (GstQsvAllocator * allocator,
mfxFrameAllocRequest * request,
mfxFrameAllocResponse * response);
GstBuffer * (*upload) (GstQsvAllocator * allocator,
const GstVideoInfo * info,
GstBuffer * buffer,
GstBufferPool * pool);
};
GType gst_qsv_allocator_get_type (void);
GstQsvFrame * gst_qsv_allocator_acquire_frame (GstQsvAllocator * allocator,
GstQsvMemoryType mem_type,
const GstVideoInfo * info,
GstBuffer * buffer,
GstBufferPool * pool);
mfxFrameAllocator * gst_qsv_allocator_get_allocator_handle (GstQsvAllocator * allocator);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstQsvAllocator, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,417 @@
/* 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_d3d11.h"
#include <string.h>
GST_DEBUG_CATEGORY_EXTERN (gst_qsv_allocator_debug);
#define GST_CAT_DEFAULT gst_qsv_allocator_debug
struct _GstQsvD3D11Allocator
{
GstQsvAllocator parent;
GstD3D11Device *device;
};
#define gst_qsv_d3d11_allocator_parent_class parent_class
G_DEFINE_TYPE (GstQsvD3D11Allocator, gst_qsv_d3d11_allocator,
GST_TYPE_QSV_ALLOCATOR);
static void gst_qsv_d3d11_allocator_dispose (GObject * object);
static mfxStatus gst_qsv_d3d11_allocator_alloc (GstQsvAllocator * allocator,
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response);
static GstBuffer *gst_qsv_d3d11_allocator_upload (GstQsvAllocator * allocator,
const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool);
static void
gst_qsv_d3d11_allocator_class_init (GstQsvD3D11AllocatorClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstQsvAllocatorClass *alloc_class = GST_QSV_ALLOCATOR_CLASS (klass);
object_class->dispose = gst_qsv_d3d11_allocator_dispose;
alloc_class->alloc = GST_DEBUG_FUNCPTR (gst_qsv_d3d11_allocator_alloc);
alloc_class->upload = GST_DEBUG_FUNCPTR (gst_qsv_d3d11_allocator_upload);
}
static void
gst_qsv_d3d11_allocator_init (GstQsvD3D11Allocator * self)
{
}
static void
gst_qsv_d3d11_allocator_dispose (GObject * object)
{
GstQsvD3D11Allocator *self = GST_QSV_D3D11_ALLOCATOR (object);
gst_clear_object (&self->device);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static mfxStatus
gst_qsv_d3d11_allocator_alloc (GstQsvAllocator * allocator,
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
{
GstQsvD3D11Allocator *self = GST_QSV_D3D11_ALLOCATOR (allocator);
DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
GstQsvFrame **mids = nullptr;
GST_TRACE_OBJECT (self, "Alloc");
/* 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:
dxgi_format = DXGI_FORMAT_NV12;
break;
default:
/* TODO: add more formats */
break;
}
if (dxgi_format == DXGI_FORMAT_UNKNOWN &&
request->Info.FourCC != MFX_FOURCC_P8) {
GST_ERROR_OBJECT (self, "Failed to convert %d to DXGI format",
request->Info.FourCC);
return MFX_ERR_UNSUPPORTED;
}
if (request->Info.FourCC == MFX_FOURCC_P8) {
GstD3D11Allocator *d3d11_alloc = nullptr;
D3D11_BUFFER_DESC desc;
GstVideoInfo info;
GstMemory *mem;
GstBuffer *buffer;
gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
guint size;
d3d11_alloc =
(GstD3D11Allocator *) gst_allocator_find (GST_D3D11_MEMORY_NAME);
if (!d3d11_alloc) {
GST_ERROR_OBJECT (self, "D3D11 allocator is unavailable");
return MFX_ERR_MEMORY_ALLOC;
}
memset (&desc, 0, sizeof (D3D11_BUFFER_DESC));
desc.ByteWidth = request->Info.Width * request->Info.Height;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
mem = gst_d3d11_allocator_alloc_buffer (d3d11_alloc, self->device, &desc);
gst_object_unref (d3d11_alloc);
if (!mem) {
GST_ERROR_OBJECT (self, "Failed to allocate buffer");
return MFX_ERR_MEMORY_ALLOC;
}
size = request->Info.Width * request->Info.Height;
stride[0] = size;
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_GRAY8, size, 1);
buffer = gst_buffer_new ();
gst_buffer_append_memory (buffer, mem);
gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_FORMAT_GRAY8, size, 1, 1, offset, stride);
mids = g_new0 (GstQsvFrame *, 1);
response->NumFrameActual = 1;
mids[0] = gst_qsv_allocator_acquire_frame (allocator,
GST_QSV_VIDEO_MEMORY, &info, buffer, nullptr);
gst_buffer_unref (buffer);
} else {
GstBufferPool *pool;
GstVideoFormat format;
GstVideoInfo info;
GstCaps *caps;
GstStructure *config;
GstD3D11AllocationParams *params;
guint bind_flags = 0;
if ((request->Type & MFX_MEMTYPE_VIDEO_MEMORY_ENCODER_TARGET) != 0)
bind_flags |= D3D11_BIND_VIDEO_ENCODER;
format = gst_d3d11_dxgi_format_to_gst (dxgi_format);
gst_video_info_set_format (&info,
format, request->Info.Width, request->Info.Height);
caps = gst_video_info_to_caps (&info);
pool = gst_d3d11_buffer_pool_new (self->device);
params = gst_d3d11_allocation_params_new (self->device, &info,
(GstD3D11AllocationFlags) 0, bind_flags);
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
gst_d3d11_allocation_params_free (params);
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);
mids = g_new0 (GstQsvFrame *, request->NumFrameSuggested);
response->NumFrameActual = request->NumFrameSuggested;
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 (allocator,
GST_QSV_VIDEO_MEMORY, &info, buffer, nullptr);
gst_buffer_unref (buffer);
}
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 GstBuffer *
gst_qsv_frame_copy_d3d11 (const GstVideoInfo * info, GstBuffer * src_buf,
GstBuffer * dst_buf)
{
D3D11_TEXTURE2D_DESC src_desc, dst_desc;
D3D11_BOX src_box;
guint subresource_idx;
GstMemory *src_mem, *dst_mem;
GstMapInfo src_info, dst_info;
ID3D11Texture2D *src_tex, *dst_tex;
GstD3D11Device *device;
ID3D11DeviceContext *device_context;
GST_TRACE ("Copying D3D11 buffer %" GST_PTR_FORMAT, src_buf);
src_mem = gst_buffer_peek_memory (src_buf, 0);
dst_mem = gst_buffer_peek_memory (dst_buf, 0);
device = GST_D3D11_MEMORY_CAST (dst_mem)->device;
device_context = gst_d3d11_device_get_device_context_handle (device);
if (!gst_memory_map (src_mem,
&src_info, (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11))) {
GST_WARNING ("Failed to map src memory");
gst_buffer_unref (dst_buf);
return nullptr;
}
if (!gst_memory_map (dst_mem,
&dst_info, (GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
GST_WARNING ("Failed to map dst memory");
gst_memory_unmap (src_mem, &src_info);
gst_buffer_unref (dst_buf);
return nullptr;
}
src_tex = (ID3D11Texture2D *) src_info.data;
dst_tex = (ID3D11Texture2D *) dst_info.data;
src_tex->GetDesc (&src_desc);
dst_tex->GetDesc (&dst_desc);
subresource_idx =
gst_d3d11_memory_get_subresource_index (GST_D3D11_MEMORY_CAST (src_mem));
src_box.left = 0;
src_box.top = 0;
src_box.front = 0;
src_box.back = 1;
src_box.right = MIN (src_desc.Width, dst_desc.Width);
src_box.bottom = MIN (src_desc.Height, dst_desc.Height);
gst_d3d11_device_lock (device);
device_context->CopySubresourceRegion (dst_tex, 0,
0, 0, 0, src_tex, subresource_idx, &src_box);
gst_d3d11_device_unlock (device);
gst_memory_unmap (dst_mem, &dst_info);
gst_memory_unmap (src_mem, &src_info);
return dst_buf;
}
static GstBuffer *
gst_qsv_frame_upload_sysmem (const GstVideoInfo * info, GstBuffer * src_buf,
GstBuffer * dst_buf)
{
GstVideoFrame src_frame, dst_frame;
GST_TRACE ("Uploading sysmem buffer %" GST_PTR_FORMAT, src_buf);
if (!gst_video_frame_map (&src_frame, info, src_buf, GST_MAP_READ)) {
GST_WARNING ("Failed to map src frame");
gst_buffer_unref (dst_buf);
return nullptr;
}
if (!gst_video_frame_map (&dst_frame, info, dst_buf, GST_MAP_WRITE)) {
GST_WARNING ("Failed to map src frame");
gst_video_frame_unmap (&src_frame);
gst_buffer_unref (dst_buf);
return nullptr;
}
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 (&src_frame, i);
dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_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;
}
static GstBuffer *
gst_qsv_d3d11_allocator_upload (GstQsvAllocator * allocator,
const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
{
GstMemory *mem;
GstD3D11Memory *dmem, *dst_dmem;
D3D11_TEXTURE2D_DESC desc, dst_desc;
GstBuffer *dst_buf;
GstFlowReturn flow_ret;
/* 1) D3D11 buffer from the same d3d11device with ours
* 1-1) Same resolution
* -> Increase refcount and wrap with GstQsvFrame
* 1-2) Different resolution
* -> GPU copy
* 2) non-D3D11 buffer or from other d3d11 device
* -> Always CPU copy
*/
if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
GST_ERROR_OBJECT (allocator, "Not a d3d11 buffer pool");
return nullptr;
}
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;
}
mem = gst_buffer_peek_memory (buffer, 0);
if (!gst_is_d3d11_memory (mem) || gst_buffer_n_memory (buffer) > 1) {
/* d3d11 buffer should hold single memory object */
return gst_qsv_frame_upload_sysmem (info, buffer, dst_buf);
}
/* FIXME: Add support for shared texture for GPU copy or wrapping
* texture from different device */
dmem = GST_D3D11_MEMORY_CAST (mem);
if (dmem->device != GST_D3D11_BUFFER_POOL (pool)->device)
return gst_qsv_frame_upload_sysmem (info, buffer, dst_buf);
dst_dmem = (GstD3D11Memory *) gst_buffer_peek_memory (dst_buf, 0);
gst_d3d11_memory_get_texture_desc (dmem, &desc);
gst_d3d11_memory_get_texture_desc (dst_dmem, &dst_desc);
if (desc.Width == dst_desc.Width && desc.Height == dst_desc.Height &&
desc.Usage == D3D11_USAGE_DEFAULT) {
/* Identical size and non-staging texture, wrap without copying */
GST_TRACE ("Wrapping D3D11 buffer without copy");
gst_buffer_unref (dst_buf);
return gst_buffer_ref (buffer);
}
return gst_qsv_frame_copy_d3d11 (info, buffer, dst_buf);
}
GstQsvAllocator *
gst_qsv_d3d11_allocator_new (GstD3D11Device * device)
{
GstQsvD3D11Allocator *self;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
self = (GstQsvD3D11Allocator *)
g_object_new (GST_TYPE_QSV_D3D11_ALLOCATOR, nullptr);
self->device = (GstD3D11Device *) gst_object_ref (device);
gst_object_ref_sink (self);
return GST_QSV_ALLOCATOR (self);
}

View file

@ -0,0 +1,34 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/d3d11/gstd3d11.h>
#include "gstqsvallocator.h"
G_BEGIN_DECLS
#define GST_TYPE_QSV_D3D11_ALLOCATOR (gst_qsv_d3d11_allocator_get_type())
G_DECLARE_FINAL_TYPE (GstQsvD3D11Allocator, gst_qsv_d3d11_allocator,
GST, QSV_D3D11_ALLOCATOR, GstQsvAllocator);
GstQsvAllocator * gst_qsv_d3d11_allocator_new (GstD3D11Device * device);
G_END_DECLS

View file

@ -0,0 +1,104 @@
/* 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_va.h"
GST_DEBUG_CATEGORY_EXTERN (gst_qsv_allocator_debug);
#define GST_CAT_DEFAULT gst_qsv_allocator_debug
struct _GstQsvVaAllocator
{
GstQsvAllocator parent;
GstVaDisplay *display;
};
#define gst_qsv_va_allocator_parent_class parent_class
G_DEFINE_TYPE (GstQsvVaAllocator, gst_qsv_va_allocator, GST_TYPE_QSV_ALLOCATOR);
static void gst_qsv_va_allocator_dispose (GObject * object);
static mfxStatus gst_qsv_va_allocator_alloc (GstQsvAllocator * allocator,
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response);
static GstBuffer *gst_qsv_va_allocator_upload (GstQsvAllocator * allocator,
const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool);
static void
gst_qsv_va_allocator_class_init (GstQsvVaAllocatorClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstQsvAllocatorClass *alloc_class = GST_QSV_ALLOCATOR_CLASS (klass);
object_class->dispose = gst_qsv_va_allocator_dispose;
alloc_class->alloc = GST_DEBUG_FUNCPTR (gst_qsv_va_allocator_alloc);
alloc_class->upload = GST_DEBUG_FUNCPTR (gst_qsv_va_allocator_upload);
}
static void
gst_qsv_va_allocator_init (GstQsvVaAllocator * self)
{
}
static void
gst_qsv_va_allocator_dispose (GObject * object)
{
GstQsvVaAllocator *self = GST_QSV_VA_ALLOCATOR (object);
gst_clear_object (&self->display);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static mfxStatus
gst_qsv_va_allocator_alloc (GstQsvAllocator * allocator,
mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
{
GST_ERROR_OBJECT (allocator, "Not implemented");
return MFX_ERR_UNSUPPORTED;
}
static GstBuffer *
gst_qsv_va_allocator_upload (GstQsvAllocator * allocator,
const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
{
GST_ERROR_OBJECT (allocator, "Not implemented");
return nullptr;
}
GstQsvAllocator *
gst_qsv_va_allocator_new (GstVaDisplay * display)
{
GstQsvVaAllocator *self;
g_return_val_if_fail (GST_IS_VA_DISPLAY (display), nullptr);
self = (GstQsvVaAllocator *)
g_object_new (GST_TYPE_QSV_VA_ALLOCATOR, nullptr);
self->display = (GstVaDisplay *) gst_object_ref (display);
gst_object_ref_sink (self);
return GST_QSV_ALLOCATOR (self);
}

View file

@ -0,0 +1,34 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/va/gstvadisplay.h>
#include "gstqsvallocator.h"
G_BEGIN_DECLS
#define GST_TYPE_QSV_VA_ALLOCATOR (gst_qsv_va_allocator_get_type())
G_DECLARE_FINAL_TYPE (GstQsvVaAllocator, gst_qsv_va_allocator,
GST, QSV_VA_ALLOCATOR, GstQsvAllocator);
GstQsvAllocator * gst_qsv_va_allocator_new (GstVaDisplay * display);
G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
#include <mfx.h>
#include "gstqsvutils.h"
G_BEGIN_DECLS
#define GST_TYPE_QSV_ENCODER (gst_qsv_encoder_get_type())
#define GST_QSV_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_QSV_ENCODER, GstQsvEncoder))
#define GST_QSV_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_QSV_ENCODER, GstQsvEncoderClass))
#define GST_IS_QSV_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_QSV_ENCODER))
#define GST_IS_QSV_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_QSV_ENCODER))
#define GST_QSV_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_QSV_ENCODER, GstQsvEncoderClass))
#define GST_QSV_ENCODER_CAST(obj) ((GstQsvEncoder *)obj)
typedef struct _GstQsvEncoder GstQsvEncoder;
typedef struct _GstQsvEncoderClass GstQsvEncoderClass;
typedef struct _GstQsvEncoderPrivate GstQsvEncoderPrivate;
#define GST_TYPE_QSV_CODING_OPTION (gst_qsv_coding_option_get_type())
GType gst_qsv_coding_option_get_type (void);
typedef enum
{
GST_QSV_ENCODER_RECONFIGURE_NONE,
GST_QSV_ENCODER_RECONFIGURE_BITRATE,
GST_QSV_ENCODER_RECONFIGURE_FULL,
} GstQsvEncoderReconfigure;
struct _GstQsvEncoder
{
GstVideoEncoder parent;
GstQsvEncoderPrivate *priv;
};
struct _GstQsvEncoderClass
{
GstVideoEncoderClass parent_class;
mfxU32 codec_id;
mfxU32 impl_index;
/* DXGI adapter LUID, for Windows */
gint64 adapter_luid;
/* VA display device path, for Linux */
gchar display_path[64];
gboolean (*set_format) (GstQsvEncoder * encoder,
GstVideoCodecState * state,
mfxVideoParam * param,
GPtrArray * extra_params);
gboolean (*set_output_state) (GstQsvEncoder * encoder,
GstVideoCodecState * state,
mfxSession session);
gboolean (*attach_payload) (GstQsvEncoder * encoder,
GstVideoCodecFrame * frame,
GPtrArray * payload);
GstBuffer * (*create_output_buffer) (GstQsvEncoder * encoder,
mfxBitstream * bitstream);
GstQsvEncoderReconfigure (*check_reconfigure) (GstQsvEncoder * encoder,
mfxVideoParam * param);
};
GType gst_qsv_encoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstQsvEncoder, gst_object_unref)
G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstqsvencoder.h"
G_BEGIN_DECLS
void gst_qsv_h264_enc_register (GstPlugin * plugin,
guint rank,
guint impl_index,
GstObject * device,
mfxSession session);
G_END_DECLS

View file

@ -0,0 +1,203 @@
/* 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 "gstqsvutils.h"
#ifdef G_OS_WIN32
#include <gst/d3d11/gstd3d11.h>
#include <wrl.h>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
/* *INDENT-ON* */
#else
#include <gst/va/gstvadisplay_drm.h>
#endif
static mfxLoader _loader = nullptr;
mfxLoader
gst_qsv_get_loader (void)
{
static gsize load_once = 0;
if (g_once_init_enter (&load_once)) {
_loader = MFXLoad ();
g_once_init_leave (&load_once, 1);
}
return _loader;
}
void
gst_qsv_deinit (void)
{
g_clear_pointer (&_loader, MFXUnload);
}
#ifdef G_OS_WIN32
static GList *
gst_qsv_get_d3d11_devices (void)
{
GList *rst = nullptr;
HRESULT hr;
ComPtr < IDXGIFactory1 > factory;
hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
if (FAILED (hr))
return nullptr;
for (guint idx = 0;; idx++) {
ComPtr < IDXGIAdapter1 > adapter;
DXGI_ADAPTER_DESC desc;
gint64 luid;
GstD3D11Device *device;
ComPtr < ID3D10Multithread > multi_thread;
ID3D11Device *device_handle;
hr = factory->EnumAdapters1 (idx, &adapter);
if (FAILED (hr))
return rst;
hr = adapter->GetDesc (&desc);
if (FAILED (hr))
continue;
if (desc.VendorId != 0x8086)
continue;
luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
device = gst_d3d11_device_new_for_adapter_luid (luid,
D3D11_CREATE_DEVICE_BGRA_SUPPORT);
if (!device)
continue;
device_handle = gst_d3d11_device_get_device_handle (device);
hr = device_handle->QueryInterface (IID_PPV_ARGS (&multi_thread));
if (FAILED (hr)) {
gst_object_unref (device);
continue;
}
/* Should enable mutithread protection layer, otherwise QSV will return
* error code when this handle is passed to QSV via
* MFXVideoCORE_SetHandle() */
multi_thread->SetMultithreadProtected (TRUE);
rst = g_list_append (rst, device);
}
return rst;
}
#else /* G_OS_WIN32 */
static GList *
gst_qsv_get_va_displays (void)
{
gchar path[64];
GList *rst = nullptr;
for (guint i = 0; i < 8; i++) {
GstVaDisplay *display;
GstVaImplementation impl;
g_snprintf (path, sizeof (path), "/dev/dri/renderD%d", 128 + i);
if (!g_file_test (path, G_FILE_TEST_EXISTS))
continue;
display = gst_va_display_drm_new_from_path (path);
if (!display)
continue;
impl = gst_va_display_get_implementation (display);
if (impl != GST_VA_IMPLEMENTATION_INTEL_I965 &&
impl != GST_VA_IMPLEMENTATION_INTEL_IHD) {
gst_object_unref (display);
continue;
}
rst = g_list_append (rst, display);
}
return rst;
}
#endif
GList *
gst_qsv_get_platform_devices (void)
{
#ifdef G_OS_WIN32
return gst_qsv_get_d3d11_devices ();
#else
return gst_qsv_get_va_displays ();
#endif
}
const gchar *
gst_qsv_status_to_string (mfxStatus status)
{
#define CASE(err) \
case err: \
return G_STRINGIFY (err);
switch (status) {
CASE (MFX_ERR_NONE);
CASE (MFX_ERR_UNKNOWN);
CASE (MFX_ERR_NULL_PTR);
CASE (MFX_ERR_UNSUPPORTED);
CASE (MFX_ERR_MEMORY_ALLOC);
CASE (MFX_ERR_NOT_ENOUGH_BUFFER);
CASE (MFX_ERR_INVALID_HANDLE);
CASE (MFX_ERR_LOCK_MEMORY);
CASE (MFX_ERR_NOT_INITIALIZED);
CASE (MFX_ERR_NOT_FOUND);
CASE (MFX_ERR_MORE_DATA);
CASE (MFX_ERR_MORE_SURFACE);
CASE (MFX_ERR_ABORTED);
CASE (MFX_ERR_DEVICE_LOST);
CASE (MFX_ERR_INCOMPATIBLE_VIDEO_PARAM);
CASE (MFX_ERR_INVALID_VIDEO_PARAM);
CASE (MFX_ERR_UNDEFINED_BEHAVIOR);
CASE (MFX_ERR_DEVICE_FAILED);
CASE (MFX_ERR_MORE_BITSTREAM);
CASE (MFX_ERR_GPU_HANG);
CASE (MFX_ERR_REALLOC_SURFACE);
CASE (MFX_ERR_RESOURCE_MAPPED);
CASE (MFX_ERR_NOT_IMPLEMENTED);
CASE (MFX_WRN_IN_EXECUTION);
CASE (MFX_WRN_DEVICE_BUSY);
CASE (MFX_WRN_VIDEO_PARAM_CHANGED);
CASE (MFX_WRN_PARTIAL_ACCELERATION);
CASE (MFX_WRN_INCOMPATIBLE_VIDEO_PARAM);
CASE (MFX_WRN_VALUE_NOT_CHANGED);
CASE (MFX_WRN_OUT_OF_RANGE);
CASE (MFX_WRN_FILTER_SKIPPED);
CASE (MFX_ERR_NONE_PARTIAL_OUTPUT);
CASE (MFX_WRN_ALLOC_TIMEOUT_EXPIRED);
default:
break;
}
#undef CASE
return "Unknown";
}

View file

@ -0,0 +1,57 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <mfx.h>
G_BEGIN_DECLS
mfxLoader gst_qsv_get_loader (void);
void gst_qsv_deinit (void);
GList * gst_qsv_get_platform_devices (void);
const gchar * gst_qsv_status_to_string (mfxStatus status);
/* helper macro for debugging log */
#define QSV_STATUS_ARGS(status) \
status, gst_qsv_status_to_string (status)
static inline GstClockTime
gst_qsv_timestamp_to_gst (mfxU64 timestamp)
{
if (timestamp == (mfxU64) MFX_TIMESTAMP_UNKNOWN)
return GST_CLOCK_TIME_NONE;
return gst_util_uint64_scale (timestamp, GST_SECOND, 90000);
}
static inline mfxU64
gst_qsv_timestamp_from_gst (GstClockTime timestamp)
{
if (!GST_CLOCK_TIME_IS_VALID (timestamp))
return (mfxU64) MFX_TIMESTAMP_UNKNOWN;
return gst_util_uint64_scale (timestamp, 90000, GST_SECOND);
}
G_END_DECLS

View file

@ -0,0 +1,100 @@
mfx_win_sources = [
'dispatcher/windows/main.cpp',
'dispatcher/windows/mfx_dispatcher_log.cpp',
'dispatcher/windows/mfx_dispatcher.cpp',
'dispatcher/windows/mfx_dxva2_device.cpp',
'dispatcher/windows/mfx_function_table.cpp',
'dispatcher/windows/mfx_load_dll.cpp',
]
mfx_win32_sources = [
'dispatcher/windows/mfx_critical_section.cpp',
'dispatcher/windows/mfx_driver_store_loader.cpp',
'dispatcher/windows/mfx_library_iterator.cpp',
'dispatcher/windows/mfx_win_reg_key.cpp',
]
mfx_uwp_sources = [
'dispatcher/windows/mfx_dispatcher_uwp.cpp',
'dispatcher/windows/mfx_driver_store_loader.cpp',
]
mfx_linux_sources = [
'dispatcher/linux/mfxloader.cpp',
]
vpl_sources = [
'dispatcher/vpl/mfx_dispatcher_vpl_config.cpp',
'dispatcher/vpl/mfx_dispatcher_vpl_loader.cpp',
'dispatcher/vpl/mfx_dispatcher_vpl_log.cpp',
'dispatcher/vpl/mfx_dispatcher_vpl_lowlatency.cpp',
'dispatcher/vpl/mfx_dispatcher_vpl_msdk.cpp',
'dispatcher/vpl/mfx_dispatcher_vpl.cpp',
]
libmfx_extra_args = [
'-DONEVPL_EXPERIMENTAL'
]
libmfx_extra_deps = []
libmfx_sources = vpl_sources
if host_system == 'windows'
libmfx_sources += mfx_win_sources
# FIXME: check UWP only
libmfx_sources += mfx_win32_sources
elif host_system == 'linux'
libmfx_sources += mfx_linux_sources
# Unlike Windows (libmfxhw64.dll is part of driver so it's system library),
# user can build/install libmfx on Linux, so we need to define
# "MFX_MODULES_DIR" for dispatcher to be able to search libmfx from
# additional search path.
libmfx_modules_dir = get_option('mfx-modules-dir')
if libmfx_modules_dir == ''
# This "libdir" will be likely wrong but may be fine since libmfx library
# will be installed in the distro default library path as part of libmfx package
# and dispatcher will try to load library from the distro default library path first
libmfx_modules_dir = join_paths(prefix, get_option('libdir'))
endif
libmfx_extra_args += ['-DMFX_MODULES_DIR="@0@"'.format(libmfx_modules_dir)]
libmfx_extra_deps += [
cc.find_library('dl'),
cc.find_library('pthread'),
]
else
error('Only Windows or Linux build is supported')
endif
# suppress build warnings
if cc.get_id() == 'msvc'
libmfx_extra_args += cc.get_supported_arguments([
'/wd4189', # local variable is initialized but not referenced
])
else
libmfx_extra_args += cc.get_supported_arguments([
'-Wno-missing-declarations',
'-Wno-deprecated-declarations',
'-Wno-redundant-decls',
'-Wno-unused-but-set-variable',
'-Wno-unused-variable',
# clang complains
'-Wno-missing-braces',
'-Wno-format-nonliteral',
])
endif
libmfx_incl = include_directories('dispatcher', 'api')
libmfx_static = static_library('libmfx-static',
libmfx_sources,
c_args : libmfx_extra_args,
cpp_args : libmfx_extra_args,
dependencies : libmfx_extra_deps,
include_directories : libmfx_incl
)
libmfx_internal_dep = declare_dependency(
link_with : libmfx_static,
include_directories: [libmfx_incl, include_directories('api/vpl')]
)

View file

@ -0,0 +1,92 @@
qsv_sources = [
'gstqsvallocator.cpp',
'gstqsvencoder.cpp',
'gstqsvh264enc.cpp',
'gstqsvutils.cpp',
'plugin.cpp',
]
qsv_d3d11_sources = [
'gstqsvallocator_d3d11.cpp',
]
qsv_va_sources = [
'gstqsvallocator_va.cpp',
]
extra_args = [
'-DGST_USE_UNSTABLE_API',
]
qsv_option = get_option('qsv')
if qsv_option.disabled()
subdir_done()
endif
qsv_platform_deps = []
if host_system == 'windows'
if not gstd3d11_dep.found()
if qsv_option.enabled()
error('The qsv was enabled explicitly, but required d3d11 was not found')
else
subdir_done()
endif
endif
code = '''
#include <windows.h>
#if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
#error "Not building for UWP"
#endif'''
if cc.compiles(code, name : 'building for UWP')
if qsv_option.enabled()
error('qsv plugin does not support UWP')
else
subdir_done()
endif
endif
qsv_sources += qsv_d3d11_sources
qsv_platform_deps += [gstd3d11_dep]
elif host_system == 'linux' and host_machine.cpu_family() == 'x86_64'
if not gstva_dep.found()
if qsv_option.enabled()
error('The qsv was enabled explicitly, but required va was not found')
else
subdir_done()
endif
endif
qsv_sources += qsv_va_sources
qsv_platform_deps += [gstva_dep]
else
if qsv_option.enabled()
error('QSV plugin supports only Windows or Linux')
else
subdir_done()
endif
endif
# suppress deprecated use of MFXInitEx. We don't use the method,
# but used in "mfxvideo++.h"
# and MinGW 32bits compiler seems to be complaining about redundant-decls
if cc.get_id() != 'msvc'
extra_args += cc.get_supported_arguments([
'-Wno-redundant-decls',
'-Wno-deprecated-declarations',
])
endif
subdir('libmfx')
gstqsv = library('gstqsv',
qsv_sources,
c_args : gst_plugins_bad_args + extra_args,
cpp_args : gst_plugins_bad_args + extra_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstvideo_dep, gstcodecparsers_dep, libmfx_internal_dep] + qsv_platform_deps,
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstqsv, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstqsv]

View file

@ -0,0 +1,266 @@
/* 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 <gst/gst.h>
#include <mfx.h>
#include "gstqsvutils.h"
#include "gstqsvh264enc.h"
#include <string.h>
#ifdef G_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <versionhelpers.h>
#include <gst/d3d11/gstd3d11.h>
#else
#include <gst/va/gstvadisplay.h>
#include <gst/va/gstvadisplay_drm.h>
#endif
GST_DEBUG_CATEGORY (gst_qsv_debug);
GST_DEBUG_CATEGORY (gst_qsv_allocator_debug);
GST_DEBUG_CATEGORY (gst_qsv_encoder_debug);
GST_DEBUG_CATEGORY (gst_qsv_h264_enc_debug);
#define GST_CAT_DEFAULT gst_qsv_debug
#ifdef G_OS_WIN32
#define MFX_ACCEL_MODE MFX_ACCEL_MODE_VIA_D3D11
#else
#define MFX_ACCEL_MODE MFX_ACCEL_MODE_VIA_VAAPI
#endif
#ifdef G_OS_WIN32
static mfxSession
create_session_with_platform_device (mfxLoader loader,
mfxImplDescription * desc, guint impl_index, GstObject ** d3d11_device,
GList ** devices)
{
mfxSession session = nullptr;
mfxStatus status;
GstD3D11Device *selected = nullptr;
GList *list = *devices;
GList *iter;
mfxU16 device_id = 0;
*d3d11_device = nullptr;
status = MFXCreateSession (loader, impl_index, &session);
if (status != MFX_ERR_NONE) {
GST_WARNING ("Failed to create session with index %d, %d (%s)",
impl_index, QSV_STATUS_ARGS (status));
return nullptr;
}
if (desc->ApiVersion.Major >= 2 ||
(desc->ApiVersion.Major == 1 && desc->ApiVersion.Minor >= 19)) {
mfxPlatform platform;
memset (&platform, 0, sizeof (mfxPlatform));
if (MFXVideoCORE_QueryPlatform (session, &platform) == MFX_ERR_NONE) {
device_id = platform.DeviceId;
/* XXX: re-create session, MFXVideoCORE_QueryPlatform() may cause
* later MFXVideoCORE_SetHandle() call failed with
* MFX_ERR_UNDEFINED_BEHAVIOR error */
g_clear_pointer (&session, MFXClose);
status = MFXCreateSession (loader, impl_index, &session);
if (status != MFX_ERR_NONE) {
GST_WARNING ("Failed to re-create session with index %d, %d (%s)",
impl_index, QSV_STATUS_ARGS (status));
return nullptr;
}
}
}
if (device_id) {
for (iter = list; iter; iter = g_list_next (iter)) {
GstD3D11Device *dev = GST_D3D11_DEVICE (iter->data);
guint dev_id;
g_object_get (dev, "device-id", &dev_id, nullptr);
if (dev_id == (guint) device_id) {
selected = dev;
list = g_list_delete_link (list, iter);
break;
}
}
}
if (!selected) {
/* Unknown device id, pick the first device */
selected = GST_D3D11_DEVICE (list->data);
list = g_list_delete_link (list, list);
}
*devices = list;
status = MFXVideoCORE_SetHandle (session, MFX_HANDLE_D3D11_DEVICE,
gst_d3d11_device_get_device_handle (selected));
if (status != MFX_ERR_NONE) {
GST_WARNING ("Failed to set d3d11 device handle, %d (%s)",
QSV_STATUS_ARGS (status));
gst_object_unref (selected);
MFXClose (session);
return nullptr;
}
*d3d11_device = GST_OBJECT (selected);
return session;
}
#else
static mfxSession
create_session_with_platform_device (mfxLoader loader,
mfxImplDescription * desc, guint impl_index, GstObject ** va_display,
GList ** devices)
{
mfxSession session = nullptr;
mfxStatus status;
GstVaDisplay *selected;
GList *list = *devices;
*va_display = nullptr;
status = MFXCreateSession (loader, impl_index, &session);
if (status != MFX_ERR_NONE) {
GST_WARNING ("Failed to create session with index %d, %d (%s)",
impl_index, QSV_STATUS_ARGS (status));
return nullptr;
}
/* XXX: what's the relation between implementation index and VA display ?
* Pick the first available device for now */
selected = GST_VA_DISPLAY (list->data);
list = g_list_delete_link (list, list);
*devices = list;
status = MFXVideoCORE_SetHandle (session, MFX_HANDLE_VA_DISPLAY,
gst_va_display_get_va_dpy (selected));
if (status != MFX_ERR_NONE) {
GST_WARNING ("Failed to set display handle, %d (%s)",
QSV_STATUS_ARGS (status));
gst_object_unref (selected);
MFXClose (session);
return nullptr;
}
*va_display = GST_OBJECT (selected);
return session;
}
#endif
static void
plugin_deinit (gpointer data)
{
gst_qsv_deinit ();
}
static gboolean
plugin_init (GstPlugin * plugin)
{
mfxLoader loader;
guint i = 0;
GList *platform_devices = nullptr;
#ifdef G_OS_WIN32
/* D3D11 Video API is supported since Windows 8.
* Do we want to support old OS (Windows 7 for example) with D3D9 ?? */
if (!IsWindows8OrGreater ())
return TRUE;
#endif
GST_DEBUG_CATEGORY_INIT (gst_qsv_debug, "qsv", 0, "Intel Quick Sync Video");
loader = gst_qsv_get_loader ();
if (!loader)
return TRUE;
platform_devices = gst_qsv_get_platform_devices ();
if (!platform_devices) {
gst_qsv_deinit ();
return TRUE;
}
GST_INFO ("Found %d platform devices", g_list_length (platform_devices));
GST_DEBUG_CATEGORY_INIT (gst_qsv_encoder_debug,
"qsvencoder", 0, "qsvencoder");
GST_DEBUG_CATEGORY_INIT (gst_qsv_allocator_debug,
"gstqsvallocator", 0, "gstqsvallocator");
GST_DEBUG_CATEGORY_INIT (gst_qsv_h264_enc_debug,
"qsvh264enc", 0, "qsvh264enc");
do {
mfxStatus status = MFX_ERR_NONE;
mfxSession session = nullptr;
mfxImplDescription *desc = nullptr;
GstObject *device = nullptr;
status = MFXEnumImplementations (loader,
i, MFX_IMPLCAPS_IMPLDESCSTRUCTURE, (mfxHDL *) & desc);
if (status != MFX_ERR_NONE)
break;
if ((desc->Impl & MFX_IMPL_TYPE_HARDWARE) == 0)
goto next;
if ((desc->AccelerationMode & MFX_ACCEL_MODE) == 0)
goto next;
session = create_session_with_platform_device (loader, desc, i, &device,
&platform_devices);
if (!session)
goto next;
gst_qsv_h264_enc_register (plugin, GST_RANK_NONE, i, device, session);
next:
MFXDispReleaseImplDescription (loader, desc);
g_clear_pointer (&session, MFXClose);
gst_clear_object (&device);
i++;
/* What's the possible maximum number of impl/device ? */
} while (i < 16 && platform_devices != nullptr);
if (platform_devices)
g_list_free_full (platform_devices, (GDestroyNotify) gst_object_unref);
g_object_set_data_full (G_OBJECT (plugin), "plugin-qsv-shutdown",
(gpointer) "shutdown-data", (GDestroyNotify) plugin_deinit);
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
qsv,
"Intel Quick Sync Video plugin",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)