Revision control
++ // 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 <common/qgstutils_p.h>
++ #include <common/qgst_p.h>
++
++ #include <QtMultimedia/qaudioformat.h>
++
++ #include <chrono>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace {
++
++ const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = {
++ nullptr,
++ #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
++ "U8",
++ "S16LE",
++ "S32LE",
++ "F32LE"
++ #else
++ "U8",
++ "S16BE",
++ "S32BE",
++ "F32BE"
++ #endif
++ };
++
++ QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt)
++ {
++ if (fmt) {
++ for (int i = 1; i < QAudioFormat::NSampleFormats; ++i) {
++ if (strcmp(fmt, audioSampleFormatNames[i]))
++ continue;
++ return QAudioFormat::SampleFormat(i);
++ }
++ }
++ return QAudioFormat::Unknown;
++ }
++
++ } // namespace
++
++ /*
++ Returns audio format for a sample \a sample.
++ If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned.
++ */
++ QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample)
++ {
++ auto caps = QGstCaps(gst_sample_get_caps(sample), QGstCaps::NeedsRef);
++ if (!caps)
++ return {};
++ return audioFormatForCaps(caps);
++ }
++
++ QAudioFormat QGstUtils::audioFormatForCaps(const QGstCaps &caps)
++ {
++ QAudioFormat format;
++ QGstStructureView s = caps.at(0);
++ if (s.name() != "audio/x-raw")
++ return format;
++
++ auto rate = s["rate"].toInt();
++ auto channels = s["channels"].toInt();
++ QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(s["format"].toString());
++ if (!rate || !channels || fmt == QAudioFormat::Unknown)
++ return format;
++
++ format.setSampleRate(*rate);
++ format.setChannelCount(*channels);
++ format.setSampleFormat(fmt);
++
++ return format;
++ }
++
++ /*
++ Builds GstCaps for an audio format \a format.
++ Returns 0 if the audio format is not valid.
++
++ \note Caller must unreference GstCaps.
++ */
++
++ QGstCaps QGstUtils::capsForAudioFormat(const QAudioFormat &format)
++ {
++ if (!format.isValid())
++ return {};
++
++ auto sampleFormat = format.sampleFormat();
++ auto caps = gst_caps_new_simple(
++ "audio/x-raw",
++ "format" , G_TYPE_STRING, audioSampleFormatNames[sampleFormat],
++ "rate" , G_TYPE_INT , format.sampleRate(),
++ "channels", G_TYPE_INT , format.channelCount(),
++ "layout" , G_TYPE_STRING, "interleaved",
++ nullptr);
++
++ return QGstCaps(caps, QGstCaps::HasRef);
++ }
++
++ QList<QAudioFormat::SampleFormat> QGValue::getSampleFormats() const
++ {
++ if (!GST_VALUE_HOLDS_LIST(value))
++ return {};
++
++ QList<QAudioFormat::SampleFormat> formats;
++ guint nFormats = gst_value_list_get_size(value);
++ for (guint f = 0; f < nFormats; ++f) {
++ QGValue v = QGValue{ gst_value_list_get_value(value, f) };
++ auto *name = v.toString();
++ QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(name);
++ if (fmt == QAudioFormat::Unknown)
++ continue;
++ formats.append(fmt);
++ }
++ return formats;
++ }
++
++ void QGstUtils::setFrameTimeStampsFromBuffer(QVideoFrame *frame, GstBuffer *buffer)
++ {
++ using namespace std::chrono;
++ using namespace std::chrono_literals;
++
++ // GStreamer uses nanoseconds, Qt uses microseconds
++ nanoseconds startTime{ GST_BUFFER_TIMESTAMP(buffer) };
++ if (startTime >= 0ns) {
++ frame->setStartTime(floor<microseconds>(startTime).count());
++
++ nanoseconds duration{ GST_BUFFER_DURATION(buffer) };
++ if (duration >= 0ns)
++ frame->setEndTime(floor<microseconds>(startTime + duration).count());
++ }
++ }
++
++ GList *qt_gst_video_sinks()
++ {
++ return gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK
++ | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO,
++ GST_RANK_MARGINAL);
++ }
++
++ QLocale::Language QGstUtils::codeToLanguage(const gchar *lang)
++ {
++ return QLocale::codeToLanguage(QString::fromUtf8(lang), QLocale::AnyLanguageCode);
++ }
++
++ // QTBUG-125249: gstreamer tries "to keep the input height (because of interlacing)". Can we align
++ // the behavior between GStreamer and FFmpeg?
++ QSize QGstUtils::qCalculateFrameSizeGStreamer(QSize resolution, Fraction par)
++ {
++ if (!resolution.isValid()) {
++ qWarning() << Q_FUNC_INFO << "invalid resolution when calculating frame size";
++ return resolution;
++ }
++
++ if (par.numerator == par.denominator || par.numerator < 1 || par.denominator < 1)
++ return resolution;
++
++ return QSize{
++ resolution.width() * par.numerator / par.denominator,
++ resolution.height(),
++ };
++ }
++
++ QT_END_NAMESPACE