gstreamer/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11testsrc.cpp

2048 lines
61 KiB
C++
Raw Normal View History

/* GStreamer
* Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-d3d11testsrc
* @title: d3d11testsrc
*
* The videotestsrc element is used to produce test video data
*
* ## Example launch line
* ```
* gst-launch-1.0 d3d11testsrc ! queue ! d3d11videosink
* ```
*
* Since: 1.22
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11testsrc.h"
#include "gstd3d11pluginutils.h"
#include <wrl.h>
#include <string.h>
#include <d2d1.h>
#include <math.h>
#include <memory>
#include <vector>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
/* *INDENT-ON* */
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_test_src_debug);
#define GST_CAT_DEFAULT gst_d3d11_test_src_debug
static GstStaticCaps template_caps =
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
(GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SRC_FORMATS) ";"
GST_VIDEO_CAPS_MAKE (GST_D3D11_SRC_FORMATS));
typedef enum
{
GST_D3D11_TEST_SRC_SMPTE,
GST_D3D11_TEST_SRC_SNOW,
GST_D3D11_TEST_SRC_BLACK,
GST_D3D11_TEST_SRC_WHITE,
GST_D3D11_TEST_SRC_RED,
GST_D3D11_TEST_SRC_GREEN,
GST_D3D11_TEST_SRC_BLUE,
GST_D3D11_TEST_SRC_CHECKERS1,
GST_D3D11_TEST_SRC_CHECKERS2,
GST_D3D11_TEST_SRC_CHECKERS4,
GST_D3D11_TEST_SRC_CHECKERS8,
GST_D3D11_TEST_SRC_CIRCULAR,
GST_D3D11_TEST_SRC_BLINK,
/* sync with videotestsrc */
GST_D3D11_TEST_SRC_BALL = 18,
} GstD3D11TestSrcPattern;
/**
* GstD3D11TestSrcPattern:
*
* Since: 1.22
*/
#define GST_TYPE_D3D11_TEST_SRC_PATTERN (gst_d3d11_test_src_pattern_get_type ())
static GType
gst_d3d11_test_src_pattern_get_type (void)
{
static GType pattern_type = 0;
GST_D3D11_CALL_ONCE_BEGIN {
static const GEnumValue pattern_types[] = {
{GST_D3D11_TEST_SRC_SMPTE, "SMPTE 100% color bars", "smpte"},
{GST_D3D11_TEST_SRC_SNOW, "Random (television snow)", "snow"},
{GST_D3D11_TEST_SRC_BLACK, "100% Black", "black"},
{GST_D3D11_TEST_SRC_WHITE, "100% White", "white"},
{GST_D3D11_TEST_SRC_RED, "Red", "red"},
{GST_D3D11_TEST_SRC_GREEN, "Green", "green"},
{GST_D3D11_TEST_SRC_BLUE, "Blue", "blue"},
{GST_D3D11_TEST_SRC_CHECKERS1, "Checkers 1px", "checkers-1"},
{GST_D3D11_TEST_SRC_CHECKERS2, "Checkers 2px", "checkers-2"},
{GST_D3D11_TEST_SRC_CHECKERS4, "Checkers 4px", "checkers-4"},
{GST_D3D11_TEST_SRC_CHECKERS8, "Checkers 8px", "checkers-8"},
/**
* GstD3D11TestSrcPattern::circular:
*
* Since: 1.24
*/
{GST_D3D11_TEST_SRC_CIRCULAR, "Circular", "circular"},
/**
* GstD3D11TestSrcPattern::blink:
*
* Since: 1.24
*/
{GST_D3D11_TEST_SRC_BLINK, "Blink", "blink"},
/**
* GstD3D11TestSrcPattern::ball:
*
* Since: 1.24
*/
{GST_D3D11_TEST_SRC_BALL, "Moving ball", "ball"},
{0, nullptr, nullptr},
};
pattern_type = g_enum_register_static ("GstD3D11TestSrcPattern",
pattern_types);
} GST_D3D11_CALL_ONCE_END;
return pattern_type;
}
typedef struct
{
union
{
struct
{
FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;
};
FLOAT color[4];
};
} ColorValue;
static const ColorValue color_table[] = {
/* white */
{1.0f, 1.0f, 1.0f, 1.0f},
/* yellow */
{1.0f, 1.0f, 0.0f, 1.0f},
/* cyan */
{0.0f, 1.0f, 1.0f, 1.0f},
/* green */
{0.0f, 1.0f, 0.0f, 1.0f},
/* magenta */
{1.0f, 0.0f, 1.0f, 1.0f},
/* red */
{1.0f, 0.0f, 0.0f, 1.0f},
/* blue */
{0.0f, 0.0f, 1.0f, 1.0f},
/* black */
{0.0f, 0.0f, 0.0f, 1.0f},
/* -I */
{0.0, 0.0f, 0.5f, 1.0f},
/* +Q */
{0.0f, 0.5, 1.0f, 1.0f},
/* superblack */
{0.0f, 0.0f, 0.0f, 1.0f},
/* 7.421875% grey */
{19. / 256.0f, 19. / 256.0f, 19. / 256.0, 1.0f},
};
enum
{
COLOR_WHITE = 0,
COLOR_YELLOW,
COLOR_CYAN,
COLOR_GREEN,
COLOR_MAGENTA,
COLOR_RED,
COLOR_BLUE,
COLOR_BLACK,
COLOR_NEG_I,
COLOR_POS_Q,
COLOR_SUPER_BLACK,
COLOR_DARK_GREY,
};
struct SnowConstBuffer
{
FLOAT time = 0.0f;
FLOAT alpha = 1.0f;
FLOAT padding[2];
};
struct CheckerConstBuffer
{
FLOAT width;
FLOAT height;
FLOAT checker_size;
FLOAT alpha = 1.0f;
};
struct GstD3D11TestSrcQuad
{
ComPtr < ID3D11PixelShader > ps;
ComPtr < ID3D11VertexShader > vs;
ComPtr < ID3D11InputLayout > layout;
ComPtr < ID3D11Buffer > vertex_buffer;
ComPtr < ID3D11Buffer > index_buffer;
ComPtr < ID3D11Buffer > const_buffer;
guint vertex_stride = 0;
guint index_count = 0;
gboolean is_checker = FALSE;
gboolean is_snow = FALSE;
CheckerConstBuffer checker_const_buffer;
SnowConstBuffer snow_const_buffer;
};
struct StaticColor
{
ColorValue value;
gboolean is_valid = FALSE;
};
/* *INDENT-OFF* */
struct GstD3D11TestSrcRender
{
StaticColor static_color[2];
std::vector < std::shared_ptr < GstD3D11TestSrcQuad >> quad;
ComPtr <ID3D11RasterizerState> rs;
GstD3D11TestSrcPattern pattern;
};
/* *INDENT-ON* */
struct _GstD3D11TestSrc
{
GstBaseSrc src;
GstD3D11Device *device;
gboolean downstream_supports_d3d11;
GstVideoInfo info;
GstD3D11Converter *converter;
GstBufferPool *render_pool;
GstBufferPool *convert_pool;
guint adapter_index;
GstD3D11TestSrcPattern pattern;
GstD3D11TestSrcRender *render;
D3D11_VIEWPORT viewport;
ID2D1Factory *d2d_factory;
gint64 token;
gfloat alpha;
GstD3D11AlphaMode alpha_mode;
gboolean reverse;
gint64 n_frames;
gint64 accum_frames;
GstClockTime accum_rtime;
GstClockTime running_time;
};
struct UvVertexData
{
struct
{
FLOAT x;
FLOAT y;
FLOAT z;
} position;
struct
{
FLOAT u;
FLOAT v;
} texture;
};
struct ColorVertexData
{
struct
{
FLOAT x;
FLOAT y;
FLOAT z;
} position;
struct
{
FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;
} color;
};
static gboolean
setup_snow_render (GstD3D11TestSrc * self, GstD3D11TestSrcRender * render,
guint on_smpte)
{
HRESULT hr;
D3D11_BUFFER_DESC buffer_desc;
D3D11_MAPPED_SUBRESOURCE map;
UvVertexData *vertex_data;
WORD *indices;
ID3D11Device *device_handle =
gst_d3d11_device_get_device_handle (self->device);
ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (self->device);
ComPtr < ID3D11PixelShader > ps;
ComPtr < ID3D11VertexShader > vs;
ComPtr < ID3D11InputLayout > layout;
ComPtr < ID3D11Buffer > vertex_buffer;
ComPtr < ID3D11Buffer > index_buffer;
ComPtr < ID3D11Buffer > const_buffer;
memset (&buffer_desc, 0, sizeof (buffer_desc));
hr = gst_d3d11_get_vertex_shader_coord (self->device, &vs, &layout);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to compile vertext shader");
return FALSE;
}
hr = gst_d3d11_get_pixel_shader_snow (self->device, &ps);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to compile pixel shader");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (UvVertexData) * 4;
buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &vertex_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create vertex buffer");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (WORD) * 6;
buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &index_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create index buffer");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (SnowConstBuffer);
buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &const_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create constant buffer");
return FALSE;
}
GstD3D11DeviceLockGuard lk (self->device);
hr = context_handle->Map (vertex_buffer.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0,
&map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map vertex buffer");
return FALSE;
}
vertex_data = (UvVertexData *) map.pData;
hr = context_handle->Map (index_buffer.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0,
&map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map index buffer");
context_handle->Unmap (vertex_buffer.Get (), 0);
return FALSE;
}
indices = (WORD *) map.pData;
if (on_smpte) {
FLOAT left, right, top, bottom;
FLOAT left_u, right_u, top_v, bottom_v;
left = 0.5f;
right = 1.0f;
top = -0.5f;
bottom = -1.0f;
left_u = 3.0f / 4.0f;
right_u = 1.0f;
top_v = 3.0f / 4.0f;
bottom_v = 1.0f;
/* bottom left */
vertex_data[0].position.x = left;
vertex_data[0].position.y = bottom;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = left_u;
vertex_data[0].texture.v = bottom_v;
/* top left */
vertex_data[1].position.x = left;
vertex_data[1].position.y = top;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = left_u;
vertex_data[1].texture.v = top_v;
/* top right */
vertex_data[2].position.x = right;
vertex_data[2].position.y = top;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = right_u;
vertex_data[2].texture.v = top_v;
/* bottom right */
vertex_data[3].position.x = right;
vertex_data[3].position.y = bottom;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = right_u;
vertex_data[3].texture.v = bottom_v;
} else {
/* bottom left */
vertex_data[0].position.x = -1.0f;
vertex_data[0].position.y = -1.0f;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = 0.0f;
vertex_data[0].texture.v = 1.0f;
/* top left */
vertex_data[1].position.x = -1.0f;
vertex_data[1].position.y = 1.0f;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = 0.0f;
vertex_data[1].texture.v = 0.0f;
/* top right */
vertex_data[2].position.x = 1.0f;
vertex_data[2].position.y = 1.0f;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = 1.0f;
vertex_data[2].texture.v = 0.0f;
/* bottom right */
vertex_data[3].position.x = 1.0f;
vertex_data[3].position.y = -1.0f;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = 1.0f;
vertex_data[3].texture.v = 1.0f;
}
/* clockwise indexing */
indices[0] = 0; /* bottom left */
indices[1] = 1; /* top left */
indices[2] = 2; /* top right */
indices[3] = 3; /* bottom right */
indices[4] = 0; /* bottom left */
indices[5] = 2; /* top right */
context_handle->Unmap (vertex_buffer.Get (), 0);
context_handle->Unmap (index_buffer.Get (), 0);
auto quad = std::make_shared < GstD3D11TestSrcQuad > ();
quad->ps = ps;
quad->vs = vs;
quad->layout = layout;
quad->vertex_buffer = vertex_buffer;
quad->index_buffer = index_buffer;
quad->const_buffer = const_buffer;
quad->vertex_stride = sizeof (UvVertexData);
quad->index_count = 6;
quad->is_snow = TRUE;
quad->snow_const_buffer.time = 0;
quad->snow_const_buffer.alpha = self->alpha;
render->quad.push_back (quad);
return TRUE;
}
static gboolean
setup_smpte_render (GstD3D11TestSrc * self, GstD3D11TestSrcRender * render)
{
HRESULT hr;
D3D11_BUFFER_DESC buffer_desc;
D3D11_MAPPED_SUBRESOURCE map;
ColorVertexData *vertex_data;
WORD *indices;
ID3D11Device *device_handle =
gst_d3d11_device_get_device_handle (self->device);
ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (self->device);
ComPtr < ID3D11PixelShader > ps;
ComPtr < ID3D11VertexShader > vs;
ComPtr < ID3D11InputLayout > layout;
ComPtr < ID3D11Buffer > vertex_buffer;
ComPtr < ID3D11Buffer > index_buffer;
guint num_vertex = 0;
guint num_index = 0;
memset (&buffer_desc, 0, sizeof (buffer_desc));
hr = gst_d3d11_get_vertex_shader_color (self->device, &vs, &layout);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to compile vertext shader");
return FALSE;
}
hr = gst_d3d11_get_pixel_shader_color (self->device, &ps);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to compile pixel shader");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (ColorVertexData) * 4 * 20;
buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &vertex_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create vertex buffer");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (WORD) * 6 * 20;
buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &index_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create index buffer");
return FALSE;
}
GstD3D11DeviceLockGuard lk (self->device);
hr = context_handle->Map (vertex_buffer.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0,
&map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map vertex buffer");
return FALSE;
}
vertex_data = (ColorVertexData *) map.pData;
hr = context_handle->Map (index_buffer.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0,
&map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map index buffer");
context_handle->Unmap (vertex_buffer.Get (), 0);
return FALSE;
}
indices = (WORD *) map.pData;
/* top row */
for (guint i = 0; i < 7; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 2.0f / 7.0f;
guint base = i * 4;
guint idx_base = i * 6;
const ColorValue *color = &color_table[i];
left = -1.0f + i * scale;
right = -1.0f + (i + 1) * scale;
top = 1.0f;
bottom = -1.0f / 3.0f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = self->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = self->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = self->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = self->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
num_vertex += 4 * 7;
num_index += 6 * 7;
/* middle row */
for (guint i = 0; i < 7; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 2.0f / 7.0f;
guint base = i * 4 + num_vertex;
guint idx_base = i * 6 + num_index;
const ColorValue *color;
if ((i % 2) != 0)
color = &color_table[COLOR_BLACK];
else
color = &color_table[COLOR_BLUE - i];
left = -1.0f + i * scale;
right = -1.0f + (i + 1) * scale;
top = -1.0f / 3.0f;
bottom = -0.5f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = self->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = self->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = self->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = self->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
num_vertex += 4 * 7;
num_index += 6 * 7;
/* bottom row, left three */
for (guint i = 0; i < 3; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 1.0f / 3.0f;
guint base = i * 4 + num_vertex;
guint idx_base = i * 6 + num_index;
const ColorValue *color;
if (i == 0)
color = &color_table[COLOR_NEG_I];
else if (i == 1)
color = &color_table[COLOR_WHITE];
else
color = &color_table[COLOR_POS_Q];
left = -1.0f + i * scale;
right = -1.0f + (i + 1) * scale;
top = -0.5f;
bottom = -1.0f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = self->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = self->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = self->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = self->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
num_vertex += 4 * 3;
num_index += 6 * 3;
/* bottom row, middle three */
for (guint i = 0; i < 3; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 1.0f / 6.0f;
guint base = i * 4 + num_vertex;
guint idx_base = i * 6 + num_index;
const ColorValue *color;
if (i == 0)
color = &color_table[COLOR_SUPER_BLACK];
else if (i == 1)
color = &color_table[COLOR_BLACK];
else
color = &color_table[COLOR_DARK_GREY];
left = i * scale;
right = (i + 1) * scale;
top = -0.5f;
bottom = -1.0f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = self->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = self->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = self->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = self->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
context_handle->Unmap (vertex_buffer.Get (), 0);
context_handle->Unmap (index_buffer.Get (), 0);
auto quad = std::make_shared < GstD3D11TestSrcQuad > ();
quad->ps = ps;
quad->vs = vs;
quad->layout = layout;
quad->vertex_buffer = vertex_buffer;
quad->index_buffer = index_buffer;
quad->vertex_stride = sizeof (ColorVertexData);
quad->index_count = 6 * 20;
render->quad.push_back (quad);
return setup_snow_render (self, render, TRUE);
}
static gboolean
setup_checker_render (GstD3D11TestSrc * self, GstD3D11TestSrcRender * render,
guint checker_size)
{
HRESULT hr;
D3D11_BUFFER_DESC buffer_desc;
D3D11_MAPPED_SUBRESOURCE map;
UvVertexData *vertex_data;
WORD *indices;
ID3D11Device *device_handle =
gst_d3d11_device_get_device_handle (self->device);
ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (self->device);
ComPtr < ID3D11PixelShader > ps;
ComPtr < ID3D11VertexShader > vs;
ComPtr < ID3D11InputLayout > layout;
ComPtr < ID3D11Buffer > vertex_buffer;
ComPtr < ID3D11Buffer > index_buffer;
ComPtr < ID3D11Buffer > const_buffer;
memset (&buffer_desc, 0, sizeof (buffer_desc));
hr = gst_d3d11_get_vertex_shader_coord (self->device, &vs, &layout);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to compile vertext shader");
return FALSE;
}
hr = gst_d3d11_get_pixel_shader_checker (self->device, &ps);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to compile pixel shader");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (UvVertexData) * 4;
buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &vertex_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create vertex buffer");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (WORD) * 6;
buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &index_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create index buffer");
return FALSE;
}
buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
buffer_desc.ByteWidth = sizeof (CheckerConstBuffer);
buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &const_buffer);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to create constant buffer");
return FALSE;
}
GstD3D11DeviceLockGuard lk (self->device);
hr = context_handle->Map (vertex_buffer.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0,
&map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map vertex buffer");
return FALSE;
}
vertex_data = (UvVertexData *) map.pData;
hr = context_handle->Map (index_buffer.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0,
&map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map index buffer");
context_handle->Unmap (vertex_buffer.Get (), 0);
return FALSE;
}
indices = (WORD *) map.pData;
/* bottom left */
vertex_data[0].position.x = -1.0f;
vertex_data[0].position.y = -1.0f;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = 0.0f;
vertex_data[0].texture.v = 1.0f;
/* top left */
vertex_data[1].position.x = -1.0f;
vertex_data[1].position.y = 1.0f;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = 0.0f;
vertex_data[1].texture.v = 0.0f;
/* top right */
vertex_data[2].position.x = 1.0f;
vertex_data[2].position.y = 1.0f;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = 1.0f;
vertex_data[2].texture.v = 0.0f;
/* bottom right */
vertex_data[3].position.x = 1.0f;
vertex_data[3].position.y = -1.0f;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = 1.0f;
vertex_data[3].texture.v = 1.0f;
/* clockwise indexing */
indices[0] = 0; /* bottom left */
indices[1] = 1; /* top left */
indices[2] = 2; /* top right */
indices[3] = 3; /* bottom right */
indices[4] = 0; /* bottom left */
indices[5] = 2; /* top right */
context_handle->Unmap (vertex_buffer.Get (), 0);
context_handle->Unmap (index_buffer.Get (), 0);
auto quad = std::make_shared < GstD3D11TestSrcQuad > ();
quad->ps = ps;
quad->vs = vs;
quad->layout = layout;
quad->vertex_buffer = vertex_buffer;
quad->index_buffer = index_buffer;
quad->const_buffer = const_buffer;
quad->vertex_stride = sizeof (UvVertexData);
quad->index_count = 6;
quad->is_checker = TRUE;
quad->checker_const_buffer.width = self->info.width;
quad->checker_const_buffer.height = self->info.height;
quad->checker_const_buffer.checker_size = checker_size;
quad->checker_const_buffer.alpha = self->alpha;
render->quad.push_back (quad);
return TRUE;
}
static gboolean
setup_d2d_render (GstD3D11TestSrc * self, GstD3D11TestSrcRender * render)
{
HRESULT hr;
ComPtr < ID2D1Factory > d2d_factory;
if (self->d2d_factory)
return TRUE;
hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
IID_PPV_ARGS (&d2d_factory));
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create D2D factory");
return FALSE;
}
self->d2d_factory = d2d_factory.Detach ();
return TRUE;
}
enum
{
PROP_0,
PROP_ADAPTER,
PROP_IS_LIVE,
PROP_PATTERN,
PROP_ALPHA,
PROP_ALPHA_MODE,
};
#define DEFAULT_ADAPTER -1
#define DEFAULT_PATTERN GST_D3D11_TEST_SRC_SMPTE
#define DEFAULT_ALPHA 1.0f
#define DEFAULT_ALPHA_MODE GST_D3D11_ALPHA_MODE_UNSPECIFIED
static void gst_d3d11_test_src_dispose (GObject * object);
static void gst_d3d11_test_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_d3d11_test_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_d3d11_test_src_set_context (GstElement * element,
GstContext * context);
static gboolean gst_d3d11_test_src_is_seekable (GstBaseSrc * bsrc);
static gboolean gst_d3d11_test_src_do_seek (GstBaseSrc * bsrc,
GstSegment * segment);
static GstCaps *gst_d3d11_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
static gboolean gst_d3d11_test_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps);
static gboolean gst_d3d11_test_src_decide_allocation (GstBaseSrc *
bsrc, GstQuery * query);
static gboolean gst_d3d11_test_src_start (GstBaseSrc * bsrc);
static gboolean gst_d3d11_test_src_stop (GstBaseSrc * bsrc);
static gboolean gst_d3d11_test_src_src_query (GstBaseSrc * bsrc,
GstQuery * query);
static void gst_d3d11_test_src_get_times (GstBaseSrc * bsrc,
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
static GstFlowReturn gst_d3d11_test_src_create (GstBaseSrc * bsrc,
guint64 offset, guint size, GstBuffer ** buf);
#define gst_d3d11_test_src_parent_class parent_class
G_DEFINE_TYPE (GstD3D11TestSrc, gst_d3d11_test_src, GST_TYPE_BASE_SRC);
static void
gst_d3d11_test_src_class_init (GstD3D11TestSrcClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
GstCaps *caps;
gobject_class->dispose = gst_d3d11_test_src_dispose;
gobject_class->set_property = gst_d3d11_test_src_set_property;
gobject_class->get_property = gst_d3d11_test_src_get_property;
g_object_class_install_property (gobject_class, PROP_ADAPTER,
g_param_spec_int ("adapter", "Adapter",
"DXGI Adapter index (-1 for any device)",
-1, G_MAXINT32, DEFAULT_ADAPTER,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_IS_LIVE,
g_param_spec_boolean ("is-live", "Is Live",
"Whether to act as a live source", FALSE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_PATTERN,
g_param_spec_enum ("pattern", "Pattern",
"Type of test pattern to generate", GST_TYPE_D3D11_TEST_SRC_PATTERN,
DEFAULT_PATTERN,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
/**
* GstD3D11TestSrc:alpha:
*
* Global alpha value to apply
*
* Since: 1.24
*/
g_object_class_install_property (gobject_class, PROP_ALPHA,
g_param_spec_float ("alpha", "Alpha", "Global alpha value to use",
0, 1, DEFAULT_ALPHA,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
/**
* GstD3D11TestSrc:alpha-mode:
*
* Alpha mode to use
*
* Since: 1.24
*/
g_object_class_install_property (gobject_class, PROP_ALPHA_MODE,
g_param_spec_enum ("alpha-mode", "Alpha Mode",
"alpha mode to use", GST_TYPE_D3D11_ALPHA_MODE, DEFAULT_ALPHA_MODE,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d11_test_src_set_context);
gst_element_class_set_static_metadata (element_class,
"Direct3D11 Test Source", "Source/Video",
"Creates a test video stream", "Seungha Yang <seungha@centricular.com>");
caps = gst_d3d11_get_updated_template_caps (&template_caps);
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps));
gst_caps_unref (caps);
basesrc_class->is_seekable =
GST_DEBUG_FUNCPTR (gst_d3d11_test_src_is_seekable);
basesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_do_seek);
basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_fixate);
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_set_caps);
basesrc_class->decide_allocation =
GST_DEBUG_FUNCPTR (gst_d3d11_test_src_decide_allocation);
basesrc_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_start);
basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_stop);
basesrc_class->query = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_src_query);
basesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_get_times);
basesrc_class->create = GST_DEBUG_FUNCPTR (gst_d3d11_test_src_create);
GST_DEBUG_CATEGORY_INIT (gst_d3d11_test_src_debug, "d3d11testsrc", 0,
"d3d11testsrc");
gst_type_mark_as_plugin_api (GST_TYPE_D3D11_TEST_SRC_PATTERN,
(GstPluginAPIFlags) 0);
gst_type_mark_as_plugin_api (GST_TYPE_D3D11_ALPHA_MODE,
(GstPluginAPIFlags) 0);
}
static void
gst_d3d11_test_src_init (GstD3D11TestSrc * self)
{
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
self->adapter_index = DEFAULT_ADAPTER;
self->pattern = DEFAULT_PATTERN;
self->alpha = DEFAULT_ALPHA;
self->alpha_mode = DEFAULT_ALPHA_MODE;
}
static void
gst_d3d11_test_src_dispose (GObject * object)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (object);
gst_clear_object (&self->device);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_d3d11_test_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (object);
switch (prop_id) {
case PROP_ADAPTER:
self->adapter_index = g_value_get_int (value);
break;
case PROP_IS_LIVE:
gst_base_src_set_live (GST_BASE_SRC (self), g_value_get_boolean (value));
break;
case PROP_PATTERN:
self->pattern = (GstD3D11TestSrcPattern) g_value_get_enum (value);
break;
case PROP_ALPHA:
self->alpha = g_value_get_float (value);
break;
case PROP_ALPHA_MODE:
self->alpha_mode = (GstD3D11AlphaMode) g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_test_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (object);
switch (prop_id) {
case PROP_ADAPTER:
g_value_set_int (value, self->adapter_index);
break;
case PROP_IS_LIVE:
g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (self)));
break;
case PROP_PATTERN:
g_value_set_enum (value, self->pattern);
break;
case PROP_ALPHA:
g_value_set_float (value, self->alpha);
break;
case PROP_ALPHA_MODE:
g_value_set_enum (value, self->alpha_mode);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_test_src_set_context (GstElement * element, GstContext * context)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (element);
gst_d3d11_handle_set_context (element,
context, self->adapter_index, &self->device);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
static gboolean
gst_d3d11_test_src_is_seekable (GstBaseSrc * bsrc)
{
return TRUE;
}
static gboolean
gst_d3d11_test_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
GstClockTime position;
segment->time = segment->start;
position = segment->position;
self->reverse = segment->rate < 0;
/* now move to the position indicated */
if (self->info.fps_n) {
self->n_frames = gst_util_uint64_scale (position,
self->info.fps_n, self->info.fps_d * GST_SECOND);
} else {
self->n_frames = 0;
}
self->accum_frames = 0;
self->accum_rtime = 0;
if (self->info.fps_n) {
self->running_time = gst_util_uint64_scale (self->n_frames,
self->info.fps_d * GST_SECOND, self->info.fps_n);
} else {
/* FIXME : Not sure what to set here */
self->running_time = 0;
}
return TRUE;
}
static GstCaps *
gst_d3d11_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
{
GstStructure *s;
caps = gst_caps_make_writable (caps);
s = gst_caps_get_structure (caps, 0);
gst_structure_fixate_field_nearest_int (s, "width", 320);
gst_structure_fixate_field_nearest_int (s, "height", 240);
gst_structure_fixate_field_nearest_fraction (s, "framerate", 30, 1);
return GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
}
static void
gst_d3d11_test_src_clear_resource (GstD3D11TestSrc * self)
{
if (self->render_pool) {
gst_buffer_pool_set_active (self->render_pool, FALSE);
gst_clear_object (&self->render_pool);
}
if (self->convert_pool) {
gst_buffer_pool_set_active (self->convert_pool, FALSE);
gst_clear_object (&self->convert_pool);
}
if (self->render) {
delete self->render;
self->render = nullptr;
}
gst_clear_object (&self->converter);
}
static gboolean
gst_d3d11_test_src_setup_resource (GstD3D11TestSrc * self, GstCaps * caps)
{
GstVideoInfo draw_info;
GstCaps *draw_caps;
GstD3D11AllocationParams *params;
GstD3D11TestSrcRender *render;
GstStructure *config;
HRESULT hr;
config = gst_structure_new ("converter-config",
GST_D3D11_CONVERTER_OPT_BACKEND, GST_TYPE_D3D11_CONVERTER_BACKEND,
GST_D3D11_CONVERTER_BACKEND_SHADER,
GST_D3D11_CONVERTER_OPT_DEST_ALPHA_MODE,
GST_TYPE_D3D11_CONVERTER_ALPHA_MODE, self->alpha_mode, nullptr);
/* D2D uses premultiplied alpha */
if (self->pattern == GST_D3D11_TEST_SRC_CIRCULAR ||
self->pattern == GST_D3D11_TEST_SRC_BALL) {
gst_structure_set (config, GST_D3D11_CONVERTER_OPT_SRC_ALPHA_MODE,
GST_TYPE_D3D11_CONVERTER_ALPHA_MODE,
GST_D3D11_CONVERTER_ALPHA_MODE_PREMULTIPLIED, nullptr);
}
gst_video_info_set_format (&draw_info, GST_VIDEO_FORMAT_BGRA,
self->info.width, self->info.height);
self->converter = gst_d3d11_converter_new (self->device,
&draw_info, &self->info, config);
if (!self->converter) {
GST_ERROR_OBJECT (self, "Failed to create converter");
goto error;
}
draw_caps = gst_video_info_to_caps (&draw_info);
params = gst_d3d11_allocation_params_new (self->device, &draw_info,
GST_D3D11_ALLOCATION_FLAG_DEFAULT,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX);
self->render_pool = gst_d3d11_buffer_pool_new_with_options (self->device,
draw_caps, params, 0, 0);
gst_d3d11_allocation_params_free (params);
gst_caps_unref (draw_caps);
if (!self->render_pool
|| !gst_buffer_pool_set_active (self->render_pool, TRUE)) {
GST_ERROR_OBJECT (self, "Failed to configure draw pool");
goto error;
}
params = gst_d3d11_allocation_params_new (self->device, &self->info,
GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_RENDER_TARGET, 0);
self->convert_pool = gst_d3d11_buffer_pool_new_with_options (self->device,
caps, params, 0, 0);
gst_d3d11_allocation_params_free (params);
if (!self->convert_pool ||
!gst_buffer_pool_set_active (self->convert_pool, TRUE)) {
GST_ERROR_OBJECT (self, "Failed to configure draw pool");
goto error;
}
self->viewport.TopLeftX = 0;
self->viewport.TopLeftY = 0;
self->viewport.Width = self->info.width;
self->viewport.Height = self->info.height;
self->viewport.MinDepth = 0.0f;
self->viewport.MaxDepth = 1.0f;
self->render = render = new GstD3D11TestSrcRender ();
render->pattern = self->pattern;
hr = gst_d3d11_device_get_rasterizer (self->device, &render->rs);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get rasterizer state");
goto error;
}
switch (self->pattern) {
case GST_D3D11_TEST_SRC_SMPTE:
if (!setup_smpte_render (self, render))
goto error;
break;
case GST_D3D11_TEST_SRC_SNOW:
if (!setup_snow_render (self, render, FALSE))
goto error;
break;
case GST_D3D11_TEST_SRC_BLACK:
render->static_color[0].value = color_table[COLOR_BLACK];
render->static_color[0].value.a = self->alpha;
render->static_color[0].is_valid = TRUE;
break;
case GST_D3D11_TEST_SRC_WHITE:
render->static_color[0].value = color_table[COLOR_WHITE];
render->static_color[0].value.a = self->alpha;
render->static_color[0].is_valid = TRUE;
break;
case GST_D3D11_TEST_SRC_RED:
render->static_color[0].value = color_table[COLOR_RED];
render->static_color[0].value.a = self->alpha;
render->static_color[0].is_valid = TRUE;
break;
case GST_D3D11_TEST_SRC_GREEN:
render->static_color[0].value = color_table[COLOR_GREEN];
render->static_color[0].value.a = self->alpha;
render->static_color[0].is_valid = TRUE;
break;
case GST_D3D11_TEST_SRC_BLUE:
render->static_color[0].value = color_table[COLOR_BLUE];
render->static_color[0].value.a = self->alpha;
render->static_color[0].is_valid = TRUE;
break;
case GST_D3D11_TEST_SRC_CHECKERS1:
if (!setup_checker_render (self, render, 1))
goto error;
break;
case GST_D3D11_TEST_SRC_CHECKERS2:
if (!setup_checker_render (self, render, 2))
goto error;
break;
case GST_D3D11_TEST_SRC_CHECKERS4:
if (!setup_checker_render (self, render, 4))
goto error;
break;
case GST_D3D11_TEST_SRC_CHECKERS8:
if (!setup_checker_render (self, render, 8))
goto error;
break;
case GST_D3D11_TEST_SRC_BLINK:
render->static_color[0].value = color_table[COLOR_BLACK];
render->static_color[0].value.a = self->alpha;
render->static_color[0].is_valid = TRUE;
render->static_color[1].value = color_table[COLOR_WHITE];
render->static_color[1].value.a = self->alpha;
render->static_color[1].is_valid = TRUE;
break;
case GST_D3D11_TEST_SRC_CIRCULAR:
case GST_D3D11_TEST_SRC_BALL:
if (!setup_d2d_render (self, render))
goto error;
break;
}
return TRUE;
error:
gst_d3d11_test_src_clear_resource (self);
return FALSE;
}
static gboolean
gst_d3d11_test_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
GstCapsFeatures *features;
GST_DEBUG_OBJECT (self, "Set caps %" GST_PTR_FORMAT, caps);
gst_d3d11_test_src_clear_resource (self);
features = gst_caps_get_features (caps, 0);
if (features && gst_caps_features_contains (features,
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
self->downstream_supports_d3d11 = TRUE;
} else {
self->downstream_supports_d3d11 = FALSE;
}
GST_OBJECT_LOCK (self);
gst_video_info_from_caps (&self->info, caps);
GST_OBJECT_UNLOCK (self);
if (self->info.fps_d <= 0 || self->info.fps_n <= 0) {
GST_ERROR_OBJECT (self, "Invalid framerate %d/%d", self->info.fps_n,
self->info.fps_d);
return FALSE;
}
gst_base_src_set_blocksize (bsrc, GST_VIDEO_INFO_SIZE (&self->info));
return gst_d3d11_test_src_setup_resource (self, caps);
}
static gboolean
gst_d3d11_test_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
GstBufferPool *pool = nullptr;
GstStructure *config;
GstD3D11AllocationParams *d3d11_params;
GstCaps *caps;
guint min, max, size;
gboolean update_pool;
GstVideoInfo vinfo;
gst_query_parse_allocation (query, &caps, nullptr);
if (!caps) {
GST_ERROR_OBJECT (self, "No output caps");
return FALSE;
}
gst_video_info_from_caps (&vinfo, caps);
if (gst_query_get_n_allocation_pools (query) > 0) {
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
update_pool = TRUE;
} else {
size = GST_VIDEO_INFO_SIZE (&vinfo);
min = max = 0;
update_pool = FALSE;
}
if (pool && self->downstream_supports_d3d11) {
if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
gst_clear_object (&pool);
} else {
GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
if (dpool->device != self->device)
gst_clear_object (&pool);
}
}
if (!pool) {
if (self->downstream_supports_d3d11)
pool = gst_d3d11_buffer_pool_new (self->device);
else
pool = gst_video_buffer_pool_new ();
}
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, min, max);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
if (self->downstream_supports_d3d11) {
d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
if (!d3d11_params) {
d3d11_params = gst_d3d11_allocation_params_new (self->device, &vinfo,
GST_D3D11_ALLOCATION_FLAG_DEFAULT,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, 0);
} else {
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);
}
if (!gst_buffer_pool_set_config (pool, config)) {
GST_ERROR_OBJECT (self, "Failed to set config");
gst_clear_object (&pool);
return FALSE;
}
if (update_pool)
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;
}
static gboolean
gst_d3d11_test_src_start (GstBaseSrc * bsrc)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
if (!gst_d3d11_ensure_element_data (GST_ELEMENT (bsrc), self->adapter_index,
&self->device)) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
("Failed to prepare device"), (nullptr));
return FALSE;
}
self->running_time = 0;
self->reverse = FALSE;
self->n_frames = 0;
self->accum_frames = 0;
self->accum_rtime = 0;
self->token = gst_d3d11_create_user_token ();
gst_video_info_init (&self->info);
return TRUE;
}
static gboolean
gst_d3d11_test_src_stop (GstBaseSrc * bsrc)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
gst_d3d11_test_src_clear_resource (self);
gst_clear_object (&self->device);
GST_D3D11_CLEAR_COM (self->d2d_factory);
return TRUE;
}
static gboolean
gst_d3d11_test_src_src_query (GstBaseSrc * bsrc, GstQuery * query)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
if (gst_d3d11_handle_context_query (GST_ELEMENT_CAST (self), query,
self->device)) {
return TRUE;
}
break;
case GST_QUERY_LATENCY:
GST_OBJECT_LOCK (self);
if (self->info.fps_n > 0 && self->info.fps_d > 0) {
GstClockTime latency;
latency =
gst_util_uint64_scale (GST_SECOND, self->info.fps_d,
self->info.fps_n);
GST_OBJECT_UNLOCK (self);
gst_query_set_latency (query,
gst_base_src_is_live (bsrc), latency, GST_CLOCK_TIME_NONE);
GST_DEBUG_OBJECT (self, "Reporting latency of %" GST_TIME_FORMAT,
GST_TIME_ARGS (latency));
return TRUE;
}
GST_OBJECT_UNLOCK (self);
break;
case GST_QUERY_DURATION:
if (bsrc->num_buffers > 0) {
GstFormat format;
gst_query_parse_duration (query, &format, nullptr);
if (format != GST_FORMAT_TIME)
return FALSE;
GST_OBJECT_LOCK (self);
if (format == GST_FORMAT_TIME && self->info.fps_n > 0 &&
self->info.fps_d > 0) {
gint64 dur;
dur = gst_util_uint64_scale_int_round (bsrc->num_buffers
* GST_SECOND, self->info.fps_d, self->info.fps_n);
gst_query_set_duration (query, GST_FORMAT_TIME, dur);
GST_OBJECT_UNLOCK (self);
return TRUE;
}
GST_OBJECT_UNLOCK (self);
}
break;
default:
break;
}
return GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
}
static void
gst_d3d11_test_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end)
{
/* for live sources, sync on the timestamp of the buffer */
if (gst_base_src_is_live (bsrc)) {
GstClockTime timestamp = GST_BUFFER_PTS (buffer);
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
/* get duration to calculate end time */
GstClockTime duration = GST_BUFFER_DURATION (buffer);
if (GST_CLOCK_TIME_IS_VALID (duration)) {
*end = timestamp + duration;
}
*start = timestamp;
}
} else {
*start = -1;
*end = -1;
}
}
struct GstD3D11TestSrcD2DData
{
ID2D1RenderTarget *target;
ID2D1Brush *brush;
ID2D1Factory *factory;
};
static void
gst_d3d11_test_src_d2d_data_free (GstD3D11TestSrcD2DData * data)
{
GST_D3D11_CLEAR_COM (data->brush);
GST_D3D11_CLEAR_COM (data->target);
GST_D3D11_CLEAR_COM (data->factory);
g_free (data);
}
static gboolean
gst_d3d11_test_src_draw_ball (GstD3D11TestSrc * self,
ID3D11DeviceContext * context, GstD3D11Memory * mem)
{
GstD3D11TestSrcD2DData *data;
HRESULT hr;
gdouble rad;
FLOAT x, y;
ID2D1RadialGradientBrush *ball_brush;
data = (GstD3D11TestSrcD2DData *)
gst_d3d11_memory_get_token_data (mem, self->token);
if (!data) {
ComPtr < IDXGISurface > surface;
ComPtr < ID2D1RenderTarget > d2d_target;
ComPtr < ID2D1GradientStopCollection > collection;
ComPtr < ID2D1RadialGradientBrush > brush;
ID3D11Texture2D *texture;
D2D1_RENDER_TARGET_PROPERTIES props;
D2D1_GRADIENT_STOP stops[3];
props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
props.dpiX = 0;
props.dpiY = 0;
props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
texture = (ID3D11Texture2D *) gst_d3d11_memory_get_resource_handle (mem);
hr = texture->QueryInterface (IID_PPV_ARGS (&surface));
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get DXGI surface");
return FALSE;
}
hr = self->d2d_factory->CreateDxgiSurfaceRenderTarget (surface.Get (),
props, &d2d_target);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get D2D render target");
return FALSE;
}
stops[0].color = D2D1::ColorF (D2D1::ColorF::White, self->alpha);
stops[0].position = 0.0f;
stops[1].color = D2D1::ColorF (D2D1::ColorF::Snow, self->alpha);
stops[1].position = 0.3f;
stops[2].color = D2D1::ColorF (D2D1::ColorF::Black, self->alpha);
stops[2].position = 1.0f;
hr = d2d_target->CreateGradientStopCollection (stops, 3, D2D1_GAMMA_1_0,
D2D1_EXTEND_MODE_CLAMP, &collection);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create gradient stop collection");
return FALSE;
}
hr = d2d_target->CreateRadialGradientBrush (D2D1::
RadialGradientBrushProperties (D2D1::Point2F (0, 0), D2D1::Point2F (0,
0), 20, 20), collection.Get (), &brush);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create brush");
return FALSE;
}
data = g_new0 (GstD3D11TestSrcD2DData, 1);
data->target = d2d_target.Detach ();
data->brush = brush.Detach ();
data->factory = self->d2d_factory;
self->d2d_factory->AddRef ();
gst_d3d11_memory_set_token_data (mem, self->token, data,
(GDestroyNotify) gst_d3d11_test_src_d2d_data_free);
}
rad = (gdouble) self->n_frames / 200;
rad = 2 * G_PI * rad;
x = 20 + (0.5 + 0.5 * sin (rad)) * (self->info.width - 40);
y = 20 + (0.5 + 0.5 * sin (rad * sqrt (2))) * (self->info.height - 40);
ball_brush = (ID2D1RadialGradientBrush *) data->brush;
ball_brush->SetCenter (D2D1::Point2F (x, y));
data->target->BeginDraw ();
data->target->Clear (D2D1::ColorF (D2D1::ColorF::Black));
data->target->FillEllipse (D2D1::Ellipse (D2D1::Point2F (x, y), 20, 20),
data->brush);
data->target->EndDraw ();
return TRUE;
}
static gboolean
gst_d3d11_test_src_draw_circular (GstD3D11TestSrc * self,
ID3D11DeviceContext * context, GstD3D11Memory * mem)
{
GstD3D11TestSrcD2DData *data;
HRESULT hr;
FLOAT x, y;
FLOAT rad;
rad = ((FLOAT) MAX (self->info.width, self->info.height)) / 2;
x = (FLOAT) self->info.width / 2;
y = (FLOAT) self->info.height / 2;
data = (GstD3D11TestSrcD2DData *)
gst_d3d11_memory_get_token_data (mem, self->token);
if (!data) {
ComPtr < IDXGISurface > surface;
ComPtr < ID2D1RenderTarget > d2d_target;
ComPtr < ID2D1GradientStopCollection > collection;
ComPtr < ID2D1RadialGradientBrush > brush;
ID3D11Texture2D *texture;
D2D1_RENDER_TARGET_PROPERTIES props;
D2D1_GRADIENT_STOP stops[129];
FLOAT position = 1.0f;
props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
props.dpiX = 0;
props.dpiY = 0;
props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
texture = (ID3D11Texture2D *) gst_d3d11_memory_get_resource_handle (mem);
hr = texture->QueryInterface (IID_PPV_ARGS (&surface));
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get DXGI surface");
return FALSE;
}
hr = self->d2d_factory->CreateDxgiSurfaceRenderTarget (surface.Get (),
props, &d2d_target);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get D2D render target");
return FALSE;
}
for (guint i = 0; i < G_N_ELEMENTS (stops); i++) {
FLOAT diff;
if ((i % 2) == 0)
stops[i].color = D2D1::ColorF (D2D1::ColorF::Black, self->alpha);
else
stops[i].color = D2D1::ColorF (D2D1::ColorF::White, self->alpha);
stops[i].position = position;
diff = position / G_N_ELEMENTS (stops) * 2;
position -= diff;
}
hr = d2d_target->CreateGradientStopCollection (stops, G_N_ELEMENTS (stops),
&collection);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create gradient stop collection");
return FALSE;
}
hr = d2d_target->CreateRadialGradientBrush (D2D1::
RadialGradientBrushProperties (D2D1::Point2F (x, y), D2D1::Point2F (0,
0), rad, rad), collection.Get (), &brush);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create brush");
return FALSE;
}
data = g_new0 (GstD3D11TestSrcD2DData, 1);
data->target = d2d_target.Detach ();
data->brush = brush.Detach ();
data->factory = self->d2d_factory;
self->d2d_factory->AddRef ();
gst_d3d11_memory_set_token_data (mem, self->token, data,
(GDestroyNotify) gst_d3d11_test_src_d2d_data_free);
}
data->target->BeginDraw ();
data->target->Clear (D2D1::ColorF (D2D1::ColorF::Black));
data->target->FillEllipse (D2D1::Ellipse (D2D1::Point2F (x, y), rad, rad),
data->brush);
data->target->EndDraw ();
return TRUE;
}
static gboolean
gst_d3d11_test_src_draw_pattern (GstD3D11TestSrc * self,
ID3D11DeviceContext * context, GstD3D11Memory * mem,
ID3D11RenderTargetView * rtv, GstClockTime pts)
{
GstD3D11TestSrcRender *render = self->render;
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE map;
UINT offsets = 0;
if (render->static_color[0].is_valid) {
if (render->static_color[1].is_valid && (self->n_frames % 2) == 1)
context->ClearRenderTargetView (rtv, render->static_color[1].value.color);
else
context->ClearRenderTargetView (rtv, render->static_color[0].value.color);
return TRUE;
}
if (render->pattern == GST_D3D11_TEST_SRC_BALL)
return gst_d3d11_test_src_draw_ball (self, context, mem);
else if (render->pattern == GST_D3D11_TEST_SRC_CIRCULAR)
return gst_d3d11_test_src_draw_circular (self, context, mem);
context->IASetPrimitiveTopology (D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->RSSetViewports (1, &self->viewport);
context->RSSetState (render->rs.Get ());
context->OMSetRenderTargets (1, &rtv, nullptr);
context->OMSetBlendState (nullptr, nullptr, 0xffffffff);
/* *INDENT-OFF* */
for (auto quad : render->quad) {
if (quad->const_buffer) {
hr = context->Map (quad->const_buffer.Get (),
0, D3D11_MAP_WRITE_DISCARD, 0, &map);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Failed to map constant buffer");
return FALSE;
}
if (quad->is_snow) {
SnowConstBuffer *const_buf = (SnowConstBuffer *) map.pData;
const_buf->time = (FLOAT) pts / GST_SECOND;
const_buf->alpha = self->alpha;
} else if (quad->is_checker) {
CheckerConstBuffer *const_buf = (CheckerConstBuffer *) map.pData;
quad->checker_const_buffer.alpha = self->alpha;
memcpy (const_buf, &quad->checker_const_buffer,
sizeof (CheckerConstBuffer));
}
context->Unmap (quad->const_buffer.Get (), 0);
ID3D11Buffer *const_bufs[] = { quad->const_buffer.Get () };
context->PSSetConstantBuffers (0, 1, const_bufs);
} else {
context->PSSetConstantBuffers (0, 0, nullptr);
}
context->IASetInputLayout (quad->layout.Get ());
ID3D11Buffer *vertex_buf[] = { quad->vertex_buffer.Get () };
context->IASetVertexBuffers (0, 1, vertex_buf,
&quad->vertex_stride, &offsets);
context->IASetIndexBuffer (quad->index_buffer.Get (), DXGI_FORMAT_R16_UINT,
0);
context->VSSetShader (quad->vs.Get (), nullptr, 0);
context->PSSetShader (quad->ps.Get (), nullptr, 0);
context->DrawIndexed (quad->index_count, 0, 0);
}
/* *INDENT-ON* */
context->OMSetRenderTargets (0, nullptr, nullptr);
return TRUE;
}
static GstFlowReturn
gst_d3d11_test_src_create (GstBaseSrc * bsrc, guint64 offset,
guint size, GstBuffer ** buf)
{
GstD3D11TestSrc *self = GST_D3D11_TEST_SRC (bsrc);
GstBuffer *buffer = nullptr;
GstBuffer *render_buffer = nullptr;
GstBuffer *convert_buffer = nullptr;
GstFlowReturn ret = GST_FLOW_ERROR;
GstClockTime pts;
GstClockTime next_time;
GstMapInfo render_info;
ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (self->device);
GstMemory *mem;
GstD3D11Memory *dmem;
ID3D11RenderTargetView *pattern_rtv;
gboolean convert_ret;
GstD3D11DeviceLockGuard lk (self->device);
ret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc, offset, size, &buffer);
if (ret != GST_FLOW_OK)
return ret;
ret =
gst_buffer_pool_acquire_buffer (self->render_pool, &render_buffer,
nullptr);
if (ret != GST_FLOW_OK)
goto error;
if (self->downstream_supports_d3d11) {
convert_buffer = buffer;
} else {
ret = gst_buffer_pool_acquire_buffer (self->convert_pool,
&convert_buffer, nullptr);
if (ret != GST_FLOW_OK)
goto error;
}
mem = gst_buffer_peek_memory (render_buffer, 0);
if (!gst_memory_map (mem, &render_info,
(GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
GST_ERROR_OBJECT (self, "Failed to map render buffer");
goto error;
}
dmem = GST_D3D11_MEMORY_CAST (mem);
pattern_rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
if (!pattern_rtv) {
GST_ERROR_OBJECT (self, "RTV is not available");
gst_memory_unmap (mem, &render_info);
goto error;
}
pts = self->accum_rtime + self->running_time;
gst_d3d11_test_src_draw_pattern (self, context_handle, dmem, pattern_rtv,
pts);
gst_memory_unmap (mem, &render_info);
convert_ret = gst_d3d11_converter_convert_buffer_unlocked (self->converter,
render_buffer, convert_buffer);
if (!convert_ret) {
GST_ERROR_OBJECT (self, "Failed to convert buffer");
goto error;
}
if (self->downstream_supports_d3d11) {
convert_buffer = nullptr;
} else {
gst_d3d11_buffer_copy_into (buffer, convert_buffer, &self->info);
gst_clear_buffer (&convert_buffer);
}
gst_clear_buffer (&render_buffer);
GST_BUFFER_PTS (buffer) = pts;
GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (buffer) = self->accum_frames + self->n_frames;
if (self->reverse) {
self->n_frames--;
} else {
self->n_frames++;
}
GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET (buffer) + 1;
next_time = gst_util_uint64_scale (self->n_frames,
self->info.fps_d * GST_SECOND, self->info.fps_n);
if (self->reverse) {
/* We already decremented to next frame */
GstClockTime prev_pts = gst_util_uint64_scale (self->n_frames + 2,
self->info.fps_d * GST_SECOND, self->info.fps_n);
GST_BUFFER_DURATION (buffer) = prev_pts - GST_BUFFER_PTS (buffer);
} else {
GST_BUFFER_DURATION (buffer) = next_time - self->running_time;
}
self->running_time = next_time;
*buf = buffer;
return GST_FLOW_OK;
error:
gst_clear_buffer (&buffer);
gst_clear_buffer (&render_buffer);
if (!self->downstream_supports_d3d11)
gst_clear_buffer (&convert_buffer);
return ret;
}