39 { AVMetadataCommonIdentifierTitle, AVMetadataIdentifieriTunesMetadataSongName,
40 AVMetadataIdentifierQuickTimeMetadataTitle,
41 AVMetadataIdentifierID3MetadataTitleDescription,
42 nil, AVMetadata3GPUserDataKeyTitle },
44 { AVMetadataCommonIdentifierAuthor,AVMetadataIdentifieriTunesMetadataAuthor,
45 AVMetadataIdentifierQuickTimeMetadataAuthor, nil,
46 AVMetadataQuickTimeUserDataKeyAuthor, AVMetadata3GPUserDataKeyAuthor },
48 { nil, AVMetadataIdentifieriTunesMetadataUserComment,
49 AVMetadataIdentifierQuickTimeMetadataComment, AVMetadataIdentifierID3MetadataComments,
50 AVMetadataQuickTimeUserDataKeyComment, nil },
52 { AVMetadataCommonIdentifierDescription,AVMetadataIdentifieriTunesMetadataDescription,
53 AVMetadataIdentifierQuickTimeMetadataDescription, nil,
54 AVMetadataQuickTimeUserDataKeyDescription, AVMetadata3GPUserDataKeyDescription },
56 { nil, AVMetadataIdentifieriTunesMetadataUserGenre,
57 AVMetadataIdentifierQuickTimeMetadataGenre, nil,
58 AVMetadataQuickTimeUserDataKeyGenre, AVMetadata3GPUserDataKeyGenre },
60 { AVMetadataCommonIdentifierCreationDate, AVMetadataIdentifieriTunesMetadataReleaseDate,
61 AVMetadataIdentifierQuickTimeMetadataCreationDate, AVMetadataIdentifierID3MetadataDate,
62 AVMetadataQuickTimeUserDataKeyCreationDate, AVMetadataISOUserDataKeyDate },
64 { AVMetadataCommonIdentifierLanguage, nil, nil, AVMetadataIdentifierID3MetadataLanguage, nil, nil },
66 { AVMetadataCommonIdentifierPublisher, AVMetadataIdentifieriTunesMetadataPublisher,
67 AVMetadataIdentifierQuickTimeMetadataPublisher, AVMetadataIdentifierID3MetadataPublisher, nil, nil },
69 { AVMetadataCommonIdentifierCopyrights, AVMetadataIdentifieriTunesMetadataCopyright,
70 AVMetadataIdentifierQuickTimeMetadataCopyright, AVMetadataIdentifierID3MetadataCopyright,
71 AVMetadataQuickTimeUserDataKeyCopyright, AVMetadataISOUserDataKeyCopyright },
73 { nil, nil, nil, AVMetadataIdentifierID3MetadataOfficialAudioSourceWebpage, nil, nil },
75 { nil, nil, nil, AVMetadataIdentifierID3MetadataLength, nil, nil },
77 { AVMetadataCommonIdentifierType, nil, nil, AVMetadataIdentifierID3MetadataContentType, nil, nil },
79 { nil, nil, nil, AVMetadataIdentifierID3MetadataFileType, nil, nil },
81 { nil, nil, nil, nil, nil, nil },
83 { nil, nil, nil, nil, nil, nil },
85 { nil, nil, nil, nil, nil, nil },
87 { nil, nil, nil, nil, nil, nil },
89 { nil, nil, AVMetadataIdentifierQuickTimeMetadataCameraFrameReadoutTime, nil, nil, nil },
91 { AVMetadataCommonIdentifierAlbumName, AVMetadataIdentifieriTunesMetadataAlbum,
92 AVMetadataIdentifierQuickTimeMetadataAlbum, AVMetadataIdentifierID3MetadataAlbumTitle,
93 AVMetadataQuickTimeUserDataKeyAlbum, AVMetadata3GPUserDataKeyAlbumAndTrack },
95 { nil, AVMetadataIdentifieriTunesMetadataAlbumArtist, nil, nil,
96 AVMetadataQuickTimeUserDataKeyArtist, AVMetadata3GPUserDataKeyPerformer },
98 { AVMetadataCommonIdentifierArtist, AVMetadataIdentifieriTunesMetadataArtist,
99 AVMetadataIdentifierQuickTimeMetadataArtist, nil, nil, nil },
101 { nil, AVMetadataIdentifieriTunesMetadataTrackNumber,
102 nil, AVMetadataIdentifierID3MetadataTrackNumber, nil, nil },
104 { nil, AVMetadataIdentifieriTunesMetadataComposer,
105 AVMetadataIdentifierQuickTimeMetadataComposer, AVMetadataIdentifierID3MetadataComposer, nil, nil },
107 { nil, AVMetadataIdentifieriTunesMetadataPerformer,
108 AVMetadataIdentifierQuickTimeMetadataPerformer, AVMetadataIdentifierID3MetadataLeadPerformer, nil, nil },
110 { nil, nil, nil, AVMetadataIdentifierID3MetadataAttachedPicture, nil, nil },
112 { AVMetadataCommonIdentifierArtwork, AVMetadataIdentifieriTunesMetadataCoverArt,
113 AVMetadataIdentifierQuickTimeMetadataArtwork, nil, nil, nil },
115 { nil, nil, AVMetadataIdentifierQuickTimeMetadataVideoOrientation, nil, nil, nil },
117 { nil, nil, nil, nil, nil, nil },
119 { nil, nil, nil, nil, nil, nil }
141 static_assert(
sizeof(keyToAVMetaDataID) /
sizeof(AVMetadataIDs) == QMediaMetaData::NumMetaData);
146 AVMetadataKey commonKey = item.commonKey;
147 if (commonKey.length != 0) {
148 if ([commonKey isEqualToString:AVMetadataCommonKeyTitle]) {
149 return QMediaMetaData::Title;
150 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyDescription]) {
151 return QMediaMetaData::Description;
152 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyPublisher]) {
153 return QMediaMetaData::Publisher;
154 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyCreationDate]) {
155 return QMediaMetaData::Date;
156 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyType]) {
157 return QMediaMetaData::MediaType;
158 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyLanguage]) {
159 return QMediaMetaData::Language;
160 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyCopyrights]) {
161 return QMediaMetaData::Copyright;
162 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) {
163 return QMediaMetaData::AlbumTitle;
164 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyAuthor]) {
165 return QMediaMetaData::Author;
166 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyArtist]) {
167 return QMediaMetaData::ContributingArtist;
173 enum keySpaces { iTunes, QuickTime, QuickTimeUserData, IsoUserData, ID3, Other } itemKeySpace;
174 itemKeySpace = Other;
175 AVMetadataKeySpace keySpace = [item keySpace];
176 AVMetadataIdentifier identifier = [item identifier];
178 if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
179 itemKeySpace = iTunes;
180 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
181 itemKeySpace = QuickTime;
182 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData]) {
183 itemKeySpace = QuickTimeUserData;
184 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceISOUserData]) {
185 itemKeySpace = IsoUserData;
186 }
else if (([keySpace isEqualToString:AVMetadataKeySpaceID3])) {
190 for (
int key = 0; key < QMediaMetaData::NumMetaData; key++) {
191 AVMetadataIdentifier idForKey = nil;
192 switch (itemKeySpace) {
194 idForKey = keyToAVMetaDataID[key].iTunes;
197 idForKey = keyToAVMetaDataID[key].quickTime;
200 idForKey = keyToAVMetaDataID[key].ID3;
202 case QuickTimeUserData:
203 idForKey = keyToAVMetaDataID[key].quickTimeUserData;
206 idForKey = keyToAVMetaDataID[key].isoUserData;
212 if ([identifier isEqualToString:idForKey])
213 return QMediaMetaData::Key(key);
262 qDebug() << Q_FUNC_INFO;
264 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
267 const CMTime time = [asset duration];
268 const qint64 duration =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
269 metadata.insert(QMediaMetaData::Duration, duration);
273 if (metadata.value(QMediaMetaData::Date).isNull()) {
274 AVMetadataItem *creationDate = asset.creationDate;
276 NSDate *dateValue = creationDate.dateValue;
278 QDateTime dt = QDateTime::fromNSDate(dateValue);
280 metadata.insert(QMediaMetaData::Date, dt);
285 std::optional<QtVideo::Rotation> rotationData;
286 std::optional<QSize> resolutionData;
288 [asset loadTracksWithMediaType:AVMediaTypeVideo
289 completionHandler:[&](NSArray<AVAssetTrack *> *tracks, NSError *error) {
290 if (!error && tracks && tracks.count > 0) {
292 AVAssetTrack *videoTrack = tracks[0];
295 QtVideo::Rotation rotation;
296 bool mirrored =
false;
297 AVFMediaPlayer::videoOrientationForAssetTrack(videoTrack, rotation, mirrored);
298 rotationData = rotation;
301 NSArray *formatDescriptions = [videoTrack formatDescriptions];
302 if (formatDescriptions.count > 0) {
303 const auto *desc = (__bridge CMVideoFormatDescriptionRef)formatDescriptions[0];
304 CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(desc);
305 if (dims.width > 0 && dims.height > 0)
306 resolutionData = QSize(dims.width, dims.height);
312 if (!sem.try_acquire_for(5s)) {
313 qWarning() <<
"Timed out waiting for video tracks to load, proceeding without orientation "
319 metadata.insert(QMediaMetaData::Orientation,
int(*rotationData));
322 metadata.insert(QMediaMetaData::Resolution, *resolutionData);
329 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
330 if ([asset.mediaType isEqualToString:AVMediaTypeAudio]) {
331 if (metadata.value(QMediaMetaData::Language).isNull()) {
332 auto *languageCode = asset.languageCode;
336 QCFString lang = CFLocaleCreateCanonicalLanguageIdentifierFromString(
337 kCFAllocatorDefault, (__bridge CFStringRef)languageCode);
338 metadata.insert(QMediaMetaData::Language, QLocale::codeToLanguage(QString{ lang }));
342 if ([asset.mediaType isEqualToString:AVMediaTypeVideo]) {
344 if (metadata.value(QMediaMetaData::Orientation).isNull()) {
345 QtVideo::Rotation angle = QtVideo::Rotation::None;
347 AVFMediaPlayer::videoOrientationForAssetTrack(asset, angle, mirrored);
349 metadata.insert(QMediaMetaData::Orientation,
int(angle));
353 if (metadata.value(QMediaMetaData::HasHdrContent).isNull()) {
354 auto hasHdrContent =
false;
356 NSArray *formatDescriptions = [asset formatDescriptions];
357 for (id formatDescription in formatDescriptions) {
358 NSDictionary *extensions = (__bridge NSDictionary *)CMFormatDescriptionGetExtensions((CMFormatDescriptionRef)formatDescription);
359 NSString *transferFunction = extensions[(__bridge NSString *)kCMFormatDescriptionExtension_TransferFunction];
360 if ([transferFunction isEqualToString:(__bridge NSString *)kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ]
361 || [transferFunction isEqualToString:(__bridge NSString *)kCVImageBufferTransferFunction_ITU_R_2100_HLG]) {
362 hasHdrContent =
true;
367 metadata.insert(QMediaMetaData::HasHdrContent, hasHdrContent);
376 AVMetadataIdentifier identifier = toIdentifier(key, keySpace);
377 if (!identifier.length)
380 AVMutableMetadataItem *item = [AVMutableMetadataItem metadataItem];
381 item.keySpace = keySpace;
382 item.identifier = identifier;
385 case QMediaMetaData::ThumbnailImage:
386 case QMediaMetaData::CoverArtImage: {
387#if defined(Q_OS_MACOS)
388 QImage img = value.value<QImage>();
391 QBuffer buffer(&arr);
392 buffer.open(QIODevice::WriteOnly);
394 NSData *data = arr.toNSData();
395 NSImage *nsImg = [[NSImage alloc] initWithData:data];
402 case QMediaMetaData::FileFormat: {
403 QMediaFormat::FileFormat qtFormat = value.value<QMediaFormat::FileFormat>();
404 AVFileType avFormat = QDarwinFormatInfo::avFileTypeForContainerFormat(qtFormat);
405 item.value = avFormat;
408 case QMediaMetaData::Language: {
409 QString lang = QLocale::languageToCode(value.value<QLocale::Language>());
411 item.value = lang.toNSString();
414 case QMediaMetaData::Orientation: {
416 int rotation = value.toInt(&ok);
418 item.value = [NSNumber numberWithInt:rotation];
421 switch (value.typeId()) {
422 case QMetaType::QString: {
423 item.value = value.toString().toNSString();
426 case QMetaType::Int: {
427 item.value = [NSNumber numberWithInt:value.toInt()];
430 case QMetaType::LongLong: {
431 item.value = [NSNumber numberWithLongLong:value.toLongLong()];
434 case QMetaType::Double: {
435 item.value = [NSNumber numberWithDouble:value.toDouble()];
438 case QMetaType::QDate:
439 case QMetaType::QDateTime: {
440 item.value = value.toDateTime().toNSDate();
443 case QMetaType::QUrl: {
444 item.value = value.toUrl().toNSURL();