41 { AVMetadataCommonIdentifierTitle, AVMetadataIdentifieriTunesMetadataSongName,
42 AVMetadataIdentifierQuickTimeMetadataTitle,
43 AVMetadataIdentifierID3MetadataTitleDescription,
44 nil, AVMetadata3GPUserDataKeyTitle },
46 { AVMetadataCommonIdentifierAuthor,AVMetadataIdentifieriTunesMetadataAuthor,
47 AVMetadataIdentifierQuickTimeMetadataAuthor, nil,
48 AVMetadataQuickTimeUserDataKeyAuthor, AVMetadata3GPUserDataKeyAuthor },
50 { nil, AVMetadataIdentifieriTunesMetadataUserComment,
51 AVMetadataIdentifierQuickTimeMetadataComment, AVMetadataIdentifierID3MetadataComments,
52 AVMetadataQuickTimeUserDataKeyComment, nil },
54 { AVMetadataCommonIdentifierDescription,AVMetadataIdentifieriTunesMetadataDescription,
55 AVMetadataIdentifierQuickTimeMetadataDescription, nil,
56 AVMetadataQuickTimeUserDataKeyDescription, AVMetadata3GPUserDataKeyDescription },
58 { nil, AVMetadataIdentifieriTunesMetadataUserGenre,
59 AVMetadataIdentifierQuickTimeMetadataGenre, nil,
60 AVMetadataQuickTimeUserDataKeyGenre, AVMetadata3GPUserDataKeyGenre },
62 { AVMetadataCommonIdentifierCreationDate, AVMetadataIdentifieriTunesMetadataReleaseDate,
63 AVMetadataIdentifierQuickTimeMetadataCreationDate, AVMetadataIdentifierID3MetadataDate,
64 AVMetadataQuickTimeUserDataKeyCreationDate, AVMetadataISOUserDataKeyDate },
66 { AVMetadataCommonIdentifierLanguage, nil, nil, AVMetadataIdentifierID3MetadataLanguage, nil, nil },
68 { AVMetadataCommonIdentifierPublisher, AVMetadataIdentifieriTunesMetadataPublisher,
69 AVMetadataIdentifierQuickTimeMetadataPublisher, AVMetadataIdentifierID3MetadataPublisher, nil, nil },
71 { AVMetadataCommonIdentifierCopyrights, AVMetadataIdentifieriTunesMetadataCopyright,
72 AVMetadataIdentifierQuickTimeMetadataCopyright, AVMetadataIdentifierID3MetadataCopyright,
73 AVMetadataQuickTimeUserDataKeyCopyright, AVMetadataISOUserDataKeyCopyright },
75 { nil, nil, nil, AVMetadataIdentifierID3MetadataOfficialAudioSourceWebpage, nil, nil },
77 { nil, nil, nil, AVMetadataIdentifierID3MetadataLength, nil, nil },
79 { AVMetadataCommonIdentifierType, nil, nil, AVMetadataIdentifierID3MetadataContentType, nil, nil },
81 { nil, nil, nil, AVMetadataIdentifierID3MetadataFileType, 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, nil, nil, nil, nil },
91 { nil, nil, AVMetadataIdentifierQuickTimeMetadataCameraFrameReadoutTime, nil, nil, nil },
93 { AVMetadataCommonIdentifierAlbumName, AVMetadataIdentifieriTunesMetadataAlbum,
94 AVMetadataIdentifierQuickTimeMetadataAlbum, AVMetadataIdentifierID3MetadataAlbumTitle,
95 AVMetadataQuickTimeUserDataKeyAlbum, AVMetadata3GPUserDataKeyAlbumAndTrack },
97 { nil, AVMetadataIdentifieriTunesMetadataAlbumArtist, nil, nil,
98 AVMetadataQuickTimeUserDataKeyArtist, AVMetadata3GPUserDataKeyPerformer },
100 { AVMetadataCommonIdentifierArtist, AVMetadataIdentifieriTunesMetadataArtist,
101 AVMetadataIdentifierQuickTimeMetadataArtist, nil, nil, nil },
103 { nil, AVMetadataIdentifieriTunesMetadataTrackNumber,
104 nil, AVMetadataIdentifierID3MetadataTrackNumber, nil, nil },
106 { nil, AVMetadataIdentifieriTunesMetadataComposer,
107 AVMetadataIdentifierQuickTimeMetadataComposer, AVMetadataIdentifierID3MetadataComposer, nil, nil },
109 { nil, AVMetadataIdentifieriTunesMetadataPerformer,
110 AVMetadataIdentifierQuickTimeMetadataPerformer, AVMetadataIdentifierID3MetadataLeadPerformer, nil, nil },
112 { nil, nil, nil, AVMetadataIdentifierID3MetadataAttachedPicture, nil, nil },
114 { AVMetadataCommonIdentifierArtwork, AVMetadataIdentifieriTunesMetadataCoverArt,
115 AVMetadataIdentifierQuickTimeMetadataArtwork, nil, nil, nil },
117 { nil, nil, AVMetadataIdentifierQuickTimeMetadataVideoOrientation, nil, nil, nil },
119 { nil, nil, nil, nil, nil, nil },
121 { nil, nil, nil, nil, nil, nil }
143 static_assert(
sizeof(keyToAVMetaDataID) /
sizeof(AVMetadataIDs) == QMediaMetaData::NumMetaData);
148 AVMetadataKey commonKey = item.commonKey;
149 if (commonKey.length != 0) {
150 if ([commonKey isEqualToString:AVMetadataCommonKeyTitle]) {
151 return QMediaMetaData::Title;
152 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyDescription]) {
153 return QMediaMetaData::Description;
154 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyPublisher]) {
155 return QMediaMetaData::Publisher;
156 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyCreationDate]) {
157 return QMediaMetaData::Date;
158 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyType]) {
159 return QMediaMetaData::MediaType;
160 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyLanguage]) {
161 return QMediaMetaData::Language;
162 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyCopyrights]) {
163 return QMediaMetaData::Copyright;
164 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) {
165 return QMediaMetaData::AlbumTitle;
166 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyAuthor]) {
167 return QMediaMetaData::Author;
168 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyArtist]) {
169 return QMediaMetaData::ContributingArtist;
170 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyArtwork]) {
171 return QMediaMetaData::CoverArtImage;
177 enum keySpaces { iTunes, QuickTime, QuickTimeUserData, IsoUserData, ID3, Other } itemKeySpace;
178 itemKeySpace = Other;
179 AVMetadataKeySpace keySpace = [item keySpace];
180 AVMetadataIdentifier identifier = [item identifier];
182 if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
183 itemKeySpace = iTunes;
184 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
185 itemKeySpace = QuickTime;
186 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData]) {
187 itemKeySpace = QuickTimeUserData;
188 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceISOUserData]) {
189 itemKeySpace = IsoUserData;
190 }
else if (([keySpace isEqualToString:AVMetadataKeySpaceID3])) {
194 for (
int key = 0; key < QMediaMetaData::NumMetaData; key++) {
195 AVMetadataIdentifier idForKey = nil;
196 switch (itemKeySpace) {
198 idForKey = keyToAVMetaDataID[key].iTunes;
201 idForKey = keyToAVMetaDataID[key].quickTime;
204 idForKey = keyToAVMetaDataID[key].ID3;
206 case QuickTimeUserData:
207 idForKey = keyToAVMetaDataID[key].quickTimeUserData;
210 idForKey = keyToAVMetaDataID[key].isoUserData;
216 if ([identifier isEqualToString:idForKey])
217 return QMediaMetaData::Key(key);
277 qDebug() << Q_FUNC_INFO;
279 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
285 QMediaMetaData common = fromAVMetadata([asset commonMetadata]);
286 for (
auto key : common.keys()) {
287 if (metadata.value(key).isNull())
288 metadata.insert(key, common.value(key));
293 const CMTime time = [asset duration];
294 const qint64 duration =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
295 metadata.insert(QMediaMetaData::Duration, duration);
299 if (metadata.value(QMediaMetaData::Date).isNull()) {
300 AVMetadataItem *creationDate = asset.creationDate;
302 NSDate *dateValue = creationDate.dateValue;
304 QDateTime dt = QDateTime::fromNSDate(dateValue);
306 metadata.insert(QMediaMetaData::Date, dt);
311 std::optional<QtVideo::Rotation> rotationData;
312 std::optional<QSize> resolutionData;
314 [asset loadTracksWithMediaType:AVMediaTypeVideo
315 completionHandler:[&](NSArray<AVAssetTrack *> *tracks, NSError *error) {
316 if (!error && tracks && tracks.count > 0) {
318 AVAssetTrack *videoTrack = tracks[0];
321 QtVideo::Rotation rotation;
322 bool mirrored =
false;
323 AVFMediaPlayer::videoOrientationForAssetTrack(videoTrack, rotation, mirrored);
324 rotationData = rotation;
327 NSArray *formatDescriptions = [videoTrack formatDescriptions];
328 if (formatDescriptions.count > 0) {
329 const auto *desc = (__bridge CMVideoFormatDescriptionRef)formatDescriptions[0];
330 CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(desc);
331 if (dims.width > 0 && dims.height > 0)
332 resolutionData = QSize(dims.width, dims.height);
338 if (!sem.try_acquire_for(5s)) {
339 qWarning() <<
"Timed out waiting for video tracks to load, proceeding without orientation "
345 metadata.insert(QMediaMetaData::Orientation,
int(*rotationData));
348 metadata.insert(QMediaMetaData::Resolution, *resolutionData);
355 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
356 if ([asset.mediaType isEqualToString:AVMediaTypeAudio]) {
357 if (metadata.value(QMediaMetaData::Language).isNull()) {
358 auto *languageCode = asset.languageCode;
362 QCFString lang = CFLocaleCreateCanonicalLanguageIdentifierFromString(
363 kCFAllocatorDefault, (__bridge CFStringRef)languageCode);
364 metadata.insert(QMediaMetaData::Language, QLocale::codeToLanguage(QString{ lang }));
368 if ([asset.mediaType isEqualToString:AVMediaTypeVideo]) {
370 if (metadata.value(QMediaMetaData::Orientation).isNull()) {
371 QtVideo::Rotation angle = QtVideo::Rotation::None;
373 AVFMediaPlayer::videoOrientationForAssetTrack(asset, angle, mirrored);
375 metadata.insert(QMediaMetaData::Orientation,
int(angle));
379 if (metadata.value(QMediaMetaData::HasHdrContent).isNull()) {
380 auto hasHdrContent =
false;
382 NSArray *formatDescriptions = [asset formatDescriptions];
383 for (id formatDescription in formatDescriptions) {
384 NSDictionary *extensions = (__bridge NSDictionary *)CMFormatDescriptionGetExtensions((CMFormatDescriptionRef)formatDescription);
385 NSString *transferFunction = extensions[(__bridge NSString *)kCMFormatDescriptionExtension_TransferFunction];
386 if ([transferFunction isEqualToString:(__bridge NSString *)kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ]
387 || [transferFunction isEqualToString:(__bridge NSString *)kCVImageBufferTransferFunction_ITU_R_2100_HLG]) {
388 hasHdrContent =
true;
393 metadata.insert(QMediaMetaData::HasHdrContent, hasHdrContent);
402 AVMetadataIdentifier identifier = toIdentifier(key, keySpace);
403 if (!identifier.length)
406 AVMutableMetadataItem *item = [AVMutableMetadataItem metadataItem];
407 item.keySpace = keySpace;
408 item.identifier = identifier;
411#if QT_DEPRECATED_SINCE(6
, 12
)
412 case QtMultimediaPrivate::deprecatedThumbnailImage:
414 case QMediaMetaData::CoverArtImage: {
415#if defined(Q_OS_MACOS)
416 QImage img = value.value<QImage>();
419 QBuffer buffer(&arr);
420 buffer.open(QIODevice::WriteOnly);
422 NSData *data = arr.toNSData();
423 NSImage *nsImg = [[NSImage alloc] initWithData:data];
430 case QMediaMetaData::FileFormat: {
431 QMediaFormat::FileFormat qtFormat = value.value<QMediaFormat::FileFormat>();
432 AVFileType avFormat = QDarwinFormatInfo::avFileTypeForContainerFormat(qtFormat);
433 item.value = avFormat;
436 case QMediaMetaData::Language: {
437 QString lang = QLocale::languageToCode(value.value<QLocale::Language>());
439 item.value = lang.toNSString();
442 case QMediaMetaData::Orientation: {
444 int rotation = value.toInt(&ok);
446 item.value = [NSNumber numberWithInt:rotation];
449 switch (value.typeId()) {
450 case QMetaType::QString: {
451 item.value = value.toString().toNSString();
454 case QMetaType::Int: {
455 item.value = [NSNumber numberWithInt:value.toInt()];
458 case QMetaType::LongLong: {
459 item.value = [NSNumber numberWithLongLong:value.toLongLong()];
462 case QMetaType::Double: {
463 item.value = [NSNumber numberWithDouble:value.toDouble()];
466 case QMetaType::QDate:
467 case QMetaType::QDateTime: {
468 item.value = value.toDateTime().toNSDate();
471 case QMetaType::QUrl: {
472 item.value = value.toUrl().toNSURL();