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 using TrackType = QPlatformMediaPlayer::TrackType;
886
887 QLatin1StringView padName = name();
888
889 if (padName.startsWith("video_"_L1))
890 return TrackType::VideoStream;
891 if (padName.startsWith("audio_"_L1))
892 return TrackType::AudioStream;
893 if (padName.startsWith("text_"_L1))
894 return TrackType::SubtitleStream;
895
896 return std::nullopt;
897}
898
899bool QGstPad::isLinked() const
900{
901 return gst_pad_is_linked(pad());
902}
903
904bool QGstPad::link(const QGstPad &sink) const
905{
906 return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK;
907}
908
909bool QGstPad::unlink(const QGstPad &sink) const
910{
911 return gst_pad_unlink(pad(), sink.pad());
912}
913
914bool QGstPad::unlinkPeer() const
915{
916 QGstPad peerPad = peer();
917 if (peerPad)
918 return GST_PAD_IS_SRC(pad()) ? unlink(peerPad) : peerPad.unlink(*this);
919
920 return true;
921}
922
924{
925 return QGstPad(gst_pad_get_peer(pad()), HasRef);
926}
927
929{
930 return QGstElement(gst_pad_get_parent_element(pad()), HasRef);
931}
932
934{
935 return qGstCheckedCast<GstPad>(object());
936}
938GstEvent *QGstPad::stickyEvent(GstEventType type)
939{
940 return gst_pad_get_sticky_event(pad(), type, 0);
941}
942
943bool QGstPad::sendEvent(GstEvent *event)
944{
945 return gst_pad_send_event(pad(), event);
946}
947
948void QGstPad::sendFlushStartStop(bool resetTime)
949{
950 GstEvent *flushStart = gst_event_new_flush_start();
951 gboolean ret = sendEvent(flushStart);
952 if (!ret) {
953 qWarning("failed to send flush-start event");
954 return;
955 }
956
957 GstEvent *flushStop = gst_event_new_flush_stop(resetTime);
958 ret = sendEvent(flushStop);
959 if (!ret)
960 qWarning("failed to send flush-stop event");
961}
962
964{
965 using namespace std::chrono_literals;
966
967 GstState state = parent().state(1s);
968
969 if (state != GST_STATE_PAUSED)
970 return;
971
972 sendFlushStartStop(/*resetTime=*/true);
973}
974
975// QGstClock
976
978 : QGstClock{
981 }
982{
983}
984
985QGstClock::QGstClock(GstClock *clock, RefMode mode)
986 : QGstObject{
988 mode,
989 }
990{
991}
992
994{
995 return qGstCheckedCast<GstClock>(object());
996}
997
999{
1000 return gst_clock_get_time(clock());
1001}
1002
1003// QGstElement
1004
1005QGstElement::QGstElement(GstElement *element, RefMode mode)
1006 : QGstObject{
1008 mode,
1009 }
1010{
1011}
1012
1013QGstElement QGstElement::createFromFactory(const char *factory, const char *name)
1014{
1015 GstElement *element = gst_element_factory_make(factory, name);
1016
1017#ifndef QT_NO_DEBUG
1018 if (!element) {
1019 qWarning() << "Failed to make element" << name << "from factory" << factory;
1020 return QGstElement{};
1021 }
1022#endif
1023
1024 return QGstElement{
1025 element,
1026 NeedsRef,
1027 };
1028}
1029
1030QGstElement QGstElement::createFromFactory(GstElementFactory *factory, const char *name)
1031{
1032 return QGstElement{
1033 gst_element_factory_create(factory, name),
1034 NeedsRef,
1035 };
1036}
1037
1038QGstElement QGstElement::createFromFactory(const QGstElementFactoryHandle &factory,
1039 const char *name)
1040{
1041 return createFromFactory(factory.get(), name);
1042}
1043
1044QGstElement QGstElement::createFromDevice(const QGstDeviceHandle &device, const char *name)
1045{
1046 return createFromDevice(device.get(), name);
1047}
1048
1049QGstElement QGstElement::createFromDevice(GstDevice *device, const char *name)
1050{
1051 return QGstElement{
1052 gst_device_create_element(device, name),
1053 QGstElement::NeedsRef,
1054 };
1055}
1056
1058{
1059 QUniqueGErrorHandle error;
1060 QGstElement element{
1061 gst_parse_launch(str, &error),
1062 QGstElement::NeedsRef,
1063 };
1064
1065 if (error) // error does not mean that the element could not be constructed
1066 qWarning() << "gst_parse_launch error:" << error;
1067
1068 return element;
1069}
1070
1071QGstElement QGstElement::createFromPipelineDescription(const QByteArray &str)
1072{
1073 return createFromPipelineDescription(str.constData());
1074}
1075
1076QGstElementFactoryHandle QGstElement::findFactory(const char *name)
1077{
1078 return QGstElementFactoryHandle{
1079 gst_element_factory_find(name),
1080 QGstElementFactoryHandle::HasRef,
1081 };
1082}
1083
1084QGstElementFactoryHandle QGstElement::findFactory(const QByteArray &name)
1085{
1086 return findFactory(name.constData());
1087}
1088
1090{
1091 GstElementFactory *factory = gst_element_get_factory(element());
1092 if (!factory)
1093 return {};
1094
1095 const char *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
1096 return name ? QByteArrayView{name} : QByteArrayView{};
1097}
1098
1099QGstPad QGstElement::staticPad(const char *name) const
1100{
1101 return QGstPad(gst_element_get_static_pad(element(), name), HasRef);
1102}
1103
1105{
1106 return staticPad("src");
1107}
1108
1110{
1111 return staticPad("sink");
1112}
1113
1114QGstPad QGstElement::getRequestPad(const char *name) const
1115{
1116 return QGstPad(gst_element_request_pad_simple(element(), name), HasRef);
1117}
1118
1120{
1121 return gst_element_release_request_pad(element(), pad.pad());
1122}
1123
1124GstState QGstElement::state(std::chrono::nanoseconds timeout) const
1125{
1126 using namespace std::chrono_literals;
1127
1128 GstState state;
1129 GstStateChangeReturn change =
1130 gst_element_get_state(element(), &state, nullptr, timeout.count());
1131
1132 if (Q_UNLIKELY(change == GST_STATE_CHANGE_ASYNC))
1133 qWarning() << "QGstElement::state detected an asynchronous state change. Return value not "
1134 "reliable";
1135
1136 return state;
1137}
1138
1140{
1141 return gst_element_set_state(element(), state);
1142}
1143
1144bool QGstElement::setStateSync(GstState state, std::chrono::nanoseconds timeout)
1145{
1146 GstStateChangeReturn change = gst_element_set_state(element(), state);
1147 if (change == GST_STATE_CHANGE_ASYNC)
1148 change = gst_element_get_state(element(), nullptr, &state, timeout.count());
1149
1150 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
1151 qWarning() << "Could not change state of" << name() << "to" << state << change;
1152 dumpPipelineGraph("setStateSyncFailure");
1153 }
1154 return change == GST_STATE_CHANGE_SUCCESS || change == GST_STATE_CHANGE_NO_PREROLL;
1155}
1156
1158{
1159 Q_ASSERT(element());
1160 return gst_element_sync_state_with_parent(element()) == TRUE;
1161}
1162
1163bool QGstElement::finishStateChange(std::chrono::nanoseconds timeout)
1164{
1165 GstState state, pending;
1166 GstStateChangeReturn change =
1167 gst_element_get_state(element(), &state, &pending, timeout.count());
1168
1169 if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
1170 qWarning() << "Could not finish change state of" << name() << change << state << pending;
1171 dumpPipelineGraph("finishStateChangeFailure");
1172 }
1173 return change == GST_STATE_CHANGE_SUCCESS;
1174}
1175
1176bool QGstElement::hasAsyncStateChange(std::chrono::nanoseconds timeout) const
1177{
1178 GstState state;
1179 GstStateChangeReturn change =
1180 gst_element_get_state(element(), &state, nullptr, timeout.count());
1181 return change == GST_STATE_CHANGE_ASYNC;
1182}
1183
1184bool QGstElement::waitForAsyncStateChangeComplete(std::chrono::nanoseconds timeout) const
1185{
1186 using namespace std::chrono_literals;
1187 for (;;) {
1188 if (!hasAsyncStateChange())
1189 return true;
1190 timeout -= 10ms;
1191 if (timeout < 0ms)
1192 return false;
1193 std::this_thread::sleep_for(10ms);
1194 }
1195}
1196
1197void QGstElement::lockState(bool locked)
1198{
1199 gst_element_set_locked_state(element(), locked);
1200}
1201
1203{
1204 return gst_element_is_locked_state(element());
1205}
1206
1207void QGstElement::sendEvent(GstEvent *event) const
1208{
1209 gst_element_send_event(element(), event);
1210}
1211
1213{
1214 sendEvent(gst_event_new_eos());
1215}
1216
1217std::optional<std::chrono::nanoseconds> QGstElement::duration() const
1218{
1219 gint64 d;
1220 if (!gst_element_query_duration(element(), GST_FORMAT_TIME, &d)) {
1221 qDebug() << "QGstElement: failed to query duration";
1222 return std::nullopt;
1223 }
1224 return std::chrono::nanoseconds{ d };
1225}
1226
1227std::optional<std::chrono::milliseconds> QGstElement::durationInMs() const
1228{
1229 using namespace std::chrono;
1230 auto dur = duration();
1231 if (dur)
1232 return round<milliseconds>(*dur);
1233 return std::nullopt;
1234}
1235
1236std::optional<std::chrono::nanoseconds> QGstElement::position() const
1237{
1238 QGstQueryHandle &query = positionQuery();
1239
1240 gint64 pos;
1241 if (gst_element_query(element(), query.get())) {
1242 gst_query_parse_position(query.get(), nullptr, &pos);
1243 return std::chrono::nanoseconds{ pos };
1244 }
1245
1246 qDebug() << "QGstElement: failed to query position";
1247 return std::nullopt;
1248}
1249
1250std::optional<std::chrono::milliseconds> QGstElement::positionInMs() const
1251{
1252 using namespace std::chrono;
1253 auto pos = position();
1254 if (pos)
1255 return round<milliseconds>(*pos);
1256 return std::nullopt;
1257}
1258
1259std::optional<bool> QGstElement::canSeek() const
1260{
1261 QGstQueryHandle query{
1262 gst_query_new_seeking(GST_FORMAT_TIME),
1263 QGstQueryHandle::HasRef,
1264 };
1265 gboolean canSeek = false;
1266 gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
1267
1268 if (gst_element_query(element(), query.get())) {
1269 gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
1270 return canSeek;
1271 }
1272 return std::nullopt;
1273}
1274
1276{
1277 return gst_element_get_base_time(element());
1278}
1279
1280void QGstElement::setBaseTime(GstClockTime time) const
1281{
1282 gst_element_set_base_time(element(), time);
1283}
1284
1286{
1287 return GST_ELEMENT_CAST(get());
1288}
1289
1291{
1292 return QGstElement{
1293 qGstCheckedCast<GstElement>(gst_element_get_parent(object())),
1294 QGstElement::HasRef,
1295 };
1296}
1297
1299{
1300 return QGstBin{
1301 qGstCheckedCast<GstBin>(gst_element_get_parent(object())),
1302 QGstElement::HasRef,
1303 };
1304}
1305
1307{
1308 QGstElement ancestor = *this;
1309 for (;;) {
1310 QGstElement greatAncestor = ancestor.getParent();
1311 if (greatAncestor) {
1312 ancestor = std::move(greatAncestor);
1313 continue;
1314 }
1315 if (GST_IS_BIN(ancestor.element())) {
1316 return QGstBin{
1317 qGstSafeCast<GstBin>(ancestor.element()),
1318 QGstBin::NeedsRef,
1319 };
1320 } else {
1321 return QGstBin{};
1322 }
1323 }
1324}
1325
1327{
1328 QGstBin rootBin = getRootBin();
1329 if (GST_IS_PIPELINE(rootBin.get())) {
1330 return QGstPipeline{
1331 qGstSafeCast<GstPipeline>(rootBin.element()),
1332 QGstPipeline::NeedsRef,
1333 };
1334 } else {
1335 qWarning() << "QGstElement::getPipeline failed for element:" << *this;
1336 return QGstPipeline{};
1337 }
1338}
1339
1341{
1342 if (QGstBin parent = getParentBin())
1343 parent.remove(*this);
1344}
1345
1346void QGstElement::dumpPipelineGraph(const char *filename) const
1347{
1348 static const bool dumpEnabled = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR");
1349 if (dumpEnabled) {
1351 }
1352}
1353
1354QGstQueryHandle &QGstElement::positionQuery() const
1355{
1356 if (Q_UNLIKELY(!m_positionQuery))
1357 m_positionQuery = QGstQueryHandle{
1358 gst_query_new_position(GST_FORMAT_TIME),
1359 QGstQueryHandle::HasRef,
1360 };
1361
1362 return m_positionQuery;
1363}
1364
1365// QGstBin
1366
1367QGstBin QGstBin::create(const char *name)
1368{
1369 return QGstBin(gst_bin_new(name), NeedsRef);
1370}
1371
1372QGstBin QGstBin::createFromFactory(const char *factory, const char *name)
1373{
1374 QGstElement element = QGstElement::createFromFactory(factory, name);
1375 Q_ASSERT(GST_IS_BIN(element.element()));
1376 return QGstBin{
1377 GST_BIN(element.release()),
1378 RefMode::HasRef,
1379 };
1380}
1381
1382QGstBin QGstBin::createFromPipelineDescription(const QByteArray &pipelineDescription,
1383 const char *name, bool ghostUnlinkedPads)
1384{
1385 return createFromPipelineDescription(pipelineDescription.constData(), name, ghostUnlinkedPads);
1386}
1387
1388QGstBin QGstBin::createFromPipelineDescription(const char *pipelineDescription, const char *name,
1389 bool ghostUnlinkedPads)
1390{
1391 QUniqueGErrorHandle error;
1392
1393 GstElement *element =
1394 gst_parse_bin_from_description_full(pipelineDescription, ghostUnlinkedPads,
1395 /*context=*/nullptr, GST_PARSE_FLAG_NONE, &error);
1396
1397 if (!element) {
1398 qWarning() << "Failed to make element from pipeline description" << pipelineDescription
1399 << error;
1400 return QGstBin{};
1401 }
1402
1403 if (name)
1404 gst_element_set_name(element, name);
1405
1406 return QGstBin{
1407 element,
1408 NeedsRef,
1409 };
1410}
1411
1412QGstBin::QGstBin(GstBin *bin, RefMode mode)
1413 : QGstElement{
1415 mode,
1416 }
1417{
1418}
1419
1421{
1422 return qGstCheckedCast<GstBin>(object());
1423}
1424
1425void QGstBin::addGhostPad(const QGstElement &child, const char *name)
1426{
1427 addGhostPad(name, child.staticPad(name));
1428}
1429
1430void QGstBin::addGhostPad(const char *name, const QGstPad &pad)
1431{
1432 gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad()));
1433}
1434
1435void QGstBin::addUnlinkedGhostPads(GstPadDirection direction)
1436{
1437 Q_ASSERT(direction != GstPadDirection::GST_PAD_UNKNOWN);
1438
1439 for (;;) {
1440 QGstPad unlinkedPad{
1441 gst_bin_find_unlinked_pad(bin(), direction),
1442 QGstPad::HasRef,
1443 };
1444
1445 if (!unlinkedPad)
1446 return;
1447
1448 addGhostPad(unlinkedPad.name().constData(), unlinkedPad);
1449 }
1450}
1451
1453{
1454 return gst_bin_sync_children_states(bin());
1455}
1456
1457void QGstBin::dumpGraph(const char *fileNamePrefix, bool includeTimestamp) const
1458{
1459 if (!get())
1460 return;
1461
1462 if (includeTimestamp)
1463 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(bin(), GST_DEBUG_GRAPH_SHOW_VERBOSE, fileNamePrefix);
1464 else
1465 GST_DEBUG_BIN_TO_DOT_FILE(bin(), GST_DEBUG_GRAPH_SHOW_VERBOSE, fileNamePrefix);
1466}
1467
1469{
1470 return QGstElement{
1471 gst_bin_get_by_name(bin(), name),
1472 QGstElement::NeedsRef,
1473 };
1474}
1475
1477{
1478 gst_bin_recalculate_latency(bin());
1479}
1480
1481// QGstBaseSink
1482
1483QGstBaseSink::QGstBaseSink(GstBaseSink *element, RefMode mode)
1484 : QGstElement{
1486 mode,
1487 }
1488{
1489}
1490
1491void QGstBaseSink::setSync(bool arg)
1492{
1493 gst_base_sink_set_sync(baseSink(), arg ? TRUE : FALSE);
1494}
1495
1497{
1498 return qGstCheckedCast<GstBaseSink>(element());
1499}
1500
1501// QGstBaseSrc
1502
1503QGstBaseSrc::QGstBaseSrc(GstBaseSrc *element, RefMode mode)
1504 : QGstElement{
1506 mode,
1507 }
1508{
1509}
1510
1512{
1513 return qGstCheckedCast<GstBaseSrc>(element());
1514}
1515
1516// QGstAppSink
1517
1518QGstAppSink::QGstAppSink(GstAppSink *element, RefMode mode)
1519 : QGstBaseSink{
1521 mode,
1522 }
1523{
1524}
1525
1527{
1528 QGstElement created = QGstElement::createFromFactory("appsink", name);
1529 return QGstAppSink{
1530 qGstCheckedCast<GstAppSink>(created.element()),
1531 QGstAppSink::NeedsRef,
1532 };
1533}
1534
1536{
1537 return qGstCheckedCast<GstAppSink>(element());
1538}
1539
1540# if GST_CHECK_VERSION(1, 24, 0)
1541void QGstAppSink::setMaxBufferTime(std::chrono::nanoseconds ns)
1542{
1543 gst_app_sink_set_max_time(appSink(), qGstClockTimeFromChrono(ns));
1544}
1545# endif
1546
1548{
1549 gst_app_sink_set_max_buffers(appSink(), n);
1550}
1551
1552void QGstAppSink::setCaps(const QGstCaps &caps)
1553{
1554 gst_app_sink_set_caps(appSink(), caps.caps());
1555}
1556
1557void QGstAppSink::setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data,
1558 GDestroyNotify notify)
1559{
1560 gst_app_sink_set_callbacks(appSink(), &callbacks, user_data, notify);
1561}
1562
1564{
1565 return QGstSampleHandle{
1566 gst_app_sink_pull_sample(appSink()),
1567 QGstSampleHandle::HasRef,
1568 };
1569}
1570
1571// QGstAppSrc
1572
1573QGstAppSrc::QGstAppSrc(GstAppSrc *element, RefMode mode)
1574 : QGstBaseSrc{
1576 mode,
1577 }
1578{
1579}
1580
1581QGstAppSrc QGstAppSrc::create(const char *name)
1582{
1583 QGstElement created = QGstElement::createFromFactory("appsrc", name);
1584 return QGstAppSrc{
1585 qGstCheckedCast<GstAppSrc>(created.element()),
1586 QGstAppSrc::NeedsRef,
1587 };
1588}
1589
1591{
1592 return qGstCheckedCast<GstAppSrc>(element());
1593}
1594
1595void QGstAppSrc::setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data,
1596 GDestroyNotify notify)
1597{
1598 gst_app_src_set_callbacks(appSrc(), &callbacks, user_data, notify);
1599}
1600
1602{
1603 return gst_app_src_push_buffer(appSrc(), buffer);
1604}
1605
1607{
1608 return QStringLiteral("Could not find the %1 GStreamer element")
1609 .arg(QLatin1StringView(element));
1610}
1611
1612QVideoFrameFormat qVideoFrameFormatFromGstVideoInfo(const QGstVideoInfo &qtVideoInfo)
1613{
1614 auto &vidInfo = qtVideoInfo.gstVideoInfo;
1615 GstVideoFormat gstFormat = GST_VIDEO_INFO_FORMAT(&vidInfo);
1616
1617 auto pixelFormat = qPixelFormatFromGstVideoFormat(gstFormat);
1618 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
1619 return QVideoFrameFormat();
1620
1621 QVideoFrameFormat format(QSize(vidInfo.width, vidInfo.height), pixelFormat);
1622
1623 if (vidInfo.fps_d > 0)
1624 format.setStreamFrameRate(qreal(vidInfo.fps_n) / vidInfo.fps_d);
1625
1626 QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown;
1627 switch (vidInfo.colorimetry.range) {
1628 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
1629 break;
1630 case GST_VIDEO_COLOR_RANGE_0_255:
1631 range = QVideoFrameFormat::ColorRange_Full;
1632 break;
1633 case GST_VIDEO_COLOR_RANGE_16_235:
1634 range = QVideoFrameFormat::ColorRange_Video;
1635 break;
1636 }
1637 format.setColorRange(range);
1638
1639 QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
1640 switch (vidInfo.colorimetry.matrix) {
1641 case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
1642 case GST_VIDEO_COLOR_MATRIX_RGB:
1643 case GST_VIDEO_COLOR_MATRIX_FCC:
1644 break;
1645 case GST_VIDEO_COLOR_MATRIX_BT709:
1646 colorSpace = QVideoFrameFormat::ColorSpace_BT709;
1647 break;
1648 case GST_VIDEO_COLOR_MATRIX_BT601:
1649 colorSpace = QVideoFrameFormat::ColorSpace_BT601;
1650 break;
1651 case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
1652 colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
1653 break;
1654 case GST_VIDEO_COLOR_MATRIX_BT2020:
1655 colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
1656 break;
1657 }
1658 format.setColorSpace(colorSpace);
1659
1660 QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown;
1661 switch (vidInfo.colorimetry.transfer) {
1662 case GST_VIDEO_TRANSFER_UNKNOWN:
1663 break;
1664 case GST_VIDEO_TRANSFER_GAMMA10:
1665 transfer = QVideoFrameFormat::ColorTransfer_Linear;
1666 break;
1667 case GST_VIDEO_TRANSFER_GAMMA22:
1668 case GST_VIDEO_TRANSFER_SMPTE240M:
1669 case GST_VIDEO_TRANSFER_SRGB:
1670 case GST_VIDEO_TRANSFER_ADOBERGB:
1671 transfer = QVideoFrameFormat::ColorTransfer_Gamma22;
1672 break;
1673 case GST_VIDEO_TRANSFER_GAMMA18:
1674 case GST_VIDEO_TRANSFER_GAMMA20:
1675 // not quite, but best fit
1676 case GST_VIDEO_TRANSFER_BT709:
1677 case GST_VIDEO_TRANSFER_BT2020_12:
1678 transfer = QVideoFrameFormat::ColorTransfer_BT709;
1679 break;
1680 case GST_VIDEO_TRANSFER_GAMMA28:
1681 transfer = QVideoFrameFormat::ColorTransfer_Gamma28;
1682 break;
1683 case GST_VIDEO_TRANSFER_LOG100:
1684 case GST_VIDEO_TRANSFER_LOG316:
1685 break;
1686 case GST_VIDEO_TRANSFER_SMPTE2084:
1687 transfer = QVideoFrameFormat::ColorTransfer_ST2084;
1688 break;
1689 case GST_VIDEO_TRANSFER_ARIB_STD_B67:
1690 transfer = QVideoFrameFormat::ColorTransfer_STD_B67;
1691 break;
1692 case GST_VIDEO_TRANSFER_BT2020_10:
1693 transfer = QVideoFrameFormat::ColorTransfer_BT709;
1694 break;
1695 case GST_VIDEO_TRANSFER_BT601:
1696 transfer = QVideoFrameFormat::ColorTransfer_BT601;
1697 break;
1698 }
1699 format.setColorTransfer(transfer);
1700
1701 return format;
1702}
1703
1705{
1706 Q_ASSERT(buffer);
1707
1709
1710 [[maybe_unused]] GstMemory *mem = gst_buffer_peek_memory(buffer, 0);
1711
1712#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
1713 if (gst_is_dmabuf_memory(mem))
1714 memoryFormat = QGstCaps::DMABuf;
1715#endif
1716#if QT_CONFIG(gstreamer_gl)
1717 if (gst_is_gl_memory(mem))
1718 memoryFormat = QGstCaps::GLTexture;
1719#endif
1720 return memoryFormat;
1721}
1722
1723QVideoFrame qCreateFrameFromGstBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo)
1724{
1725 auto format = qVideoFrameFormatFromGstVideoInfo(videoInfo);
1726 auto videoBuffer = std::make_unique<QGstVideoBuffer>(buffer, videoInfo, format);
1727 return QVideoFramePrivate::createFrame(std::move(videoBuffer), format);
1728}
1729
1730QT_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:1563
void setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data, GDestroyNotify notify)
Definition qgst.cpp:1557
GstAppSink * appSink() const
Definition qgst.cpp:1535
void setCaps(const QGstCaps &caps)
Definition qgst.cpp:1552
void setMaxBuffers(int)
Definition qgst.cpp:1547
static QGstAppSink create(const char *name)
Definition qgst.cpp:1526
QGstAppSink(GstAppSink *, RefMode)
Definition qgst.cpp:1518
static QGstAppSrc create(const char *name)
Definition qgst.cpp:1581
GstFlowReturn pushBuffer(GstBuffer *)
Definition qgst.cpp:1601
void setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data, GDestroyNotify notify)
Definition qgst.cpp:1595
QGstAppSrc(GstAppSrc *, RefMode)
Definition qgst.cpp:1573
GstAppSrc * appSrc() const
Definition qgst.cpp:1590
void setSync(bool)
Definition qgst.cpp:1491
QGstBaseSink(GstBaseSink *, RefMode)
Definition qgst.cpp:1483
GstBaseSink * baseSink() const
Definition qgst.cpp:1496
QGstBaseSrc(GstBaseSrc *, RefMode)
Definition qgst.cpp:1503
GstBaseSrc * baseSrc() const
Definition qgst.cpp:1511
void addUnlinkedGhostPads(GstPadDirection)
Definition qgst.cpp:1435
void dumpGraph(const char *fileNamePrefix, bool includeTimestamp=true) const
Definition qgst.cpp:1457
QGstElement findByName(const char *)
Definition qgst.cpp:1468
void recalculateLatency()
Definition qgst.cpp:1476
GstBin * bin() const
Definition qgst.cpp:1420
static QGstBin create(const char *name)
Definition qgst.cpp:1367
void addGhostPad(const QGstElement &child, const char *name)
Definition qgst.cpp:1425
static QGstBin createFromPipelineDescription(const char *pipelineDescription, const char *name=nullptr, bool ghostUnlinkedPads=false)
Definition qgst.cpp:1388
static QGstBin createFromFactory(const char *factory, const char *name)
Definition qgst.cpp:1372
void addGhostPad(const char *name, const QGstPad &pad)
Definition qgst.cpp:1430
bool syncChildrenState()
Definition qgst.cpp:1452
QGstBin(GstBin *bin, RefMode mode=NeedsRef)
Definition qgst.cpp:1412
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:985
GstClockTime time() const
Definition qgst.cpp:998
GstClock * clock() const
Definition qgst.cpp:993
QGstClock(const QGstObject &o)
Definition qgst.cpp:977
void setBaseTime(GstClockTime time) const
Definition qgst.cpp:1280
GstStateChangeReturn setState(GstState state)
Definition qgst.cpp:1139
std::optional< std::chrono::nanoseconds > position() const
Definition qgst.cpp:1236
GstElement * element() const
Definition qgst.cpp:1285
void removeFromParent()
Definition qgst.cpp:1340
QGstElement(GstElement *element, RefMode mode)
Definition qgst.cpp:1005
void lockState(bool locked)
Definition qgst.cpp:1197
QGstBin getRootBin() const
Definition qgst.cpp:1306
QGstPad getRequestPad(const char *name) const
Definition qgst.cpp:1114
std::optional< bool > canSeek() const
Definition qgst.cpp:1259
std::optional< std::chrono::nanoseconds > duration() const
Definition qgst.cpp:1217
bool isStateLocked() const
Definition qgst.cpp:1202
void sendEvent(GstEvent *event) const
Definition qgst.cpp:1207
void dumpPipelineGraph(const char *filename) const
Definition qgst.cpp:1346
GstClockTime baseTime() const
Definition qgst.cpp:1275
QByteArrayView factoryName() const
Definition qgst.cpp:1089
void releaseRequestPad(const QGstPad &pad) const
Definition qgst.cpp:1119
bool syncStateWithParent()
Definition qgst.cpp:1157
QGstPad sink() const
Definition qgst.cpp:1109
QGstPad staticPad(const char *name) const
Definition qgst.cpp:1099
void sendEos() const
Definition qgst.cpp:1212
QGstBin getParentBin() const
Definition qgst.cpp:1298
std::optional< std::chrono::milliseconds > positionInMs() const
Definition qgst.cpp:1250
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:1013
QGstElement getParent() const
Definition qgst.cpp:1290
QGstPipeline getPipeline() const
Definition qgst.cpp:1326
static QGstElement createFromPipelineDescription(const char *)
Definition qgst.cpp:1057
QGstPad src() const
Definition qgst.cpp:1104
std::optional< std::chrono::milliseconds > durationInMs() const
Definition qgst.cpp:1227
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:904
GstEvent * stickyEvent(GstEventType type)
Definition qgst.cpp:938
QGstTagListHandle tags() const
Definition qgst.cpp:868
QGstPad(const QGstObject &o)
Definition qgst.cpp:842
bool isLinked() const
Definition qgst.cpp:899
bool unlinkPeer() const
Definition qgst.cpp:914
QGstCaps queryCaps() const
Definition qgst.cpp:863
GstPad * pad() const
Definition qgst.cpp:933
QGstPad(GstPad *pad, RefMode mode)
Definition qgst.cpp:850
void sendFlushIfPaused()
Definition qgst.cpp:963
void sendFlushStartStop(bool resetTime)
Definition qgst.cpp:948
QGString streamId() const
Definition qgst.cpp:875
std::optional< QPlatformMediaPlayer::TrackType > inferTrackTypeFromName() const
Definition qgst.cpp:882
bool sendEvent(GstEvent *event)
Definition qgst.cpp:943
QGstCaps currentCaps() const
Definition qgst.cpp:858
QGstElement parent() const
Definition qgst.cpp:928
bool unlink(const QGstPad &sink) const
Definition qgst.cpp:909
QGstPad peer() const
Definition qgst.cpp:923
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:1704
QVideoFrameFormat qVideoFrameFormatFromGstVideoInfo(const QGstVideoInfo &vidInfo)
Definition qgst.cpp:1612
#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:1606
QVideoFrame qCreateFrameFromGstBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo)
Definition qgst.cpp:1723
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