mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-09 19:09:41 +00:00
9ee40679b5
This reverts commit ddd13fc7c0
Dynamic usage can reduce the number of copy per frame but make
things complicated and the benefit seems to not significant.
Also since we don't provide _map() method for the dynamic usage,
application cannot read buffers which make "last-sample" property
unusable in case of d3d11videosink.
1025 lines
29 KiB
C
1025 lines
29 KiB
C
/* GStreamer
|
|
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.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 "gstd3d11decoder.h"
|
|
#include "gstd3d11memory.h"
|
|
#include "gstd3d11bufferpool.h"
|
|
#include "gstd3d11device.h"
|
|
#include <string.h>
|
|
|
|
GST_DEBUG_CATEGORY (d3d11_decoder_debug);
|
|
#define GST_CAT_DEFAULT d3d11_decoder_debug
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_DEVICE,
|
|
};
|
|
|
|
struct _GstD3D11DecoderPrivate
|
|
{
|
|
GstD3D11Device *device;
|
|
|
|
ID3D11VideoDevice *video_device;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
ID3D11VideoDecoder *decoder;
|
|
|
|
GstBufferPool *internal_pool;
|
|
|
|
/* for staging */
|
|
ID3D11Texture2D *staging;
|
|
D3D11_TEXTURE2D_DESC staging_desc;
|
|
};
|
|
|
|
#define OUTPUT_VIEW_QUARK _decoder_output_view_get()
|
|
static GQuark
|
|
_decoder_output_view_get (void)
|
|
{
|
|
static gsize g_quark;
|
|
|
|
if (g_once_init_enter (&g_quark)) {
|
|
gsize quark =
|
|
(gsize) g_quark_from_static_string ("GstD3D11DecoderOutputView");
|
|
g_once_init_leave (&g_quark, quark);
|
|
}
|
|
return g_quark;
|
|
}
|
|
|
|
static void gst_d3d11_decoder_constructed (GObject * object);
|
|
static void gst_d3d11_decoder_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_d3d11_decoder_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static void gst_d3d11_decoder_dispose (GObject * obj);
|
|
|
|
#define parent_class gst_d3d11_decoder_parent_class
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11Decoder,
|
|
gst_d3d11_decoder, GST_TYPE_OBJECT);
|
|
|
|
static void
|
|
gst_d3d11_decoder_class_init (GstD3D11DecoderClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->constructed = gst_d3d11_decoder_constructed;
|
|
gobject_class->set_property = gst_d3d11_decoder_set_property;
|
|
gobject_class->get_property = gst_d3d11_decoder_get_property;
|
|
gobject_class->dispose = gst_d3d11_decoder_dispose;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DEVICE,
|
|
g_param_spec_object ("device", "Device",
|
|
"D3D11 Devicd to use", GST_TYPE_D3D11_DEVICE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
|
|
GST_DEBUG_CATEGORY_INIT (d3d11_decoder_debug,
|
|
"d3d11decoder", 0, "Direct3D11 Base Video Decoder object");
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_init (GstD3D11Decoder * self)
|
|
{
|
|
self->priv = gst_d3d11_decoder_get_instance_private (self);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_constructed (GObject * object)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (object);
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
HRESULT hr;
|
|
ID3D11Device *device_handle;
|
|
ID3D11DeviceContext *device_context_handle;
|
|
|
|
if (!priv->device) {
|
|
GST_ERROR_OBJECT (self, "No D3D11Device available");
|
|
return;
|
|
}
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (priv->device);
|
|
device_context_handle =
|
|
gst_d3d11_device_get_device_context_handle (priv->device);
|
|
|
|
hr = ID3D11Device_QueryInterface (device_handle, &IID_ID3D11VideoDevice,
|
|
(void **) &priv->video_device);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device) || !priv->video_device) {
|
|
GST_WARNING_OBJECT (self, "Cannot create VideoDevice Object: 0x%x",
|
|
(guint) hr);
|
|
priv->video_device = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
hr = ID3D11DeviceContext_QueryInterface (device_context_handle,
|
|
&IID_ID3D11VideoContext, (void **) &priv->video_context);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device) || !priv->video_context) {
|
|
GST_WARNING_OBJECT (self, "Cannot create VideoContext Object: 0x%x",
|
|
(guint) hr);
|
|
priv->video_context = NULL;
|
|
|
|
goto fail;
|
|
}
|
|
|
|
return;
|
|
|
|
fail:
|
|
if (priv->video_device) {
|
|
ID3D11VideoDevice_Release (priv->video_device);
|
|
priv->video_device = NULL;
|
|
}
|
|
|
|
if (priv->video_context) {
|
|
ID3D11VideoContext_Release (priv->video_context);
|
|
priv->video_context = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (object);
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_DEVICE:
|
|
priv->device = g_value_dup_object (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (object);
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_DEVICE:
|
|
g_value_set_object (value, priv->device);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_close (GstD3D11Decoder * self)
|
|
{
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
|
|
gst_d3d11_decoder_reset (self);
|
|
|
|
if (priv->video_device) {
|
|
ID3D11VideoDevice_Release (priv->video_device);
|
|
priv->video_device = NULL;
|
|
}
|
|
|
|
if (priv->video_context) {
|
|
ID3D11VideoContext_Release (priv->video_context);
|
|
priv->video_context = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_reset_unlocked (GstD3D11Decoder * decoder)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
|
|
priv = decoder->priv;
|
|
gst_clear_object (&priv->internal_pool);
|
|
|
|
if (priv->decoder) {
|
|
ID3D11VideoDecoder_Release (priv->decoder);
|
|
priv->decoder = NULL;
|
|
}
|
|
|
|
if (priv->staging) {
|
|
ID3D11Texture2D_Release (priv->staging);
|
|
priv->staging = NULL;
|
|
}
|
|
|
|
decoder->opened = FALSE;
|
|
}
|
|
|
|
void
|
|
gst_d3d11_decoder_reset (GstD3D11Decoder * decoder)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_D3D11_DECODER (decoder));
|
|
|
|
priv = decoder->priv;
|
|
gst_d3d11_device_lock (priv->device);
|
|
gst_d3d11_decoder_reset_unlocked (decoder);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_dispose (GObject * obj)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (obj);
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
|
|
if (priv->device) {
|
|
gst_d3d11_decoder_close (self);
|
|
gst_object_unref (priv->device);
|
|
priv->device = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
|
}
|
|
|
|
GstD3D11Decoder *
|
|
gst_d3d11_decoder_new (GstD3D11Device * device)
|
|
{
|
|
GstD3D11Decoder *decoder;
|
|
GstD3D11DecoderPrivate *priv;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
decoder = g_object_new (GST_TYPE_D3D11_DECODER, "device", device, NULL);
|
|
priv = decoder->priv;
|
|
|
|
if (!priv->video_device || !priv->video_context) {
|
|
gst_object_unref (decoder);
|
|
return NULL;
|
|
}
|
|
|
|
gst_object_ref_sink (decoder);
|
|
|
|
return decoder;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_output_view_free (GstD3D11DecoderOutputView * view)
|
|
{
|
|
GST_DEBUG_OBJECT (view->device, "Free view %p", view);
|
|
|
|
if (view->handle) {
|
|
gst_d3d11_device_lock (view->device);
|
|
ID3D11VideoDecoderOutputView_Release (view->handle);
|
|
gst_d3d11_device_unlock (view->device);
|
|
}
|
|
|
|
gst_clear_object (&view->device);
|
|
g_free (view);
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_prepare_output_view (GstD3D11Decoder * self,
|
|
GstBufferPool * pool, guint pool_size, const GUID * decoder_profile)
|
|
{
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
GstBuffer **list = NULL;
|
|
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = { 0, };
|
|
guint i;
|
|
|
|
view_desc.DecodeProfile = *decoder_profile;
|
|
view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
|
|
|
|
list = g_newa (GstBuffer *, pool_size);
|
|
memset (list, 0, sizeof (GstBuffer *) * pool_size);
|
|
|
|
/* create output view here */
|
|
for (i = 0; i < pool_size; i++) {
|
|
GstFlowReturn ret;
|
|
GstBuffer *buf = NULL;
|
|
GstD3D11Memory *mem;
|
|
GstD3D11DecoderOutputView *view;
|
|
ID3D11VideoDecoderOutputView *view_handle;
|
|
HRESULT hr;
|
|
ID3D11Texture2D *texture;
|
|
GstD3D11Allocator *allocator;
|
|
|
|
ret = gst_buffer_pool_acquire_buffer (pool, &buf, NULL);
|
|
|
|
if (ret != GST_FLOW_OK || !buf) {
|
|
GST_ERROR_OBJECT (self, "Could acquire buffer from pool");
|
|
goto error;
|
|
}
|
|
|
|
list[i] = buf;
|
|
mem = (GstD3D11Memory *) gst_buffer_peek_memory (buf, 0);
|
|
|
|
allocator = GST_D3D11_ALLOCATOR (GST_MEMORY_CAST (mem)->allocator);
|
|
texture = allocator->texture;
|
|
|
|
if (!texture) {
|
|
GST_ERROR_OBJECT (self, "D3D11 allocator does not have texture");
|
|
goto error;
|
|
}
|
|
|
|
view_desc.Texture2D.ArraySlice = mem->subresource_index;
|
|
GST_LOG_OBJECT (self,
|
|
"Create decoder output view with index %d", mem->subresource_index);
|
|
|
|
hr = ID3D11VideoDevice_CreateVideoDecoderOutputView (priv->video_device,
|
|
(ID3D11Resource *) texture, &view_desc, &view_handle);
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (self,
|
|
"Could not create decoder output view, hr: 0x%x", (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
view = g_new0 (GstD3D11DecoderOutputView, 1);
|
|
view->device = gst_object_ref (priv->device);
|
|
view->handle = view_handle;
|
|
view->view_id = mem->subresource_index;
|
|
|
|
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (mem), OUTPUT_VIEW_QUARK,
|
|
view, (GDestroyNotify) gst_d3d11_decoder_output_view_free);
|
|
}
|
|
|
|
/* return buffers to pool */
|
|
for (i = 0; i < pool_size; i++) {
|
|
gst_buffer_unref (list[i]);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
if (list) {
|
|
for (i = 0; i < pool_size; i++) {
|
|
if (list[i])
|
|
gst_buffer_unref (list[i]);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Must be called from D3D11Device thread */
|
|
static gboolean
|
|
gst_d3d11_decoder_prepare_output_view_pool (GstD3D11Decoder * self,
|
|
GstVideoInfo * info, guint pool_size, const GUID * decoder_profile)
|
|
{
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
GstD3D11AllocationParams *alloc_params = NULL;
|
|
GstBufferPool *pool = NULL;
|
|
GstStructure *config = NULL;
|
|
GstCaps *caps = NULL;
|
|
|
|
gst_clear_object (&priv->internal_pool);
|
|
|
|
alloc_params = gst_d3d11_allocation_params_new (info,
|
|
GST_D3D11_ALLOCATION_FLAG_TEXTURE_ARRAY, D3D11_BIND_DECODER);
|
|
|
|
if (!alloc_params) {
|
|
GST_ERROR_OBJECT (self, "Failed to create allocation param");
|
|
goto error;
|
|
}
|
|
|
|
alloc_params->desc[0].ArraySize = pool_size;
|
|
|
|
pool = gst_d3d11_buffer_pool_new (priv->device);
|
|
if (!pool) {
|
|
GST_ERROR_OBJECT (self, "Failed to create buffer pool");
|
|
goto error;
|
|
}
|
|
|
|
/* Setup buffer pool */
|
|
config = gst_buffer_pool_get_config (pool);
|
|
caps = gst_video_info_to_caps (info);
|
|
if (!caps) {
|
|
GST_ERROR_OBJECT (self, "Couldn't convert video info to caps");
|
|
goto error;
|
|
}
|
|
|
|
gst_buffer_pool_config_set_params (config, caps, GST_VIDEO_INFO_SIZE (info),
|
|
0, pool_size);
|
|
gst_buffer_pool_config_set_d3d11_allocation_params (config, alloc_params);
|
|
gst_caps_unref (caps);
|
|
gst_d3d11_allocation_params_free (alloc_params);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (self, "Invalid pool config");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't activate pool");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_d3d11_decoder_prepare_output_view (self,
|
|
pool, pool_size, decoder_profile)) {
|
|
goto error;
|
|
}
|
|
|
|
priv->internal_pool = pool;
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
if (alloc_params)
|
|
gst_d3d11_allocation_params_free (alloc_params);
|
|
if (pool)
|
|
gst_object_unref (pool);
|
|
if (caps)
|
|
gst_caps_unref (caps);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_open (GstD3D11Decoder * decoder, GstD3D11Codec codec,
|
|
GstVideoInfo * info, guint pool_size, const GUID ** decoder_profiles,
|
|
guint profile_size)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
const GstD3D11Format *d3d11_format;
|
|
HRESULT hr;
|
|
BOOL can_support = FALSE;
|
|
guint config_count;
|
|
D3D11_VIDEO_DECODER_CONFIG *config_list;
|
|
D3D11_VIDEO_DECODER_CONFIG *best_config = NULL;
|
|
D3D11_VIDEO_DECODER_DESC decoder_desc = { 0, };
|
|
const GUID *selected_profile = NULL;
|
|
GUID *guid_list = NULL;
|
|
guint available_profile_count;
|
|
gint i, j;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (codec > GST_D3D11_CODEC_NONE, FALSE);
|
|
g_return_val_if_fail (codec < GST_D3D11_CODEC_LAST, FALSE);
|
|
g_return_val_if_fail (info != NULL, FALSE);
|
|
g_return_val_if_fail (pool_size > 0, FALSE);
|
|
g_return_val_if_fail (decoder_profiles != NULL, FALSE);
|
|
g_return_val_if_fail (profile_size > 0, FALSE);
|
|
|
|
priv = decoder->priv;
|
|
decoder->opened = FALSE;
|
|
|
|
d3d11_format = gst_d3d11_format_from_gst (GST_VIDEO_INFO_FORMAT (info));
|
|
if (!d3d11_format || d3d11_format->dxgi_format == DXGI_FORMAT_UNKNOWN) {
|
|
GST_ERROR_OBJECT (decoder, "Could not determine dxgi format from %s",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
|
|
return FALSE;
|
|
}
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
available_profile_count =
|
|
ID3D11VideoDevice_GetVideoDecoderProfileCount (priv->video_device);
|
|
|
|
if (available_profile_count == 0) {
|
|
GST_ERROR_OBJECT (decoder, "No available decoder profile");
|
|
goto error;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (decoder,
|
|
"Have %u available decoder profiles", available_profile_count);
|
|
guid_list = g_alloca (sizeof (GUID) * available_profile_count);
|
|
|
|
for (i = 0; i < available_profile_count; i++) {
|
|
hr = ID3D11VideoDevice_GetVideoDecoderProfile (priv->video_device,
|
|
i, &guid_list[i]);
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (decoder, "Failed to get %d th decoder profile", i);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
|
GST_LOG_OBJECT (decoder, "Supported decoder GUID");
|
|
for (i = 0; i < available_profile_count; i++) {
|
|
const GUID *guid = &guid_list[i];
|
|
|
|
GST_LOG_OBJECT (decoder,
|
|
"\t { %8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x }",
|
|
(guint) guid->Data1, (guint) guid->Data2, (guint) guid->Data3,
|
|
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
|
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
|
}
|
|
|
|
GST_LOG_OBJECT (decoder, "Requested decoder GUID");
|
|
for (i = 0; i < profile_size; i++) {
|
|
const GUID *guid = decoder_profiles[i];
|
|
|
|
GST_LOG_OBJECT (decoder,
|
|
"\t { %8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x }",
|
|
(guint) guid->Data1, (guint) guid->Data2, (guint) guid->Data3,
|
|
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
|
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < profile_size; i++) {
|
|
for (j = 0; j < available_profile_count; j++) {
|
|
if (IsEqualGUID (decoder_profiles[i], &guid_list[j])) {
|
|
selected_profile = decoder_profiles[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!selected_profile) {
|
|
GST_ERROR_OBJECT (decoder, "No supported decoder profile");
|
|
goto error;
|
|
} else {
|
|
GST_DEBUG_OBJECT (decoder,
|
|
"Selected guid "
|
|
"{ %8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x }",
|
|
(guint) selected_profile->Data1, (guint) selected_profile->Data2,
|
|
(guint) selected_profile->Data3,
|
|
selected_profile->Data4[0], selected_profile->Data4[1],
|
|
selected_profile->Data4[2], selected_profile->Data4[3],
|
|
selected_profile->Data4[4], selected_profile->Data4[5],
|
|
selected_profile->Data4[6], selected_profile->Data4[7]);
|
|
}
|
|
|
|
hr = ID3D11VideoDevice_CheckVideoDecoderFormat (priv->video_device,
|
|
selected_profile, d3d11_format->dxgi_format, &can_support);
|
|
if (!gst_d3d11_result (hr, priv->device) || !can_support) {
|
|
GST_ERROR_OBJECT (decoder,
|
|
"VideoDevice could not support dxgi format %d, hr: 0x%x",
|
|
d3d11_format->dxgi_format, (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
gst_d3d11_decoder_reset_unlocked (decoder);
|
|
|
|
decoder_desc.SampleWidth = GST_VIDEO_INFO_WIDTH (info);
|
|
decoder_desc.SampleHeight = GST_VIDEO_INFO_HEIGHT (info);
|
|
decoder_desc.OutputFormat = d3d11_format->dxgi_format;
|
|
decoder_desc.Guid = *selected_profile;
|
|
|
|
hr = ID3D11VideoDevice_GetVideoDecoderConfigCount (priv->video_device,
|
|
&decoder_desc, &config_count);
|
|
if (!gst_d3d11_result (hr, priv->device) || config_count == 0) {
|
|
GST_ERROR_OBJECT (decoder, "Could not get decoder config count, hr: 0x%x",
|
|
(guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (decoder, "Total %d config available", config_count);
|
|
|
|
config_list = g_alloca (sizeof (D3D11_VIDEO_DECODER_CONFIG) * config_count);
|
|
|
|
for (i = 0; i < config_count; i++) {
|
|
hr = ID3D11VideoDevice_GetVideoDecoderConfig (priv->video_device,
|
|
&decoder_desc, i, &config_list[i]);
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (decoder, "Could not get decoder %dth config, hr: 0x%x",
|
|
i, (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
/* FIXME: need support DXVA_Slice_H264_Long ?? */
|
|
/* this config uses DXVA_Slice_H264_Short */
|
|
if (codec == GST_D3D11_CODEC_H264 && config_list[i].ConfigBitstreamRaw == 2) {
|
|
best_config = &config_list[i];
|
|
break;
|
|
}
|
|
|
|
if ((codec == GST_D3D11_CODEC_VP9 || codec == GST_D3D11_CODEC_H265)
|
|
&& config_list[i].ConfigBitstreamRaw == 1) {
|
|
best_config = &config_list[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (best_config == NULL) {
|
|
GST_ERROR_OBJECT (decoder, "Could not determine decoder config");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_d3d11_decoder_prepare_output_view_pool (decoder,
|
|
info, pool_size, selected_profile)) {
|
|
GST_ERROR_OBJECT (decoder, "Couldn't prepare output view pool");
|
|
goto error;
|
|
}
|
|
|
|
hr = ID3D11VideoDevice_CreateVideoDecoder (priv->video_device,
|
|
&decoder_desc, best_config, &priv->decoder);
|
|
if (!gst_d3d11_result (hr, priv->device) || !priv->decoder) {
|
|
GST_ERROR_OBJECT (decoder,
|
|
"Could not create decoder object, hr: 0x%x", (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (decoder, "Decoder object %p created", priv->decoder);
|
|
|
|
/* create stage texture to copy out */
|
|
memset (&priv->staging_desc, 0, sizeof (D3D11_TEXTURE2D_DESC));
|
|
priv->staging_desc.Width = GST_VIDEO_INFO_WIDTH (info);
|
|
priv->staging_desc.Height = GST_VIDEO_INFO_HEIGHT (info);
|
|
priv->staging_desc.MipLevels = 1;
|
|
priv->staging_desc.Format = d3d11_format->dxgi_format;
|
|
priv->staging_desc.SampleDesc.Count = 1;
|
|
priv->staging_desc.ArraySize = 1;
|
|
priv->staging_desc.Usage = D3D11_USAGE_STAGING;
|
|
priv->staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
|
|
priv->staging = gst_d3d11_device_create_texture (priv->device,
|
|
&priv->staging_desc, NULL);
|
|
if (!priv->staging) {
|
|
GST_ERROR_OBJECT (decoder, "Couldn't create staging texture");
|
|
goto error;
|
|
}
|
|
|
|
decoder->opened = TRUE;
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_begin_frame (GstD3D11Decoder * decoder,
|
|
GstD3D11DecoderOutputView * output_view, guint content_key_size,
|
|
gconstpointer content_key)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
guint retry_count = 0;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (output_view != NULL, FALSE);
|
|
g_return_val_if_fail (output_view->handle != NULL, FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
do {
|
|
GST_LOG_OBJECT (decoder, "Try begin frame, retry count %d", retry_count);
|
|
gst_d3d11_device_lock (priv->device);
|
|
hr = ID3D11VideoContext_DecoderBeginFrame (priv->video_context,
|
|
priv->decoder, output_view->handle, content_key_size, content_key);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
if (gst_d3d11_result (hr, priv->device)) {
|
|
GST_LOG_OBJECT (decoder, "Success with retry %d", retry_count);
|
|
break;
|
|
} else if (hr == E_PENDING && retry_count < 50) {
|
|
GST_LOG_OBJECT (decoder, "GPU busy, try again");
|
|
|
|
/* HACK: no better idea other than sleep...
|
|
* 1ms waiting like msdkdec */
|
|
g_usleep (1000);
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
retry_count++;
|
|
} while (TRUE);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (decoder, "Failed to begin frame, hr: 0x%x", (guint) hr);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_end_frame (GstD3D11Decoder * decoder)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
hr = ID3D11VideoContext_DecoderEndFrame (priv->video_context, priv->decoder);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_WARNING_OBJECT (decoder, "EndFrame failed, hr: 0x%x", (guint) hr);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_get_decoder_buffer (GstD3D11Decoder * decoder,
|
|
D3D11_VIDEO_DECODER_BUFFER_TYPE type, guint * buffer_size,
|
|
gpointer * buffer)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
UINT size;
|
|
void *decoder_buffer;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
hr = ID3D11VideoContext_GetDecoderBuffer (priv->video_context,
|
|
priv->decoder, type, &size, &decoder_buffer);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_WARNING_OBJECT (decoder, "Getting buffer type %d error, hr: 0x%x",
|
|
type, (guint) hr);
|
|
return FALSE;
|
|
}
|
|
|
|
*buffer_size = size;
|
|
*buffer = decoder_buffer;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_release_decoder_buffer (GstD3D11Decoder * decoder,
|
|
D3D11_VIDEO_DECODER_BUFFER_TYPE type)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
hr = ID3D11VideoContext_ReleaseDecoderBuffer (priv->video_context,
|
|
priv->decoder, type);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_WARNING_OBJECT (decoder, "ReleaseDecoderBuffer failed, hr: 0x%x",
|
|
(guint) hr);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_submit_decoder_buffers (GstD3D11Decoder * decoder,
|
|
guint buffer_count, const D3D11_VIDEO_DECODER_BUFFER_DESC * buffers)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
hr = ID3D11VideoContext_SubmitDecoderBuffers (priv->video_context,
|
|
priv->decoder, buffer_count, buffers);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_WARNING_OBJECT (decoder, "SubmitDecoderBuffers failed, hr: 0x%x",
|
|
(guint) hr);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstBuffer *
|
|
gst_d3d11_decoder_get_output_view_buffer (GstD3D11Decoder * decoder)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
GstBuffer *buf = NULL;
|
|
GstFlowReturn ret;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
ret = gst_buffer_pool_acquire_buffer (priv->internal_pool, &buf, NULL);
|
|
|
|
if (ret != GST_FLOW_OK || !buf) {
|
|
GST_ERROR_OBJECT (decoder, "Couldn't get buffer from pool, ret %s",
|
|
gst_flow_get_name (ret));
|
|
return FALSE;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
GstD3D11DecoderOutputView *
|
|
gst_d3d11_decoder_get_output_view_from_buffer (GstD3D11Decoder * decoder,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstMemory *mem;
|
|
GstD3D11DecoderOutputView *view;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), NULL);
|
|
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
|
|
|
|
mem = gst_buffer_peek_memory (buffer, 0);
|
|
if (!gst_is_d3d11_memory (mem)) {
|
|
GST_WARNING_OBJECT (decoder, "nemory is not d3d11 memory");
|
|
return NULL;
|
|
}
|
|
|
|
view = (GstD3D11DecoderOutputView *)
|
|
gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (mem), OUTPUT_VIEW_QUARK);
|
|
|
|
if (!view) {
|
|
GST_WARNING_OBJECT (decoder, "memory does not have output view");
|
|
}
|
|
|
|
return view;
|
|
}
|
|
|
|
guint
|
|
gst_d3d11_decoder_get_output_view_index (GstD3D11Decoder * decoder,
|
|
ID3D11VideoDecoderOutputView * view_handle)
|
|
{
|
|
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), 0xff);
|
|
g_return_val_if_fail (view_handle != NULL, 0xff);
|
|
|
|
ID3D11VideoDecoderOutputView_GetDesc (view_handle, &view_desc);
|
|
|
|
return view_desc.Texture2D.ArraySlice;
|
|
}
|
|
|
|
static gboolean
|
|
copy_to_system (GstD3D11Decoder * self, GstVideoInfo * info,
|
|
GstBuffer * decoder_buffer, GstBuffer * output)
|
|
{
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
D3D11_TEXTURE2D_DESC *desc = &priv->staging_desc;
|
|
GstVideoFrame out_frame;
|
|
gint i;
|
|
GstD3D11Memory *in_mem;
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
gsize offset[GST_VIDEO_MAX_PLANES];
|
|
gint stride[GST_VIDEO_MAX_PLANES];
|
|
gsize dummy;
|
|
HRESULT hr;
|
|
ID3D11DeviceContext *device_context =
|
|
gst_d3d11_device_get_device_context_handle (priv->device);
|
|
|
|
if (!gst_video_frame_map (&out_frame, info, output, GST_MAP_WRITE)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map output buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
in_mem = (GstD3D11Memory *) gst_buffer_peek_memory (decoder_buffer, 0);
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
ID3D11DeviceContext_CopySubresourceRegion (device_context,
|
|
(ID3D11Resource *) priv->staging, 0, 0, 0, 0,
|
|
(ID3D11Resource *) in_mem->texture, in_mem->subresource_index, NULL);
|
|
|
|
hr = ID3D11DeviceContext_Map (device_context,
|
|
(ID3D11Resource *) priv->staging, 0, D3D11_MAP_READ, 0, &map);
|
|
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (self, "Failed to map, hr: 0x%x", (guint) hr);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_d3d11_dxgi_format_get_size (desc->Format, desc->Width, desc->Height,
|
|
map.RowPitch, offset, stride, &dummy);
|
|
|
|
for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&out_frame); i++) {
|
|
guint8 *src, *dst;
|
|
gint j;
|
|
gint width;
|
|
|
|
src = (guint8 *) map.pData + offset[i];
|
|
dst = GST_VIDEO_FRAME_PLANE_DATA (&out_frame, i);
|
|
width = GST_VIDEO_FRAME_COMP_WIDTH (&out_frame, i) *
|
|
GST_VIDEO_FRAME_COMP_PSTRIDE (&out_frame, i);
|
|
|
|
for (j = 0; j < GST_VIDEO_FRAME_COMP_HEIGHT (&out_frame, i); j++) {
|
|
memcpy (dst, src, width);
|
|
dst += GST_VIDEO_FRAME_PLANE_STRIDE (&out_frame, i);
|
|
src += map.RowPitch;
|
|
}
|
|
}
|
|
|
|
gst_video_frame_unmap (&out_frame);
|
|
ID3D11DeviceContext_Unmap (device_context, (ID3D11Resource *) priv->staging,
|
|
0);
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
copy_to_d3d11 (GstD3D11Decoder * self, GstVideoInfo * info,
|
|
GstBuffer * decoder_buffer, GstBuffer * output)
|
|
{
|
|
GstD3D11DecoderPrivate *priv = self->priv;
|
|
gint i;
|
|
ID3D11DeviceContext *device_context =
|
|
gst_d3d11_device_get_device_context_handle (priv->device);
|
|
|
|
gst_d3d11_device_lock (priv->device);
|
|
for (i = 0; i < gst_buffer_n_memory (output); i++) {
|
|
GstD3D11Memory *in_mem;
|
|
GstD3D11Memory *out_mem;
|
|
D3D11_BOX src_box;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
|
|
in_mem = (GstD3D11Memory *) gst_buffer_peek_memory (decoder_buffer, i);
|
|
out_mem = (GstD3D11Memory *) gst_buffer_peek_memory (output, i);
|
|
|
|
ID3D11Texture2D_GetDesc (out_mem->texture, &desc);
|
|
|
|
src_box.left = 0;
|
|
src_box.top = 0;
|
|
src_box.front = 0;
|
|
src_box.back = 1;
|
|
src_box.right = desc.Width;
|
|
src_box.bottom = desc.Height;
|
|
|
|
ID3D11DeviceContext_CopySubresourceRegion (device_context,
|
|
(ID3D11Resource *) out_mem->texture,
|
|
out_mem->subresource_index, 0, 0, 0,
|
|
(ID3D11Resource *) in_mem->texture, in_mem->subresource_index,
|
|
&src_box);
|
|
|
|
GST_MINI_OBJECT_FLAG_SET (out_mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
|
|
}
|
|
gst_d3d11_device_unlock (priv->device);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_copy_decoder_buffer (GstD3D11Decoder * decoder,
|
|
GstVideoInfo * info, GstBuffer * decoder_buffer, GstBuffer * output)
|
|
{
|
|
GstD3D11DecoderPrivate *priv;
|
|
gboolean can_device_copy = TRUE;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (GST_IS_BUFFER (decoder_buffer), FALSE);
|
|
g_return_val_if_fail (GST_IS_BUFFER (output), FALSE);
|
|
|
|
priv = decoder->priv;
|
|
|
|
if (gst_buffer_n_memory (decoder_buffer) == gst_buffer_n_memory (output)) {
|
|
gint i;
|
|
|
|
for (i = 0; i < gst_buffer_n_memory (output); i++) {
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
|
|
mem = gst_buffer_peek_memory (output, i);
|
|
|
|
if (!gst_is_d3d11_memory (mem)) {
|
|
can_device_copy = FALSE;
|
|
break;
|
|
}
|
|
|
|
dmem = (GstD3D11Memory *) mem;
|
|
|
|
if (dmem->device != priv->device) {
|
|
can_device_copy = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
can_device_copy = FALSE;
|
|
}
|
|
|
|
if (can_device_copy) {
|
|
return copy_to_d3d11 (decoder, info, decoder_buffer, output);
|
|
}
|
|
|
|
return copy_to_system (decoder, info, decoder_buffer, output);
|
|
}
|