Revision control

Copy as Markdown

// 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