1 | // Copyright (C) 2016 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/qgstutils_p.h> |
5 | #include <common/qgst_p.h> |
6 | |
7 | #include <QtMultimedia/qaudioformat.h> |
8 | |
9 | #include <chrono> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | namespace { |
14 | |
15 | const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = { |
16 | nullptr, |
17 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
18 | "U8" , |
19 | "S16LE" , |
20 | "S32LE" , |
21 | "F32LE" |
22 | #else |
23 | "U8" , |
24 | "S16BE" , |
25 | "S32BE" , |
26 | "F32BE" |
27 | #endif |
28 | }; |
29 | |
30 | QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt) |
31 | { |
32 | if (fmt) { |
33 | for (int i = 1; i < QAudioFormat::NSampleFormats; ++i) { |
34 | if (strcmp(s1: fmt, s2: audioSampleFormatNames[i])) |
35 | continue; |
36 | return QAudioFormat::SampleFormat(i); |
37 | } |
38 | } |
39 | return QAudioFormat::Unknown; |
40 | } |
41 | |
42 | } // namespace |
43 | |
44 | /* |
45 | Returns audio format for a sample \a sample. |
46 | If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. |
47 | */ |
48 | QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample) |
49 | { |
50 | auto caps = QGstCaps(gst_sample_get_caps(sample), QGstCaps::NeedsRef); |
51 | if (caps.isNull()) |
52 | return {}; |
53 | return audioFormatForCaps(caps); |
54 | } |
55 | |
56 | QAudioFormat QGstUtils::audioFormatForCaps(const QGstCaps &caps) |
57 | { |
58 | QAudioFormat format; |
59 | QGstStructureView s = caps.at(index: 0); |
60 | if (s.name() != "audio/x-raw" ) |
61 | return format; |
62 | |
63 | auto rate = s["rate" ].toInt(); |
64 | auto channels = s["channels" ].toInt(); |
65 | QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(fmt: s["format" ].toString()); |
66 | if (!rate || !channels || fmt == QAudioFormat::Unknown) |
67 | return format; |
68 | |
69 | format.setSampleRate(*rate); |
70 | format.setChannelCount(*channels); |
71 | format.setSampleFormat(fmt); |
72 | |
73 | return format; |
74 | } |
75 | |
76 | /* |
77 | Builds GstCaps for an audio format \a format. |
78 | Returns 0 if the audio format is not valid. |
79 | |
80 | \note Caller must unreference GstCaps. |
81 | */ |
82 | |
83 | QGstCaps QGstUtils::capsForAudioFormat(const QAudioFormat &format) |
84 | { |
85 | if (!format.isValid()) |
86 | return {}; |
87 | |
88 | auto sampleFormat = format.sampleFormat(); |
89 | auto caps = gst_caps_new_simple( |
90 | media_type: "audio/x-raw" , |
91 | fieldname: "format" , G_TYPE_STRING, audioSampleFormatNames[sampleFormat], |
92 | "rate" , G_TYPE_INT , format.sampleRate(), |
93 | "channels" , G_TYPE_INT , format.channelCount(), |
94 | "layout" , G_TYPE_STRING, "interleaved" , |
95 | nullptr); |
96 | |
97 | return QGstCaps(caps, QGstCaps::HasRef); |
98 | } |
99 | |
100 | QList<QAudioFormat::SampleFormat> QGValue::getSampleFormats() const |
101 | { |
102 | if (!GST_VALUE_HOLDS_LIST(value)) |
103 | return {}; |
104 | |
105 | QList<QAudioFormat::SampleFormat> formats; |
106 | guint nFormats = gst_value_list_get_size(value); |
107 | for (guint f = 0; f < nFormats; ++f) { |
108 | QGValue v = QGValue{ gst_value_list_get_value(value, index: f) }; |
109 | auto *name = v.toString(); |
110 | QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(fmt: name); |
111 | if (fmt == QAudioFormat::Unknown) |
112 | continue; |
113 | formats.append(t: fmt); |
114 | } |
115 | return formats; |
116 | } |
117 | |
118 | void QGstUtils::setFrameTimeStampsFromBuffer(QVideoFrame *frame, GstBuffer *buffer) |
119 | { |
120 | using namespace std::chrono; |
121 | using namespace std::chrono_literals; |
122 | |
123 | // GStreamer uses nanoseconds, Qt uses microseconds |
124 | nanoseconds startTime{ GST_BUFFER_TIMESTAMP(buffer) }; |
125 | if (startTime >= 0ns) { |
126 | frame->setStartTime(floor<microseconds>(d: startTime).count()); |
127 | |
128 | nanoseconds duration{ GST_BUFFER_DURATION(buffer) }; |
129 | if (duration >= 0ns) |
130 | frame->setEndTime(floor<microseconds>(d: startTime + duration).count()); |
131 | } |
132 | } |
133 | |
134 | GList *qt_gst_video_sinks() |
135 | { |
136 | return gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK |
137 | | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, |
138 | minrank: GST_RANK_MARGINAL); |
139 | } |
140 | |
141 | QLocale::Language QGstUtils::codeToLanguage(const gchar *lang) |
142 | { |
143 | return QLocale::codeToLanguage(languageCode: QString::fromUtf8(utf8: lang), codeTypes: QLocale::AnyLanguageCode); |
144 | } |
145 | |
146 | QT_END_NAMESPACE |
147 | |