File indexing completed on 2025-01-19 03:55:58
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2019-02-26 0007 * Description : item metadata interface - video helpers. 0008 * 0009 * References : 0010 * 0011 * FFMpeg metadata review: https://wiki.multimedia.cx/index.php/FFmpeg_Metadata 0012 * FFMpeg MP4 parser : https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mov.c#L298 0013 * Exiv2 XMP video : https://github.com/Exiv2/exiv2/blob/master/src/properties.cpp#L1331 0014 * Exiv2 RIFF tags : https://github.com/Exiv2/exiv2/blob/master/src/riffvideo.cpp#L83 0015 * Apple metadata desc : https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html 0016 * Matroska metadata desc: https://matroska.org/technical/specs/tagging/index.html 0017 * FFMpeg metadata writer: https://github.com/kritzikratzi/ofxAvCodec/blob/master/src/ofxAvUtils.cpp#L61 0018 * 0019 * FFMpeg tags names origin: 0020 * 0021 * Generic : common tags generated by FFMpeg codecs. 0022 * RIFF files : Resource Interchange File Format tags (as AVI). 0023 * MKV files : Matroska container tags. 0024 * QT files : Quicktime container tags (Apple). 0025 * 0026 * SPDX-FileCopyrightText: 2019-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0027 * 0028 * SPDX-License-Identifier: GPL-2.0-or-later 0029 * 0030 * ============================================================ */ 0031 0032 #include "dmetadata.h" 0033 0034 // C Ansi includes 0035 0036 #include <stdint.h> 0037 0038 // Qt includes 0039 0040 #include <QDateTime> 0041 #include <QFileInfo> 0042 #include <QMimeDatabase> 0043 #include <QStringList> 0044 #include <QTimeZone> 0045 #include <QtMath> 0046 0047 // KDE includes 0048 0049 #include <klocalizedstring.h> 0050 0051 // Local includes 0052 0053 #include "captionvalues.h" 0054 #include "digikam_debug.h" 0055 #include "digikam_config.h" 0056 0057 #ifdef HAVE_MEDIAPLAYER 0058 0059 // Libav includes 0060 0061 extern "C" 0062 { 0063 #include <libavformat/avformat.h> 0064 #include <libavutil/dict.h> 0065 #include <libavutil/pixdesc.h> 0066 #include <libavcodec/avcodec.h> 0067 0068 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 18, 0) 0069 0070 # include <libavutil/display.h> 0071 0072 #endif 0073 0074 } 0075 0076 #endif 0077 0078 namespace Digikam 0079 { 0080 0081 /** 0082 * Search first occurrence of string in 'map' with keys given by 'lst'. 0083 * Return the string match. 0084 * If 'xmpTags' is not empty, register XMP tags value with string. 0085 */ 0086 QString s_setXmpTagStringFromEntry(DMetadata* const meta, 0087 const QStringList& lst, 0088 const DMetadata::MetaDataMap& map, 0089 const QStringList& xmpTags = QStringList()) 0090 { 0091 Q_FOREACH (const QString& tag, lst) 0092 { 0093 DMetadata::MetaDataMap::const_iterator it = map.find(tag); 0094 0095 if (it != map.end()) 0096 { 0097 if (meta && // Protection. 0098 !xmpTags.isEmpty()) // If xmpTags is empty, we only return the matching value from the map. 0099 { 0100 Q_FOREACH (const QString& tag2, xmpTags) 0101 { 0102 // Only register the tag value if it doesn't exists yet. 0103 0104 if (meta->getXmpTagString(tag2.toLatin1().data()).isNull()) 0105 { 0106 meta->setXmpTagString(tag2.toLatin1().data(), it.value()); 0107 } 0108 } 0109 } 0110 0111 return it.value(); 0112 } 0113 } 0114 0115 return QString(); 0116 } 0117 0118 QStringList s_keywordsSeparation(const QString& data) 0119 { 0120 QStringList keywords = data.split(QLatin1Char('/')); 0121 0122 if (keywords.isEmpty()) 0123 { 0124 keywords = data.split(QLatin1Char(',')); 0125 0126 if (keywords.isEmpty()) 0127 { 0128 keywords = data.split(QLatin1Char(' ')); 0129 } 0130 } 0131 0132 return keywords; 0133 } 0134 0135 qint64 s_secondsSinceJanuary1904(const QDateTime& dt) 0136 { 0137 QDateTime dt1904(QDate(1904, 1, 1), QTime(0, 0, 0)); 0138 0139 return dt1904.secsTo(dt); 0140 } 0141 0142 #ifdef HAVE_MEDIAPLAYER 0143 0144 QString s_convertFFMpegFormatToXMP(int format) 0145 { 0146 QString data; 0147 0148 switch (format) 0149 { 0150 case AV_SAMPLE_FMT_U8: 0151 case AV_SAMPLE_FMT_U8P: 0152 { 0153 data = QLatin1String("8Int"); 0154 break; 0155 } 0156 0157 case AV_SAMPLE_FMT_S16: 0158 case AV_SAMPLE_FMT_S16P: 0159 { 0160 data = QLatin1String("16Int"); 0161 break; 0162 } 0163 0164 case AV_SAMPLE_FMT_S32: 0165 case AV_SAMPLE_FMT_S32P: 0166 { 0167 data = QLatin1String("32Int"); 0168 break; 0169 } 0170 0171 case AV_SAMPLE_FMT_FLT: 0172 case AV_SAMPLE_FMT_FLTP: 0173 { 0174 data = QLatin1String("32Float"); 0175 break; 0176 } 0177 0178 case AV_SAMPLE_FMT_DBL: // Not supported by XMP spec. 0179 case AV_SAMPLE_FMT_DBLP: // Not supported by XMP spec. 0180 case AV_SAMPLE_FMT_S64: // Not supported by XMP spec. 0181 case AV_SAMPLE_FMT_S64P: // Not supported by XMP spec. 0182 case AV_SAMPLE_FMT_NONE: 0183 case AV_SAMPLE_FMT_NB: 0184 default: 0185 { 0186 data = QLatin1String("Other"); 0187 break; 0188 } 0189 0190 // NOTE: where are 'Compressed' and 'Packed' type from XMP spec into FFMPEG ? 0191 } 0192 0193 return data; 0194 } 0195 0196 DMetadata::MetaDataMap s_extractFFMpegMetadataEntriesFromDictionary(AVDictionary* const dict) 0197 { 0198 AVDictionaryEntry* entry = nullptr; 0199 DMetadata::MetaDataMap meta; 0200 0201 do 0202 { 0203 entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX); 0204 0205 if (entry) 0206 { 0207 QString entryValue = QString::fromUtf8(entry->value); 0208 0209 if (QString::fromUtf8(entry->key) == QLatin1String("creation_time")) 0210 { 0211 if (QDateTime::fromString(entryValue, Qt::ISODate).toMSecsSinceEpoch() == 0) 0212 { 0213 continue; 0214 } 0215 } 0216 0217 meta.insert(QString::fromUtf8(entry->key), entryValue); 0218 } 0219 } 0220 while (entry); 0221 0222 return meta; 0223 } 0224 0225 #endif 0226 0227 bool DMetadata::loadUsingFFmpeg(const QString& filePath) 0228 { 0229 0230 #ifdef HAVE_MEDIAPLAYER 0231 0232 qCDebug(DIGIKAM_METAENGINE_LOG) << "Parse metadada with FFMpeg:" << filePath; 0233 0234 #if LIBAVFORMAT_VERSION_MAJOR < 58 0235 0236 av_register_all(); 0237 0238 #endif 0239 0240 AVFormatContext* fmt_ctx = avformat_alloc_context(); 0241 int ret = avformat_open_input(&fmt_ctx, filePath.toUtf8().data(), nullptr, nullptr); 0242 0243 if (ret < 0) 0244 { 0245 qCDebug(DIGIKAM_METAENGINE_LOG) << "avformat_open_input error: " << ret; 0246 0247 return false; 0248 } 0249 0250 ret = avformat_find_stream_info(fmt_ctx, nullptr); 0251 0252 if (ret < 0) 0253 { 0254 qCDebug(DIGIKAM_METAENGINE_LOG) << "avform_find_stream_info error: " << ret; 0255 0256 return false; 0257 } 0258 0259 QString data; 0260 0261 setXmpTagString("Xmp.video.duration", 0262 QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE))); 0263 setXmpTagString("Xmp.xmpDM.duration", 0264 QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE))); 0265 0266 if (fmt_ctx->bit_rate > 0) 0267 { 0268 setXmpTagString("Xmp.video.MaxBitRate", QString::number(fmt_ctx->bit_rate)); 0269 } 0270 0271 setXmpTagString("Xmp.video.StreamCount", 0272 QString::number(fmt_ctx->nb_streams)); 0273 0274 // To only register one video, one audio stream, and one subtitle stream in XMP metadata. 0275 0276 bool vstream = false; 0277 bool astream = false; 0278 bool sstream = false; 0279 0280 for (uint i = 0 ; i < fmt_ctx->nb_streams ; ++i) 0281 { 0282 const AVStream* const stream = fmt_ctx->streams[i]; 0283 0284 if (!stream) 0285 { 0286 continue; 0287 } 0288 0289 AVCodecParameters* const codec = stream->codecpar; 0290 0291 if (!codec) 0292 { 0293 continue; 0294 } 0295 0296 const char* cname = avcodec_get_name(codec->codec_id); 0297 0298 if (QLatin1String(cname) == QLatin1String("none")) 0299 { 0300 if (codec->codec_type == AVMEDIA_TYPE_AUDIO) 0301 { 0302 setXmpTagString("Xmp.audio.Codec", 0303 QString::fromUtf8(cname)); 0304 } 0305 else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) 0306 { 0307 setXmpTagString("Xmp.video.Codec", 0308 QString::fromUtf8(cname)); 0309 } 0310 0311 continue; 0312 } 0313 0314 // ----------------------------------------- 0315 // Audio stream parsing 0316 // ----------------------------------------- 0317 0318 if (!astream && (codec->codec_type == AVMEDIA_TYPE_AUDIO)) 0319 { 0320 astream = true; 0321 0322 setXmpTagString("Xmp.audio.Codec", 0323 QString::fromUtf8(cname)); 0324 0325 setXmpTagString("Xmp.audio.CodecDescription", 0326 QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name)); 0327 0328 setXmpTagString("Xmp.audio.SampleRate", 0329 QString::number(codec->sample_rate)); 0330 setXmpTagString("Xmp.xmpDM.audioSampleRate", 0331 QString::number(codec->sample_rate)); 0332 0333 // See XMP Dynamic Media properties from Adobe. 0334 // Audio Channel type is a limited untranslated string choice depending of amount of audio channels 0335 0336 data = QString(); 0337 0338 #if LIBAVUTIL_VERSION_MAJOR > 56 0339 0340 int channels = codec->ch_layout.nb_channels; 0341 0342 #else 0343 0344 int channels = codec->channels; 0345 0346 #endif 0347 0348 switch (channels) 0349 { 0350 case 0: 0351 { 0352 break; 0353 } 0354 0355 case 1: 0356 { 0357 data = QLatin1String("Mono"); 0358 break; 0359 } 0360 0361 case 2: 0362 { 0363 data = QLatin1String("Stereo"); 0364 break; 0365 } 0366 0367 case 6: 0368 { 0369 data = QLatin1String("5.1"); 0370 break; 0371 } 0372 0373 case 8: 0374 { 0375 data = QLatin1String("7.1"); 0376 break; 0377 } 0378 0379 case 16: 0380 { 0381 data = QLatin1String("16 Channel"); 0382 break; 0383 } 0384 0385 default: 0386 { 0387 data = QLatin1String("Other"); 0388 break; 0389 } 0390 } 0391 0392 if (!data.isEmpty()) 0393 { 0394 setXmpTagString("Xmp.audio.ChannelType", data); 0395 setXmpTagString("Xmp.xmpDM.audioChannelType", data); 0396 } 0397 0398 setXmpTagString("Xmp.audio.Format", 0399 QString::fromUtf8(av_get_sample_fmt_name((AVSampleFormat)codec->format))); 0400 0401 // See XMP Dynamic Media properties from Adobe. 0402 // Audio Sample type is a limited untranslated string choice depending of amount of audio samples 0403 0404 data = s_convertFFMpegFormatToXMP(codec->format); 0405 0406 if (!data.isEmpty()) 0407 { 0408 setXmpTagString("Xmp.audio.SampleType", data); 0409 setXmpTagString("Xmp.xmpDM.audioSampleType", data); 0410 } 0411 0412 // -------------- 0413 0414 MetaDataMap ameta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata); 0415 0416 qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg audio stream metadata entries :"; 0417 qCDebug(DIGIKAM_METAENGINE_LOG) << ameta; 0418 qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------"; 0419 0420 // -------------- 0421 0422 s_setXmpTagStringFromEntry(this, 0423 QStringList() << QLatin1String("language"), // Generic. 0424 ameta, 0425 QStringList() << QLatin1String("Xmp.audio.TrackLang")); 0426 0427 // -------------- 0428 0429 data = s_setXmpTagStringFromEntry(this, 0430 QStringList() << QLatin1String("creation_time"), // Generic. 0431 ameta); 0432 0433 if (!data.isEmpty()) 0434 { 0435 QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); 0436 setXmpTagString("Xmp.audio.TrackCreateDate", 0437 QString::number(s_secondsSinceJanuary1904(dt))); 0438 } 0439 0440 // -------------- 0441 0442 s_setXmpTagStringFromEntry(this, 0443 QStringList() << QLatin1String("handler_name"), // Generic. 0444 ameta, 0445 QStringList() << QLatin1String("Xmp.audio.HandlerDescription")); 0446 } 0447 0448 // ----------------------------------------- 0449 // Video stream parsing 0450 // ----------------------------------------- 0451 0452 if (!vstream && (codec->codec_type == AVMEDIA_TYPE_VIDEO)) 0453 { 0454 vstream = true; 0455 0456 setXmpTagString("Xmp.video.Codec", 0457 QString::fromUtf8(cname)); 0458 0459 setXmpTagString("Xmp.video.CodecDescription", 0460 QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name)); 0461 0462 setXmpTagString("Xmp.video.Format", 0463 QString::fromUtf8(av_get_pix_fmt_name((AVPixelFormat)codec->format))); 0464 0465 // Store in this tag the full description off FFMPEG video color space. 0466 0467 setXmpTagString("Xmp.video.ColorMode", 0468 QString::fromUtf8(av_color_space_name((AVColorSpace)codec->color_space))); 0469 0470 VIDEOCOLORMODEL cm = VIDEOCOLORMODEL_OTHER; 0471 0472 switch (codec->color_space) 0473 { 0474 case AVCOL_SPC_RGB: 0475 { 0476 cm = VIDEOCOLORMODEL_SRGB; 0477 break; 0478 } 0479 0480 case AVCOL_SPC_BT470BG: 0481 case AVCOL_SPC_SMPTE170M: 0482 case AVCOL_SPC_SMPTE240M: 0483 { 0484 cm = VIDEOCOLORMODEL_BT601; 0485 break; 0486 } 0487 0488 case AVCOL_SPC_BT709: 0489 { 0490 cm = VIDEOCOLORMODEL_BT709; 0491 break; 0492 } 0493 0494 case AVCOL_SPC_UNSPECIFIED: 0495 case AVCOL_SPC_RESERVED: 0496 case AVCOL_SPC_NB: 0497 { 0498 cm = VIDEOCOLORMODEL_UNKNOWN; 0499 break; 0500 } 0501 0502 default: 0503 { 0504 break; 0505 } 0506 } 0507 0508 // See XMP Dynamic Media properties from Adobe. 0509 // Video Color Space is a limited untranslated string choice depending of video color space value. 0510 0511 data = videoColorModelToString(cm); 0512 0513 if (!data.isEmpty()) 0514 { 0515 setXmpTagString("Xmp.video.ColorSpace", data); 0516 setXmpTagString("Xmp.xmpDM.videoColorSpace", data); 0517 } 0518 0519 // ---------- 0520 0521 QString fo; 0522 0523 switch (codec->field_order) 0524 { 0525 case AV_FIELD_PROGRESSIVE: 0526 { 0527 fo = QLatin1String("Progressive"); 0528 break; 0529 } 0530 0531 case AV_FIELD_TT: // Top coded first, top displayed first 0532 case AV_FIELD_BT: // Bottom coded first, top displayed first 0533 { 0534 fo = QLatin1String("Upper"); 0535 break; 0536 } 0537 0538 case AV_FIELD_BB: // Bottom coded first, bottom displayed first 0539 case AV_FIELD_TB: // Top coded first, bottom displayed first 0540 { 0541 fo = QLatin1String("Lower"); 0542 break; 0543 } 0544 0545 default: 0546 { 0547 break; 0548 } 0549 } 0550 0551 if (!fo.isEmpty()) 0552 { 0553 setXmpTagString("Xmp.xmpDM.FieldOrder", fo); 0554 } 0555 0556 // ---------- 0557 0558 QString aspectRatio; 0559 double frameRate = -1.0; 0560 0561 if (codec->sample_aspect_ratio.num != 0) // Check if undefined by ffmpeg 0562 { 0563 AVRational displayAspectRatio; 0564 0565 av_reduce(&displayAspectRatio.num, &displayAspectRatio.den, 0566 codec->width * (int64_t)codec->sample_aspect_ratio.num, 0567 codec->height * (int64_t)codec->sample_aspect_ratio.den, 0568 1024 * 1024); 0569 0570 aspectRatio = QString::fromLatin1("%1/%2").arg(displayAspectRatio.num) 0571 .arg(displayAspectRatio.den); 0572 } 0573 else if (codec->height) 0574 { 0575 aspectRatio = QString::fromLatin1("%1/%2").arg(codec->width) 0576 .arg(codec->height); 0577 } 0578 0579 if (stream->avg_frame_rate.den) 0580 { 0581 frameRate = (double)stream->avg_frame_rate.num / (double)stream->avg_frame_rate.den; 0582 } 0583 0584 setXmpTagString("Xmp.video.Width", 0585 QString::number(codec->width)); 0586 setXmpTagString("Xmp.video.FrameWidth", 0587 QString::number(codec->width)); 0588 setXmpTagString("Xmp.video.SourceImageWidth", 0589 QString::number(codec->width)); 0590 0591 setXmpTagString("Xmp.video.Height", 0592 QString::number(codec->height)); 0593 setXmpTagString("Xmp.video.FrameHeight", 0594 QString::number(codec->height)); 0595 setXmpTagString("Xmp.video.SourceImageHeight", 0596 QString::number(codec->height)); 0597 0598 setXmpTagString("Xmp.video.FrameSize", 0599 QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height)); 0600 setXmpTagString("Xmp.xmpDM.videoFrameSize", 0601 QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height)); 0602 0603 // Backport size in Exif and Iptc 0604 0605 setItemDimensions(QSize(codec->width, codec->height)); 0606 0607 if (!aspectRatio.isEmpty()) 0608 { 0609 setXmpTagString("Xmp.video.AspectRatio", aspectRatio); 0610 setXmpTagString("Xmp.xmpDM.videoPixelAspectRatio", aspectRatio); 0611 } 0612 0613 if (frameRate != -1.0) 0614 { 0615 setXmpTagString("Xmp.video.FrameRate", QString::number(frameRate)); 0616 0617 // See XMP Dynamic Media properties from Adobe. 0618 // Video Color Space is a limited untranslated string choice depending of video frame rate. 0619 // https://documentation.apple.com/en/finalcutpro/usermanual/index.html#chapter=D%26section=4%26tasks=true 0620 0621 data = QLatin1String("Other"); 0622 0623 if (frameRate == 24.0) 0624 { 0625 data = QLatin1String("24"); 0626 } 0627 else if ((frameRate == 23.98) || (frameRate == 29.97) || 0628 (frameRate == 30.0) || (frameRate == 59.94)) 0629 { 0630 data = QLatin1String("NTSC"); 0631 } 0632 else if (frameRate == 25.0 || frameRate == 50.0) 0633 { 0634 data = QLatin1String("PAL"); 0635 } 0636 0637 setXmpTagString("Xmp.xmpDM.videoFrameRate", data); 0638 } 0639 0640 setXmpTagString("Xmp.video.BitDepth", QString::number(codec->bits_per_coded_sample)); 0641 0642 // See XMP Dynamic Media properties from Adobe. 0643 // Video Pixel Depth is a limited untranslated string choice depending of amount of samples format. 0644 0645 data = s_convertFFMpegFormatToXMP(codec->format); 0646 0647 if (!data.isEmpty()) 0648 { 0649 setXmpTagString("Xmp.xmpDM.videoPixelDepth", data); 0650 } 0651 0652 // ----------------------------------------- 0653 0654 MetaDataMap vmeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata); 0655 0656 qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg video stream metadata entries :"; 0657 qCDebug(DIGIKAM_METAENGINE_LOG) << vmeta; 0658 qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------"; 0659 0660 // -------------- 0661 0662 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 18, 0) 0663 0664 quint8* const sideData = reinterpret_cast<quint8*>(av_stream_get_side_data(stream, 0665 AV_PKT_DATA_DISPLAYMATRIX, 0666 nullptr)); 0667 0668 if (sideData) 0669 { 0670 double r = av_display_rotation_get(reinterpret_cast<qint32*>(sideData)); 0671 0672 if (!qIsNaN(r)) 0673 { 0674 r -= 360 * qFloor(r / 360 + 0.9 / 360); 0675 data = QString::number(360 - r); 0676 } 0677 } 0678 0679 #else 0680 0681 data = s_setXmpTagStringFromEntry(this, 0682 QStringList() << QLatin1String("rotate"), // Generic. 0683 vmeta); 0684 0685 #endif 0686 0687 if (!data.isEmpty()) 0688 { 0689 bool b = false; 0690 int val = data.toInt(&b); 0691 ImageOrientation ori = ORIENTATION_UNSPECIFIED; 0692 0693 if (b) 0694 { 0695 switch (val) 0696 { 0697 case 0: 0698 { 0699 ori = ORIENTATION_NORMAL; 0700 break; 0701 } 0702 0703 case 90: 0704 { 0705 ori = ORIENTATION_ROT_90; 0706 break; 0707 } 0708 0709 case 180: 0710 { 0711 ori = ORIENTATION_ROT_180; 0712 break; 0713 } 0714 0715 case 270: 0716 { 0717 ori = ORIENTATION_ROT_270; 0718 break; 0719 } 0720 0721 default: 0722 { 0723 break; 0724 } 0725 } 0726 0727 setXmpTagString("Xmp.video.Orientation", QString::number(ori)); 0728 0729 // Backport orientation in Exif 0730 0731 setItemOrientation(ori); 0732 } 0733 } 0734 0735 // -------------- 0736 0737 s_setXmpTagStringFromEntry(this, 0738 QStringList() << QLatin1String("language") // Generic. 0739 << QLatin1String("ILNG") // RIFF files. 0740 << QLatin1String("LANG"), // RIFF files. 0741 vmeta, 0742 QStringList() << QLatin1String("Xmp.video.Language")); 0743 0744 // -------------- 0745 0746 data = s_setXmpTagStringFromEntry(this, 0747 QStringList() << QLatin1String("creation_time") // Generic. 0748 << QLatin1String("_STATISTICS_WRITING_DATE_UTC"), // MKV files. 0749 vmeta); 0750 0751 if (!data.isEmpty()) 0752 { 0753 QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); 0754 setXmpTagString("Xmp.video.TrackCreateDate", 0755 QString::number(s_secondsSinceJanuary1904(dt))); 0756 0757 setXmpTagString("Xmp.xmpDM.shotDate", dt.toString()); 0758 } 0759 0760 // -------------- 0761 0762 s_setXmpTagStringFromEntry(this, 0763 QStringList() << QLatin1String("handler_name"), // Generic. 0764 vmeta, 0765 QStringList() << QLatin1String("Xmp.video.HandlerDescription")); 0766 0767 // -------------- 0768 0769 s_setXmpTagStringFromEntry(this, 0770 QStringList() << QLatin1String("TVER") // RIFF files. 0771 << QLatin1String("_STATISTICS_WRITING_APP"), // MKV files. 0772 vmeta, 0773 QStringList() << QLatin1String("Xmp.video.SoftwareVersion")); 0774 } 0775 0776 // ----------------------------------------- 0777 // Subtitle stream parsing 0778 // ----------------------------------------- 0779 0780 if (!sstream && (codec->codec_type == AVMEDIA_TYPE_SUBTITLE)) 0781 { 0782 sstream = true; 0783 0784 setXmpTagString("Xmp.video.SubTCodec", 0785 QString::fromUtf8(cname)); 0786 setXmpTagString("Xmp.video.SubTCodecInfo", 0787 QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name)); 0788 0789 // ----------------------------------------- 0790 0791 MetaDataMap smeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata); 0792 0793 qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg subtitle stream metadata entries :"; 0794 qCDebug(DIGIKAM_METAENGINE_LOG) << smeta; 0795 qCDebug(DIGIKAM_METAENGINE_LOG) << "--------------------------------------------"; 0796 0797 // -------------- 0798 0799 s_setXmpTagStringFromEntry(this, 0800 QStringList() << QLatin1String("subtitle") // Generic. 0801 << QLatin1String("title"), // Generic. 0802 smeta, 0803 QStringList() << QLatin1String("Xmp.video.Subtitle")); 0804 0805 // -------------- 0806 0807 s_setXmpTagStringFromEntry(this, 0808 QStringList() << QLatin1String("language"), // Generic. 0809 smeta, 0810 QStringList() << QLatin1String("Xmp.video.SubTLang")); 0811 } 0812 } 0813 0814 // ----------------------------------------- 0815 // Root container parsing 0816 // ----------------------------------------- 0817 0818 MetaDataMap rmeta = s_extractFFMpegMetadataEntriesFromDictionary(fmt_ctx->metadata); 0819 0820 qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg root container metadata entries :"; 0821 qCDebug(DIGIKAM_METAENGINE_LOG) << rmeta; 0822 qCDebug(DIGIKAM_METAENGINE_LOG) << "------------------------------------------"; 0823 0824 // ---------------------------- 0825 0826 s_setXmpTagStringFromEntry(this, 0827 QStringList() << QLatin1String("major_brand"), // Generic. 0828 rmeta, 0829 QStringList() << QLatin1String("Xmp.video.MajorBrand")); 0830 0831 // -------------- 0832 0833 s_setXmpTagStringFromEntry(this, 0834 QStringList() << QLatin1String("compatible_brands"), // Generic. 0835 rmeta, 0836 QStringList() << QLatin1String("Xmp.video.CompatibleBrands")); 0837 0838 // -------------- 0839 0840 s_setXmpTagStringFromEntry(this, 0841 QStringList() << QLatin1String("minor_version"), // Generic. 0842 rmeta, 0843 QStringList() << QLatin1String("Xmp.video.MinorVersion")); 0844 0845 // -------------- 0846 0847 data = s_setXmpTagStringFromEntry(this, 0848 QStringList() << QLatin1String("keywords") // Generic. 0849 << QLatin1String("IMIT") // RIFF files. 0850 << QLatin1String("KEYWORDS") // MKV files. 0851 << QLatin1String("com.apple.quicktime.keywords"), // QT files. 0852 rmeta, 0853 QStringList() << QLatin1String("Xmp.video.InfoText")); 0854 0855 if (!data.isEmpty()) 0856 { 0857 QStringList keywords = s_keywordsSeparation(data); 0858 0859 if (!keywords.isEmpty()) 0860 { 0861 setXmpKeywords(keywords); 0862 setIptcKeywords(QStringList(), keywords); 0863 } 0864 } 0865 0866 // -------------- 0867 0868 data = s_setXmpTagStringFromEntry(this, 0869 QStringList() << QLatin1String("category") // Generic. 0870 << QLatin1String("ISBJ") // RIFF files. 0871 << QLatin1String("SUBJECT"), // MKV files. 0872 rmeta, 0873 QStringList() << QLatin1String("Xmp.video.Subject")); 0874 0875 if (!data.isEmpty()) 0876 { 0877 QStringList categories = s_keywordsSeparation(data); 0878 0879 if (!categories.isEmpty()) 0880 { 0881 setXmpSubCategories(categories); 0882 setIptcSubCategories(QStringList(), categories); 0883 } 0884 } 0885 0886 // -------------- 0887 0888 s_setXmpTagStringFromEntry(this, 0889 QStringList() << QLatin1String("premiere_version") // Generic. 0890 << QLatin1String("quicktime_version") // Generic. 0891 << QLatin1String("ISFT") // Riff files 0892 << QLatin1String("com.apple.quicktime.software"), // QT files. 0893 rmeta, 0894 QStringList() << QLatin1String("Xmp.video.SoftwareVersion")); 0895 0896 // -------------- 0897 0898 s_setXmpTagStringFromEntry(this, 0899 QStringList() << QLatin1String("firmware") // Generic. 0900 << QLatin1String("com.apple.proapps.serialno"), // QT files. 0901 rmeta, 0902 QStringList() << QLatin1String("Xmp.video.FirmwareVersion")); 0903 0904 // -------------- 0905 0906 s_setXmpTagStringFromEntry(this, 0907 QStringList() << QLatin1String("composer") // Generic. 0908 << QLatin1String("COMPOSER"), // MKV files 0909 rmeta, 0910 QStringList() << QLatin1String("Xmp.video.Composer") 0911 << QLatin1String("Xmp.xmpDM.composer")); 0912 0913 // -------------- 0914 0915 s_setXmpTagStringFromEntry(this, 0916 QStringList() << QLatin1String("com.apple.quicktime.displayname"), // QT files. 0917 rmeta, 0918 QStringList() << QLatin1String("Xmp.video.Name")); 0919 0920 // -------------- 0921 0922 s_setXmpTagStringFromEntry(this, 0923 QStringList() << QLatin1String("playback_requirements"), // Generic. 0924 rmeta, 0925 QStringList() << QLatin1String("Xmp.video.Requirements")); 0926 0927 // -------------- 0928 0929 s_setXmpTagStringFromEntry(this, 0930 QStringList() << QLatin1String("lyrics"), // Generic. 0931 rmeta, 0932 QStringList() << QLatin1String("Xmp.video.Lyrics")); 0933 0934 // -------------- 0935 0936 s_setXmpTagStringFromEntry(this, 0937 QStringList() << QLatin1String("filename"), // Generic. 0938 rmeta, 0939 QStringList() << QLatin1String("Xmp.video.FileName")); 0940 0941 // -------------- 0942 0943 s_setXmpTagStringFromEntry(this, 0944 QStringList() << QLatin1String("disk"), // Generic. 0945 rmeta, 0946 QStringList() << QLatin1String("Xmp.xmpDM.discNumber")); 0947 0948 // -------------- 0949 0950 s_setXmpTagStringFromEntry(this, 0951 QStringList() << QLatin1String("performers"), // Generic. 0952 rmeta, 0953 QStringList() << QLatin1String("Xmp.video.Performers")); 0954 0955 // -------------- 0956 0957 s_setXmpTagStringFromEntry(this, 0958 QStringList() << QLatin1String("producer") // Generic. 0959 << QLatin1String("PRODUCER") // MKV files. 0960 << QLatin1String("com.apple.quicktime.producer"), // QT files. 0961 rmeta, 0962 QStringList() << QLatin1String("Xmp.video.Producer")); 0963 0964 // -------------- 0965 0966 s_setXmpTagStringFromEntry(this, 0967 QStringList() << QLatin1String("artist") // Generic. 0968 << QLatin1String("album_artist") // Generic. 0969 << QLatin1String("original_artist") // Generic. 0970 << QLatin1String("com.apple.quicktime.artist") // QT files. 0971 << QLatin1String("IART") // RIFF files. 0972 << QLatin1String("ARTIST") // MKV files. 0973 << QLatin1String("author") // Generic. 0974 << QLatin1String("com.apple.quicktime.author"), // QT files. 0975 rmeta, 0976 QStringList() << QLatin1String("Xmp.video.Artist") 0977 << QLatin1String("Xmp.xmpDM.artist")); 0978 0979 // -------------- 0980 0981 s_setXmpTagStringFromEntry(this, 0982 QStringList() << QLatin1String("director") // Generic. 0983 << QLatin1String("DIRC") // RIFF files. 0984 << QLatin1String("DIRECTOR") // MKV files. 0985 << QLatin1String("com.apple.quicktime.director"), // QT files. 0986 rmeta, 0987 QStringList() << QLatin1String("Xmp.video.Director") 0988 << QLatin1String("Xmp.xmpDM.director")); 0989 0990 // -------------- 0991 0992 s_setXmpTagStringFromEntry(this, 0993 QStringList() << QLatin1String("media_type") // Generic. 0994 << QLatin1String("IMED") // RIFF files. 0995 << QLatin1String("ORIGINAL_MEDIA_TYPE"), // MKV files. 0996 rmeta, 0997 QStringList() << QLatin1String("Xmp.video.Medium")); 0998 0999 // -------------- 1000 1001 s_setXmpTagStringFromEntry(this, 1002 QStringList() << QLatin1String("grouping"), // Generic. 1003 rmeta, 1004 QStringList() << QLatin1String("Xmp.video.Grouping")); 1005 1006 // -------------- 1007 1008 s_setXmpTagStringFromEntry(this, 1009 QStringList() << QLatin1String("BPS"), // MKV files 1010 rmeta, 1011 QStringList() << QLatin1String("Xmp.video.MaxBitRate")); 1012 1013 // -------------- 1014 1015 s_setXmpTagStringFromEntry(this, 1016 QStringList() << QLatin1String("ISRC"), // MKV files 1017 rmeta, 1018 QStringList() << QLatin1String("Xmp.video.ISRCCode")); 1019 1020 // -------------- 1021 1022 s_setXmpTagStringFromEntry(this, 1023 QStringList() << QLatin1String("CONTENT_TYPE"), // MKV files 1024 rmeta, 1025 QStringList() << QLatin1String("Xmp.video.ExtendedContentDescription")); 1026 1027 // -------------- 1028 1029 s_setXmpTagStringFromEntry(this, 1030 QStringList() << QLatin1String("FPS"), // MKV files. 1031 rmeta, 1032 QStringList() << QLatin1String("Xmp.video.videoFrameRate") 1033 << QLatin1String("Xmp.xmpDM.FrameRate")); 1034 1035 // -------------- 1036 1037 s_setXmpTagStringFromEntry(this, 1038 QStringList() << QLatin1String("encoder") // Generic. 1039 << QLatin1String("ENCODER"), // MKV files. 1040 rmeta, 1041 QStringList() << QLatin1String("Xmp.video.Encoder")); 1042 1043 // -------------- 1044 1045 s_setXmpTagStringFromEntry(this, 1046 QStringList() << QLatin1String("com.apple.proapps.clipID"), // QT files. 1047 rmeta, 1048 QStringList() << QLatin1String("Xmp.video.FileID")); 1049 1050 // -------------- 1051 1052 s_setXmpTagStringFromEntry(this, 1053 QStringList() << QLatin1String("original_source") // Generic. 1054 << QLatin1String("ISRC") // Riff files 1055 << QLatin1String("com.apple.proapps.cameraName"), // QT files. 1056 rmeta, 1057 QStringList() << QLatin1String("Xmp.video.Source")); 1058 1059 // -------------- 1060 1061 s_setXmpTagStringFromEntry(this, 1062 QStringList() << QLatin1String("original_format") // Generic. 1063 << QLatin1String("com.apple.proapps.originalFormat"), // QT files. 1064 rmeta, 1065 QStringList() << QLatin1String("Xmp.video.Format")); 1066 1067 // -------------- 1068 1069 data = s_setXmpTagStringFromEntry(this, 1070 QStringList() << QLatin1String("rating") // Generic. 1071 << QLatin1String("IRTD") // RIFF files. 1072 << QLatin1String("RATE") // RIFF files. 1073 << QLatin1String("RATING") // MKV files. 1074 << QLatin1String("com.apple.quicktime.rating.user"), // QT files. 1075 rmeta, 1076 QStringList() << QLatin1String("Xmp.video.Rating") 1077 << QLatin1String("Xmp.video.Rate")); 1078 1079 if (!data.isEmpty()) 1080 { 1081 // Backport rating in Exif and Iptc 1082 1083 bool b = false; 1084 int rating = data.toInt(&b); 1085 1086 if (b) 1087 { 1088 setItemRating(rating); 1089 } 1090 } 1091 1092 // -------------- 1093 1094 s_setXmpTagStringFromEntry(this, 1095 QStringList() << QLatin1String("make") // Generic. 1096 << QLatin1String("com.apple.quicktime.make"), // QT files. 1097 rmeta, 1098 QStringList() << QLatin1String("Xmp.video.Make")); 1099 1100 // -------------- 1101 1102 s_setXmpTagStringFromEntry(this, 1103 QStringList() << QLatin1String("model") // Generic. 1104 << QLatin1String("com.apple.quicktime.model"), // QT files. 1105 rmeta, 1106 QStringList() << QLatin1String("Xmp.video.Model") 1107 << QLatin1String("Xmp.xmpDM.cameraModel")); 1108 1109 // -------------- 1110 1111 s_setXmpTagStringFromEntry(this, 1112 QStringList() << QLatin1String("URL") // Generic. 1113 << QLatin1String("TURL"), // RIFF files. 1114 rmeta, 1115 QStringList() << QLatin1String("Xmp.video.URL")); 1116 1117 1118 // -------------- 1119 1120 s_setXmpTagStringFromEntry(this, 1121 QStringList() << QLatin1String("title") // Generic. 1122 << QLatin1String("INAM") // RIFF files. 1123 << QLatin1String("TITL") // RIFF files. 1124 << QLatin1String("TITLE") // MKV files. 1125 << QLatin1String("com.apple.quicktime.title"), // QT files. 1126 rmeta, 1127 QStringList() << QLatin1String("Xmp.video.Title") 1128 << QLatin1String("Xmp.xmpDM.shotName")); 1129 1130 // -------------- 1131 1132 s_setXmpTagStringFromEntry(this, 1133 QStringList() << QLatin1String("copyright") // Generic. 1134 << QLatin1String("ICOP") // RIFF files. 1135 << QLatin1String("COPYRIGHT") // MKV files. 1136 << QLatin1String("com.apple.quicktime.copyright"), // QT files. 1137 rmeta, 1138 QStringList() << QLatin1String("Xmp.video.Copyright") 1139 << QLatin1String("Xmp.xmpDM.copyright")); 1140 1141 // -------------- 1142 1143 data = s_setXmpTagStringFromEntry(this, 1144 QStringList() << QLatin1String("comment") // Generic. 1145 << QLatin1String("description") // Generic. 1146 << QLatin1String("CMNT") // Riff Files. 1147 << QLatin1String("COMN") // Riff Files. 1148 << QLatin1String("ICMT") // Riff Files. 1149 << QLatin1String("COMMENT") // MKV Files. 1150 << QLatin1String("DESCRIPTION") // MKV Files. 1151 << QLatin1String("com.apple.quicktime.description"), // QT files. 1152 rmeta, 1153 QStringList() << QLatin1String("Xmp.video.Comment") 1154 << QLatin1String("Xmp.xmpDM.logComment")); 1155 1156 if (!data.isEmpty()) 1157 { 1158 // Backport comment in Exif and Iptc 1159 1160 CaptionsMap capMap; 1161 MetaEngine::AltLangMap comMap; 1162 comMap.insert(QLatin1String("x-default"), data); 1163 capMap.setData(comMap, MetaEngine::AltLangMap(), QString(), MetaEngine::AltLangMap()); 1164 1165 setItemComments(capMap); 1166 } 1167 1168 // -------------- 1169 1170 s_setXmpTagStringFromEntry(this, 1171 QStringList() << QLatin1String("synopsis") // Generic. 1172 << QLatin1String("SUMMARY") // MKV files. 1173 << QLatin1String("SYNOPSIS"), // MKV files. 1174 rmeta, 1175 QStringList() << QLatin1String("Xmp.video.Information")); 1176 1177 // -------------- 1178 1179 s_setXmpTagStringFromEntry(this, 1180 QStringList() << QLatin1String("lyrics") // Generic. 1181 << QLatin1String("LYRICS"), // MKV files. 1182 rmeta, 1183 QStringList() << QLatin1String("Xmp.xmpDM.lyrics")); 1184 1185 // -------------- 1186 1187 for (int i = 1 ; i <= 9 ; ++i) 1188 { 1189 s_setXmpTagStringFromEntry(this, 1190 QStringList() << QString::fromLatin1("IAS%1").arg(i), // RIFF files. 1191 rmeta, 1192 QStringList() << QString::fromLatin1("Xmp.video.Edit%1").arg(i)); 1193 } 1194 1195 // -------------- 1196 1197 s_setXmpTagStringFromEntry(this, 1198 QStringList() << QLatin1String("encoded_by") // Generic. 1199 << QLatin1String("CODE") // RIFF files. 1200 << QLatin1String("IECN") // RIFF files. 1201 << QLatin1String("ENCODED_BY"), // MKV files. 1202 rmeta, 1203 QStringList() << QLatin1String("Xmp.video.EncodedBy")); 1204 1205 // -------------- 1206 1207 s_setXmpTagStringFromEntry(this, 1208 QStringList() << QLatin1String("DISP"), // RIFF files. 1209 rmeta, 1210 QStringList() << QLatin1String("Xmp.video.SchemeTitle")); 1211 1212 // -------------- 1213 1214 s_setXmpTagStringFromEntry(this, 1215 QStringList() << QLatin1String("AGES") // RIFF files. 1216 << QLatin1String("ICRA") // MKV files. 1217 << QLatin1String("LAW_RATING"), // MKV files. 1218 rmeta, 1219 QStringList() << QLatin1String("Xmp.video.Rated")); 1220 1221 // -------------- 1222 1223 s_setXmpTagStringFromEntry(this, 1224 QStringList() << QLatin1String("IBSU"), // RIFF files. 1225 rmeta, 1226 QStringList() << QLatin1String("Xmp.video.BaseURL")); 1227 1228 // -------------- 1229 1230 s_setXmpTagStringFromEntry(this, 1231 QStringList() << QLatin1String("ICAS"), // RIFF files. 1232 rmeta, 1233 QStringList() << QLatin1String("Xmp.video.DefaultStream")); 1234 1235 // -------------- 1236 1237 s_setXmpTagStringFromEntry(this, 1238 QStringList() << QLatin1String("ICDS"), // RIFF files. 1239 rmeta, 1240 QStringList() << QLatin1String("Xmp.video.CostumeDesigner")); 1241 1242 // -------------- 1243 1244 s_setXmpTagStringFromEntry(this, 1245 QStringList() << QLatin1String("ICMS"), // RIFF files. 1246 rmeta, 1247 QStringList() << QLatin1String("Xmp.video.Commissioned")); 1248 1249 // -------------- 1250 1251 s_setXmpTagStringFromEntry(this, 1252 QStringList() << QLatin1String("ICNM"), // RIFF files. 1253 rmeta, 1254 QStringList() << QLatin1String("Xmp.video.Cinematographer")); 1255 1256 // -------------- 1257 1258 s_setXmpTagStringFromEntry(this, 1259 QStringList() << QLatin1String("ICNT"), // RIFF files. 1260 rmeta, 1261 QStringList() << QLatin1String("Xmp.video.Country")); 1262 1263 // -------------- 1264 1265 s_setXmpTagStringFromEntry(this, 1266 QStringList() << QLatin1String("IARL"), // RIFF files. 1267 rmeta, 1268 QStringList() << QLatin1String("Xmp.video.ArchivalLocation")); 1269 1270 // -------------- 1271 1272 s_setXmpTagStringFromEntry(this, 1273 QStringList() << QLatin1String("ICRP"), // RIFF files. 1274 rmeta, 1275 QStringList() << QLatin1String("Xmp.video.Cropped")); 1276 1277 // -------------- 1278 1279 s_setXmpTagStringFromEntry(this, 1280 QStringList() << QLatin1String("IDIM"), // RIFF files. 1281 rmeta, 1282 QStringList() << QLatin1String("Xmp.video.Dimensions")); 1283 1284 // -------------- 1285 1286 s_setXmpTagStringFromEntry(this, 1287 QStringList() << QLatin1String("IDPI"), // RIFF files. 1288 rmeta, 1289 QStringList() << QLatin1String("Xmp.video.DotsPerInch")); 1290 1291 // -------------- 1292 1293 s_setXmpTagStringFromEntry(this, 1294 QStringList() << QLatin1String("IDST") // RIFF files. 1295 << QLatin1String("DISTRIBUTED_BY"), // MKV files. 1296 rmeta, 1297 QStringList() << QLatin1String("Xmp.video.DistributedBy")); 1298 1299 // -------------- 1300 1301 s_setXmpTagStringFromEntry(this, 1302 QStringList() << QLatin1String("IEDT"), // RIFF files. 1303 rmeta, 1304 QStringList() << QLatin1String("Xmp.video.EditedBy")); 1305 1306 // -------------- 1307 1308 s_setXmpTagStringFromEntry(this, 1309 QStringList() << QLatin1String("IENG"), // RIFF files. 1310 rmeta, 1311 QStringList() << QLatin1String("Xmp.video.Engineer") 1312 << QLatin1String("Xmp.xmpDM.engineer")); 1313 1314 // -------------- 1315 1316 s_setXmpTagStringFromEntry(this, 1317 QStringList() << QLatin1String("IKEY"), // RIFF files. 1318 rmeta, 1319 QStringList() << QLatin1String("Xmp.video.PerformerKeywords")); 1320 1321 // -------------- 1322 1323 s_setXmpTagStringFromEntry(this, 1324 QStringList() << QLatin1String("ILGT"), // RIFF files. 1325 rmeta, 1326 QStringList() << QLatin1String("Xmp.video.Lightness")); 1327 1328 // -------------- 1329 1330 s_setXmpTagStringFromEntry(this, 1331 QStringList() << QLatin1String("ILGU"), // RIFF files. 1332 rmeta, 1333 QStringList() << QLatin1String("Xmp.video.LogoURL")); 1334 1335 // -------------- 1336 1337 s_setXmpTagStringFromEntry(this, 1338 QStringList() << QLatin1String("ILIU"), // RIFF files. 1339 rmeta, 1340 QStringList() << QLatin1String("Xmp.video.LogoIconURL")); 1341 1342 // -------------- 1343 1344 s_setXmpTagStringFromEntry(this, 1345 QStringList() << QLatin1String("IMBI"), // RIFF files. 1346 rmeta, 1347 QStringList() << QLatin1String("Xmp.video.InfoBannerImage")); 1348 1349 // -------------- 1350 1351 s_setXmpTagStringFromEntry(this, 1352 QStringList() << QLatin1String("IMBU"), // RIFF files. 1353 rmeta, 1354 QStringList() << QLatin1String("Xmp.video.InfoBannerURL")); 1355 1356 // -------------- 1357 1358 s_setXmpTagStringFromEntry(this, 1359 QStringList() << QLatin1String("IMIU"), // RIFF files. 1360 rmeta, 1361 QStringList() << QLatin1String("Xmp.video.InfoURL")); 1362 1363 // -------------- 1364 1365 s_setXmpTagStringFromEntry(this, 1366 QStringList() << QLatin1String("IMUS"), // RIFF files. 1367 rmeta, 1368 QStringList() << QLatin1String("Xmp.video.MusicBy")); 1369 1370 // -------------- 1371 1372 s_setXmpTagStringFromEntry(this, 1373 QStringList() << QLatin1String("IPDS"), // RIFF files. 1374 rmeta, 1375 QStringList() << QLatin1String("Xmp.video.ProductionDesigner")); 1376 1377 // -------------- 1378 1379 s_setXmpTagStringFromEntry(this, 1380 QStringList() << QLatin1String("IPLT"), // RIFF files. 1381 rmeta, 1382 QStringList() << QLatin1String("Xmp.video.NumOfColors")); 1383 1384 // -------------- 1385 1386 s_setXmpTagStringFromEntry(this, 1387 QStringList() << QLatin1String("IPRD"), // RIFF files. 1388 rmeta, 1389 QStringList() << QLatin1String("Xmp.video.Product")); 1390 1391 // -------------- 1392 1393 s_setXmpTagStringFromEntry(this, 1394 QStringList() << QLatin1String("IPRO"), // RIFF files. 1395 rmeta, 1396 QStringList() << QLatin1String("Xmp.video.ProducedBy")); 1397 1398 // -------------- 1399 1400 s_setXmpTagStringFromEntry(this, 1401 QStringList() << QLatin1String("IRIP"), // RIFF files. 1402 rmeta, 1403 QStringList() << QLatin1String("Xmp.video.RippedBy")); 1404 1405 // -------------- 1406 1407 s_setXmpTagStringFromEntry(this, 1408 QStringList() << QLatin1String("ISGN"), // RIFF files. 1409 rmeta, 1410 QStringList() << QLatin1String("Xmp.video.SecondaryGenre")); 1411 1412 // -------------- 1413 1414 s_setXmpTagStringFromEntry(this, 1415 QStringList() << QLatin1String("ISHP"), // RIFF files. 1416 rmeta, 1417 QStringList() << QLatin1String("Xmp.video.Sharpness")); 1418 1419 // -------------- 1420 1421 s_setXmpTagStringFromEntry(this, 1422 QStringList() << QLatin1String("ISRF"), // RIFF files. 1423 rmeta, 1424 QStringList() << QLatin1String("Xmp.video.SourceForm")); 1425 1426 // -------------- 1427 1428 s_setXmpTagStringFromEntry(this, 1429 QStringList() << QLatin1String("ISTD"), // RIFF files. 1430 rmeta, 1431 QStringList() << QLatin1String("Xmp.video.ProductionStudio")); 1432 1433 // -------------- 1434 1435 s_setXmpTagStringFromEntry(this, 1436 QStringList() << QLatin1String("ISTR") // RIFF files. 1437 << QLatin1String("STAR"), // RIFF files. 1438 rmeta, 1439 QStringList() << QLatin1String("Xmp.video.Starring")); 1440 1441 // -------------- 1442 1443 s_setXmpTagStringFromEntry(this, 1444 QStringList() << QLatin1String("ITCH"), // RIFF files. 1445 rmeta, 1446 QStringList() << QLatin1String("Xmp.video.Technician")); 1447 1448 // -------------- 1449 1450 s_setXmpTagStringFromEntry(this, 1451 QStringList() << QLatin1String("IWMU"), // RIFF files. 1452 rmeta, 1453 QStringList() << QLatin1String("Xmp.video.WatermarkURL")); 1454 1455 // -------------- 1456 1457 s_setXmpTagStringFromEntry(this, 1458 QStringList() << QLatin1String("IWRI"), // RIFF files. 1459 rmeta, 1460 QStringList() << QLatin1String("Xmp.video.WrittenBy")); 1461 1462 // -------------- 1463 1464 s_setXmpTagStringFromEntry(this, 1465 QStringList() << QLatin1String("PRT1"), // RIFF files. 1466 rmeta, 1467 QStringList() << QLatin1String("Xmp.video.Part")); 1468 1469 // -------------- 1470 1471 s_setXmpTagStringFromEntry(this, 1472 QStringList() << QLatin1String("PRT2"), // RIFF files. 1473 rmeta, 1474 QStringList() << QLatin1String("Xmp.video.NumOfParts")); 1475 1476 // -------------- 1477 1478 s_setXmpTagStringFromEntry(this, 1479 QStringList() << QLatin1String("STAT"), // RIFF files. 1480 rmeta, 1481 QStringList() << QLatin1String("Xmp.video.Statistics")); 1482 1483 // -------------- 1484 1485 s_setXmpTagStringFromEntry(this, 1486 QStringList() << QLatin1String("TAPE"), // RIFF files. 1487 rmeta, 1488 QStringList() << QLatin1String("Xmp.video.TapeName")); 1489 1490 // -------------- 1491 1492 s_setXmpTagStringFromEntry(this, 1493 QStringList() << QLatin1String("TCDO"), // RIFF files. 1494 rmeta, 1495 QStringList() << QLatin1String("Xmp.video.EndTimecode")); 1496 1497 // -------------- 1498 1499 s_setXmpTagStringFromEntry(this, 1500 QStringList() << QLatin1String("TCOD"), // RIFF files. 1501 rmeta, 1502 QStringList() << QLatin1String("Xmp.video.StartTimecode")); 1503 1504 // -------------- 1505 1506 s_setXmpTagStringFromEntry(this, 1507 QStringList() << QLatin1String("TLEN"), // RIFF files. 1508 rmeta, 1509 QStringList() << QLatin1String("Xmp.video.Length")); 1510 1511 // -------------- 1512 1513 s_setXmpTagStringFromEntry(this, 1514 QStringList() << QLatin1String("TORG"), // RIFF files. 1515 rmeta, 1516 QStringList() << QLatin1String("Xmp.video.Organization")); 1517 1518 // -------------- 1519 1520 s_setXmpTagStringFromEntry(this, 1521 QStringList() << QLatin1String("VMAJ"), // RIFF files. 1522 rmeta, 1523 QStringList() << QLatin1String("Xmp.video.VegasVersionMajor")); 1524 1525 // -------------- 1526 1527 s_setXmpTagStringFromEntry(this, 1528 QStringList() << QLatin1String("VMIN"), // RIFF files. 1529 rmeta, 1530 QStringList() << QLatin1String("Xmp.video.VegasVersionMinor")); 1531 1532 // -------------- 1533 1534 s_setXmpTagStringFromEntry(this, 1535 QStringList() << QLatin1String("LOCA"), // RIFF files. 1536 rmeta, 1537 QStringList() << QLatin1String("Xmp.video.LocationInfo") 1538 << QLatin1String("Xmp.xmpDM.shotLocation")); 1539 1540 // -------------- 1541 1542 s_setXmpTagStringFromEntry(this, 1543 QStringList() << QLatin1String("album") // Generic. 1544 << QLatin1String("com.apple.quicktime.album"), // QT files. 1545 rmeta, 1546 QStringList() << QLatin1String("Xmp.video.Album") 1547 << QLatin1String("Xmp.xmpDM.album")); 1548 1549 // -------------- 1550 1551 s_setXmpTagStringFromEntry(this, 1552 QStringList() << QLatin1String("genre") // Generic. 1553 << QLatin1String("GENR") // RIFF files. 1554 << QLatin1String("IGNR") // RIFF files. 1555 << QLatin1String("GENRE") // MKV files. 1556 << QLatin1String("com.apple.quicktime.genre"), // QT files. 1557 rmeta, 1558 QStringList() << QLatin1String("Xmp.video.Genre") 1559 << QLatin1String("Xmp.xmpDM.genre")); 1560 1561 // -------------- 1562 1563 s_setXmpTagStringFromEntry(this, 1564 QStringList() << QLatin1String("track") // Generic. 1565 << QLatin1String("TRCK"), // RIFF files. 1566 rmeta, 1567 QStringList() << QLatin1String("Xmp.video.TrackNumber") 1568 << QLatin1String("Xmp.xmpDM.trackNumber")); 1569 1570 // -------------- 1571 1572 s_setXmpTagStringFromEntry(this, 1573 QStringList() << QLatin1String("year") // Generic. 1574 << QLatin1String("YEAR") // RIFF files. 1575 << QLatin1String("com.apple.quicktime.year"), 1576 rmeta, 1577 QStringList() << QLatin1String("Xmp.video.Year")); 1578 1579 // -------------- 1580 1581 s_setXmpTagStringFromEntry(this, 1582 QStringList() << QLatin1String("ICRD") // Riff files 1583 << QLatin1String("DATE_DIGITIZED"), // MKV files 1584 rmeta, 1585 QStringList() << QLatin1String("Xmp.video.DateTimeDigitized")); 1586 1587 // -------------- 1588 1589 QStringList videoDateTimeOriginal; 1590 videoDateTimeOriginal << QLatin1String("creation_time") // Generic. 1591 << QLatin1String("DTIM") // RIFF files. 1592 << QLatin1String("DATE_RECORDED") // MKV files. 1593 << QLatin1String("com.apple.quicktime.creationdate"); // QT files. 1594 1595 if (rmeta.contains(QLatin1String("creation_time"))) 1596 { 1597 if (rmeta.contains(QLatin1String("com.apple.quicktime.creationdate"))) 1598 { 1599 if (rmeta.value(QLatin1String("com.apple.quicktime.make")) == QLatin1String("Apple")) 1600 { 1601 QString dateStr = rmeta.value(QLatin1String("com.apple.quicktime.creationdate")); 1602 QDateTime creationdDate = QDateTime::fromString(dateStr, Qt::ISODate); 1603 1604 if (creationdDate.isValid()) 1605 { 1606 creationdDate.setTimeZone(QTimeZone(0)); 1607 dateStr = creationdDate.toString(Qt::ISODate); 1608 dateStr.chop(6); 1609 rmeta.insert(QLatin1String("com.apple.quicktime.creationdate"), dateStr); 1610 } 1611 } 1612 1613 videoDateTimeOriginal.prepend(videoDateTimeOriginal.takeLast()); 1614 } 1615 else if ( 1616 !((rmeta.contains(QLatin1String("com.android.model"))) || 1617 (rmeta.contains(QLatin1String("com.android.version"))) || 1618 (rmeta.contains(QLatin1String("com.android.capture.fps"))) || 1619 (rmeta.contains(QLatin1String("com.android.manufacturer"))) || 1620 (rmeta.value(QLatin1String("encoder")) == QLatin1String("DJI OsmoPocket3")) || 1621 (rmeta.value(QLatin1String("com.apple.quicktime.make")) == QLatin1String("Canon"))) && 1622 !(rmeta.value(QLatin1String("compatible_brands")) == QLatin1String("mp42avc1niko")) 1623 ) 1624 { 1625 if (rmeta[QLatin1String("creation_time")].endsWith(QLatin1Char('Z'))) 1626 { 1627 rmeta[QLatin1String("creation_time")].chop(1); 1628 } 1629 } 1630 } 1631 1632 data = s_setXmpTagStringFromEntry(this, 1633 videoDateTimeOriginal, 1634 rmeta, 1635 QStringList() << QLatin1String("Xmp.video.DateTimeOriginal")); 1636 1637 if (!data.isEmpty()) 1638 { 1639 // Backport date in Exif and Iptc. 1640 1641 QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); 1642 setImageDateTime(dt, true); 1643 } 1644 1645 // -------------- 1646 1647 s_setXmpTagStringFromEntry(this, 1648 QStringList() << QLatin1String("edit_date"), // Generic. 1649 rmeta, 1650 QStringList() << QLatin1String("Xmp.video.ModificationDate") 1651 << QLatin1String("Xmp.xmpDM.videoModDate")); 1652 1653 // -------------- 1654 1655 data = s_setXmpTagStringFromEntry(this, 1656 QStringList() << QLatin1String("date") // Generic. 1657 << QLatin1String("DATE_RELEASED"), // MKV files. 1658 rmeta); 1659 1660 if (!data.isEmpty()) 1661 { 1662 QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); 1663 setXmpTagString("Xmp.video.MediaCreateDate", 1664 QString::number(s_secondsSinceJanuary1904(dt))); 1665 } 1666 1667 // -------------- 1668 1669 // GPS info as string. ex: "+44.8511-000.6229/" 1670 // Defined in ISO 6709:2008. 1671 // Notes: altitude can be passed as 3rd values. 1672 // each value is separated from others by '-' or '+'. 1673 // '/' is always the terminaison character. 1674 1675 data = s_setXmpTagStringFromEntry(this, 1676 QStringList() << QLatin1String("location") // Generic. 1677 << QLatin1String("RECORDING_LOCATION") // MKV files. 1678 << QLatin1String("com.apple.quicktime.location.ISO6709"), // QT files. 1679 rmeta, 1680 QStringList() << QLatin1String("Xmp.video.GPSCoordinates") 1681 << QLatin1String("Xmp.xmpDM.shotLocation")); 1682 1683 if (!data.isEmpty()) 1684 { 1685 // Backport location to Exif. 1686 1687 QList<int> digits; 1688 1689 for (int i = 0 ; i < data.length() ; ++i) 1690 { 1691 QChar c = data[i]; 1692 1693 if ((c == QLatin1Char('+')) || (c == QLatin1Char('-')) || (c == QLatin1Char('/'))) 1694 { 1695 digits << i; 1696 } 1697 } 1698 1699 QString coord; 1700 double lattitude = 0.0; 1701 double longitude = 0.0; 1702 double altitude = 0.0; 1703 bool b1 = false; 1704 bool b2 = false; 1705 bool b3 = false; 1706 1707 if (digits.size() > 1) 1708 { 1709 coord = data.mid(digits[0], digits[1] - digits[0]); 1710 lattitude = coord.toDouble(&b1); 1711 } 1712 1713 if (digits.size() > 2) 1714 { 1715 coord = data.mid(digits[1], digits[2] - digits[1]); 1716 longitude = coord.toDouble(&b2); 1717 } 1718 1719 if (digits.size() > 3) 1720 { 1721 coord = data.mid(digits[2], digits[3] - digits[2]); 1722 altitude = coord.toDouble(&b3); 1723 } 1724 1725 if (b1 && b2) 1726 { 1727 if (b3) 1728 { 1729 // All GPS values are available. 1730 1731 setGPSInfo(altitude, lattitude, longitude); 1732 1733 setXmpTagString("Xmp.video.GPSAltitude", 1734 getXmpTagString("Xmp.exif.GPSAltitude")); 1735 setXmpTagString("Xmp.exif.GPSAltitude", 1736 getXmpTagString("Xmp.exif.GPSAltitude")); 1737 } 1738 else 1739 { 1740 // No altitude available. 1741 1742 double* alt = nullptr; 1743 setGPSInfo(alt, lattitude, longitude); 1744 } 1745 1746 setXmpTagString("Xmp.video.GPSLatitude", 1747 getXmpTagString("Xmp.exif.GPSLatitude")); 1748 setXmpTagString("Xmp.video.GPSLongitude", 1749 getXmpTagString("Xmp.exif.GPSLongitude")); 1750 setXmpTagString("Xmp.video.GPSMapDatum", 1751 getXmpTagString("Xmp.exif.GPSMapDatum")); 1752 setXmpTagString("Xmp.video.GPSVersionID", 1753 getXmpTagString("Xmp.exif.GPSVersionID")); 1754 1755 setXmpTagString("Xmp.exif.GPSLatitude", 1756 getXmpTagString("Xmp.exif.GPSLatitude")); 1757 setXmpTagString("Xmp.exif.GPSLongitude", 1758 getXmpTagString("Xmp.exif.GPSLongitude")); 1759 setXmpTagString("Xmp.exif.GPSMapDatum", 1760 getXmpTagString("Xmp.exif.GPSMapDatum")); 1761 setXmpTagString("Xmp.exif.GPSVersionID", 1762 getXmpTagString("Xmp.exif.GPSVersionID")); 1763 } 1764 } 1765 1766 avformat_close_input(&fmt_ctx); 1767 1768 QFileInfo fi(filePath); 1769 1770 if (getXmpTagString("Xmp.video.FileName").isNull()) 1771 { 1772 setXmpTagString("Xmp.video.FileName", fi.fileName()); 1773 } 1774 1775 if (getXmpTagString("Xmp.video.FileSize").isNull()) 1776 { 1777 setXmpTagString("Xmp.video.FileSize", QString::number(fi.size() / (1024*1024))); 1778 } 1779 1780 if (getXmpTagString("Xmp.video.FileType").isNull()) 1781 { 1782 setXmpTagString("Xmp.video.FileType", fi.suffix()); 1783 } 1784 1785 if (getXmpTagString("Xmp.video.MimeType").isNull()) 1786 { 1787 setXmpTagString("Xmp.video.MimeType", QMimeDatabase().mimeTypeForFile(filePath).name()); 1788 } 1789 1790 return true; 1791 1792 #else 1793 1794 Q_UNUSED(filePath); 1795 1796 return false; 1797 1798 #endif 1799 1800 } 1801 1802 QString DMetadata::videoColorModelToString(VIDEOCOLORMODEL videoColorModel) 1803 { 1804 QString cs; 1805 1806 switch (videoColorModel) 1807 { 1808 case VIDEOCOLORMODEL_SRGB: 1809 { 1810 cs = QLatin1String("sRGB"); 1811 break; 1812 } 1813 1814 case VIDEOCOLORMODEL_BT601: 1815 { 1816 cs = QLatin1String("CCIR-601"); 1817 break; 1818 } 1819 1820 case VIDEOCOLORMODEL_BT709: 1821 { 1822 cs = QLatin1String("CCIR-709"); 1823 break; 1824 } 1825 1826 case VIDEOCOLORMODEL_OTHER: 1827 { 1828 cs = QLatin1String("Other"); 1829 break; 1830 } 1831 1832 default: // VIDEOCOLORMODEL_UNKNOWN 1833 { 1834 break; 1835 } 1836 } 1837 1838 return cs; 1839 } 1840 1841 VideoInfoContainer DMetadata::getVideoInformation() const 1842 { 1843 VideoInfoContainer videoInfo; 1844 1845 if (hasXmp()) 1846 { 1847 if (videoInfo.aspectRatio.isEmpty()) 1848 { 1849 videoInfo.aspectRatio = getMetadataField(MetadataInfo::AspectRatio).toString(); 1850 } 1851 1852 if (videoInfo.audioBitRate.isEmpty()) 1853 { 1854 videoInfo.audioBitRate = getXmpTagString("Xmp.audio.SampleRate"); 1855 } 1856 1857 if (videoInfo.audioChannelType.isEmpty()) 1858 { 1859 videoInfo.audioChannelType = getXmpTagString("Xmp.audio.ChannelType"); 1860 } 1861 1862 if (videoInfo.audioCodec.isEmpty()) 1863 { 1864 videoInfo.audioCodec = getXmpTagString("Xmp.audio.Codec"); 1865 } 1866 1867 if (videoInfo.duration.isEmpty()) 1868 { 1869 videoInfo.duration = getXmpTagString("Xmp.video.duration"); 1870 } 1871 1872 if (videoInfo.frameRate.isEmpty()) 1873 { 1874 videoInfo.frameRate = getXmpTagString("Xmp.video.FrameRate"); 1875 } 1876 1877 if (videoInfo.videoCodec.isEmpty()) 1878 { 1879 videoInfo.videoCodec = getXmpTagString("Xmp.video.Codec"); 1880 } 1881 } 1882 1883 return videoInfo; 1884 } 1885 1886 } // namespace Digikam