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_discoverer_p.h"
5
6#include <QtMultimedia/qmediaformat.h>
7
8#include <common/qglist_helper_p.h>
9#include <common/qgst_debug_p.h>
10#include <common/qgstreamermetadata_p.h>
11#include <common/qgstutils_p.h>
12#include <uri_handler/qgstreamer_qiodevice_handler_p.h>
13
14QT_BEGIN_NAMESPACE
15
16namespace QGst {
17
18namespace {
19
20template <typename StreamInfo>
21struct GstDiscovererStreamInfoList : QGstUtils::GListRangeAdaptor<StreamInfo *>
22{
23 using BaseType = QGstUtils::GListRangeAdaptor<StreamInfo *>;
24 using BaseType::BaseType;
25
26 ~GstDiscovererStreamInfoList() { gst_discoverer_stream_info_list_free(BaseType::head); }
27};
28
29QGstTagListHandle duplicateTagList(const GstTagList *tagList)
30{
31 if (!tagList)
32 return {};
33 return QGstTagListHandle{
34 gst_tag_list_copy(tagList),
35 QGstTagListHandle::HasRef,
36 };
37}
38
39QGstDiscovererStreamInfo parseGstDiscovererStreamInfo(GstDiscovererStreamInfo *info)
40{
41 QGstDiscovererStreamInfo result;
42
43 result.streamID = QString::fromUtf8(utf8: gst_discoverer_stream_info_get_stream_id(info));
44 result.tags = duplicateTagList(tagList: gst_discoverer_stream_info_get_tags(info));
45
46 result.streamNumber = gst_discoverer_stream_info_get_stream_number(info);
47
48 result.caps = QGstCaps{
49 gst_discoverer_stream_info_get_caps(info),
50 QGstCaps::HasRef,
51 };
52
53 return result;
54}
55
56template <typename T>
57constexpr bool isStreamInfo = std::is_same_v<std::decay_t<T>, GstDiscovererVideoInfo>
58 || std::is_same_v<std::decay_t<T>, GstDiscovererAudioInfo>
59 || std::is_same_v<std::decay_t<T>, GstDiscovererSubtitleInfo>
60 || std::is_same_v<std::decay_t<T>, GstDiscovererContainerInfo>;
61
62template <typename T>
63QGstDiscovererStreamInfo parseGstDiscovererStreamInfo(T *info)
64{
65 static_assert(isStreamInfo<T>);
66 return parseGstDiscovererStreamInfo(GST_DISCOVERER_STREAM_INFO(info));
67}
68
69QGstDiscovererVideoInfo parseGstDiscovererVideoInfo(GstDiscovererVideoInfo *info)
70{
71 QGstDiscovererVideoInfo result;
72 static_cast<QGstDiscovererStreamInfo &>(result) = parseGstDiscovererStreamInfo(info);
73
74 result.size = QSize{
75 int(gst_discoverer_video_info_get_width(info)),
76 int(gst_discoverer_video_info_get_height(info)),
77 };
78
79 result.bitDepth = gst_discoverer_video_info_get_depth(info);
80 result.framerate = Fraction{
81 .numerator: int(gst_discoverer_video_info_get_framerate_num(info)),
82 .denominator: int(gst_discoverer_video_info_get_framerate_denom(info)),
83 };
84
85 result.pixelAspectRatio = Fraction{
86 .numerator: int(gst_discoverer_video_info_get_par_num(info)),
87 .denominator: int(gst_discoverer_video_info_get_par_denom(info)),
88 };
89
90 result.isInterlaced = gst_discoverer_video_info_is_interlaced(info);
91 result.bitrate = int(gst_discoverer_video_info_get_bitrate(info));
92 result.maxBitrate = int(gst_discoverer_video_info_get_max_bitrate(info));
93 result.isImage = int(gst_discoverer_video_info_is_image(info));
94
95 return result;
96}
97
98QGstDiscovererAudioInfo parseGstDiscovererAudioInfo(GstDiscovererAudioInfo *info)
99{
100 QGstDiscovererAudioInfo result;
101 static_cast<QGstDiscovererStreamInfo &>(result) = parseGstDiscovererStreamInfo(info);
102
103 result.channels = int(gst_discoverer_audio_info_get_channels(info));
104 result.channelMask = gst_discoverer_audio_info_get_channel_mask(info);
105 result.sampleRate = gst_discoverer_audio_info_get_sample_rate(info);
106 result.bitsPerSample = gst_discoverer_audio_info_get_depth(info);
107
108 result.bitrate = int(gst_discoverer_audio_info_get_bitrate(info));
109 result.maxBitrate = int(gst_discoverer_audio_info_get_max_bitrate(info));
110 result.language = QGstUtils::codeToLanguage(gst_discoverer_audio_info_get_language(info));
111
112 return result;
113}
114
115QGstDiscovererSubtitleInfo parseGstDiscovererSubtitleInfo(GstDiscovererSubtitleInfo *info)
116{
117 QGstDiscovererSubtitleInfo result;
118 static_cast<QGstDiscovererStreamInfo &>(result) = parseGstDiscovererStreamInfo(info);
119 result.language = QGstUtils::codeToLanguage(gst_discoverer_subtitle_info_get_language(info));
120 return result;
121}
122
123QGstDiscovererContainerInfo parseGstDiscovererContainerInfo(GstDiscovererContainerInfo *info)
124{
125 QGstDiscovererContainerInfo result;
126 static_cast<QGstDiscovererStreamInfo &>(result) = parseGstDiscovererStreamInfo(info);
127
128 result.tags = duplicateTagList(tagList: gst_discoverer_container_info_get_tags(info));
129
130 return result;
131}
132
133QGstDiscovererInfo parseGstDiscovererInfo(GstDiscovererInfo *info)
134{
135 using namespace QGstUtils;
136
137 QGstDiscovererInfo result;
138 result.isLive = gst_discoverer_info_get_live(info);
139 result.isSeekable = gst_discoverer_info_get_seekable(info);
140
141 GstClockTime duration = gst_discoverer_info_get_duration(info);
142 if (duration != GST_CLOCK_TIME_NONE)
143 result.duration = std::chrono::nanoseconds{ duration };
144
145 GstDiscovererStreamInfo *streamInfo = gst_discoverer_info_get_stream_info(info);
146 if (streamInfo && GST_IS_DISCOVERER_CONTAINER_INFO(streamInfo))
147 result.containerInfo =
148 parseGstDiscovererContainerInfo(GST_DISCOVERER_CONTAINER_INFO(streamInfo));
149 result.tags = duplicateTagList(tagList: gst_discoverer_info_get_tags(info));
150
151 GstDiscovererStreamInfoList<GstDiscovererVideoInfo> videoStreams{
152 gst_discoverer_info_get_video_streams(info),
153 };
154 for (GstDiscovererVideoInfo *videoInfo : videoStreams)
155 result.videoStreams.emplace_back(args: parseGstDiscovererVideoInfo(info: videoInfo));
156
157 GstDiscovererStreamInfoList<GstDiscovererAudioInfo> audioStreams{
158 gst_discoverer_info_get_audio_streams(info),
159 };
160 for (GstDiscovererAudioInfo *audioInfo : audioStreams)
161 result.audioStreams.emplace_back(args: parseGstDiscovererAudioInfo(info: audioInfo));
162
163 GstDiscovererStreamInfoList<GstDiscovererSubtitleInfo> subtitleStreams{
164 gst_discoverer_info_get_subtitle_streams(info),
165 };
166 for (GstDiscovererSubtitleInfo *subtitleInfo : subtitleStreams)
167 result.subtitleStreams.emplace_back(args: parseGstDiscovererSubtitleInfo(info: subtitleInfo));
168
169 GstDiscovererStreamInfoList<GstDiscovererContainerInfo> containerStreams{
170 gst_discoverer_info_get_container_streams(info),
171 };
172 for (GstDiscovererContainerInfo *containerInfo : containerStreams)
173 result.containerStreams.emplace_back(args: parseGstDiscovererContainerInfo(info: containerInfo));
174
175 return result;
176}
177
178} // namespace
179
180//----------------------------------------------------------------------------------------------------------------------
181
182static constexpr std::chrono::nanoseconds discovererTimeout = std::chrono::seconds(10);
183
184QGstDiscoverer::QGstDiscoverer()
185 : m_instance{
186 gst_discoverer_new(timeout: discovererTimeout.count(), err: nullptr),
187 }
188{
189}
190
191QMaybe<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(const QString &uri)
192{
193 return discover(uri.toUtf8().constData());
194}
195
196QMaybe<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(const QUrl &url)
197{
198 return discover(url.toEncoded().constData());
199}
200
201QMaybe<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(QIODevice *device)
202{
203 return discover(url: qGstRegisterQIODevice(device));
204}
205
206QMaybe<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(const char *uri)
207{
208 QUniqueGErrorHandle error;
209 QGstDiscovererInfoHandle info{
210 gst_discoverer_discover_uri(discoverer: m_instance.get(), uri, err: &error),
211 };
212
213 if (error)
214 return {
215 unexpect,
216 std::move(error),
217 };
218
219 QGstDiscovererInfo result = parseGstDiscovererInfo(info: info.get());
220 return result;
221}
222
223//----------------------------------------------------------------------------------------------------------------------
224
225QMediaMetaData toContainerMetadata(const QGstDiscovererInfo &info)
226{
227 QMediaMetaData metadata;
228 using Key = QMediaMetaData::Key;
229 using namespace std::chrono;
230
231 if (info.containerInfo)
232 extendMetaDataFromTagList(metadata, info.containerInfo->tags);
233 else
234 extendMetaDataFromTagList(metadata, info.tags);
235
236 auto updateMetadata = [&](Key key, auto value) {
237 QVariant currentValue = metadata.value(k: key);
238 if (!currentValue.isValid() || currentValue != value)
239 metadata.insert(k: key, value);
240 };
241
242 if (info.duration)
243 updateMetadata(Key::Duration,
244 QVariant::fromValue(value: round<milliseconds>(d: *info.duration).count()));
245
246 return metadata;
247}
248
249void addMissingKeysFromTaglist(QMediaMetaData &metadata, const QGstTagListHandle &tagList)
250{
251 QMediaMetaData tagMetaData = taglistToMetaData(tagList);
252 for (auto element : tagMetaData.asKeyValueRange()) {
253 if (metadata.keys().contains(t: element.first))
254 continue;
255
256 metadata.insert(k: element.first, value: element.second);
257 }
258}
259
260template <typename ValueType>
261static void updateMetadata(QMediaMetaData &metadata, QMediaMetaData::Key key, ValueType value)
262{
263 QVariant currentValue = metadata.value(k: key);
264 if (!currentValue.isValid() || currentValue != value)
265 metadata.insert(k: key, value);
266}
267
268QMediaMetaData toStreamMetadata(const QGstDiscovererVideoInfo &info)
269{
270 QMediaMetaData metadata;
271 using Key = QMediaMetaData::Key;
272
273 updateMetadata(metadata, key: Key::VideoBitRate, value: info.bitrate);
274
275 extendMetaDataFromCaps(metadata, info.caps);
276 addMissingKeysFromTaglist(metadata, tagList: info.tags);
277
278 return metadata;
279}
280
281QMediaMetaData toStreamMetadata(const QGstDiscovererAudioInfo &info)
282{
283 QMediaMetaData metadata;
284 using Key = QMediaMetaData::Key;
285
286 updateMetadata(metadata, key: Key::AudioBitRate, value: info.bitrate);
287 updateMetadata(metadata, key: Key::Language, value: info.language);
288
289 extendMetaDataFromCaps(metadata, info.caps);
290 addMissingKeysFromTaglist(metadata, tagList: info.tags);
291
292 return metadata;
293}
294
295QMediaMetaData toStreamMetadata(const QGstDiscovererSubtitleInfo &info)
296{
297 QMediaMetaData metadata;
298 using Key = QMediaMetaData::Key;
299
300 updateMetadata(metadata, key: Key::Language, value: info.language);
301
302 extendMetaDataFromCaps(metadata, info.caps);
303 addMissingKeysFromTaglist(metadata, tagList: info.tags);
304
305 return metadata;
306}
307
308} // namespace QGst
309
310QT_END_NAMESPACE
311

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtmultimedia/src/plugins/multimedia/gstreamer/common/qgst_discoverer.cpp