mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +00:00
4ede9fef07
Selected blend mode should not be cleared on device update Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6508>
1312 lines
38 KiB
C++
1312 lines
38 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2023 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/d3d11/gstd3d11-private.h>
|
|
#include "gstdwriteoverlayobject.h"
|
|
#include "gstdwritebitmappool.h"
|
|
#include "gstdwrite-renderer.h"
|
|
#include "gstdwrite-effect.h"
|
|
#include <wrl.h>
|
|
#include <mutex>
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (dwrite_overlay_object_debug);
|
|
#define GST_CAT_DEFAULT dwrite_overlay_object_debug
|
|
|
|
/* *INDENT-OFF* */
|
|
using namespace Microsoft::WRL;
|
|
|
|
struct GstDWriteOverlayObjectPrivate
|
|
{
|
|
GstDWriteOverlayObjectPrivate ()
|
|
{
|
|
gst_video_info_init (&info);
|
|
gst_video_info_init (&bgra_info);
|
|
gst_video_info_init (&layout_info);
|
|
}
|
|
|
|
~GstDWriteOverlayObjectPrivate ()
|
|
{
|
|
ClearResource (true);
|
|
gst_clear_caps (&outcaps);
|
|
gst_clear_object (&device);
|
|
}
|
|
|
|
void ClearResource (bool hard)
|
|
{
|
|
if (hard)
|
|
blend_mode = GstDWriteBlendMode::NOT_SUPPORTED;
|
|
|
|
g_clear_pointer (&overlay_rect, gst_video_overlay_rectangle_unref);
|
|
gst_clear_buffer (&layout_buf);
|
|
layout = nullptr;
|
|
|
|
if (layout_pool) {
|
|
gst_buffer_pool_set_active (layout_pool, FALSE);
|
|
gst_clear_object (&layout_pool);
|
|
}
|
|
|
|
if (blend_pool) {
|
|
gst_buffer_pool_set_active (blend_pool, FALSE);
|
|
gst_clear_object (&blend_pool);
|
|
}
|
|
|
|
gst_clear_object (&pre_conv);
|
|
gst_clear_object (&blend_conv);
|
|
gst_clear_object (&post_conv);
|
|
}
|
|
|
|
GstVideoInfo info;
|
|
GstVideoInfo bgra_info;
|
|
GstVideoInfo layout_info;
|
|
|
|
GstD3D11Device *device = nullptr;
|
|
GstCaps *outcaps = nullptr;
|
|
|
|
ComPtr<ID2D1Factory> d2d_factory;
|
|
ComPtr<IDWriteFactory> dwrite_factory;
|
|
ComPtr<IDWriteTextLayout> layout;
|
|
ComPtr<IGstDWriteTextRenderer> renderer;
|
|
|
|
GstDWriteBlendMode blend_mode = GstDWriteBlendMode::NOT_SUPPORTED;
|
|
|
|
GstBufferPool *layout_pool = nullptr;
|
|
GstBufferPool *blend_pool = nullptr;
|
|
GstBuffer *layout_buf = nullptr;
|
|
/* Convert input texture to BGRA */
|
|
GstD3D11Converter *pre_conv = nullptr;
|
|
/* Blend converted BGRA texture with rendered text texture */
|
|
GstD3D11Converter *blend_conv = nullptr;
|
|
/* Convert blended texture to original format */
|
|
GstD3D11Converter *post_conv = nullptr;
|
|
GstVideoOverlayRectangle *overlay_rect = nullptr;
|
|
|
|
gboolean is_d3d11 = FALSE;
|
|
gboolean attach_meta = FALSE;
|
|
gboolean use_bitmap = FALSE;
|
|
|
|
std::recursive_mutex ctx_lock;
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
struct _GstDWriteOverlayObject
|
|
{
|
|
GstObject parent;
|
|
|
|
GstDWriteOverlayObjectPrivate *priv;
|
|
};
|
|
|
|
static void gst_dwrite_overlay_object_finalize (GObject * object);
|
|
|
|
#define gst_dwrite_overlay_object_parent_class parent_class
|
|
G_DEFINE_TYPE (GstDWriteOverlayObject, gst_dwrite_overlay_object,
|
|
GST_TYPE_OBJECT);
|
|
|
|
static void
|
|
gst_dwrite_overlay_object_class_init (GstDWriteOverlayObjectClass * klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gst_dwrite_overlay_object_finalize;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (dwrite_overlay_object_debug,
|
|
"dwriteoverlayobject", 0, "dwriteoverlayobject");
|
|
}
|
|
|
|
static void
|
|
gst_dwrite_overlay_object_init (GstDWriteOverlayObject * self)
|
|
{
|
|
self->priv = new GstDWriteOverlayObjectPrivate ();
|
|
}
|
|
|
|
static void
|
|
gst_dwrite_overlay_object_finalize (GObject * object)
|
|
{
|
|
GstDWriteOverlayObject *self = GST_DWRITE_OVERLAY_OBJECT (object);
|
|
|
|
delete self->priv;
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gst_dwrite_overlay_object_decide_blend_mode (GstDWriteOverlayObject * self)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
|
|
if (priv->attach_meta) {
|
|
if (priv->is_d3d11)
|
|
priv->blend_mode = GstDWriteBlendMode::ATTACH_TEXTURE;
|
|
else
|
|
priv->blend_mode = GstDWriteBlendMode::ATTACH_BITMAP;
|
|
return;
|
|
}
|
|
|
|
if (!priv->is_d3d11) {
|
|
priv->blend_mode = GstDWriteBlendMode::SW_BLEND;
|
|
return;
|
|
}
|
|
|
|
/* Decide best blend mode to use based on format */
|
|
switch (GST_VIDEO_INFO_FORMAT (&priv->info)) {
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_VUYA:
|
|
case GST_VIDEO_FORMAT_RGBA64_LE:
|
|
case GST_VIDEO_FORMAT_RGB10A2_LE:
|
|
/* Alpha aware formats */
|
|
priv->blend_mode = GstDWriteBlendMode::BLEND;
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_Y42B:
|
|
case GST_VIDEO_FORMAT_Y444:
|
|
case GST_VIDEO_FORMAT_GRAY8:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
case GST_VIDEO_FORMAT_RGBP:
|
|
case GST_VIDEO_FORMAT_BGRP:
|
|
case GST_VIDEO_FORMAT_GBR:
|
|
case GST_VIDEO_FORMAT_GBRA:
|
|
/* 8bits formats */
|
|
priv->blend_mode = GstDWriteBlendMode::CONVERT;
|
|
break;
|
|
case GST_VIDEO_FORMAT_P010_10LE:
|
|
case GST_VIDEO_FORMAT_P012_LE:
|
|
case GST_VIDEO_FORMAT_P016_LE:
|
|
case GST_VIDEO_FORMAT_I420_10LE:
|
|
case GST_VIDEO_FORMAT_I420_12LE:
|
|
case GST_VIDEO_FORMAT_I422_10LE:
|
|
case GST_VIDEO_FORMAT_I422_12LE:
|
|
case GST_VIDEO_FORMAT_Y444_10LE:
|
|
case GST_VIDEO_FORMAT_Y444_12LE:
|
|
case GST_VIDEO_FORMAT_Y444_16LE:
|
|
case GST_VIDEO_FORMAT_GRAY16_LE:
|
|
case GST_VIDEO_FORMAT_AYUV64:
|
|
case GST_VIDEO_FORMAT_GBR_10LE:
|
|
case GST_VIDEO_FORMAT_GBR_12LE:
|
|
case GST_VIDEO_FORMAT_GBRA_10LE:
|
|
case GST_VIDEO_FORMAT_GBRA_12LE:
|
|
/* high bitdept formats */
|
|
priv->blend_mode = GstDWriteBlendMode::CONVERT_64;
|
|
break;
|
|
default:
|
|
/* d3d11 blending is not supported, fallback to software blending */
|
|
priv->blend_mode = GstDWriteBlendMode::SW_BLEND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
is_subsampled_yuv (const GstVideoInfo * info)
|
|
{
|
|
if (!GST_VIDEO_INFO_IS_YUV (info))
|
|
return FALSE;
|
|
|
|
for (guint i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
|
|
if (info->finfo->w_sub[i] != 0 || info->finfo->h_sub[i] != 0)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GstD3D11Converter *
|
|
gst_dwrite_overlay_object_create_converter (GstDWriteOverlayObject * self,
|
|
const GstVideoInfo * in_info, const GstVideoInfo * out_info,
|
|
gboolean is_blend)
|
|
{
|
|
GstD3D11Converter *ret;
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstStructure *config;
|
|
D3D11_FILTER filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
|
|
|
if (is_subsampled_yuv (in_info) || is_subsampled_yuv (out_info))
|
|
filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
|
|
|
config = gst_structure_new ("convert-config",
|
|
GST_D3D11_CONVERTER_OPT_BACKEND, GST_TYPE_D3D11_CONVERTER_BACKEND,
|
|
GST_D3D11_CONVERTER_BACKEND_SHADER,
|
|
GST_D3D11_CONVERTER_OPT_SAMPLER_FILTER,
|
|
GST_TYPE_D3D11_CONVERTER_SAMPLER_FILTER, filter, nullptr);
|
|
if (is_blend) {
|
|
gst_structure_set (config, GST_D3D11_CONVERTER_OPT_SRC_ALPHA_MODE,
|
|
GST_TYPE_D3D11_CONVERTER_ALPHA_MODE,
|
|
GST_D3D11_CONVERTER_ALPHA_MODE_PREMULTIPLIED, nullptr);
|
|
}
|
|
|
|
ret = gst_d3d11_converter_new (priv->device, in_info, out_info, config);
|
|
if (!ret)
|
|
GST_ERROR_OBJECT (self, "Couldn't create converter");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstBufferPool *
|
|
gst_dwrite_overlay_object_create_d3d11_pool (GstDWriteOverlayObject * self,
|
|
const GstVideoInfo * info)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstCaps *caps = nullptr;
|
|
GstStructure *config;
|
|
GstD3D11AllocationParams *params;
|
|
GstBufferPool *pool = nullptr;
|
|
|
|
caps = gst_video_info_to_caps (info);
|
|
if (!caps) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create caps");
|
|
return nullptr;
|
|
}
|
|
|
|
pool = gst_d3d11_buffer_pool_new (priv->device);
|
|
config = gst_buffer_pool_get_config (pool);
|
|
|
|
params = gst_d3d11_allocation_params_new (priv->device, info,
|
|
GST_D3D11_ALLOCATION_FLAG_DEFAULT,
|
|
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, 0);
|
|
gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
|
|
gst_d3d11_allocation_params_free (params);
|
|
gst_buffer_pool_config_set_params (config, caps, info->size, 0, 0);
|
|
gst_caps_unref (caps);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't set pool config");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't activate pool");
|
|
goto error;
|
|
}
|
|
|
|
return pool;
|
|
|
|
error:
|
|
gst_clear_object (&pool);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static GstBufferPool *
|
|
gst_dwrite_overlay_object_create_bitmap_pool (GstDWriteOverlayObject * self,
|
|
const GstVideoInfo * info)
|
|
{
|
|
GstCaps *caps = nullptr;
|
|
GstStructure *config;
|
|
GstBufferPool *pool = nullptr;
|
|
|
|
caps = gst_video_info_to_caps (info);
|
|
if (!caps) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create caps");
|
|
return nullptr;
|
|
}
|
|
|
|
pool = gst_dwrite_bitmap_pool_new ();
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_params (config, caps, info->size, 0, 0);
|
|
gst_caps_unref (caps);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't set pool config");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't activate pool");
|
|
goto error;
|
|
}
|
|
|
|
return pool;
|
|
|
|
error:
|
|
gst_clear_object (&pool);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_prepare_resource (GstDWriteOverlayObject * self)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
|
|
switch (priv->blend_mode) {
|
|
case GstDWriteBlendMode::ATTACH_TEXTURE:
|
|
case GstDWriteBlendMode::ATTACH_BITMAP:
|
|
case GstDWriteBlendMode::SW_BLEND:
|
|
/* Updated later */
|
|
break;
|
|
case GstDWriteBlendMode::BLEND:
|
|
priv->blend_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&priv->bgra_info, &priv->info, TRUE);
|
|
if (!priv->blend_conv)
|
|
return FALSE;
|
|
break;
|
|
case GstDWriteBlendMode::CONVERT:
|
|
priv->blend_pool = gst_dwrite_overlay_object_create_d3d11_pool (self,
|
|
&priv->bgra_info);
|
|
if (!priv->blend_pool)
|
|
return FALSE;
|
|
|
|
priv->pre_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&priv->info, &priv->bgra_info, FALSE);
|
|
if (!priv->pre_conv)
|
|
return FALSE;
|
|
|
|
priv->blend_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&priv->bgra_info, &priv->bgra_info, TRUE);
|
|
if (!priv->blend_conv)
|
|
return FALSE;
|
|
|
|
priv->post_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&priv->bgra_info, &priv->info, FALSE);
|
|
if (!priv->blend_conv)
|
|
return FALSE;
|
|
break;
|
|
case GstDWriteBlendMode::CONVERT_64:
|
|
{
|
|
GstVideoInfo blend_info;
|
|
gst_video_info_set_format (&blend_info, GST_VIDEO_FORMAT_RGBA64_LE,
|
|
priv->info.width, priv->info.height);
|
|
|
|
priv->blend_pool = gst_dwrite_overlay_object_create_d3d11_pool (self,
|
|
&blend_info);
|
|
if (!priv->blend_pool)
|
|
return FALSE;
|
|
|
|
priv->pre_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&priv->info, &blend_info, FALSE);
|
|
if (!priv->pre_conv)
|
|
return FALSE;
|
|
|
|
priv->blend_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&priv->bgra_info, &blend_info, TRUE);
|
|
if (!priv->pre_conv)
|
|
return FALSE;
|
|
|
|
priv->post_conv = gst_dwrite_overlay_object_create_converter (self,
|
|
&blend_info, &priv->info, FALSE);
|
|
if (!priv->post_conv)
|
|
return FALSE;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
if (priv->blend_conv) {
|
|
D3D11_BLEND_DESC desc = { 0, };
|
|
ComPtr < ID3D11BlendState > blend;
|
|
ID3D11Device *device = gst_d3d11_device_get_device_handle (priv->device);
|
|
HRESULT hr;
|
|
|
|
desc.AlphaToCoverageEnable = FALSE;
|
|
desc.IndependentBlendEnable = FALSE;
|
|
desc.RenderTarget[0].BlendEnable = TRUE;
|
|
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
|
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
|
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
|
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
|
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
|
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
|
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
|
|
|
hr = device->CreateBlendState (&desc, &blend);
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create blend state");
|
|
return FALSE;
|
|
}
|
|
|
|
g_object_set (priv->blend_conv, "blend-state", blend.Get (), nullptr);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const gchar *
|
|
gst_dwrite_overlay_mode_to_string (GstDWriteBlendMode mode)
|
|
{
|
|
switch (mode) {
|
|
case GstDWriteBlendMode::NOT_SUPPORTED:
|
|
return "not-supported";
|
|
case GstDWriteBlendMode::ATTACH_TEXTURE:
|
|
return "attach-texture";
|
|
case GstDWriteBlendMode::ATTACH_BITMAP:
|
|
return "attach-bitmap";
|
|
case GstDWriteBlendMode::SW_BLEND:
|
|
return "sw-blend";
|
|
case GstDWriteBlendMode::BLEND:
|
|
return "blend";
|
|
case GstDWriteBlendMode::CONVERT:
|
|
return "convert";
|
|
case GstDWriteBlendMode::CONVERT_64:
|
|
return "convert-64";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
GstDWriteOverlayObject *
|
|
gst_dwrite_overlay_object_new (void)
|
|
{
|
|
GstDWriteOverlayObject *self;
|
|
|
|
self = (GstDWriteOverlayObject *)
|
|
g_object_new (GST_TYPE_DWRITE_OVERLAY_OBJECT, nullptr);
|
|
gst_object_ref_sink (self);
|
|
|
|
return self;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_start (GstDWriteOverlayObject * object,
|
|
IDWriteFactory * dwrite_factory)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
HRESULT hr;
|
|
ComPtr < ID2D1Factory > d2d_factory;
|
|
|
|
hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
|
|
IID_PPV_ARGS (&d2d_factory));
|
|
if (FAILED (hr)) {
|
|
GST_ERROR_OBJECT (object, "Couldn't create d2d factory");
|
|
return FALSE;
|
|
}
|
|
|
|
priv->d2d_factory = d2d_factory;
|
|
priv->dwrite_factory = dwrite_factory;
|
|
IGstDWriteTextRenderer::CreateInstance (dwrite_factory, &priv->renderer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_stop (GstDWriteOverlayObject * object)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
|
|
priv->ClearResource (true);
|
|
priv->dwrite_factory = nullptr;
|
|
priv->d2d_factory = nullptr;
|
|
priv->renderer = nullptr;
|
|
gst_clear_object (&priv->device);
|
|
gst_clear_caps (&priv->outcaps);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gst_dwrite_overlay_object_set_context (GstDWriteOverlayObject * object,
|
|
GstElement * elem, GstContext * context)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
std::lock_guard < std::recursive_mutex > lk (priv->ctx_lock);
|
|
|
|
gst_d3d11_handle_set_context (elem, context, -1, &priv->device);
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_handle_query (GstDWriteOverlayObject * object,
|
|
GstElement * elem, GstQuery * query)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
|
|
if (GST_QUERY_TYPE (query) != GST_QUERY_CONTEXT)
|
|
return FALSE;
|
|
|
|
std::lock_guard < std::recursive_mutex > lk (priv->ctx_lock);
|
|
if (gst_d3d11_handle_context_query (elem, query, priv->device))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_decide_allocation (GstDWriteOverlayObject * object,
|
|
GstElement * elem, GstQuery * query)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
guint min, max, size;
|
|
gboolean update_pool;
|
|
GstCaps *caps = nullptr;
|
|
GstVideoInfo info;
|
|
GstBufferPool *pool = nullptr;
|
|
GstCapsFeatures *features;
|
|
GstStructure *config;
|
|
GstD3D11AllocationParams *params;
|
|
guint bind_flags = 0;
|
|
GstD3D11Format d3d11_format;
|
|
|
|
GST_DEBUG_OBJECT (elem, "Decide allocation");
|
|
|
|
gst_query_parse_allocation (query, &caps, nullptr);
|
|
if (!caps) {
|
|
GST_WARNING_OBJECT (elem, "Query without caps");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_info_from_caps (&info, caps)) {
|
|
GST_ERROR_OBJECT (elem, "Invalid caps %" GST_PTR_FORMAT, caps);
|
|
return FALSE;
|
|
}
|
|
|
|
features = gst_caps_get_features (caps, 0);
|
|
if (!features || !gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
|
|
GST_DEBUG_OBJECT (elem, "Not a d3d11 memory");
|
|
return TRUE;
|
|
}
|
|
|
|
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 {
|
|
min = max = 0;
|
|
size = info.size;
|
|
}
|
|
|
|
if (pool) {
|
|
std::lock_guard < std::recursive_mutex > lk (priv->ctx_lock);
|
|
if (!gst_d3d11_ensure_element_data (elem, -1, &priv->device)) {
|
|
GST_ERROR_OBJECT (elem, "Couldn't create deice");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
|
|
gst_clear_object (&pool);
|
|
} else {
|
|
GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
|
|
if (dpool->device != priv->device)
|
|
gst_clear_object (&pool);
|
|
}
|
|
}
|
|
|
|
if (!pool)
|
|
pool = gst_d3d11_buffer_pool_new (priv->device);
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
gst_buffer_pool_config_set_params (config, caps, size, min, max);
|
|
|
|
gst_d3d11_device_get_format (priv->device, GST_VIDEO_INFO_FORMAT (&info),
|
|
&d3d11_format);
|
|
if ((d3d11_format.format_support[0] &
|
|
D3D11_FORMAT_SUPPORT_SHADER_SAMPLE) != 0) {
|
|
bind_flags |= D3D11_BIND_SHADER_RESOURCE;
|
|
}
|
|
|
|
if ((d3d11_format.format_support[0] &
|
|
D3D11_FORMAT_SUPPORT_RENDER_TARGET) != 0) {
|
|
bind_flags |= D3D11_BIND_RENDER_TARGET;
|
|
}
|
|
|
|
params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
|
|
if (!params) {
|
|
params = gst_d3d11_allocation_params_new (priv->device, &info,
|
|
GST_D3D11_ALLOCATION_FLAG_DEFAULT, bind_flags, 0);
|
|
} else {
|
|
gst_d3d11_allocation_params_set_bind_flags (params, bind_flags);
|
|
}
|
|
|
|
gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
|
|
gst_d3d11_allocation_params_free (params);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (elem, "Couldn't set config");
|
|
gst_object_unref (pool);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get updated size in case of d3d11 */
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
|
|
gst_structure_free (config);
|
|
|
|
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;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_propose_allocation (GstDWriteOverlayObject * object,
|
|
GstElement * elem, GstQuery * query)
|
|
{
|
|
GstCaps *caps = nullptr;
|
|
GstVideoInfo info;
|
|
GstCapsFeatures *features;
|
|
guint min, max, size;
|
|
GstBufferPool *pool;
|
|
GstD3D11BufferPool *dpool;
|
|
GstStructure *config;
|
|
GstD3D11AllocationParams *params;
|
|
guint bind_flags = 0;
|
|
GstD3D11Format d3d11_format;
|
|
|
|
GST_DEBUG_OBJECT (elem, "Propose allocation");
|
|
|
|
gst_query_parse_allocation (query, &caps, nullptr);
|
|
if (!caps) {
|
|
GST_WARNING_OBJECT (elem, "Query without caps");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_info_from_caps (&info, caps)) {
|
|
GST_ERROR_OBJECT (elem, "Invalid caps %" GST_PTR_FORMAT, caps);
|
|
return FALSE;
|
|
}
|
|
|
|
features = gst_caps_get_features (caps, 0);
|
|
if (!features || !gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
|
|
GST_DEBUG_OBJECT (elem, "Not a d3d11 memory");
|
|
return TRUE;
|
|
}
|
|
|
|
if (gst_query_get_n_allocation_pools (query) == 0)
|
|
return TRUE;
|
|
|
|
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
|
if (!pool)
|
|
return TRUE;
|
|
|
|
if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
|
|
gst_object_unref (pool);
|
|
return TRUE;
|
|
}
|
|
|
|
dpool = GST_D3D11_BUFFER_POOL (pool);
|
|
gst_d3d11_device_get_format (dpool->device, GST_VIDEO_INFO_FORMAT (&info),
|
|
&d3d11_format);
|
|
if ((d3d11_format.format_support[0] &
|
|
D3D11_FORMAT_SUPPORT_SHADER_SAMPLE) != 0) {
|
|
bind_flags |= D3D11_BIND_SHADER_RESOURCE;
|
|
}
|
|
|
|
if ((d3d11_format.format_support[0] &
|
|
D3D11_FORMAT_SUPPORT_RENDER_TARGET) != 0) {
|
|
bind_flags |= D3D11_BIND_RENDER_TARGET;
|
|
}
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
|
|
if (!params) {
|
|
params = gst_d3d11_allocation_params_new (dpool->device, &info,
|
|
GST_D3D11_ALLOCATION_FLAG_DEFAULT, bind_flags, 0);
|
|
} else {
|
|
gst_d3d11_allocation_params_set_bind_flags (params, bind_flags);
|
|
}
|
|
|
|
gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
|
|
gst_d3d11_allocation_params_free (params);
|
|
|
|
gst_buffer_pool_config_set_params (config, caps, size, min, max);
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (elem, "Couldn't set config");
|
|
gst_object_unref (pool);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
|
|
gst_object_unref (pool);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_set_caps (GstDWriteOverlayObject * object,
|
|
GstElement * elem, GstCaps * in_caps, GstCaps * out_caps,
|
|
GstVideoInfo * info, GstDWriteBlendMode * selected_mode)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
gboolean is_system;
|
|
GstCapsFeatures *features;
|
|
|
|
*selected_mode = GstDWriteBlendMode::NOT_SUPPORTED;
|
|
|
|
priv->ClearResource (true);
|
|
gst_caps_replace (&priv->outcaps, out_caps);
|
|
|
|
if (!gst_video_info_from_caps (info, in_caps)) {
|
|
GST_WARNING_OBJECT (elem, "Invalid caps %" GST_PTR_FORMAT, in_caps);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_info_from_caps (&priv->info, out_caps)) {
|
|
GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, out_caps);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_video_info_set_format (&priv->bgra_info, GST_VIDEO_FORMAT_BGRA,
|
|
priv->info.width, priv->info.height);
|
|
|
|
features = gst_caps_get_features (out_caps, 0);
|
|
priv->is_d3d11 = gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY);
|
|
is_system = gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
|
|
priv->attach_meta = gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
|
|
|
|
if (priv->is_d3d11) {
|
|
std::lock_guard < std::recursive_mutex > lk (priv->ctx_lock);
|
|
if (!gst_d3d11_ensure_element_data (elem, -1, &priv->device)) {
|
|
GST_ERROR_OBJECT (elem, "Couldn't create deice");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!priv->is_d3d11 && !is_system && !priv->attach_meta) {
|
|
GST_WARNING_OBJECT (elem,
|
|
"Not d3d11/system memory without composition meta support");
|
|
return TRUE;
|
|
}
|
|
|
|
gst_dwrite_overlay_object_decide_blend_mode (object);
|
|
GST_INFO_OBJECT (elem, "Selected blend mode: %s",
|
|
gst_dwrite_overlay_mode_to_string (priv->blend_mode));
|
|
|
|
if (priv->blend_mode == GstDWriteBlendMode::SW_BLEND ||
|
|
priv->blend_mode == GstDWriteBlendMode::ATTACH_BITMAP) {
|
|
priv->use_bitmap = TRUE;
|
|
} else {
|
|
priv->use_bitmap = FALSE;
|
|
}
|
|
|
|
if (!gst_dwrite_overlay_object_prepare_resource (object)) {
|
|
GST_ERROR_OBJECT (elem, "Couldn't prepare resource");
|
|
priv->ClearResource (true);
|
|
return FALSE;
|
|
}
|
|
|
|
*selected_mode = priv->blend_mode;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_update_device (GstDWriteOverlayObject * object,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
|
|
if (priv->blend_mode == GstDWriteBlendMode::NOT_SUPPORTED || priv->use_bitmap)
|
|
return FALSE;
|
|
|
|
mem = gst_buffer_peek_memory (buffer, 0);
|
|
if (!gst_is_d3d11_memory (mem))
|
|
return FALSE;
|
|
|
|
dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
std::lock_guard < std::recursive_mutex > lk (priv->ctx_lock);
|
|
if (dmem->device == priv->device)
|
|
return FALSE;
|
|
|
|
GST_DEBUG_OBJECT (object, "Updating device");
|
|
gst_clear_object (&priv->device);
|
|
priv->device = (GstD3D11Device *) gst_object_ref (dmem->device);
|
|
priv->ClearResource (false);
|
|
gst_dwrite_overlay_object_prepare_resource (object);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_upload_system (GstDWriteOverlayObject * self,
|
|
GstBuffer * dst, GstBuffer * src)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstVideoFrame in_frame, out_frame;
|
|
gboolean ret;
|
|
|
|
GST_TRACE_OBJECT (self, "system copy");
|
|
|
|
if (!gst_video_frame_map (&in_frame, &priv->info, src,
|
|
(GstMapFlags) (GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map input frame");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_frame_map (&out_frame, &priv->info, dst,
|
|
(GstMapFlags) (GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))) {
|
|
gst_video_frame_unmap (&in_frame);
|
|
GST_ERROR_OBJECT (self, "Couldn't map output frame");
|
|
return FALSE;
|
|
}
|
|
|
|
ret = gst_video_frame_copy (&out_frame, &in_frame);
|
|
|
|
gst_video_frame_unmap (&in_frame);
|
|
gst_video_frame_unmap (&out_frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_upload_d3d11 (GstDWriteOverlayObject * self,
|
|
GstBuffer * dst, GstBuffer * src)
|
|
{
|
|
GST_TRACE_OBJECT (self, "d3d11 copy");
|
|
|
|
for (guint i = 0; i < gst_buffer_n_memory (dst); i++) {
|
|
GstMemory *dst_mem, *src_mem;
|
|
GstD3D11Memory *dst_dmem, *src_dmem;
|
|
GstMapInfo dst_info;
|
|
GstMapInfo src_info;
|
|
ID3D11Resource *dst_texture, *src_texture;
|
|
ID3D11DeviceContext *device_context;
|
|
GstD3D11Device *device;
|
|
D3D11_BOX src_box = { 0, };
|
|
D3D11_TEXTURE2D_DESC dst_desc, src_desc;
|
|
guint dst_subidx, src_subidx;
|
|
|
|
dst_mem = gst_buffer_peek_memory (dst, i);
|
|
src_mem = gst_buffer_peek_memory (src, i);
|
|
|
|
dst_dmem = (GstD3D11Memory *) dst_mem;
|
|
src_dmem = (GstD3D11Memory *) src_mem;
|
|
|
|
device = dst_dmem->device;
|
|
|
|
gst_d3d11_memory_get_texture_desc (dst_dmem, &dst_desc);
|
|
gst_d3d11_memory_get_texture_desc (src_dmem, &src_desc);
|
|
|
|
device_context = gst_d3d11_device_get_device_context_handle (device);
|
|
|
|
if (!gst_memory_map (dst_mem, &dst_info,
|
|
(GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
|
|
GST_ERROR_OBJECT (self, "Cannot map dst d3d11 memory");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_memory_map (src_mem, &src_info,
|
|
(GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11))) {
|
|
GST_ERROR_OBJECT (self, "Cannot map src d3d11 memory");
|
|
gst_memory_unmap (dst_mem, &dst_info);
|
|
return FALSE;
|
|
}
|
|
|
|
dst_texture = (ID3D11Resource *) dst_info.data;
|
|
src_texture = (ID3D11Resource *) src_info.data;
|
|
|
|
/* src/dst texture size might be different if padding was used.
|
|
* select smaller size */
|
|
src_box.left = 0;
|
|
src_box.top = 0;
|
|
src_box.front = 0;
|
|
src_box.back = 1;
|
|
src_box.right = MIN (src_desc.Width, dst_desc.Width);
|
|
src_box.bottom = MIN (src_desc.Height, dst_desc.Height);
|
|
|
|
dst_subidx = gst_d3d11_memory_get_subresource_index (dst_dmem);
|
|
src_subidx = gst_d3d11_memory_get_subresource_index (src_dmem);
|
|
|
|
GstD3D11DeviceLockGuard lk (device);
|
|
device_context->CopySubresourceRegion (dst_texture, dst_subidx, 0, 0, 0,
|
|
src_texture, src_subidx, &src_box);
|
|
|
|
gst_memory_unmap (src_mem, &src_info);
|
|
gst_memory_unmap (dst_mem, &dst_info);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_dwrite_overlay_object_prepare_output (GstDWriteOverlayObject * object,
|
|
GstBaseTransform * trans, gpointer trans_class, GstBuffer * inbuf,
|
|
GstBuffer ** outbuf)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
GstMemory *mem = gst_buffer_peek_memory (inbuf, 0);
|
|
GstFlowReturn ret;
|
|
gboolean is_d3d11 = FALSE;
|
|
gboolean upload_ret;
|
|
|
|
/* attaching meta or software blending can be in-place processing */
|
|
switch (priv->blend_mode) {
|
|
case GstDWriteBlendMode::ATTACH_TEXTURE:
|
|
case GstDWriteBlendMode::ATTACH_BITMAP:
|
|
case GstDWriteBlendMode::SW_BLEND:
|
|
goto inplace;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (gst_is_d3d11_memory (mem)) {
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
GstD3D11Memory *dmem;
|
|
const guint bind_flags = (D3D11_BIND_RENDER_TARGET |
|
|
D3D11_BIND_SHADER_RESOURCE);
|
|
|
|
is_d3d11 = TRUE;
|
|
|
|
dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
gst_d3d11_memory_get_texture_desc (dmem, &desc);
|
|
|
|
/* Cannot write on decoder resource */
|
|
if ((desc.BindFlags & D3D11_BIND_DECODER) == 0 &&
|
|
(desc.BindFlags & bind_flags) == bind_flags) {
|
|
goto inplace;
|
|
}
|
|
}
|
|
|
|
/* Needs to allocate new buffer */
|
|
ret = GST_BASE_TRANSFORM_CLASS (trans_class)->prepare_output_buffer (trans,
|
|
inbuf, outbuf);
|
|
if (ret != GST_FLOW_OK)
|
|
return ret;
|
|
|
|
if (is_d3d11) {
|
|
upload_ret = gst_dwrite_overlay_object_upload_d3d11 (object,
|
|
*outbuf, inbuf);
|
|
} else {
|
|
upload_ret = gst_dwrite_overlay_object_upload_system (object,
|
|
*outbuf, inbuf);
|
|
}
|
|
|
|
if (!upload_ret) {
|
|
gst_clear_buffer (outbuf);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
inplace:
|
|
if (gst_buffer_is_writable (inbuf))
|
|
*outbuf = inbuf;
|
|
else
|
|
*outbuf = gst_buffer_copy (inbuf);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_get_target_from_d3d11 (GstDWriteOverlayObject * self,
|
|
GstMemory * mem, ID2D1RenderTarget ** target)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
ComPtr < IDXGISurface > surface;
|
|
GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
ID3D11Resource *texture;
|
|
HRESULT hr;
|
|
static const D2D1_RENDER_TARGET_PROPERTIES props = {
|
|
D2D1_RENDER_TARGET_TYPE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM,
|
|
D2D1_ALPHA_MODE_PREMULTIPLIED, 0, 0, D2D1_RENDER_TARGET_USAGE_NONE,
|
|
D2D1_FEATURE_LEVEL_DEFAULT
|
|
};
|
|
|
|
texture = gst_d3d11_memory_get_resource_handle (dmem);
|
|
hr = texture->QueryInterface (IID_PPV_ARGS (&surface));
|
|
if (!gst_d3d11_result (hr, priv->device))
|
|
return FALSE;
|
|
|
|
hr = priv->d2d_factory->CreateDxgiSurfaceRenderTarget (surface.Get (), props,
|
|
target);
|
|
|
|
return gst_d3d11_result (hr, priv->device);
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_get_target_from_bitmap (GstDWriteOverlayObject * self,
|
|
GstMemory * mem, ID2D1RenderTarget ** target)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstDWriteBitmapMemory *dmem = (GstDWriteBitmapMemory *) mem;
|
|
HRESULT hr;
|
|
static const D2D1_RENDER_TARGET_PROPERTIES props = {
|
|
D2D1_RENDER_TARGET_TYPE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM,
|
|
D2D1_ALPHA_MODE_PREMULTIPLIED, 0, 0, D2D1_RENDER_TARGET_USAGE_NONE,
|
|
D2D1_FEATURE_LEVEL_DEFAULT
|
|
};
|
|
|
|
hr = priv->d2d_factory->CreateWicBitmapRenderTarget (dmem->bitmap, &props,
|
|
target);
|
|
|
|
return SUCCEEDED (hr);
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_draw_layout (GstDWriteOverlayObject * self,
|
|
IDWriteTextLayout * layout, gint x, gint y)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
gint width, height;
|
|
GstMemory *mem;
|
|
ComPtr < ID2D1RenderTarget > target;
|
|
GstMapInfo info;
|
|
|
|
if (priv->layout_buf) {
|
|
if (priv->layout && priv->layout.Get () == layout)
|
|
return TRUE;
|
|
|
|
gst_clear_buffer (&priv->layout_buf);
|
|
g_clear_pointer (&priv->overlay_rect, gst_video_overlay_rectangle_unref);
|
|
}
|
|
|
|
priv->layout = nullptr;
|
|
priv->layout = layout;
|
|
|
|
if (priv->layout_buf)
|
|
return TRUE;
|
|
|
|
width = (gint) layout->GetMaxWidth ();
|
|
height = (gint) layout->GetMaxHeight ();
|
|
|
|
if (priv->layout_pool &&
|
|
(priv->layout_info.width != width
|
|
|| priv->layout_info.height != height)) {
|
|
gst_buffer_pool_set_active (priv->layout_pool, FALSE);
|
|
gst_clear_object (&priv->layout_pool);
|
|
}
|
|
|
|
if (!priv->layout_pool) {
|
|
gst_video_info_set_format (&priv->layout_info, GST_VIDEO_FORMAT_BGRA,
|
|
width, height);
|
|
if (priv->use_bitmap) {
|
|
priv->layout_pool = gst_dwrite_overlay_object_create_bitmap_pool (self,
|
|
&priv->layout_info);
|
|
} else {
|
|
priv->layout_pool = gst_dwrite_overlay_object_create_d3d11_pool (self,
|
|
&priv->layout_info);
|
|
}
|
|
}
|
|
|
|
if (!priv->layout_pool)
|
|
return FALSE;
|
|
|
|
gst_buffer_pool_acquire_buffer (priv->layout_pool,
|
|
&priv->layout_buf, nullptr);
|
|
if (!priv->layout_buf) {
|
|
GST_ERROR_OBJECT (self, "Couldn't acquire layout buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
mem = gst_buffer_peek_memory (priv->layout_buf, 0);
|
|
if (priv->use_bitmap) {
|
|
if (!gst_dwrite_overlay_object_get_target_from_bitmap (self, mem, &target)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get target from bitmap");
|
|
gst_clear_buffer (&priv->layout_buf);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if (!gst_memory_map (mem, &info,
|
|
(GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
|
|
GST_ERROR_OBJECT (self, "Could not map buffer");
|
|
gst_clear_buffer (&priv->layout_buf);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_dwrite_overlay_object_get_target_from_d3d11 (self, mem, &target)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get target from texture");
|
|
gst_memory_unmap (mem, &info);
|
|
gst_clear_buffer (&priv->layout_buf);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
target->BeginDraw ();
|
|
target->Clear (D2D1::ColorF (D2D1::ColorF::Black, 0.0));
|
|
priv->renderer->Draw (D2D1::Point2F (),
|
|
D2D1::Rect (0, 0, width, height), layout, target.Get ());
|
|
target->EndDraw ();
|
|
|
|
/* Release render target before unmapping. Otherwise pending GPU operations
|
|
* can be executed after releasing keyed-mutex, if texture was allocated with
|
|
* keyed-mutex enabled */
|
|
target = nullptr;
|
|
|
|
if (!priv->use_bitmap)
|
|
gst_memory_unmap (mem, &info);
|
|
|
|
priv->overlay_rect = gst_video_overlay_rectangle_new_raw (priv->layout_buf,
|
|
x, y, width, height, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_object_mode_attach (GstDWriteOverlayObject * self,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstVideoOverlayCompositionMeta *meta;
|
|
|
|
meta = gst_buffer_get_video_overlay_composition_meta (buffer);
|
|
if (meta) {
|
|
if (meta->overlay) {
|
|
meta->overlay =
|
|
gst_video_overlay_composition_make_writable (meta->overlay);
|
|
gst_video_overlay_composition_add_rectangle (meta->overlay,
|
|
priv->overlay_rect);
|
|
} else {
|
|
meta->overlay = gst_video_overlay_composition_new (priv->overlay_rect);
|
|
}
|
|
} else {
|
|
GstVideoOverlayComposition *comp =
|
|
gst_video_overlay_composition_new (priv->overlay_rect);
|
|
meta = gst_buffer_add_video_overlay_composition_meta (buffer, comp);
|
|
gst_video_overlay_composition_unref (comp);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_mode_sw_blend (GstDWriteOverlayObject * self,
|
|
GstBuffer * buffer, gint x, gint y)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstVideoFrame dst_frame, src_frame;
|
|
gboolean ret;
|
|
|
|
if (!gst_video_frame_map (&dst_frame, &priv->info, buffer,
|
|
(GstMapFlags) (GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map input buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_frame_map (&src_frame, &priv->layout_info, priv->layout_buf,
|
|
(GstMapFlags) (GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))) {
|
|
gst_video_frame_unmap (&dst_frame);
|
|
GST_ERROR_OBJECT (self, "Couldn't map text buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
src_frame.info.flags = (GstVideoFlags)
|
|
(src_frame.info.flags | GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA);
|
|
ret = gst_video_blend (&dst_frame, &src_frame, x, y, 1.0);
|
|
gst_video_frame_unmap (&src_frame);
|
|
gst_video_frame_unmap (&dst_frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_mode_blend (GstDWriteOverlayObject * self,
|
|
GstBuffer * buffer, gint x, gint y)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
|
|
g_object_set (priv->blend_conv, "src-width", priv->layout_info.width,
|
|
"src-height", priv->layout_info.height,
|
|
"dest-x", x, "dest-y", y, "dest-width", priv->layout_info.width,
|
|
"dest-height", priv->layout_info.height, nullptr);
|
|
|
|
return gst_d3d11_converter_convert_buffer (priv->blend_conv,
|
|
priv->layout_buf, buffer);
|
|
}
|
|
|
|
static gboolean
|
|
gst_dwrite_overlay_mode_convert (GstDWriteOverlayObject * self,
|
|
GstBuffer * buffer, gint x, gint y)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = self->priv;
|
|
GstBuffer *pre_buf = nullptr;
|
|
|
|
g_object_set (priv->blend_conv, "src-width", priv->layout_info.width,
|
|
"src-height", priv->layout_info.height,
|
|
"dest-x", x, "dest-y", y, "dest-width", priv->layout_info.width,
|
|
"dest-height", priv->layout_info.height, nullptr);
|
|
|
|
gst_buffer_pool_acquire_buffer (priv->blend_pool, &pre_buf, nullptr);
|
|
if (!pre_buf) {
|
|
GST_ERROR_OBJECT (self, "Couldn't acquire preconv buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_d3d11_converter_convert_buffer_unlocked (priv->pre_conv,
|
|
buffer, pre_buf)) {
|
|
GST_ERROR_OBJECT (self, "pre-convert failed");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_d3d11_converter_convert_buffer_unlocked (priv->blend_conv,
|
|
priv->layout_buf, pre_buf)) {
|
|
GST_ERROR_OBJECT (self, "blend-convert failed");
|
|
goto error;
|
|
}
|
|
|
|
if (!gst_d3d11_converter_convert_buffer_unlocked (priv->post_conv,
|
|
pre_buf, buffer)) {
|
|
GST_ERROR_OBJECT (self, "post-convert failed");
|
|
goto error;
|
|
}
|
|
|
|
gst_buffer_unref (pre_buf);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
gst_clear_buffer (&pre_buf);
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_dwrite_overlay_object_draw (GstDWriteOverlayObject * object,
|
|
GstBuffer * buffer, IDWriteTextLayout * layout, gint x, gint y)
|
|
{
|
|
GstDWriteOverlayObjectPrivate *priv = object->priv;
|
|
gboolean ret = FALSE;
|
|
|
|
if (priv->device)
|
|
gst_d3d11_device_lock (priv->device);
|
|
|
|
if (!gst_dwrite_overlay_object_draw_layout (object, layout, x, y))
|
|
goto out;
|
|
|
|
switch (priv->blend_mode) {
|
|
case GstDWriteBlendMode::ATTACH_TEXTURE:
|
|
case GstDWriteBlendMode::ATTACH_BITMAP:
|
|
ret = gst_dwrite_overlay_object_mode_attach (object, buffer);
|
|
break;
|
|
case GstDWriteBlendMode::SW_BLEND:
|
|
ret = gst_dwrite_overlay_mode_sw_blend (object, buffer, x, y);
|
|
break;
|
|
case GstDWriteBlendMode::BLEND:
|
|
ret = gst_dwrite_overlay_mode_blend (object, buffer, x, y);
|
|
break;
|
|
case GstDWriteBlendMode::CONVERT:
|
|
case GstDWriteBlendMode::CONVERT_64:
|
|
ret = gst_dwrite_overlay_mode_convert (object, buffer, x, y);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
out:
|
|
if (priv->device)
|
|
gst_d3d11_device_unlock (priv->device);
|
|
return ret;
|
|
}
|