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
qffmpegmediametadata.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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 <QDebug>
6#include <QtCore/qdatetime.h>
7#include <qstringlist.h>
8#include <qurl.h>
9#include <qlocale.h>
10
11#include <qloggingcategory.h>
12
14
15Q_STATIC_LOGGING_CATEGORY(qLcMetaData, "qt.multimedia.ffmpeg.metadata")
16
17namespace {
18
20{
21 const char *tag;
23};
24
25constexpr ffmpegTagToMetaDataKey ffmpegTagToMetaDataKey[] = {
26 { "title", QMediaMetaData::Title },
27 { "comment", QMediaMetaData::Comment },
28 { "description", QMediaMetaData::Description },
29 { "genre", QMediaMetaData::Genre },
30 { "date", QMediaMetaData::Date },
31
32 { "language", QMediaMetaData::Language },
33
34 { "copyright", QMediaMetaData::Copyright },
35
36 // Music
37 { "album", QMediaMetaData::AlbumTitle },
38 { "album_artist", QMediaMetaData::AlbumArtist },
39 { "artist", QMediaMetaData::ContributingArtist },
40 { "track", QMediaMetaData::TrackNumber },
41
42 // Movie
43 { "performer", QMediaMetaData::LeadPerformer },
44
45 { nullptr, QMediaMetaData::Title }
46};
47
48} // namespace
49
50static QMediaMetaData::Key tagToKey(const char *tag)
51{
52 const auto *map = ffmpegTagToMetaDataKey;
53 while (map->tag) {
54 if (!strcmp(map->tag, tag))
55 return map->key;
56 ++map;
57 }
58 return QMediaMetaData::Key(-1);
59}
60
61static const char *keyToTag(QMediaMetaData::Key key)
62{
63 const auto *map = ffmpegTagToMetaDataKey;
64 while (map->tag) {
65 if (map->key == key)
66 return map->tag;
67 ++map;
68 }
69 return nullptr;
70}
71
72static QDateTime getRecordingTime(const AVDictionary *tags)
73{
74 constexpr std::array prioritizedDateTags = {
75 "date", // Time of work creation provided by FFmpeg
76 "com.apple.quicktime.creationdate", // QuickTime creation time
77 "year", // Year of work, prioritized after more specific times
78 "creation_time", // Time of file creation or encoding, prioritized last
79 };
80
81 AVDictionaryEntry *entry = nullptr;
82
83 for (const char *dateTag : prioritizedDateTags) {
84 using namespace std::string_view_literals;
85 entry = av_dict_get(tags, dateTag, nullptr, 0);
86 if (!entry)
87 continue;
88 else if (entry->key == "year"sv)
89 return QDateTime(QDate(QByteArray(entry->value).toInt(), 1, 1), QTime(0, 0, 0));
90 else
91 return QDateTime::fromString(QString::fromUtf8(entry->value), Qt::ISODate);
92 }
93
94 return QDateTime();
95}
96
97//internal
98void QFFmpegMetaData::addEntry(QMediaMetaData &metaData, AVDictionaryEntry *entry)
99{
100 qCDebug(qLcMetaData) << " checking:" << entry->key << entry->value;
101 QByteArray tag(entry->key);
102 QMediaMetaData::Key key = tagToKey(tag.toLower());
103 if (key == QMediaMetaData::Key(-1))
104 return;
105 qCDebug(qLcMetaData) << " adding" << key;
106
107 auto *map = &metaData;
108
109 int metaTypeId = keyType(key).id();
110 switch (metaTypeId) {
111 case qMetaTypeId<QString>():
112 map->insert(key, QString::fromUtf8(entry->value));
113 return;
114 case qMetaTypeId<QStringList>():
115 map->insert(key, QString::fromUtf8(entry->value).split(QLatin1Char(',')));
116 return;
117 case qMetaTypeId<QDateTime>():
118 // Dates have their own handling
119 return;
120 case qMetaTypeId<QUrl>():
121 map->insert(key, QUrl::fromEncoded(entry->value));
122 return;
123 case qMetaTypeId<qint64>():
124 map->insert(key, (qint64)QByteArray(entry->value).toLongLong());
125 return;
126 case qMetaTypeId<int>():
127 map->insert(key, QByteArray(entry->value).toInt());
128 return;
129 case qMetaTypeId<qreal>():
130 map->insert(key, (qreal)QByteArray(entry->value).toDouble());
131 return;
132 default:
133 break;
134 }
135 if (metaTypeId == qMetaTypeId<QLocale::Language>()) {
136 map->insert(key, QVariant::fromValue(QLocale::codeToLanguage(QString::fromUtf8(entry->value), QLocale::ISO639Part2)));
137 }
138}
139
140
141QMediaMetaData QFFmpegMetaData::fromAVMetaData(const AVDictionary *tags)
142{
143 QMediaMetaData metaData;
144 AVDictionaryEntry *entry = nullptr;
145 while ((entry = av_dict_get(tags, "", entry, AV_DICT_IGNORE_SUFFIX)))
146 addEntry(metaData, entry);
147
148 // Get date from provided tags in order of priority
149 QDateTime date = getRecordingTime(tags);
150 if (!date.isNull())
151 metaData.insert(QMediaMetaData::Date, date);
152
153 return metaData;
154}
155
156QByteArray QFFmpegMetaData::value(const QMediaMetaData &metaData, QMediaMetaData::Key key)
157{
158 const int metaTypeId = keyType(key).id();
159 const QVariant val = metaData.value(key);
160 switch (metaTypeId) {
161 case qMetaTypeId<QString>():
162 return val.toString().toUtf8();
163 case qMetaTypeId<QStringList>():
164 return val.toStringList().join(u",").toUtf8();
165 case qMetaTypeId<QDateTime>():
166 return val.toDateTime().toString(Qt::ISODate).toUtf8();
167 case qMetaTypeId<QUrl>():
168 return val.toUrl().toEncoded();
169 case qMetaTypeId<qint64>():
170 case qMetaTypeId<int>():
171 return QByteArray::number(val.toLongLong());
172 case qMetaTypeId<qreal>():
173 return QByteArray::number(val.toDouble());
174 default:
175 break;
176 }
177 if (metaTypeId == qMetaTypeId<QLocale::Language>())
178 return QLocale::languageToCode(val.value<QLocale::Language>(), QLocale::ISO639Part2).toUtf8();
179 return {};
180}
181
182
183AVDictionary *QFFmpegMetaData::toAVMetaData(const QMediaMetaData &metaData)
184{
185 AVDictionary *dict = nullptr;
186 for (const auto &&[k, v] : metaData.asKeyValueRange()) {
187 const char *key = ::keyToTag(k);
188 if (!key)
189 continue;
190 QByteArray val = value(metaData, k);
191 if (val.isEmpty())
192 continue;
193 av_dict_set(&dict, key, val.constData(), 0);
194 }
195 return dict;
196}
197
198
199
200QT_END_NAMESPACE
Combined button and popup list for selecting options.
static QMediaMetaData::Key tagToKey(const char *tag)
static QDateTime getRecordingTime(const AVDictionary *tags)
static const char * keyToTag(QMediaMetaData::Key key)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)