dwrite: Add DirectWrite text rendering plugin

Adding DirectWrite text rendering elements
* dwriteclockoverlay: Equivalent to clockoverlay
* dwritetimeoverlay: Equivalent to timeoverlay
* dwritetextoverlay: Similar to textoverlay but subtitle is not
  supported

Newly added elements support system memory and d3d11 memory

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4826>
This commit is contained in:
Seungha Yang 2023-05-28 01:59:23 +09:00 committed by GStreamer Marge Bot
parent 01edbf6c6b
commit ba6800ce23
25 changed files with 5199 additions and 0 deletions

View file

@ -4,6 +4,7 @@ subprojects/gst-plugins-bad/gst-libs/gst/va
subprojects/gst-plugins-bad/gst-libs/gst/winrt subprojects/gst-plugins-bad/gst-libs/gst/winrt
subprojects/gst-plugins-bad/sys/amfcodec subprojects/gst-plugins-bad/sys/amfcodec
subprojects/gst-plugins-bad/sys/d3d11 subprojects/gst-plugins-bad/sys/d3d11
subprojects/gst-plugins-bad/sys/dwrite
subprojects/gst-plugins-bad/sys/mediafoundation subprojects/gst-plugins-bad/sys/mediafoundation
subprojects/gst-plugins-bad/sys/nvcodec subprojects/gst-plugins-bad/sys/nvcodec
^(subprojects/gst-plugins-bad/sys/qsv/)+(\w)+([^/])+(cpp$) ^(subprojects/gst-plugins-bad/sys/qsv/)+(\w)+([^/])+(cpp$)

View file

