diff options
author | Tim Blechmann <tim@klingt.org> | 2024-03-05 10:06:15 +0800 |
---|---|---|
committer | Artem Dyomin <artem.dyomin@qt.io> | 2024-03-12 12:40:01 +0000 |
commit | 5a6f60417b827a9ffa14146567ce5a09ad84342a (patch) | |
tree | 42210a1e6a944a4132724b9abf2a6f6f64328562 | |
parent | 582ad5aa6cd650d90d3e3d74a882e3b5bb2c8ff2 (diff) |
GStreamer: cleanups - move implementations into cpp file
Some implementations of qgst_p.h were located in qgstutils.cpp. This
patch performs some cleanups:
* move all functionality related to `qgst_p.h` into `qgst.cpp`
* move all inline functions from `qgst_p.h` into `qgst.cpp`
* header streamlining
Pick-to: 6.5
Change-Id: I22b0ba4f23ad2f7f8579d7f61040b58a91c5cf43
Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
(cherry picked from commit 2e47c94382b174f30c3ec14de245fbb320b7a393)
-rw-r--r-- | src/plugins/multimedia/gstreamer/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/plugins/multimedia/gstreamer/common/qgst.cpp | 876 | ||||
-rw-r--r-- | src/plugins/multimedia/gstreamer/common/qgst_p.h | 411 | ||||
-rw-r--r-- | src/plugins/multimedia/gstreamer/common/qgstutils.cpp | 374 |
4 files changed, 983 insertions, 680 deletions
diff --git a/src/plugins/multimedia/gstreamer/CMakeLists.txt b/src/plugins/multimedia/gstreamer/CMakeLists.txt index 8945d253e..d2205ad85 100644 --- a/src/plugins/multimedia/gstreamer/CMakeLists.txt +++ b/src/plugins/multimedia/gstreamer/CMakeLists.txt @@ -11,7 +11,7 @@ qt_internal_add_plugin(QGstreamerMediaPlugin audio/qgstreameraudiosource.cpp audio/qgstreameraudiosource_p.h audio/qgstreameraudiosink.cpp audio/qgstreameraudiosink_p.h audio/qgstreameraudiodecoder.cpp audio/qgstreameraudiodecoder_p.h - common/qgst_p.h + common/qgst.cpp common/qgst_p.h common/qgst_debug.cpp common/qgst_debug_p.h common/qgst_handle_types_p.h common/qgstappsrc.cpp common/qgstappsrc_p.h diff --git a/src/plugins/multimedia/gstreamer/common/qgst.cpp b/src/plugins/multimedia/gstreamer/common/qgst.cpp new file mode 100644 index 000000000..002d5aaf7 --- /dev/null +++ b/src/plugins/multimedia/gstreamer/common/qgst.cpp @@ -0,0 +1,876 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <common/qgst_p.h> +#include <common/qgst_debug_p.h> +#include <common/qgstreamermessage_p.h> + +#include <QtCore/qdebug.h> +#include <QtMultimedia/qcameradevice.h> + +#include <array> + +QT_BEGIN_NAMESPACE + +namespace { + +struct VideoFormat +{ + QVideoFrameFormat::PixelFormat pixelFormat; + GstVideoFormat gstFormat; +}; + +constexpr std::array<VideoFormat, 19> qt_videoFormatLookup{ { + { QVideoFrameFormat::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, + { QVideoFrameFormat::Format_YUV422P, GST_VIDEO_FORMAT_Y42B }, + { QVideoFrameFormat::Format_YV12, GST_VIDEO_FORMAT_YV12 }, + { QVideoFrameFormat::Format_UYVY, GST_VIDEO_FORMAT_UYVY }, + { QVideoFrameFormat::Format_YUYV, GST_VIDEO_FORMAT_YUY2 }, + { QVideoFrameFormat::Format_NV12, GST_VIDEO_FORMAT_NV12 }, + { QVideoFrameFormat::Format_NV21, GST_VIDEO_FORMAT_NV21 }, + { QVideoFrameFormat::Format_AYUV, GST_VIDEO_FORMAT_AYUV }, + { QVideoFrameFormat::Format_Y8, GST_VIDEO_FORMAT_GRAY8 }, + { QVideoFrameFormat::Format_XRGB8888, GST_VIDEO_FORMAT_xRGB }, + { QVideoFrameFormat::Format_XBGR8888, GST_VIDEO_FORMAT_xBGR }, + { QVideoFrameFormat::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx }, + { QVideoFrameFormat::Format_BGRX8888, GST_VIDEO_FORMAT_BGRx }, + { QVideoFrameFormat::Format_ARGB8888, GST_VIDEO_FORMAT_ARGB }, + { QVideoFrameFormat::Format_ABGR8888, GST_VIDEO_FORMAT_ABGR }, + { QVideoFrameFormat::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, + { QVideoFrameFormat::Format_BGRA8888, GST_VIDEO_FORMAT_BGRA }, +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + { QVideoFrameFormat::Format_Y16, GST_VIDEO_FORMAT_GRAY16_LE }, + { QVideoFrameFormat::Format_P010, GST_VIDEO_FORMAT_P010_10LE }, +#else + { QVideoFrameFormat::Format_Y16, GST_VIDEO_FORMAT_GRAY16_BE }, + { QVideoFrameFormat::Format_P010, GST_VIDEO_FORMAT_P010_10BE }, +#endif +} }; + +int indexOfVideoFormat(QVideoFrameFormat::PixelFormat format) +{ + for (size_t i = 0; i < qt_videoFormatLookup.size(); ++i) + if (qt_videoFormatLookup[i].pixelFormat == format) + return int(i); + + return -1; +} + +int indexOfVideoFormat(GstVideoFormat format) +{ + for (size_t i = 0; i < qt_videoFormatLookup.size(); ++i) + if (qt_videoFormatLookup[i].gstFormat == format) + return int(i); + + return -1; +} + +} // namespace + +// QGValue + +QGValue::QGValue(const GValue *v) : value(v) { } + +bool QGValue::isNull() const +{ + return !value; +} + +std::optional<bool> QGValue::toBool() const +{ + if (!G_VALUE_HOLDS_BOOLEAN(value)) + return std::nullopt; + return g_value_get_boolean(value); +} + +std::optional<int> QGValue::toInt() const +{ + if (!G_VALUE_HOLDS_INT(value)) + return std::nullopt; + return g_value_get_int(value); +} + +std::optional<int> QGValue::toInt64() const +{ + if (!G_VALUE_HOLDS_INT64(value)) + return std::nullopt; + return g_value_get_int64(value); +} + +const char *QGValue::toString() const +{ + return value ? g_value_get_string(value) : nullptr; +} + +std::optional<float> QGValue::getFraction() const +{ + if (!GST_VALUE_HOLDS_FRACTION(value)) + return std::nullopt; + return (float)gst_value_get_fraction_numerator(value) + / (float)gst_value_get_fraction_denominator(value); +} + +std::optional<QGRange<float>> QGValue::getFractionRange() const +{ + if (!GST_VALUE_HOLDS_FRACTION_RANGE(value)) + return std::nullopt; + QGValue min = QGValue{ gst_value_get_fraction_range_min(value) }; + QGValue max = QGValue{ gst_value_get_fraction_range_max(value) }; + return QGRange<float>{ *min.getFraction(), *max.getFraction() }; +} + +std::optional<QGRange<int>> QGValue::toIntRange() const +{ + if (!GST_VALUE_HOLDS_INT_RANGE(value)) + return std::nullopt; + return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) }; +} + +QGstStructure QGValue::toStructure() const +{ + if (!value || !GST_VALUE_HOLDS_STRUCTURE(value)) + return QGstStructure(); + return QGstStructure(gst_value_get_structure(value)); +} + +QGstCaps QGValue::toCaps() const +{ + if (!value || !GST_VALUE_HOLDS_CAPS(value)) + return {}; + return QGstCaps(gst_caps_copy(gst_value_get_caps(value)), QGstCaps::HasRef); +} + +bool QGValue::isList() const +{ + return value && GST_VALUE_HOLDS_LIST(value); +} + +int QGValue::listSize() const +{ + return gst_value_list_get_size(value); +} + +QGValue QGValue::at(int index) const +{ + return QGValue{ gst_value_list_get_value(value, index) }; +} + +// QGstStructure + +QGstStructure::QGstStructure(const GstStructure *s) : structure(s) { } + +void QGstStructure::free() +{ + if (structure) + gst_structure_free(const_cast<GstStructure *>(structure)); + structure = nullptr; +} + +bool QGstStructure::isNull() const +{ + return !structure; +} + +QByteArrayView QGstStructure::name() const +{ + return gst_structure_get_name(structure); +} + +QGValue QGstStructure::operator[](const char *name) const +{ + return QGValue{ gst_structure_get_value(structure, name) }; +} + +QGstStructure QGstStructure::copy() const +{ + return gst_structure_copy(structure); +} + +QSize QGstStructure::resolution() const +{ + QSize size; + + int w, h; + if (structure && gst_structure_get_int(structure, "width", &w) + && gst_structure_get_int(structure, "height", &h)) { + size.rwidth() = w; + size.rheight() = h; + } + + return size; +} + +QVideoFrameFormat::PixelFormat QGstStructure::pixelFormat() const +{ + QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid; + + if (!structure) + return pixelFormat; + + if (gst_structure_has_name(structure, "video/x-raw")) { + const gchar *s = gst_structure_get_string(structure, "format"); + if (s) { + GstVideoFormat format = gst_video_format_from_string(s); + int index = indexOfVideoFormat(format); + + if (index != -1) + pixelFormat = qt_videoFormatLookup[index].pixelFormat; + } + } else if (gst_structure_has_name(structure, "image/jpeg")) { + pixelFormat = QVideoFrameFormat::Format_Jpeg; + } + + return pixelFormat; +} + +QGRange<float> QGstStructure::frameRateRange() const +{ + float minRate = 0.; + float maxRate = 0.; + + if (!structure) + return { 0.f, 0.f }; + + auto extractFraction = [](const GValue *v) -> float { + return (float)gst_value_get_fraction_numerator(v) + / (float)gst_value_get_fraction_denominator(v); + }; + auto extractFrameRate = [&](const GValue *v) { + auto insert = [&](float min, float max) { + if (max > maxRate) + maxRate = max; + if (min < minRate) + minRate = min; + }; + + if (GST_VALUE_HOLDS_FRACTION(v)) { + float rate = extractFraction(v); + insert(rate, rate); + } else if (GST_VALUE_HOLDS_FRACTION_RANGE(v)) { + auto *min = gst_value_get_fraction_range_max(v); + auto *max = gst_value_get_fraction_range_max(v); + insert(extractFraction(min), extractFraction(max)); + } + }; + + const GValue *gstFrameRates = gst_structure_get_value(structure, "framerate"); + if (gstFrameRates) { + if (GST_VALUE_HOLDS_LIST(gstFrameRates)) { + guint nFrameRates = gst_value_list_get_size(gstFrameRates); + for (guint f = 0; f < nFrameRates; ++f) { + extractFrameRate(gst_value_list_get_value(gstFrameRates, f)); + } + } else { + extractFrameRate(gstFrameRates); + } + } else { + const GValue *min = gst_structure_get_value(structure, "min-framerate"); + const GValue *max = gst_structure_get_value(structure, "max-framerate"); + if (min && max) { + minRate = extractFraction(min); + maxRate = extractFraction(max); + } + } + + return { minRate, maxRate }; +} + +QGstreamerMessage QGstStructure::getMessage() +{ + GstMessage *message = nullptr; + gst_structure_get(structure, "message", GST_TYPE_MESSAGE, &message, nullptr); + return QGstreamerMessage(message, QGstreamerMessage::HasRef); +} + +std::optional<Fraction> QGstStructure::pixelAspectRatio() const +{ + gint numerator; + gint denominator; + if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &numerator, &denominator)) { + return Fraction{ + numerator, + denominator, + }; + } + + return std::nullopt; +} + +QSize QGstStructure::nativeSize() const +{ + QSize size = resolution(); + if (!size.isValid()) { + qWarning() << Q_FUNC_INFO << "invalid resolution when querying nativeSize"; + return size; + } + + std::optional<Fraction> par = pixelAspectRatio(); + if (par) + size = qCalculateFrameSize(size, *par); + return size; +} + +// QGstCaps + +std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> QGstCaps::formatAndVideoInfo() const +{ + GstVideoInfo vidInfo; + + bool success = gst_video_info_from_caps(&vidInfo, get()); + if (!success) + return std::nullopt; + + int index = indexOfVideoFormat(vidInfo.finfo->format); + if (index == -1) + return std::nullopt; + + QVideoFrameFormat format(QSize(vidInfo.width, vidInfo.height), + qt_videoFormatLookup[index].pixelFormat); + + if (vidInfo.fps_d > 0) + format.setFrameRate(qreal(vidInfo.fps_n) / vidInfo.fps_d); + + QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown; + switch (vidInfo.colorimetry.range) { + case GST_VIDEO_COLOR_RANGE_UNKNOWN: + break; + case GST_VIDEO_COLOR_RANGE_0_255: + range = QVideoFrameFormat::ColorRange_Full; + break; + case GST_VIDEO_COLOR_RANGE_16_235: + range = QVideoFrameFormat::ColorRange_Video; + break; + } + format.setColorRange(range); + + QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined; + switch (vidInfo.colorimetry.matrix) { + case GST_VIDEO_COLOR_MATRIX_UNKNOWN: + case GST_VIDEO_COLOR_MATRIX_RGB: + case GST_VIDEO_COLOR_MATRIX_FCC: + break; + case GST_VIDEO_COLOR_MATRIX_BT709: + colorSpace = QVideoFrameFormat::ColorSpace_BT709; + break; + case GST_VIDEO_COLOR_MATRIX_BT601: + colorSpace = QVideoFrameFormat::ColorSpace_BT601; + break; + case GST_VIDEO_COLOR_MATRIX_SMPTE240M: + colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb; + break; + case GST_VIDEO_COLOR_MATRIX_BT2020: + colorSpace = QVideoFrameFormat::ColorSpace_BT2020; + break; + } + format.setColorSpace(colorSpace); + + QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown; + switch (vidInfo.colorimetry.transfer) { + case GST_VIDEO_TRANSFER_UNKNOWN: + break; + case GST_VIDEO_TRANSFER_GAMMA10: + transfer = QVideoFrameFormat::ColorTransfer_Linear; + break; + case GST_VIDEO_TRANSFER_GAMMA22: + case GST_VIDEO_TRANSFER_SMPTE240M: + case GST_VIDEO_TRANSFER_SRGB: + case GST_VIDEO_TRANSFER_ADOBERGB: + transfer = QVideoFrameFormat::ColorTransfer_Gamma22; + break; + case GST_VIDEO_TRANSFER_GAMMA18: + case GST_VIDEO_TRANSFER_GAMMA20: + // not quite, but best fit + case GST_VIDEO_TRANSFER_BT709: + case GST_VIDEO_TRANSFER_BT2020_12: + transfer = QVideoFrameFormat::ColorTransfer_BT709; + break; + case GST_VIDEO_TRANSFER_GAMMA28: + transfer = QVideoFrameFormat::ColorTransfer_Gamma28; + break; + case GST_VIDEO_TRANSFER_LOG100: + case GST_VIDEO_TRANSFER_LOG316: + break; +#if GST_CHECK_VERSION(1, 18, 0) + case GST_VIDEO_TRANSFER_SMPTE2084: + transfer = QVideoFrameFormat::ColorTransfer_ST2084; + break; + case GST_VIDEO_TRANSFER_ARIB_STD_B67: + transfer = QVideoFrameFormat::ColorTransfer_STD_B67; + break; + case GST_VIDEO_TRANSFER_BT2020_10: + transfer = QVideoFrameFormat::ColorTransfer_BT709; + break; + case GST_VIDEO_TRANSFER_BT601: + transfer = QVideoFrameFormat::ColorTransfer_BT601; + break; +#endif + } + format.setColorTransfer(transfer); + + return std::pair{ + std::move(format), + vidInfo, + }; +} + +void QGstCaps::addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, + const char *modifier) +{ + if (!gst_caps_is_writable(get())) + *this = QGstCaps(gst_caps_make_writable(release()), QGstCaps::RefMode::HasRef); + + GValue list = {}; + g_value_init(&list, GST_TYPE_LIST); + + for (QVideoFrameFormat::PixelFormat format : formats) { + int index = indexOfVideoFormat(format); + if (index == -1) + continue; + GValue item = {}; + + g_value_init(&item, G_TYPE_STRING); + g_value_set_string(&item, + gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat)); + gst_value_list_append_value(&list, &item); + g_value_unset(&item); + } + + auto *structure = gst_structure_new("video/x-raw", "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, + INT_MAX, 1, "width", GST_TYPE_INT_RANGE, 1, INT_MAX, + "height", GST_TYPE_INT_RANGE, 1, INT_MAX, nullptr); + gst_structure_set_value(structure, "format", &list); + gst_caps_append_structure(get(), structure); + g_value_unset(&list); + + if (modifier) + gst_caps_set_features(get(), size() - 1, gst_caps_features_from_string(modifier)); +} + +QGstCaps QGstCaps::fromCameraFormat(const QCameraFormat &format) +{ + QSize size = format.resolution(); + GstStructure *structure = nullptr; + if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) { + structure = gst_structure_new("image/jpeg", "width", G_TYPE_INT, size.width(), "height", + G_TYPE_INT, size.height(), nullptr); + } else { + int index = indexOfVideoFormat(format.pixelFormat()); + if (index < 0) + return {}; + auto gstFormat = qt_videoFormatLookup[index].gstFormat; + structure = gst_structure_new("video/x-raw", "format", G_TYPE_STRING, + gst_video_format_to_string(gstFormat), "width", G_TYPE_INT, + size.width(), "height", G_TYPE_INT, size.height(), nullptr); + } + auto caps = QGstCaps::create(); + gst_caps_append_structure(caps.get(), structure); + return caps; +} + +QGstCaps::MemoryFormat QGstCaps::memoryFormat() const +{ + auto *features = gst_caps_get_features(get(), 0); + if (gst_caps_features_contains(features, "memory:GLMemory")) + return GLTexture; + if (gst_caps_features_contains(features, "memory:DMABuf")) + return DMABuf; + return CpuMemory; +} + +int QGstCaps::size() const +{ + return int(gst_caps_get_size(get())); +} + +QGstStructure QGstCaps::at(int index) const +{ + return gst_caps_get_structure(get(), index); +} + +GstCaps *QGstCaps::caps() const +{ + return get(); +} + +QGstCaps QGstCaps::create() +{ + return QGstCaps(gst_caps_new_empty(), HasRef); +} + +// QGstObject + +void QGstObject::set(const char *property, const char *str) +{ + g_object_set(get(), property, str, nullptr); +} + +void QGstObject::set(const char *property, bool b) +{ + g_object_set(get(), property, gboolean(b), nullptr); +} + +void QGstObject::set(const char *property, uint i) +{ + g_object_set(get(), property, guint(i), nullptr); +} + +void QGstObject::set(const char *property, int i) +{ + g_object_set(get(), property, gint(i), nullptr); +} + +void QGstObject::set(const char *property, qint64 i) +{ + g_object_set(get(), property, gint64(i), nullptr); +} + +void QGstObject::set(const char *property, quint64 i) +{ + g_object_set(get(), property, guint64(i), nullptr); +} + +void QGstObject::set(const char *property, double d) +{ + g_object_set(get(), property, gdouble(d), nullptr); +} + +void QGstObject::set(const char *property, const QGstObject &o) +{ + g_object_set(get(), property, o.object(), nullptr); +} + +void QGstObject::set(const char *property, const QGstCaps &c) +{ + g_object_set(get(), property, c.caps(), nullptr); +} + +QGString QGstObject::getString(const char *property) const +{ + char *s = nullptr; + g_object_get(get(), property, &s, nullptr); + return QGString(s); +} + +QGstStructure QGstObject::getStructure(const char *property) const +{ + GstStructure *s = nullptr; + g_object_get(get(), property, &s, nullptr); + return QGstStructure(s); +} + +bool QGstObject::getBool(const char *property) const +{ + gboolean b = false; + g_object_get(get(), property, &b, nullptr); + return b; +} + +uint QGstObject::getUInt(const char *property) const +{ + guint i = 0; + g_object_get(get(), property, &i, nullptr); + return i; +} + +int QGstObject::getInt(const char *property) const +{ + gint i = 0; + g_object_get(get(), property, &i, nullptr); + return i; +} + +quint64 QGstObject::getUInt64(const char *property) const +{ + guint64 i = 0; + g_object_get(get(), property, &i, nullptr); + return i; +} + +qint64 QGstObject::getInt64(const char *property) const +{ + gint64 i = 0; + g_object_get(get(), property, &i, nullptr); + return i; +} + +float QGstObject::getFloat(const char *property) const +{ + gfloat d = 0; + g_object_get(get(), property, &d, nullptr); + return d; +} + +double QGstObject::getDouble(const char *property) const +{ + gdouble d = 0; + g_object_get(get(), property, &d, nullptr); + return d; +} + +QGstObject QGstObject::getObject(const char *property) const +{ + GstObject *o = nullptr; + g_object_get(get(), property, &o, nullptr); + return QGstObject(o, HasRef); +} + +void QGstObject::connect(const char *name, GCallback callback, gpointer userData) +{ + g_signal_connect(get(), name, callback, userData); +} + +GstObject *QGstObject::object() const +{ + return get(); +} + +const char *QGstObject::name() const +{ + return get() ? GST_OBJECT_NAME(get()) : "(null)"; +} + +// QGstPad + +QGstPad::QGstPad(const QGstObject &o) : QGstPad(GST_PAD(o.object()), NeedsRef) { } + +QGstPad::QGstPad(GstPad *pad, RefMode mode) : QGstObject(&pad->object, mode) { } + +QGstCaps QGstPad::currentCaps() const +{ + return QGstCaps(gst_pad_get_current_caps(pad()), QGstCaps::HasRef); +} + +QGstCaps QGstPad::queryCaps() const +{ + return QGstCaps(gst_pad_query_caps(pad(), nullptr), QGstCaps::HasRef); +} + +bool QGstPad::isLinked() const +{ + return gst_pad_is_linked(pad()); +} + +bool QGstPad::link(const QGstPad &sink) const +{ + return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK; +} + +bool QGstPad::unlink(const QGstPad &sink) const +{ + return gst_pad_unlink(pad(), sink.pad()); +} + +bool QGstPad::unlinkPeer() const +{ + return unlink(peer()); +} + +QGstPad QGstPad::peer() const +{ + return QGstPad(gst_pad_get_peer(pad()), HasRef); +} + +QGstElement QGstPad::parent() const +{ + return QGstElement(gst_pad_get_parent_element(pad()), HasRef); +} + +GstPad *QGstPad::pad() const +{ + return GST_PAD_CAST(object()); +} + +GstEvent *QGstPad::stickyEvent(GstEventType type) +{ + return gst_pad_get_sticky_event(pad(), type, 0); +} + +bool QGstPad::sendEvent(GstEvent *event) +{ + return gst_pad_send_event(pad(), event); +} + +// QGstClock + +QGstClock::QGstClock(const QGstObject &o) : QGstClock(GST_CLOCK(o.object())) { } + +QGstClock::QGstClock(GstClock *clock, RefMode mode) : QGstObject(&clock->object, mode) { } + +GstClock *QGstClock::clock() const +{ + return GST_CLOCK_CAST(object()); +} + +GstClockTime QGstClock::time() const +{ + return gst_clock_get_time(clock()); +} + +// QGstElement + +QGstElement::QGstElement(GstElement *element, RefMode mode) : QGstObject(&element->object, mode) { } + +QGstElement QGstElement::createFromFactory(const char *factory, const char *name) +{ + GstElement *element = gst_element_factory_make(factory, name); + +#ifndef QT_NO_DEBUG + if (!element) { + qWarning() << "Failed to make element" << name << "from factory" << factory; + return QGstElement{}; + } +#endif + + return QGstElement{ + element, + NeedsRef, + }; +} + +QGstPad QGstElement::staticPad(const char *name) const +{ + return QGstPad(gst_element_get_static_pad(element(), name), HasRef); +} + +QGstPad QGstElement::src() const +{ + return staticPad("src"); +} + +QGstPad QGstElement::sink() const +{ + return staticPad("sink"); +} + +QGstPad QGstElement::getRequestPad(const char *name) const +{ +#if GST_CHECK_VERSION(1, 19, 1) + return QGstPad(gst_element_request_pad_simple(element(), name), HasRef); +#else + return QGstPad(gst_element_get_request_pad(element(), name), HasRef); +#endif +} + +void QGstElement::releaseRequestPad(const QGstPad &pad) const +{ + return gst_element_release_request_pad(element(), pad.pad()); +} + +GstState QGstElement::state() const +{ + GstState state; + gst_element_get_state(element(), &state, nullptr, 0); + return state; +} + +GstStateChangeReturn QGstElement::setState(GstState state) +{ + return gst_element_set_state(element(), state); +} + +bool QGstElement::setStateSync(GstState state, std::chrono::nanoseconds timeout) +{ + GstStateChangeReturn change = gst_element_set_state(element(), state); + if (change == GST_STATE_CHANGE_ASYNC) { + change = gst_element_get_state(element(), nullptr, &state, timeout.count()); + } +#ifndef QT_NO_DEBUG + if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) + qWarning() << "Could not change state of" << name() << "to" << state << change; +#endif + return change == GST_STATE_CHANGE_SUCCESS; +} + +bool QGstElement::syncStateWithParent() +{ + return gst_element_sync_state_with_parent(element()) == TRUE; +} + +bool QGstElement::finishStateChange(std::chrono::nanoseconds timeout) +{ + GstState state, pending; + GstStateChangeReturn change = + gst_element_get_state(element(), &state, &pending, timeout.count()); + +#ifndef QT_NO_DEBUG + if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) + qWarning() << "Could not finish change state of" << name() << change << state << pending; +#endif + return change == GST_STATE_CHANGE_SUCCESS; +} + +void QGstElement::lockState(bool locked) +{ + gst_element_set_locked_state(element(), locked); +} + +bool QGstElement::isStateLocked() const +{ + return gst_element_is_locked_state(element()); +} + +void QGstElement::sendEvent(GstEvent *event) const +{ + gst_element_send_event(element(), event); +} + +void QGstElement::sendEos() const +{ + sendEvent(gst_event_new_eos()); +} + +GstClockTime QGstElement::baseTime() const +{ + return gst_element_get_base_time(element()); +} + +void QGstElement::setBaseTime(GstClockTime time) const +{ + gst_element_set_base_time(element(), time); +} + +GstElement *QGstElement::element() const +{ + return GST_ELEMENT_CAST(get()); +} + +// QGstBin + +QGstBin QGstBin::create(const char *name) +{ + return QGstBin(gst_bin_new(name), NeedsRef); +} + +QGstBin QGstBin::createFromFactory(const char *factory, const char *name) +{ + QGstElement element = QGstElement::createFromFactory(factory, name); + Q_ASSERT(GST_IS_BIN(element.element())); + return QGstBin{ + GST_BIN(element.release()), + RefMode::HasRef, + }; +} + +QGstBin::QGstBin(GstBin *bin, RefMode mode) : QGstElement(&bin->element, mode) { } + +GstBin *QGstBin::bin() const +{ + return GST_BIN_CAST(get()); +} + +void QGstBin::addGhostPad(const QGstElement &child, const char *name) +{ + addGhostPad(name, child.staticPad(name)); +} + +void QGstBin::addGhostPad(const char *name, const QGstPad &pad) +{ + gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad())); +} + +bool QGstBin::syncChildrenState() +{ + return gst_bin_sync_children_states(bin()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/multimedia/gstreamer/common/qgst_p.h b/src/plugins/multimedia/gstreamer/common/qgst_p.h index 40a7ab2c2..95a1f6b5f 100644 --- a/src/plugins/multimedia/gstreamer/common/qgst_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgst_p.h @@ -15,23 +15,22 @@ // We mean it. // +#include "qgst_handle_types_p.h" + #include <private/qtmultimediaglobal_p.h> #include <private/qmultimediautils_p.h> -#include <QtCore/qsemaphore.h> -#include <QtCore/qlist.h> #include <QtCore/qdebug.h> +#include <QtCore/qlist.h> +#include <QtCore/qsemaphore.h> #include <QtMultimedia/qaudioformat.h> #include <QtMultimedia/qvideoframe.h> -#include "qgst_handle_types_p.h" - #include <gst/gst.h> #include <gst/video/video-info.h> #include <type_traits> -#include <functional> #if QT_CONFIG(gstreamer_photography) #define GST_USE_UNSTABLE_API @@ -64,70 +63,33 @@ struct QGString : QUniqueGStringHandle class QGValue { public: - explicit QGValue(const GValue *v) : value(v) { } + explicit QGValue(const GValue *v); const GValue *value; - bool isNull() const { return !value; } + bool isNull() const; - std::optional<bool> toBool() const - { - if (!G_VALUE_HOLDS_BOOLEAN(value)) - return std::nullopt; - return g_value_get_boolean(value); - } - std::optional<int> toInt() const - { - if (!G_VALUE_HOLDS_INT(value)) - return std::nullopt; - return g_value_get_int(value); - } - std::optional<int> toInt64() const - { - if (!G_VALUE_HOLDS_INT64(value)) - return std::nullopt; - return g_value_get_int64(value); - } + std::optional<bool> toBool() const; + std::optional<int> toInt() const; + std::optional<int> toInt64() const; template<typename T> T *getPointer() const { return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr; } - const char *toString() const - { - return value ? g_value_get_string(value) : nullptr; - } - std::optional<float> getFraction() const - { - if (!GST_VALUE_HOLDS_FRACTION(value)) - return std::nullopt; - return (float)gst_value_get_fraction_numerator(value)/(float)gst_value_get_fraction_denominator(value); - } + const char *toString() const; + std::optional<float> getFraction() const; + std::optional<QGRange<float>> getFractionRange() const; + std::optional<QGRange<int>> toIntRange() const; - std::optional<QGRange<float>> getFractionRange() const - { - if (!GST_VALUE_HOLDS_FRACTION_RANGE(value)) - return std::nullopt; - QGValue min = QGValue{ gst_value_get_fraction_range_min(value) }; - QGValue max = QGValue{ gst_value_get_fraction_range_max(value) }; - return QGRange<float>{ *min.getFraction(), *max.getFraction() }; - } + QGstStructure toStructure() const; + QGstCaps toCaps() const; - std::optional<QGRange<int>> toIntRange() const - { - if (!GST_VALUE_HOLDS_INT_RANGE(value)) - return std::nullopt; - return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) }; - } - - inline QGstStructure toStructure() const; - inline QGstCaps toCaps() const; - - inline bool isList() const { return value && GST_VALUE_HOLDS_LIST(value); } - inline int listSize() const { return gst_value_list_get_size(value); } - inline QGValue at(int index) const { return QGValue{ gst_value_list_get_value(value, index) }; } + bool isList() const; + int listSize() const; + QGValue at(int index) const; - Q_MULTIMEDIA_EXPORT QList<QAudioFormat::SampleFormat> getSampleFormats() const; + QList<QAudioFormat::SampleFormat> getSampleFormats() const; }; namespace QGstPointerImpl { @@ -218,30 +180,27 @@ protected: class QGstreamerMessage; -class QGstStructure { +class QGstStructure +{ public: const GstStructure *structure = nullptr; QGstStructure() = default; - QGstStructure(const GstStructure *s) : structure(s) {} - void free() { if (structure) gst_structure_free(const_cast<GstStructure *>(structure)); structure = nullptr; } - - bool isNull() const { return !structure; } + QGstStructure(const GstStructure *s); + void free(); - QByteArrayView name() const { return gst_structure_get_name(structure); } + bool isNull() const; - QGValue operator[](const char *name) const - { - return QGValue{ gst_structure_get_value(structure, name) }; - } + QByteArrayView name() const; + QGValue operator[](const char *name) const; - Q_MULTIMEDIA_EXPORT QSize resolution() const; - Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormat() const; - Q_MULTIMEDIA_EXPORT QGRange<float> frameRateRange() const; - Q_MULTIMEDIA_EXPORT QGstreamerMessage getMessage(); - Q_MULTIMEDIA_EXPORT std::optional<Fraction> pixelAspectRatio() const; - Q_MULTIMEDIA_EXPORT QSize nativeSize() const; + QSize resolution() const; + QVideoFrameFormat::PixelFormat pixelFormat() const; + QGRange<float> frameRateRange() const; + QGstreamerMessage getMessage(); + std::optional<Fraction> pixelAspectRatio() const; + QSize nativeSize() const; - QGstStructure copy() const { return gst_structure_copy(structure); } + QGstStructure copy() const; }; template <> @@ -264,18 +223,16 @@ public: enum MemoryFormat { CpuMemory, GLTexture, DMABuf }; - int size() const { return int(gst_caps_get_size(get())); } - QGstStructure at(int index) const { return gst_caps_get_structure(get(), index); } - GstCaps *caps() const { return get(); } + int size() const; + QGstStructure at(int index) const; + GstCaps *caps() const; MemoryFormat memoryFormat() const; std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> formatAndVideoInfo() const; void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr); - static QGstCaps create() { - return QGstCaps(gst_caps_new_empty(), HasRef); - } + static QGstCaps create(); static QGstCaps fromCameraFormat(const QCameraFormat &format); }; @@ -301,94 +258,31 @@ public: QGstObject &operator=(const QGstObject &) = default; QGstObject &operator=(QGstObject &&) noexcept = default; - void set(const char *property, const char *str) { g_object_set(get(), property, str, nullptr); } - void set(const char *property, bool b) { g_object_set(get(), property, gboolean(b), nullptr); } - void set(const char *property, uint i) { g_object_set(get(), property, guint(i), nullptr); } - void set(const char *property, int i) { g_object_set(get(), property, gint(i), nullptr); } - void set(const char *property, qint64 i) { g_object_set(get(), property, gint64(i), nullptr); } - void set(const char *property, quint64 i) - { - g_object_set(get(), property, guint64(i), nullptr); - } - void set(const char *property, double d) { g_object_set(get(), property, gdouble(d), nullptr); } - void set(const char *property, const QGstObject &o) - { - g_object_set(get(), property, o.object(), nullptr); - } - void set(const char *property, const QGstCaps &c) - { - g_object_set(get(), property, c.caps(), nullptr); - } - - QGString getString(const char *property) const - { - char *s = nullptr; - g_object_get(get(), property, &s, nullptr); - return QGString(s); - } - - QGstStructure getStructure(const char *property) const - { - GstStructure *s = nullptr; - g_object_get(get(), property, &s, nullptr); - return QGstStructure(s); - } - bool getBool(const char *property) const - { - gboolean b = false; - g_object_get(get(), property, &b, nullptr); - return b; - } - uint getUInt(const char *property) const - { - guint i = 0; - g_object_get(get(), property, &i, nullptr); - return i; - } - int getInt(const char *property) const - { - gint i = 0; - g_object_get(get(), property, &i, nullptr); - return i; - } - quint64 getUInt64(const char *property) const - { - guint64 i = 0; - g_object_get(get(), property, &i, nullptr); - return i; - } - qint64 getInt64(const char *property) const - { - gint64 i = 0; - g_object_get(get(), property, &i, nullptr); - return i; - } - float getFloat(const char *property) const - { - gfloat d = 0; - g_object_get(get(), property, &d, nullptr); - return d; - } - double getDouble(const char *property) const - { - gdouble d = 0; - g_object_get(get(), property, &d, nullptr); - return d; - } - QGstObject getObject(const char *property) const - { - GstObject *o = nullptr; - g_object_get(get(), property, &o, nullptr); - return QGstObject(o, HasRef); - } - - void connect(const char *name, GCallback callback, gpointer userData) - { - g_signal_connect(get(), name, callback, userData); - } - - GstObject *object() const { return get(); } - const char *name() const { return get() ? GST_OBJECT_NAME(get()) : "(null)"; } + void set(const char *property, const char *str); + void set(const char *property, bool b); + void set(const char *property, uint i); + void set(const char *property, int i); + void set(const char *property, qint64 i); + void set(const char *property, quint64 i); + void set(const char *property, double d); + void set(const char *property, const QGstObject &o); + void set(const char *property, const QGstCaps &c); + + QGString getString(const char *property) const; + QGstStructure getStructure(const char *property) const; + bool getBool(const char *property) const; + uint getUInt(const char *property) const; + int getInt(const char *property) const; + quint64 getUInt64(const char *property) const; + qint64 getInt64(const char *property) const; + float getFloat(const char *property) const; + double getDouble(const char *property) const; + QGstObject getObject(const char *property) const; + + void connect(const char *name, GCallback callback, gpointer userData); + + GstObject *object() const; + const char *name() const; }; class QGstElement; @@ -400,35 +294,26 @@ public: QGstPad(const QGstPad &) = default; QGstPad(QGstPad &&) noexcept = default; - explicit QGstPad(const QGstObject &o) : QGstPad(GST_PAD(o.object()), NeedsRef) { } - explicit QGstPad(GstPad *pad, RefMode mode = NeedsRef) : QGstObject(&pad->object, mode) { } + explicit QGstPad(const QGstObject &o); + explicit QGstPad(GstPad *pad, RefMode mode = NeedsRef); QGstPad &operator=(const QGstPad &) = default; QGstPad &operator=(QGstPad &&) noexcept = default; - QGstCaps currentCaps() const - { - return QGstCaps(gst_pad_get_current_caps(pad()), QGstCaps::HasRef); - } - QGstCaps queryCaps() const - { - return QGstCaps(gst_pad_query_caps(pad(), nullptr), QGstCaps::HasRef); - } + QGstCaps currentCaps() const; + QGstCaps queryCaps() const; - bool isLinked() const { return gst_pad_is_linked(pad()); } - bool link(const QGstPad &sink) const - { - return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK; - } - bool unlink(const QGstPad &sink) const { return gst_pad_unlink(pad(), sink.pad()); } - bool unlinkPeer() const { return unlink(peer()); } - QGstPad peer() const { return QGstPad(gst_pad_get_peer(pad()), HasRef); } - inline QGstElement parent() const; + bool isLinked() const; + bool link(const QGstPad &sink) const; + bool unlink(const QGstPad &sink) const; + bool unlinkPeer() const; + QGstPad peer() const; + QGstElement parent() const; - GstPad *pad() const { return GST_PAD_CAST(object()); } + GstPad *pad() const; - GstEvent *stickyEvent(GstEventType type) { return gst_pad_get_sticky_event(pad(), type, 0); } - bool sendEvent(GstEvent *event) { return gst_pad_send_event (pad(), event); } + GstEvent *stickyEvent(GstEventType type); + bool sendEvent(GstEvent *event); template<auto Member, typename T> void addProbe(T *instance, GstPadProbeType type) { @@ -480,14 +365,11 @@ class QGstClock : public QGstObject { public: QGstClock() = default; - explicit QGstClock(const QGstObject &o) : QGstClock(GST_CLOCK(o.object())) { } - explicit QGstClock(GstClock *clock, RefMode mode = NeedsRef) : QGstObject(&clock->object, mode) - { - } - - GstClock *clock() const { return GST_CLOCK_CAST(object()); } + explicit QGstClock(const QGstObject &o); + explicit QGstClock(GstClock *clock, RefMode mode = NeedsRef); - GstClockTime time() const { return gst_clock_get_time(clock()); } + GstClock *clock() const; + GstClockTime time() const; }; class QGstElement : public QGstObject @@ -500,76 +382,26 @@ public: QGstElement &operator=(const QGstElement &) = default; QGstElement &operator=(QGstElement &&) noexcept = default; - explicit QGstElement(GstElement *element, RefMode mode = NeedsRef) - : QGstObject(&element->object, mode) - { - } + explicit QGstElement(GstElement *element, RefMode mode = NeedsRef); + static QGstElement createFromFactory(const char *factory, const char *name = nullptr); - static QGstElement createFromFactory(const char *factory, const char *name = nullptr) - { - GstElement *element = gst_element_factory_make(factory, name); - -#ifndef QT_NO_DEBUG - if (!element) { - qWarning() << "Failed to make element" << name << "from factory" << factory; - return QGstElement{}; - } -#endif - - return QGstElement{ - element, - NeedsRef, - }; - } - - QGstPad staticPad(const char *name) const { return QGstPad(gst_element_get_static_pad(element(), name), HasRef); } - QGstPad src() const { return staticPad("src"); } - QGstPad sink() const { return staticPad("sink"); } - QGstPad getRequestPad(const char *name) const - { -#if GST_CHECK_VERSION(1,19,1) - return QGstPad(gst_element_request_pad_simple(element(), name), HasRef); -#else - return QGstPad(gst_element_get_request_pad(element(), name), HasRef); -#endif - } - void releaseRequestPad(const QGstPad &pad) const { return gst_element_release_request_pad(element(), pad.pad()); } + QGstPad staticPad(const char *name) const; + QGstPad src() const; + QGstPad sink() const; + QGstPad getRequestPad(const char *name) const; + void releaseRequestPad(const QGstPad &pad) const; - GstState state() const - { - GstState state; - gst_element_get_state(element(), &state, nullptr, 0); - return state; - } - GstStateChangeReturn setState(GstState state) { return gst_element_set_state(element(), state); } - bool setStateSync(GstState state) - { - auto change = gst_element_set_state(element(), state); - if (change == GST_STATE_CHANGE_ASYNC) { - change = gst_element_get_state(element(), nullptr, &state, 1000*1e6 /*nano seconds*/); - } -#ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) - qWarning() << "Could not change state of" << name() << "to" << state << change; -#endif - return change == GST_STATE_CHANGE_SUCCESS; - } - bool syncStateWithParent() { return gst_element_sync_state_with_parent(element()) == TRUE; } - bool finishStateChange() - { - auto change = gst_element_get_state(element(), nullptr, nullptr, 1000*1e6 /*nano seconds*/); -#ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) - qWarning() << "Could finish change state of" << name(); -#endif - return change == GST_STATE_CHANGE_SUCCESS; - } + GstState state() const; + GstStateChangeReturn setState(GstState state); + bool setStateSync(GstState state, std::chrono::nanoseconds timeout = std::chrono::seconds(1)); + bool syncStateWithParent(); + bool finishStateChange(std::chrono::nanoseconds timeout = std::chrono::seconds(5)); - void lockState(bool locked) { gst_element_set_locked_state(element(), locked); } - bool isStateLocked() const { return gst_element_is_locked_state(element()); } + void lockState(bool locked); + bool isStateLocked() const; - void sendEvent(GstEvent *event) const { gst_element_send_event(element(), event); } - void sendEos() const { sendEvent(gst_event_new_eos()); } + void sendEvent(GstEvent *event) const; + void sendEos() const; template<auto Member, typename T> void onPadAdded(T *instance) { @@ -602,10 +434,10 @@ public: connect("no-more-pads", G_CALLBACK(Impl::callback), instance); } - GstClockTime baseTime() const { return gst_element_get_base_time(element()); } - void setBaseTime(GstClockTime time) const { gst_element_set_base_time(element(), time); } + GstClockTime baseTime() const; + void setBaseTime(GstClockTime time) const; - GstElement *element() const { return GST_ELEMENT_CAST(get()); } + GstElement *element() const; }; template <typename... Ts> @@ -637,11 +469,6 @@ qUnlinkGstElements(const Ts &...ts) gst_element_unlink_many(ts.element()..., nullptr); } -inline QGstElement QGstPad::parent() const -{ - return QGstElement(gst_pad_get_parent_element(pad()), HasRef); -} - class QGstBin : public QGstElement { public: @@ -651,18 +478,9 @@ public: QGstBin &operator=(const QGstBin &) = default; QGstBin &operator=(QGstBin &&) noexcept = default; - static QGstBin create(const char *name) { return QGstBin(gst_bin_new(name), NeedsRef); } - static QGstBin createFromFactory(const char *factory, const char *name) - { - QGstElement element = QGstElement::createFromFactory(factory, name); - Q_ASSERT(GST_IS_BIN(element.element())); - return QGstBin{ - GST_BIN(element.release()), - RefMode::HasRef, - }; - } - - explicit QGstBin(GstBin *bin, RefMode mode = NeedsRef) : QGstElement(&bin->element, mode) { } + explicit QGstBin(GstBin *bin, RefMode mode = NeedsRef); + static QGstBin create(const char *name); + static QGstBin createFromFactory(const char *factory, const char *name); template <typename... Ts> std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> add(const Ts &...ts) @@ -691,33 +509,14 @@ public: remove(ts...); } - GstBin *bin() const { return GST_BIN_CAST(get()); } + GstBin *bin() const; - void addGhostPad(const QGstElement &child, const char *name) - { - addGhostPad(name, child.staticPad(name)); - } - void addGhostPad(const char *name, const QGstPad &pad) - { - gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad())); - } + void addGhostPad(const QGstElement &child, const char *name); + void addGhostPad(const char *name, const QGstPad &pad); - bool syncChildrenState() { return gst_bin_sync_children_states(bin()); } + bool syncChildrenState(); }; -inline QGstStructure QGValue::toStructure() const -{ - if (!value || !GST_VALUE_HOLDS_STRUCTURE(value)) - return QGstStructure(); - return QGstStructure(gst_value_get_structure(value)); -} - -inline QGstCaps QGValue::toCaps() const -{ - if (!value || !GST_VALUE_HOLDS_CAPS(value)) - return {}; - return QGstCaps(gst_caps_copy(gst_value_get_caps(value)), QGstCaps::HasRef); -} inline QString errorMessageCannotFindElement(std::string_view element) { diff --git a/src/plugins/multimedia/gstreamer/common/qgstutils.cpp b/src/plugins/multimedia/gstreamer/common/qgstutils.cpp index dfbe241d4..ca3047b81 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstutils.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstutils.cpp @@ -1,37 +1,14 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include <QtMultimedia/private/qtmultimediaglobal_p.h> #include "qgstutils_p.h" -#include "qgstreamermessage_p.h" - -#include <QtCore/qdatetime.h> -#include <QtCore/qdir.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qvariant.h> -#include <QtCore/qregularexpression.h> -#include <QtCore/qsize.h> -#include <QtCore/qset.h> -#include <QtCore/qstringlist.h> -#include <QtGui/qimage.h> -#include <qaudioformat.h> -#include <QtCore/qelapsedtimer.h> -#include <QtMultimedia/qvideoframeformat.h> -#include <private/qmultimediautils_p.h> -#include <gst/audio/audio.h> -#include <gst/video/video.h> +#include <QtMultimedia/private/qtmultimediaglobal_p.h> QT_BEGIN_NAMESPACE namespace { -template <typename T, int N> -constexpr int lengthOf(const T (&)[N]) -{ - return N; -} - const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = { nullptr, #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN @@ -135,230 +112,6 @@ QList<QAudioFormat::SampleFormat> QGValue::getSampleFormats() const return formats; } -namespace { - -struct VideoFormat -{ - QVideoFrameFormat::PixelFormat pixelFormat; - GstVideoFormat gstFormat; -}; - -const VideoFormat qt_videoFormatLookup[] = { - { QVideoFrameFormat::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, - { QVideoFrameFormat::Format_YUV422P, GST_VIDEO_FORMAT_Y42B }, - { QVideoFrameFormat::Format_YV12 , GST_VIDEO_FORMAT_YV12 }, - { QVideoFrameFormat::Format_UYVY , GST_VIDEO_FORMAT_UYVY }, - { QVideoFrameFormat::Format_YUYV , GST_VIDEO_FORMAT_YUY2 }, - { QVideoFrameFormat::Format_NV12 , GST_VIDEO_FORMAT_NV12 }, - { QVideoFrameFormat::Format_NV21 , GST_VIDEO_FORMAT_NV21 }, - { QVideoFrameFormat::Format_AYUV , GST_VIDEO_FORMAT_AYUV }, - { QVideoFrameFormat::Format_Y8 , GST_VIDEO_FORMAT_GRAY8 }, - { QVideoFrameFormat::Format_XRGB8888 , GST_VIDEO_FORMAT_xRGB }, - { QVideoFrameFormat::Format_XBGR8888 , GST_VIDEO_FORMAT_xBGR }, - { QVideoFrameFormat::Format_RGBX8888 , GST_VIDEO_FORMAT_RGBx }, - { QVideoFrameFormat::Format_BGRX8888 , GST_VIDEO_FORMAT_BGRx }, - { QVideoFrameFormat::Format_ARGB8888, GST_VIDEO_FORMAT_ARGB }, - { QVideoFrameFormat::Format_ABGR8888, GST_VIDEO_FORMAT_ABGR }, - { QVideoFrameFormat::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, - { QVideoFrameFormat::Format_BGRA8888, GST_VIDEO_FORMAT_BGRA }, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - { QVideoFrameFormat::Format_Y16 , GST_VIDEO_FORMAT_GRAY16_LE }, - { QVideoFrameFormat::Format_P010 , GST_VIDEO_FORMAT_P010_10LE }, -#else - { QVideoFrameFormat::Format_Y16 , GST_VIDEO_FORMAT_GRAY16_BE }, - { QVideoFrameFormat::Format_P010 , GST_VIDEO_FORMAT_P010_10BE }, -#endif -}; - -int indexOfVideoFormat(QVideoFrameFormat::PixelFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].pixelFormat == format) - return i; - - return -1; -} - -int indexOfVideoFormat(GstVideoFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].gstFormat == format) - return i; - - return -1; -} - -} // namespace - -QGstCaps::MemoryFormat QGstCaps::memoryFormat() const -{ - auto *features = gst_caps_get_features(get(), 0); - if (gst_caps_features_contains(features, "memory:GLMemory")) - return GLTexture; - if (gst_caps_features_contains(features, "memory:DMABuf")) - return DMABuf; - return CpuMemory; -} - -std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> QGstCaps::formatAndVideoInfo() const -{ - GstVideoInfo vidInfo; - - bool success = gst_video_info_from_caps(&vidInfo, get()); - if (!success) - return std::nullopt; - - int index = indexOfVideoFormat(vidInfo.finfo->format); - if (index == -1) - return std::nullopt; - - QVideoFrameFormat format(QSize(vidInfo.width, vidInfo.height), - qt_videoFormatLookup[index].pixelFormat); - - if (vidInfo.fps_d > 0) - format.setFrameRate(qreal(vidInfo.fps_n) / vidInfo.fps_d); - - QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown; - switch (vidInfo.colorimetry.range) { - case GST_VIDEO_COLOR_RANGE_UNKNOWN: - break; - case GST_VIDEO_COLOR_RANGE_0_255: - range = QVideoFrameFormat::ColorRange_Full; - break; - case GST_VIDEO_COLOR_RANGE_16_235: - range = QVideoFrameFormat::ColorRange_Video; - break; - } - format.setColorRange(range); - - QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined; - switch (vidInfo.colorimetry.matrix) { - case GST_VIDEO_COLOR_MATRIX_UNKNOWN: - case GST_VIDEO_COLOR_MATRIX_RGB: - case GST_VIDEO_COLOR_MATRIX_FCC: - break; - case GST_VIDEO_COLOR_MATRIX_BT709: - colorSpace = QVideoFrameFormat::ColorSpace_BT709; - break; - case GST_VIDEO_COLOR_MATRIX_BT601: - colorSpace = QVideoFrameFormat::ColorSpace_BT601; - break; - case GST_VIDEO_COLOR_MATRIX_SMPTE240M: - colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb; - break; - case GST_VIDEO_COLOR_MATRIX_BT2020: - colorSpace = QVideoFrameFormat::ColorSpace_BT2020; - break; - } - format.setColorSpace(colorSpace); - - QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown; - switch (vidInfo.colorimetry.transfer) { - case GST_VIDEO_TRANSFER_UNKNOWN: - break; - case GST_VIDEO_TRANSFER_GAMMA10: - transfer = QVideoFrameFormat::ColorTransfer_Linear; - break; - case GST_VIDEO_TRANSFER_GAMMA22: - case GST_VIDEO_TRANSFER_SMPTE240M: - case GST_VIDEO_TRANSFER_SRGB: - case GST_VIDEO_TRANSFER_ADOBERGB: - transfer = QVideoFrameFormat::ColorTransfer_Gamma22; - break; - case GST_VIDEO_TRANSFER_GAMMA18: - case GST_VIDEO_TRANSFER_GAMMA20: - // not quite, but best fit - case GST_VIDEO_TRANSFER_BT709: - case GST_VIDEO_TRANSFER_BT2020_12: - transfer = QVideoFrameFormat::ColorTransfer_BT709; - break; - case GST_VIDEO_TRANSFER_GAMMA28: - transfer = QVideoFrameFormat::ColorTransfer_Gamma28; - break; - case GST_VIDEO_TRANSFER_LOG100: - case GST_VIDEO_TRANSFER_LOG316: - break; -#if GST_CHECK_VERSION(1, 18, 0) - case GST_VIDEO_TRANSFER_SMPTE2084: - transfer = QVideoFrameFormat::ColorTransfer_ST2084; - break; - case GST_VIDEO_TRANSFER_ARIB_STD_B67: - transfer = QVideoFrameFormat::ColorTransfer_STD_B67; - break; - case GST_VIDEO_TRANSFER_BT2020_10: - transfer = QVideoFrameFormat::ColorTransfer_BT709; - break; - case GST_VIDEO_TRANSFER_BT601: - transfer = QVideoFrameFormat::ColorTransfer_BT601; - break; -#endif - } - format.setColorTransfer(transfer); - - return std::pair{ - std::move(format), - vidInfo, - }; -} - -void QGstCaps::addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier) -{ - if (!gst_caps_is_writable(get())) - *this = QGstCaps(gst_caps_make_writable(release()), QGstCaps::RefMode::HasRef); - - GValue list = {}; - g_value_init(&list, GST_TYPE_LIST); - - for (QVideoFrameFormat::PixelFormat format : formats) { - int index = indexOfVideoFormat(format); - if (index == -1) - continue; - GValue item = {}; - - g_value_init(&item, G_TYPE_STRING); - g_value_set_string(&item, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat)); - gst_value_list_append_value(&list, &item); - g_value_unset(&item); - } - - auto *structure = gst_structure_new("video/x-raw", - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, - "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, - nullptr); - gst_structure_set_value(structure, "format", &list); - gst_caps_append_structure(get(), structure); - g_value_unset(&list); - - if (modifier) - gst_caps_set_features(get(), size() - 1, gst_caps_features_from_string(modifier)); -} - -QGstCaps QGstCaps::fromCameraFormat(const QCameraFormat &format) -{ - QSize size = format.resolution(); - GstStructure *structure = nullptr; - if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) { - structure = gst_structure_new("image/jpeg", - "width" , G_TYPE_INT, size.width(), - "height" , G_TYPE_INT, size.height(), - nullptr); - } else { - int index = indexOfVideoFormat(format.pixelFormat()); - if (index < 0) - return {}; - auto gstFormat = qt_videoFormatLookup[index].gstFormat; - structure = gst_structure_new("video/x-raw", - "format" , G_TYPE_STRING, gst_video_format_to_string(gstFormat), - "width" , G_TYPE_INT, size.width(), - "height" , G_TYPE_INT, size.height(), - nullptr); - } - auto caps = QGstCaps::create(); - gst_caps_append_structure(caps.get(), structure); - return caps; -} - void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) { // GStreamer uses nanoseconds, Qt uses microseconds @@ -371,131 +124,6 @@ void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); } } - -QSize QGstStructure::resolution() const -{ - QSize size; - - int w, h; - if (structure && - gst_structure_get_int(structure, "width", &w) && - gst_structure_get_int(structure, "height", &h)) { - size.rwidth() = w; - size.rheight() = h; - } - - return size; -} - -QVideoFrameFormat::PixelFormat QGstStructure::pixelFormat() const -{ - QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid; - - if (!structure) - return pixelFormat; - - if (gst_structure_has_name(structure, "video/x-raw")) { - const gchar *s = gst_structure_get_string(structure, "format"); - if (s) { - GstVideoFormat format = gst_video_format_from_string(s); - int index = indexOfVideoFormat(format); - - if (index != -1) - pixelFormat = qt_videoFormatLookup[index].pixelFormat; - } - } else if (gst_structure_has_name(structure, "image/jpeg")) { - pixelFormat = QVideoFrameFormat::Format_Jpeg; - } - - return pixelFormat; -} - -QGRange<float> QGstStructure::frameRateRange() const -{ - float minRate = 0.; - float maxRate = 0.; - - if (!structure) - return {0.f, 0.f}; - - auto extractFraction = [] (const GValue *v) -> float { - return (float)gst_value_get_fraction_numerator(v)/(float)gst_value_get_fraction_denominator(v); - }; - auto extractFrameRate = [&] (const GValue *v) { - auto insert = [&] (float min, float max) { - if (max > maxRate) - maxRate = max; - if (min < minRate) - minRate = min; - }; - - if (GST_VALUE_HOLDS_FRACTION(v)) { - float rate = extractFraction(v); - insert(rate, rate); - } else if (GST_VALUE_HOLDS_FRACTION_RANGE(v)) { - auto *min = gst_value_get_fraction_range_max(v); - auto *max = gst_value_get_fraction_range_max(v); - insert(extractFraction(min), extractFraction(max)); - } - }; - - const GValue *gstFrameRates = gst_structure_get_value(structure, "framerate"); - if (gstFrameRates) { - if (GST_VALUE_HOLDS_LIST(gstFrameRates)) { - guint nFrameRates = gst_value_list_get_size(gstFrameRates); - for (guint f = 0; f < nFrameRates; ++f) { - extractFrameRate(gst_value_list_get_value(gstFrameRates, f)); - } - } else { - extractFrameRate(gstFrameRates); - } - } else { - const GValue *min = gst_structure_get_value(structure, "min-framerate"); - const GValue *max = gst_structure_get_value(structure, "max-framerate"); - if (min && max) { - minRate = extractFraction(min); - maxRate = extractFraction(max); - } - } - - return {minRate, maxRate}; -} - -QGstreamerMessage QGstStructure::getMessage() -{ - GstMessage *message = nullptr; - gst_structure_get(structure, "message", GST_TYPE_MESSAGE, &message, nullptr); - return QGstreamerMessage(message, QGstreamerMessage::HasRef); -} - -std::optional<Fraction> QGstStructure::pixelAspectRatio() const -{ - gint numerator; - gint denominator; - if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &numerator, &denominator)) { - return Fraction{ - numerator, - denominator, - }; - } - - return std::nullopt; -} - -QSize QGstStructure::nativeSize() const -{ - QSize size = resolution(); - if (!size.isValid()) { - qWarning() << Q_FUNC_INFO << "invalid resolution when querying nativeSize"; - return size; - } - - std::optional<Fraction> par = pixelAspectRatio(); - if (par) - size = qCalculateFrameSize(size, *par); - return size; -} - GList *qt_gst_video_sinks() { return gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK |