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

source code of qtmultimedia/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp