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 QGstStructure dmaDrmStructure{ "video/x-raw" };
89 dmaDrmStructure.setString("format", gst_video_format_to_string(GST_VIDEO_FORMAT_DMA_DRM));
90 dmaDrmStructure.setFractionRange("framerate", Fraction{ 0, 1 }, Fraction{ INT_MAX, 1 });
91 dmaDrmStructure.setIntRange("width", 1, INT_MAX);
92 dmaDrmStructure.setIntRange("height", 1, INT_MAX);
93 dmaDrmStructure.setValue("drm-format", &drmFormatList);
94
95 gst_caps_append_structure(caps.get(), dmaDrmStructure.release());
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 QGstStructure structure{ "video/x-raw" };
514 structure.setFractionRange("framerate", Fraction{ 0, 1 }, Fraction{ INT_MAX, 1 });
515 structure.setIntRange("width", 1, INT_MAX);
516 structure.setIntRange("height", 1, INT_MAX);
517 structure.setValue("format", &list);
518
519 gst_caps_append_structure(get(), structure.release());
520 g_value_unset(&list);
521
522 if (capsFeatures)
523 gst_caps_set_features(get(), size() - 1,
524 gst_caps_features_from_string(capsFeatures));
525}
526
527void QGstCaps::setResolution(QSize resolution)
528{
529 Q_ASSERT(resolution.isValid());
530 GValue width{};
531 g_value_init(&width, G_TYPE_INT);
532 g_value_set_int(&width, resolution.width());
533 GValue height{};
534 g_value_init(&height, G_TYPE_INT);
535 g_value_set_int(&height, resolution.height());
536
537 gst_caps_set_value(caps(), "width", &width);
538 gst_caps_set_value(caps(), "height", &height);
539}
540
541QGstCaps QGstCaps::fromCameraFormat(const QCameraFormat &format)
542{
543 QSize size = format.resolution();
544 auto caps = QGstCaps::create();
545
546 if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
547 QGstStructure jpegStructure("image/jpeg");
548 jpegStructure.setInt("width", size.width());
549 jpegStructure.setInt("height", size.height());
550
551 gst_caps_append_structure(caps.get(), jpegStructure.release());
552 return caps;
553 }
554
555 const GstVideoFormat gstFormat = qGstVideoFormatFromPixelFormat(format.pixelFormat());
556 if (gstFormat == GST_VIDEO_FORMAT_UNKNOWN)
557 return {};
558
559 QGstStructure rawStructure("video/x-raw");
560 rawStructure.setString("format", gst_video_format_to_string(gstFormat));
561 rawStructure.setInt("width", size.width());
562 rawStructure.setInt("height", size.height());
563
564 gst_caps_append_structure(caps.get(), rawStructure.release());
565
567 if (const guint32 fourcc = gst_video_dma_drm_fourcc_from_format(gstFormat)) {
568 if (QGString drmFormat{gst_video_dma_drm_fourcc_to_string(fourcc, 0)}) {
569 QGstStructure drmFormatDmabufRawStructure("video/x-raw");
570 drmFormatDmabufRawStructure.setString(
571 "format", gst_video_format_to_string(GST_VIDEO_FORMAT_DMA_DRM));
572 drmFormatDmabufRawStructure.setString("drm-format", drmFormat.get());
573 drmFormatDmabufRawStructure.setInt("width", size.width());
574 drmFormatDmabufRawStructure.setInt("height", size.height());
575
576 gst_caps_append_structure(caps.get(), drmFormatDmabufRawStructure.release());
577 gst_caps_set_features(
578 caps.get(), caps.size() - 1,
579 gst_caps_features_from_string(GST_CAPS_FEATURE_MEMORY_DMABUF));
580 }
581 }
582#endif
583
584#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
585 QGstStructure dmabufRawStructure("video/x-raw");
586 dmabufRawStructure.setString("format", gst_video_format_to_string(gstFormat));
587 dmabufRawStructure.setInt("width", size.width());
588 dmabufRawStructure.setInt("height", size.height());
589
590 gst_caps_append_structure(caps.get(), dmabufRawStructure.release());
591 gst_caps_set_features(caps.get(), caps.size() - 1,
592 gst_caps_features_from_string(GST_CAPS_FEATURE_MEMORY_DMABUF));
593#endif
594
595 return caps;
596}
597
599{
600 return QGstCaps{
601 gst_caps_copy(caps()),
602 QGstCaps::HasRef,
603 };
604}
605
607{
608 auto *features = gst_caps_get_features(get(), 0);
609 if (gst_caps_features_contains(features, "memory:GLMemory"))
610 return GLTexture;
611 if (gst_caps_features_contains(features, "memory:DMABuf"))
612 return DMABuf;
613 return CpuMemory;
614}
615
616int QGstCaps::size() const
618 return int(gst_caps_get_size(get()));
619}
620
622{
623 return QGstStructureView{
624 gst_caps_get_structure(get(), index),
625 };
626}
627
629{
630 return get();
631}
632
634{
635 return QGstCaps(gst_caps_new_empty(), HasRef);
636}
637
638// QGstObject
639
640void QGstObject::set(const char *property, const char *str)
641{
642 g_object_set(get(), property, str, nullptr);
643}
644
645void QGstObject::set(const char *property, bool b)
646{
647 g_object_set(get(), property, gboolean(b), nullptr);
648}
649
650void QGstObject::set(const char *property, uint32_t i)
651{
652 g_object_set(get(), property, guint(i), nullptr);
653}
654
655void QGstObject::set(const char *property, int32_t i)
656{
657 g_object_set(get(), property, gint(i), nullptr);
658}
659
660void QGstObject::set(const char *property, int64_t i)
661{
662 g_object_set(get(), property, gint64(i), nullptr);
663}
664
665void QGstObject::set(const char *property, uint64_t i)
666{
667 g_object_set(get(), property, guint64(i), nullptr);
668}
669
670void QGstObject::set(const char *property, double d)
671{
672 g_object_set(get(), property, gdouble(d), nullptr);
673}
674
675void QGstObject::set(const char *property, const QGstObject &o)
676{
677 g_object_set(get(), property, o.object(), nullptr);
678}
679
680void QGstObject::set(const char *property, const QGstCaps &c)
681{
682 g_object_set(get(), property, c.caps(), nullptr);
683}
684
685void QGstObject::set(const char *property, void *object, GDestroyNotify destroyFunction)
686{
687 g_object_set_data_full(qGstCheckedCast<GObject>(get()), property, object, destroyFunction);
688}
689
690QGString QGstObject::getString(const char *property) const
691{
692 char *s = nullptr;
693 g_object_get(get(), property, &s, nullptr);
694 return QGString(s);
695}
696
697QGstStructureView QGstObject::getStructure(const char *property) const
698{
699 GstStructure *s = nullptr;
700 g_object_get(get(), property, &s, nullptr);
701 return QGstStructureView(s);
702}
703
704bool QGstObject::getBool(const char *property) const
705{
706 gboolean b = false;
707 g_object_get(get(), property, &b, nullptr);
708 return b;
709}
710
711uint QGstObject::getUInt(const char *property) const
712{
713 guint i = 0;
714 g_object_get(get(), property, &i, nullptr);
715 return i;
716}
717
718int QGstObject::getInt(const char *property) const
719{
720 gint i = 0;
721 g_object_get(get(), property, &i, nullptr);
722 return i;
723}
724
725quint64 QGstObject::getUInt64(const char *property) const
726{
727 guint64 i = 0;
728 g_object_get(get(), property, &i, nullptr);
729 return i;
730}
731
732qint64 QGstObject::getInt64(const char *property) const
733{
734 gint64 i = 0;
735 g_object_get(get(), property, &i, nullptr);
736 return i;
737}
738
739float QGstObject::getFloat(const char *property) const
740{
741 gfloat d = 0;
742 g_object_get(get(), property, &d, nullptr);
743 return d;
744}
745
746double QGstObject::getDouble(const char *property) const
747{
748 gdouble d = 0;
749 g_object_get(get(), property, &d, nullptr);
750 return d;
751}
752
753QGstObject QGstObject::getGstObject(const char *property) const
754{
755 GstObject *o = nullptr;
756 g_object_get(get(), property, &o, nullptr);
757 return QGstObject(o, HasRef);
758}
759
760void *QGstObject::getObject(const char *property) const
761{
762 return g_object_get_data(qGstCheckedCast<GObject>(get()), property);
763}
764
765QGObjectHandlerConnection QGstObject::connect(const char *name, GCallback callback,
766 gpointer userData)
767{
769 *this,
770 g_signal_connect(get(), name, callback, userData),
771 };
772}
773
774void QGstObject::disconnect(gulong handlerId)
775{
776 g_signal_handler_disconnect(get(), handlerId);
777}
778
780{
781 return G_OBJECT_TYPE(get());
782}
783
785{
786 return QLatin1StringView{
787 g_type_name(type()),
788 };
789}
790
792{
793 return get();
794}
795
797{
798 using namespace Qt::StringLiterals;
799
800 return get() ? QLatin1StringView{ GST_OBJECT_NAME(get()) } : "(null)"_L1;
801}
802
803// QGObjectHandlerConnection
804
806 : object{ std::move(object) }, handlerId{ handlerId }
807{
808}
809
811{
812 if (!object)
813 return;
814
815 object.disconnect(handlerId);
816 object = {};
817 handlerId = invalidHandlerId;
818}
819
820// QGObjectHandlerScopedConnection
821
829
831{
832 connection.disconnect();
833}
834
836{
837 connection.disconnect();
838}
839
840// QGstPad
841
843 : QGstPad{
846 }
847{
848}
849
850QGstPad::QGstPad(GstPad *pad, RefMode mode)
851 : QGstObject{
853 mode,
854 }
855{
856}
857
859{
860 return QGstCaps(gst_pad_get_current_caps(pad()), QGstCaps::HasRef);
861}
862
864{
865 return QGstCaps(gst_pad_query_caps(pad(), nullptr), QGstCaps::HasRef);
866}
867
869{
870 QGstTagListHandle tagList;
871 g_object_get(object(), "tags", &tagList, nullptr);
872 return tagList;
873}
874
876{
877 return QGString{
878 gst_pad_get_stream_id(pad()),
879 };
880}
881
883{
884 using namespace Qt::Literals;
885 QLatin1StringView padName = name();
886
887 if (padName.startsWith("video_"_L1))
888 return QPlatformMediaPlayer::TrackType::VideoStream;
889 if (padName.startsWith("audio_"_L1))
890 return QPlatformMediaPlayer::TrackType::AudioStream;
891 if (padName.startsWith("text_"_L1))
892 return QPlatformMediaPlayer::TrackType::SubtitleStream;
893
894 return std::nullopt;
895}
896
897bool QGstPad::isLinked() const
898{
899 return gst_pad_is_linked(pad());
900}
901
902bool QGstPad::link(const QGstPad &sink) const
903{
904 return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK;
905}
906
907bool QGstPad::unlink(const QGstPad &sink) const
908{
909 return gst_pad_unlink(pad(), sink.pad());
910}
911
912bool QGstPad::unlinkPeer() const
913{
914 QGstPad peerPad = peer();
915 if (peerPad)
916 return GST_PAD_IS_SRC(pad()) ? unlink(peerPad) : peerPad.unlink(*this);
917
918 return true;
919}
922{
923 return QGstPad(gst_pad_get_peer(pad()), HasRef);
924}
925
927{
928 return QGstElement(gst_pad_get_parent_element(pad()), HasRef);
929}
930
932{
933 return qGstCheckedCast<GstPad>(object());
934}
935
936GstEvent *QGstPad::stickyEvent(GstEventType type)
938 return gst_pad_get_sticky_event(pad(), type, 0);
939}
940
941bool QGstPad::sendEvent(GstEvent *event)
942{
943 return gst_pad_send_event(pad(), event);
944}
945
946void QGstPad::sendFlushStartStop(bool resetTime)
947{
948 GstEvent *flushStart = gst_event_new_flush_start();
949 gboolean ret = sendEvent(flushStart);
950 if (!ret) {
951 qWarning("failed to send flush-start event");
952 return;
953 }
954
955 GstEvent *flushStop = gst_event_new_flush_stop(resetTime);
956 ret = sendEvent(flushStop);
957 if (!ret)
958 qWarning("failed to send flush-stop event");
959}
960
962{
963 using namespace std::chrono_literals;
964
965 GstState state = parent().state(1s);
966
967 if (state != GST_STATE_PAUSED)
968 return;
969
970 sendFlushStartStop(/*resetTime=*/true);
971}
972
973// QGstClock
974
976 : QGstClock{
979 }
980{
981}
982
983QGstClock::QGstClock(GstClock *clock, RefMode mode)
984 : QGstObject{
986 mode,
987 }
988{
989}
990
992{
993 return qGstCheckedCast<GstClock>(object());
994}
995
997{
998 return gst_clock_get_time(clock());
999}
1000
1001// QGstElement
1002
1003QGstElement::QGstElement(GstElement *element, RefMode mode)
1004 : QGstObject{
1006 mode,
1007 }
1008{
1009}
1010
1011QGstElement QGstElement::createFromFactory(const char *factory, const char *name)
1012{
1013 GstElement *element = gst_element_factory_make(factory, name);
1014
1015#ifndef QT_NO_DEBUG
1016 if (!element) {
1017 qWarning() << "Failed to make element" << name << "from factory" << factory;
1018 return QGstElement{};
1019 }
1020#endif
1021
1022 return QGstElement{
1023 element,
1024 NeedsRef,
1025 };
1026}
1027
1028QGstElement QGstElement::createFromFactory(GstElementFactory *factory, const char *name)
1029{
1030 return QGstElement{
1031 gst_element_factory_create(factory, name),
1032 NeedsRef,
1033 };
1034}
1035
1036QGstElement QGstElement::createFromFactory(const QGstElementFactoryHandle &factory,
1037 const char *name)
1038{
1039 return createFromFactory(factory.get(), name);
1040}
1041
1042QGstElement QGstElement::createFromDevice(const QGstDeviceHandle &device, const char *name)
1043{
1044 return createFromDevice(device.get(), name);
1045}
1046
1047QGstElement QGstElement::createFromDevice(GstDevice *device, const char *name)
1048{
1049 return QGstElement{
1050 gst_device_create_element(device, name),
1051 QGstElement::NeedsRef,
1052 };
1053}
1054
1056{
1057 QUniqueGErrorHandle error;
1058 QGstElement element{
1059 gst_parse_launch(str, &error),
1060 QGstElement::NeedsRef,
1061 };
1062
1063 if (error) // error does not mean that the element could not be constructed
1064 qWarning() << "gst_parse_launch error:" << error;
1065
1066 return element;
1067}
1068
1069QGstElement QGstElement::createFromPipelineDescription(const QByteArray &str)
1070{
1071 return createFromPipelineDescription(str.constData());
1072}
1073
1074QGstElementFactoryHandle QGstElement::findFactory(const char *name)
1075{
1076 return QGstElementFactoryHandle{
1077 gst_element_factory_find(name),
1078 QGstElementFactoryHandle::HasRef,
1079 };
1080}
1081
1082QGstElementFactoryHandle QGstElement::findFactory(const QByteArray &name)
1083{
1084 return findFactory(name.constData());
1085}
1086
1088{
1089 GstElementFactory *factory = gst_element_get_factory(element());
1090 if (!factory)
1091 return {};
1092
1093 const char *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
1094 return name ? QByteArrayView{name} : QByteArrayView{};
1095}
1096
1097QGstPad QGstElement::staticPad(const char *name) const
1098{
1099 return QGstPad(gst_element_get_static_pad(element(), name), HasRef);
1100}
1101
1103{
1104 return staticPad("src");
1105}
1106
1108{
1109 return staticPad("sink");
1110}
1111
1112QGstPad QGstElement::getRequestPad(const char *name) const
1113{
1114 return QGstPad(gst_element_request_pad_simple(element(), name), HasRef);
1115}
1116
1118{
1119 return gst_element_release_request_pad(element(), pad.pad());
1120}
1121
1122GstState QGstElement::state(std::chrono::nanoseconds timeout) const
1123{
1124 using namespace std::chrono_literals;
1125
1126 GstState state;
1127 GstStateChangeReturn change =
1128 gst_element_get_state(element(), &state, nullptr, timeout.count());
1129
1130 if (Q_UNLIKELY(change == GST_STATE_CHANGE_ASYNC))
1131 qWarning() << "QGstElement::state detected an asynchronous state change. Return value not "
1132 "reliable";
1133
1134 return state;
1135}
1136
1138{
1139 return gst_element_set_state(element(), state);
1140}
1141
1142bool QGstElement::setStateSync(GstState state, std::chrono::nanoseconds timeout)
1143{
1144 GstStateChangeReturn change = gst_element_set_state(element(), state);
1145 if (change == GST_STATE_CHANGE_ASYNC)
1146 change = gst_element_get_state(element(), nullptr, &state, timeout.count());
1147
1148 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
1149 qWarning() << "Could not change state of" << name() << "to" << state << change;
1150 dumpPipelineGraph("setStateSyncFailure");
1151 }
1152 return change == GST_STATE_CHANGE_SUCCESS || change == GST_STATE_CHANGE_NO_PREROLL;
1153}
1154
1156{
1157 Q_ASSERT(element());
1158 return gst_element_sync_state_with_parent(element()) == TRUE;
1159}
1160
1161bool QGstElement::finishStateChange(std::chrono::nanoseconds timeout)
1162{
1163 GstState state, pending;
1164 GstStateChangeReturn change =
1165 gst_element_get_state(element(), &state, &pending, timeout.count());
1166
1167 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
1168 qWarning() << "Could not finish change state of" << name() << change << state << pending;
1169 dumpPipelineGraph("finishStateChangeFailure");
1170 }
1171 return change == GST_STATE_CHANGE_SUCCESS;
1172}
1173
1174bool QGstElement::hasAsyncStateChange(std::chrono::nanoseconds timeout) const
1175{
1176 GstState state;
1177 GstStateChangeReturn change =
1178 gst_element_get_state(element(), &state, nullptr, timeout.count());
1179 return change == GST_STATE_CHANGE_ASYNC;
1180}
1181
1182bool QGstElement::waitForAsyncStateChangeComplete(std::chrono::nanoseconds timeout) const
1183{
1184 using namespace std::chrono_literals;
1185 for (;;) {
1186 if (!hasAsyncStateChange())
1187 return true;
1188 timeout -= 10ms;
1189 if (timeout < 0ms)
1190 return false;
1191 std::this_thread::sleep_for(10ms);
1192 }
1193}
1194
1195void QGstElement::lockState(bool locked)
1196{
1197 gst_element_set_locked_state(element(), locked);
1198}
1199
1201{
1202 return gst_element_is_locked_state(element());
1203}
1204
1205void QGstElement::sendEvent(GstEvent *event) const
1206{
1207 gst_element_send_event(element(), event);
1208}
1209
1211{
1212 sendEvent(gst_event_new_eos());
1213}
1214
1215std::optional<std::chrono::nanoseconds> QGstElement::duration() const
1216{
1217 gint64 d;
1218 if (!gst_element_query_duration(element(), GST_FORMAT_TIME, &d)) {
1219 qDebug() << "QGstElement: failed to query duration";
1220 return std::nullopt;
1221 }
1222 return std::chrono::nanoseconds{ d };
1223}
1224
1225std::optional<std::chrono::milliseconds> QGstElement::durationInMs() const
1226{
1227 using namespace std::chrono;
1228 auto dur = duration();
1229 if (dur)
1230 return round<milliseconds>(*dur);
1231 return std::nullopt;
1232}
1233
1234std::optional<std::chrono::nanoseconds> QGstElement::position() const
1235{
1236 QGstQueryHandle &query = positionQuery();
1237
1238 gint64 pos;
1239 if (gst_element_query(element(), query.get())) {
1240 gst_query_parse_position(query.get(), nullptr, &pos);
1241 return std::chrono::nanoseconds{ pos };
1242 }
1243
1244 qDebug() << "QGstElement: failed to query position";
1245 return std::nullopt;
1246}
1247
1248std::optional<std::chrono::milliseconds> QGstElement::positionInMs() const
1249{
1250 using namespace std::chrono;
1251 auto pos = position();
1252 if (pos)
1253 return round<milliseconds>(*pos);
1254 return std::nullopt;
1255}
1256
1257std::optional<bool> QGstElement::canSeek() const
1258{
1259 QGstQueryHandle query{
1260 gst_query_new_seeking(GST_FORMAT_TIME),
1261 QGstQueryHandle::HasRef,
1262 };
1263 gboolean canSeek = false;
1264 gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
1265
1266 if (gst_element_query(element(), query.get())) {
1267 gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
1268 return canSeek;
1269 }
1270 return std::nullopt;
1271}
1272
1274{
1275 return gst_element_get_base_time(element());
1276}
1277
1278void QGstElement::setBaseTime(GstClockTime time) const
1279{
1280 gst_element_set_base_time(element(), time);
1281}
1282
1284{
1285 return GST_ELEMENT_CAST(get());
1286}
1287
1289{
1290 return QGstElement{
1291 qGstCheckedCast<GstElement>(gst_element_get_parent(object())),
1292 QGstElement::HasRef,
1293 };
1294}
1295
1297{
1298 return QGstBin{
1299 qGstCheckedCast<GstBin>(gst_element_get_parent(object())),
1300 QGstElement::HasRef,
1301 };
1302}
1303
1305{
1306 QGstElement ancestor = *this;
1307 for (;;) {
1308 QGstElement greatAncestor = ancestor.getParent();
1309 if (greatAncestor) {
1310 ancestor = std::move(greatAncestor);
1311 continue;
1312 }
1313 if (GST_IS_BIN(ancestor.element())) {
1314 return QGstBin{
1315 qGstSafeCast<GstBin>(ancestor.element()),
1316 QGstBin::NeedsRef,
1317 };
1318 } else {
1319 return QGstBin{};
1320 }
1321 }
1322}
1323
1325{
1326 QGstBin rootBin = getRootBin();
1327 if (GST_IS_PIPELINE(rootBin.get())) {
1328 return QGstPipeline{
1329 qGstSafeCast<GstPipeline>(rootBin.element()),
1330 QGstPipeline::NeedsRef,
1331 };
1332 } else {
1333 qWarning() << "QGstElement::getPipeline failed for element:" << *this;
1334 return QGstPipeline{};
1335 }
1336}
1337
1339{
1340 if (QGstBin parent = getParentBin())
1341 parent.remove(*this);
1342}
1343
1344void QGstElement::dumpPipelineGraph(const char *filename) const
1345{
1346 static const bool dumpEnabled = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR");
1347 if (dumpEnabled) {
1349 }
1350}
1351
1352QGstQueryHandle &QGstElement::positionQuery() const
1353{
1354 if (Q_UNLIKELY(!m_positionQuery))
1355 m_positionQuery = QGstQueryHandle{
1356 gst_query_new_position(GST_FORMAT_TIME),
1357 QGstQueryHandle::HasRef,
1358 };
1359
1360 return m_positionQuery;
1361}
1362
1363// QGstBin
1364
1365QGstBin QGstBin::create(const char *name)
1366{
1367 return QGstBin(gst_bin_new(name), NeedsRef);
1368}
1369
1370QGstBin QGstBin::createFromFactory(const char *factory, const char *name)
1371{
1372 QGstElement element = QGstElement::createFromFactory(factory, name);
1373 Q_ASSERT(GST_IS_BIN(element.element()));
1374 return QGstBin{
1375 GST_BIN(element.release()),
1376 RefMode::HasRef,
1377 };
1378}
1379
1380QGstBin QGstBin::createFromPipelineDescription(const QByteArray &pipelineDescription,
1381 const char *name, bool ghostUnlinkedPads)
1382{
1383 return createFromPipelineDescription(pipelineDescription.constData(), name, ghostUnlinkedPads);
1384}
1385
1386QGstBin QGstBin::createFromPipelineDescription(const char *pipelineDescription, const char *name,
1387 bool ghostUnlinkedPads)
1388{
1389 QUniqueGErrorHandle error;
1390
1391 GstElement *element =
1392 gst_parse_bin_from_description_full(pipelineDescription, ghostUnlinkedPads,
1393 /*context=*/nullptr, GST_PARSE_FLAG_NONE, &error);
1394
1395 if (!element) {
1396 qWarning() << "Failed to make element from pipeline description" << pipelineDescription
1397 << error;
1398 return QGstBin{};
1399 }
1400
1401 if (name)
1402 gst_element_set_name(element, name);
1403
1404 return QGstBin{
1405 element,
1406 NeedsRef,
1407 };
1408}
1409
1410QGstBin::QGstBin(GstBin *bin, RefMode mode)
1411 : QGstElement{
1413 mode,
1414 }
1415{
1416}
1417
1419{
1420 return qGstCheckedCast<GstBin>(object());
1421}
1422
1423void QGstBin::addGhostPad(const QGstElement &child, const char *name)
1424{
1425 addGhostPad(name, child.staticPad(name));
1426}
1427
1428void QGstBin::addGhostPad(const char *name, const QGstPad &pad)
1429{
1430 gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad()));
1431}
1432
1433void QGstBin::addUnlinkedGhostPads(GstPadDirection direction)
1434{
1435 Q_ASSERT(direction != GstPadDirection::GST_PAD_UNKNOWN);
1436
1437 for (;;) {
1438 QGstPad unlinkedPad{
1439 gst_bin_find_unlinked_pad(bin(), direction),
1440 QGstPad::HasRef,
1441 };
1442
1443 if (!unlinkedPad)
1444 return;
1445
1446 addGhostPad(unlinkedPad.name().constData(), unlinkedPad);
1447 }
1448}
1449
1451{
1452 return gst_bin_sync_children_states(bin());
1453}
1454
1455void QGstBin::dumpGraph(const char *fileNamePrefix, bool includeTimestamp) const
1456{
1457 if (!get())
1458 return;
1459
1460 if (includeTimestamp)
1461 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin(), GST_DEBUG_GRAPH_SHOW_VERBOSE, fileNamePrefix);
1462 else
1463 GST_DEBUG_BIN_TO_DOT_FILE(bin(), GST_DEBUG_GRAPH_SHOW_VERBOSE, fileNamePrefix);
1464}
1465
1467{
1468 return QGstElement{
1469 gst_bin_get_by_name(bin(), name),
1470 QGstElement::NeedsRef,
1471 };
1472}
1473
1475{
1476 gst_bin_recalculate_latency(bin());
1477}
1478
1479// QGstBaseSink
1480
1481QGstBaseSink::QGstBaseSink(GstBaseSink *element, RefMode mode)
1482 : QGstElement{
1484 mode,
1485 }
1486{
1487}
1488
1489void QGstBaseSink::setSync(bool arg)
1490{
1491 gst_base_sink_set_sync(baseSink(), arg ? TRUE : FALSE);
1492}
1493
1495{
1496 return qGstCheckedCast<GstBaseSink>(element());
1497}
1498
1499// QGstBaseSrc
1500
1501QGstBaseSrc::QGstBaseSrc(GstBaseSrc *element, RefMode mode)
1502 : QGstElement{
1504 mode,
1505 }
1506{
1507}
1508
1510{
1511 return qGstCheckedCast<GstBaseSrc>(element());
1512}
1513
1514// QGstAppSink
1515
1516QGstAppSink::QGstAppSink(GstAppSink *element, RefMode mode)
1517 : QGstBaseSink{
1519 mode,
1520 }
1521{
1522}
1523
1525{
1526 QGstElement created = QGstElement::createFromFactory("appsink", name);
1527 return QGstAppSink{
1528 qGstCheckedCast<GstAppSink>(created.element()),
1529 QGstAppSink::NeedsRef,
1530 };
1531}
1532
1534{
1535 return qGstCheckedCast<GstAppSink>(element());
1536}
1537
1538# if GST_CHECK_VERSION(1, 24, 0)
1539void QGstAppSink::setMaxBufferTime(std::chrono::nanoseconds ns)
1540{
1541 gst_app_sink_set_max_time(appSink(), qGstClockTimeFromChrono(ns));
1542}
1543# endif
1544
1546{
1547 gst_app_sink_set_max_buffers(appSink(), n);
1548}
1549
1550void QGstAppSink::setCaps(const QGstCaps &caps)
1551{
1552 gst_app_sink_set_caps(appSink(), caps.caps());
1553}
1554
1555void QGstAppSink::setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data,
1556 GDestroyNotify notify)
1557{
1558 gst_app_sink_set_callbacks(appSink(), &callbacks, user_data, notify);
1559}
1560
1562{
1563 return QGstSampleHandle{
1564 gst_app_sink_pull_sample(appSink()),
1565 QGstSampleHandle::HasRef,
1566 };
1567}
1568
1569// QGstAppSrc
1570
1571QGstAppSrc::QGstAppSrc(GstAppSrc *element, RefMode mode)
1572 : QGstBaseSrc{
1574 mode,
1575 }
1576{
1577}
1578
1579QGstAppSrc QGstAppSrc::create(const char *name)
1580{
1581 QGstElement created = QGstElement::createFromFactory("appsrc", name);
1582 return QGstAppSrc{
1583 qGstCheckedCast<GstAppSrc>(created.element()),
1584 QGstAppSrc::NeedsRef,
1585 };
1586}
1587
1589{
1590 return qGstCheckedCast<GstAppSrc>(element());
1591}
1592
1593void QGstAppSrc::setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data,
1594 GDestroyNotify notify)
1595{
1596 gst_app_src_set_callbacks(appSrc(), &callbacks, user_data, notify);
1597}
1598
1600{
1601 return gst_app_src_push_buffer(appSrc(), buffer);
1602}
1603
1605{
1606 return QStringLiteral("Could not find the %1 GStreamer element")
1607 .arg(QLatin1StringView(element));
1608}
1609
1610QVideoFrameFormat qVideoFrameFormatFromGstVideoInfo(const QGstVideoInfo &qtVideoInfo)
1611{
1612 auto &vidInfo = qtVideoInfo.gstVideoInfo;
1613 GstVideoFormat gstFormat = GST_VIDEO_INFO_FORMAT(&vidInfo);
1614
1615 auto pixelFormat = qPixelFormatFromGstVideoFormat(gstFormat);
1616 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
1617 return QVideoFrameFormat();
1618
1619 QVideoFrameFormat format(QSize(vidInfo.width, vidInfo.height), pixelFormat);
1620
1621 if (vidInfo.fps_d > 0)
1622 format.setStreamFrameRate(qreal(vidInfo.fps_n) / vidInfo.fps_d);
1623
1624 QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown;
1625 switch (vidInfo.colorimetry.range) {
1626 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
1627 break;
1628 case GST_VIDEO_COLOR_RANGE_0_255:
1629 range = QVideoFrameFormat::ColorRange_Full;
1630 break;
1631 case GST_VIDEO_COLOR_RANGE_16_235:
1632 range = QVideoFrameFormat::ColorRange_Video;
1633 break;
1634 }
1635 format.setColorRange(range);
1636
1637 QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
1638 switch (vidInfo.colorimetry.matrix) {
1639 case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
1640 case GST_VIDEO_COLOR_MATRIX_RGB:
1641 case GST_VIDEO_COLOR_MATRIX_FCC:
1642 break;
1643 case GST_VIDEO_COLOR_MATRIX_BT709:
1644 colorSpace = QVideoFrameFormat::ColorSpace_BT709;
1645 break;
1646 case GST_VIDEO_COLOR_MATRIX_BT601:
1647 colorSpace = QVideoFrameFormat::ColorSpace_BT601;
1648 break;
1649 case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
1650 colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
1651 break;
1652 case GST_VIDEO_COLOR_MATRIX_BT2020:
1653 colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
1654 break;
1655 }
1656 format.setColorSpace(colorSpace);
1657
1658 QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown;
1659 switch (vidInfo.colorimetry.transfer) {
1660 case GST_VIDEO_TRANSFER_UNKNOWN:
1661 break;
1662 case GST_VIDEO_TRANSFER_GAMMA10:
1663 transfer = QVideoFrameFormat::ColorTransfer_Linear;
1664 break;
1665 case GST_VIDEO_TRANSFER_GAMMA22:
1666 case GST_VIDEO_TRANSFER_SMPTE240M:
1667 case GST_VIDEO_TRANSFER_SRGB:
1668 case GST_VIDEO_TRANSFER_ADOBERGB:
1669 transfer = QVideoFrameFormat::ColorTransfer_Gamma22;
1670 break;
1671 case GST_VIDEO_TRANSFER_GAMMA18:
1672 case GST_VIDEO_TRANSFER_GAMMA20:
1673 // not quite, but best fit
1674 case GST_VIDEO_TRANSFER_BT709:
1675 case GST_VIDEO_TRANSFER_BT2020_12:
1676 transfer = QVideoFrameFormat::ColorTransfer_BT709;
1677 break;
1678 case GST_VIDEO_TRANSFER_GAMMA28:
1679 transfer = QVideoFrameFormat::ColorTransfer_Gamma28;
1680 break;
1681 case GST_VIDEO_TRANSFER_LOG100:
1682 case GST_VIDEO_TRANSFER_LOG316:
1683 break;
1684 case GST_VIDEO_TRANSFER_SMPTE2084:
1685 transfer = QVideoFrameFormat::ColorTransfer_ST2084;
1686 break;
1687 case GST_VIDEO_TRANSFER_ARIB_STD_B67:
1688 transfer = QVideoFrameFormat::ColorTransfer_STD_B67;
1689 break;
1690 case GST_VIDEO_TRANSFER_BT2020_10:
1691 transfer = QVideoFrameFormat::ColorTransfer_BT709;
1692 break;
1693 case GST_VIDEO_TRANSFER_BT601:
1694 transfer = QVideoFrameFormat::ColorTransfer_BT601;
1695 break;
1696 }
1697 format.setColorTransfer(transfer);
1698
1699 return format;
1700}
1701
1703{
1704 Q_ASSERT(buffer);
1705
1707
1708 [[maybe_unused]] GstMemory *mem = gst_buffer_peek_memory(buffer, 0);
1709
1710#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
1711 if (gst_is_dmabuf_memory(mem))
1712 memoryFormat = QGstCaps::DMABuf;
1713#endif
1714#if QT_CONFIG(gstreamer_gl)
1715 if (gst_is_gl_memory(mem))
1716 memoryFormat = QGstCaps::GLTexture;
1717#endif
1718 return memoryFormat;
1719}
1720
1721QVideoFrame qCreateFrameFromGstBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo)
1722{
1723 auto format = qVideoFrameFormatFromGstVideoInfo(videoInfo);
1724 auto videoBuffer = std::make_unique<QGstVideoBuffer>(buffer, videoInfo, format);
1725 return QVideoFramePrivate::createFrame(std::move(videoBuffer), format);
1726}
1727
1728QT_END_NAMESPACE
QGObjectHandlerConnection(QGstObject object, gulong handler)
Definition qgst.cpp:805
QGObjectHandlerScopedConnection(QGObjectHandlerConnection connection)
Definition qgst.cpp:822
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:1561
void setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data, GDestroyNotify notify)
Definition qgst.cpp:1555
GstAppSink * appSink() const
Definition qgst.cpp:1533
void setCaps(const QGstCaps &caps)
Definition qgst.cpp:1550
void setMaxBuffers(int)
Definition qgst.cpp:1545
static QGstAppSink create(const char *name)
Definition qgst.cpp:1524
QGstAppSink(GstAppSink *, RefMode)
Definition qgst.cpp:1516
static QGstAppSrc create(const char *name)
Definition qgst.cpp:1579
GstFlowReturn pushBuffer(GstBuffer *)
Definition qgst.cpp:1599
void setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data, GDestroyNotify notify)
Definition qgst.cpp:1593
QGstAppSrc(GstAppSrc *, RefMode)
Definition qgst.cpp:1571
GstAppSrc * appSrc() const
Definition qgst.cpp:1588
void setSync(bool)
Definition qgst.cpp:1489
QGstBaseSink(GstBaseSink *, RefMode)
Definition qgst.cpp:1481
GstBaseSink * baseSink() const
Definition qgst.cpp:1494
QGstBaseSrc(GstBaseSrc *, RefMode)
Definition qgst.cpp:1501
GstBaseSrc * baseSrc() const
Definition qgst.cpp:1509
void addUnlinkedGhostPads(GstPadDirection)
Definition qgst.cpp:1433
void dumpGraph(const char *fileNamePrefix, bool includeTimestamp=true) const
Definition qgst.cpp:1455
QGstElement findByName(const char *)
Definition qgst.cpp:1466
void recalculateLatency()
Definition qgst.cpp:1474
GstBin * bin() const
Definition qgst.cpp:1418
static QGstBin create(const char *name)
Definition qgst.cpp:1365
void addGhostPad(const QGstElement &child, const char *name)
Definition qgst.cpp:1423
static QGstBin createFromPipelineDescription(const char *pipelineDescription, const char *name=nullptr, bool ghostUnlinkedPads=false)
Definition qgst.cpp:1386
static QGstBin createFromFactory(const char *factory, const char *name)
Definition qgst.cpp:1370
void addGhostPad(const char *name, const QGstPad &pad)
Definition qgst.cpp:1428
bool syncChildrenState()
Definition qgst.cpp:1450
QGstBin(GstBin *bin, RefMode mode=NeedsRef)
Definition qgst.cpp:1410
MemoryFormat memoryFormat() const
Definition qgst.cpp:606
std::optional< QGstVideoInfo > videoInfo() const
Definition qgst.cpp:465
int size() const
Definition qgst.cpp:616
static QGstCaps fromCameraFormat(const QCameraFormat &format)
Definition qgst.cpp:541
static QGstCaps create()
Definition qgst.cpp:633
GstCaps * caps() const
Definition qgst.cpp:628
void setResolution(QSize)
Definition qgst.cpp:527
MemoryFormat
Definition qgst_p.h:394
@ DMABuf
Definition qgst_p.h:394
@ CpuMemory
Definition qgst_p.h:394
@ GLTexture
Definition qgst_p.h:394
QGstCaps copy() const
Definition qgst.cpp:598
void addPixelFormats(const QList< QVideoFrameFormat::PixelFormat > &formats, const char *capsFeatures=nullptr)
Definition qgst.cpp:487
QGstStructureView at(int index) const
Definition qgst.cpp:621
QGstClock(GstClock *clock, RefMode mode)
Definition qgst.cpp:983
GstClockTime time() const
Definition qgst.cpp:996
GstClock * clock() const
Definition qgst.cpp:991
QGstClock(const QGstObject &o)
Definition qgst.cpp:975
void setBaseTime(GstClockTime time) const
Definition qgst.cpp:1278
GstStateChangeReturn setState(GstState state)
Definition qgst.cpp:1137
std::optional< std::chrono::nanoseconds > position() const
Definition qgst.cpp:1234
GstElement * element() const
Definition qgst.cpp:1283
void removeFromParent()
Definition qgst.cpp:1338
QGstElement(GstElement *element, RefMode mode)
Definition qgst.cpp:1003
void lockState(bool locked)
Definition qgst.cpp:1195
QGstBin getRootBin() const
Definition qgst.cpp:1304
QGstPad getRequestPad(const char *name) const
Definition qgst.cpp:1112
std::optional< bool > canSeek() const
Definition qgst.cpp:1257
std::optional< std::chrono::nanoseconds > duration() const
Definition qgst.cpp:1215
bool isStateLocked() const
Definition qgst.cpp:1200
void sendEvent(GstEvent *event) const
Definition qgst.cpp:1205
void dumpPipelineGraph(const char *filename) const
Definition qgst.cpp:1344
GstClockTime baseTime() const
Definition qgst.cpp:1273
QByteArrayView factoryName() const
Definition qgst.cpp:1087
void releaseRequestPad(const QGstPad &pad) const
Definition qgst.cpp:1117
bool syncStateWithParent()
Definition qgst.cpp:1155
QGstPad sink() const
Definition qgst.cpp:1107
QGstPad staticPad(const char *name) const
Definition qgst.cpp:1097
void sendEos() const
Definition qgst.cpp:1210
QGstBin getParentBin() const
Definition qgst.cpp:1296
std::optional< std::chrono::milliseconds > positionInMs() const
Definition qgst.cpp:1248
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:1011
QGstElement getParent() const
Definition qgst.cpp:1288
QGstPipeline getPipeline() const
Definition qgst.cpp:1324
static QGstElement createFromPipelineDescription(const char *)
Definition qgst.cpp:1055
QGstPad src() const
Definition qgst.cpp:1102
std::optional< std::chrono::milliseconds > durationInMs() const
Definition qgst.cpp:1225
void set(const char *property, double d)
Definition qgst.cpp:670
QGObjectHandlerConnection connect(const char *name, GCallback callback, gpointer userData)
Definition qgst.cpp:765
void * getObject(const char *property) const
Definition qgst.cpp:760
double getDouble(const char *property) const
Definition qgst.cpp:746
int getInt(const char *property) const
Definition qgst.cpp:718
void set(const char *property, int32_t i)
Definition qgst.cpp:655
QLatin1StringView name() const
Definition qgst.cpp:796
void set(const char *property, const QGstObject &o)
Definition qgst.cpp:675
QGString getString(const char *property) const
Definition qgst.cpp:690
void disconnect(gulong handlerId)
Definition qgst.cpp:774
QGstObject getGstObject(const char *property) const
Definition qgst.cpp:753
QLatin1StringView typeName() const
Definition qgst.cpp:784
void set(const char *property, const char *str)
Definition qgst.cpp:640
bool getBool(const char *property) const
Definition qgst.cpp:704
GType type() const
Definition qgst.cpp:779
QGstObject & operator=(QGstObject &&) noexcept=default
void set(const char *property, void *object, GDestroyNotify destroyFunction)
Definition qgst.cpp:685
void set(const char *property, bool b)
Definition qgst.cpp:645
quint64 getUInt64(const char *property) const
Definition qgst.cpp:725
QGstStructureView getStructure(const char *property) const
Definition qgst.cpp:697
qint64 getInt64(const char *property) const
Definition qgst.cpp:732
void set(const char *property, const QGstCaps &c)
Definition qgst.cpp:680
float getFloat(const char *property) const
Definition qgst.cpp:739
uint getUInt(const char *property) const
Definition qgst.cpp:711
GstObject * object() const
Definition qgst.cpp:791
bool link(const QGstPad &sink) const
Definition qgst.cpp:902
GstEvent * stickyEvent(GstEventType type)
Definition qgst.cpp:936
QGstTagListHandle tags() const
Definition qgst.cpp:868
QGstPad(const QGstObject &o)
Definition qgst.cpp:842
bool isLinked() const
Definition qgst.cpp:897
bool unlinkPeer() const
Definition qgst.cpp:912
QGstCaps queryCaps() const
Definition qgst.cpp:863
GstPad * pad() const
Definition qgst.cpp:931
QGstPad(GstPad *pad, RefMode mode)
Definition qgst.cpp:850
void sendFlushIfPaused()
Definition qgst.cpp:961
void sendFlushStartStop(bool resetTime)
Definition qgst.cpp:946
QGString streamId() const
Definition qgst.cpp:875
std::optional< QPlatformMediaPlayer::TrackType > inferTrackTypeFromName() const
Definition qgst.cpp:882
bool sendEvent(GstEvent *event)
Definition qgst.cpp:941
QGstCaps currentCaps() const
Definition qgst.cpp:858
QGstElement parent() const
Definition qgst.cpp:926
bool unlink(const QGstPad &sink) const
Definition qgst.cpp:907
QGstPad peer() const
Definition qgst.cpp:921
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:1702
QVideoFrameFormat qVideoFrameFormatFromGstVideoInfo(const QGstVideoInfo &vidInfo)
Definition qgst.cpp:1610
#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:1604
QVideoFrame qCreateFrameFromGstBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo)
Definition qgst.cpp:1721
QVideoFrameFormat::PixelFormat qPixelFormatFromGstVideoFormat(GstVideoFormat format)
Definition qgst.cpp:113
void setInt(const char *field, int value)
Definition qgst_p.h:203
void setIntRange(const char *field, int min, int max)
Definition qgst_p.h:213
void setString(const char *field, const char *value)
Definition qgst_p.h:208
QGstStructure(const char *name)
Definition qgst_p.h:198
QVideoFrameFormat::PixelFormat pixelFormat
Definition qgst.cpp:34