mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-03 16:09:39 +00:00
fe4ec03a4b
User can get the required buffer size by using buffer pool config. Since d3d11 implementation is a candidate for public library in the future, we need to hide everything from header as much as possible. Note that the total size of allocated d3d11 texture memory by GPU is not controllable factor. It depends on hardware specific alignment/padding requirement. So, GstD3D11 implementation updates actual buffer size by allocating D3D11 texture, since there's no way to get CPU accessible memory size without allocating real D3D11 texture. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2482>
1989 lines
61 KiB
C++
1989 lines
61 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 "gstd3d11converter.h"
|
|
#include "gstd3d11pluginutils.h"
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_WINMM
|
|
#include <timeapi.h>
|
|
#endif
|
|
|
|
GST_DEBUG_CATEGORY (d3d11_decoder_debug);
|
|
#define GST_CAT_DEFAULT d3d11_decoder_debug
|
|
|
|
/* GUID might not be defined in MinGW header */
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_H264_IDCT_FGT, 0x1b81be67, 0xa0c7,
|
|
0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_H264_VLD_NOFGT, 0x1b81be68, 0xa0c7,
|
|
0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_H264_VLD_FGT, 0x1b81be69, 0xa0c7,
|
|
0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_HEVC_VLD_MAIN,
|
|
0x5b11d51b, 0x2f4c, 0x4452, 0xbc, 0xc3, 0x09, 0xf2, 0xa1, 0x16, 0x0c, 0xc0);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_HEVC_VLD_MAIN10,
|
|
0x107af0e0, 0xef1a, 0x4d19, 0xab, 0xa8, 0x67, 0xa1, 0x63, 0x07, 0x3d, 0x13);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_VP8_VLD,
|
|
0x90b899ea, 0x3a62, 0x4705, 0x88, 0xb3, 0x8d, 0xf0, 0x4b, 0x27, 0x44, 0xe7);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0,
|
|
0x463707f8, 0xa1d0, 0x4585, 0x87, 0x6d, 0x83, 0xaa, 0x6d, 0x60, 0xb8, 0x9e);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2,
|
|
0xa4c749ef, 0x6ecf, 0x48aa, 0x84, 0x48, 0x50, 0xa7, 0xa1, 0x16, 0x5f, 0xf7);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_MPEG2_VLD, 0xee27417f, 0x5e28,
|
|
0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_MPEG2and1_VLD, 0x86695f12, 0x340e,
|
|
0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60);
|
|
DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_AV1_VLD_PROFILE0, 0xb8be4ccb,
|
|
0xcf53, 0x46ba, 0x8d, 0x59, 0xd6, 0xb8, 0xa6, 0xda, 0x5d, 0x2a);
|
|
|
|
static const GUID *profile_h264_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_H264_IDCT_FGT,
|
|
&GST_GUID_D3D11_DECODER_PROFILE_H264_VLD_NOFGT,
|
|
&GST_GUID_D3D11_DECODER_PROFILE_H264_VLD_FGT,
|
|
};
|
|
|
|
static const GUID *profile_hevc_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_HEVC_VLD_MAIN,
|
|
};
|
|
|
|
static const GUID *profile_hevc_10_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_HEVC_VLD_MAIN10,
|
|
};
|
|
|
|
static const GUID *profile_vp8_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_VP8_VLD,
|
|
};
|
|
|
|
static const GUID *profile_vp9_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0,
|
|
};
|
|
|
|
static const GUID *profile_vp9_10_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2,
|
|
};
|
|
|
|
static const GUID *profile_mpeg2_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_MPEG2_VLD,
|
|
&GST_GUID_D3D11_DECODER_PROFILE_MPEG2and1_VLD
|
|
};
|
|
|
|
static const GUID *profile_av1_list[] = {
|
|
&GST_GUID_D3D11_DECODER_PROFILE_AV1_VLD_PROFILE0,
|
|
/* TODO: add more profile */
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_DEVICE,
|
|
};
|
|
|
|
struct _GstD3D11Decoder
|
|
{
|
|
GstObject parent;
|
|
|
|
gboolean configured;
|
|
gboolean opened;
|
|
|
|
GstD3D11Device *device;
|
|
|
|
ID3D11VideoDevice *video_device;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
ID3D11VideoDecoder *decoder_handle;
|
|
|
|
GstVideoInfo info;
|
|
GstVideoInfo output_info;
|
|
GstD3D11Codec codec;
|
|
gint coded_width;
|
|
gint coded_height;
|
|
DXGI_FORMAT decoder_format;
|
|
gboolean downstream_supports_d3d11;
|
|
|
|
GstVideoCodecState *input_state;
|
|
GstVideoCodecState *output_state;
|
|
|
|
/* Protect internal pool */
|
|
GMutex internal_pool_lock;
|
|
|
|
GstBufferPool *internal_pool;
|
|
/* Internal pool params */
|
|
gint aligned_width;
|
|
gint aligned_height;
|
|
gboolean use_array_of_texture;
|
|
guint dpb_size;
|
|
guint downstream_min_buffers;
|
|
gboolean wait_on_pool_full;
|
|
|
|
/* Used for array-of-texture */
|
|
guint8 next_view_id;
|
|
|
|
/* for staging */
|
|
ID3D11Texture2D *staging;
|
|
gsize staging_texture_offset[GST_VIDEO_MAX_PLANES];
|
|
gint stating_texture_stride[GST_VIDEO_MAX_PLANES];
|
|
|
|
GUID decoder_profile;
|
|
|
|
/* For device specific workaround */
|
|
gboolean can_direct_rendering;
|
|
|
|
/* For high precision clock */
|
|
guint timer_resolution;
|
|
};
|
|
|
|
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);
|
|
static void gst_d3d11_decoder_finalize (GObject * obj);
|
|
static gboolean gst_d3d11_decoder_can_direct_render (GstD3D11Decoder * decoder,
|
|
GstVideoDecoder * videodec, GstBuffer * view_buffer,
|
|
gint display_width, gint display_height);
|
|
|
|
#define parent_class gst_d3d11_decoder_parent_class
|
|
G_DEFINE_TYPE (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;
|
|
gobject_class->finalize = gst_d3d11_decoder_finalize;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DEVICE,
|
|
g_param_spec_object ("device", "Device",
|
|
"D3D11 Devicd to use", GST_TYPE_D3D11_DEVICE,
|
|
(GParamFlags) (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)
|
|
{
|
|
g_mutex_init (&self->internal_pool_lock);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_constructed (GObject * object)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (object);
|
|
ID3D11VideoDevice *video_device;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
if (!self->device) {
|
|
GST_ERROR_OBJECT (self, "No D3D11Device available");
|
|
return;
|
|
}
|
|
|
|
video_device = gst_d3d11_device_get_video_device_handle (self->device);
|
|
if (!video_device) {
|
|
GST_WARNING_OBJECT (self, "ID3D11VideoDevice is not available");
|
|
return;
|
|
}
|
|
|
|
video_context = gst_d3d11_device_get_video_context_handle (self->device);
|
|
if (!video_context) {
|
|
GST_WARNING_OBJECT (self, "ID3D11VideoContext is not available");
|
|
return;
|
|
}
|
|
|
|
self->video_device = video_device;
|
|
video_device->AddRef ();
|
|
|
|
self->video_context = video_context;
|
|
video_context->AddRef ();
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DEVICE:
|
|
self->device = (GstD3D11Device *) 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);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DEVICE:
|
|
g_value_set_object (value, self->device);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_clear_resource (GstD3D11Decoder * self)
|
|
{
|
|
g_mutex_lock (&self->internal_pool_lock);
|
|
if (self->internal_pool) {
|
|
gst_buffer_pool_set_active (self->internal_pool, FALSE);
|
|
gst_clear_object (&self->internal_pool);
|
|
}
|
|
g_mutex_unlock (&self->internal_pool_lock);
|
|
|
|
GST_D3D11_CLEAR_COM (self->decoder_handle);
|
|
GST_D3D11_CLEAR_COM (self->staging);
|
|
|
|
memset (self->staging_texture_offset,
|
|
0, sizeof (self->staging_texture_offset));
|
|
memset (self->stating_texture_stride,
|
|
0, sizeof (self->stating_texture_stride));
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_reset (GstD3D11Decoder * self)
|
|
{
|
|
gst_d3d11_decoder_clear_resource (self);
|
|
|
|
self->dpb_size = 0;
|
|
self->downstream_min_buffers = 0;
|
|
|
|
self->configured = FALSE;
|
|
self->opened = FALSE;
|
|
|
|
self->use_array_of_texture = FALSE;
|
|
self->downstream_supports_d3d11 = FALSE;
|
|
|
|
g_clear_pointer (&self->output_state, gst_video_codec_state_unref);
|
|
g_clear_pointer (&self->input_state, gst_video_codec_state_unref);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_dispose (GObject * obj)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (obj);
|
|
|
|
gst_d3d11_decoder_reset (self);
|
|
|
|
GST_D3D11_CLEAR_COM (self->video_device);
|
|
GST_D3D11_CLEAR_COM (self->video_context);
|
|
|
|
gst_clear_object (&self->device);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_finalize (GObject * obj)
|
|
{
|
|
GstD3D11Decoder *self = GST_D3D11_DECODER (obj);
|
|
|
|
#if HAVE_WINMM
|
|
/* Restore clock precision */
|
|
if (self->timer_resolution)
|
|
timeEndPeriod (self->timer_resolution);
|
|
#endif
|
|
|
|
g_mutex_clear (&self->internal_pool_lock);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
|
}
|
|
|
|
GstD3D11Decoder *
|
|
gst_d3d11_decoder_new (GstD3D11Device * device)
|
|
{
|
|
GstD3D11Decoder *self;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
self = (GstD3D11Decoder *)
|
|
g_object_new (GST_TYPE_D3D11_DECODER, "device", device, NULL);
|
|
|
|
if (!self->video_device || !self->video_context) {
|
|
gst_object_unref (self);
|
|
return NULL;
|
|
}
|
|
|
|
gst_object_ref_sink (self);
|
|
|
|
return self;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_is_configured (GstD3D11Decoder * decoder)
|
|
{
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
return decoder->configured;
|
|
}
|
|
|
|
static GQuark
|
|
gst_d3d11_decoder_view_id_quark (void)
|
|
{
|
|
static gsize id_quark = 0;
|
|
|
|
if (g_once_init_enter (&id_quark)) {
|
|
GQuark quark = g_quark_from_string ("GstD3D11DecoderViewId");
|
|
g_once_init_leave (&id_quark, quark);
|
|
}
|
|
|
|
return (GQuark) id_quark;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_ensure_output_view (GstD3D11Decoder * self,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstD3D11Memory *mem;
|
|
gpointer val = NULL;
|
|
|
|
mem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
|
|
if (!gst_d3d11_memory_get_decoder_output_view (mem, self->video_device,
|
|
&self->decoder_profile)) {
|
|
GST_ERROR_OBJECT (self, "Decoder output view is unavailable");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!self->use_array_of_texture)
|
|
return TRUE;
|
|
|
|
val = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
|
|
gst_d3d11_decoder_view_id_quark ());
|
|
if (!val) {
|
|
g_assert (self->next_view_id < 128);
|
|
g_assert (self->next_view_id > 0);
|
|
|
|
gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
|
|
gst_d3d11_decoder_view_id_quark (),
|
|
GUINT_TO_POINTER (self->next_view_id), NULL);
|
|
|
|
self->next_view_id++;
|
|
/* valid view range is [0, 126], but 0 is not used to here
|
|
* (it's NULL as well) */
|
|
self->next_view_id %= 128;
|
|
if (self->next_view_id == 0)
|
|
self->next_view_id = 1;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_prepare_output_view_pool (GstD3D11Decoder * self)
|
|
{
|
|
GstD3D11AllocationParams *alloc_params = NULL;
|
|
GstBufferPool *pool = NULL;
|
|
GstCaps *caps = NULL;
|
|
GstVideoAlignment align;
|
|
GstD3D11AllocationFlags alloc_flags = (GstD3D11AllocationFlags) 0;
|
|
gint bind_flags = D3D11_BIND_DECODER;
|
|
GstVideoInfo *info = &self->info;
|
|
guint pool_size;
|
|
|
|
g_mutex_lock (&self->internal_pool_lock);
|
|
if (self->internal_pool) {
|
|
gst_buffer_pool_set_active (self->internal_pool, FALSE);
|
|
gst_clear_object (&self->internal_pool);
|
|
}
|
|
g_mutex_unlock (&self->internal_pool_lock);
|
|
|
|
if (!self->use_array_of_texture) {
|
|
alloc_flags = GST_D3D11_ALLOCATION_FLAG_TEXTURE_ARRAY;
|
|
} else {
|
|
/* array of texture can have shader resource view */
|
|
bind_flags |= D3D11_BIND_SHADER_RESOURCE;
|
|
}
|
|
|
|
alloc_params = gst_d3d11_allocation_params_new (self->device, info,
|
|
alloc_flags, bind_flags);
|
|
|
|
if (!alloc_params) {
|
|
GST_ERROR_OBJECT (self, "Failed to create allocation param");
|
|
goto error;
|
|
}
|
|
|
|
pool_size = self->dpb_size + self->downstream_min_buffers;
|
|
GST_DEBUG_OBJECT (self,
|
|
"Configuring internal pool with size %d "
|
|
"(dpb size: %d, downstream min buffers: %d)", pool_size, self->dpb_size,
|
|
self->downstream_min_buffers);
|
|
|
|
if (!self->use_array_of_texture) {
|
|
alloc_params->desc[0].ArraySize = pool_size;
|
|
} else {
|
|
/* Valid view id is [0, 126], but we will use [1, 127] range so that
|
|
* it can be used by qdata, because zero is equal to null */
|
|
self->next_view_id = 1;
|
|
|
|
/* our pool size can be increased as much as possbile */
|
|
pool_size = 0;
|
|
}
|
|
|
|
gst_video_alignment_reset (&align);
|
|
|
|
align.padding_right = self->aligned_width - GST_VIDEO_INFO_WIDTH (info);
|
|
align.padding_bottom = self->aligned_height - GST_VIDEO_INFO_HEIGHT (info);
|
|
if (!gst_d3d11_allocation_params_alignment (alloc_params, &align)) {
|
|
GST_ERROR_OBJECT (self, "Cannot set alignment");
|
|
goto error;
|
|
}
|
|
|
|
caps = gst_video_info_to_caps (info);
|
|
if (!caps) {
|
|
GST_ERROR_OBJECT (self, "Couldn't convert video info to caps");
|
|
goto error;
|
|
}
|
|
|
|
pool = gst_d3d11_buffer_pool_new_with_options (self->device,
|
|
caps, alloc_params, 0, pool_size);
|
|
gst_clear_caps (&caps);
|
|
g_clear_pointer (&alloc_params, gst_d3d11_allocation_params_free);
|
|
|
|
if (!pool) {
|
|
GST_ERROR_OBJECT (self, "Failed to create buffer pool");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't activate pool");
|
|
goto error;
|
|
}
|
|
|
|
g_mutex_lock (&self->internal_pool_lock);
|
|
self->internal_pool = pool;
|
|
g_mutex_unlock (&self->internal_pool_lock);
|
|
|
|
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;
|
|
}
|
|
|
|
static const gchar *
|
|
gst_d3d11_codec_to_string (GstD3D11Codec codec)
|
|
{
|
|
switch (codec) {
|
|
case GST_D3D11_CODEC_NONE:
|
|
return "none";
|
|
case GST_D3D11_CODEC_H264:
|
|
return "H.264";
|
|
case GST_D3D11_CODEC_VP9:
|
|
return "VP9";
|
|
case GST_D3D11_CODEC_H265:
|
|
return "H.265";
|
|
case GST_D3D11_CODEC_VP8:
|
|
return "VP8";
|
|
case GST_D3D11_CODEC_MPEG2:
|
|
return "MPEG2";
|
|
case GST_D3D11_CODEC_AV1:
|
|
return "AV1";
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_get_supported_decoder_profile (GstD3D11Decoder * decoder,
|
|
GstD3D11Codec codec, GstVideoFormat format, const GUID ** selected_profile)
|
|
{
|
|
GUID *guid_list = NULL;
|
|
const GUID *profile = NULL;
|
|
guint available_profile_count;
|
|
guint i, j;
|
|
HRESULT hr;
|
|
ID3D11VideoDevice *video_device;
|
|
const GUID **profile_list = NULL;
|
|
guint profile_size = 0;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (selected_profile != NULL, FALSE);
|
|
|
|
switch (codec) {
|
|
case GST_D3D11_CODEC_H264:
|
|
if (format == GST_VIDEO_FORMAT_NV12) {
|
|
profile_list = profile_h264_list;
|
|
profile_size = G_N_ELEMENTS (profile_h264_list);
|
|
}
|
|
break;
|
|
case GST_D3D11_CODEC_H265:
|
|
if (format == GST_VIDEO_FORMAT_NV12) {
|
|
profile_list = profile_hevc_list;
|
|
profile_size = G_N_ELEMENTS (profile_hevc_list);
|
|
} else if (format == GST_VIDEO_FORMAT_P010_10LE) {
|
|
profile_list = profile_hevc_10_list;
|
|
profile_size = G_N_ELEMENTS (profile_hevc_10_list);
|
|
}
|
|
break;
|
|
case GST_D3D11_CODEC_VP8:
|
|
if (format == GST_VIDEO_FORMAT_NV12) {
|
|
profile_list = profile_vp8_list;
|
|
profile_size = G_N_ELEMENTS (profile_vp8_list);
|
|
}
|
|
break;
|
|
case GST_D3D11_CODEC_VP9:
|
|
if (format == GST_VIDEO_FORMAT_NV12) {
|
|
profile_list = profile_vp9_list;
|
|
profile_size = G_N_ELEMENTS (profile_vp9_list);
|
|
} else if (format == GST_VIDEO_FORMAT_P010_10LE) {
|
|
profile_list = profile_vp9_10_list;
|
|
profile_size = G_N_ELEMENTS (profile_vp9_10_list);
|
|
}
|
|
break;
|
|
case GST_D3D11_CODEC_MPEG2:
|
|
if (format == GST_VIDEO_FORMAT_NV12) {
|
|
profile_list = profile_mpeg2_list;
|
|
profile_size = G_N_ELEMENTS (profile_mpeg2_list);
|
|
}
|
|
break;
|
|
case GST_D3D11_CODEC_AV1:
|
|
profile_list = profile_av1_list;
|
|
profile_size = G_N_ELEMENTS (profile_av1_list);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!profile_list) {
|
|
GST_ERROR_OBJECT (decoder,
|
|
"Not supported codec (%d) and format (%s) configuration", codec,
|
|
gst_video_format_to_string (format));
|
|
return FALSE;
|
|
}
|
|
|
|
video_device = decoder->video_device;
|
|
|
|
available_profile_count = video_device->GetVideoDecoderProfileCount ();
|
|
|
|
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 = (GUID *) g_alloca (sizeof (GUID) * available_profile_count);
|
|
|
|
for (i = 0; i < available_profile_count; i++) {
|
|
hr = video_device->GetVideoDecoderProfile (i, &guid_list[i]);
|
|
if (!gst_d3d11_result (hr, decoder->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 = profile_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]);
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < profile_size; i++) {
|
|
for (j = 0; j < available_profile_count; j++) {
|
|
if (IsEqualGUID (*profile_list[i], guid_list[j])) {
|
|
profile = profile_list[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!profile) {
|
|
GST_INFO_OBJECT (decoder, "No supported decoder profile for %s codec",
|
|
gst_d3d11_codec_to_string (codec));
|
|
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) profile->Data1, (guint) profile->Data2, (guint) profile->Data3,
|
|
profile->Data4[0], profile->Data4[1], profile->Data4[2],
|
|
profile->Data4[3], profile->Data4[4], profile->Data4[5],
|
|
profile->Data4[6], profile->Data4[7]);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_configure (GstD3D11Decoder * decoder, GstD3D11Codec codec,
|
|
GstVideoCodecState * input_state, GstVideoInfo * info, gint coded_width,
|
|
gint coded_height, guint dpb_size)
|
|
{
|
|
const GstD3D11Format *d3d11_format;
|
|
|
|
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 (input_state != 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 (dpb_size > 0, FALSE);
|
|
|
|
gst_d3d11_decoder_reset (decoder);
|
|
|
|
d3d11_format = gst_d3d11_device_format_from_gst (decoder->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;
|
|
}
|
|
|
|
decoder->codec = codec;
|
|
decoder->input_state = gst_video_codec_state_ref (input_state);
|
|
decoder->info = decoder->output_info = *info;
|
|
decoder->coded_width = coded_width;
|
|
decoder->coded_height = coded_height;
|
|
decoder->dpb_size = dpb_size;
|
|
decoder->decoder_format = d3d11_format->dxgi_format;
|
|
|
|
decoder->configured = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_ensure_staging_texture (GstD3D11Decoder * self)
|
|
{
|
|
ID3D11Device *device_handle;
|
|
D3D11_TEXTURE2D_DESC desc = { 0, };
|
|
HRESULT hr;
|
|
|
|
if (self->staging)
|
|
return TRUE;
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (self->device);
|
|
|
|
/* create stage texture to copy out */
|
|
desc.Width = self->aligned_width;
|
|
desc.Height = self->aligned_height;
|
|
desc.MipLevels = 1;
|
|
desc.Format = self->decoder_format;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Usage = D3D11_USAGE_STAGING;
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
|
|
hr = device_handle->CreateTexture2D (&desc, NULL, &self->staging);
|
|
if (!gst_d3d11_result (hr, self->device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create staging texture");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_decoder_enable_high_precision_timer (GstD3D11Decoder * self)
|
|
{
|
|
#if HAVE_WINMM
|
|
GstD3D11DeviceVendor vendor;
|
|
|
|
if (self->timer_resolution)
|
|
return;
|
|
|
|
vendor = gst_d3d11_get_device_vendor (self->device);
|
|
/* Do this only for NVIDIA at the moment, other vendors doesn't seem to be
|
|
* requiring retry for BeginFrame() */
|
|
if (vendor == GST_D3D11_DEVICE_VENDOR_NVIDIA) {
|
|
TIMECAPS time_caps;
|
|
if (timeGetDevCaps (&time_caps, sizeof (TIMECAPS)) == TIMERR_NOERROR) {
|
|
guint resolution;
|
|
MMRESULT ret;
|
|
|
|
resolution = MIN (MAX (time_caps.wPeriodMin, 1), time_caps.wPeriodMax);
|
|
|
|
ret = timeBeginPeriod (resolution);
|
|
if (ret == TIMERR_NOERROR) {
|
|
self->timer_resolution = resolution;
|
|
GST_INFO_OBJECT (self, "Updated timer resolution to %d", resolution);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_open (GstD3D11Decoder * self)
|
|
{
|
|
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;
|
|
guint i;
|
|
gint aligned_width, aligned_height;
|
|
guint alignment;
|
|
GstD3D11DeviceVendor vendor;
|
|
ID3D11VideoDevice *video_device;
|
|
GstVideoInfo *info = &self->info;
|
|
|
|
if (self->opened)
|
|
return TRUE;
|
|
|
|
if (!self->configured) {
|
|
GST_ERROR_OBJECT (self, "Should configure first");
|
|
return FALSE;
|
|
}
|
|
|
|
video_device = self->video_device;
|
|
|
|
gst_d3d11_device_lock (self->device);
|
|
if (!gst_d3d11_decoder_get_supported_decoder_profile (self,
|
|
self->codec, GST_VIDEO_INFO_FORMAT (info), &selected_profile)) {
|
|
goto error;
|
|
}
|
|
|
|
hr = video_device->CheckVideoDecoderFormat (selected_profile,
|
|
self->decoder_format, &can_support);
|
|
if (!gst_d3d11_result (hr, self->device) || !can_support) {
|
|
GST_ERROR_OBJECT (self,
|
|
"VideoDevice could not support dxgi format %d, hr: 0x%x",
|
|
self->decoder_format, (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
gst_d3d11_decoder_clear_resource (self);
|
|
self->can_direct_rendering = TRUE;
|
|
|
|
vendor = gst_d3d11_get_device_vendor (self->device);
|
|
switch (vendor) {
|
|
case GST_D3D11_DEVICE_VENDOR_XBOX:
|
|
/* FIXME: Need to figure out Xbox device's behavior
|
|
* https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1312
|
|
*/
|
|
self->can_direct_rendering = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* NOTE: other dxva implementations (ffmpeg and vlc) do this
|
|
* and they say the required alignment were mentioned by dxva spec.
|
|
* See ff_dxva2_common_frame_params() in dxva.c of ffmpeg and
|
|
* directx_va_Setup() in directx_va.c of vlc.
|
|
* But... where it is? */
|
|
switch (self->codec) {
|
|
case GST_D3D11_CODEC_H265:
|
|
case GST_D3D11_CODEC_AV1:
|
|
/* See directx_va_Setup() impl. in vlc */
|
|
if (vendor != GST_D3D11_DEVICE_VENDOR_XBOX)
|
|
alignment = 128;
|
|
else
|
|
alignment = 16;
|
|
break;
|
|
case GST_D3D11_CODEC_MPEG2:
|
|
/* XXX: ffmpeg does this */
|
|
alignment = 32;
|
|
break;
|
|
default:
|
|
alignment = 16;
|
|
break;
|
|
}
|
|
|
|
aligned_width = GST_ROUND_UP_N (self->coded_width, alignment);
|
|
aligned_height = GST_ROUND_UP_N (self->coded_height, alignment);
|
|
if (aligned_width != self->coded_width ||
|
|
aligned_height != self->coded_height) {
|
|
GST_DEBUG_OBJECT (self,
|
|
"coded resolution %dx%d is not aligned to %d, adjust to %dx%d",
|
|
self->coded_width, self->coded_height, alignment, aligned_width,
|
|
aligned_height);
|
|
}
|
|
|
|
self->aligned_width = aligned_width;
|
|
self->aligned_height = aligned_height;
|
|
|
|
decoder_desc.SampleWidth = aligned_width;
|
|
decoder_desc.SampleHeight = aligned_height;
|
|
decoder_desc.OutputFormat = self->decoder_format;
|
|
decoder_desc.Guid = *selected_profile;
|
|
|
|
hr = video_device->GetVideoDecoderConfigCount (&decoder_desc, &config_count);
|
|
if (!gst_d3d11_result (hr, self->device) || config_count == 0) {
|
|
GST_ERROR_OBJECT (self, "Could not get decoder config count, hr: 0x%x",
|
|
(guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Total %d config available", config_count);
|
|
|
|
config_list = (D3D11_VIDEO_DECODER_CONFIG *)
|
|
g_alloca (sizeof (D3D11_VIDEO_DECODER_CONFIG) * config_count);
|
|
|
|
for (i = 0; i < config_count; i++) {
|
|
hr = video_device->GetVideoDecoderConfig (&decoder_desc, i,
|
|
&config_list[i]);
|
|
if (!gst_d3d11_result (hr, self->device)) {
|
|
GST_ERROR_OBJECT (self, "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 */
|
|
switch (self->codec) {
|
|
case GST_D3D11_CODEC_H264:
|
|
if (config_list[i].ConfigBitstreamRaw == 2)
|
|
best_config = &config_list[i];
|
|
break;
|
|
case GST_D3D11_CODEC_H265:
|
|
case GST_D3D11_CODEC_VP9:
|
|
case GST_D3D11_CODEC_VP8:
|
|
case GST_D3D11_CODEC_MPEG2:
|
|
case GST_D3D11_CODEC_AV1:
|
|
if (config_list[i].ConfigBitstreamRaw == 1)
|
|
best_config = &config_list[i];
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
goto error;
|
|
}
|
|
|
|
if (best_config)
|
|
break;
|
|
}
|
|
|
|
if (best_config == NULL) {
|
|
GST_ERROR_OBJECT (self, "Could not determine decoder config");
|
|
goto error;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "ConfigDecoderSpecific 0x%x",
|
|
best_config->ConfigDecoderSpecific);
|
|
|
|
/* bit 14 is equal to 1b means this config support array of texture and
|
|
* it's recommended type as per DXVA spec */
|
|
if ((best_config->ConfigDecoderSpecific & 0x4000) == 0x4000) {
|
|
GST_DEBUG_OBJECT (self, "Config support array of texture");
|
|
self->use_array_of_texture = TRUE;
|
|
}
|
|
|
|
hr = video_device->CreateVideoDecoder (&decoder_desc,
|
|
best_config, &self->decoder_handle);
|
|
if (!gst_d3d11_result (hr, self->device) || !self->decoder_handle) {
|
|
GST_ERROR_OBJECT (self,
|
|
"Could not create decoder object, hr: 0x%x", (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Decoder object %p created", self->decoder_handle);
|
|
|
|
if (!self->downstream_supports_d3d11 &&
|
|
!gst_d3d11_decoder_ensure_staging_texture (self)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't prepare staging texture");
|
|
goto error;
|
|
}
|
|
|
|
self->decoder_profile = *selected_profile;
|
|
|
|
/* Store pool related information here, then we will setup internal pool
|
|
* later once the number of min buffer size required by downstream is known.
|
|
* Actual buffer pool size will be "dpb_size + downstream_min_buffers"
|
|
*/
|
|
self->downstream_min_buffers = 0;
|
|
self->wait_on_pool_full = FALSE;
|
|
|
|
self->opened = TRUE;
|
|
gst_d3d11_device_unlock (self->device);
|
|
|
|
gst_d3d11_decoder_enable_high_precision_timer (self);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
gst_d3d11_decoder_reset (self);
|
|
gst_d3d11_device_unlock (self->device);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_begin_frame (GstD3D11Decoder * decoder,
|
|
ID3D11VideoDecoderOutputView * output_view, guint content_key_size,
|
|
gconstpointer content_key)
|
|
{
|
|
ID3D11VideoContext *video_context;
|
|
guint retry_count = 0;
|
|
HRESULT hr;
|
|
guint retry_threshold = 100;
|
|
|
|
/* if we have high resolution timer, do more retry */
|
|
if (decoder->timer_resolution)
|
|
retry_threshold = 500;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (output_view != NULL, FALSE);
|
|
|
|
video_context = decoder->video_context;
|
|
|
|
gst_d3d11_device_lock (decoder->device);
|
|
do {
|
|
GST_LOG_OBJECT (decoder, "Try begin frame, retry count %d", retry_count);
|
|
hr = video_context->DecoderBeginFrame (decoder->decoder_handle,
|
|
output_view, content_key_size, content_key);
|
|
|
|
/* HACK: Do retry with 1ms sleep per failure, since DXVA/D3D11
|
|
* doesn't provide API for "GPU-IS-READY-TO-DECODE" like signal.
|
|
*/
|
|
if (hr == E_PENDING && retry_count < retry_threshold) {
|
|
GST_LOG_OBJECT (decoder, "GPU is busy, try again. Retry count %d",
|
|
retry_count);
|
|
g_usleep (1000);
|
|
} else {
|
|
if (gst_d3d11_result (hr, decoder->device))
|
|
GST_LOG_OBJECT (decoder, "Succeeded with retry count %d", retry_count);
|
|
break;
|
|
}
|
|
|
|
retry_count++;
|
|
} while (TRUE);
|
|
gst_d3d11_device_unlock (decoder->device);
|
|
|
|
if (!gst_d3d11_result (hr, decoder->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)
|
|
{
|
|
HRESULT hr;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
video_context = decoder->video_context;
|
|
|
|
gst_d3d11_device_lock (decoder->device);
|
|
hr = video_context->DecoderEndFrame (decoder->decoder_handle);
|
|
gst_d3d11_device_unlock (decoder->device);
|
|
|
|
if (!gst_d3d11_result (hr, decoder->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)
|
|
{
|
|
UINT size;
|
|
void *decoder_buffer;
|
|
HRESULT hr;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
video_context = decoder->video_context;
|
|
|
|
gst_d3d11_device_lock (decoder->device);
|
|
hr = video_context->GetDecoderBuffer (decoder->decoder_handle,
|
|
type, &size, &decoder_buffer);
|
|
gst_d3d11_device_unlock (decoder->device);
|
|
|
|
if (!gst_d3d11_result (hr, decoder->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)
|
|
{
|
|
HRESULT hr;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
video_context = decoder->video_context;
|
|
|
|
gst_d3d11_device_lock (decoder->device);
|
|
hr = video_context->ReleaseDecoderBuffer (decoder->decoder_handle, type);
|
|
gst_d3d11_device_unlock (decoder->device);
|
|
|
|
if (!gst_d3d11_result (hr, decoder->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)
|
|
{
|
|
HRESULT hr;
|
|
ID3D11VideoContext *video_context;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
video_context = decoder->video_context;
|
|
|
|
gst_d3d11_device_lock (decoder->device);
|
|
hr = video_context->SubmitDecoderBuffers (decoder->decoder_handle,
|
|
buffer_count, buffers);
|
|
gst_d3d11_device_unlock (decoder->device);
|
|
|
|
if (!gst_d3d11_result (hr, decoder->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,
|
|
GstVideoDecoder * videodec)
|
|
{
|
|
GstBuffer *buf = NULL;
|
|
GstFlowReturn ret;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
if (!decoder->internal_pool) {
|
|
gboolean reconfigured;
|
|
|
|
/* Replicate gst_video_decoder_allocate_output_buffer().
|
|
* In case of zero-copy playback, this is the last chance for querying
|
|
* required min-buffer size by downstream and take account of
|
|
* the min-buffer size into our internel pool size */
|
|
GST_VIDEO_DECODER_STREAM_LOCK (videodec);
|
|
reconfigured =
|
|
gst_pad_check_reconfigure (GST_VIDEO_DECODER_SRC_PAD (videodec));
|
|
GST_DEBUG_OBJECT (videodec,
|
|
"Downstream was reconfigured, negotiating again");
|
|
GST_VIDEO_DECODER_STREAM_UNLOCK (videodec);
|
|
|
|
if (reconfigured)
|
|
gst_video_decoder_negotiate (videodec);
|
|
|
|
if (!gst_d3d11_decoder_prepare_output_view_pool (decoder)) {
|
|
GST_ERROR_OBJECT (videodec, "Failed to setup internal pool");
|
|
return NULL;
|
|
}
|
|
} else if (!gst_buffer_pool_set_active (decoder->internal_pool, TRUE)) {
|
|
GST_ERROR_OBJECT (videodec, "Couldn't set active internal pool");
|
|
return NULL;
|
|
}
|
|
|
|
ret = gst_buffer_pool_acquire_buffer (decoder->internal_pool, &buf, NULL);
|
|
|
|
if (ret != GST_FLOW_OK || !buf) {
|
|
if (ret != GST_FLOW_FLUSHING) {
|
|
GST_ERROR_OBJECT (videodec, "Couldn't get buffer from pool, ret %s",
|
|
gst_flow_get_name (ret));
|
|
} else {
|
|
GST_DEBUG_OBJECT (videodec, "We are flusing");
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (!gst_d3d11_decoder_ensure_output_view (decoder, buf)) {
|
|
GST_ERROR_OBJECT (videodec, "Output view unavailable");
|
|
gst_buffer_unref (buf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
ID3D11VideoDecoderOutputView *
|
|
gst_d3d11_decoder_get_output_view_from_buffer (GstD3D11Decoder * decoder,
|
|
GstBuffer * buffer, guint8 * index)
|
|
{
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
ID3D11VideoDecoderOutputView *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, "Not a d3d11 memory");
|
|
return NULL;
|
|
}
|
|
|
|
dmem = (GstD3D11Memory *) mem;
|
|
view = gst_d3d11_memory_get_decoder_output_view (dmem, decoder->video_device,
|
|
&decoder->decoder_profile);
|
|
|
|
if (!view) {
|
|
GST_ERROR_OBJECT (decoder, "Decoder output view is unavailable");
|
|
return NULL;
|
|
}
|
|
|
|
if (index) {
|
|
if (decoder->use_array_of_texture) {
|
|
guint8 id;
|
|
gpointer val = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
|
|
gst_d3d11_decoder_view_id_quark ());
|
|
if (!val) {
|
|
GST_ERROR_OBJECT (decoder, "memory has no qdata");
|
|
return NULL;
|
|
}
|
|
|
|
id = (guint8) GPOINTER_TO_UINT (val);
|
|
g_assert (id < 128);
|
|
|
|
*index = (id - 1);
|
|
} else {
|
|
*index = gst_d3d11_memory_get_subresource_index (dmem);
|
|
}
|
|
}
|
|
|
|
return view;
|
|
}
|
|
|
|
static gboolean
|
|
copy_to_system (GstD3D11Decoder * self, GstBuffer * decoder_buffer,
|
|
GstBuffer * output)
|
|
{
|
|
GstVideoFrame out_frame;
|
|
GstVideoInfo *info = &self->output_info;
|
|
guint i;
|
|
GstD3D11Memory *in_mem;
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
HRESULT hr;
|
|
ID3D11Texture2D *in_texture;
|
|
guint in_subresource_index;
|
|
ID3D11DeviceContext *device_context =
|
|
gst_d3d11_device_get_device_context_handle (self->device);
|
|
|
|
if (!gst_d3d11_decoder_ensure_staging_texture (self)) {
|
|
GST_ERROR_OBJECT (self, "Staging texture is not available");
|
|
return FALSE;
|
|
}
|
|
|
|
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);
|
|
|
|
in_texture = gst_d3d11_memory_get_texture_handle (in_mem);
|
|
in_subresource_index = gst_d3d11_memory_get_subresource_index (in_mem);
|
|
|
|
gst_d3d11_device_lock (self->device);
|
|
device_context->CopySubresourceRegion (self->staging, 0, 0, 0, 0,
|
|
in_texture, in_subresource_index, NULL);
|
|
|
|
hr = device_context->Map (self->staging, 0, D3D11_MAP_READ, 0, &map);
|
|
|
|
if (!gst_d3d11_result (hr, self->device)) {
|
|
GST_ERROR_OBJECT (self, "Failed to map, hr: 0x%x", (guint) hr);
|
|
|
|
gst_d3d11_device_unlock (self->device);
|
|
gst_video_frame_unmap (&out_frame);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* calculate stride and offset only once */
|
|
if (self->stating_texture_stride[0] == 0) {
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
gsize dummy;
|
|
|
|
self->staging->GetDesc (&desc);
|
|
|
|
gst_d3d11_dxgi_format_get_size (desc.Format, desc.Width, desc.Height,
|
|
map.RowPitch, self->staging_texture_offset,
|
|
self->stating_texture_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 + self->staging_texture_offset[i];
|
|
dst = (guint8 *) 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 += self->stating_texture_stride[i];
|
|
}
|
|
}
|
|
|
|
gst_video_frame_unmap (&out_frame);
|
|
device_context->Unmap (self->staging, 0);
|
|
gst_d3d11_device_unlock (self->device);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
copy_to_d3d11 (GstD3D11Decoder * self, GstBuffer * decoder_buffer,
|
|
GstBuffer * output)
|
|
{
|
|
GstVideoInfo *info = &self->output_info;
|
|
GstD3D11Memory *in_mem;
|
|
GstD3D11Memory *out_mem;
|
|
GstMapInfo out_map;
|
|
D3D11_BOX src_box;
|
|
ID3D11Texture2D *in_texture;
|
|
guint in_subresource_index, out_subresource_index;
|
|
ID3D11DeviceContext *device_context =
|
|
gst_d3d11_device_get_device_context_handle (self->device);
|
|
|
|
in_mem = (GstD3D11Memory *) gst_buffer_peek_memory (decoder_buffer, 0);
|
|
out_mem = (GstD3D11Memory *) gst_buffer_peek_memory (output, 0);
|
|
|
|
if (!gst_memory_map (GST_MEMORY_CAST (out_mem),
|
|
&out_map, (GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map output d3d11 memory");
|
|
return FALSE;
|
|
}
|
|
|
|
gst_d3d11_device_lock (self->device);
|
|
in_texture = gst_d3d11_memory_get_texture_handle (in_mem);
|
|
in_subresource_index = gst_d3d11_memory_get_subresource_index (in_mem);
|
|
|
|
src_box.left = 0;
|
|
src_box.top = 0;
|
|
src_box.front = 0;
|
|
src_box.back = 1;
|
|
|
|
src_box.right = GST_ROUND_UP_2 (GST_VIDEO_INFO_WIDTH (info));
|
|
src_box.bottom = GST_ROUND_UP_2 (GST_VIDEO_INFO_HEIGHT (info));
|
|
|
|
out_subresource_index = gst_d3d11_memory_get_subresource_index (out_mem);
|
|
device_context->CopySubresourceRegion ((ID3D11Resource *) out_map.data,
|
|
out_subresource_index, 0, 0, 0, in_texture, in_subresource_index,
|
|
&src_box);
|
|
|
|
gst_d3d11_device_unlock (self->device);
|
|
gst_memory_unmap (GST_MEMORY_CAST (out_mem), &out_map);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_process_output (GstD3D11Decoder * decoder,
|
|
GstVideoDecoder * videodec, gint display_width, gint display_height,
|
|
GstBuffer * decoder_buffer, GstBuffer ** output)
|
|
{
|
|
gboolean can_device_copy = TRUE;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), FALSE);
|
|
g_return_val_if_fail (GST_IS_BUFFER (decoder_buffer), FALSE);
|
|
g_return_val_if_fail (output != NULL, FALSE);
|
|
|
|
if (display_width != GST_VIDEO_INFO_WIDTH (&decoder->output_info) ||
|
|
display_height != GST_VIDEO_INFO_HEIGHT (&decoder->output_info)) {
|
|
GST_INFO_OBJECT (videodec, "Frame size changed, do renegotiate");
|
|
|
|
gst_video_info_set_format (&decoder->output_info,
|
|
GST_VIDEO_INFO_FORMAT (&decoder->info), display_width, display_height);
|
|
GST_VIDEO_INFO_INTERLACE_MODE (&decoder->output_info) =
|
|
GST_VIDEO_INFO_INTERLACE_MODE (&decoder->info);
|
|
|
|
if (!gst_video_decoder_negotiate (videodec)) {
|
|
GST_ERROR_OBJECT (videodec, "Failed to re-negotiate with new frame size");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (gst_d3d11_decoder_can_direct_render (decoder, videodec, decoder_buffer,
|
|
display_width, display_height)) {
|
|
GstMemory *mem;
|
|
|
|
mem = gst_buffer_peek_memory (decoder_buffer, 0);
|
|
GST_MINI_OBJECT_FLAG_SET (mem, GST_D3D11_MEMORY_TRANSFER_NEED_DOWNLOAD);
|
|
|
|
*output = gst_buffer_ref (decoder_buffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
*output = gst_video_decoder_allocate_output_buffer (videodec);
|
|
if (*output == NULL) {
|
|
GST_ERROR_OBJECT (videodec, "Couldn't allocate output buffer");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* decoder buffer must have single memory */
|
|
if (gst_buffer_n_memory (decoder_buffer) == gst_buffer_n_memory (*output)) {
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
|
|
mem = gst_buffer_peek_memory (*output, 0);
|
|
if (!gst_is_d3d11_memory (mem)) {
|
|
can_device_copy = FALSE;
|
|
goto do_process;
|
|
}
|
|
|
|
dmem = (GstD3D11Memory *) mem;
|
|
if (dmem->device != decoder->device)
|
|
can_device_copy = FALSE;
|
|
} else {
|
|
can_device_copy = FALSE;
|
|
}
|
|
|
|
do_process:
|
|
if (can_device_copy) {
|
|
return copy_to_d3d11 (decoder, decoder_buffer, *output);
|
|
}
|
|
|
|
return copy_to_system (decoder, decoder_buffer, *output);
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_negotiate (GstD3D11Decoder * decoder,
|
|
GstVideoDecoder * videodec)
|
|
{
|
|
GstVideoInfo *info;
|
|
GstCaps *peer_caps;
|
|
GstVideoCodecState *state = NULL;
|
|
gboolean alternate_interlaced;
|
|
gboolean alternate_supported = FALSE;
|
|
gboolean d3d11_supported = FALSE;
|
|
GstVideoCodecState *input_state;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), FALSE);
|
|
|
|
info = &decoder->output_info;
|
|
input_state = decoder->input_state;
|
|
|
|
alternate_interlaced =
|
|
(GST_VIDEO_INFO_INTERLACE_MODE (info) ==
|
|
GST_VIDEO_INTERLACE_MODE_ALTERNATE);
|
|
|
|
peer_caps = gst_pad_get_allowed_caps (GST_VIDEO_DECODER_SRC_PAD (videodec));
|
|
GST_DEBUG_OBJECT (videodec, "Allowed caps %" GST_PTR_FORMAT, peer_caps);
|
|
|
|
if (!peer_caps || gst_caps_is_any (peer_caps)) {
|
|
GST_DEBUG_OBJECT (videodec,
|
|
"cannot determine output format, use system memory");
|
|
} else {
|
|
GstCapsFeatures *features;
|
|
guint size = gst_caps_get_size (peer_caps);
|
|
guint i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
features = gst_caps_get_features (peer_caps, i);
|
|
|
|
if (!features)
|
|
continue;
|
|
|
|
if (gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
|
|
d3d11_supported = TRUE;
|
|
}
|
|
|
|
/* FIXME: software deinterlace element will not return interlaced caps
|
|
* feature... We should fix it */
|
|
if (gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_FORMAT_INTERLACED)) {
|
|
alternate_supported = TRUE;
|
|
}
|
|
}
|
|
}
|
|
gst_clear_caps (&peer_caps);
|
|
|
|
GST_DEBUG_OBJECT (videodec,
|
|
"Downstream feature support, D3D11 memory: %d, interlaced format %d",
|
|
d3d11_supported, alternate_supported);
|
|
|
|
if (alternate_interlaced) {
|
|
/* FIXME: D3D11 cannot support alternating interlaced stream yet */
|
|
GST_FIXME_OBJECT (videodec,
|
|
"Implement alternating interlaced stream for D3D11");
|
|
|
|
if (alternate_supported) {
|
|
gint height = GST_VIDEO_INFO_HEIGHT (info);
|
|
|
|
/* Set caps resolution with display size, that's how we designed
|
|
* for alternating interlaced stream */
|
|
height = 2 * height;
|
|
state = gst_video_decoder_set_interlaced_output_state (videodec,
|
|
GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_INTERLACE_MODE (info),
|
|
GST_VIDEO_INFO_WIDTH (info), height, input_state);
|
|
} else {
|
|
GST_WARNING_OBJECT (videodec,
|
|
"Downstream doesn't support alternating interlaced stream");
|
|
|
|
state = gst_video_decoder_set_output_state (videodec,
|
|
GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
|
|
GST_VIDEO_INFO_HEIGHT (info), input_state);
|
|
|
|
/* XXX: adjust PAR, this would produce output similar to that of
|
|
* "line doubling" (so called bob deinterlacing) processing.
|
|
* apart from missing anchor line (top-field or bottom-field) information.
|
|
* Potentially flickering could happen. So this might not be correct.
|
|
* But it would be better than negotiation error of half-height squeezed
|
|
* image */
|
|
state->info.par_d *= 2;
|
|
state->info.fps_n *= 2;
|
|
}
|
|
} else {
|
|
state = gst_video_decoder_set_interlaced_output_state (videodec,
|
|
GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_INTERLACE_MODE (info),
|
|
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), input_state);
|
|
}
|
|
|
|
if (!state) {
|
|
GST_ERROR_OBJECT (decoder, "Couldn't set output state");
|
|
return FALSE;
|
|
}
|
|
|
|
state->caps = gst_video_info_to_caps (&state->info);
|
|
|
|
g_clear_pointer (&decoder->output_state, gst_video_codec_state_unref);
|
|
decoder->output_state = state;
|
|
|
|
if (d3d11_supported) {
|
|
gst_caps_set_features (state->caps, 0,
|
|
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, NULL));
|
|
}
|
|
|
|
decoder->downstream_supports_d3d11 = d3d11_supported;
|
|
|
|
return gst_d3d11_decoder_open (decoder);
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_decide_allocation (GstD3D11Decoder * decoder,
|
|
GstVideoDecoder * videodec, GstQuery * query)
|
|
{
|
|
GstCaps *outcaps;
|
|
GstBufferPool *pool = NULL;
|
|
guint n, size, min = 0, max = 0;
|
|
GstVideoInfo vinfo = { 0, };
|
|
GstStructure *config;
|
|
GstD3D11AllocationParams *d3d11_params;
|
|
gboolean use_d3d11_pool;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
g_return_val_if_fail (GST_IS_VIDEO_DECODER (videodec), FALSE);
|
|
g_return_val_if_fail (query != NULL, FALSE);
|
|
|
|
if (!decoder->opened) {
|
|
GST_ERROR_OBJECT (videodec, "Should open decoder first");
|
|
return FALSE;
|
|
}
|
|
|
|
gst_query_parse_allocation (query, &outcaps, NULL);
|
|
|
|
if (!outcaps) {
|
|
GST_DEBUG_OBJECT (decoder, "No output caps");
|
|
return FALSE;
|
|
}
|
|
|
|
use_d3d11_pool = decoder->downstream_supports_d3d11;
|
|
|
|
gst_video_info_from_caps (&vinfo, outcaps);
|
|
n = gst_query_get_n_allocation_pools (query);
|
|
if (n > 0)
|
|
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
|
|
|
/* create our own pool */
|
|
if (pool && use_d3d11_pool) {
|
|
if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
|
|
GST_DEBUG_OBJECT (videodec,
|
|
"Downstream pool is not d3d11, will create new one");
|
|
gst_clear_object (&pool);
|
|
} else {
|
|
GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
|
|
if (dpool->device != decoder->device) {
|
|
GST_DEBUG_OBJECT (videodec, "Different device, will create new one");
|
|
gst_clear_object (&pool);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pool) {
|
|
if (use_d3d11_pool)
|
|
pool = gst_d3d11_buffer_pool_new (decoder->device);
|
|
else
|
|
pool = gst_video_buffer_pool_new ();
|
|
|
|
size = (guint) vinfo.size;
|
|
}
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
|
|
if (use_d3d11_pool) {
|
|
GstVideoAlignment align;
|
|
gint width, height;
|
|
|
|
gst_video_alignment_reset (&align);
|
|
|
|
d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
|
|
if (!d3d11_params)
|
|
d3d11_params = gst_d3d11_allocation_params_new (decoder->device, &vinfo,
|
|
(GstD3D11AllocationFlags) 0, 0);
|
|
|
|
width = GST_VIDEO_INFO_WIDTH (&vinfo);
|
|
height = GST_VIDEO_INFO_HEIGHT (&vinfo);
|
|
|
|
/* need alignment to copy decoder output texture to downstream texture */
|
|
align.padding_right = GST_ROUND_UP_16 (width) - width;
|
|
align.padding_bottom = GST_ROUND_UP_16 (height) - height;
|
|
if (!gst_d3d11_allocation_params_alignment (d3d11_params, &align)) {
|
|
GST_ERROR_OBJECT (videodec, "Cannot set alignment");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Needs render target bind flag so that it can be used for
|
|
* output of shader pipeline if internal resizing is required.
|
|
* Also, downstream can keep using video processor even if we copy
|
|
* some decoded textures into downstream buffer */
|
|
d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
|
|
|
|
gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
|
|
gst_d3d11_allocation_params_free (d3d11_params);
|
|
|
|
/* Store min buffer size. We need to take account of the amount of buffers
|
|
* which might be held by downstream in case of zero-copy playback */
|
|
if (!decoder->internal_pool) {
|
|
if (n > 0) {
|
|
GST_DEBUG_OBJECT (videodec, "Downstream proposed pool");
|
|
decoder->wait_on_pool_full = TRUE;
|
|
/* XXX: hardcoded bound 16, to avoid too large pool size */
|
|
decoder->downstream_min_buffers = MIN (min, 16);
|
|
} else {
|
|
GST_DEBUG_OBJECT (videodec, "Downstream didn't propose pool");
|
|
decoder->wait_on_pool_full = FALSE;
|
|
/* don't know how many buffers would be queued by downstream */
|
|
decoder->downstream_min_buffers = 4;
|
|
}
|
|
} else {
|
|
/* We configured our DPB pool already, let's check if our margin can
|
|
* cover min size */
|
|
decoder->wait_on_pool_full = FALSE;
|
|
|
|
if (n > 0) {
|
|
if (decoder->downstream_min_buffers >= min)
|
|
decoder->wait_on_pool_full = TRUE;
|
|
|
|
GST_DEBUG_OBJECT (videodec,
|
|
"Pre-allocated margin %d can%s cover downstream min size %d",
|
|
decoder->downstream_min_buffers,
|
|
decoder->wait_on_pool_full ? "" : "not", min);
|
|
} else {
|
|
GST_DEBUG_OBJECT (videodec, "Downstream min size is unknown");
|
|
}
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (videodec, "Downstream min buffres: %d", min);
|
|
}
|
|
|
|
gst_buffer_pool_set_config (pool, config);
|
|
if (use_d3d11_pool) {
|
|
/* d3d11 buffer pool will update buffer size based on allocated texture,
|
|
* get size from config again */
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_get_params (config,
|
|
nullptr, &size, nullptr, nullptr);
|
|
gst_structure_free (config);
|
|
}
|
|
|
|
if (n > 0)
|
|
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
|
|
else
|
|
gst_query_add_allocation_pool (query, pool, size, min, max);
|
|
gst_object_unref (pool);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_d3d11_decoder_set_flushing (GstD3D11Decoder * decoder,
|
|
GstVideoDecoder * videodec, gboolean flushing)
|
|
{
|
|
g_return_val_if_fail (GST_IS_D3D11_DECODER (decoder), FALSE);
|
|
|
|
g_mutex_lock (&decoder->internal_pool_lock);
|
|
if (decoder->internal_pool)
|
|
gst_buffer_pool_set_flushing (decoder->internal_pool, flushing);
|
|
g_mutex_unlock (&decoder->internal_pool_lock);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_decoder_can_direct_render (GstD3D11Decoder * decoder,
|
|
GstVideoDecoder * videodec, GstBuffer * view_buffer,
|
|
gint display_width, gint display_height)
|
|
{
|
|
GstMemory *mem;
|
|
GstD3D11PoolAllocator *alloc;
|
|
guint max_size = 0, outstanding_size = 0;
|
|
|
|
/* We don't support direct render for reverse playback */
|
|
if (videodec->input_segment.rate < 0)
|
|
return FALSE;
|
|
|
|
if (!decoder->can_direct_rendering || !decoder->downstream_supports_d3d11)
|
|
return FALSE;
|
|
|
|
/* different size, need copy */
|
|
/* TODO: crop meta */
|
|
if (display_width != GST_VIDEO_INFO_WIDTH (&decoder->info) ||
|
|
display_height != GST_VIDEO_INFO_HEIGHT (&decoder->info))
|
|
return FALSE;
|
|
|
|
/* we can do direct render in this case, since there is no DPB pool size
|
|
* limit */
|
|
if (decoder->use_array_of_texture)
|
|
return TRUE;
|
|
|
|
/* Let's believe downstream info */
|
|
if (decoder->wait_on_pool_full)
|
|
return TRUE;
|
|
|
|
/* Check if we are about to full */
|
|
mem = gst_buffer_peek_memory (view_buffer, 0);
|
|
|
|
/* something went wrong */
|
|
if (!gst_is_d3d11_memory (mem)) {
|
|
GST_ERROR_OBJECT (decoder, "Not a D3D11 memory");
|
|
return FALSE;
|
|
}
|
|
|
|
alloc = GST_D3D11_POOL_ALLOCATOR (mem->allocator);
|
|
if (!gst_d3d11_pool_allocator_get_pool_size (alloc, &max_size,
|
|
&outstanding_size)) {
|
|
GST_ERROR_OBJECT (decoder, "Couldn't query pool size");
|
|
return FALSE;
|
|
}
|
|
|
|
/* 2 buffer margin */
|
|
if (max_size <= outstanding_size + 1) {
|
|
GST_DEBUG_OBJECT (decoder, "memory pool is about to full (%u/%u)",
|
|
outstanding_size, max_size);
|
|
return FALSE;
|
|
}
|
|
|
|
GST_LOG_OBJECT (decoder, "Can do direct rendering");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
HRESULT hr;
|
|
BOOL can_support = FALSE;
|
|
ID3D11VideoDevice *video_device;
|
|
|
|
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);
|
|
|
|
video_device = decoder->video_device;
|
|
|
|
hr = video_device->CheckVideoDecoderFormat (decoder_profile, format,
|
|
&can_support);
|
|
if (!gst_d3d11_result (hr, decoder->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;
|
|
HRESULT hr;
|
|
UINT config_count;
|
|
ID3D11VideoDevice *video_device;
|
|
|
|
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);
|
|
|
|
video_device = decoder->video_device;
|
|
|
|
desc.SampleWidth = width;
|
|
desc.SampleHeight = height;
|
|
desc.OutputFormat = format;
|
|
desc.Guid = *decoder_profile;
|
|
|
|
hr = video_device->GetVideoDecoderConfigCount (&desc, &config_count);
|
|
if (!gst_d3d11_result (hr, decoder->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);
|
|
}
|