GST-Tensordecoder-ov_ep/sys/d3d11/gstd3d11decoder.c
Seungha Yang fe72bf6053 d3d11decoder: Register elements per GPU device with capability check
This implementation is similar to what we've done for nvcodec plugin.
Since supported resolution, profiles, and formats are device dependent ones,
single template caps cannot represent them, so this modification
will help autoplugging and fallback.

Note that the legacy gpu list and list of resolution to query were
taken from chromium's code.
2020-02-18 11:58:45 +00:00

1272 lines
39 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.
*
* NOTE: some of implementations are copied/modified from Chromium code
*
* Copyright 2015 The Chromium Authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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;
D3D11_BOX staging_box;
GUID decoder_profile;
};
#define OUTPUT_VIEW_QUARK _decoder_output_view_get()
static GQuark
_decoder_output_view_get (void)
{
static volatile gsize g_quark = 0;
if (g_once_init_enter (&g_quark)) {
gsize quark =
(gsize) g_quark_from_static_string ("GstD3D11DecoderOutputView");
g_once_init_leave (&g_quark, quark);
}
return (GQuark) 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_ensure_output_view (GstD3D11Decoder * self,
GstBuffer * buffer)
{
GstD3D11DecoderPrivate *priv = self->priv;
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = { 0, };
GstD3D11Memory *mem;
GstD3D11DecoderOutputView *view;
ID3D11VideoDecoderOutputView *view_handle;
HRESULT hr;
mem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
view = (GstD3D11DecoderOutputView *)
gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (mem), OUTPUT_VIEW_QUARK);
if (view)
return TRUE;
view_desc.DecodeProfile = priv->decoder_profile;
view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
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 *) mem->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);
return FALSE;
}
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 TRUE;
}
/* Must be called from D3D11Device thread */
static gboolean
gst_d3d11_decoder_prepare_output_view_pool (GstD3D11Decoder * self,
GstVideoInfo * info, guint coded_width, guint coded_height,
guint pool_size, const GUID * decoder_profile)
{
GstD3D11DecoderPrivate *priv = self->priv;
GstD3D11AllocationParams *alloc_params = NULL;
GstBufferPool *pool = NULL;
GstStructure *config = NULL;
GstCaps *caps = NULL;
GstVideoAlignment align;
gst_clear_object (&priv->internal_pool);
alloc_params = gst_d3d11_allocation_params_new (priv->device, 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;
gst_video_alignment_reset (&align);
align.padding_right = coded_width - GST_VIDEO_INFO_WIDTH (info);
align.padding_bottom = coded_height - GST_VIDEO_INFO_HEIGHT (info);
if (!gst_d3d11_allocation_params_alignment (alloc_params, &align)) {
GST_ERROR_OBJECT (self, "Cannot set alignment");
return FALSE;
}
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;
}
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_get_supported_decoder_profile (GstD3D11Decoder * decoder,
const GUID ** decoder_profiles, guint profile_size, GUID * selected_profile)
{
GstD3D11DecoderPrivate *priv;
GUID *guid_list = NULL;
const GUID *profile = NULL;
guint available_profile_count;
gint i, j;
HRESULT hr;
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
g_return_val_if_fail (decoder_profiles != NULL, FALSE);
g_return_val_if_fail (profile_size > 0, FALSE);
g_return_val_if_fail (selected_profile != NULL, FALSE);
priv = decoder->priv;
available_profile_count =
ID3D11VideoDevice_GetVideoDecoderProfileCount (priv->video_device);
if (available_profile_count == 0) {
GST_WARNING_OBJECT (decoder, "No available decoder profile");
return FALSE;
}
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_WARNING_OBJECT (decoder, "Failed to get %d th decoder profile", i);
return FALSE;
}
}
#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])) {
profile = decoder_profiles[i];
break;
}
}
}
if (!profile) {
GST_WARNING_OBJECT (decoder, "No supported decoder profile");
return FALSE;
}
*selected_profile = *profile;
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]);
return TRUE;
}
gboolean
gst_d3d11_decoder_open (GstD3D11Decoder * decoder, GstD3D11Codec codec,
GstVideoInfo * info, guint coded_width, guint coded_height,
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, };
GUID selected_profile;
gint i;
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 (coded_width >= GST_VIDEO_INFO_WIDTH (info), FALSE);
g_return_val_if_fail (coded_height >= GST_VIDEO_INFO_HEIGHT (info), 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_device_format_from_gst (priv->device,
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);
if (!gst_d3d11_decoder_get_supported_decoder_profile (decoder,
decoder_profiles, profile_size, &selected_profile)) {
goto error;
}
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 = coded_width;
decoder_desc.SampleHeight = coded_height;
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, coded_width, coded_height, 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;
}
/* This D3D11_BOX structure is used to copy decoder view to staging texture,
* in case of system memory downstream.
* Since resolution of decoder view might be larger than this staging texture,
* this D3D11_BOX structure will guide the target area which need to be copied.
*/
priv->staging_box.left = 0;
priv->staging_box.top = 0;
priv->staging_box.front = 0;
priv->staging_box.back = 1;
priv->staging_box.right = GST_VIDEO_INFO_WIDTH (info);
priv->staging_box.bottom = GST_VIDEO_INFO_HEIGHT (info);
priv->decoder_profile = selected_profile;
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 NULL;
}
if (!gst_d3d11_decoder_ensure_output_view (decoder, buf)) {
GST_ERROR_OBJECT (decoder, "Output view unavailable");
gst_buffer_unref (buf);
return NULL;
}
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,
&priv->staging_box);
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);
}
/* Keep sync with chromium and keep in sorted order.
* See supported_profile_helpers.cc in chromium */
static const guint legacy_amd_list[] = {
0x130f, 0x6700, 0x6701, 0x6702, 0x6703, 0x6704, 0x6705, 0x6706, 0x6707,
0x6708, 0x6709, 0x6718, 0x6719, 0x671c, 0x671d, 0x671f, 0x6720, 0x6721,
0x6722, 0x6723, 0x6724, 0x6725, 0x6726, 0x6727, 0x6728, 0x6729, 0x6738,
0x6739, 0x673e, 0x6740, 0x6741, 0x6742, 0x6743, 0x6744, 0x6745, 0x6746,
0x6747, 0x6748, 0x6749, 0x674a, 0x6750, 0x6751, 0x6758, 0x6759, 0x675b,
0x675d, 0x675f, 0x6760, 0x6761, 0x6762, 0x6763, 0x6764, 0x6765, 0x6766,
0x6767, 0x6768, 0x6770, 0x6771, 0x6772, 0x6778, 0x6779, 0x677b, 0x6798,
0x67b1, 0x6821, 0x683d, 0x6840, 0x6841, 0x6842, 0x6843, 0x6849, 0x6850,
0x6858, 0x6859, 0x6880, 0x6888, 0x6889, 0x688a, 0x688c, 0x688d, 0x6898,
0x6899, 0x689b, 0x689c, 0x689d, 0x689e, 0x68a0, 0x68a1, 0x68a8, 0x68a9,
0x68b0, 0x68b8, 0x68b9, 0x68ba, 0x68be, 0x68bf, 0x68c0, 0x68c1, 0x68c7,
0x68c8, 0x68c9, 0x68d8, 0x68d9, 0x68da, 0x68de, 0x68e0, 0x68e1, 0x68e4,
0x68e5, 0x68e8, 0x68e9, 0x68f1, 0x68f2, 0x68f8, 0x68f9, 0x68fa, 0x68fe,
0x9400, 0x9401, 0x9402, 0x9403, 0x9405, 0x940a, 0x940b, 0x940f, 0x9440,
0x9441, 0x9442, 0x9443, 0x9444, 0x9446, 0x944a, 0x944b, 0x944c, 0x944e,
0x9450, 0x9452, 0x9456, 0x945a, 0x945b, 0x945e, 0x9460, 0x9462, 0x946a,
0x946b, 0x947a, 0x947b, 0x9480, 0x9487, 0x9488, 0x9489, 0x948a, 0x948f,
0x9490, 0x9491, 0x9495, 0x9498, 0x949c, 0x949e, 0x949f, 0x94a0, 0x94a1,
0x94a3, 0x94b1, 0x94b3, 0x94b4, 0x94b5, 0x94b9, 0x94c0, 0x94c1, 0x94c3,
0x94c4, 0x94c5, 0x94c6, 0x94c7, 0x94c8, 0x94c9, 0x94cb, 0x94cc, 0x94cd,
0x9500, 0x9501, 0x9504, 0x9505, 0x9506, 0x9507, 0x9508, 0x9509, 0x950f,
0x9511, 0x9515, 0x9517, 0x9519, 0x9540, 0x9541, 0x9542, 0x954e, 0x954f,
0x9552, 0x9553, 0x9555, 0x9557, 0x955f, 0x9580, 0x9581, 0x9583, 0x9586,
0x9587, 0x9588, 0x9589, 0x958a, 0x958b, 0x958c, 0x958d, 0x958e, 0x958f,
0x9590, 0x9591, 0x9593, 0x9595, 0x9596, 0x9597, 0x9598, 0x9599, 0x959b,
0x95c0, 0x95c2, 0x95c4, 0x95c5, 0x95c6, 0x95c7, 0x95c9, 0x95cc, 0x95cd,
0x95ce, 0x95cf, 0x9610, 0x9611, 0x9612, 0x9613, 0x9614, 0x9615, 0x9616,
0x9640, 0x9641, 0x9642, 0x9643, 0x9644, 0x9645, 0x9647, 0x9648, 0x9649,
0x964a, 0x964b, 0x964c, 0x964e, 0x964f, 0x9710, 0x9711, 0x9712, 0x9713,
0x9714, 0x9715, 0x9802, 0x9803, 0x9804, 0x9805, 0x9806, 0x9807, 0x9808,
0x9809, 0x980a, 0x9830, 0x983d, 0x9850, 0x9851, 0x9874, 0x9900, 0x9901,
0x9903, 0x9904, 0x9905, 0x9906, 0x9907, 0x9908, 0x9909, 0x990a, 0x990b,
0x990c, 0x990d, 0x990e, 0x990f, 0x9910, 0x9913, 0x9917, 0x9918, 0x9919,
0x9990, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998,
0x9999, 0x999a, 0x999b, 0x999c, 0x999d, 0x99a0, 0x99a2, 0x99a4
};
static const guint legacy_intel_list[] = {
0x102, 0x106, 0x116, 0x126, 0x152, 0x156, 0x166,
0x402, 0x406, 0x416, 0x41e, 0xa06, 0xa16, 0xf31,
};
static gint
binary_search_compare (const guint * a, const guint * b)
{
return *a - *b;
}
/* Certain AMD GPU drivers like R600, R700, Evergreen and Cayman and some second
* generation Intel GPU drivers crash if we create a video device with a
* resolution higher then 1920 x 1088. This function checks if the GPU is in
* this list and if yes returns true. */
gboolean
gst_d3d11_decoder_util_is_legacy_device (GstD3D11Device * device)
{
const guint amd_id[] = { 0x1002, 0x1022 };
const guint intel_id = 0x8086;
guint device_id = 0;
guint vendor_id = 0;
guint *match = NULL;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), FALSE);
g_object_get (device, "device-id", &device_id, "vendor-id", &vendor_id, NULL);
if (vendor_id == amd_id[0] || vendor_id == amd_id[1]) {
match =
(guint *) gst_util_array_binary_search ((gpointer) legacy_amd_list,
G_N_ELEMENTS (legacy_amd_list), sizeof (guint),
(GCompareDataFunc) binary_search_compare,
GST_SEARCH_MODE_EXACT, &device_id, NULL);
} else if (vendor_id == intel_id) {
match =
(guint *) gst_util_array_binary_search ((gpointer) legacy_intel_list,
G_N_ELEMENTS (legacy_intel_list), sizeof (guint),
(GCompareDataFunc) binary_search_compare,
GST_SEARCH_MODE_EXACT, &device_id, NULL);
}
if (match) {
GST_DEBUG_OBJECT (device, "it's legacy device");
return TRUE;
}
return FALSE;
}
gboolean
gst_d3d11_decoder_supports_format (GstD3D11Decoder * decoder,
const GUID * decoder_profile, DXGI_FORMAT format)
{
GstD3D11DecoderPrivate *priv;
HRESULT hr;
BOOL can_support = FALSE;
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
g_return_val_if_fail (decoder_profile != NULL, FALSE);
g_return_val_if_fail (format != DXGI_FORMAT_UNKNOWN, FALSE);
priv = decoder->priv;
hr = ID3D11VideoDevice_CheckVideoDecoderFormat (priv->video_device,
decoder_profile, format, &can_support);
if (!gst_d3d11_result (hr, priv->device) || !can_support) {
GST_DEBUG_OBJECT (decoder,
"VideoDevice could not support dxgi format %d, hr: 0x%x",
format, (guint) hr);
return FALSE;
}
return TRUE;
}
/* Don't call this method with legacy device */
gboolean
gst_d3d11_decoder_supports_resolution (GstD3D11Decoder * decoder,
const GUID * decoder_profile, DXGI_FORMAT format, guint width, guint height)
{
D3D11_VIDEO_DECODER_DESC desc;
GstD3D11DecoderPrivate *priv;
HRESULT hr;
UINT config_count;
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
g_return_val_if_fail (decoder_profile != NULL, FALSE);
g_return_val_if_fail (format != DXGI_FORMAT_UNKNOWN, FALSE);
priv = decoder->priv;
desc.SampleWidth = width;
desc.SampleHeight = height;
desc.OutputFormat = format;
desc.Guid = *decoder_profile;
hr = ID3D11VideoDevice_GetVideoDecoderConfigCount (priv->video_device,
&desc, &config_count);
if (!gst_d3d11_result (hr, priv->device) || config_count == 0) {
GST_DEBUG_OBJECT (decoder, "Could not get decoder config count, hr: 0x%x",
(guint) hr);
return FALSE;
}
return TRUE;
}
/**
* gst_d3d11_decoder_class_data_new:
* @device: (transfer none): a #GstD3D11Device
* @sink_caps: (transfer full): a #GstCaps
* @src_caps: (transfer full): a #GstCaps
*
* Create new #GstD3D11DecoderClassData
*
* Returns: (transfer full): the new #GstD3D11DecoderClassData
*/
GstD3D11DecoderClassData *
gst_d3d11_decoder_class_data_new (GstD3D11Device * device,
GstCaps * sink_caps, GstCaps * src_caps)
{
GstD3D11DecoderClassData *ret;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
g_return_val_if_fail (sink_caps != NULL, NULL);
g_return_val_if_fail (src_caps != NULL, NULL);
ret = g_new0 (GstD3D11DecoderClassData, 1);
/* class data will be leaked if the element never gets instantiated */
GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
g_object_get (device, "adapter", &ret->adapter,
"device-id", &ret->device_id, "vendor-id", &ret->vendor_id,
"description", &ret->description, NULL);
ret->sink_caps = sink_caps;
ret->src_caps = src_caps;
return ret;
}
void
gst_d3d11_decoder_class_data_free (GstD3D11DecoderClassData * data)
{
if (!data)
return;
gst_clear_caps (&data->sink_caps);
gst_clear_caps (&data->src_caps);
g_free (data->description);
g_free (data);
}