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
qgst.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
4#include <common/qgst_p.h>
5
6#include <common/qgst_debug_p.h>
7#include <common/qgstpipeline_p.h>
8#include <common/qgstreamermessage_p.h>
9#include <common/qgstutils_p.h>
10#include <common/qgstvideobuffer_p.h>
11
12#include <QtCore/qdebug.h>
13#include <QtMultimedia/private/qvideoframe_p.h>
14#include <QtMultimedia/qcameradevice.h>
15
16#if QT_CONFIG(gstreamer_gl)
17# include <gst/gl/gstglmemory.h>
18#endif
19
20#include <array>
21#include <thread>
22
23// DMA support
24#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
25# include <gst/allocators/gstdmabuf.h>
26#endif
27
28QT_BEGIN_NAMESPACE
29
30namespace {
31
37
38constexpr std::array<VideoFormat, 19> qt_videoFormatLookup{ {
39 { QVideoFrameFormat::Format_YUV420P, GST_VIDEO_FORMAT_I420 },
40 { QVideoFrameFormat::Format_YUV422P, GST_VIDEO_FORMAT_Y42B },
41 { QVideoFrameFormat::Format_YV12, GST_VIDEO_FORMAT_YV12 },
42 { QVideoFrameFormat::Format_UYVY, GST_VIDEO_FORMAT_UYVY },
43 { QVideoFrameFormat::Format_YUYV, GST_VIDEO_FORMAT_YUY2 },
44 { QVideoFrameFormat::Format_NV12, GST_VIDEO_FORMAT_NV12 },
45 { QVideoFrameFormat::Format_NV21, GST_VIDEO_FORMAT_NV21 },
46 { QVideoFrameFormat::Format_AYUV, GST_VIDEO_FORMAT_AYUV },
47 { QVideoFrameFormat::Format_Y8, GST_VIDEO_FORMAT_GRAY8 },
48 { QVideoFrameFormat::Format_XRGB8888, GST_VIDEO_FORMAT_xRGB },
49 { QVideoFrameFormat::Format_XBGR8888, GST_VIDEO_FORMAT_xBGR },
50 { QVideoFrameFormat::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx },
51 { QVideoFrameFormat::Format_BGRX8888, GST_VIDEO_FORMAT_BGRx },
52 { QVideoFrameFormat::Format_ARGB8888, GST_VIDEO_FORMAT_ARGB },
53 { QVideoFrameFormat::Format_ABGR8888, GST_VIDEO_FORMAT_ABGR },
54 { QVideoFrameFormat::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA },
55 { QVideoFrameFormat::Format_BGRA8888, GST_VIDEO_FORMAT_BGRA },
56#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
57 { QVideoFrameFormat::Format_Y16, GST_VIDEO_FORMAT_GRAY16_LE },
58 { QVideoFrameFormat::Format_P010, GST_VIDEO_FORMAT_P010_10LE },
59#else
60 { QVideoFrameFormat::Format_Y16, GST_VIDEO_FORMAT_GRAY16_BE },
61 { QVideoFrameFormat::Format_P010, GST_VIDEO_FORMAT_P010_10BE },
62#endif
63} };
64
66void appendDmaDrmPixelFormats(QGstCaps &caps, const QList<QVideoFrameFormat::PixelFormat> &formats)
67{
68 GValue drmFormatList = {};
69 g_value_init(&drmFormatList, GST_TYPE_LIST);
70
71 for (QVideoFrameFormat::PixelFormat format : formats) {
72 const GstVideoFormat gstFormat = qGstVideoFormatFromPixelFormat(format);
73 if (gstFormat == GST_VIDEO_FORMAT_UNKNOWN)
74 continue;
75
76 const guint32 fourcc =
77 gst_video_dma_drm_fourcc_from_format(gstFormat);
78 if (!fourcc)
79 continue;
80
81 GValue drmFormat = {};
82 g_value_init(&drmFormat, G_TYPE_STRING);
83 g_value_take_string(&drmFormat, gst_video_dma_drm_fourcc_to_string(fourcc, 0));
84 gst_value_list_append_value(&drmFormatList, &drmFormat);
85 g_value_unset(&drmFormat);
86 }
87
88 auto *dmaDrmStructure =
89 gst_structure_new("video/x-raw", "format", G_TYPE_STRING,
90 gst_video_format_to_string(GST_VIDEO_FORMAT_DMA_DRM),
91 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
92 "width", GST_TYPE_INT_RANGE, 1, INT_MAX,
93 "height", GST_TYPE_INT_RANGE, 1, INT_MAX, nullptr);
94 gst_structure_set_value(dmaDrmStructure, "drm-format", &drmFormatList);
95 gst_caps_append_structure(caps.get(), dmaDrmStructure);
96 gst_caps_set_features(caps.get(), caps.size() - 1,
97 gst_caps_features_from_string(GST_CAPS_FEATURE_MEMORY_DMABUF));
98 g_value_unset(&drmFormatList);
99}
100#endif
101
102} // namespace
103
104GstVideoFormat qGstVideoFormatFromPixelFormat(QVideoFrameFormat::PixelFormat format)
105{
106 for (const VideoFormat &formatPair : qt_videoFormatLookup)
107 if (formatPair.pixelFormat == format)
108 return formatPair.gstFormat;
109
110 return GST_VIDEO_FORMAT_UNKNOWN;
111}
112
114{
115 for (const VideoFormat &formatPair : qt_videoFormatLookup)
116 if (formatPair.gstFormat == format)
117 return formatPair.pixelFormat;
118
119 return QVideoFrameFormat::Format_Invalid;
120}
121
122// QGValue
123
124QGValue::QGValue(const GValue *v) : value(v) { }
125
126bool QGValue::isNull() const
127{
128 return !value;
129}
130
131std::optional<bool> QGValue::toBool() const
132{
133 if (!G_VALUE_HOLDS_BOOLEAN(value))
134 return std::nullopt;
135 return g_value_get_boolean(value);
136}
137
138std::optional<int> QGValue::toInt() const
139{
140 if (!G_VALUE_HOLDS_INT(value))
141 return std::nullopt;
142 return g_value_get_int(value);
143}
144
145std::optional<int> QGValue::toInt64() const
146{
147 if (!G_VALUE_HOLDS_INT64(value))
148 return std::nullopt;
149 return g_value_get_int64(value);
150}
151
152const char *QGValue::toString() const
153{
154 return value ? g_value_get_string(value) : nullptr;
155}
156
157std::optional<float> QGValue::getFraction() const
158{
159 if (!GST_VALUE_HOLDS_FRACTION(value))
160 return std::nullopt;
161 return (float)gst_value_get_fraction_numerator(value)
162 / (float)gst_value_get_fraction_denominator(value);
163}
164
165std::optional<QGRange<float>> QGValue::getFractionRange() const
166{
167 if (!GST_VALUE_HOLDS_FRACTION_RANGE(value))
168 return std::nullopt;
169 QGValue min = QGValue{ gst_value_get_fraction_range_min(value) };
170 QGValue max = QGValue{ gst_value_get_fraction_range_max(value) };
171 return QGRange<float>{ *min.getFraction(), *max.getFraction() };
172}
173
174std::optional<QGRange<int>> QGValue::toIntRange() const
175{
176 if (!GST_VALUE_HOLDS_INT_RANGE(value))
177 return std::nullopt;
178 return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) };
179}
180
182{
183 if (!value || !GST_VALUE_HOLDS_STRUCTURE(value))
184 return QGstStructureView(nullptr);
185 return QGstStructureView(gst_value_get_structure(value));
186}
187
189{
190 if (!value || !GST_VALUE_HOLDS_CAPS(value))
191 return {};
192 return QGstCaps(gst_caps_copy(gst_value_get_caps(value)), QGstCaps::HasRef);
193}
194
195bool QGValue::isList() const
196{
197 return value && GST_VALUE_HOLDS_LIST(value);
198}
199
200int QGValue::listSize() const
201{
202 return gst_value_list_get_size(value);
203}
204
205QGValue QGValue::at(int index) const
206{
207 return QGValue{ gst_value_list_get_value(value, index) };
208}
209
210// QGstStructureView
211
212QGstStructureView::QGstStructureView(const GstStructure *s) : structure(s) { }
213
214QGstStructureView::QGstStructureView(const QUniqueGstStructureHandle &handle)
215 : QGstStructureView{ handle.get() }
216{
217}
218
220{
221 return QUniqueGstStructureHandle{ gst_structure_copy(structure) };
222}
223
225{
226 return !structure;
227}
228
230{
231 return gst_structure_get_name(structure);
232}
233
234QGValue QGstStructureView::operator[](const char *fieldname) const
235{
236 return QGValue{ gst_structure_get_value(structure, fieldname) };
237}
238
240{
241 return operator[]("caps").toCaps();
242}
243
245{
246 QGValue tags = operator[]("tags");
247 if (tags.isNull())
248 return {};
249
250 QGstTagListHandle tagList;
251 gst_structure_get(structure, "tags", GST_TYPE_TAG_LIST, &tagList, nullptr);
252 return tagList;
253}
254
256{
257 QSize size;
258
259 int w, h;
260 if (structure && gst_structure_get_int(structure, "width", &w)
261 && gst_structure_get_int(structure, "height", &h)) {
262 size.rwidth() = w;
263 size.rheight() = h;
264 }
265
266 return size;
267}
268
270{
271 QList<QVideoFrameFormat::PixelFormat> pixelFormats;
272
273 if (!structure)
274 return pixelFormats;
275
276 auto appendFromGstVideoFormat = [&](GstVideoFormat format) {
277 const QVideoFrameFormat::PixelFormat pixelFormat = qPixelFormatFromGstVideoFormat(format);
278 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
279 return;
280
281 if (!pixelFormats.contains(pixelFormat))
282 pixelFormats.append(pixelFormat);
283 };
284
285 if (gst_structure_has_name(structure, "video/x-raw")) {
287 if (const GValue *drmFormatValue = gst_structure_get_value(structure, "drm-format")) {
288 auto parseDrmFormat = [&](const GValue *value) {
289 if (!value || !G_VALUE_HOLDS_STRING(value))
290 return;
291
292 const char *drmFormatString = g_value_get_string(value);
293 if (!drmFormatString)
294 return;
295
296 guint64 modifier = 0;
297 const guint32 fourcc = gst_video_dma_drm_fourcc_from_string(drmFormatString,
298 &modifier);
299 if (fourcc) {
300 if (modifier != 0)
301 qWarning() << "Ignoring drm-format modifier when mapping to Qt PixelFormat:"
302 << drmFormatString;
303 appendFromGstVideoFormat(gst_video_dma_drm_fourcc_to_format(fourcc));
304 }
305 };
306
307 if (GST_VALUE_HOLDS_LIST(drmFormatValue)) {
308 const guint listSize = gst_value_list_get_size(drmFormatValue);
309 for (guint i = 0; i < listSize; ++i)
310 parseDrmFormat(gst_value_list_get_value(drmFormatValue, i));
311 } else {
312 parseDrmFormat(drmFormatValue);
313 }
314
315 if (!pixelFormats.isEmpty())
316 return pixelFormats; // Skip checking "format" field when "drm-format" succeeds
317 }
318#endif
319 if (const GValue *formatValue = gst_structure_get_value(structure, "format")) {
320 auto parseFormat = [&](const GValue *value) {
321 if (!value || !G_VALUE_HOLDS_STRING(value))
322 return;
323 const char *formatString = g_value_get_string(value);
324 if (!formatString)
325 return;
326 appendFromGstVideoFormat(gst_video_format_from_string(formatString));
327 };
328
329 if (GST_VALUE_HOLDS_LIST(formatValue)) {
330 const guint listSize = gst_value_list_get_size(formatValue);
331 for (guint i = 0; i < listSize; ++i)
332 parseFormat(gst_value_list_get_value(formatValue, i));
333 } else {
334 parseFormat(formatValue);
335 }
336 }
337 } else if (gst_structure_has_name(structure, "image/jpeg")) {
338 pixelFormats.append(QVideoFrameFormat::Format_Jpeg);
339 }
340
341 return pixelFormats;
342}
343
345{
346 if (!structure)
347 return { 0.f, 0.f };
348
349 std::optional<float> minRate;
350 std::optional<float> maxRate;
351
352 auto extractFraction = [](const GValue *v) -> float {
353 return (float)gst_value_get_fraction_numerator(v)
354 / (float)gst_value_get_fraction_denominator(v);
355 };
356 auto extractFrameRate = [&](const GValue *v) {
357 auto insert = [&](float min, float max) {
358 if (!maxRate || max > maxRate)
359 maxRate = max;
360 if (!minRate || min < minRate)
361 minRate = min;
362 };
363
364 if (GST_VALUE_HOLDS_FRACTION(v)) {
365 float rate = extractFraction(v);
366 insert(rate, rate);
367 } else if (GST_VALUE_HOLDS_FRACTION_RANGE(v)) {
368 const GValue *min = gst_value_get_fraction_range_min(v);
369 const GValue *max = gst_value_get_fraction_range_max(v);
370 insert(extractFraction(min), extractFraction(max));
371 }
372 };
373
374 const GValue *gstFrameRates = gst_structure_get_value(structure, "framerate");
375 if (gstFrameRates) {
376 if (GST_VALUE_HOLDS_LIST(gstFrameRates)) {
377 guint nFrameRates = gst_value_list_get_size(gstFrameRates);
378 for (guint f = 0; f < nFrameRates; ++f) {
379 extractFrameRate(gst_value_list_get_value(gstFrameRates, f));
380 }
381 } else {
382 extractFrameRate(gstFrameRates);
383 }
384 } else {
385 const GValue *min = gst_structure_get_value(structure, "min-framerate");
386 const GValue *max = gst_structure_get_value(structure, "max-framerate");
387 if (min && max) {
388 minRate = extractFraction(min);
389 maxRate = extractFraction(max);
390 }
391 }
392
393 if (!minRate || !maxRate)
394 return { 0.f, 0.f };
395
396 return {
397 minRate.value_or(*maxRate),
398 maxRate.value_or(*minRate),
399 };
400}
401
403{
404 if (!structure)
405 return std::nullopt;
406
407 const GValue *width = gst_structure_get_value(structure, "width");
408 const GValue *height = gst_structure_get_value(structure, "height");
409
410 if (!width || !height)
411 return std::nullopt;
412
413 for (const GValue *v : { width, height })
414 if (!GST_VALUE_HOLDS_INT_RANGE(v))
415 return std::nullopt;
416
417 int minWidth = gst_value_get_int_range_min(width);
418 int maxWidth = gst_value_get_int_range_max(width);
419 int minHeight = gst_value_get_int_range_min(height);
420 int maxHeight = gst_value_get_int_range_max(height);
421
422 return QGRange<QSize>{
423 QSize(minWidth, minHeight),
424 QSize(maxWidth, maxHeight),
425 };
426}
427
429{
430 GstMessage *message = nullptr;
431 gst_structure_get(structure, "message", GST_TYPE_MESSAGE, &message, nullptr);
432 return QGstreamerMessage(message, QGstreamerMessage::HasRef);
433}
434
436{
437 gint numerator;
438 gint denominator;
439 if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &numerator, &denominator)) {
440 return Fraction{
441 numerator,
442 denominator,
443 };
444 }
445
446 return std::nullopt;
447}
448
450{
451 QSize size = resolution();
452 if (!size.isValid()) {
453 qWarning() << Q_FUNC_INFO << "invalid resolution when querying nativeSize";
454 return size;
455 }
456
457 std::optional<Fraction> par = pixelAspectRatio();
458 if (par)
460 return size;
461}
462
463// QGstCaps
464
466{
467 GstVideoInfo vidInfo;
468 std::optional<guint64> dmaDrmModifier;
469
471 GstVideoInfoDmaDrm videoInfoDmaDrm;
472 if (gst_video_info_dma_drm_from_caps(&videoInfoDmaDrm, get())) {
473 dmaDrmModifier = videoInfoDmaDrm.drm_modifier;
474 // Sets vidInfo‘s format based on drm_fourcc from videoInfoDmaDrm:
475 if (!gst_video_info_dma_drm_to_video_info(&videoInfoDmaDrm, &vidInfo))
476 return std::nullopt;
477 } else
478#endif
479 {
480 if (!gst_video_info_from_caps(&vidInfo, get()))
481 return std::nullopt;
482 }
483
484 return QGstVideoInfo{vidInfo, dmaDrmModifier};
485}
486
487void QGstCaps::addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats,
488 const char *capsFeatures)
489{
490 if (!gst_caps_is_writable(get()))
491 *this = QGstCaps(gst_caps_make_writable(release()), QGstCaps::RefMode::HasRef);
492
494 if (qstrcmp(capsFeatures, GST_CAPS_FEATURE_MEMORY_DMABUF) == 0)
495 appendDmaDrmPixelFormats(*this, formats);
496#endif
497
498 GValue list = {};
499 g_value_init(&list, GST_TYPE_LIST);
500
501 for (QVideoFrameFormat::PixelFormat format : formats) {
502 const GstVideoFormat gstFormat = qGstVideoFormatFromPixelFormat(format);
503 if (gstFormat == GST_VIDEO_FORMAT_UNKNOWN)
504 continue;
505 GValue item = {};
506
507 g_value_init(&item, G_TYPE_STRING);
508 g_value_set_string(&item, gst_video_format_to_string(gstFormat));
509 gst_value_list_append_value(&list, &item);
510 g_value_unset(&item);
511 }
512
513 auto *structure = gst_structure_new("video/x-raw", "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
514 INT_MAX, 1, "width", GST_TYPE_INT_RANGE, 1, INT_MAX,
515 "height", GST_TYPE_INT_RANGE, 1, INT_MAX, nullptr);
516 gst_structure_set_value(structure, "format", &list);
517 gst_caps_append_structure(get(), structure);
518 g_value_unset(&list);
519
520 if (capsFeatures)
521 gst_caps_set_features(get(), size() - 1,
522 gst_caps_features_from_string(capsFeatures));
523}
524
525void QGstCaps::setResolution(QSize resolution)
526{
527 Q_ASSERT(resolution.isValid());
528 GValue width{};
529 g_value_init(&width, G_TYPE_INT);
530 g_value_set_int(&width, resolution.width());
531 GValue height{};
532 g_value_init(&height, G_TYPE_INT);
533 g_value_set_int(&height, resolution.height());
534
535 gst_caps_set_value(caps(), "width", &width);
536 gst_caps_set_value(caps(), "height", &height);
537}
538
539QGstCaps QGstCaps::fromCameraFormat(const QCameraFormat &format)
540{
541 QSize size = format.resolution();
542 auto caps = QGstCaps::create();
543
544 if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
545 auto *jpegStructure = gst_structure_new("image/jpeg",
546 "width", G_TYPE_INT, size.width(),
547 "height", G_TYPE_INT, size.height(), nullptr);
548 gst_caps_append_structure(caps.get(), jpegStructure);
549 return caps;
550 }
551
552 const GstVideoFormat gstFormat = qGstVideoFormatFromPixelFormat(format.pixelFormat());
553 if (gstFormat == GST_VIDEO_FORMAT_UNKNOWN)
554 return {};
555
556 auto *rawStructure =
557 gst_structure_new("video/x-raw",
558 "format", G_TYPE_STRING, gst_video_format_to_string(gstFormat),
559 "width", G_TYPE_INT, size.width(),
560 "height", G_TYPE_INT, size.height(), nullptr);
561 gst_caps_append_structure(caps.get(), rawStructure);
562
564 if (const guint32 fourcc = gst_video_dma_drm_fourcc_from_format(gstFormat)) {
565 if (QGString drmFormat{gst_video_dma_drm_fourcc_to_string(fourcc, 0)}) {
566 auto *drmFormatDmabufRawStructure =
567 gst_structure_new("video/x-raw", "format", G_TYPE_STRING,
568 gst_video_format_to_string(GST_VIDEO_FORMAT_DMA_DRM),
569 "drm-format", G_TYPE_STRING, drmFormat.get(),
570 "width", G_TYPE_INT, size.width(),
571 "height", G_TYPE_INT, size.height(), nullptr);
572 gst_caps_append_structure(caps.get(), drmFormatDmabufRawStructure);
573 gst_caps_set_features(
574 caps.get(), caps.size() - 1,
575 gst_caps_features_from_string(GST_CAPS_FEATURE_MEMORY_DMABUF));
576 }
577 }
578#endif
579
580#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
581 auto *dmabufRawStructure =
582 gst_structure_new("video/x-raw",
583 "format", G_TYPE_STRING, gst_video_format_to_string(gstFormat),
584 "width", G_TYPE_INT, size.width(),
585 "height", G_TYPE_INT, size.height(), nullptr);
586 gst_caps_append_structure(caps.get(), dmabufRawStructure);
587 gst_caps_set_features(caps.get(), caps.size() - 1,
588 gst_caps_features_from_string(GST_CAPS_FEATURE_MEMORY_DMABUF));
589#endif
590
591 return caps;
592}
593
595{
596 return QGstCaps{
597 gst_caps_copy(caps()),
598 QGstCaps::HasRef,
599 };
600}
601
603{
604 auto *features = gst_caps_get_features(get(), 0);
605 if (gst_caps_features_contains(features, "memory:GLMemory"))
606 return GLTexture;
607 if (gst_caps_features_contains(features, "memory:DMABuf"))
608 return DMABuf;
609 return CpuMemory;
610}
611
612int QGstCaps::size() const
613{
614 return int(gst_caps_get_size(get()));
615}
616
618{
619 return QGstStructureView{
620 gst_caps_get_structure(get(), index),
621 };
622}
623
625{
626 return get();
627}
628
630{
631 return QGstCaps(gst_caps_new_empty(), HasRef);
632}
633
634// QGstObject
635
636void QGstObject::set(const char *property, const char *str)
637{
638 g_object_set(get(), property, str, nullptr);
639}
640
641void QGstObject::set(const char *property, bool b)
642{
643 g_object_set(get(), property, gboolean(b), nullptr);
644}
645
646void QGstObject::set(const char *property, uint32_t i)
647{
648 g_object_set(get(), property, guint(i), nullptr);
649}
650
651void QGstObject::set(const char *property, int32_t i)
652{
653 g_object_set(get(), property, gint(i), nullptr);
654}
655
656void QGstObject::set(const char *property, int64_t i)
657{
658 g_object_set(get(), property, gint64(i), nullptr);
659}
660
661void QGstObject::set(const char *property, uint64_t i)
662{
663 g_object_set(get(), property, guint64(i), nullptr);
664}
665
666void QGstObject::set(const char *property, double d)
667{
668 g_object_set(get(), property, gdouble(d), nullptr);
669}
670
671void QGstObject::set(const char *property, const QGstObject &o)
672{
673 g_object_set(get(), property, o.object(), nullptr);
674}
675
676void QGstObject::set(const char *property, const QGstCaps &c)
677{
678 g_object_set(get(), property, c.caps(), nullptr);
679}
680
681void QGstObject::set(const char *property, void *object, GDestroyNotify destroyFunction)
682{
683 g_object_set_data_full(qGstCheckedCast<GObject>(get()), property, object, destroyFunction);
684}
685
686QGString QGstObject::getString(const char *property) const
687{
688 char *s = nullptr;
689 g_object_get(get(), property, &s, nullptr);
690 return QGString(s);
691}
692
693QGstStructureView QGstObject::getStructure(const char *property) const
694{
695 GstStructure *s = nullptr;
696 g_object_get(get(), property, &s, nullptr);
697 return QGstStructureView(s);
698}
699
700bool QGstObject::getBool(const char *property) const
701{
702 gboolean b = false;
703 g_object_get(get(), property, &b, nullptr);
704 return b;
705}
706
707uint QGstObject::getUInt(const char *property) const
708{
709 guint i = 0;
710 g_object_get(get(), property, &i, nullptr);
711 return i;
712}
713
714int QGstObject::getInt(const char *property) const
715{
716 gint i = 0;
717 g_object_get(get(), property, &i, nullptr);
718 return i;
719}
720
721quint64 QGstObject::getUInt64(const char *property) const
722{
723 guint64 i = 0;
724 g_object_get(get(), property, &i, nullptr);
725 return i;
726}
727
728qint64 QGstObject::getInt64(const char *property) const
729{
730 gint64 i = 0;
731 g_object_get(get(), property, &i, nullptr);
732 return i;
733}
734
735float QGstObject::getFloat(const char *property) const
736{
737 gfloat d = 0;
738 g_object_get(get(), property, &d, nullptr);
739 return d;
740}
741
742double QGstObject::getDouble(const char *property) const
743{
744 gdouble d = 0;
745 g_object_get(get(), property, &d, nullptr);
746 return d;
747}
748
749QGstObject QGstObject::getGstObject(const char *property) const
750{
751 GstObject *o = nullptr;
752 g_object_get(get(), property, &o, nullptr);
753 return QGstObject(o, HasRef);
754}
755
756void *QGstObject::getObject(const char *property) const
757{
758 return g_object_get_data(qGstCheckedCast<GObject>(get()), property);
759}
760
761QGObjectHandlerConnection QGstObject::connect(const char *name, GCallback callback,
762 gpointer userData)
763{
765 *this,
766 g_signal_connect(get(), name, callback, userData),
767 };
768}
769
770void QGstObject::disconnect(gulong handlerId)
771{
772 g_signal_handler_disconnect(get(), handlerId);
773}
774
776{
777 return G_OBJECT_TYPE(get());
778}
779
781{
782 return QLatin1StringView{
783 g_type_name(type()),
784 };
785}
786
788{
789 return get();
790}
791
793{
794 using namespace Qt::StringLiterals;
795
796 return get() ? QLatin1StringView{ GST_OBJECT_NAME(get()) } : "(null)"_L1;
797}
798
799// QGObjectHandlerConnection
800
802 : object{ std::move(object) }, handlerId{ handlerId }
803{
804}
805
807{
808 if (!object)
809 return;
810
811 object.disconnect(handlerId);
812 object = {};
813 handlerId = invalidHandlerId;
814}
815
816// QGObjectHandlerScopedConnection
817
825
827{
828 connection.disconnect();
829}
830
832{
833 connection.disconnect();
834}
835
836// QGstPad
837
839 : QGstPad{
842 }
843{
844}
845
846QGstPad::QGstPad(GstPad *pad, RefMode mode)
847 : QGstObject{
849 mode,
850 }
851{
852}
853
855{
856 return QGstCaps(gst_pad_get_current_caps(pad()), QGstCaps::HasRef);
857}
858
860{
861 return QGstCaps(gst_pad_query_caps(pad(), nullptr), QGstCaps::HasRef);
862}
863
865{
866 QGstTagListHandle tagList;
867 g_object_get(object(), "tags", &tagList, nullptr);
868 return tagList;
869}
870
872{
873 return QGString{
874 gst_pad_get_stream_id(pad()),
875 };
876}
877
879{
880 using namespace Qt::Literals;
881 QLatin1StringView padName = name();
882
883 if (padName.startsWith("video_"_L1))
884 return QPlatformMediaPlayer::TrackType::VideoStream;
885 if (padName.startsWith("audio_"_L1))
886 return QPlatformMediaPlayer::TrackType::AudioStream;
887 if (padName.startsWith("text_"_L1))
888 return QPlatformMediaPlayer::TrackType::SubtitleStream;
889
890 return std::nullopt;
891}
892
893bool QGstPad::isLinked() const
894{
895 return gst_pad_is_linked(pad());
896}
897
898bool QGstPad::link(const QGstPad &sink) const
899{
900 return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK;
902
903bool QGstPad::unlink(const QGstPad &sink) const
904{
905 return gst_pad_unlink(pad(), sink.pad());
906}
907
908bool QGstPad::unlinkPeer() const
909{
910 QGstPad peerPad = peer();
911 if (peerPad)
912 return GST_PAD_IS_SRC(pad()) ? unlink(peerPad) : peerPad.unlink(*this);
913
914 return true;
915}
916
918{
919 return QGstPad(gst_pad_get_peer(pad()), HasRef);
920}
921
923{
924 return QGstElement(gst_pad_get_parent_element(pad()), HasRef);
925}
926
928{
929 return qGstCheckedCast<GstPad>(object());
930}
931
932GstEvent *QGstPad::stickyEvent(GstEventType type)
933{
934 return gst_pad_get_sticky_event(pad(), type, 0);
935}
936
937bool QGstPad::sendEvent(GstEvent *event)
938{
939 return gst_pad_send_event(pad(), event);
940}
941
942void QGstPad::sendFlushStartStop(bool resetTime)
943{
944 GstEvent *flushStart = gst_event_new_flush_start();
945 gboolean ret = sendEvent(flushStart);
946 if (!ret) {
947 qWarning("failed to send flush-start event");
948 return;
949 }
950
951 GstEvent *flushStop = gst_event_new_flush_stop(resetTime);
952 ret = sendEvent(flushStop);
953 if (!ret)
954 qWarning("failed to send flush-stop event");
955}
956
958{
959 using namespace std::chrono_literals;
960
961 GstState state = parent().state(1s);
962
963 if (state != GST_STATE_PAUSED)
964 return;
965
966 sendFlushStartStop(/*resetTime=*/true);
967}
968
969// QGstClock
970
972 : QGstClock{
975 }
976{
977}
978
979QGstClock::QGstClock(GstClock *clock, RefMode mode)
980 : QGstObject{
982 mode,
983 }
984{
985}
986
988{
989 return qGstCheckedCast<GstClock>(object());
990}
991
993{
994 return gst_clock_get_time(clock());
995}
996
997// QGstElement
998
999QGstElement::QGstElement(GstElement *element, RefMode mode)
1000 : QGstObject{
1002 mode,
1003 }
1004{
1005}
1006
1007QGstElement QGstElement::createFromFactory(const char *factory, const char *name)
1008{
1009 GstElement *element = gst_element_factory_make(factory, name);
1010
1011#ifndef QT_NO_DEBUG
1012 if (!element) {
1013 qWarning() << "Failed to make element" << name << "from factory" << factory;
1014 return QGstElement{};
1015 }
1016#endif
1017
1018 return QGstElement{
1019 element,
1020 NeedsRef,
1021 };
1022}
1023
1024QGstElement QGstElement::createFromFactory(GstElementFactory *factory, const char *name)
1025{
1026 return QGstElement{
1027 gst_element_factory_create(factory, name),
1028 NeedsRef,
1029 };
1030}
1031
1032QGstElement QGstElement::createFromFactory(const QGstElementFactoryHandle &factory,
1033 const char *name)
1034{
1035 return createFromFactory(factory.get(), name);
1036}
1037
1038QGstElement QGstElement::createFromDevice(const QGstDeviceHandle &device, const char *name)
1039{
1040 return createFromDevice(device.get(), name);
1041}
1042
1043QGstElement QGstElement::createFromDevice(GstDevice *device, const char *name)
1044{
1045 return QGstElement{
1046 gst_device_create_element(device, name),
1047 QGstElement::NeedsRef,
1048 };
1049}
1050
1052{
1053 QUniqueGErrorHandle error;
1054 QGstElement element{
1055 gst_parse_launch(str, &error),
1056 QGstElement::NeedsRef,
1057 };
1058
1059 if (error) // error does not mean that the element could not be constructed
1060 qWarning() << "gst_parse_launch error:" << error;
1061
1062 return element;
1063}
1064
1065QGstElement QGstElement::createFromPipelineDescription(const QByteArray &str)
1066{
1067 return createFromPipelineDescription(str.constData());
1068}
1069
1070QGstElementFactoryHandle QGstElement::findFactory(const char *name)
1071{
1072 return QGstElementFactoryHandle{
1073 gst_element_factory_find(name),
1074 QGstElementFactoryHandle::HasRef,
1075 };
1076}
1077
1078QGstElementFactoryHandle QGstElement::findFactory(const QByteArray &name)
1079{
1080 return findFactory(name.constData());
1081}
1082
1084{
1085 GstElementFactory *factory = gst_element_get_factory(element());
1086 if (!factory)
1087 return {};
1088
1089 const char *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
1090 return name ? QByteArrayView{name} : QByteArrayView{};
1091}
1092
1093QGstPad QGstElement::staticPad(const char *name) const
1094{
1095 return QGstPad(gst_element_get_static_pad(element(), name), HasRef);
1096}
1097
1099{
1100 return staticPad("src");
1101}
1102
1104{
1105 return staticPad("sink");
1106}
1107
1108QGstPad QGstElement::getRequestPad(const char *name) const
1109{
1110 return QGstPad(gst_element_request_pad_simple(element(), name), HasRef);
1111}
1112
1114{
1115 return gst_element_release_request_pad(element(), pad.pad());
1116}
1117
1118GstState QGstElement::state(std::chrono::nanoseconds timeout) const
1119{
1120 using namespace std::chrono_literals;
1121
1122 GstState state;
1123 GstStateChangeReturn change =
1124 gst_element_get_state(element(), &state, nullptr, timeout.count());
1125
1126 if (Q_UNLIKELY(change == GST_STATE_CHANGE_ASYNC))
1127 qWarning() << "QGstElement::state detected an asynchronous state change. Return value not "
1128 "reliable";
1129
1130 return state;
1131}
1132
1134{
1135 return gst_element_set_state(element(), state);
1136}
1137
1138bool QGstElement::setStateSync(GstState state, std::chrono::nanoseconds timeout)
1139{
1140 GstStateChangeReturn change = gst_element_set_state(element(), state);
1141 if (change == GST_STATE_CHANGE_ASYNC)
1142 change = gst_element_get_state(element(), nullptr, &state, timeout.count());
1143
1144 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
1145 qWarning() << "Could not change state of" << name() << "to" << state << change;
1146 dumpPipelineGraph("setStateSyncFailure");
1147 }
1148 return change == GST_STATE_CHANGE_SUCCESS || change == GST_STATE_CHANGE_NO_PREROLL;
1149}
1150
1152{
1153 Q_ASSERT(element());
1154 return gst_element_sync_state_with_parent(element()) == TRUE;
1155}
1156
1157bool QGstElement::finishStateChange(std::chrono::nanoseconds timeout)
1158{
1159 GstState state, pending;
1160 GstStateChangeReturn change =
1161 gst_element_get_state(element(), &state, &pending, timeout.count());
1162
1163 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
1164 qWarning() << "Could not finish change state of" << name() << change << state << pending;
1165 dumpPipelineGraph("finishStateChangeFailure");
1166 }
1167 return change == GST_STATE_CHANGE_SUCCESS;
1168}
1169
1170bool QGstElement::hasAsyncStateChange(std::chrono::nanoseconds timeout) const
1171{
1172 GstState state;
1173 GstStateChangeReturn change =
1174 gst_element_get_state(element(), &state, nullptr, timeout.count());
1175 return change == GST_STATE_CHANGE_ASYNC;
1176}
1177
1178bool QGstElement::waitForAsyncStateChangeComplete(std::chrono::nanoseconds timeout) const
1179{
1180 using namespace std::chrono_literals;
1181 for (;;) {
1182 if (!hasAsyncStateChange())
1183 return true;
1184 timeout -= 10ms;
1185 if (timeout < 0ms)
1186 return false;
1187 std::this_thread::sleep_for(10ms);
1188 }
1189}
1190
1191void QGstElement::lockState(bool locked)
1192{
1193 gst_element_set_locked_state(element(), locked);
1194}
1195
1197{
1198 return gst_element_is_locked_state(element());
1199}
1200
1201void QGstElement::sendEvent(GstEvent *event) const
1202{
1203 gst_element_send_event(element(), event);
1204}
1205
1207{
1208 sendEvent(gst_event_new_eos());
1209}
1210
1211std::optional<std::chrono::nanoseconds> QGstElement::duration() const
1212{
1213 gint64 d;
1214 if (!gst_element_query_duration(element(), GST_FORMAT_TIME, &d)) {
1215 qDebug() << "QGstElement: failed to query duration";
1216 return std::nullopt;
1217 }
1218 return std::chrono::nanoseconds{ d };
1219}
1220
1221std::optional<std::chrono::milliseconds> QGstElement::durationInMs() const
1222{
1223 using namespace std::chrono;
1224 auto dur = duration();
1225 if (dur)
1226 return round<milliseconds>(*dur);
1227 return std::nullopt;
1228}
1229
1230std::optional<std::chrono::nanoseconds> QGstElement::position() const
1231{
1232 QGstQueryHandle &query = positionQuery();
1233
1234 gint64 pos;
1235 if (gst_element_query(element(), query.get())) {
1236 gst_query_parse_position(query.get(), nullptr, &pos);
1237 return std::chrono::nanoseconds{ pos };
1238 }
1239
1240 qDebug() << "QGstElement: failed to query position";
1241 return std::nullopt;
1242}
1243
1244std::optional<std::chrono::milliseconds> QGstElement::positionInMs() const
1245{
1246 using namespace std::chrono;
1247 auto pos = position();
1248 if (pos)
1249 return round<milliseconds>(*pos);
1250 return std::nullopt;
1251}
1252
1253std::optional<bool> QGstElement::canSeek() const
1254{
1255 QGstQueryHandle query{
1256 gst_query_new_seeking(GST_FORMAT_TIME),
1257 QGstQueryHandle::HasRef,
1258 };
1259 gboolean canSeek = false;
1260 gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
1261
1262 if (gst_element_query(element(), query.get())) {
1263 gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
1264 return canSeek;
1265 }
1266 return std::nullopt;
1267}
1268
1270{
1271 return gst_element_get_base_time(element());
1272}
1273
1274void QGstElement::setBaseTime(GstClockTime time) const
1275{
1276 gst_element_set_base_time(element(), time);
1277}
1278
1280{
1281 return GST_ELEMENT_CAST(get());
1282}
1283
1285{
1286 return QGstElement{
1287 qGstCheckedCast<GstElement>(gst_element_get_parent(object())),
1288 QGstElement::HasRef,
1289 };
1290}
1291
1293{
1294 return QGstBin{
1295 qGstCheckedCast<GstBin>(gst_element_get_parent(object())),
1296 QGstElement::HasRef,
1297 };
1298}
1299
1301{
1302 QGstElement ancestor = *this;
1303 for (;;) {
1304 QGstElement greatAncestor = ancestor.getParent();
1305 if (greatAncestor) {
1306 ancestor = std::move(greatAncestor);
1307 continue;
1308 }
1309 if (GST_IS_BIN(ancestor.element())) {
1310 return QGstBin{
1311 qGstSafeCast<GstBin>(ancestor.element()),
1312 QGstBin::NeedsRef,
1313 };
1314 } else {
1315 return QGstBin{};
1316 }
1317 }
1318}
1319
1321{
1322 QGstBin rootBin = getRootBin();
1323 if (GST_IS_PIPELINE(rootBin.get())) {
1324 return QGstPipeline{
1325 qGstSafeCast<GstPipeline>(rootBin.element()),
1326 QGstPipeline::NeedsRef,
1327 };
1328 } else {
1329 qWarning() << "QGstElement::getPipeline failed for element:" << *this;
1330 return QGstPipeline{};
1331 }
1332}
1333
1335{
1336 if (QGstBin parent = getParentBin())
1337 parent.remove(*this);
1338}
1339
1340void QGstElement::dumpPipelineGraph(const char *filename) const
1341{
1342 static const bool dumpEnabled = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR");
1343 if (dumpEnabled) {
1345 }
1346}
1347
1348QGstQueryHandle &QGstElement::positionQuery() const
1349{
1350 if (Q_UNLIKELY(!m_positionQuery))
1351 m_positionQuery = QGstQueryHandle{
1352 gst_query_new_position(GST_FORMAT_TIME),
1353 QGstQueryHandle::HasRef,
1354 };
1355
1356 return m_positionQuery;
1357}
1358
1359// QGstBin
1360
1361QGstBin QGstBin::create(const char *name)
1362{
1363 return QGstBin(gst_bin_new(name), NeedsRef);
1364}
1365
1366QGstBin QGstBin::createFromFactory(const char *factory, const char *name)
1367{
1368 QGstElement element = QGstElement::createFromFactory(factory, name);
1369 Q_ASSERT(GST_IS_BIN(element.element()));
1370 return QGstBin{
1371 GST_BIN(element.release()),
1372 RefMode::HasRef,
1373 };
1374}
1375
1376QGstBin QGstBin::createFromPipelineDescription(const QByteArray &pipelineDescription,
1377 const char *name, bool ghostUnlinkedPads)
1378{
1379 return createFromPipelineDescription(pipelineDescription.constData(), name, ghostUnlinkedPads);
1380}
1381
1382QGstBin QGstBin::createFromPipelineDescription(const char *pipelineDescription, const char *name,
1383 bool ghostUnlinkedPads)
1384{
1385 QUniqueGErrorHandle error;
1386
1387 GstElement *element =
1388 gst_parse_bin_from_description_full(pipelineDescription, ghostUnlinkedPads,
1389 /*context=*/nullptr, GST_PARSE_FLAG_NONE, &error);
1390
1391 if (!element) {
1392 qWarning() << "Failed to make element from pipeline description" << pipelineDescription
1393 << error;
1394 return QGstBin{};
1395 }
1396
1397 if (name)
1398 gst_element_set_name(element, name);
1399
1400 return QGstBin{
1401 element,
1402 NeedsRef,
1403 };
1404}
1405
1406QGstBin::QGstBin(GstBin *bin, RefMode mode)
1407 : QGstElement{
1409 mode,
1410 }
1411{
1412}
1413
1415{
1416 return qGstCheckedCast<GstBin>(object());
1417}
1418
1419void QGstBin::addGhostPad(const QGstElement &child, const char *name)
1420{
1421 addGhostPad(name, child.staticPad(name));
1422}
1423
1424void QGstBin::addGhostPad(const char *name, const QGstPad &pad)
1425{
1426 gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad()));
1427}
1428
1429void QGstBin::addUnlinkedGhostPads(GstPadDirection direction)
1430{
1431 Q_ASSERT(direction != GstPadDirection::GST_PAD_UNKNOWN);
1432
1433 for (;;) {
1434 QGstPad unlinkedPad{
1435 gst_bin_find_unlinked_pad(bin(), direction),
1436 QGstPad::HasRef,
1437 };
1438
1439 if (!unlinkedPad)
1440 return;
1441
1442 addGhostPad(unlinkedPad.name().constData(), unlinkedPad);
1443 }
1444}
1445
1447{
1448 return gst_bin_sync_children_states(bin());
1449}
1450
1451void QGstBin::dumpGraph(const char *fileNamePrefix, bool includeTimestamp) const
1452{
1453 if (!get())
1454 return;
1455
1456 if (includeTimestamp)
1457 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin(), GST_DEBUG_GRAPH_SHOW_VERBOSE, fileNamePrefix);
1458 else
1459 GST_DEBUG_BIN_TO_DOT_FILE(bin(), GST_DEBUG_GRAPH_SHOW_VERBOSE, fileNamePrefix);
1460}
1461
1463{
1464 return QGstElement{
1465 gst_bin_get_by_name(bin(), name),
1466 QGstElement::NeedsRef,
1467 };
1468}
1469
1471{
1472 gst_bin_recalculate_latency(bin());
1473}
1474
1475// QGstBaseSink
1476
1477QGstBaseSink::QGstBaseSink(GstBaseSink *element, RefMode mode)
1478 : QGstElement{
1480 mode,
1481 }
1482{
1483}
1484
1485void QGstBaseSink::setSync(bool arg)
1486{
1487 gst_base_sink_set_sync(baseSink(), arg ? TRUE : FALSE);
1488}
1489
1491{
1492 return qGstCheckedCast<GstBaseSink>(element());
1493}
1494
1495// QGstBaseSrc
1496
1497QGstBaseSrc::QGstBaseSrc(GstBaseSrc *element, RefMode mode)
1498 : QGstElement{
1500 mode,
1501 }
1502{
1503}
1504
1506{
1507 return qGstCheckedCast<GstBaseSrc>(element());
1508}
1509
1510// QGstAppSink
1511
1512QGstAppSink::QGstAppSink(GstAppSink *element, RefMode mode)
1513 : QGstBaseSink{
1515 mode,
1516 }
1517{
1518}
1519
1521{
1522 QGstElement created = QGstElement::createFromFactory("appsink", name);
1523 return QGstAppSink{
1524 qGstCheckedCast<GstAppSink>(created.element()),
1525 QGstAppSink::NeedsRef,
1526 };
1527}
1528
1530{
1531 return qGstCheckedCast<GstAppSink>(element());
1532}
1533
1534# if GST_CHECK_VERSION(1, 24, 0)
1535void QGstAppSink::setMaxBufferTime(std::chrono::nanoseconds ns)
1536{
1537 gst_app_sink_set_max_time(appSink(), qGstClockTimeFromChrono(ns));
1538}
1539# endif
1540
1542{
1543 gst_app_sink_set_max_buffers(appSink(), n);
1544}
1545
1546void QGstAppSink::setCaps(const QGstCaps &caps)
1547{
1548 gst_app_sink_set_caps(appSink(), caps.caps());
1549}
1550
1551void QGstAppSink::setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data,
1552 GDestroyNotify notify)
1553{
1554 gst_app_sink_set_callbacks(appSink(), &callbacks, user_data, notify);
1555}
1556
1558{
1559 return QGstSampleHandle{
1560 gst_app_sink_pull_sample(appSink()),
1561 QGstSampleHandle::HasRef,
1562 };
1563}
1564
1565// QGstAppSrc
1566
1567QGstAppSrc::QGstAppSrc(GstAppSrc *element, RefMode mode)
1568 : QGstBaseSrc{
1570 mode,
1571 }
1572{
1573}
1574
1575QGstAppSrc QGstAppSrc::create(const char *name)
1576{
1577 QGstElement created = QGstElement::createFromFactory("appsrc", name);
1578 return QGstAppSrc{
1579 qGstCheckedCast<GstAppSrc>(created.element()),
1580 QGstAppSrc::NeedsRef,
1581 };
1582}
1583
1585{
1586 return qGstCheckedCast<GstAppSrc>(element());
1587}
1588
1589void QGstAppSrc::setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data,
1590 GDestroyNotify notify)
1591{
1592 gst_app_src_set_callbacks(appSrc(), &callbacks, user_data, notify);
1593}
1594
1596{
1597 return gst_app_src_push_buffer(appSrc(), buffer);
1598}
1599
1601{
1602 return QStringLiteral("Could not find the %1 GStreamer element")
1603 .arg(QLatin1StringView(element));
1604}
1605
1606QVideoFrameFormat qVideoFrameFormatFromGstVideoInfo(const QGstVideoInfo &qtVideoInfo)
1607{
1608 auto &vidInfo = qtVideoInfo.gstVideoInfo;
1609 GstVideoFormat gstFormat = GST_VIDEO_INFO_FORMAT(&vidInfo);
1610
1611 auto pixelFormat = qPixelFormatFromGstVideoFormat(gstFormat);
1612 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
1613 return QVideoFrameFormat();
1614
1615 QVideoFrameFormat format(QSize(vidInfo.width, vidInfo.height), pixelFormat);
1616
1617 if (vidInfo.fps_d > 0)
1618 format.setStreamFrameRate(qreal(vidInfo.fps_n) / vidInfo.fps_d);
1619
1620 QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown;
1621 switch (vidInfo.colorimetry.range) {
1622 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
1623 break;
1624 case GST_VIDEO_COLOR_RANGE_0_255:
1625 range = QVideoFrameFormat::ColorRange_Full;
1626 break;
1627 case GST_VIDEO_COLOR_RANGE_16_235:
1628 range = QVideoFrameFormat::ColorRange_Video;
1629 break;
1630 }
1631 format.setColorRange(range);
1632
1633 QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
1634 switch (vidInfo.colorimetry.matrix) {
1635 case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
1636 case GST_VIDEO_COLOR_MATRIX_RGB:
1637 case GST_VIDEO_COLOR_MATRIX_FCC:
1638 break;
1639 case GST_VIDEO_COLOR_MATRIX_BT709:
1640 colorSpace = QVideoFrameFormat::ColorSpace_BT709;
1641 break;
1642 case GST_VIDEO_COLOR_MATRIX_BT601:
1643 colorSpace = QVideoFrameFormat::ColorSpace_BT601;
1644 break;
1645 case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
1646 colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
1647 break;
1648 case GST_VIDEO_COLOR_MATRIX_BT2020:
1649 colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
1650 break;
1651 }
1652 format.setColorSpace(colorSpace);
1653
1654 QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown;
1655 switch (vidInfo.colorimetry.transfer) {
1656 case GST_VIDEO_TRANSFER_UNKNOWN:
1657 break;
1658 case GST_VIDEO_TRANSFER_GAMMA10:
1659 transfer = QVideoFrameFormat::ColorTransfer_Linear;
1660 break;
1661 case GST_VIDEO_TRANSFER_GAMMA22:
1662 case GST_VIDEO_TRANSFER_SMPTE240M:
1663 case GST_VIDEO_TRANSFER_SRGB:
1664 case GST_VIDEO_TRANSFER_ADOBERGB:
1665 transfer = QVideoFrameFormat::ColorTransfer_Gamma22;
1666 break;
1667 case GST_VIDEO_TRANSFER_GAMMA18:
1668 case GST_VIDEO_TRANSFER_GAMMA20:
1669 // not quite, but best fit
1670 case GST_VIDEO_TRANSFER_BT709:
1671 case GST_VIDEO_TRANSFER_BT2020_12:
1672 transfer = QVideoFrameFormat::ColorTransfer_BT709;
1673 break;
1674 case GST_VIDEO_TRANSFER_GAMMA28:
1675 transfer = QVideoFrameFormat::ColorTransfer_Gamma28;
1676 break;
1677 case GST_VIDEO_TRANSFER_LOG100:
1678 case GST_VIDEO_TRANSFER_LOG316:
1679 break;
1680 case GST_VIDEO_TRANSFER_SMPTE2084:
1681 transfer = QVideoFrameFormat::ColorTransfer_ST2084;
1682 break;
1683 case GST_VIDEO_TRANSFER_ARIB_STD_B67:
1684 transfer = QVideoFrameFormat::ColorTransfer_STD_B67;
1685 break;
1686 case GST_VIDEO_TRANSFER_BT2020_10:
1687 transfer = QVideoFrameFormat::ColorTransfer_BT709;
1688 break;
1689 case GST_VIDEO_TRANSFER_BT601:
1690 transfer = QVideoFrameFormat::ColorTransfer_BT601;
1691 break;
1692 }
1693 format.setColorTransfer(transfer);
1694
1695 return format;
1696}
1697
1699{
1700 Q_ASSERT(buffer);
1701
1703
1704 [[maybe_unused]] GstMemory *mem = gst_buffer_peek_memory(buffer, 0);
1705
1706#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
1707 if (gst_is_dmabuf_memory(mem))
1708 memoryFormat = QGstCaps::DMABuf;
1709#endif
1710#if QT_CONFIG(gstreamer_gl)
1711 if (gst_is_gl_memory(mem))
1712 memoryFormat = QGstCaps::GLTexture;
1713#endif
1714 return memoryFormat;
1715}
1716
1717QVideoFrame qCreateFrameFromGstBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo)
1718{
1719 auto format = qVideoFrameFormatFromGstVideoInfo(videoInfo);
1720 auto videoBuffer = std::make_unique<QGstVideoBuffer>(buffer, videoInfo, format);
1721 return QVideoFramePrivate::createFrame(std::move(videoBuffer), format);
1722}
1723
1724QT_END_NAMESPACE
QGObjectHandlerConnection(QGstObject object, gulong handler)
Definition qgst.cpp:801
QGObjectHandlerScopedConnection(QGObjectHandlerConnection connection)
Definition qgst.cpp:818
std::optional< int > toInt64() const
Definition qgst.cpp:145
std::optional< QGRange< int > > toIntRange() const
Definition qgst.cpp:174
std::optional< int > toInt() const
Definition qgst.cpp:138
int listSize() const
Definition qgst.cpp:200
bool isList() const
Definition qgst.cpp:195
QGstStructureView toStructure() const
Definition qgst.cpp:181
std::optional< QGRange< float > > getFractionRange() const
Definition qgst.cpp:165
const char * toString() const
Definition qgst.cpp:152
std::optional< float > getFraction() const
Definition qgst.cpp:157
QGValue at(int index) const
Definition qgst.cpp:205
bool isNull() const
Definition qgst.cpp:126
QGstCaps toCaps() const
Definition qgst.cpp:188
std::optional< bool > toBool() const
Definition qgst.cpp:131
QGValue(const GValue *v)
Definition qgst.cpp:124
QGstSampleHandle pullSample()
Definition qgst.cpp:1557
void setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data, GDestroyNotify notify)
Definition qgst.cpp:1551
GstAppSink * appSink() const
Definition qgst.cpp:1529
void setCaps(const QGstCaps &caps)
Definition qgst.cpp:1546
void setMaxBuffers(int)
Definition qgst.cpp:1541
static QGstAppSink create(const char *name)
Definition qgst.cpp:1520
QGstAppSink(GstAppSink *, RefMode)
Definition qgst.cpp:1512
static QGstAppSrc create(const char *name)
Definition qgst.cpp:1575
GstFlowReturn pushBuffer(GstBuffer *)
Definition qgst.cpp:1595
void setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data, GDestroyNotify notify)
Definition qgst.cpp:1589
QGstAppSrc(GstAppSrc *, RefMode)
Definition qgst.cpp:1567
GstAppSrc * appSrc() const
Definition qgst.cpp:1584
void setSync(bool)
Definition qgst.cpp:1485
QGstBaseSink(GstBaseSink *, RefMode)
Definition qgst.cpp:1477
GstBaseSink * baseSink() const
Definition qgst.cpp:1490
QGstBaseSrc(GstBaseSrc *, RefMode)
Definition qgst.cpp:1497
GstBaseSrc * baseSrc() const
Definition qgst.cpp:1505
void addUnlinkedGhostPads(GstPadDirection)
Definition qgst.cpp:1429
void dumpGraph(const char *fileNamePrefix, bool includeTimestamp=true) const
Definition qgst.cpp:1451
QGstElement findByName(const char *)
Definition qgst.cpp:1462
void recalculateLatency()
Definition qgst.cpp:1470
GstBin * bin() const
Definition qgst.cpp:1414
static QGstBin create(const char *name)
Definition qgst.cpp:1361
void addGhostPad(const QGstElement &child, const char *name)
Definition qgst.cpp:1419
static QGstBin createFromPipelineDescription(const char *pipelineDescription, const char *name=nullptr, bool ghostUnlinkedPads=false)
Definition qgst.cpp:1382
static QGstBin createFromFactory(const char *factory, const char *name)
Definition qgst.cpp:1366
void addGhostPad(const char *name, const QGstPad &pad)
Definition qgst.cpp:1424
bool syncChildrenState()
Definition qgst.cpp:1446
QGstBin(GstBin *bin, RefMode mode=NeedsRef)
Definition qgst.cpp:1406
MemoryFormat memoryFormat() const
Definition qgst.cpp:602
std::optional< QGstVideoInfo > videoInfo() const
Definition qgst.cpp:465
int size() const
Definition qgst.cpp:612
static QGstCaps fromCameraFormat(const QCameraFormat &format)
Definition qgst.cpp:539
static QGstCaps create()
Definition qgst.cpp:629
GstCaps * caps() const
Definition qgst.cpp:624
void setResolution(QSize)
Definition qgst.cpp:525
MemoryFormat
Definition qgst_p.h:358
@ DMABuf
Definition qgst_p.h:358
@ CpuMemory
Definition qgst_p.h:358
@ GLTexture
Definition qgst_p.h:358
QGstCaps copy() const
Definition qgst.cpp:594
void addPixelFormats(const QList< QVideoFrameFormat::PixelFormat > &formats, const char *capsFeatures=nullptr)
Definition qgst.cpp:487
QGstStructureView at(int index) const
Definition qgst.cpp:617
QGstClock(GstClock *clock, RefMode mode)
Definition qgst.cpp:979
GstClockTime time() const
Definition qgst.cpp:992
GstClock * clock() const
Definition qgst.cpp:987
QGstClock(const QGstObject &o)
Definition qgst.cpp:971
void setBaseTime(GstClockTime time) const
Definition qgst.cpp:1274
GstStateChangeReturn setState(GstState state)
Definition qgst.cpp:1133
std::optional< std::chrono::nanoseconds > position() const
Definition qgst.cpp:1230
GstElement * element() const
Definition qgst.cpp:1279
void removeFromParent()
Definition qgst.cpp:1334
QGstElement(GstElement *element, RefMode mode)
Definition qgst.cpp:999
void lockState(bool locked)
Definition qgst.cpp:1191
QGstBin getRootBin() const
Definition qgst.cpp:1300
QGstPad getRequestPad(const char *name) const
Definition qgst.cpp:1108
std::optional< bool > canSeek() const
Definition qgst.cpp:1253
std::optional< std::chrono::nanoseconds > duration() const
Definition qgst.cpp:1211
bool isStateLocked() const
Definition qgst.cpp:1196
void sendEvent(GstEvent *event) const
Definition qgst.cpp:1201
void dumpPipelineGraph(const char *filename) const
Definition qgst.cpp:1340
GstClockTime baseTime() const
Definition qgst.cpp:1269
QByteArrayView factoryName() const
Definition qgst.cpp:1083
void releaseRequestPad(const QGstPad &pad) const
Definition qgst.cpp:1113
bool syncStateWithParent()
Definition qgst.cpp:1151
QGstPad sink() const
Definition qgst.cpp:1103
QGstPad staticPad(const char *name) const
Definition qgst.cpp:1093
void sendEos() const
Definition qgst.cpp:1206
QGstBin getParentBin() const
Definition qgst.cpp:1292
std::optional< std::chrono::milliseconds > positionInMs() const
Definition qgst.cpp:1244
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:1007
QGstElement getParent() const
Definition qgst.cpp:1284
QGstPipeline getPipeline() const
Definition qgst.cpp:1320
static QGstElement createFromPipelineDescription(const char *)
Definition qgst.cpp:1051
QGstPad src() const
Definition qgst.cpp:1098
std::optional< std::chrono::milliseconds > durationInMs() const
Definition qgst.cpp:1221
void set(const char *property, double d)
Definition qgst.cpp:666
QGObjectHandlerConnection connect(const char *name, GCallback callback, gpointer userData)
Definition qgst.cpp:761
void * getObject(const char *property) const
Definition qgst.cpp:756
double getDouble(const char *property) const
Definition qgst.cpp:742
int getInt(const char *property) const
Definition qgst.cpp:714
void set(const char *property, int32_t i)
Definition qgst.cpp:651
QLatin1StringView name() const
Definition qgst.cpp:792
void set(const char *property, const QGstObject &o)
Definition qgst.cpp:671
QGString getString(const char *property) const
Definition qgst.cpp:686
void disconnect(gulong handlerId)
Definition qgst.cpp:770
QGstObject getGstObject(const char *property) const
Definition qgst.cpp:749
QLatin1StringView typeName() const
Definition qgst.cpp:780
void set(const char *property, const char *str)
Definition qgst.cpp:636
bool getBool(const char *property) const
Definition qgst.cpp:700
GType type() const
Definition qgst.cpp:775
QGstObject & operator=(QGstObject &&) noexcept=default
void set(const char *property, void *object, GDestroyNotify destroyFunction)
Definition qgst.cpp:681
void set(const char *property, bool b)
Definition qgst.cpp:641
quint64 getUInt64(const char *property) const
Definition qgst.cpp:721
QGstStructureView getStructure(const char *property) const
Definition qgst.cpp:693
qint64 getInt64(const char *property) const
Definition qgst.cpp:728
void set(const char *property, const QGstCaps &c)
Definition qgst.cpp:676
float getFloat(const char *property) const
Definition qgst.cpp:735
uint getUInt(const char *property) const
Definition qgst.cpp:707
GstObject * object() const
Definition qgst.cpp:787
bool link(const QGstPad &sink) const
Definition qgst.cpp:898
GstEvent * stickyEvent(GstEventType type)
Definition qgst.cpp:932
QGstTagListHandle tags() const
Definition qgst.cpp:864
QGstPad(const QGstObject &o)
Definition qgst.cpp:838
bool isLinked() const
Definition qgst.cpp:893
bool unlinkPeer() const
Definition qgst.cpp:908
QGstCaps queryCaps() const
Definition qgst.cpp:859
GstPad * pad() const
Definition qgst.cpp:927
QGstPad(GstPad *pad, RefMode mode)
Definition qgst.cpp:846
void sendFlushIfPaused()
Definition qgst.cpp:957
void sendFlushStartStop(bool resetTime)
Definition qgst.cpp:942
QGString streamId() const
Definition qgst.cpp:871
std::optional< QPlatformMediaPlayer::TrackType > inferTrackTypeFromName() const
Definition qgst.cpp:878
bool sendEvent(GstEvent *event)
Definition qgst.cpp:937
QGstCaps currentCaps() const
Definition qgst.cpp:854
QGstElement parent() const
Definition qgst.cpp:922
bool unlink(const QGstPad &sink) const
Definition qgst.cpp:903
QGstPad peer() const
Definition qgst.cpp:917
QGstStructureView(const QUniqueGstStructureHandle &)
Definition qgst.cpp:214
bool isNull() const
Definition qgst.cpp:224
QGRange< float > frameRateRange() const
Definition qgst.cpp:344
QSize nativeSize() const
Definition qgst.cpp:449
QGValue operator[](const char *fieldname) const
Definition qgst.cpp:234
QUniqueGstStructureHandle clone() const
Definition qgst.cpp:219
QList< QVideoFrameFormat::PixelFormat > pixelFormats() const
Definition qgst.cpp:269
std::optional< QGRange< QSize > > resolutionRange() const
Definition qgst.cpp:402
QGstreamerMessage getMessage()
Definition qgst.cpp:428
QGstCaps caps() const
Definition qgst.cpp:239
std::optional< Fraction > pixelAspectRatio() const
Definition qgst.cpp:435
QGstTagListHandle tags() const
Definition qgst.cpp:244
QSize resolution() const
Definition qgst.cpp:255
QByteArrayView name() const
Definition qgst.cpp:229
QGstStructureView(const GstStructure *)
Definition qgst.cpp:212
QSize qCalculateFrameSizeGStreamer(QSize resolution, Fraction par)
constexpr std::array< VideoFormat, 19 > qt_videoFormatLookup
Definition qgst.cpp:38
QGstCaps::MemoryFormat qMemoryFormatFromGstBuffer(GstBuffer *buffer)
Definition qgst.cpp:1698
QVideoFrameFormat qVideoFrameFormatFromGstVideoInfo(const QGstVideoInfo &vidInfo)
Definition qgst.cpp:1606
#define QT_GSTREAMER_SUPPORTS_GST_VIDEO_FORMAT_DMA_DRM
Definition qgst_p.h:38
GstVideoFormat qGstVideoFormatFromPixelFormat(QVideoFrameFormat::PixelFormat format)
Definition qgst.cpp:104
QString qGstErrorMessageCannotFindElement(std::string_view element)
Definition qgst.cpp:1600
QVideoFrame qCreateFrameFromGstBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo)
Definition qgst.cpp:1717
QVideoFrameFormat::PixelFormat qPixelFormatFromGstVideoFormat(GstVideoFormat format)
Definition qgst.cpp:113
QVideoFrameFormat::PixelFormat pixelFormat
Definition qgst.cpp:34