@ -108,6 +108,7 @@ option('directshow', type : 'feature', value : 'auto', description : 'Directshow
option('dtls', type : 'feature', value : 'auto', description : 'DTLS encoder and decoder plugin') option('dtls', type : 'feature', value : 'auto', description : 'DTLS encoder and decoder plugin')
option('dts', type : 'feature', value : 'auto', description : 'DTS audio decoder plugin (GPL - only built if gpl option is also enabled!)') option('dts', type : 'feature', value : 'auto', description : 'DTS audio decoder plugin (GPL - only built if gpl option is also enabled!)')
option('dvb', type : 'feature', value : 'auto', description : 'DVB video bin and source plugin') option('dvb', type : 'feature', value : 'auto', description : 'DVB video bin and source plugin')
option('dwrite', type : 'feature', value : 'auto', description : 'DirectWrite plugin')
option('faac', type : 'feature', value : 'auto', description : 'Free AAC audio encoder plugin') option('faac', type : 'feature', value : 'auto', description : 'Free AAC audio encoder plugin')
option('faad', type : 'feature', value : 'auto', description : 'Free AAC audio decoder plugin (GPL - only built if gpl option is also enabled!)') option('faad', type : 'feature', value : 'auto', description : 'Free AAC audio decoder plugin (GPL - only built if gpl option is also enabled!)')
option('fbdev', type : 'feature', value : 'auto', description : 'Framebuffer video sink plugin') option('fbdev', type : 'feature', value : 'auto', description : 'Framebuffer video sink plugin')

View file

@ -0,0 +1,142 @@
/* 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 "gstdwrite-effect.h"
/* *INDENT-OFF* */
STDMETHODIMP
IGstDWriteTextEffect::CreateInstance (IGstDWriteTextEffect ** effect)
{
IGstDWriteTextEffect *self = new IGstDWriteTextEffect ();
if (!self)
return E_OUTOFMEMORY;
*effect = self;
return S_OK;
}
STDMETHODIMP_ (BOOL)
IGstDWriteTextEffect::IsEnabledColor (const D2D1_COLOR_F & color)
{
if (color.r != 0 || color.g != 0 || color.b != 0 || color.a != 0)
return TRUE;
return FALSE;
}
STDMETHODIMP_ (ULONG)
IGstDWriteTextEffect::AddRef (void)
{
return InterlockedIncrement (&ref_count_);
}
STDMETHODIMP_ (ULONG)
IGstDWriteTextEffect::Release (void)
{
ULONG ref_count;
ref_count = InterlockedDecrement (&ref_count_);
if (ref_count == 0)
delete this;
return ref_count;
}
STDMETHODIMP
IGstDWriteTextEffect::QueryInterface (REFIID riid, void ** object)
{
if (riid == IID_IUnknown) {
*object = static_cast<IUnknown *>
(static_cast<IGstDWriteTextEffect *> (this));
} else if (riid == IID_IGstDWriteTextEffect) {
*object = this;
} else {
*object = nullptr;
return E_NOINTERFACE;
}
AddRef ();
return S_OK;
}
STDMETHODIMP
IGstDWriteTextEffect::Clone (IGstDWriteTextEffect ** effect)
{
IGstDWriteTextEffect *copy = new IGstDWriteTextEffect ();
if (!copy)
return E_OUTOFMEMORY;
for (UINT i = 0; i <= GST_DWRITE_BRUSH_SHADOW; i++)
copy->brush_[i] = this->brush_[i];
*effect = copy;
return S_OK;
}
STDMETHODIMP
IGstDWriteTextEffect::GetBrushColor (GST_DWRITE_BRUSH_TARGET target,
D2D1_COLOR_F * color, BOOL * enabled)
{
if (color)
*color = brush_[target];
if (enabled) {
if (IGstDWriteTextEffect::IsEnabledColor (brush_[target]))
*enabled = TRUE;
else
*enabled = FALSE;
}
return S_OK;
}
STDMETHODIMP
IGstDWriteTextEffect::SetBrushColor (GST_DWRITE_BRUSH_TARGET target,
const D2D1_COLOR_F * color)
{
if (!color)
brush_[target] = D2D1::ColorF (D2D1::ColorF::Black, 0);
else
brush_[target] = *color;
return S_OK;
}
IGstDWriteTextEffect::IGstDWriteTextEffect (void)
{
for (UINT32 i = 0; i < GST_DWRITE_BRUSH_SHADOW; i++)
brush_[i] = D2D1::ColorF (D2D1::ColorF::Black);
/* Disable custom shadow effects by default */
brush_[GST_DWRITE_BRUSH_SHADOW] = D2D1::ColorF (D2D1::ColorF::Black, 0);
}
IGstDWriteTextEffect::~IGstDWriteTextEffect (void)
{
}
/* *INDENT-ON* */

View file

@ -0,0 +1,65 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include "gstdwrite-enums.h"
enum GST_DWRITE_BRUSH_TARGET
{
GST_DWRITE_BRUSH_TEXT = 0,
GST_DWRITE_BRUSH_TEXT_OUTLINE,
GST_DWRITE_BRUSH_UNDERLINE,
GST_DWRITE_BRUSH_UNDERLINE_OUTLINE,
GST_DWRITE_BRUSH_STRIKETHROUGH,
GST_DWRITE_BRUSH_STRIKETHROUGH_OUTLINE,
GST_DWRITE_BRUSH_SHADOW,
};
DEFINE_GUID (IID_IGstDWriteTextEffect, 0x23c579ae, 0x2e18,
0x11ed, 0xa2, 0x61, 0x02, 0x42, 0xac, 0x12, 0x00, 0x02);
class IGstDWriteTextEffect : public IUnknown
{
public:
static STDMETHODIMP CreateInstance (IGstDWriteTextEffect ** effect);
static STDMETHODIMP_ (BOOL) IsEnabledColor (const D2D1_COLOR_F & color);
/* IUnknown */
STDMETHODIMP_ (ULONG) AddRef (void);
STDMETHODIMP_ (ULONG) Release (void);
STDMETHODIMP QueryInterface (REFIID riid,
void ** object);
/* effect methods */
STDMETHODIMP Clone (IGstDWriteTextEffect ** effect);
STDMETHODIMP GetBrushColor (GST_DWRITE_BRUSH_TARGET target,
D2D1_COLOR_F * color,
BOOL * enabled);
STDMETHODIMP SetBrushColor (GST_DWRITE_BRUSH_TARGET target,
const D2D1_COLOR_F * color);
private:
IGstDWriteTextEffect (void);
virtual ~IGstDWriteTextEffect (void);
private:
ULONG ref_count_ = 1;
D2D1_COLOR_F brush_[GST_DWRITE_BRUSH_SHADOW + 1];
};

View file

@ -0,0 +1,237 @@
/* 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 "gstdwrite-enums.h"
#include "gstdwrite-utils.h"
/* XXX: MinGW's header does not define some enum values */
enum
{
GST_DWRITE_TEXT_ALIGNMENT_LEADING,
GST_DWRITE_TEXT_ALIGNMENT_TRAILING,
GST_DWRITE_TEXT_ALIGNMENT_CENTER,
GST_DWRITE_TEXT_ALIGNMENT_JUSTIFIED,
};
enum
{
GST_DWRITE_PARAGRAPH_ALIGNMENT_NEAR,
GST_DWRITE_PARAGRAPH_ALIGNMENT_FAR,
GST_DWRITE_PARAGRAPH_ALIGNMENT_CENTER,
};
enum
{
GST_DWRITE_FONT_WEIGHT_THIN = 100,
GST_DWRITE_FONT_WEIGHT_EXTRA_LIGHT = 200,
GST_DWRITE_FONT_WEIGHT_ULTRA_LIGHT = 200,
GST_DWRITE_FONT_WEIGHT_LIGHT = 300,
GST_DWRITE_FONT_WEIGHT_SEMI_LIGHT = 350,
GST_DWRITE_FONT_WEIGHT_NORMAL = 400,
GST_DWRITE_FONT_WEIGHT_REGULAR = 400,
GST_DWRITE_FONT_WEIGHT_MEDIUM = 500,
GST_DWRITE_FONT_WEIGHT_DEMI_BOLD = 600,
GST_DWRITE_FONT_WEIGHT_SEMI_BOLD = 600,
GST_DWRITE_FONT_WEIGHT_BOLD = 700,
GST_DWRITE_FONT_WEIGHT_EXTRA_BOLD = 800,
GST_DWRITE_FONT_WEIGHT_ULTRA_BOLD = 800,
GST_DWRITE_FONT_WEIGHT_BLACK = 900,
GST_DWRITE_FONT_WEIGHT_HEAVY = 900,
GST_DWRITE_FONT_WEIGHT_EXTRA_BLACK = 950,
GST_DWRITE_FONT_WEIGHT_ULTRA_BLACK = 950
};
enum
{
GST_DWRITE_FONT_STYLE_NORMAL,
GST_DWRITE_FONT_STYLE_OBLIQUE,
GST_DWRITE_FONT_STYLE_ITALIC,
};
enum
{
GST_DWRITE_FONT_STRETCH_UNDEFINED = 0,
GST_DWRITE_FONT_STRETCH_ULTRA_CONDENSED = 1,
GST_DWRITE_FONT_STRETCH_EXTRA_CONDENSED = 2,
GST_DWRITE_FONT_STRETCH_CONDENSED = 3,
GST_DWRITE_FONT_STRETCH_SEMI_CONDENSED = 4,
GST_DWRITE_FONT_STRETCH_NORMAL = 5,
GST_DWRITE_FONT_STRETCH_MEDIUM = 5,
GST_DWRITE_FONT_STRETCH_SEMI_EXPANDED = 6,
GST_DWRITE_FONT_STRETCH_EXPANDED = 7,
GST_DWRITE_FONT_STRETCH_EXTRA_EXPANDED = 8,
GST_DWRITE_FONT_STRETCH_ULTRA_EXPANDED = 9
};
GType
gst_dwrite_text_alignment_get_type (void)
{
static GType text_align_type = 0;
static const GEnumValue text_align_types[] = {
{GST_DWRITE_TEXT_ALIGNMENT_LEADING, "DWRITE_TEXT_ALIGNMENT_LEADING",
"leading"},
{GST_DWRITE_TEXT_ALIGNMENT_TRAILING,
"DWRITE_TEXT_ALIGNMENT_TRAILING", "trailing"},
{GST_DWRITE_TEXT_ALIGNMENT_CENTER,
"DWRITE_TEXT_ALIGNMENT_CENTER", "center"},
{GST_DWRITE_TEXT_ALIGNMENT_JUSTIFIED,
"DWRITE_TEXT_ALIGNMENT_JUSTIFIED", "justified"},
{0, nullptr, nullptr},
};
GST_DWRITE_CALL_ONCE_BEGIN {
text_align_type = g_enum_register_static ("GstDWriteTextAlignment",
text_align_types);
} GST_DWRITE_CALL_ONCE_END;
return text_align_type;
}
GType
gst_dwrite_paragraph_alignment_get_type (void)
{
static GType paragraph_align_type = 0;
static const GEnumValue paragraph_align_types[] = {
{GST_DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "DWRITE_PARAGRAPH_ALIGNMENT_NEAR",
"near"},
{GST_DWRITE_PARAGRAPH_ALIGNMENT_FAR,
"DWRITE_PARAGRAPH_ALIGNMENT_FAR", "far"},
{GST_DWRITE_PARAGRAPH_ALIGNMENT_CENTER,
"DWRITE_PARAGRAPH_ALIGNMENT_CENTER", "center"},
{0, nullptr, nullptr},
};
GST_DWRITE_CALL_ONCE_BEGIN {
paragraph_align_type =
g_enum_register_static ("GstDWriteParagraphAlignment",
paragraph_align_types);
} GST_DWRITE_CALL_ONCE_END;
return paragraph_align_type;
}
GType
gst_dwrite_font_weight_get_type (void)
{
static GType font_weight_type = 0;
static const GEnumValue font_weight_types[] = {
{GST_DWRITE_FONT_WEIGHT_THIN, "DWRITE_FONT_WEIGHT_THIN", "thin"},
{GST_DWRITE_FONT_WEIGHT_EXTRA_LIGHT, "DWRITE_FONT_WEIGHT_EXTRA_LIGHT",
"extra-light"},
{GST_DWRITE_FONT_WEIGHT_ULTRA_LIGHT,
"DWRITE_FONT_WEIGHT_ULTRA_LIGHT", "ultra-light"},
{GST_DWRITE_FONT_WEIGHT_LIGHT, "DWRITE_FONT_WEIGHT_LIGHT", "light"},
{GST_DWRITE_FONT_WEIGHT_SEMI_LIGHT, "DWRITE_FONT_WEIGHT_SEMI_LIGHT",
"semi-light"},
{GST_DWRITE_FONT_WEIGHT_NORMAL, "DWRITE_FONT_WEIGHT_NORMAL", "normal"},
{GST_DWRITE_FONT_WEIGHT_REGULAR, "DWRITE_FONT_WEIGHT_REGULAR",
"regular"},
{GST_DWRITE_FONT_WEIGHT_MEDIUM, "DWRITE_FONT_WEIGHT_MEDIUM", "medium"},
{GST_DWRITE_FONT_WEIGHT_DEMI_BOLD, "DWRITE_FONT_WEIGHT_DEMI_BOLD",
"demi-bold"},
{GST_DWRITE_FONT_WEIGHT_SEMI_BOLD, "DWRITE_FONT_WEIGHT_SEMI_BOLD",
"semi-bold"},
{GST_DWRITE_FONT_WEIGHT_BOLD, "DWRITE_FONT_WEIGHT_BOLD", "bold"},
{GST_DWRITE_FONT_WEIGHT_EXTRA_BOLD, "DWRITE_FONT_WEIGHT_EXTRA_BOLD",
"extra-bold"},
{GST_DWRITE_FONT_WEIGHT_ULTRA_BOLD, "DWRITE_FONT_WEIGHT_ULTRA_BOLD",
"ultra-bold"},
{GST_DWRITE_FONT_WEIGHT_BLACK, "DWRITE_FONT_WEIGHT_BLACK", "black"},
{GST_DWRITE_FONT_WEIGHT_HEAVY, "DWRITE_FONT_WEIGHT_HEAVY", "heavy"},
{GST_DWRITE_FONT_WEIGHT_EXTRA_BLACK, "DWRITE_FONT_WEIGHT_EXTRA_BLACK",
"extra-black"},
{GST_DWRITE_FONT_WEIGHT_ULTRA_BLACK, "DWRITE_FONT_WEIGHT_ULTRA_BLACK",
"ultra-black"},
{0, nullptr, nullptr},
};
GST_DWRITE_CALL_ONCE_BEGIN {
font_weight_type = g_enum_register_static ("GstDWriteFontWeight",
font_weight_types);
} GST_DWRITE_CALL_ONCE_END;
return font_weight_type;
}
GType
gst_dwrite_font_style_get_type (void)
{
static GType font_style_type = 0;
static const GEnumValue font_style_types[] = {
{GST_DWRITE_FONT_STYLE_NORMAL, "DWRITE_FONT_STYLE_NORMAL", "normal"},
{GST_DWRITE_FONT_STYLE_OBLIQUE, "DWRITE_FONT_STYLE_OBLIQUE", "oblique"},
{GST_DWRITE_FONT_STYLE_ITALIC, "DWRITE_FONT_STYLE_ITALIC", "italic"},
{0, nullptr, nullptr},
};
GST_DWRITE_CALL_ONCE_BEGIN {
font_style_type = g_enum_register_static ("GstDWriteFontStyle",
font_style_types);
} GST_DWRITE_CALL_ONCE_END;
return font_style_type;
}
GType
gst_dwrite_font_stretch_get_type (void)
{
static GType font_stretch_type = 0;
static const GEnumValue font_stretch_types[] = {
{GST_DWRITE_FONT_STRETCH_UNDEFINED, "DWRITE_FONT_STRETCH_UNDEFINED",
"undefined"},
{GST_DWRITE_FONT_STRETCH_ULTRA_CONDENSED,
"DWRITE_FONT_STRETCH_ULTRA_CONDENSED",
"ultra-condensed"},
{GST_DWRITE_FONT_STRETCH_EXTRA_CONDENSED,
"DWRITE_FONT_STRETCH_EXTRA_CONDENSED",
"extra-condensed"},
{GST_DWRITE_FONT_STRETCH_CONDENSED, "DWRITE_FONT_STRETCH_CONDENSED",
"condensed"},
{GST_DWRITE_FONT_STRETCH_SEMI_CONDENSED,
"DWRITE_FONT_STRETCH_SEMI_CONDENSED",
"semi-condensed"},
{GST_DWRITE_FONT_STRETCH_NORMAL, "DWRITE_FONT_STRETCH_NORMAL",
"normal"},
{GST_DWRITE_FONT_STRETCH_MEDIUM, "DWRITE_FONT_STRETCH_MEDIUM",
"medium"},
{GST_DWRITE_FONT_STRETCH_SEMI_EXPANDED,
"DWRITE_FONT_STRETCH_SEMI_EXPANDED",
"semi-expanded"},
{GST_DWRITE_FONT_STRETCH_EXPANDED, "DWRITE_FONT_STRETCH_EXPANDED",
"expanded"},
{GST_DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
"DWRITE_FONT_STRETCH_EXTRA_EXPANDED",
"extra-expanded"},
{GST_DWRITE_FONT_STRETCH_ULTRA_EXPANDED,
"DWRITE_FONT_STRETCH_ULTRA_EXPANDED",
"ultra-expanded"},
{0, nullptr, nullptr},
};
GST_DWRITE_CALL_ONCE_BEGIN {
font_stretch_type = g_enum_register_static ("GstDWriteFontStretch",
font_stretch_types);
} GST_DWRITE_CALL_ONCE_END;
return font_stretch_type;
}

View file

@ -0,0 +1,49 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/d3d11/gstd3d11.h>
#include <d2d1_1.h>
#include <dwrite.h>
#ifdef HAVE_DWRITE_COLOR_FONT
#include <d2d1_3.h>
#include <dwrite_3.h>
#endif
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_TEXT_ALIGNMENT (gst_dwrite_text_alignment_get_type ())
GType gst_dwrite_text_alignment_get_type (void);
#define GST_TYPE_DWRITE_PARAGRAPH_ALIGNMENT (gst_dwrite_paragraph_alignment_get_type ())
GType gst_dwrite_paragraph_alignment_get_type (void);
#define GST_TYPE_DWRITE_FONT_WEIGHT (gst_dwrite_font_weight_get_type ())
GType gst_dwrite_font_weight_get_type (void);
#define GST_TYPE_DWRITE_FONT_STYLE (gst_dwrite_font_style_get_type ())
GType gst_dwrite_font_style_get_type (void);
#define GST_TYPE_DWRITE_FONT_STRETCH (gst_dwrite_font_stretch_get_type ())
GType gst_dwrite_font_stretch_get_type (void);
G_END_DECLS

View file

@ -0,0 +1,660 @@
/* 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 "gstdwrite-renderer.h"
#include <wrl.h>
#include <vector>
GST_DEBUG_CATEGORY_EXTERN (gst_dwrite_debug);
#define GST_CAT_DEFAULT gst_dwrite_debug
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
struct RenderContext
{
gboolean collect_geometry;
std::vector<ComPtr<ID2D1Geometry>> backgrounds;
D2D1_RECT_F background_padding = D2D1::RectF ();
ID2D1Factory *factory;
ID2D1RenderTarget *target;
RECT client_rect;
D2D1_SIZE_F scale;
gboolean enable_color_font;
};
/* *INDENT-ON* */
static HRESULT
CombineTwoGeometries (ID2D1Factory * factory, ID2D1Geometry * a,
ID2D1Geometry * b, ID2D1Geometry ** result)
{
HRESULT hr;
ComPtr < ID2D1GeometrySink > sink;
ComPtr < ID2D1PathGeometry > geometry;
hr = factory->CreatePathGeometry (&geometry);
if (FAILED (hr)) {
GST_WARNING ("Couldn't create path geometry, 0x%x", (guint) hr);
return hr;
}
hr = geometry->Open (&sink);
if (FAILED (hr)) {
GST_WARNING ("Couldn't open path geometry, 0x%x", (guint) hr);
return hr;
}
hr = a->CombineWithGeometry (b,
D2D1_COMBINE_MODE_UNION, nullptr, sink.Get ());
if (FAILED (hr)) {
GST_WARNING ("Couldn't combine geometry, 0x%x", (guint) hr);
return hr;
}
hr = sink->Close ();
if (FAILED (hr)) {
GST_WARNING ("Couldn't close sink, 0x%x", (guint) hr);
return hr;
}
*result = geometry.Detach ();
return S_OK;
}
static void
CombineGeometries (ID2D1Factory * factory,
std::vector < ComPtr < ID2D1Geometry >> &geometries,
ID2D1Geometry ** result)
{
ComPtr < ID2D1Geometry > combined;
HRESULT hr;
if (geometries.empty ())
return;
/* *INDENT-OFF* */
for (const auto & it: geometries) {
if (!combined) {
combined = it;
} else {
ComPtr <ID2D1Geometry> tmp;
hr = CombineTwoGeometries (factory, it.Get (), combined.Get (), &tmp);
if (FAILED (hr))
return;
combined = tmp;
}
}
/* *INDENT-ON* */
if (!combined)
return;
*result = combined.Detach ();
}
STDMETHODIMP
IGstDWriteTextRenderer::CreateInstance (IDWriteFactory * factory,
IGstDWriteTextRenderer ** renderer)
{
IGstDWriteTextRenderer *self = new IGstDWriteTextRenderer ();
if (!self)
return E_OUTOFMEMORY;
self->factory_ = factory;
factory->AddRef ();
*renderer = self;
return S_OK;
}
/* IUnknown */
STDMETHODIMP_ (ULONG)
IGstDWriteTextRenderer::AddRef (void)
{
return InterlockedIncrement (&ref_count_);
}
STDMETHODIMP_ (ULONG)
IGstDWriteTextRenderer::Release (void)
{
ULONG ref_count;
ref_count = InterlockedDecrement (&ref_count_);
if (ref_count == 0)
delete this;
return ref_count;
}
STDMETHODIMP
IGstDWriteTextRenderer::QueryInterface (REFIID riid, void **object)
{
if (riid == __uuidof (IUnknown)) {
*object = static_cast < IUnknown * >
(static_cast < IGstDWriteTextRenderer * >(this));
} else if (riid == __uuidof (IDWritePixelSnapping)) {
*object = static_cast < IDWritePixelSnapping * >
(static_cast < IGstDWriteTextRenderer * >(this));
} else if (riid == IID_IGstDWriteTextRenderer) {
*object = static_cast < IDWriteTextRenderer * >
(static_cast < IGstDWriteTextRenderer * >(this));
} else {
*object = nullptr;
return E_NOINTERFACE;
}
AddRef ();
return S_OK;
}
/* IDWritePixelSnapping */
STDMETHODIMP
IGstDWriteTextRenderer::IsPixelSnappingDisabled (void *context,
BOOL * is_disabled)
{
*is_disabled = FALSE;
return S_OK;
}
STDMETHODIMP
IGstDWriteTextRenderer::GetCurrentTransform (void *context,
DWRITE_MATRIX * transform)
{
RenderContext *render_ctx;
g_assert (context != nullptr);
render_ctx = (RenderContext *) context;
render_ctx->target->GetTransform (reinterpret_cast <
D2D1_MATRIX_3X2_F * >(transform));
return S_OK;
}
STDMETHODIMP
IGstDWriteTextRenderer::GetPixelsPerDip (void *context,
FLOAT * pixels_per_dip)
{
*pixels_per_dip = 1.0f;
return S_OK;
}
/* IDWriteTextRenderer */
STDMETHODIMP
IGstDWriteTextRenderer::DrawGlyphRun (void *context, FLOAT origin_x,
FLOAT origin_y, DWRITE_MEASURING_MODE mode,
DWRITE_GLYPH_RUN const *glyph_run,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyph_run_desc,
IUnknown * client_effect)
{
ComPtr < ID2D1PathGeometry > geometry;
ComPtr < ID2D1GeometrySink > sink;
ComPtr < ID2D1TransformedGeometry > transformed;
ComPtr < ID2D1TransformedGeometry > shadow_transformed;
ComPtr < IGstDWriteTextEffect > effect;
ComPtr < ID2D1SolidColorBrush > brush;
ComPtr < ID2D1SolidColorBrush > outline_brush;
ComPtr < ID2D1SolidColorBrush > shadow_brush;
RenderContext *render_ctx;
ID2D1RenderTarget *target;
ID2D1Factory *factory;
HRESULT hr;
RECT client_rect;
D2D1_COLOR_F fg_color = D2D1::ColorF (D2D1::ColorF::Black);
g_assert (context != nullptr);
render_ctx = (RenderContext *) context;
client_rect = render_ctx->client_rect;
target = render_ctx->target;
factory = render_ctx->factory;
hr = factory->CreatePathGeometry (&geometry);
if (FAILED (hr))
return hr;
hr = geometry->Open (&sink);
if (FAILED (hr))
return hr;
hr = glyph_run->fontFace->GetGlyphRunOutline (glyph_run->fontEmSize,
glyph_run->glyphIndices, glyph_run->glyphAdvances,
glyph_run->glyphOffsets, glyph_run->glyphCount, glyph_run->isSideways,
glyph_run->bidiLevel % 2, sink.Get ());
if (FAILED (hr))
return hr;
sink->Close ();
hr = factory->CreateTransformedGeometry (geometry.Get (),
D2D1::Matrix3x2F::Translation (origin_x, origin_y) *
D2D1::Matrix3x2F::Scale (render_ctx->scale), &transformed);
if (FAILED (hr))
return hr;
/* Create new path geometry from the bound rect.
* Note that rect geometry cannot be used since the combined background
* geometry might not be represented as a single rectangle */
if (render_ctx->collect_geometry) {
D2D1_RECT_F bounds;
ComPtr < ID2D1RectangleGeometry > rect_geometry;
ComPtr < ID2D1PathGeometry > path_geometry;
ComPtr < ID2D1GeometrySink > path_sink;
hr = transformed->GetBounds (nullptr, &bounds);
if (FAILED (hr))
return hr;
bounds.left += render_ctx->background_padding.left;
bounds.right += render_ctx->background_padding.right;
bounds.top += render_ctx->background_padding.top;
bounds.bottom += render_ctx->background_padding.bottom;
bounds.left = MAX (bounds.left, (FLOAT) client_rect.left);
bounds.right = MIN (bounds.right, (FLOAT) client_rect.right);
bounds.top = MAX (bounds.top, (FLOAT) client_rect.top);
bounds.bottom = MIN (bounds.bottom, (FLOAT) client_rect.bottom);
hr = factory->CreateRectangleGeometry (bounds, &rect_geometry);
if (FAILED (hr))
return hr;
hr = factory->CreatePathGeometry (&path_geometry);
if (FAILED (hr))
return hr;
hr = path_geometry->Open (&path_sink);
if (FAILED (hr))
return hr;
hr = rect_geometry->Outline (nullptr, path_sink.Get ());
if (FAILED (hr))
return hr;
path_sink->Close ();
render_ctx->backgrounds.push_back (path_geometry);
return S_OK;
}
if (client_effect)
client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect);
if (effect) {
D2D1_COLOR_F color;
BOOL enabled;
effect->GetBrushColor (GST_DWRITE_BRUSH_TEXT, &color, &enabled);
if (enabled) {
target->CreateSolidColorBrush (color, &brush);
fg_color = color;
}
effect->GetBrushColor (GST_DWRITE_BRUSH_TEXT_OUTLINE, &color, &enabled);
if (enabled)
target->CreateSolidColorBrush (color, &outline_brush);
effect->GetBrushColor (GST_DWRITE_BRUSH_SHADOW, &color, &enabled);
if (enabled)
target->CreateSolidColorBrush (color, &shadow_brush);
} else {
target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush);
outline_brush = brush;
}
#ifdef HAVE_DWRITE_COLOR_FONT
if (render_ctx->enable_color_font) {
const DWRITE_GLYPH_IMAGE_FORMATS supported_formats =
DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE |
DWRITE_GLYPH_IMAGE_FORMATS_CFF |
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
DWRITE_GLYPH_IMAGE_FORMATS_SVG |
DWRITE_GLYPH_IMAGE_FORMATS_PNG |
DWRITE_GLYPH_IMAGE_FORMATS_JPEG |
DWRITE_GLYPH_IMAGE_FORMATS_TIFF |
DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
ComPtr < IDWriteColorGlyphRunEnumerator1 > glyph_run_enum;
ComPtr < IDWriteFactory4 > factory4;
ComPtr < ID2D1DeviceContext4 > ctx4;
hr = factory_->QueryInterface (IID_PPV_ARGS (&factory4));
if (SUCCEEDED (hr))
hr = target->QueryInterface (IID_PPV_ARGS (&ctx4));
if (SUCCEEDED (hr)) {
hr = factory4->TranslateColorGlyphRun (D2D1::Point2 (origin_x, origin_y),
glyph_run, glyph_run_desc, supported_formats,
DWRITE_MEASURING_MODE_NATURAL, nullptr, 0, &glyph_run_enum);
if (hr != DWRITE_E_NOCOLOR && SUCCEEDED (hr)) {
ComPtr < ID2D1SolidColorBrush > tmp_brush;
BOOL has_run = FALSE;
do {
DWRITE_COLOR_GLYPH_RUN1 const *color_run;
hr = glyph_run_enum->MoveNext (&has_run);
if (FAILED (hr))
return hr;
if (!has_run)
return S_OK;
hr = glyph_run_enum->GetCurrentRun (&color_run);
if (FAILED (hr))
return hr;
const auto cur_origin = D2D1::Point2F (color_run->baselineOriginX,
color_run->baselineOriginY);
switch (color_run->glyphImageFormat) {
case DWRITE_GLYPH_IMAGE_FORMATS_PNG:
case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8:
ctx4->DrawColorBitmapGlyphRun (color_run->glyphImageFormat,
cur_origin, &color_run->glyphRun,
DWRITE_MEASURING_MODE_NATURAL);
break;
case DWRITE_GLYPH_IMAGE_FORMATS_SVG:
{
ComPtr < ID2D1SolidColorBrush > svg_brush;
if (brush)
svg_brush = brush;
else if (outline_brush)
svg_brush = outline_brush;
ctx4->DrawSvgGlyphRun (cur_origin, &color_run->glyphRun,
svg_brush.Get ());
break;
}
case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE:
case DWRITE_GLYPH_IMAGE_FORMATS_CFF:
case DWRITE_GLYPH_IMAGE_FORMATS_COLR:
default:
if (!tmp_brush) {
hr = target->CreateSolidColorBrush (fg_color, &tmp_brush);
if (FAILED (hr))
return hr;
}
if (color_run->paletteIndex == 0xffff)
tmp_brush->SetColor (fg_color);
else
tmp_brush->SetColor (color_run->runColor);
target->DrawGlyphRun (cur_origin, &color_run->glyphRun,
tmp_brush.Get ());
break;
}
} while (has_run && SUCCEEDED (hr));
return S_OK;
}
}
}
#endif /* HAVE_DWRITE_COLOR_FONT */
if (shadow_brush) {
/* TODO: do we want to make this shadow configurable ? */
hr = factory->CreateTransformedGeometry (geometry.Get (),
D2D1::Matrix3x2F::Translation (origin_x + 1.5, origin_y + 1.5) *
D2D1::Matrix3x2F::Scale (render_ctx->scale), &shadow_transformed);
if (FAILED (hr))
return hr;
}
if (shadow_brush)
target->FillGeometry (shadow_transformed.Get (), shadow_brush.Get ());
if (outline_brush)
target->DrawGeometry (transformed.Get (), outline_brush.Get ());
if (brush)
target->FillGeometry (transformed.Get (), brush.Get ());
return S_OK;
}
STDMETHODIMP
IGstDWriteTextRenderer::DrawUnderline (void *context, FLOAT origin_x,
FLOAT origin_y, DWRITE_UNDERLINE const *underline, IUnknown * client_effect)
{
ComPtr < ID2D1RectangleGeometry > geometry;
ComPtr < ID2D1TransformedGeometry > transformed;
ComPtr < IGstDWriteTextEffect > effect;
ComPtr < ID2D1SolidColorBrush > brush;
ComPtr < ID2D1SolidColorBrush > outline_brush;
RenderContext *render_ctx;
ID2D1RenderTarget *target;
ID2D1Factory *factory;
HRESULT hr;
g_assert (context != nullptr);
render_ctx = (RenderContext *) context;
if (render_ctx->collect_geometry)
return S_OK;
target = render_ctx->target;
factory = render_ctx->factory;
hr = factory->CreateRectangleGeometry (D2D1::RectF (0, underline->offset,
underline->width, underline->offset + underline->thickness),
&geometry);
if (FAILED (hr)) {
GST_WARNING ("Couldn't create geometry, 0x%x", (guint) hr);
return hr;
}
hr = factory->CreateTransformedGeometry (geometry.Get (),
D2D1::Matrix3x2F::Translation (origin_x, origin_y) *
D2D1::Matrix3x2F::Scale (render_ctx->scale), &transformed);
if (FAILED (hr)) {
GST_WARNING ("Couldn't create transformed geometry, 0x%x", (guint) hr);
return hr;
}
if (client_effect)
client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect);
if (effect) {
D2D1_COLOR_F color;
BOOL enabled;
effect->GetBrushColor (GST_DWRITE_BRUSH_UNDERLINE, &color, &enabled);
if (enabled)
target->CreateSolidColorBrush (color, &brush);
effect->GetBrushColor (GST_DWRITE_BRUSH_UNDERLINE_OUTLINE, &color,
&enabled);
if (enabled)
target->CreateSolidColorBrush (color, &outline_brush);
} else {
target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush);
outline_brush = brush;
}
if (outline_brush)
target->DrawGeometry (transformed.Get (), outline_brush.Get ());
if (brush)
target->FillGeometry (transformed.Get (), brush.Get ());
return S_OK;
}
STDMETHODIMP
IGstDWriteTextRenderer::DrawStrikethrough (void *context, FLOAT origin_x,
FLOAT origin_y, DWRITE_STRIKETHROUGH const *strikethrough,
IUnknown * client_effect)
{
ComPtr < ID2D1RectangleGeometry > geometry;
ComPtr < ID2D1TransformedGeometry > transformed;
ComPtr < IGstDWriteTextEffect > effect;
ComPtr < ID2D1SolidColorBrush > brush;
ComPtr < ID2D1SolidColorBrush > outline_brush;
RenderContext *render_ctx;
ID2D1RenderTarget *target;
ID2D1Factory *factory;
HRESULT hr;
g_assert (context != nullptr);
render_ctx = (RenderContext *) context;
if (render_ctx->collect_geometry)
return S_OK;
target = render_ctx->target;
factory = render_ctx->factory;
hr = factory->CreateRectangleGeometry (D2D1::RectF (0,
strikethrough->offset, strikethrough->width,
strikethrough->offset + strikethrough->thickness), &geometry);
if (FAILED (hr)) {
GST_WARNING ("Couldn't create geometry, 0x%x", (guint) hr);
return hr;
}
hr = factory->CreateTransformedGeometry (geometry.Get (),
D2D1::Matrix3x2F::Translation (origin_x, origin_y) *
D2D1::Matrix3x2F::Scale (render_ctx->scale), &transformed);
if (FAILED (hr)) {
GST_WARNING ("Couldn't create transformed geometry, 0x%x", (guint) hr);
return hr;
}
if (client_effect)
client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect);
if (effect) {
D2D1_COLOR_F color;
BOOL enabled;
effect->GetBrushColor (GST_DWRITE_BRUSH_STRIKETHROUGH, &color, &enabled);
if (enabled)
target->CreateSolidColorBrush (color, &brush);
effect->GetBrushColor (GST_DWRITE_BRUSH_STRIKETHROUGH_OUTLINE, &color,
&enabled);
if (enabled)
target->CreateSolidColorBrush (color, &outline_brush);
} else {
target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush);
outline_brush = brush;
}
if (outline_brush)
target->DrawGeometry (transformed.Get (), outline_brush.Get ());
if (brush)
target->FillGeometry (transformed.Get (), brush.Get ());
return S_OK;
}
STDMETHODIMP
IGstDWriteTextRenderer::DrawInlineObject (void *context, FLOAT origin_x,
FLOAT origin_y, IDWriteInlineObject * inline_object, BOOL is_sideways,
BOOL is_right_to_left, IUnknown * client_effect)
{
GST_WARNING ("Not implemented");
return E_NOTIMPL;
}
IGstDWriteTextRenderer::IGstDWriteTextRenderer (void)
{
}
IGstDWriteTextRenderer::~IGstDWriteTextRenderer (void)
{
factory_->Release ();
}
STDMETHODIMP
IGstDWriteTextRenderer::Draw (const D2D1_POINT_2F & origin,
const D2D1_SIZE_F & scale, const RECT & client_rect,
const D2D1_COLOR_F & background_color,
const D2D1_RECT_F & background_padding,
gboolean enable_color_font,
IDWriteTextLayout * layout, ID2D1RenderTarget * target)
{
HRESULT hr;
RenderContext context;
ComPtr < ID2D1Factory > d2d_factory;
g_return_val_if_fail (layout != nullptr, E_INVALIDARG);
g_return_val_if_fail (target != nullptr, E_INVALIDARG);
target->GetFactory (&d2d_factory);
context.client_rect = client_rect;
context.target = target;
context.factory = d2d_factory.Get ();
context.scale = scale;
context.enable_color_font = enable_color_font;
if (IGstDWriteTextEffect::IsEnabledColor (background_color)) {
ComPtr < ID2D1Geometry > geometry;
context.collect_geometry = TRUE;
context.background_padding = background_padding;
hr = layout->Draw (&context, this, origin.x, origin.y);
if (FAILED (hr)) {
GST_WARNING ("Couldn't draw layout for collecting geometry, 0x%x",
(guint) hr);
return hr;
}
CombineGeometries (d2d_factory.Get (), context.backgrounds, &geometry);
if (geometry) {
ComPtr < ID2D1SolidColorBrush > brush;
hr = target->CreateSolidColorBrush (background_color, &brush);
if (FAILED (hr)) {
GST_WARNING ("Couldn't create solid brush, 0x%x", (guint) hr);
return hr;
}
target->FillGeometry (geometry.Get (), brush.Get ());
}
}
context.collect_geometry = FALSE;
hr = layout->Draw (&context, this, origin.x, origin.y);
if (FAILED (hr)) {
GST_WARNING ("Draw failed with 0x%x", (guint) hr);
}
return hr;
}

