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.
1272 lines
39 KiB
C
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);
|
|
}
|