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