Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgstreamermetadata.cpp
Go to the documentation of this file.
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
5#include <QtMultimedia/qmediametadata.h>
6#include <QtMultimedia/qtvideo.h>
7#include <QtCore/qdebug.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qlocale.h>
10#include <QtCore/qtimezone.h>
11#include <QtGui/qimage.h>
12
13#include <gst/gstversion.h>
15#include <common/qgstutils_p.h>
16
18
19namespace {
20
21namespace MetadataLookupImpl {
22
23#ifdef __cpp_lib_constexpr_algorithms
24# define constexpr_lookup constexpr
25#else
26# define constexpr_lookup /*constexpr*/
27#endif
28
34
35constexpr const char *toTag(const char *t)
36{
37 return t;
38}
39constexpr const char *toTag(const MetadataKeyValuePair &kv)
40{
41 return kv.tag;
42}
43
45{
46 return k;
47}
49{
50 return kv.key;
51}
52
53constexpr auto compareByKey = [](const auto &lhs, const auto &rhs) {
54 return toKey(lhs) < toKey(rhs);
55};
56
57constexpr auto compareByTag = [](const auto &lhs, const auto &rhs) {
58 return std::strcmp(toTag(lhs), toTag(rhs)) < 0;
59};
60
62{
63 std::array<MetadataKeyValuePair, 22> lookupTable{ {
64 { GST_TAG_TITLE, QMediaMetaData::Title },
65 { GST_TAG_COMMENT, QMediaMetaData::Comment },
66 { GST_TAG_DESCRIPTION, QMediaMetaData::Description },
67 { GST_TAG_GENRE, QMediaMetaData::Genre },
68 { GST_TAG_DATE_TIME, QMediaMetaData::Date },
69 { GST_TAG_DATE, QMediaMetaData::Date },
70
71 { GST_TAG_LANGUAGE_CODE, QMediaMetaData::Language },
72
73 { GST_TAG_ORGANIZATION, QMediaMetaData::Publisher },
74 { GST_TAG_COPYRIGHT, QMediaMetaData::Copyright },
75
76 // Media
77 { GST_TAG_DURATION, QMediaMetaData::Duration },
78
79 // Audio
80 { GST_TAG_BITRATE, QMediaMetaData::AudioBitRate },
81 { GST_TAG_AUDIO_CODEC, QMediaMetaData::AudioCodec },
82
83 // Music
84 { GST_TAG_ALBUM, QMediaMetaData::AlbumTitle },
85 { GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist },
86 { GST_TAG_ARTIST, QMediaMetaData::ContributingArtist },
87 { GST_TAG_TRACK_NUMBER, QMediaMetaData::TrackNumber },
88
89 { GST_TAG_PREVIEW_IMAGE, QMediaMetaData::ThumbnailImage },
90 { GST_TAG_IMAGE, QMediaMetaData::CoverArtImage },
91
92 // Image/Video
93 { "resolution", QMediaMetaData::Resolution },
94 { GST_TAG_IMAGE_ORIENTATION, QMediaMetaData::Orientation },
95
96 // Video
97 { GST_TAG_VIDEO_CODEC, QMediaMetaData::VideoCodec },
98
99 // Movie
100 { GST_TAG_PERFORMER, QMediaMetaData::LeadPerformer },
101 } };
102
103 std::sort(lookupTable.begin(), lookupTable.end(),
104 [](const MetadataKeyValuePair &lhs, const MetadataKeyValuePair &rhs) {
105 return std::string_view(lhs.tag) < std::string_view(rhs.tag);
106 });
107 return lookupTable;
108}
109
113 std::sort(array.begin(), array.end(), compareByKey);
114 return array;
115}();
116
117} // namespace MetadataLookupImpl
118
120{
121 if (tag == nullptr)
122 return QMediaMetaData::Key(-1);
123
124 using namespace MetadataLookupImpl;
125 auto foundIterator = std::lower_bound(gstTagToMetaDataKey.begin(), gstTagToMetaDataKey.end(),
126 tag, compareByTag);
127 if (std::strcmp(foundIterator->tag, tag) == 0)
128 return foundIterator->key;
129
130 return QMediaMetaData::Key(-1);
131}
132
134{
135 using namespace MetadataLookupImpl;
136 auto foundIterator = std::lower_bound(metaDataKeyToGstTag.begin(), metaDataKeyToGstTag.end(),
137 key, compareByKey);
138 if (foundIterator->key == key)
139 return foundIterator->tag;
140
141 return nullptr;
142}
143
144#undef constexpr_lookup
145
147{
148 using namespace std::string_view_literals;
149
150 if (string == "rotate-90"sv)
152 if (string == "rotate-180"sv)
154 if (string == "rotate-270"sv)
156 if (string == "rotate-0"sv)
158
159 qCritical() << "cannot parse orientation: {}" << string;
161}
162
163QDateTime parseDate(const GValue &val)
164{
165 Q_ASSERT(G_VALUE_TYPE(&val) == G_TYPE_DATE);
166
167 const GDate *date = (const GDate *)g_value_get_boxed(&val);
168 if (!g_date_valid(date))
169 return {};
170
171 int year = g_date_get_year(date);
172 int month = g_date_get_month(date);
173 int day = g_date_get_day(date);
174 return QDateTime(QDate(year, month, day), QTime());
175}
176
178{
179 Q_ASSERT(G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME);
180
181 const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val);
182 int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0;
183 int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0;
184 int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0;
185 int hour = 0;
186 int minute = 0;
187 int second = 0;
188 float tz = 0;
189 if (gst_date_time_has_time(dateTime)) {
190 hour = gst_date_time_get_hour(dateTime);
191 minute = gst_date_time_get_minute(dateTime);
192 second = gst_date_time_get_second(dateTime);
193 tz = gst_date_time_get_time_zone_offset(dateTime);
194 }
195 return QDateTime{
196 QDate(year, month, day),
197 QTime(hour, minute, second),
198 QTimeZone(tz * 60 * 60),
199 };
200}
201
202QImage parseImage(const GValue &val)
203{
204 Q_ASSERT(G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE);
205
206 GstSample *sample = (GstSample *)g_value_get_boxed(&val);
207 GstCaps *caps = gst_sample_get_caps(sample);
208 if (caps && !gst_caps_is_empty(caps)) {
209 GstStructure *structure = gst_caps_get_structure(caps, 0);
210 const gchar *name = gst_structure_get_name(structure);
211 if (QByteArray(name).startsWith("image/")) {
212 GstBuffer *buffer = gst_sample_get_buffer(sample);
213 if (buffer) {
214 GstMapInfo info;
215 gst_buffer_map(buffer, &info, GST_MAP_READ);
216 QImage image = QImage::fromData(info.data, info.size, name);
217 gst_buffer_unmap(buffer, &info);
218 return image;
219 }
220 }
221 }
222
223 return {};
224}
225
226std::optional<double> parseFractionAsDouble(const GValue &val)
227{
228 Q_ASSERT(G_VALUE_TYPE(&val) == GST_TYPE_FRACTION);
229
230 int nom = gst_value_get_fraction_numerator(&val);
231 int denom = gst_value_get_fraction_denominator(&val);
232 if (denom == 0)
233 return std::nullopt;
234 return double(nom) / double(denom);
235}
236
237constexpr std::string_view extendedComment{ GST_TAG_EXTENDED_COMMENT };
238
239void addTagsFromExtendedComment(const GstTagList *list, const gchar *tag, QMediaMetaData &metadata)
240{
241 using namespace Qt::Literals;
243
244 int entryCount = gst_tag_list_get_tag_size(list, tag);
245 for (int i = 0; i != entryCount; ++i) {
246 const GValue *value = gst_tag_list_get_value_index(list, tag, i);
247
248 const QLatin1StringView strValue{ g_value_get_string(value) };
249
250 auto equalIndex = strValue.indexOf(QLatin1StringView("="));
251 if (equalIndex == -1) {
252 qDebug() << "Cannot parse GST_TAG_EXTENDED_COMMENT entry: " << value;
253 continue;
254 }
255
256 const QLatin1StringView key = strValue.first(equalIndex);
257 const QLatin1StringView valueString = strValue.last(strValue.size() - equalIndex - 1);
258
259 if (key == "DURATION"_L1) {
261 gst_date_time_new_from_iso8601_string(valueString.data()),
262 };
263
264 if (duration) {
265 using namespace std::chrono;
266
267 auto chronoDuration = hours(gst_date_time_get_hour(duration.get()))
268 + minutes(gst_date_time_get_minute(duration.get()))
269 + seconds(gst_date_time_get_second(duration.get()))
270 + microseconds(gst_date_time_get_microsecond(duration.get()));
271
273 QVariant::fromValue(round<milliseconds>(chronoDuration).count()));
274 }
275 }
276 }
277}
278
279void addTagToMetaData(const GstTagList *list, const gchar *tag, void *userdata)
280{
281 QMediaMetaData &metadata = *reinterpret_cast<QMediaMetaData *>(userdata);
282
284 if (key == QMediaMetaData::Key(-1)) {
285 if (tag == extendedComment)
287
288 return;
289 }
290
291 GValue val{};
292 gst_tag_list_copy_value(&val, list, tag);
293
294 GType type = G_VALUE_TYPE(&val);
295
296 if (auto entryCount = gst_tag_list_get_tag_size(list, tag) != 0; entryCount != 1)
297 qWarning() << "addTagToMetaData: invaled entry count for" << tag << "-" << entryCount;
298
299 if (type == G_TYPE_STRING) {
300 const gchar *str_value = g_value_get_string(&val);
301
302 switch (key) {
304 metadata.insert(key,
307 break;
308 }
310 metadata.insert(key, QVariant::fromValue(parseRotationTag(str_value)));
311 break;
312 }
313 default:
314 metadata.insert(key, QString::fromUtf8(str_value));
315 break;
316 };
317 } else if (type == G_TYPE_INT) {
318 metadata.insert(key, g_value_get_int(&val));
319 } else if (type == G_TYPE_UINT) {
320 metadata.insert(key, g_value_get_uint(&val));
321 } else if (type == G_TYPE_LONG) {
322 metadata.insert(key, qint64(g_value_get_long(&val)));
323 } else if (type == G_TYPE_BOOLEAN) {
324 metadata.insert(key, g_value_get_boolean(&val));
325 } else if (type == G_TYPE_CHAR) {
326 metadata.insert(key, g_value_get_schar(&val));
327 } else if (type == G_TYPE_DOUBLE) {
328 metadata.insert(key, g_value_get_double(&val));
329 } else if (type == G_TYPE_DATE) {
330 if (!metadata.keys().contains(key)) {
332 if (date.isValid())
333 metadata.insert(key, date);
334 }
335 } else if (type == GST_TYPE_DATE_TIME) {
336 metadata.insert(key, parseDateTime(val));
337 } else if (type == GST_TYPE_SAMPLE) {
339 if (!image.isNull())
340 metadata.insert(key, image);
341 } else if (type == GST_TYPE_FRACTION) {
342 std::optional<double> fraction = parseFractionAsDouble(val);
343
344 if (fraction)
345 metadata.insert(key, *fraction);
346 }
347
348 g_value_unset(&val);
349}
350
351} // namespace
352
353QMediaMetaData taglistToMetaData(const GstTagList *tagList)
354{
356 if (tagList)
357 gst_tag_list_foreach(tagList, reinterpret_cast<GstTagForeachFunc>(&addTagToMetaData), &m);
358 return m;
359}
360
365
366static void applyMetaDataToTagSetter(const QMediaMetaData &metadata, GstTagSetter *element)
367{
368 gst_tag_setter_reset_tags(element);
369
370 for (QMediaMetaData::Key key : metadata.keys()) {
371 const char *tagName = keyToTag(key);
372 if (!tagName)
373 continue;
374 const QVariant &tagValue = metadata.value(key);
375
376 auto setTag = [&](const auto &value) {
377 gst_tag_setter_add_tags(element, GST_TAG_MERGE_REPLACE, tagName, value, nullptr);
378 };
379
380 switch (tagValue.typeId()) {
381 case QMetaType::QString:
382 setTag(tagValue.toString().toUtf8().constData());
383 break;
384 case QMetaType::Int:
385 case QMetaType::LongLong:
386 setTag(tagValue.toInt());
387 break;
388 case QMetaType::Double:
389 setTag(tagValue.toDouble());
390 break;
391 case QMetaType::QDate:
392 case QMetaType::QDateTime: {
393 QDateTime date = tagValue.toDateTime();
394
396 gst_date_time_new(date.offsetFromUtc() / 60. / 60., date.date().year(),
397 date.date().month(), date.date().day(), date.time().hour(),
398 date.time().minute(), date.time().second()),
400 };
401
402 setTag(dateTime.get());
403 break;
404 }
405 default: {
406 if (tagValue.typeId() == qMetaTypeId<QLocale::Language>()) {
409 .toUtf8();
410 setTag(language.constData());
411 }
412
413 break;
414 }
415 }
416 }
417}
418
419void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstElement &element)
420{
421 GstTagSetter *tagSetter = qGstSafeCast<GstTagSetter>(element.element());
422 if (tagSetter)
423 applyMetaDataToTagSetter(metadata, tagSetter);
424 else
425 qWarning() << "applyMetaDataToTagSetter failed: element not a GstTagSetter"
426 << element.name();
427}
428
429void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstBin &bin)
430{
431 GstIterator *elements = gst_bin_iterate_all_by_interface(bin.bin(), GST_TYPE_TAG_SETTER);
432 GValue item = {};
433
434 while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) {
435 GstElement *element = static_cast<GstElement *>(g_value_get_object(&item));
436 if (!element)
437 continue;
438
439 GstTagSetter *tagSetter = qGstSafeCast<GstTagSetter>(element);
440
441 if (tagSetter)
442 applyMetaDataToTagSetter(metadata, tagSetter);
443 }
444
445 gst_iterator_free(elements);
446}
447
static std::optional< QMediaMetaData::Key > toKey(AVMetadataItem *item)
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
\inmodule QtCore\reentrant
Definition qdatetime.h:283
\inmodule QtCore \reentrant
Definition qdatetime.h:29
constexpr bool isValid() const
Returns true if this date is valid; otherwise returns false.
Definition qdatetime.h:71
int month() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int day() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
GstElement * element() const
Definition qgst.cpp:1026
const char * name() const
Definition qgst.cpp:682
\inmodule QtGui
Definition qimage.h:37
static QImage fromData(QByteArrayView data, const char *format=nullptr)
Definition qimage.cpp:3841
constexpr const char * data() const noexcept
constexpr QLatin1Char last() const
constexpr QLatin1Char first() const
qsizetype indexOf(QStringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
static QString languageToCode(Language language, LanguageCodeTypes codeTypes=AnyLanguageCode)
Returns the two- or three-letter language code for language, as defined in the ISO 639 standards.
Definition qlocale.cpp:1510
@ ISO639Part2
Definition qlocale.h:1109
static Language codeToLanguage(QStringView languageCode, LanguageCodeTypes codeTypes=AnyLanguageCode) noexcept
Returns the QLocale::Language enum corresponding to the two- or three-letter languageCode,...
Definition qlocale.cpp:1530
\inmodule QtMultimedia
Q_INVOKABLE void insert(Key k, const QVariant &value)
\qmlmethod void QtMultimedia::mediaMetaData::insert(Key k, variant value) Inserts a value into a Key:...
Q_INVOKABLE QVariant value(Key k) const
\variable QMediaMetaData::NumMetaData
Q_INVOKABLE QList< Key > keys() const
\qmlmethod list<Key> QtMultimedia::mediaMetaData::keys() Returns a list of MediaMetaData....
Key
Provides meta-data for media files.
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QByteArray toUtf8() const &
Definition qstring.h:634
\inmodule QtCore
Definition qtimezone.h:26
\inmodule QtCore \reentrant
Definition qdatetime.h:215
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
QDateTime toDateTime() const
Returns the variant as a QDateTime if the variant has userType() \l QMetaType::QDateTime,...
double toDouble(bool *ok=nullptr) const
Returns the variant as a double if the variant has userType() \l QMetaType::Double,...
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
int typeId() const
Returns the storage type of the value stored in the variant.
Definition qvariant.h:340
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
QDate date
[1]
QPlaceContent parseImage(const QJsonObject &imageObject, const QPlaceManagerEngineNokiaV2 *engine)
constexpr const char * toTag(const char *t)
Combined button and popup list for selecting options.
QDateTime parseDateTime(const GValue &val)
QtVideo::Rotation parseRotationTag(const char *string)
std::optional< double > parseFractionAsDouble(const GValue &val)
constexpr std::string_view extendedComment
QDateTime parseDate(const GValue &val)
void addTagsFromExtendedComment(const GstTagList *list, const gchar *tag, QMediaMetaData &metadata)
void addTagToMetaData(const GstTagList *list, const gchar *tag, void *userdata)
Definition image.cpp:4
#define assert
AudioChannelLayoutTag tag
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static QMediaMetaData::Key tagToKey(const char *tag)
static const char * keyToTag(QMediaMetaData::Key key)
QUniqueHandle< QGstImpl::QUniqueGstDateTimeHandleTraits > QUniqueGstDateTimeHandle
#define constexpr_lookup
QMediaMetaData taglistToMetaData(const GstTagList *tagList)
static void applyMetaDataToTagSetter(const QMediaMetaData &metadata, GstTagSetter *element)
#define qCritical
Definition qlogging.h:167
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
GLuint64 GLenum void * handle
const GLfloat * m
GLuint64 key
GLenum GLenum GLsizei count
GLenum GLuint buffer
GLenum type
GLuint name
GLbyte GLbyte tz
GLuint GLfloat * val
GLenum array
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static const QTextHtmlElement elements[Html_NumElements]
long long qint64
Definition qtypes.h:60
QList< int > list
[14]
QDateTime dateTime
[12]
QGraphicsItem * item
QHostInfo info
[0]