1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
// Copyright (C) 2021 The Qt Company
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDebug>
#include <QThread>
#include <QEvent>
#include "qgstreamervideosink_p.h"
#include "qgstsubtitlesink_p.h"
QT_BEGIN_NAMESPACE
static GstBaseSinkClass *gst_sink_parent_class;
static thread_local QGstreamerVideoSink *gst_current_sink;
#define ST_SINK(s) QGstSubtitleSink *sink(reinterpret_cast<QGstSubtitleSink *>(s))
QGstSubtitleSink *QGstSubtitleSink::createSink(QGstreamerVideoSink *sink)
{
gst_current_sink = sink;
QGstSubtitleSink *gstSink = reinterpret_cast<QGstSubtitleSink *>(
g_object_new(QGstSubtitleSink::get_type(), nullptr));
g_object_set(gstSink, "async", false, nullptr);
return gstSink;
}
GType QGstSubtitleSink::get_type()
{
static const GTypeInfo info =
{
sizeof(QGstSubtitleSinkClass), // class_size
base_init, // base_init
nullptr, // base_finalize
class_init, // class_init
nullptr, // class_finalize
nullptr, // class_data
sizeof(QGstSubtitleSink), // instance_size
0, // n_preallocs
instance_init, // instance_init
nullptr // value_table
};
static const GType type = []() {
const auto result = g_type_register_static(
GST_TYPE_BASE_SINK, "QGstSubtitleSink", &info, GTypeFlags(0));
// Register the sink type to be used in custom piplines.
// When surface is ready the sink can be used.
gst_element_register(nullptr, "qtsubtitlesink", GST_RANK_PRIMARY, result);
return result;
}();
return type;
}
void QGstSubtitleSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
gst_sink_parent_class = reinterpret_cast<GstBaseSinkClass *>(g_type_class_peek_parent(g_class));
GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class);
base_sink_class->render = QGstSubtitleSink::render;
base_sink_class->get_caps = QGstSubtitleSink::get_caps;
base_sink_class->set_caps = QGstSubtitleSink::set_caps;
base_sink_class->propose_allocation = QGstSubtitleSink::propose_allocation;
base_sink_class->wait_event = QGstSubtitleSink::wait_event;
GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class);
element_class->change_state = QGstSubtitleSink::change_state;
gst_element_class_set_metadata(element_class,
"Qt built-in subtitle sink",
"Sink/Subtitle",
"Qt default built-in subtitle sink",
"The Qt Company");
GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class);
object_class->finalize = QGstSubtitleSink::finalize;
}
void QGstSubtitleSink::base_init(gpointer g_class)
{
static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
"sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("ANY"));
gst_element_class_add_pad_template(
GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
}
void QGstSubtitleSink::instance_init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
ST_SINK(instance);
Q_ASSERT(gst_current_sink);
sink->sink = gst_current_sink;
gst_current_sink = nullptr;
}
void QGstSubtitleSink::finalize(GObject *object)
{
// Chain up
G_OBJECT_CLASS(gst_sink_parent_class)->finalize(object);
}
GstStateChangeReturn QGstSubtitleSink::change_state(GstElement *element, GstStateChange transition)
{
return GST_ELEMENT_CLASS(gst_sink_parent_class)->change_state(element, transition);
}
GstCaps *QGstSubtitleSink::get_caps(GstBaseSink *base, GstCaps *filter)
{
return gst_sink_parent_class->get_caps(base, filter);
}
gboolean QGstSubtitleSink::set_caps(GstBaseSink *base, GstCaps *caps)
{
qDebug() << "set_caps:" << caps;
return gst_sink_parent_class->set_caps(base, caps);
}
gboolean QGstSubtitleSink::propose_allocation(GstBaseSink *base, GstQuery *query)
{
return gst_sink_parent_class->propose_allocation(base, query);
}
GstFlowReturn QGstSubtitleSink::wait_event(GstBaseSink *base, GstEvent *event)
{
GstFlowReturn retval = gst_sink_parent_class->wait_event(base, event);
ST_SINK(base);
if (event->type == GST_EVENT_GAP) {
// qDebug() << "gap, clearing subtitle";
sink->sink->setSubtitleText(QString());
}
return retval;
}
GstFlowReturn QGstSubtitleSink::render(GstBaseSink *base, GstBuffer *buffer)
{
ST_SINK(base);
GstMemory *mem = gst_buffer_get_memory(buffer, 0);
GstMapInfo info;
QString subtitle;
if (gst_memory_map(mem, &info, GST_MAP_READ))
subtitle = QString::fromUtf8(reinterpret_cast<const char *>(info.data));
gst_memory_unmap(mem, &info);
// qDebug() << "render" << buffer << subtitle;
sink->sink->setSubtitleText(subtitle);
return GST_FLOW_OK;
}
QT_END_NAMESPACE
|