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