mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 19:21:06 +00:00
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:
parent
01edbf6c6b
commit
ba6800ce23
25 changed files with 5199 additions and 0 deletions
|
@ -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$)
|
||||||
|
|
|
@ -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')
|
||||||
|
|
142
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.cpp
Normal file
142
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.cpp
Normal 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* */
|
65
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.h
Normal file
65
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.h
Normal 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];
|
||||||
|
};
|
237
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-enums.cpp
Normal file
237
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-enums.cpp
Normal 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;
|
||||||
|
}
|
49
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-enums.h
Normal file
49
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-enums.h
Normal 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
|
660
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.cpp
Normal file
660
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.cpp
Normal 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;
|
||||||
|
}
|
92
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.h
Normal file
92
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.h
Normal 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;
|
||||||
|
};
|
62
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-utils.cpp
Normal file
62
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-utils.cpp
Normal 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;
|
||||||
|
}
|
79
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-utils.h
Normal file
79
subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-utils.h
Normal 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 */
|
2245
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebaseoverlay.cpp
Normal file
2245
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebaseoverlay.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
300
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebitmapmemory.cpp
Normal file
300
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebitmapmemory.cpp
Normal 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);
|
||||||
|
}
|
|
@ -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
|
195
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebitmappool.cpp
Normal file
195
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebitmappool.cpp
Normal 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;
|
||||||
|
}
|
33
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebitmappool.h
Normal file
33
subprojects/gst-plugins-bad/sys/dwrite/gstdwritebitmappool.h
Normal 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
|
193
subprojects/gst-plugins-bad/sys/dwrite/gstdwriteclockoverlay.cpp
Normal file
193
subprojects/gst-plugins-bad/sys/dwrite/gstdwriteclockoverlay.cpp
Normal 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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
438
subprojects/gst-plugins-bad/sys/dwrite/gstdwritetimeoverlay.cpp
Normal file
438
subprojects/gst-plugins-bad/sys/dwrite/gstdwritetimeoverlay.cpp
Normal 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;
|
||||||
|
}
|
|
@ -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
|
70
subprojects/gst-plugins-bad/sys/dwrite/meson.build
Normal file
70
subprojects/gst-plugins-bad/sys/dwrite/meson.build
Normal 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]
|
55
subprojects/gst-plugins-bad/sys/dwrite/plugin.cpp
Normal file
55
subprojects/gst-plugins-bad/sys/dwrite/plugin.cpp
Normal 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);
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in a new issue