View file

@ -0,0 +1,92 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include "gstdwrite-enums.h"
#include "gstdwrite-effect.h"
DEFINE_GUID (IID_IGstDWriteTextRenderer, 0xb969dc25, 0xf7d2,
0x4cf8, 0x92, 0x0f, 0x5a, 0x27, 0xd1, 0x6d, 0x03, 0x6a);
class IGstDWriteTextRenderer : public IDWriteTextRenderer
{
public:
static STDMETHODIMP CreateInstance (IDWriteFactory * factory,
IGstDWriteTextRenderer ** renderer);
/* IUnknown */
STDMETHODIMP_ (ULONG) AddRef (void);
STDMETHODIMP_ (ULONG) Release (void);
STDMETHODIMP QueryInterface (REFIID riid,
void ** object);
/* IDWritePixelSnapping */
STDMETHODIMP IsPixelSnappingDisabled (void * context,
BOOL * is_disabled);
STDMETHODIMP GetCurrentTransform (void * context,
DWRITE_MATRIX * transform);
STDMETHODIMP GetPixelsPerDip (void * context,
FLOAT * pixels_per_dip);
/* IDWriteTextRenderer */
STDMETHODIMP DrawGlyphRun (void * context,
FLOAT origin_x,
FLOAT origin_y,
DWRITE_MEASURING_MODE mode,
DWRITE_GLYPH_RUN const *glyph_run,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyph_run_desc,
IUnknown * client_effect);
STDMETHODIMP DrawUnderline (void * context,
FLOAT origin_x,
FLOAT origin_y,
DWRITE_UNDERLINE const * underline,
IUnknown * client_effect);
STDMETHODIMP DrawStrikethrough (void * context,
FLOAT origin_x,
FLOAT origin_y,
DWRITE_STRIKETHROUGH const * strikethrough,
IUnknown * client_effect);
STDMETHODIMP DrawInlineObject (void * context,
FLOAT origin_x,
FLOAT origin_y,
IDWriteInlineObject * inline_object,
BOOL is_sideways,
BOOL is_right_to_left,
IUnknown * client_effect);
/* Entry point for drawing text */
STDMETHODIMP Draw (const D2D1_POINT_2F & origin,
const D2D1_SIZE_F & scale,
const RECT & client_rect,
const D2D1_COLOR_F & background_color,
const D2D1_RECT_F & background_padding,
gboolean enable_color_font,
IDWriteTextLayout * layout,
ID2D1RenderTarget * target);
private:
IGstDWriteTextRenderer (void);
virtual ~IGstDWriteTextRenderer (void);
private:
IDWriteFactory *factory_;
ULONG ref_count_ = 1;
};

