40 { AVMetadataCommonIdentifierTitle, AVMetadataIdentifieriTunesMetadataSongName,
41 AVMetadataIdentifierQuickTimeMetadataTitle,
42 AVMetadataIdentifierID3MetadataTitleDescription,
43 nil, AVMetadata3GPUserDataKeyTitle },
45 { AVMetadataCommonIdentifierAuthor,AVMetadataIdentifieriTunesMetadataAuthor,
46 AVMetadataIdentifierQuickTimeMetadataAuthor, nil,
47 AVMetadataQuickTimeUserDataKeyAuthor, AVMetadata3GPUserDataKeyAuthor },
49 { nil, AVMetadataIdentifieriTunesMetadataUserComment,
50 AVMetadataIdentifierQuickTimeMetadataComment, AVMetadataIdentifierID3MetadataComments,
51 AVMetadataQuickTimeUserDataKeyComment, nil },
53 { AVMetadataCommonIdentifierDescription,AVMetadataIdentifieriTunesMetadataDescription,
54 AVMetadataIdentifierQuickTimeMetadataDescription, nil,
55 AVMetadataQuickTimeUserDataKeyDescription, AVMetadata3GPUserDataKeyDescription },
57 { nil, AVMetadataIdentifieriTunesMetadataUserGenre,
58 AVMetadataIdentifierQuickTimeMetadataGenre, nil,
59 AVMetadataQuickTimeUserDataKeyGenre, AVMetadata3GPUserDataKeyGenre },
61 { AVMetadataCommonIdentifierCreationDate, AVMetadataIdentifieriTunesMetadataReleaseDate,
62 AVMetadataIdentifierQuickTimeMetadataCreationDate, AVMetadataIdentifierID3MetadataDate,
63 AVMetadataQuickTimeUserDataKeyCreationDate, AVMetadataISOUserDataKeyDate },
65 { AVMetadataCommonIdentifierLanguage, nil, nil, AVMetadataIdentifierID3MetadataLanguage, nil, nil },
67 { AVMetadataCommonIdentifierPublisher, AVMetadataIdentifieriTunesMetadataPublisher,
68 AVMetadataIdentifierQuickTimeMetadataPublisher, AVMetadataIdentifierID3MetadataPublisher, nil, nil },
70 { AVMetadataCommonIdentifierCopyrights, AVMetadataIdentifieriTunesMetadataCopyright,
71 AVMetadataIdentifierQuickTimeMetadataCopyright, AVMetadataIdentifierID3MetadataCopyright,
72 AVMetadataQuickTimeUserDataKeyCopyright, AVMetadataISOUserDataKeyCopyright },
74 { nil, nil, nil, AVMetadataIdentifierID3MetadataOfficialAudioSourceWebpage, nil, nil },
76 { nil, nil, nil, AVMetadataIdentifierID3MetadataLength, nil, nil },
78 { AVMetadataCommonIdentifierType, nil, nil, AVMetadataIdentifierID3MetadataContentType, nil, nil },
80 { nil, nil, nil, AVMetadataIdentifierID3MetadataFileType, nil, nil },
82 { nil, nil, nil, nil, nil, nil },
84 { nil, nil, nil, nil, nil, nil },
86 { nil, nil, nil, nil, nil, nil },
88 { nil, nil, nil, nil, nil, nil },
90 { nil, nil, AVMetadataIdentifierQuickTimeMetadataCameraFrameReadoutTime, nil, nil, nil },
92 { AVMetadataCommonIdentifierAlbumName, AVMetadataIdentifieriTunesMetadataAlbum,
93 AVMetadataIdentifierQuickTimeMetadataAlbum, AVMetadataIdentifierID3MetadataAlbumTitle,
94 AVMetadataQuickTimeUserDataKeyAlbum, AVMetadata3GPUserDataKeyAlbumAndTrack },
96 { nil, AVMetadataIdentifieriTunesMetadataAlbumArtist, nil, nil,
97 AVMetadataQuickTimeUserDataKeyArtist, AVMetadata3GPUserDataKeyPerformer },
99 { AVMetadataCommonIdentifierArtist, AVMetadataIdentifieriTunesMetadataArtist,
100 AVMetadataIdentifierQuickTimeMetadataArtist, nil, nil, nil },
102 { nil, AVMetadataIdentifieriTunesMetadataTrackNumber,
103 nil, AVMetadataIdentifierID3MetadataTrackNumber, nil, nil },
105 { nil, AVMetadataIdentifieriTunesMetadataComposer,
106 AVMetadataIdentifierQuickTimeMetadataComposer, AVMetadataIdentifierID3MetadataComposer, nil, nil },
108 { nil, AVMetadataIdentifieriTunesMetadataPerformer,
109 AVMetadataIdentifierQuickTimeMetadataPerformer, AVMetadataIdentifierID3MetadataLeadPerformer, nil, nil },
111 { nil, nil, nil, AVMetadataIdentifierID3MetadataAttachedPicture, nil, nil },
113 { AVMetadataCommonIdentifierArtwork, AVMetadataIdentifieriTunesMetadataCoverArt,
114 AVMetadataIdentifierQuickTimeMetadataArtwork, nil, nil, nil },
116 { nil, nil, AVMetadataIdentifierQuickTimeMetadataVideoOrientation, nil, nil, nil },
118 { nil, nil, nil, nil, nil, nil },
120 { nil, nil, nil, nil, nil, nil }
142 static_assert(
sizeof(keyToAVMetaDataID) /
sizeof(AVMetadataIDs) == QMediaMetaData::NumMetaData);
147 AVMetadataKey commonKey = item.commonKey;
148 if (commonKey.length != 0) {
149 if ([commonKey isEqualToString:AVMetadataCommonKeyTitle]) {
150 return QMediaMetaData::Title;
151 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyDescription]) {
152 return QMediaMetaData::Description;
153 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyPublisher]) {
154 return QMediaMetaData::Publisher;
155 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyCreationDate]) {
156 return QMediaMetaData::Date;
157 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyType]) {
158 return QMediaMetaData::MediaType;
159 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyLanguage]) {
160 return QMediaMetaData::Language;
161 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyCopyrights]) {
162 return QMediaMetaData::Copyright;
163 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) {
164 return QMediaMetaData::AlbumTitle;
165 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyAuthor]) {
166 return QMediaMetaData::Author;
167 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyArtist]) {
168 return QMediaMetaData::ContributingArtist;
169 }
else if ([commonKey isEqualToString:AVMetadataCommonKeyArtwork]) {
170 return QMediaMetaData::CoverArtImage;
176 enum keySpaces { iTunes, QuickTime, QuickTimeUserData, IsoUserData, ID3, Other } itemKeySpace;
177 itemKeySpace = Other;
178 AVMetadataKeySpace keySpace = [item keySpace];
179 AVMetadataIdentifier identifier = [item identifier];
181 if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
182 itemKeySpace = iTunes;
183 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
184 itemKeySpace = QuickTime;
185 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData]) {
186 itemKeySpace = QuickTimeUserData;
187 }
else if ([keySpace isEqualToString:AVMetadataKeySpaceISOUserData]) {
188 itemKeySpace = IsoUserData;
189 }
else if (([keySpace isEqualToString:AVMetadataKeySpaceID3])) {
193 for (
int key = 0; key < QMediaMetaData::NumMetaData; key++) {
194 AVMetadataIdentifier idForKey = nil;
195 switch (itemKeySpace) {
197 idForKey = keyToAVMetaDataID[key].iTunes;
200 idForKey = keyToAVMetaDataID[key].quickTime;
203 idForKey = keyToAVMetaDataID[key].ID3;
205 case QuickTimeUserData:
206 idForKey = keyToAVMetaDataID[key].quickTimeUserData;
209 idForKey = keyToAVMetaDataID[key].isoUserData;
215 if ([identifier isEqualToString:idForKey])
216 return QMediaMetaData::Key(key);
276 qDebug() << Q_FUNC_INFO;
278 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
284 QMediaMetaData common = fromAVMetadata([asset commonMetadata]);
285 for (
auto key : common.keys()) {
286 if (metadata.value(key).isNull())
287 metadata.insert(key, common.value(key));
292 const CMTime time = [asset duration];
293 const qint64 duration =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
294 metadata.insert(QMediaMetaData::Duration, duration);
298 if (metadata.value(QMediaMetaData::Date).isNull()) {
299 AVMetadataItem *creationDate = asset.creationDate;
301 NSDate *dateValue = creationDate.dateValue;
303 QDateTime dt = QDateTime::fromNSDate(dateValue);
305 metadata.insert(QMediaMetaData::Date, dt);
310 std::optional<QtVideo::Rotation> rotationData;
311 std::optional<QSize> resolutionData;
313 [asset loadTracksWithMediaType:AVMediaTypeVideo
314 completionHandler:[&](NSArray<AVAssetTrack *> *tracks, NSError *error) {
315 if (!error && tracks && tracks.count > 0) {
317 AVAssetTrack *videoTrack = tracks[0];
320 QtVideo::Rotation rotation;
321 bool mirrored =
false;
322 AVFMediaPlayer::videoOrientationForAssetTrack(videoTrack, rotation, mirrored);
323 rotationData = rotation;
326 NSArray *formatDescriptions = [videoTrack formatDescriptions];
327 if (formatDescriptions.count > 0) {
328 const auto *desc = (__bridge CMVideoFormatDescriptionRef)formatDescriptions[0];
329 CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(desc);
330 if (dims.width > 0 && dims.height > 0)
331 resolutionData = QSize(dims.width, dims.height);
337 if (!sem.try_acquire_for(5s)) {
338 qWarning() <<
"Timed out waiting for video tracks to load, proceeding without orientation "
344 metadata.insert(QMediaMetaData::Orientation,
int(*rotationData));
347 metadata.insert(QMediaMetaData::Resolution, *resolutionData);
354 QMediaMetaData metadata = fromAVMetadata([asset metadata]);
355 if ([asset.mediaType isEqualToString:AVMediaTypeAudio]) {
356 if (metadata.value(QMediaMetaData::Language).isNull()) {
357 auto *languageCode = asset.languageCode;
361 QCFString lang = CFLocaleCreateCanonicalLanguageIdentifierFromString(
362 kCFAllocatorDefault, (__bridge CFStringRef)languageCode);
363 metadata.insert(QMediaMetaData::Language, QLocale::codeToLanguage(QString{ lang }));
367 if ([asset.mediaType isEqualToString:AVMediaTypeVideo]) {
369 if (metadata.value(QMediaMetaData::Orientation).isNull()) {
370 QtVideo::Rotation angle = QtVideo::Rotation::None;
372 AVFMediaPlayer::videoOrientationForAssetTrack(asset, angle, mirrored);
374 metadata.insert(QMediaMetaData::Orientation,
int(angle));
378 if (metadata.value(QMediaMetaData::HasHdrContent).isNull()) {
379 auto hasHdrContent =
false;
381 NSArray *formatDescriptions = [asset formatDescriptions];
382 for (id formatDescription in formatDescriptions) {
383 NSDictionary *extensions = (__bridge NSDictionary *)CMFormatDescriptionGetExtensions((CMFormatDescriptionRef)formatDescription);
384 NSString *transferFunction = extensions[(__bridge NSString *)kCMFormatDescriptionExtension_TransferFunction];
385 if ([transferFunction isEqualToString:(__bridge NSString *)kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ]
386 || [transferFunction isEqualToString:(__bridge NSString *)kCVImageBufferTransferFunction_ITU_R_2100_HLG]) {
387 hasHdrContent =
true;
392 metadata.insert(QMediaMetaData::HasHdrContent, hasHdrContent);
401 AVMetadataIdentifier identifier = toIdentifier(key, keySpace);
402 if (!identifier.length)
405 AVMutableMetadataItem *item = [AVMutableMetadataItem metadataItem];
406 item.keySpace = keySpace;
407 item.identifier = identifier;
410#if QT_DEPRECATED_SINCE(6
, 12
)
411 case QtMultimediaPrivate::deprecatedThumbnailImage:
413 case QMediaMetaData::CoverArtImage: {
414#if defined(Q_OS_MACOS)
415 QImage img = value.value<QImage>();
418 QBuffer buffer(&arr);
419 buffer.open(QIODevice::WriteOnly);
421 NSData *data = arr.toNSData();
422 NSImage *nsImg = [[NSImage alloc] initWithData:data];
429 case QMediaMetaData::FileFormat: {
430 QMediaFormat::FileFormat qtFormat = value.value<QMediaFormat::FileFormat>();
431 AVFileType avFormat = QDarwinFormatInfo::avFileTypeForContainerFormat(qtFormat);
432 item.value = avFormat;
435 case QMediaMetaData::Language: {
436 QString lang = QLocale::languageToCode(value.value<QLocale::Language>());
438 item.value = lang.toNSString();
441 case QMediaMetaData::Orientation: {
443 int rotation = value.toInt(&ok);
445 item.value = [NSNumber numberWithInt:rotation];
448 switch (value.typeId()) {
449 case QMetaType::QString: {
450 item.value = value.toString().toNSString();
453 case QMetaType::Int: {
454 item.value = [NSNumber numberWithInt:value.toInt()];
457 case QMetaType::LongLong: {
458 item.value = [NSNumber numberWithLongLong:value.toLongLong()];
461 case QMetaType::Double: {
462 item.value = [NSNumber numberWithDouble:value.toDouble()];
465 case QMetaType::QDate:
466 case QMetaType::QDateTime: {
467 item.value = value.toDateTime().toNSDate();
470 case QMetaType::QUrl: {
471 item.value = value.toUrl().toNSURL();