summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2021-09-02 10:47:15 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-09-07 12:33:53 +0000
commitdec5393964668279d673b73ef8ebac3b4310985f (patch)
tree0b4f2bc44591b2c1e19f53854bd7c3020433f8b2
parent674c93e8677dd270c08c8d7ea05c845088911828 (diff)
Move gstreamer over to the new subtitle handling
Implement a custom sink for subtitles that notifies the video sink about changes in the subtitles. Remove the subtitleoverlay we have been using so far. Change-Id: I6ecfb1ab8538169d7365f21e0c0648b4aa2cd769 Reviewed-by: Piotr Srebrny <piotr.srebrny@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io> (cherry picked from commit 8d5182d03c1967d77ca8da531b004e85bb0892f0) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/multimedia/CMakeLists.txt1
-rw-r--r--src/multimedia/platform/gstreamer/common/qgst_p.h3
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstpipeline.cpp14
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstpipeline_p.h2
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp9
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h1
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp68
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h9
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp3
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h2
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp192
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h106
12 files changed, 379 insertions, 31 deletions
diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt
index 13e166a93..b7d7a8b0d 100644
--- a/src/multimedia/CMakeLists.txt
+++ b/src/multimedia/CMakeLists.txt
@@ -212,6 +212,7 @@ qt_internal_extend_target(Multimedia CONDITION QT_FEATURE_gstreamer
platform/gstreamer/common/qgstutils.cpp platform/gstreamer/common/qgstutils_p.h
platform/gstreamer/common/qgstvideobuffer.cpp platform/gstreamer/common/qgstvideobuffer_p.h
platform/gstreamer/common/qgstvideorenderersink.cpp platform/gstreamer/common/qgstvideorenderersink_p.h
+ platform/gstreamer/common/qgstsubtitlesink.cpp platform/gstreamer/common/qgstsubtitlesink_p.h
platform/gstreamer/qgstreamermediadevices.cpp platform/gstreamer/qgstreamermediadevices_p.h
platform/gstreamer/qgstreamerformatinfo.cpp platform/gstreamer/qgstreamerformatinfo_p.h
platform/gstreamer/qgstreamerintegration.cpp platform/gstreamer/qgstreamerintegration_p.h
diff --git a/src/multimedia/platform/gstreamer/common/qgst_p.h b/src/multimedia/platform/gstreamer/common/qgst_p.h
index f0a0bf79d..12eb123c0 100644
--- a/src/multimedia/platform/gstreamer/common/qgst_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgst_p.h
@@ -377,6 +377,9 @@ public:
GstPad *pad() const { return GST_PAD_CAST(object()); }
+ GstEvent *stickyEvent(GstEventType type) { return gst_pad_get_sticky_event(pad(), type, 0); }
+ bool sendEvent(GstEvent *event) { return gst_pad_send_event (pad(), event); }
+
template<auto Member, typename T>
void addProbe(T *instance, GstPadProbeType type) {
struct Impl {
diff --git a/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp b/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp
index ab2aa1c0e..3877694dd 100644
--- a/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp
@@ -66,6 +66,7 @@ public:
mutable qint64 m_position = 0;
double m_rate = 1.;
bool m_flushOnConfigChanges = false;
+ bool m_pendingFlush = false;
int m_configCounter = 0;
GstState m_savedState = GST_STATE_NULL;
@@ -271,6 +272,16 @@ void QGstPipeline::removeMessageFilter(QGstreamerBusMessageFilter *filter)
d->removeMessageFilter(filter);
}
+GstStateChangeReturn QGstPipeline::setState(GstState state)
+{
+ auto retval = gst_element_set_state(element(), state);
+ if (d->m_pendingFlush) {
+ d->m_pendingFlush = false;
+ flush();
+ }
+ return retval;
+}
+
void QGstPipeline::beginConfig()
{
if (!d)
@@ -296,8 +307,7 @@ void QGstPipeline::endConfig()
if (d->m_configCounter)
return;
- if (d->m_savedState != GST_STATE_NULL && d->m_flushOnConfigChanges)
- flush();
+ d->m_pendingFlush = true;
if (d->m_savedState == GST_STATE_PLAYING)
setState(GST_STATE_PLAYING);
d->m_savedState = GST_STATE_NULL;
diff --git a/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h b/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h
index d5621c1b3..6ffaf33d6 100644
--- a/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h
@@ -100,6 +100,8 @@ public:
void installMessageFilter(QGstreamerBusMessageFilter *filter);
void removeMessageFilter(QGstreamerBusMessageFilter *filter);
+ GstStateChangeReturn setState(GstState state);
+
GstPipeline *pipeline() const { return GST_PIPELINE_CAST(m_object); }
void dumpGraph(const char *fileName)
diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp
index 874b7bba1..f1aba4eda 100644
--- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp
@@ -78,6 +78,11 @@ QGstreamerMediaPlayer::QGstreamerMediaPlayer(QMediaPlayer *parent)
inputSelector[AudioStream] = QGstElement("input-selector", "audioInputSelector");
inputSelector[VideoStream] = QGstElement("input-selector", "videoInputSelector");
inputSelector[SubtitleStream] = QGstElement("input-selector", "subTitleInputSelector");
+ for (int i = 0; i < 3; ++i)
+ inputSelector[i].set ("sync-streams", true);
+
+ inputSelector[SubtitleStream].set("sync-mode", 1 /*clock*/);
+ inputSelector[SubtitleStream].set("cache-buffers", true);
playerPipeline.add(inputSelector[AudioStream], inputSelector[VideoStream], inputSelector[SubtitleStream]);
@@ -795,6 +800,10 @@ void QGstreamerMediaPlayer::setActiveTrack(QPlatformMediaPlayer::TrackType type,
auto &selector = inputSelector[type];
if (selector.isNull())
return;
+
+ if (type == QPlatformMediaPlayer::SubtitleStream)
+ gstVideoOutput->flushSubtitles();
+
selector.set("active-pad", streams.at(index));
// seek to force an immediate change of the stream
if (playerPipeline.state() == GST_STATE_PLAYING)
diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h
index ca0139784..aba416498 100644
--- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h
@@ -115,6 +115,7 @@ public:
bool processBusMessage(const QGstreamerMessage& message) override;
bool processSyncMessage(const QGstreamerMessage& message) override;
+
public Q_SLOTS:
void updatePosition() { positionChanged(position()); }
diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp
index 2cc4b38d8..36e0823d8 100644
--- a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp
@@ -39,6 +39,7 @@
#include <private/qgstreamervideooutput_p.h>
#include <private/qgstreamervideosink_p.h>
+#include <private/qgstsubtitlesink_p.h>
#include <qvideosink.h>
#include <QtCore/qloggingcategory.h>
@@ -55,10 +56,8 @@ QGstreamerVideoOutput::QGstreamerVideoOutput(QObject *parent)
videoQueue = QGstElement("queue", "videoQueue");
videoConvert = QGstElement("videoconvert", "videoConvert");
videoSink = QGstElement("fakesink", "fakeVideoSink");
- subTitleQueue = QGstElement("queue", "subtitleQueue");
- subtitleOverlay = QGstElement("subtitleoverlay", "subtitleoverlay");
- gstVideoOutput.add(videoQueue, subtitleOverlay, videoConvert, videoSink, subTitleQueue);
- if (!videoQueue.link(subtitleOverlay, videoConvert, videoSink))
+ gstVideoOutput.add(videoQueue, videoConvert, videoSink);
+ if (!videoQueue.link(videoConvert, videoSink))
qCDebug(qLcMediaVideoOutput) << ">>>>>> linking failed";
gstVideoOutput.addGhostPad(videoQueue, "sink");
@@ -72,19 +71,19 @@ QGstreamerVideoOutput::~QGstreamerVideoOutput()
void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink)
{
auto *gstVideoSink = sink ? static_cast<QGstreamerVideoSink *>(sink->platformVideoSink()) : nullptr;
- if (gstVideoSink == m_videoWindow)
+ if (gstVideoSink == m_videoSink)
return;
- if (m_videoWindow)
- m_videoWindow->setPipeline({});
+ if (m_videoSink)
+ m_videoSink->setPipeline({});
- m_videoWindow = gstVideoSink;
- if (m_videoWindow)
- m_videoWindow->setPipeline(gstPipeline);
+ m_videoSink = gstVideoSink;
+ if (m_videoSink)
+ m_videoSink->setPipeline(gstPipeline);
QGstElement gstSink;
- if (m_videoWindow) {
- gstSink = m_videoWindow->gstSink();
+ if (m_videoSink) {
+ gstSink = m_videoSink->gstSink();
isFakeSink = false;
} else {
gstSink = QGstElement("fakesink", "fakevideosink");
@@ -107,6 +106,8 @@ void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink)
gst_element_send_event(videoSink.element(), event);
videoSink.setState(GST_STATE_PAUSED);
+ doLinkSubtitleStream();
+
gstPipeline.endConfig();
qCDebug(qLcMediaVideoOutput) << "sinkChanged" << gstSink.name();
@@ -121,8 +122,8 @@ void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink)
void QGstreamerVideoOutput::setPipeline(const QGstPipeline &pipeline)
{
gstPipeline = pipeline;
- if (m_videoWindow)
- m_videoWindow->setPipeline(gstPipeline);
+ if (m_videoSink)
+ m_videoSink->setPipeline(gstPipeline);
}
void QGstreamerVideoOutput::linkSubtitleStream(QGstElement src)
@@ -132,20 +133,26 @@ void QGstreamerVideoOutput::linkSubtitleStream(QGstElement src)
return;
gstPipeline.beginConfig();
+ subtitleSrc = src;
+ doLinkSubtitleStream();
+ gstPipeline.endConfig();
+}
- if (!subtitleSrc.isNull()) {
- subtitleSrc.unlink(subTitleQueue);
- subTitleQueue.unlink(subtitleOverlay);
+void QGstreamerVideoOutput::doLinkSubtitleStream()
+{
+ if (!subtitleSink.isNull()) {
+ subtitleSink.setStateSync(GST_STATE_NULL);
+ gstPipeline.remove(subtitleSink);
+ subtitleSink = {};
}
- subtitleSrc = src;
- if (!subtitleSrc.isNull()) {
- if (!subtitleSrc.link(subTitleQueue))
- qCDebug(qLcMediaVideoOutput) << "link subtitle stream 1 failed";
- if (!subTitleQueue.link(subtitleOverlay))
- qCDebug(qLcMediaVideoOutput) << "link subtitle stream 1 failed";
+ if (!m_videoSink || subtitleSrc.isNull())
+ return;
+ if (subtitleSink.isNull()) {
+ subtitleSink = m_videoSink->subtitleSink();
+ gstPipeline.add(subtitleSink);
}
-
- gstPipeline.endConfig();
+ if (!subtitleSrc.link(subtitleSink))
+ qCDebug(qLcMediaVideoOutput) << "link subtitle stream failed";
}
void QGstreamerVideoOutput::setIsPreview()
@@ -159,4 +166,15 @@ void QGstreamerVideoOutput::setIsPreview()
videoQueue.set("max-size-time", 0);
}
+void QGstreamerVideoOutput::flushSubtitles()
+{
+ if (!subtitleSink.isNull()) {
+ auto pad = subtitleSink.staticPad("sink");
+ auto *event = gst_event_new_flush_start();
+ pad.sendEvent(event);
+ event = gst_event_new_flush_stop(false);
+ pad.sendEvent(event);
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h
index 1468343ab..41e8da413 100644
--- a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h
@@ -73,7 +73,7 @@ public:
~QGstreamerVideoOutput();
void setVideoSink(QVideoSink *sink);
- QGstreamerVideoSink *gstreamerVideoSink() const { return m_videoWindow; }
+ QGstreamerVideoSink *gstreamerVideoSink() const { return m_videoSink; }
void setPipeline(const QGstPipeline &pipeline);
@@ -81,23 +81,24 @@ public:
void linkSubtitleStream(QGstElement subtitleSrc);
void setIsPreview();
+ void flushSubtitles();
private:
+ void doLinkSubtitleStream();
- QPointer<QGstreamerVideoSink> m_videoWindow;
+ QPointer<QGstreamerVideoSink> m_videoSink;
bool isFakeSink = true;
// Gst elements
QGstPipeline gstPipeline;
QGstBin gstVideoOutput;
- QGstElement subTitleQueue;
QGstElement videoQueue;
QGstElement videoConvert;
- QGstElement subtitleOverlay;
QGstElement videoSink;
QGstElement subtitleSrc;
+ QGstElement subtitleSink;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp
index a770d0e57..ca7daf6eb 100644
--- a/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp
@@ -39,6 +39,7 @@
#include "qgstreamervideosink_p.h"
#include "private/qgstvideorenderersink_p.h"
+#include "private/qgstsubtitlesink_p.h"
#include <private/qgstutils_p.h>
#include <QtGui/private/qrhi_p.h>
@@ -91,6 +92,8 @@ QGstreamerVideoSink::QGstreamerVideoSink(QVideoSink *parent)
sinkBin.add(gstQueue, gstPreprocess);
gstQueue.link(gstPreprocess);
sinkBin.addGhostPad(gstQueue, "sink");
+
+ gstSubtitleSink = GST_ELEMENT(QGstSubtitleSink::createSink(this));
}
QGstreamerVideoSink::~QGstreamerVideoSink()
diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h
index e6d588a36..a049bc465 100644
--- a/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h
@@ -79,6 +79,7 @@ public:
QRhi *rhi() const { return m_rhi; }
QGstElement gstSink();
+ QGstElement subtitleSink() const { return gstSubtitleSink; }
void setPipeline(QGstPipeline pipeline);
@@ -100,6 +101,7 @@ private:
QGstElement gstPreprocess;
QGstElement gstVideoSink;
QGstElement gstQtSink;
+ QGstElement gstSubtitleSink;
QRhi *m_rhi = nullptr;
diff --git a/src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp b/src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp
new file mode 100644
index 000000000..d9b76d57f
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QDebug>
+#include <QThread>
+#include <QEvent>
+
+#include "qgstreamervideosink_p.h"
+#include "qgstsubtitlesink_p.h"
+#include "qgstutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static GstBaseSinkClass *sink_parent_class;
+static thread_local QGstreamerVideoSink *current_sink;
+
+#define ST_SINK(s) QGstSubtitleSink *sink(reinterpret_cast<QGstSubtitleSink *>(s))
+
+QGstSubtitleSink *QGstSubtitleSink::createSink(QGstreamerVideoSink *sink)
+{
+ 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 GType type = 0;
+
+ if (type == 0) {
+ 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
+ };
+
+ type = 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, type);
+ }
+
+ return type;
+}
+
+void QGstSubtitleSink::class_init(gpointer g_class, gpointer class_data)
+{
+ Q_UNUSED(class_data);
+
+ 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(current_sink);
+ sink->sink = current_sink;
+ current_sink = nullptr;
+}
+
+void QGstSubtitleSink::finalize(GObject *object)
+{
+ // Chain up
+ G_OBJECT_CLASS(sink_parent_class)->finalize(object);
+}
+
+GstStateChangeReturn QGstSubtitleSink::change_state(GstElement *element, GstStateChange transition)
+{
+ return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition);
+}
+
+GstCaps *QGstSubtitleSink::get_caps(GstBaseSink *base, GstCaps *filter)
+{
+ return sink_parent_class->get_caps(base, filter);
+}
+
+gboolean QGstSubtitleSink::set_caps(GstBaseSink *base, GstCaps *caps)
+{
+ qDebug() << "set_caps:" << QGstCaps(caps).toString();
+ return sink_parent_class->set_caps(base, caps);
+}
+
+gboolean QGstSubtitleSink::propose_allocation(GstBaseSink *base, GstQuery *query)
+{
+ return sink_parent_class->propose_allocation(base, query);
+}
+
+GstFlowReturn QGstSubtitleSink::wait_event(GstBaseSink *base, GstEvent *event)
+{
+ GstFlowReturn retval = 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(info.data);
+ gst_memory_unmap(mem, &info);
+// qDebug() << "render" << buffer << subtitle;
+ sink->sink->setSubtitleText(subtitle);
+ return GST_FLOW_OK;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h b/src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h
new file mode 100644
index 000000000..aa9127c7d
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGSTSUBTITLESINK_P_H
+#define QGSTSUBTITLESINK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qwaitcondition.h>
+#include <private/qgst_p.h>
+#include <gst/base/gstbasesink.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerVideoSink;
+
+class Q_MULTIMEDIA_EXPORT QGstSubtitleSink
+{
+public:
+ GstBaseSink parent;
+
+ static QGstSubtitleSink *createSink(QGstreamerVideoSink *sink);
+
+private:
+ static GType get_type();
+ static void class_init(gpointer g_class, gpointer class_data);
+ static void base_init(gpointer g_class);
+ static void instance_init(GTypeInstance *instance, gpointer g_class);
+
+ static void finalize(GObject *object);
+
+ static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
+
+ static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter);
+ static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
+
+ static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query);
+
+ static GstFlowReturn wait_event(GstBaseSink * sink, GstEvent * event);
+ static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer);
+
+private:
+ QGstreamerVideoSink *sink = nullptr;
+};
+
+
+class QGstSubtitleSinkClass
+{
+public:
+ GstBaseSinkClass parent_class;
+};
+
+QT_END_NAMESPACE
+
+#endif