Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgst_discoverer.cpp
Go to the documentation of this file.
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
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{
42
43 result.streamID = QString::fromUtf8(gst_discoverer_stream_info_get_stream_id(info));
44 result.tags = duplicateTagList(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{
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 int(gst_discoverer_video_info_get_framerate_num(info)),
82 int(gst_discoverer_video_info_get_framerate_denom(info)),
83 };
84
85 result.pixelAspectRatio = Fraction{
86 int(gst_discoverer_video_info_get_par_num(info)),
87 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{
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{
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{
126 static_cast<QGstDiscovererStreamInfo &>(result) = parseGstDiscovererStreamInfo(info);
127
128 result.tags = duplicateTagList(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 QGstDiscovererStreamInfoHandle streamInfo{
146 gst_discoverer_info_get_stream_info(info),
147 QGstDiscovererStreamInfoHandle::HasRef,
148 };
149 if (streamInfo && GST_IS_DISCOVERER_CONTAINER_INFO(streamInfo.get()))
150 result.containerInfo =
151 parseGstDiscovererContainerInfo(GST_DISCOVERER_CONTAINER_INFO(streamInfo.get()));
152 result.tags = duplicateTagList(gst_discoverer_info_get_tags(info));
153
154 GstDiscovererStreamInfoList<GstDiscovererVideoInfo> videoStreams{
155 gst_discoverer_info_get_video_streams(info),
156 };
157 for (GstDiscovererVideoInfo *videoInfo : videoStreams) {
158 QGstDiscovererVideoInfo item = parseGstDiscovererVideoInfo(videoInfo);
159 if (!item.streamID.isNull()) // Ignore stream info without stream ID
160 result.videoStreams.emplace_back(std::move(item));
161 }
162 GstDiscovererStreamInfoList<GstDiscovererAudioInfo> audioStreams{
163 gst_discoverer_info_get_audio_streams(info),
164 };
165 for (GstDiscovererAudioInfo *audioInfo : audioStreams) {
166 QGstDiscovererAudioInfo item = parseGstDiscovererAudioInfo(audioInfo);
167 if (!item.streamID.isNull()) // Ignore stream info without stream ID
168 result.audioStreams.emplace_back(std::move(item));
169 }
170 GstDiscovererStreamInfoList<GstDiscovererSubtitleInfo> subtitleStreams{
171 gst_discoverer_info_get_subtitle_streams(info),
172 };
173 for (GstDiscovererSubtitleInfo *subtitleInfo : subtitleStreams) {
174 QGstDiscovererSubtitleInfo item = parseGstDiscovererSubtitleInfo(subtitleInfo);
175 if (!item.streamID.isNull()) // Ignore stream info without stream ID
176 result.subtitleStreams.emplace_back(std::move(item));
177 }
178
179 GstDiscovererStreamInfoList<GstDiscovererContainerInfo> containerStreams{
180 gst_discoverer_info_get_container_streams(info),
181 };
182 for (GstDiscovererContainerInfo *containerInfo : containerStreams)
183 result.containerStreams.emplace_back(parseGstDiscovererContainerInfo(containerInfo));
184
185 return result;
186}
187
188} // namespace
189
190//----------------------------------------------------------------------------------------------------------------------
191
192static constexpr std::chrono::nanoseconds discovererTimeout = std::chrono::seconds(10);
193
201
203{
204 return discover(uri.toUtf8().constData());
205}
206
207q23::expected<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(const QUrl &url)
208{
209 return discover(url.toEncoded().constData());
210}
211
212q23::expected<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(QIODevice *device)
213{
214 return discover(qGstRegisterQIODevice(device));
215}
216
217q23::expected<QGstDiscovererInfo, QUniqueGErrorHandle> QGstDiscoverer::discover(const char *uri)
218{
219 QUniqueGErrorHandle error;
220 QGstDiscovererInfoHandle info{
221 gst_discoverer_discover_uri(m_instance.get(), uri, &error),
222 QGstDiscovererInfoHandle::HasRef,
223 };
224
225 if (error)
226 return q23::unexpected{
227 std::move(error),
228 };
229
230 QGstDiscovererInfo result = parseGstDiscovererInfo(info.get());
231 return result;
232}
233
234//----------------------------------------------------------------------------------------------------------------------
235
236QMediaMetaData toContainerMetadata(const QGstDiscovererInfo &info)
237{
238 QMediaMetaData metadata;
239 using Key = QMediaMetaData::Key;
240 using namespace std::chrono;
241
242 if (info.containerInfo)
243 extendMetaDataFromTagList(metadata, info.containerInfo->tags);
244 else
245 extendMetaDataFromTagList(metadata, info.tags);
246
247 auto updateMetadata = [&](Key key, auto value) {
248 QVariant currentValue = metadata.value(key);
249 if (!currentValue.isValid() || currentValue != value)
250 metadata.insert(key, value);
251 };
252
253 if (info.duration)
254 updateMetadata(Key::Duration,
255 QVariant::fromValue(round<milliseconds>(*info.duration).count()));
256
257 return metadata;
258}
259
260void addMissingKeysFromTaglist(QMediaMetaData &metadata, const QGstTagListHandle &tagList)
261{
262 QMediaMetaData tagMetaData = taglistToMetaData(tagList);
263 for (auto element : tagMetaData.asKeyValueRange()) {
264 if (metadata.keys().contains(element.first))
265 continue;
266
267 metadata.insert(element.first, element.second);
268 }
269}
270
271template <typename ValueType>
272static void updateMetadata(QMediaMetaData &metadata, QMediaMetaData::Key key, ValueType value)
273{
274 QVariant currentValue = metadata.value(key);
275 if (!currentValue.isValid() || currentValue != value)
276 metadata.insert(key, value);
277}
278
279QMediaMetaData toStreamMetadata(const QGstDiscovererVideoInfo &info)
280{
281 QMediaMetaData metadata;
282 using Key = QMediaMetaData::Key;
283
284 updateMetadata(metadata, Key::VideoBitRate, info.bitrate);
285
286 extendMetaDataFromCaps(metadata, info.caps);
287 addMissingKeysFromTaglist(metadata, info.tags);
288
289 return metadata;
290}
291
292QMediaMetaData toStreamMetadata(const QGstDiscovererAudioInfo &info)
293{
294 QMediaMetaData metadata;
295 using Key = QMediaMetaData::Key;
296
297 updateMetadata(metadata, Key::AudioBitRate, info.bitrate);
298 updateMetadata(metadata, Key::Language, info.language);
299
300 extendMetaDataFromCaps(metadata, info.caps);
301 addMissingKeysFromTaglist(metadata, info.tags);
302
303 return metadata;
304}
305
307{
308 QMediaMetaData metadata;
309 using Key = QMediaMetaData::Key;
310
311 updateMetadata(metadata, Key::Language, info.language);
312
313 extendMetaDataFromCaps(metadata, info.caps);
314 addMissingKeysFromTaglist(metadata, info.tags);
315
316 return metadata;
317}
318
319} // namespace QGst
320
321QT_END_NAMESPACE
q23::expected< QGstDiscovererInfo, QUniqueGErrorHandle > discover(const QString &uri)
QMediaMetaData toStreamMetadata(const QGstDiscovererAudioInfo &info)
static constexpr std::chrono::nanoseconds discovererTimeout
QMediaMetaData toStreamMetadata(const QGstDiscovererSubtitleInfo &info)
QMediaMetaData toStreamMetadata(const QGstDiscovererVideoInfo &info)
QMediaMetaData toContainerMetadata(const QGstDiscovererInfo &info)
static void updateMetadata(QMediaMetaData &metadata, QMediaMetaData::Key key, ValueType value)
void addMissingKeysFromTaglist(QMediaMetaData &metadata, const QGstTagListHandle &tagList)