View file

@ -0,0 +1,62 @@
/* 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.h>
#include "gstdwrite-utils.h"
gboolean
gst_dwrite_is_windows_10_or_greater (void)
{
static gboolean ret = FALSE;
GST_DWRITE_CALL_ONCE_BEGIN {
#if (!GST_D3D11_WINAPI_ONLY_APP)
OSVERSIONINFOEXW osverinfo;
typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW);
fRtlGetVersion *RtlGetVersion = NULL;
HMODULE hmodule = NULL;
memset (&osverinfo, 0, sizeof (OSVERSIONINFOEXW));
osverinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW);
hmodule = LoadLibraryW (L"ntdll.dll");
if (!hmodule)
return;
RtlGetVersion =
(fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion");
if (RtlGetVersion) {
RtlGetVersion (&osverinfo);
if (osverinfo.dwMajorVersion > 10 || osverinfo.dwMajorVersion == 10)
ret = TRUE;
}
if (hmodule)
FreeLibrary (hmodule);
#else
ret = TRUE;
#endif
} GST_DWRITE_CALL_ONCE_END;
return ret;
}

View file

@ -0,0 +1,79 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#define GST_DWRITE_TEXT_META_NAME "GstDWriteTextMeta"
#define GST_DWRITE_CAPS \
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY "," \
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, \
GST_D3D11_ALL_FORMATS) "; " \
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, \
GST_D3D11_ALL_FORMATS) "; " \
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY "," \
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, GST_VIDEO_FORMATS_ALL) ";" \
GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)
G_BEGIN_DECLS
gboolean gst_dwrite_is_windows_10_or_greater (void);
G_END_DECLS
#ifdef __cplusplus
#include <string>
#include <mutex>
static inline std::wstring
gst_dwrite_string_to_wstring (const std::string & str)
{
auto tmp = g_utf8_to_utf16 (str.c_str (), -1, nullptr, nullptr, nullptr);
if (!tmp)
return std::wstring ();
std::wstring ret = (wchar_t *) tmp;
g_free (tmp);
return ret;
}
static inline std::string
gst_dwrite_wstring_to_string (const std::wstring & str)
{
auto tmp = g_utf16_to_utf8 ((const gunichar2 *) str.c_str (),
-1, nullptr, nullptr, nullptr);
if (!tmp)
return std::string ();
std::string ret = tmp;
g_free (tmp);
return ret;
}
#define GST_DWRITE_CALL_ONCE_BEGIN \
static std::once_flag __once_flag; \
std::call_once (__once_flag, [&]()
#define GST_DWRITE_CALL_ONCE_END )
#endif /* __cplusplus */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
/* 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.
*/
#pragma once
#include <gst/d3d11/gstd3d11.h>
#include <gst/video/video.h>
#include <gst/base/base.h>
#include <string>
#include "gstdwrite-utils.h"
#include "gstdwrite-enums.h"
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_BASE_OVERLAY (gst_dwrite_base_overlay_get_type())
#define GST_DWRITE_BASE_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DWRITE_BASE_OVERLAY,GstDWriteBaseOverlay))
#define GST_DWRITE_BASE_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DWRITE_BASE_OVERLAY,GstDWriteBaseOverlayClass))
#define GST_DWRITE_BASE_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_DWRITE_BASE_OVERLAY,GstDWriteBaseOverlayClass))
#define GST_IS_DWRITE_BASE_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DWRITE_BASE_OVERLAY))
#define GST_IS_DWRITE_BASE_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DWRITE_BASE_OVERLAY))
typedef struct _GstDWriteBaseOverlay GstDWriteBaseOverlay;
typedef struct _GstDWriteBaseOverlayClass GstDWriteBaseOverlayClass;
typedef struct _GstDWriteBaseOverlayPrivate GstDWriteBaseOverlayPrivate;
typedef std::wstring WString;
struct _GstDWriteBaseOverlay
{
GstBaseTransform parent;
GstVideoInfo info;
GstDWriteBaseOverlayPrivate *priv;
};
struct _GstDWriteBaseOverlayClass
{
GstBaseTransformClass parent_class;
gboolean (*sink_event) (GstDWriteBaseOverlay * overlay,
GstEvent * event);
gboolean (*start) (GstDWriteBaseOverlay * overlay);
WString (*get_text) (GstDWriteBaseOverlay * overlay,
const std::wstring & default_text,
GstBuffer * buffer);
};
GType gst_dwrite_base_overlay_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDWriteBaseOverlay, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,300 @@
/* 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 "gstdwritebitmapmemory.h"
#include <mutex>
#include <condition_variable>
#include <thread>
#include <wrl.h>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
/* *INDENT-ON* */
GST_DEBUG_CATEGORY_STATIC (gst_dwrite_bitmap_allocator_debug);
#define GST_CAT_DEFAULT gst_dwrite_bitmap_allocator_debug
#define GST_DWRITE_BITMAP_MEMORY_NAME "DWriteBitmapMemory"
struct GstDWriteBitmapAllocatorPrivate
{
GstDWriteBitmapAllocatorPrivate ()
{
com_thread = std::thread (&GstDWriteBitmapAllocatorPrivate::ComThreadFunc,
this);
std::unique_lock < std::mutex > lk (init_lock);
while (!running)
init_cond.wait (lk);
}
~GstDWriteBitmapAllocatorPrivate ()
{
thread_lock.lock ();
terminate = true;
thread_cond.notify_one ();
thread_lock.unlock ();
com_thread.join ();
}
void ComThreadFunc ()
{
HRESULT hr;
CoInitializeEx (nullptr, COINIT_MULTITHREADED);
hr = CoCreateInstance (CLSID_WICImagingFactory, nullptr,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS (&factory));
if (FAILED (hr))
GST_ERROR ("Couldn't create image factory");
init_lock.lock ();
running = true;
init_cond.notify_one ();
init_lock.unlock ();
std::unique_lock < std::mutex > lk (thread_lock);
while (!terminate)
thread_cond.wait (lk);
if (factory)
factory->Release ();
CoUninitialize ();
}
IWICImagingFactory *factory = nullptr;
std::mutex init_lock;
std::condition_variable init_cond;
std::mutex thread_lock;
std::condition_variable thread_cond;
bool running = false;
bool terminate = false;
std::thread com_thread;
};
struct _GstDWriteBitmapAllocator
{
GstAllocator parent;
GstDWriteBitmapAllocatorPrivate *priv;
};
static void gst_dwrite_bitmap_allocator_finalize (GObject * object);
static GstMemory *gst_dwrite_bitmap_allocator_dummy_alloc (GstAllocator * alloc,
gsize size, GstAllocationParams * params);
static void gst_dwrite_bitmap_allocator_free (GstAllocator * alloc,
GstMemory * mem);
static gpointer gst_dwrite_bitmap_allocator_map (GstMemory * mem,
GstMapInfo * info, gsize maxsize);
static void gst_dwrite_bitmap_allocator_unmap (GstMemory * mem,
GstMapInfo * info);
static GstMemory *gst_dwrite_bitmap_allocator_share (GstMemory * mem,
gssize offset, gssize size);
#define gst_dwrite_bitmap_allocator_parent_class parent_class
G_DEFINE_TYPE (GstDWriteBitmapAllocator, gst_dwrite_bitmap_allocator,
GST_TYPE_ALLOCATOR);
static void
gst_dwrite_bitmap_allocator_class_init (GstDWriteBitmapAllocatorClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstAllocatorClass *alloc_class = GST_ALLOCATOR_CLASS (klass);
object_class->finalize = gst_dwrite_bitmap_allocator_finalize;
alloc_class->alloc = gst_dwrite_bitmap_allocator_dummy_alloc;
alloc_class->free = gst_dwrite_bitmap_allocator_free;
GST_DEBUG_CATEGORY_INIT (gst_dwrite_bitmap_allocator_debug,
"dwritebitmapallocator", 0, "dwritebitmapallocator");
}
static void
gst_dwrite_bitmap_allocator_init (GstDWriteBitmapAllocator * self)
{
GstAllocator *alloc = GST_ALLOCATOR_CAST (self);
alloc->mem_type = GST_DWRITE_BITMAP_MEMORY_NAME;
alloc->mem_map_full = gst_dwrite_bitmap_allocator_map;
alloc->mem_unmap_full = gst_dwrite_bitmap_allocator_unmap;
alloc->mem_share = gst_dwrite_bitmap_allocator_share;
GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
self->priv = new GstDWriteBitmapAllocatorPrivate ();
}
static void
gst_dwrite_bitmap_allocator_finalize (GObject * object)
{
GstDWriteBitmapAllocator *self = GST_DWRITE_BITMAP_ALLOCATOR (object);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstMemory *
gst_dwrite_bitmap_allocator_dummy_alloc (GstAllocator * alloc, gsize size,
GstAllocationParams * params)
{
g_assert_not_reached ();
return nullptr;
}
static void
gst_dwrite_bitmap_allocator_free (GstAllocator * alloc, GstMemory * mem)
{
GstDWriteBitmapMemory *dmem = (GstDWriteBitmapMemory *) mem;
if (dmem->bitmap)
dmem->bitmap->Release ();
g_free (dmem);
}
static GstMemory *
gst_dwrite_bitmap_allocator_share (GstMemory * mem, gssize offset, gssize size)
{
return nullptr;
}
static gpointer
gst_dwrite_bitmap_allocator_map (GstMemory * mem, GstMapInfo * info,
gsize maxsize)
{
GstDWriteBitmapMemory *dmem = (GstDWriteBitmapMemory *) mem;
ComPtr < IWICBitmapLock > bitmap_lock;
HRESULT hr;
WICRect rect = { 0, 0, dmem->info.width, dmem->info.height };
DWORD map_flags = 0;
BYTE *ptr = nullptr;
UINT size;
info->user_data[0] = nullptr;
if ((info->flags & GST_MAP_READ) == GST_MAP_READ)
map_flags |= (gint) WICBitmapLockRead;
if ((info->flags & GST_MAP_WRITE) == GST_MAP_WRITE)
map_flags |= (gint) WICBitmapLockWrite;
hr = dmem->bitmap->Lock (&rect, map_flags, &bitmap_lock);
if (FAILED (hr)) {
GST_ERROR_OBJECT (mem->allocator,
"Couldn't map bitmap, hr: 0x%x", (guint) hr);
return nullptr;
}
hr = bitmap_lock->GetDataPointer (&size, &ptr);
if (FAILED (hr)) {
GST_ERROR_OBJECT (mem->allocator, "Couldn't get data pointer, hr: 0x%x",
(guint) hr);
return nullptr;
}
info->user_data[0] = (gpointer) bitmap_lock.Detach ();
return (gpointer) ptr;
}
static void
gst_dwrite_bitmap_allocator_unmap (GstMemory * mem, GstMapInfo * info)
{
IWICBitmapLock *bitmap_lock;
bitmap_lock = (IWICBitmapLock *) info->user_data[0];
if (!bitmap_lock) {
GST_WARNING_OBJECT (mem->allocator, "No attached bitmap lock");
return;
}
bitmap_lock->Release ();
info->user_data[0] = nullptr;
}
GstDWriteBitmapAllocator *
gst_dwrite_bitmap_allocator_new (void)
{
GstDWriteBitmapAllocator *self = (GstDWriteBitmapAllocator *)
g_object_new (GST_TYPE_DWRITE_BITMAP_ALLOCATOR, nullptr);
gst_object_ref_sink (self);
if (!self->priv->factory) {
gst_object_unref (self);
return nullptr;
}
return self;
}
GstMemory *
gst_dwrite_bitmap_allocator_alloc (GstDWriteBitmapAllocator * alloc,
guint width, guint height)
{
HRESULT hr;
IWICImagingFactory *factory = alloc->priv->factory;
ComPtr < IWICBitmap > bitmap;
ComPtr < IWICBitmapLock > bitmap_lock;
WICRect rect = { 0, 0, (INT) width, (INT) height };
guint stride = width * 4;
GstDWriteBitmapMemory *mem;
gsize size;
hr = factory->CreateBitmap (width, height, GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnDemand, &bitmap);
if (FAILED (hr)) {
GST_ERROR_OBJECT (alloc, "Couldn't create bitmap, hr: 0x%x", (guint) hr);
return nullptr;
}
hr = bitmap->Lock (&rect, WICBitmapLockRead, &bitmap_lock);
if (FAILED (hr)) {
GST_ERROR_OBJECT (alloc, "Couldn't lock bitmap, hr: 0x%x", (guint) hr);
return nullptr;
}
bitmap_lock = nullptr;
size = stride * height;
mem = g_new0 (GstDWriteBitmapMemory, 1);
gst_memory_init (GST_MEMORY_CAST (mem), (GstMemoryFlags) 0,
GST_ALLOCATOR_CAST (alloc), nullptr, size, 0, 0, size);
gst_video_info_set_format (&mem->info, GST_VIDEO_FORMAT_BGRA, width, height);
mem->info.size = size;
mem->info.stride[0] = stride;
mem->bitmap = bitmap.Detach ();
return GST_MEMORY_CAST (mem);
}

View file

@ -0,0 +1,48 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
#include <wincodec.h>
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_BITMAP_ALLOCATOR (gst_dwrite_bitmap_allocator_get_type())
G_DECLARE_FINAL_TYPE (GstDWriteBitmapAllocator,
gst_dwrite_bitmap_allocator, GST, DWRITE_BITMAP_ALLOCATOR, GstAllocator);
typedef struct _GstDWriteBitmapMemory GstDWriteBitmapMemory;
struct _GstDWriteBitmapMemory
{
GstMemory mem;
GstVideoInfo info;
IWICBitmap *bitmap;
};
GstDWriteBitmapAllocator * gst_dwrite_bitmap_allocator_new (void);
GstMemory * gst_dwrite_bitmap_allocator_alloc (GstDWriteBitmapAllocator * alloc,
guint width,
guint height);
G_END_DECLS

View file

@ -0,0 +1,195 @@
/* 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 "gstdwritebitmappool.h"
GST_DEBUG_CATEGORY_STATIC (dwrite_bitmap_pool_debug);
#define GST_CAT_DEFAULT dwrite_bitmap_pool_debug
struct _GstDWriteBitmapPool
{
GstBufferPool parent;
GstDWriteBitmapAllocator *alloc;
GstVideoInfo info;
};
#define gst_dwrite_bitmap_pool_parent_class parent_class
G_DEFINE_TYPE (GstDWriteBitmapPool,
gst_dwrite_bitmap_pool, GST_TYPE_BUFFER_POOL);
static void gst_dwrite_bitmap_pool_finalize (GObject * object);
static const gchar **gst_dwrite_bitmap_pool_get_options (GstBufferPool * pool);
static gboolean gst_dwrite_bitmap_pool_set_config (GstBufferPool * pool,
GstStructure * config);
static GstFlowReturn gst_dwrite_bitmap_pool_alloc_buffer (GstBufferPool *
pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params);
static void
gst_dwrite_bitmap_pool_class_init (GstDWriteBitmapPoolClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstBufferPoolClass *pool_class = GST_BUFFER_POOL_CLASS (klass);
object_class->finalize = gst_dwrite_bitmap_pool_finalize;
pool_class->get_options =
GST_DEBUG_FUNCPTR (gst_dwrite_bitmap_pool_get_options);
pool_class->set_config =
GST_DEBUG_FUNCPTR (gst_dwrite_bitmap_pool_set_config);
pool_class->alloc_buffer =
GST_DEBUG_FUNCPTR (gst_dwrite_bitmap_pool_alloc_buffer);
GST_DEBUG_CATEGORY_INIT (dwrite_bitmap_pool_debug,
"dwritebitmappool", 0, "dwritebitmappool");
}
static void
gst_dwrite_bitmap_pool_init (GstDWriteBitmapPool * self)
{
}
static void
gst_dwrite_bitmap_pool_finalize (GObject * object)
{
GstDWriteBitmapPool *self = GST_DWRITE_BITMAP_POOL (object);
gst_clear_object (&self->alloc);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static const gchar **
gst_dwrite_bitmap_pool_get_options (GstBufferPool * pool)
{
static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
nullptr
};
return options;
}
static gboolean
gst_dwrite_bitmap_pool_set_config (GstBufferPool * pool, GstStructure * config)
{
GstDWriteBitmapPool *self = GST_DWRITE_BITMAP_POOL (pool);
GstCaps *caps;
guint size, min_buffers, max_buffers;
GstDWriteBitmapMemory *dmem;
GstMemory *mem;
if (!gst_buffer_pool_config_get_params (config,
&caps, &size, &min_buffers, &max_buffers)) {
GST_WARNING_OBJECT (self, "Invalid config");
return FALSE;
}
if (!caps) {
GST_WARNING_OBJECT (self, "No caps");
return FALSE;
}
if (!gst_video_info_from_caps (&self->info, caps)) {
GST_WARNING_OBJECT (self, "Invalid caps");
return FALSE;
}
if (GST_VIDEO_INFO_FORMAT (&self->info) != GST_VIDEO_FORMAT_BGRA) {
GST_WARNING_OBJECT (self, "Unsupported format");
return FALSE;
}
if (!self->alloc)
self->alloc = gst_dwrite_bitmap_allocator_new ();
if (!self->alloc) {
GST_WARNING_OBJECT (self, "Couldn't create allocator");
return FALSE;
}
mem = gst_dwrite_bitmap_allocator_alloc (self->alloc,
self->info.width, self->info.height);
if (!mem) {
GST_WARNING_OBJECT (self, "Couldn't allocate memory");
return FALSE;
}
dmem = (GstDWriteBitmapMemory *) mem;
size = dmem->info.size;
gst_memory_unref (mem);
gst_buffer_pool_config_set_params (config, caps, size, min_buffers,
max_buffers);
return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
}
static GstFlowReturn
gst_dwrite_bitmap_pool_alloc_buffer (GstBufferPool * pool,
GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
{
GstDWriteBitmapPool *self = GST_DWRITE_BITMAP_POOL (pool);
GstBuffer *buf;
GstMemory *mem;
GstDWriteBitmapMemory *dmem;
*buffer = nullptr;
if (!self->alloc) {
GST_ERROR_OBJECT (self, "Allocator was not configured");
return GST_FLOW_ERROR;
}
mem = gst_dwrite_bitmap_allocator_alloc (self->alloc,
self->info.width, self->info.height);
if (!mem) {
GST_ERROR_OBJECT (self, "Couldn't allocate memory");
return GST_FLOW_ERROR;
}
buf = gst_buffer_new ();
gst_buffer_append_memory (buf, mem);
dmem = (GstDWriteBitmapMemory *) mem;
gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_INFO_FORMAT (&dmem->info), GST_VIDEO_INFO_WIDTH (&dmem->info),
GST_VIDEO_INFO_HEIGHT (&dmem->info),
GST_VIDEO_INFO_N_PLANES (&dmem->info), dmem->info.offset,
dmem->info.stride);
*buffer = buf;
return GST_FLOW_OK;
}
GstBufferPool *
gst_dwrite_bitmap_pool_new (void)
{
GstBufferPool *pool = (GstBufferPool *)
g_object_new (GST_TYPE_DWRITE_BITMAP_POOL, nullptr);
gst_object_ref_sink (pool);
return pool;
}

View file

@ -0,0 +1,33 @@
/* 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.
*/
#pragma once
#include <gst/gst.h>
#include "gstdwritebitmapmemory.h"
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_BITMAP_POOL (gst_dwrite_bitmap_pool_get_type())
G_DECLARE_FINAL_TYPE (GstDWriteBitmapPool,
gst_dwrite_bitmap_pool, GST, DWRITE_BITMAP_POOL, GstBufferPool);
GstBufferPool * gst_dwrite_bitmap_pool_new (void);
G_END_DECLS

View file

@ -0,0 +1,193 @@
/* 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 "gstdwriteclockoverlay.h"
#include <mutex>
#include <time.h>
GST_DEBUG_CATEGORY_STATIC (dwrite_clock_overlay_debug);
#define GST_CAT_DEFAULT dwrite_clock_overlay_debug
enum
{
PROP_0,
PROP_TIME_FORMAT,
};
#define DEFAULT_TIME_FORMAT "%H:%M:%S"
struct GstDWriteClockOverlayPrivate
{
std::mutex lock;
std::string format;
WString wformat;
};
struct _GstDWriteClockOverlay
{
GstDWriteBaseOverlay parent;
GstDWriteClockOverlayPrivate *priv;
};
static void gst_dwrite_clock_overlay_finalize (GObject * object);
static void gst_dwrite_clock_overlay_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_dwrite_clock_overlay_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static WString
gst_dwrite_clock_overlay_get_text (GstDWriteBaseOverlay * overlay,
const WString & default_text, GstBuffer * buffer);
#define gst_dwrite_clock_overlay_parent_class parent_class
G_DEFINE_TYPE (GstDWriteClockOverlay, gst_dwrite_clock_overlay,
GST_TYPE_DWRITE_BASE_OVERLAY);
static void
gst_dwrite_clock_overlay_class_init (GstDWriteClockOverlayClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstDWriteBaseOverlayClass *overlay_class =
GST_DWRITE_BASE_OVERLAY_CLASS (klass);
object_class->finalize = gst_dwrite_clock_overlay_finalize;
object_class->set_property = gst_dwrite_clock_overlay_set_property;
object_class->get_property = gst_dwrite_clock_overlay_get_property;
g_object_class_install_property (object_class, PROP_TIME_FORMAT,
g_param_spec_string ("time-format", "Date/Time Format",
"Format to use for time and date value, as in strftime.",
DEFAULT_TIME_FORMAT,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
gst_element_class_set_static_metadata (element_class,
"DirectWrite Clock Overlay", "Filter/Editor/Video",
"Overlays the current clock time on a video stream",
"Seungha Yang <seungha@centricular.com>");
overlay_class->get_text =
GST_DEBUG_FUNCPTR (gst_dwrite_clock_overlay_get_text);
GST_DEBUG_CATEGORY_INIT (dwrite_clock_overlay_debug,
"dwriteclockoverlay", 0, "dwriteclockoverlay");
}
static void
gst_dwrite_clock_overlay_init (GstDWriteClockOverlay * self)
{
g_object_set (self, "text-alignment", DWRITE_TEXT_ALIGNMENT_LEADING,
"paragraph-alignment", DWRITE_PARAGRAPH_ALIGNMENT_NEAR, nullptr);
self->priv = new GstDWriteClockOverlayPrivate ();
self->priv->format = DEFAULT_TIME_FORMAT;
self->priv->wformat = gst_dwrite_string_to_wstring (DEFAULT_TIME_FORMAT);
}
static void
gst_dwrite_clock_overlay_finalize (GObject * object)
{
GstDWriteClockOverlay *self = GST_DWRITE_CLOCK_OVERLAY (object);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dwrite_clock_overlay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstDWriteClockOverlay *self = GST_DWRITE_CLOCK_OVERLAY (object);
GstDWriteClockOverlayPrivate *priv = self->priv;
std::lock_guard < std::mutex > lk (priv->lock);
switch (prop_id) {
case PROP_TIME_FORMAT:
{
const gchar *format = g_value_get_string (value);
if (format)
priv->format = format;
else
priv->format = DEFAULT_TIME_FORMAT;
priv->wformat = gst_dwrite_string_to_wstring (priv->format);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_dwrite_clock_overlay_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstDWriteClockOverlay *self = GST_DWRITE_CLOCK_OVERLAY (object);
GstDWriteClockOverlayPrivate *priv = self->priv;
std::lock_guard < std::mutex > lk (priv->lock);
switch (prop_id) {
case PROP_TIME_FORMAT:
g_value_set_string (value, priv->format.c_str ());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static WString
gst_dwrite_clock_overlay_render_time (GstDWriteClockOverlay * self)
{
GstDWriteClockOverlayPrivate *priv = self->priv;
struct tm *t;
time_t now;
wchar_t text[256];
now = time (nullptr);
t = localtime (&now);
if (!t)
return WString (L"--:--:--");
if (wcsftime (text, G_N_ELEMENTS (text), priv->wformat.c_str (), t) == 0)
return WString (L"--:--:--");
return WString (text);
}
static WString
gst_dwrite_clock_overlay_get_text (GstDWriteBaseOverlay * overlay,
const WString & default_text, GstBuffer * buffer)
{
GstDWriteClockOverlay *self = GST_DWRITE_CLOCK_OVERLAY (overlay);
WString time_str = gst_dwrite_clock_overlay_render_time (self);
if (default_text.empty ())
return time_str;
return default_text + WString (L" ") + time_str;
}

View file

@ -0,0 +1,30 @@
/* 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.
*/
#pragma once
#include "gstdwritebaseoverlay.h"
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_CLOCK_OVERLAY (gst_dwrite_clock_overlay_get_type())
G_DECLARE_FINAL_TYPE (GstDWriteClockOverlay,
gst_dwrite_clock_overlay, GST, DWRITE_CLOCK_OVERLAY, GstDWriteBaseOverlay);
G_END_DECLS

View file

@ -0,0 +1,72 @@
/* 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 "gstdwritetextoverlay.h"
GST_DEBUG_CATEGORY_STATIC (dwrite_text_overlay_debug);
#define GST_CAT_DEFAULT dwrite_text_overlay_debug
struct _GstDWriteTextOverlay
{
GstDWriteBaseOverlay parent;
};
static WString gst_dwrite_text_overlay_get_text (GstDWriteBaseOverlay * overlay,
const WString & default_text, GstBuffer * buffer);
#define gst_dwrite_text_overlay_parent_class parent_class
G_DEFINE_TYPE (GstDWriteTextOverlay, gst_dwrite_text_overlay,
GST_TYPE_DWRITE_BASE_OVERLAY);
static void
gst_dwrite_text_overlay_class_init (GstDWriteTextOverlayClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstDWriteBaseOverlayClass *overlay_class =
GST_DWRITE_BASE_OVERLAY_CLASS (klass);
gst_element_class_set_static_metadata (element_class,
"DirectWrite Text Overlay", "Filter/Editor/Video",
"Adds text strings on top of a video buffer",
"Seungha Yang <seungha@centricular.com>");
overlay_class->get_text =
GST_DEBUG_FUNCPTR (gst_dwrite_text_overlay_get_text);
GST_DEBUG_CATEGORY_INIT (dwrite_text_overlay_debug,
"dwritetextoverlay", 0, "dwritetextoverlay");
}
static void
gst_dwrite_text_overlay_init (GstDWriteTextOverlay * self)
{
g_object_set (self, "text-alignment", DWRITE_TEXT_ALIGNMENT_CENTER,
"paragraph-alignment", DWRITE_PARAGRAPH_ALIGNMENT_FAR, nullptr);
}
static WString
gst_dwrite_text_overlay_get_text (GstDWriteBaseOverlay * overlay,
const WString & default_text, GstBuffer * buffer)
{
return default_text;
}

View file

@ -0,0 +1,30 @@
/* 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.
*/
#pragma once
#include "gstdwritebaseoverlay.h"
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_TEXT_OVERLAY (gst_dwrite_text_overlay_get_type())
G_DECLARE_FINAL_TYPE (GstDWriteTextOverlay,
gst_dwrite_text_overlay, GST, DWRITE_TEXT_OVERLAY, GstDWriteBaseOverlay);
G_END_DECLS

View file

@ -0,0 +1,438 @@
/* 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 "gstdwritetimeoverlay.h"
#include <mutex>
#include <strsafe.h>
GST_DEBUG_CATEGORY_STATIC (dwrite_time_overlay_debug);
#define GST_CAT_DEFAULT dwrite_time_overlay_debug
typedef enum
{
GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_TIME,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_STREAM_TIME,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_RUNNING_TIME,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_TIME_CODE,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_ELAPSED_RUNNING_TIME,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_REFERENCE_TIMESTAMP,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_COUNT,
GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_OFFSET,
} GstDWriteTimeOverlayTimeLine;
#define GST_TYPE_DWrite_TIME_OVERLAY_TIME_LINE (gst_dwrite_time_overlay_time_line_type ())
static GType
gst_dwrite_time_overlay_time_line_type (void)
{
static GType type;
static const GEnumValue modes[] = {
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_TIME,
"buffer-time", "buffer-time"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_STREAM_TIME,
"stream-time", "stream-time"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_RUNNING_TIME,
"running-time", "running-time"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_TIME_CODE, "time-code", "time-code"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_ELAPSED_RUNNING_TIME,
"elapsed-running-time", "elapsed-running-time"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_REFERENCE_TIMESTAMP,
"reference-timestamp", "reference-timestamp"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_COUNT,
"buffer-count", "buffer-count"},
{GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_OFFSET,
"buffer-offset", "buffer-offset"},
{0, nullptr, nullptr},
};
GST_DWRITE_CALL_ONCE_BEGIN {
type = g_enum_register_static ("GstDWriteTimeOverlayTimeLine", modes);
} GST_DWRITE_CALL_ONCE_END;
return type;
}
enum
{
PROP_0,
PROP_TIME_LINE,
PROP_SHOW_TIMES_AS_DATES,
PROP_DATETIME_EPOCH,
PROP_DATETIME_FORMAT,
PROP_REFERENCE_TIMESTAMP_CAPS,
};
#define DEFAULT_TIME_LINE GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_TIME
#define DEFAULT_SHOW_TIMES_AS_DATES FALSE
#define DEFAULT_DATETIME_FORMAT "%F %T" /* YYYY-MM-DD hh:mm:ss */
static GstStaticCaps ntp_reference_timestamp_caps =
GST_STATIC_CAPS ("timestamp/x-ntp");
struct GstDWriteTimeOverlayPrivate
{
GstDWriteTimeOverlayPrivate ()
{
datetime_epoch = g_date_time_new_utc (1900, 1, 1, 0, 0, 0);
reference_timestamp_caps =
gst_static_caps_get (&ntp_reference_timestamp_caps);
}
~GstDWriteTimeOverlayPrivate ()
{
if (datetime_epoch)
g_date_time_unref (datetime_epoch);
gst_clear_caps (&reference_timestamp_caps);
}
std::mutex lock;
GstDWriteTimeOverlayTimeLine time_line = DEFAULT_TIME_LINE;
gboolean show_times_as_dates = DEFAULT_SHOW_TIMES_AS_DATES;
guint64 buffer_count = 0;
std::string datetime_format = DEFAULT_DATETIME_FORMAT;
GDateTime *datetime_epoch;
GstCaps *reference_timestamp_caps;
GstClockTime first_running_time = GST_CLOCK_TIME_NONE;
};
struct _GstDWriteTimeOverlay
{
GstDWriteBaseOverlay parent;
GstDWriteTimeOverlayPrivate *priv;
};
static void gst_dwrite_time_overlay_finalize (GObject * object);
static void gst_dwrite_time_overlay_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_dwrite_time_overlay_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static gboolean gst_dwrite_time_overlay_sink_event (GstBaseTransform * trans,
GstEvent * event);
static gboolean gst_dwrite_time_overlay_start (GstDWriteBaseOverlay * overlay);
static WString gst_dwrite_time_overlay_get_text (GstDWriteBaseOverlay * overlay,
const WString & default_text, GstBuffer * buffer);
#define gst_dwrite_time_overlay_parent_class parent_class
G_DEFINE_TYPE (GstDWriteTimeOverlay, gst_dwrite_time_overlay,
GST_TYPE_DWRITE_BASE_OVERLAY);
static void
gst_dwrite_time_overlay_class_init (GstDWriteTimeOverlayClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
GstDWriteBaseOverlayClass *overlay_class =
GST_DWRITE_BASE_OVERLAY_CLASS (klass);
object_class->finalize = gst_dwrite_time_overlay_finalize;
object_class->set_property = gst_dwrite_time_overlay_set_property;
object_class->get_property = gst_dwrite_time_overlay_get_property;
g_object_class_install_property (object_class, PROP_TIME_LINE,
g_param_spec_enum ("time-mode", "Time Mode", "What time to show",
GST_TYPE_DWrite_TIME_OVERLAY_TIME_LINE, DEFAULT_TIME_LINE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_DATETIME_EPOCH,
g_param_spec_boxed ("datetime-epoch", "Datetime Epoch",
"When showing times as dates, the initial date from which time "
"is counted, if not specified prime epoch is used (1900-01-01)",
G_TYPE_DATE_TIME,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_DATETIME_FORMAT,
g_param_spec_string ("datetime-format", "Datetime Format",
"When showing times as dates, the format to render date and time in",
DEFAULT_DATETIME_FORMAT,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_SHOW_TIMES_AS_DATES,
g_param_spec_boolean ("show-times-as-dates", "Show times as dates",
"Whether to display times, counted from datetime-epoch, as dates",
DEFAULT_SHOW_TIMES_AS_DATES,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_REFERENCE_TIMESTAMP_CAPS,
g_param_spec_boxed ("reference-timestamp-caps",
"Reference Timestamp Caps",
"Caps to use for the reference timestamp time mode",
GST_TYPE_CAPS,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
gst_element_class_set_static_metadata (element_class,
"DirectWrite Time Overlay", "Filter/Editor/Video",
"Overlays buffer time stamps on a video stream",
"Seungha Yang <seungha@centricular.com>");
trans_class->sink_event =
GST_DEBUG_FUNCPTR (gst_dwrite_time_overlay_sink_event);
overlay_class->start = GST_DEBUG_FUNCPTR (gst_dwrite_time_overlay_start);
overlay_class->get_text =
GST_DEBUG_FUNCPTR (gst_dwrite_time_overlay_get_text);
GST_DEBUG_CATEGORY_INIT (dwrite_time_overlay_debug,
"d3d11timeoverlay", 0, "d3d11timeoverlay");
gst_type_mark_as_plugin_api (GST_TYPE_DWrite_TIME_OVERLAY_TIME_LINE,
(GstPluginAPIFlags) 0);
}
static void
gst_dwrite_time_overlay_init (GstDWriteTimeOverlay * self)
{
g_object_set (self, "text-alignment", DWRITE_TEXT_ALIGNMENT_LEADING,
"paragraph-alignment", DWRITE_PARAGRAPH_ALIGNMENT_NEAR, nullptr);
self->priv = new GstDWriteTimeOverlayPrivate ();
}
static void
gst_dwrite_time_overlay_finalize (GObject * object)
{
GstDWriteTimeOverlay *self = GST_DWRITE_TIME_OVERLAY (object);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dwrite_time_overlay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstDWriteTimeOverlay *self = GST_DWRITE_TIME_OVERLAY (object);
GstDWriteTimeOverlayPrivate *priv = self->priv;
std::lock_guard < std::mutex > lk (priv->lock);
switch (prop_id) {
case PROP_TIME_LINE:
priv->time_line = (GstDWriteTimeOverlayTimeLine) g_value_get_enum (value);
break;
case PROP_SHOW_TIMES_AS_DATES:
priv->show_times_as_dates = g_value_get_boolean (value);
break;
case PROP_DATETIME_EPOCH:
g_date_time_unref (priv->datetime_epoch);
priv->datetime_epoch = (GDateTime *) g_value_dup_boxed (value);
break;
case PROP_DATETIME_FORMAT:
{
const gchar *format = g_value_get_string (value);
if (format)
priv->datetime_format = format;
else
priv->datetime_format = DEFAULT_DATETIME_FORMAT;
break;
}
case PROP_REFERENCE_TIMESTAMP_CAPS:
gst_clear_caps (&priv->reference_timestamp_caps);
priv->reference_timestamp_caps = (GstCaps *) g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_dwrite_time_overlay_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstDWriteTimeOverlay *self = GST_DWRITE_TIME_OVERLAY (object);
GstDWriteTimeOverlayPrivate *priv = self->priv;
std::lock_guard < std::mutex > lk (priv->lock);
switch (prop_id) {
case PROP_TIME_LINE:
g_value_set_enum (value, priv->time_line);
break;
case PROP_SHOW_TIMES_AS_DATES:
g_value_set_boolean (value, priv->show_times_as_dates);
break;
case PROP_DATETIME_EPOCH:
g_value_set_boxed (value, priv->datetime_epoch);
break;
case PROP_DATETIME_FORMAT:
g_value_set_string (value, priv->datetime_format.c_str ());
break;
case PROP_REFERENCE_TIMESTAMP_CAPS:
g_value_set_boxed (value, priv->reference_timestamp_caps);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gst_dwrite_time_overlay_start (GstDWriteBaseOverlay * overlay)
{
GstDWriteTimeOverlay *self = GST_DWRITE_TIME_OVERLAY (overlay);
GstDWriteTimeOverlayPrivate *priv = self->priv;
priv->first_running_time = GST_CLOCK_TIME_NONE;
priv->buffer_count = 0;
return TRUE;
}
static gboolean
gst_dwrite_time_overlay_sink_event (GstBaseTransform * trans, GstEvent * event)
{
GstDWriteTimeOverlay *self = GST_DWRITE_TIME_OVERLAY (trans);
GstDWriteTimeOverlayPrivate *priv = self->priv;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
priv->first_running_time = GST_CLOCK_TIME_NONE;
break;
default:
break;
}
return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
}
static WString
gst_dwrite_time_overlay_render_time (GstDWriteTimeOverlay * self,
GstClockTime time)
{
wchar_t text[256];
HRESULT hr;
guint h, m, s, ms;
if (!GST_CLOCK_TIME_IS_VALID (time))
return WString ();
h = (guint) (time / (GST_SECOND * 60 * 60));
m = (guint) ((time / (GST_SECOND * 60)) % 60);
s = (guint) ((time / GST_SECOND) % 60);
ms = (guint) ((time % GST_SECOND) / (1000 * 1000));
hr = StringCbPrintfW (text, sizeof (text), L"%u:%02u:%02u.%03u", h, m, s, ms);
if (FAILED (hr))
return WString ();
return WString (text);
}
static WString
gst_dwrite_time_overlay_get_text (GstDWriteBaseOverlay * overlay,
const WString & default_text, GstBuffer * buffer)
{
GstDWriteTimeOverlay *self = GST_DWRITE_TIME_OVERLAY (overlay);
GstDWriteTimeOverlayPrivate *priv = self->priv;
WString time_str;
WString ret;
std::lock_guard < std::mutex > lk (priv->lock);
gboolean show_buffer_count = FALSE;
if (priv->time_line == GST_DWRITE_TIME_OVERLAY_TIME_LINE_TIME_CODE) {
GstVideoTimeCodeMeta *tc_meta =
gst_buffer_get_video_time_code_meta (buffer);
if (!tc_meta) {
GST_DEBUG_OBJECT (self, "buffer without valid timecode");
time_str = L"00:00:00:00";
} else {
gchar *str = gst_video_time_code_to_string (&tc_meta->tc);
GST_DEBUG_OBJECT (self, "buffer with timecode %s", str);
time_str = gst_dwrite_string_to_wstring (str);
g_free (str);
}
} else {
GstBaseTransform *trans = GST_BASE_TRANSFORM (overlay);
GstClockTime ts, ts_buffer;
GstSegment *seg = &trans->segment;
ts = ts_buffer = GST_BUFFER_TIMESTAMP (buffer);
if (GST_CLOCK_TIME_IS_VALID (ts)) {
switch (priv->time_line) {
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_STREAM_TIME:
ts = gst_segment_to_stream_time (seg, GST_FORMAT_TIME, ts_buffer);
break;
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_RUNNING_TIME:
ts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, ts_buffer);
break;
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_ELAPSED_RUNNING_TIME:
ts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, ts_buffer);
if (!GST_CLOCK_TIME_IS_VALID (priv->first_running_time))
priv->first_running_time = ts;
ts -= priv->first_running_time;
break;
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_REFERENCE_TIMESTAMP:
{
GstReferenceTimestampMeta *meta;
if (priv->reference_timestamp_caps) {
meta = gst_buffer_get_reference_timestamp_meta (buffer,
priv->reference_timestamp_caps);
if (meta)
ts = meta->timestamp;
else
ts = 0;
} else {
ts = 0;
}
break;
}
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_COUNT:
show_buffer_count = TRUE;
priv->buffer_count++;
break;
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_OFFSET:
show_buffer_count = TRUE;
ts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, ts_buffer);
priv->buffer_count = gst_util_uint64_scale (ts, overlay->info.fps_n,
overlay->info.fps_d * GST_SECOND);
break;
case GST_DWRITE_TIME_OVERLAY_TIME_LINE_BUFFER_TIME:
default:
ts = ts_buffer;
break;
}
if (show_buffer_count) {
time_str = std::to_wstring (priv->buffer_count);
} else if (priv->show_times_as_dates) {
GDateTime *datetime;
gchar *str;
datetime =
g_date_time_add_seconds (priv->datetime_epoch,
((gdouble) ts) / GST_SECOND);
str = g_date_time_format (datetime, priv->datetime_format.c_str ());
time_str = gst_dwrite_string_to_wstring (str);
g_free (str);
g_date_time_unref (datetime);
} else {
time_str = gst_dwrite_time_overlay_render_time (self, ts);
}
}
}
if (default_text.empty ())
return time_str;
return default_text + WString (L" ") + time_str;
}

View file

@ -0,0 +1,30 @@
/* 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.
*/
#pragma once
#include "gstdwritebaseoverlay.h"
G_BEGIN_DECLS
#define GST_TYPE_DWRITE_TIME_OVERLAY (gst_dwrite_time_overlay_get_type())
G_DECLARE_FINAL_TYPE (GstDWriteTimeOverlay,
gst_dwrite_time_overlay, GST, DWRITE_TIME_OVERLAY, GstDWriteBaseOverlay);
G_END_DECLS

View file

@ -0,0 +1,70 @@
dwrite_sources = [
'gstdwrite-effect.cpp',
'gstdwrite-enums.cpp',
'gstdwrite-renderer.cpp',
'gstdwrite-utils.cpp',
'gstdwritebaseoverlay.cpp',
'gstdwritebitmapmemory.cpp',
'gstdwritebitmappool.cpp',
'gstdwriteclockoverlay.cpp',
'gstdwritetextoverlay.cpp',
'gstdwritetimeoverlay.cpp',
'plugin.cpp',
]
extra_args = ['-DGST_USE_UNSTABLE_API']
dwrite_option = get_option('dwrite')
if host_system != 'windows' or dwrite_option.disabled()
subdir_done()
endif
if not gstd3d11_dep.found()
if dwrite_option.enabled()
error('The dwrite was enabled explicitly, but required dependencies were not found.')
endif
subdir_done()
endif
d2d_dep = cc.find_library('d2d1', required: dwrite_option)
dwrite_lib = cc.find_library('dwrite', required : dwrite_option)
windowscodecs_lib = cc.find_library('windowscodecs', required : dwrite_option)
have_d2d_h = cc.has_header('d2d1_1.h')
have_dwrite_h = cc.has_header('dwrite.h')
have_wincodec_h = cc.has_header('wincodec.h')
if not have_d2d_h or not have_dwrite_h or not have_wincodec_h
if dwrite_option.enabled()
error('The dwrite was enabled explicitly, but required dependencies were not found.')
endif
subdir_done ()
endif
if cc.has_header('d2d1_3.h') and cc.has_header('dwrite_3.h')
# DWRITE_GLYPH_IMAGE_FORMATS enum requires NTDDI_WIN10_RS1
extra_args += ['-DWINVER=0x0A00',
'-D_WIN32_WINNT=0x0A00',
'-DNTDDI_VERSION=0x0A000002',
'-DHAVE_DWRITE_COLOR_FONT']
endif
# MinGW 32bits compiler seems to be complaining about redundant-decls
# when ComPtr is in use. Let's just disable the warning
if cc.get_id() != 'msvc'
extra_mingw_args = cc.get_supported_arguments([
'-Wno-redundant-decls',
])
extra_args += extra_mingw_args
endif
gstdwrite = library('gstdwrite',
dwrite_sources,
c_args : gst_plugins_bad_args + extra_args,
cpp_args: gst_plugins_bad_args + extra_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstvideo_dep, gstd3d11_dep, d2d_dep, dwrite_lib],
install : true,
install_dir : plugins_install_dir,
)
plugins += [gstdwrite]

View file

@ -0,0 +1,55 @@
/* 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.
*/
/**
* plugin-dwrite:
*
* Since: 1.24
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdwriteclockoverlay.h"
#include "gstdwritetextoverlay.h"
#include "gstdwritetimeoverlay.h"
GST_DEBUG_CATEGORY (gst_dwrite_debug);
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_dwrite_debug, "dwrite", 0, "dwrite");
gst_element_register (plugin, "dwriteclockoverlay", GST_RANK_NONE,
GST_TYPE_DWRITE_CLOCK_OVERLAY);
gst_element_register (plugin, "dwritetextoverlay", GST_RANK_NONE,
GST_TYPE_DWRITE_TEXT_OVERLAY);
gst_element_register (plugin, "dwritetimeoverlay", GST_RANK_NONE,
GST_TYPE_DWRITE_TIME_OVERLAY);
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
dwrite,
"dwrite",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);

View file

@ -9,6 +9,7 @@ subdir('decklink')
subdir('directsound') subdir('directsound')
subdir('directshow') subdir('directshow')
subdir('dvb') subdir('dvb')
subdir('dwrite')
subdir('fbdev') subdir('fbdev')
subdir('ipcpipeline') subdir('ipcpipeline')
subdir('kms') subdir('kms')