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
avfmetadata.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
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 "avfmetadata_p.h"
6#include <avfmediaplayer_p.h>
7
8#include <QtCore/qbuffer.h>
9#include <QtCore/qiodevice.h>
10#include <QtCore/qdatetime.h>
11#include <QtCore/qlocale.h>
12#include <QtCore/qurl.h>
13#include <QImage>
14#include <QtMultimedia/qvideoframe.h>
15
16#if __has_include(<AppKit/AppKit.h>)
17#include <AppKit/AppKit.h>
18#endif
19
20#include <CoreFoundation/CoreFoundation.h>
21
23
25 AVMetadataIdentifier common;
26 AVMetadataIdentifier iTunes;
27 AVMetadataIdentifier quickTime;
28 AVMetadataIdentifier ID3;
29 AVMetadataIdentifier quickTimeUserData;
30 AVMetadataIdentifier isoUserData;
31};
32
34 // Title
35 { AVMetadataCommonIdentifierTitle, AVMetadataIdentifieriTunesMetadataSongName,
36 AVMetadataIdentifierQuickTimeMetadataTitle,
37 AVMetadataIdentifierID3MetadataTitleDescription,
38 nil, AVMetadata3GPUserDataKeyTitle },
39 // Author
40 { AVMetadataCommonIdentifierAuthor,AVMetadataIdentifieriTunesMetadataAuthor,
41 AVMetadataIdentifierQuickTimeMetadataAuthor, nil,
42 AVMetadataQuickTimeUserDataKeyAuthor, AVMetadata3GPUserDataKeyAuthor },
43 // Comment
44 { nil, AVMetadataIdentifieriTunesMetadataUserComment,
45 AVMetadataIdentifierQuickTimeMetadataComment, AVMetadataIdentifierID3MetadataComments,
46 AVMetadataQuickTimeUserDataKeyComment, nil },
47 // Description
48 { AVMetadataCommonIdentifierDescription,AVMetadataIdentifieriTunesMetadataDescription,
49 AVMetadataIdentifierQuickTimeMetadataDescription, nil,
50 AVMetadataQuickTimeUserDataKeyDescription, AVMetadata3GPUserDataKeyDescription },
51 // Genre
52 { nil, AVMetadataIdentifieriTunesMetadataUserGenre,
53 AVMetadataIdentifierQuickTimeMetadataGenre, nil,
54 AVMetadataQuickTimeUserDataKeyGenre, AVMetadata3GPUserDataKeyGenre },
55 // Date
56 { AVMetadataCommonIdentifierCreationDate, AVMetadataIdentifieriTunesMetadataReleaseDate,
57 AVMetadataIdentifierQuickTimeMetadataCreationDate, AVMetadataIdentifierID3MetadataDate,
58 AVMetadataQuickTimeUserDataKeyCreationDate, AVMetadataISOUserDataKeyDate },
59 // Language
60 { AVMetadataCommonIdentifierLanguage, nil, nil, AVMetadataIdentifierID3MetadataLanguage, nil, nil },
61 // Publisher
62 { AVMetadataCommonIdentifierPublisher, AVMetadataIdentifieriTunesMetadataPublisher,
63 AVMetadataIdentifierQuickTimeMetadataPublisher, AVMetadataIdentifierID3MetadataPublisher, nil, nil },
64 // Copyright
65 { AVMetadataCommonIdentifierCopyrights, AVMetadataIdentifieriTunesMetadataCopyright,
66 AVMetadataIdentifierQuickTimeMetadataCopyright, AVMetadataIdentifierID3MetadataCopyright,
67 AVMetadataQuickTimeUserDataKeyCopyright, AVMetadataISOUserDataKeyCopyright },
68 // Url
69 { nil, nil, nil, AVMetadataIdentifierID3MetadataOfficialAudioSourceWebpage, nil, nil },
70 // Duration
71 { nil, nil, nil, AVMetadataIdentifierID3MetadataLength, nil, nil },
72 // MediaType
73 { AVMetadataCommonIdentifierType, nil, nil, AVMetadataIdentifierID3MetadataContentType, nil, nil },
74 // FileFormat
75 { nil, nil, nil, AVMetadataIdentifierID3MetadataFileType, nil, nil },
76 // AudioBitRate
77 { nil, nil, nil, nil, nil, nil },
78 // AudioCodec
79 { nil, nil, nil, nil, nil, nil },
80 // VideoBitRate
81 { nil, nil, nil, nil, nil, nil },
82 // VideoCodec
83 { nil, nil, nil, nil, nil, nil },
84 // VideoFrameRate
85 { nil, nil, AVMetadataIdentifierQuickTimeMetadataCameraFrameReadoutTime, nil, nil, nil },
86 // AlbumTitle
87 { AVMetadataCommonIdentifierAlbumName, AVMetadataIdentifieriTunesMetadataAlbum,
88 AVMetadataIdentifierQuickTimeMetadataAlbum, AVMetadataIdentifierID3MetadataAlbumTitle,
89 AVMetadataQuickTimeUserDataKeyAlbum, AVMetadata3GPUserDataKeyAlbumAndTrack },
90 // AlbumArtist
91 { nil, AVMetadataIdentifieriTunesMetadataAlbumArtist, nil, nil,
92 AVMetadataQuickTimeUserDataKeyArtist, AVMetadata3GPUserDataKeyPerformer },
93 // ContributingArtist
94 { AVMetadataCommonIdentifierArtist, AVMetadataIdentifieriTunesMetadataArtist,
95 AVMetadataIdentifierQuickTimeMetadataArtist, nil, nil, nil },
96 // TrackNumber
97 { nil, AVMetadataIdentifieriTunesMetadataTrackNumber,
98 nil, AVMetadataIdentifierID3MetadataTrackNumber, nil, nil },
99 // Composer
100 { nil, AVMetadataIdentifieriTunesMetadataComposer,
101 AVMetadataIdentifierQuickTimeMetadataComposer, AVMetadataIdentifierID3MetadataComposer, nil, nil },
102 // LeadPerformer
103 { nil, AVMetadataIdentifieriTunesMetadataPerformer,
104 AVMetadataIdentifierQuickTimeMetadataPerformer, AVMetadataIdentifierID3MetadataLeadPerformer, nil, nil },
105 // ThumbnailImage
106 { nil, nil, nil, AVMetadataIdentifierID3MetadataAttachedPicture, nil, nil },
107 // CoverArtImage
108 { AVMetadataCommonIdentifierArtwork, AVMetadataIdentifieriTunesMetadataCoverArt,
109 AVMetadataIdentifierQuickTimeMetadataArtwork, nil, nil, nil },
110 // Orientation
111 { nil, nil, AVMetadataIdentifierQuickTimeMetadataVideoOrientation, nil, nil, nil },
112 // Resolution
113 { nil, nil, nil, nil, nil, nil }
114};
115
116static AVMetadataIdentifier toIdentifier(QMediaMetaData::Key key, AVMetadataKeySpace keySpace)
117{
118 static_assert(sizeof(keyToAVMetaDataID)/sizeof(AVMetadataIDs) == QMediaMetaData::Key::Resolution + 1);
119
120 AVMetadataIdentifier identifier = nil;
121 if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
122 identifier = keyToAVMetaDataID[key].iTunes;
123 } else if ([keySpace isEqualToString:AVMetadataKeySpaceID3]) {
124 identifier = keyToAVMetaDataID[key].ID3;
125 } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
126 identifier = keyToAVMetaDataID[key].quickTime;
127 } else {
128 identifier = keyToAVMetaDataID[key].common;
129 }
130 return identifier;
131}
132
133static std::optional<QMediaMetaData::Key> toKey(AVMetadataItem *item)
134{
135 static_assert(sizeof(keyToAVMetaDataID)/sizeof(AVMetadataIDs) == QMediaMetaData::Key::Resolution + 1);
136
137 // The item identifier may be different than the ones we support,
138 // so check by common key first, as it will get the metadata
139 // irrespective of the format.
140 AVMetadataKey commonKey = item.commonKey;
141 if (commonKey.length != 0) {
142 if ([commonKey isEqualToString:AVMetadataCommonKeyTitle]) {
144 } else if ([commonKey isEqualToString:AVMetadataCommonKeyDescription]) {
146 } else if ([commonKey isEqualToString:AVMetadataCommonKeyPublisher]) {
148 } else if ([commonKey isEqualToString:AVMetadataCommonKeyCreationDate]) {
150 } else if ([commonKey isEqualToString:AVMetadataCommonKeyType]) {
152 } else if ([commonKey isEqualToString:AVMetadataCommonKeyLanguage]) {
154 } else if ([commonKey isEqualToString:AVMetadataCommonKeyCopyrights]) {
156 } else if ([commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) {
158 } else if ([commonKey isEqualToString:AVMetadataCommonKeyAuthor]) {
160 } else if ([commonKey isEqualToString:AVMetadataCommonKeyArtist]) {
162 }
163 }
164
165 // Check by identifier if no common key found
166 // No need to check for the common keySpace since there's no common key
167 enum keySpaces { iTunes, QuickTime, QuickTimeUserData, IsoUserData, ID3, Other } itemKeySpace;
168 itemKeySpace = Other;
169 AVMetadataKeySpace keySpace = [item keySpace];
170 AVMetadataIdentifier identifier = [item identifier];
171
172 if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
173 itemKeySpace = iTunes;
174 } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
175 itemKeySpace = QuickTime;
176 } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData]) {
177 itemKeySpace = QuickTimeUserData;
178 } else if ([keySpace isEqualToString:AVMetadataKeySpaceISOUserData]) {
179 itemKeySpace = IsoUserData;
180 } else if (([keySpace isEqualToString:AVMetadataKeySpaceID3])) {
181 itemKeySpace = ID3;
182 }
183
184 for (int key = 0; key < QMediaMetaData::Resolution + 1; key++) {
185 AVMetadataIdentifier idForKey = nil;
186 switch (itemKeySpace) {
187 case iTunes:
188 idForKey = keyToAVMetaDataID[key].iTunes;
189 break;
190 case QuickTime:
191 idForKey = keyToAVMetaDataID[key].quickTime;
192 break;
193 case ID3:
194 idForKey = keyToAVMetaDataID[key].ID3;
195 break;
196 case QuickTimeUserData:
198 break;
199 case IsoUserData:
201 break;
202 default:
203 break;
204 }
205
206 if ([identifier isEqualToString:idForKey])
207 return QMediaMetaData::Key(key);
208 }
209
210 return std::nullopt;
211}
212
213static QMediaMetaData fromAVMetadata(NSArray *metadataItems)
214{
215 QMediaMetaData metadata;
216
217 for (AVMetadataItem* item in metadataItems) {
218 auto key = toKey(item);
219 if (!key)
220 continue;
221
222 const QString value = QString::fromNSString([item stringValue]);
223 if (!value.isNull())
224 metadata.insert(*key, value);
225 }
226 return metadata;
227}
228
230{
231#ifdef QT_DEBUG_AVF
232 qDebug() << Q_FUNC_INFO;
233#endif
234 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
235
236 // add duration
237 const CMTime time = [asset duration];
238 const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f);
239 metadata.insert(QMediaMetaData::Duration, duration);
240
241 return metadata;
242}
243
245{
246 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
247 if ([asset.mediaType isEqualToString:AVMediaTypeAudio]) {
248 if (metadata.value(QMediaMetaData::Language).isNull()) {
249 auto *languageCode = asset.languageCode;
250 if (languageCode) {
251 // languageCode is encoded as ISO 639-2, which QLocale does not handle.
252 // Convert it to 639-1 first.
253 auto id = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault,
254 (__bridge CFStringRef)languageCode);
255 QString lang = QString::fromCFString(id);
256 CFRelease(id);
258 }
259 }
260 }
261 if ([asset.mediaType isEqualToString:AVMediaTypeVideo]) {
262 // add orientation
263 if (metadata.value(QMediaMetaData::Orientation).isNull()) {
265 bool mirrored;
267 Q_UNUSED(mirrored);
269 }
270 }
271 return metadata;
272}
273
274static AVMutableMetadataItem *setAVMetadataItemForKey(QMediaMetaData::Key key, const QVariant &value,
275 AVMetadataKeySpace keySpace = AVMetadataKeySpaceCommon)
276{
277 AVMetadataIdentifier identifier = toIdentifier(key, keySpace);
278 if (!identifier.length)
279 return nil;
280
281 AVMutableMetadataItem *item = [AVMutableMetadataItem metadataItem];
282 item.keySpace = keySpace;
283 item.identifier = identifier;
284
285 switch (key) {
288#if defined(Q_OS_MACOS)
289 QImage img = value.value<QImage>();
290 if (!img.isNull()) {
291 QByteArray arr;
292 QBuffer buffer(&arr);
294 img.save(&buffer);
295 NSData *data = arr.toNSData();
296 NSImage *nsImg = [[NSImage alloc] initWithData:data];
297 item.value = nsImg;
298 [nsImg release];
299 }
300#endif
301 break;
302 }
305 AVFileType avFormat = QDarwinFormatInfo::avFileTypeForContainerFormat(qtFormat);
306 item.value = avFormat;
307 break;
308 }
311 if (!lang.isEmpty())
312 item.value = lang.toNSString();
313 break;
314 }
316 bool ok;
317 int rotation = value.toInt(&ok);
318 if (ok)
319 item.value = [NSNumber numberWithInt:rotation];
320 }
321 default: {
322 switch (value.typeId()) {
323 case QMetaType::QString: {
324 item.value = value.toString().toNSString();
325 break;
326 }
327 case QMetaType::Int: {
328 item.value = [NSNumber numberWithInt:value.toInt()];
329 break;
330 }
331 case QMetaType::LongLong: {
332 item.value = [NSNumber numberWithLongLong:value.toLongLong()];
333 break;
334 }
335 case QMetaType::Double: {
336 item.value = [NSNumber numberWithDouble:value.toDouble()];
337 break;
338 }
339 case QMetaType::QDate:
340 case QMetaType::QDateTime: {
341 item.value = value.toDateTime().toNSDate();
342 break;
343 }
344 case QMetaType::QUrl: {
345 item.value = value.toUrl().toNSURL();
346 break;
347 }
348 default:
349 break;
350 }
351 }
352 }
353
354 return item;
355}
356
357NSMutableArray<AVMetadataItem *> *AVFMetaData::toAVMetadataForFormat(QMediaMetaData metadata, AVFileType format)
358{
359 NSMutableArray<AVMetadataKeySpace> *keySpaces = [NSMutableArray<AVMetadataKeySpace> array];
360 if (format == AVFileTypeAppleM4A) {
361 [keySpaces addObject:AVMetadataKeySpaceiTunes];
362 } else if (format == AVFileTypeMPEGLayer3) {
363 [keySpaces addObject:AVMetadataKeySpaceID3];
364 [keySpaces addObject:AVMetadataKeySpaceiTunes];
365 } else if (format == AVFileTypeQuickTimeMovie) {
366 [keySpaces addObject:AVMetadataKeySpaceQuickTimeMetadata];
367 } else {
368 [keySpaces addObject:AVMetadataKeySpaceCommon];
369 }
370 NSMutableArray<AVMetadataItem *> *avMetaDataArr = [NSMutableArray array];
371 for (const auto &key : metadata.keys()) {
372 for (NSUInteger i = 0; i < [keySpaces count]; i++) {
373 const QVariant &value = metadata.value(key);
374 // set format-specific metadata
375 AVMetadataItem *item = setAVMetadataItemForKey(key, value, keySpaces[i]);
376 if (item)
377 [avMetaDataArr addObject:item];
378 }
379 }
380 return avMetaDataArr;
381}
382
static AVMetadataIdentifier toIdentifier(QMediaMetaData::Key key, AVMetadataKeySpace keySpace)
static AVMutableMetadataItem * setAVMetadataItemForKey(QMediaMetaData::Key key, const QVariant &value, AVMetadataKeySpace keySpace=AVMetadataKeySpaceCommon)
const AVMetadataIDs keyToAVMetaDataID[]
static std::optional< QMediaMetaData::Key > toKey(AVMetadataItem *item)
static QMediaMetaData fromAVMetadata(NSArray *metadataItems)
static void videoOrientationForAssetTrack(AVAssetTrack *track, QtVideo::Rotation &angle, bool &mirrored)
static QMediaMetaData fromAssetTrack(AVAssetTrack *asset)
static QMediaMetaData fromAsset(AVAsset *asset)
static NSMutableArray< AVMetadataItem * > * toAVMetadataForFormat(QMediaMetaData metaData, AVFileType format)
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
static NSString * avFileTypeForContainerFormat(QMediaFormat::FileFormat fileType)
\inmodule QtGui
Definition qimage.h:37
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
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
FileFormat
Describes the container format used in a multimedia file or stream.
\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.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:731
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
\inmodule QtCore
Definition qvariant.h:65
bool isNull() const
Returns true if this is a null variant, false otherwise.
unsigned long NSUInteger
#define Q_FUNC_INFO
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
GLuint64 key
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLfloat angle
GLint GLsizei GLsizei GLenum format
GLint void * img
Definition qopenglext.h:233
GLenum array
GLuint in
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:60
sem release()
QGraphicsItem * item
AVMetadataIdentifier quickTimeUserData
AVMetadataIdentifier common
AVMetadataIdentifier ID3
AVMetadataIdentifier iTunes
AVMetadataIdentifier isoUserData
AVMetadataIdentifier quickTime