Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgstreamer_qrc_handler.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qfile.h>
7#include <QtCore/qglobal.h>
8#include <QtCore/qstring.h>
9#include <QtCore/qurl.h>
10
11#include <gst/base/gstbasesrc.h>
12
14
15namespace {
16
17// qt helpers
18
19using namespace Qt::Literals;
20
22{
23 if (url.scheme() == "qrc"_L1)
24 return ':'_L1 + url.path();
25 return std::nullopt;
26}
27
28std::optional<QUrl> qQrcPathToQUrl(QStringView path)
29{
30 if (!path.empty() && path[0] == ':'_L1)
31 return QUrl(u"qrc://"_s + path.mid(1));
32 return std::nullopt;
33}
34
35// glib / gstreamer object
36
40};
41
43{
44 void getProperty(guint propId, GValue *value, const GParamSpec *pspec) const;
45 void setProperty(guint propId, const GValue *value, const GParamSpec *pspec);
46 auto lockObject() const;
47
48 bool start();
49 bool stop();
50
52 GstFlowReturn fill(guint64 offset, guint length, GstBuffer *buf);
53 void getURI(GValue *value) const;
54 bool setURI(const char *location, GError **err = nullptr);
55
58};
59
60void QGstQrcSrc::getProperty(guint propId, GValue *value, const GParamSpec *pspec) const
61{
62 switch (propId) {
63 case PROP_URI:
64 return getURI(value);
65
66 default:
67 G_OBJECT_WARN_INVALID_PROPERTY_ID(this, propId, pspec);
68 break;
69 }
70}
71
72void QGstQrcSrc::setProperty(guint propId, const GValue *value, const GParamSpec *pspec)
73{
74 switch (propId) {
75 case PROP_URI:
76 setURI(g_value_get_string(value));
77 break;
78 default:
79 G_OBJECT_WARN_INVALID_PROPERTY_ID(this, propId, pspec);
80 break;
81 }
82}
83
85{
86 GST_OBJECT_LOCK(this);
87 return qScopeGuard([this] {
88 GST_OBJECT_UNLOCK(this);
89 });
90}
91
93{
94 auto lock = lockObject();
95 if (file.fileName().isEmpty()) {
96 GST_ELEMENT_ERROR(this, RESOURCE, NOT_FOUND, ("No resource name specified for reading."),
97 (nullptr));
98 return false;
99 }
100
101 bool opened = file.open(QIODevice::ReadOnly);
102 if (!opened) {
103 GST_ELEMENT_ERROR(this, RESOURCE, NOT_FOUND, (nullptr),
104 ("No such resource \"%s\"", file.fileName().toUtf8().constData()));
105 return false;
106 }
107
108 gst_base_src_set_dynamic_size(&baseSrc, false);
109
110 Q_ASSERT(file.isOpen());
111 return true;
112}
113
115{
116 auto lock = lockObject();
117 file.close();
118 return true;
119}
120
122{
123 auto lock = lockObject();
124 if (!file.isOpen())
125 return std::nullopt;
126 return file.size();
127}
128
129GstFlowReturn QGstQrcSrc::fill(guint64 offset, guint length, GstBuffer *buf)
130{
131 auto lock = lockObject();
132
133 if (!file.isOpen())
134 return GST_FLOW_ERROR;
135
136 if (G_UNLIKELY(offset != guint64(-1) && guint64(file.pos()) != offset)) {
137 bool success = file.seek(int64_t(offset));
138 if (!success) {
139 GST_ELEMENT_ERROR(this, RESOURCE, READ, (nullptr), GST_ERROR_SYSTEM);
140 return GST_FLOW_ERROR;
141 }
142 }
143
144 GstMapInfo info;
145 if (!gst_buffer_map(buf, &info, GST_MAP_WRITE)) {
146 GST_ELEMENT_ERROR(this, RESOURCE, WRITE, (nullptr), ("Can't map buffer for writing"));
147 return GST_FLOW_ERROR;
148 }
149
150 int64_t remain = length;
151 int64_t totalRead = 0;
152 while (remain) {
153 int64_t bytesRead = file.read(reinterpret_cast<char *>(info.data) + totalRead, remain);
154 if (bytesRead == -1) {
155 if (file.atEnd()) {
156 gst_buffer_unmap(buf, &info);
157 gst_buffer_resize(buf, 0, 0);
158 return GST_FLOW_EOS;
159 }
160 GST_ELEMENT_ERROR(this, RESOURCE, READ, (nullptr), GST_ERROR_SYSTEM);
161 gst_buffer_unmap(buf, &info);
162 gst_buffer_resize(buf, 0, 0);
163 return GST_FLOW_ERROR;
164 }
165
166 remain -= bytesRead;
167 totalRead += bytesRead;
168 }
169
170 gst_buffer_unmap(buf, &info);
171 if (totalRead != length)
172 gst_buffer_resize(buf, 0, totalRead);
173
174 GST_BUFFER_OFFSET(buf) = offset;
175 GST_BUFFER_OFFSET_END(buf) = offset + totalRead;
176
177 return GST_FLOW_OK;
178}
179
180void QGstQrcSrc::getURI(GValue *value) const
181{
182 auto lock = lockObject();
183 std::optional<QUrl> url = qQrcPathToQUrl(file.fileName());
184 if (url)
185 g_value_set_string(value, url->toString().toUtf8().constData());
186 else
187 g_value_set_string(value, nullptr);
188}
189
190bool QGstQrcSrc::setURI(const char *location, GError **err)
191{
192 Q_ASSERT(QLatin1StringView(location).startsWith("qrc:/"_L1));
193
194 {
195 auto lock = lockObject();
196 GstState state = GST_STATE(this);
197 if (state != GST_STATE_READY && state != GST_STATE_NULL) {
198 g_warning("Changing the `uri' property on qrcsrc when the resource is open is not "
199 "supported.");
200 if (err)
201 g_set_error(err, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
202 "Changing the `uri' property on qrcsrc when the resource is open "
203 "is not supported.");
204 return false;
205 }
206 }
207
208 std::optional<QString> path = qQUrlToQrcPath(QString::fromUtf8(location));
209
210 {
211 auto lock = lockObject();
212 file.close();
213 file.setFileName(path.value_or(u""_s));
214 }
215
216 g_object_notify(G_OBJECT(this), "uri");
217
218 return true;
219}
220
225
226// GObject
227static void gst_qrc_src_class_init(QGstQrcSrcClass *klass);
228static void gst_qrc_src_init(QGstQrcSrc *self);
229
231
232template <typename T>
234{
235 return (G_TYPE_CHECK_INSTANCE_CAST((obj), gst_qrc_src_get_type(), QGstQrcSrc));
236}
237
238// URI handler
240
241#define gst_qrc_src_parent_class parent_class
243 G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, qGstInitURIHandler));
244
245// implementations
246
248{
249 // GObject
250 GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
251 gobjectClass->set_property = [](GObject *instance, guint propId, const GValue *value,
252 GParamSpec *pspec) {
253 QGstQrcSrc *src = asQGstQrcSrc(instance);
254 return src->setProperty(propId, value, pspec);
255 };
256
257 gobjectClass->get_property = [](GObject *instance, guint propId, GValue *value,
258 GParamSpec *pspec) {
259 QGstQrcSrc *src = asQGstQrcSrc(instance);
260 return src->getProperty(propId, value, pspec);
261 };
262
263 g_object_class_install_property(
264 gobjectClass, PROP_URI,
265 g_param_spec_string("uri", "QRC Location", "Path of the qrc to read", nullptr,
266 GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
267 | GST_PARAM_MUTABLE_READY)));
268
269 gobjectClass->finalize = [](GObject *instance) {
270 QGstQrcSrc *src = asQGstQrcSrc(instance);
271 src->file.~QFile();
272 G_OBJECT_CLASS(parent_class)->finalize(instance);
273 };
274
275 // GstElement
276 GstElementClass *gstelementClass = GST_ELEMENT_CLASS(klass);
277 gst_element_class_set_static_metadata(gstelementClass, "QRC Source", "Source/QRC",
278 "Read from arbitrary point in QRC resource",
279 "Tim Blechmann <tim.blechmann@qt.io>");
280
281 static GstStaticPadTemplate srctemplate =
282 GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
283
284 gst_element_class_add_static_pad_template(gstelementClass, &srctemplate);
285
286 // GstBaseSrc
287 GstBaseSrcClass *gstbasesrcClass = GST_BASE_SRC_CLASS(klass);
288 gstbasesrcClass->start = [](GstBaseSrc *basesrc) -> gboolean {
289 QGstQrcSrc *src = asQGstQrcSrc(basesrc);
290 return src->start();
291 };
292 gstbasesrcClass->stop = [](GstBaseSrc *basesrc) -> gboolean {
293 QGstQrcSrc *src = asQGstQrcSrc(basesrc);
294 return src->stop();
295 };
296
297 gstbasesrcClass->is_seekable = [](GstBaseSrc *) -> gboolean {
298 return true;
299 };
300 gstbasesrcClass->get_size = [](GstBaseSrc *basesrc, guint64 *size) -> gboolean {
301 QGstQrcSrc *src = asQGstQrcSrc(basesrc);
302 auto optionalSize = src->size();
303 if (!optionalSize)
304 return false;
305 *size = optionalSize.value();
306 return true;
307 };
308 gstbasesrcClass->fill = [](GstBaseSrc *basesrc, guint64 offset, guint length,
309 GstBuffer *buf) -> GstFlowReturn {
310 QGstQrcSrc *src = asQGstQrcSrc(basesrc);
311 return src->fill(offset, length, buf);
312 };
313}
314
316{
317 new (reinterpret_cast<void *>(&self->file)) QFile;
318
319 static constexpr guint defaultBlockSize = 16384;
320 gst_base_src_set_blocksize(&self->baseSrc, defaultBlockSize);
321}
322
323void qGstInitURIHandler(gpointer g_handlerInterface, gpointer)
324{
325 GstURIHandlerInterface *iface = (GstURIHandlerInterface *)g_handlerInterface;
326
327 iface->get_type = [](GType) {
328 return GST_URI_SRC;
329 };
330 iface->get_protocols = [](GType) {
331 static constexpr const gchar *protocols[] = {
332 "qrc",
333 nullptr,
334 };
335 return protocols;
336 };
337 iface->get_uri = [](GstURIHandler *handler) -> gchar * {
338 QGstQrcSrc *src = asQGstQrcSrc(handler);
339 auto lock = src->lockObject();
340 std::optional<QUrl> url = qQrcPathToQUrl(src->file.fileName());
341 if (url)
342 return g_strdup(url->toString().toUtf8().constData());
343
344 return nullptr;
345 };
346 iface->set_uri = [](GstURIHandler *handler, const gchar *uri, GError **err) -> gboolean {
347 QGstQrcSrc *src = asQGstQrcSrc(handler);
348 return src->setURI(uri, err);
349 };
350}
351
352} // namespace
353
354// plugin registration
355
356void qGstRegisterQRCHandler(GstPlugin *plugin)
357{
358 gst_element_register(plugin, "qrcsrc", GST_RANK_PRIMARY, gst_qrc_src_get_type());
359}
360
361QT_END_NAMESPACE
Combined button and popup list for selecting options.
static void gst_qrc_src_init(QGstQrcSrc *self)
static void gst_qrc_src_class_init(QGstQrcSrcClass *klass)
G_DEFINE_TYPE_WITH_CODE(QGstQrcSrc, gst_qrc_src, GST_TYPE_BASE_SRC, G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, qGstInitURIHandler))
std::optional< QString > qQUrlToQrcPath(const QUrl &url)
void qGstInitURIHandler(gpointer, gpointer)
GType gst_qrc_src_get_type()
std::optional< QUrl > qQrcPathToQUrl(QStringView path)
QGstQrcSrc * asQGstQrcSrc(T *obj)
void qGstRegisterQRCHandler(GstPlugin *plugin)
GstFlowReturn fill(guint64 offset, guint length, GstBuffer *buf)
bool setURI(const char *location, GError **err=nullptr)
void setProperty(guint propId, const GValue *value, const GParamSpec *pspec)
void getProperty(guint propId, GValue *value, const GParamSpec *pspec) const