/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2019-02-26
 * Description : item metadata interface - video helpers.
 *
 * References  :
 *
 * FFMpeg metadata review: https://wiki.multimedia.cx/index.php/FFmpeg_Metadata
 * FFMpeg MP4 parser     : https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mov.c#L298
 * Exiv2 XMP video       : https://github.com/Exiv2/exiv2/blob/master/src/properties.cpp#L1331
 * Exiv2 RIFF tags       : https://github.com/Exiv2/exiv2/blob/master/src/riffvideo.cpp#L83
 * Apple metadata desc   : https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html
 * Matroska metadata desc: https://matroska.org/technical/specs/tagging/index.html
 * FFMpeg metadata writer: https://github.com/kritzikratzi/ofxAvCodec/blob/master/src/ofxAvUtils.cpp#L61
 *
 * FFMpeg tags names origin:
 *
 * Generic    : common tags generated by FFMpeg codecs.
 * RIFF files : Resource Interchange File Format tags (as AVI).
 * MKV files  : Matroska container tags.
 * QT files   : Quicktime container tags (Apple).
 *
 * SPDX-FileCopyrightText: 2019-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * ============================================================ */

#include "dmetadata.h"

// C Ansi includes

#include <stdint.h>

// Qt includes

#include <QDateTime>
#include <QFileInfo>
#include <QMimeDatabase>
#include <QStringList>
#include <QTimeZone>
#include <QtMath>

// KDE includes

#include <klocalizedstring.h>

// Local includes

#include "captionvalues.h"
#include "digikam_debug.h"
#include "digikam_config.h"
#include "digikam_globals.h"

#ifdef HAVE_MEDIAPLAYER

// Libav includes

extern "C"
{

#include <libavformat/avformat.h>
#include <libavutil/dict.h>
#include <libavutil/pixdesc.h>
#include <libavcodec/avcodec.h>

#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 18, 0)

#   include <libavutil/display.h>

#endif

}

#endif

namespace Digikam
{

/**
 * Search first occurrence of string in 'map' with keys given by 'lst'.
 * Return the string match.
 * If 'xmpTags' is not empty, register XMP tags value with string.
 */
QString s_setXmpTagStringFromEntry(const DMetadata* const meta,
                                   const QStringList& lst,
                                   const DMetadata::MetaDataMap& map,
                                   const QStringList& xmpTags = QStringList())
{
    for (const QString& tag : EXIV2_AS_CONST(lst))
    {
        DMetadata::MetaDataMap::const_iterator it = map.find(tag);

        if (it != map.end())
        {
            if (
                meta &&                     // Protection.
                !xmpTags.isEmpty()          // If xmpTags is empty, we only return the matching value from the map.
               )
            {
                for (const QString& tag2 : EXIV2_AS_CONST(xmpTags))
                {
                    // Only register the tag value if it doesn't exists yet.

                    if (meta->getXmpTagString(tag2.toLatin1().data()).isNull())
                    {
                        meta->setXmpTagString(tag2.toLatin1().data(), it.value());
                    }
                }
            }

            return it.value();
        }
    }

    return QString();
}

QStringList s_keywordsSeparation(const QString& data)
{
    QStringList keywords = data.split(QLatin1Char('/'));

    if (keywords.isEmpty())
    {
        keywords = data.split(QLatin1Char(','));

        if (keywords.isEmpty())
        {
            keywords = data.split(QLatin1Char(' '));
        }
    }

    return keywords;
}

qint64 s_secondsSinceJanuary1904(const QDateTime& dt)
{
    QDateTime dt1904(QDate(1904, 1, 1), QTime(0, 0, 0));

    return dt1904.secsTo(dt);
}

#ifdef HAVE_MEDIAPLAYER

QString s_convertFFMpegFormatToXMP(int format)
{
    QString data;

    switch (format)
    {
        case AV_SAMPLE_FMT_U8:
        case AV_SAMPLE_FMT_U8P:
        {
            data = QLatin1String("8Int");
            break;
        }

        case AV_SAMPLE_FMT_S16:
        case AV_SAMPLE_FMT_S16P:
        {
            data = QLatin1String("16Int");
            break;
        }

        case AV_SAMPLE_FMT_S32:
        case AV_SAMPLE_FMT_S32P:
        {
            data = QLatin1String("32Int");
            break;
        }

        case AV_SAMPLE_FMT_FLT:
        case AV_SAMPLE_FMT_FLTP:
        {
            data = QLatin1String("32Float");
            break;
        }

        case AV_SAMPLE_FMT_DBL:     // Not supported by XMP spec.
        case AV_SAMPLE_FMT_DBLP:    // Not supported by XMP spec.
        case AV_SAMPLE_FMT_S64:     // Not supported by XMP spec.
        case AV_SAMPLE_FMT_S64P:    // Not supported by XMP spec.
        case AV_SAMPLE_FMT_NONE:
        case AV_SAMPLE_FMT_NB:
        default:
        {
            data = QLatin1String("Other");
            break;
        }

        // NOTE: where are 'Compressed' and 'Packed' type from XMP spec into FFMPEG ?
    }

    return data;
}

DMetadata::MetaDataMap s_extractFFMpegMetadataEntriesFromDictionary(AVDictionary* const dict)
{
    AVDictionaryEntry* entry = nullptr;
    DMetadata::MetaDataMap meta;

    do
    {
        entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX);

        if (entry)
        {
            QString entryValue = QString::fromUtf8(entry->value);

            if (QString::fromUtf8(entry->key) == QLatin1String("creation_time"))
            {
                if (QDateTime::fromString(entryValue, Qt::ISODate).toMSecsSinceEpoch() == 0)
                {
                    continue;
                }
            }

            meta.insert(QString::fromUtf8(entry->key), entryValue);
        }
    }
    while (entry);

    return meta;
}

#endif

bool DMetadata::loadUsingFFmpeg(const QString& filePath)
{

#ifdef HAVE_MEDIAPLAYER

    qCDebug(DIGIKAM_METAENGINE_LOG) << "Parse metadada with FFMpeg:" << filePath;

#if LIBAVFORMAT_VERSION_MAJOR < 58

    av_register_all();

#endif

    AVFormatContext* fmt_ctx = avformat_alloc_context();
    int ret                  = avformat_open_input(&fmt_ctx, filePath.toUtf8().data(), nullptr, nullptr);

    if (ret < 0)
    {
        qCDebug(DIGIKAM_METAENGINE_LOG) << "avformat_open_input error: " << ret;

        return false;
    }

    ret = avformat_find_stream_info(fmt_ctx, nullptr);

    if (ret < 0)
    {
        qCDebug(DIGIKAM_METAENGINE_LOG) << "avform_find_stream_info error: " << ret;

        return false;
    }

    QString data;

    setXmpTagString("Xmp.video.duration",
        QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE)));
    setXmpTagString("Xmp.xmpDM.duration",
        QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE)));

    if (fmt_ctx->bit_rate > 0)
    {
        setXmpTagString("Xmp.video.MaxBitRate", QString::number(fmt_ctx->bit_rate));
    }

    setXmpTagString("Xmp.video.StreamCount",
        QString::number(fmt_ctx->nb_streams));

    // To only register one video, one audio stream, and one subtitle stream in XMP metadata.

    bool vstream = false;
    bool astream = false;
    bool sstream = false;

    for (uint i = 0 ; i < fmt_ctx->nb_streams ; ++i)
    {
        const AVStream* const stream   = fmt_ctx->streams[i];

        if (!stream)
        {
            continue;
        }

        AVCodecParameters* const codec = stream->codecpar;

        if (!codec)
        {
            continue;
        }

        const char* cname              = avcodec_get_name(codec->codec_id);

        if (QLatin1String(cname) == QLatin1String("none"))
        {
            if      (codec->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                setXmpTagString("Xmp.audio.Codec",
                    QString::fromUtf8(cname));
            }
            else if (codec->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                setXmpTagString("Xmp.video.Codec",
                    QString::fromUtf8(cname));
            }

            continue;
        }

        // -----------------------------------------
        // Audio stream parsing
        // -----------------------------------------

        if (!astream && (codec->codec_type == AVMEDIA_TYPE_AUDIO))
        {
            astream = true;

            setXmpTagString("Xmp.audio.Codec",
                QString::fromUtf8(cname));

            setXmpTagString("Xmp.audio.CodecDescription",
                QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name));

            setXmpTagString("Xmp.audio.SampleRate",
                QString::number(codec->sample_rate));
            setXmpTagString("Xmp.xmpDM.audioSampleRate",
                QString::number(codec->sample_rate));

            // See XMP Dynamic Media properties from Adobe.
            // Audio Channel type is a limited untranslated string choice depending of amount of audio channels

            data = QString();

#if LIBAVUTIL_VERSION_MAJOR > 56

            int channels = codec->ch_layout.nb_channels;

#else

            int channels = codec->channels;

#endif

            switch (channels)
            {
                case 0:
                {
                    break;
                }

                case 1:
                {
                    data = QLatin1String("Mono");
                    break;
                }

                case 2:
                {
                    data = QLatin1String("Stereo");
                    break;
                }

                case 6:
                {
                    data = QLatin1String("5.1");
                    break;
                }

                case 8:
                {
                    data = QLatin1String("7.1");
                    break;
                }

                case 16:
                {
                    data = QLatin1String("16 Channel");
                    break;
                }

                default:
                {
                    data = QLatin1String("Other");
                    break;
                }
            }

            if (!data.isEmpty())
            {
                setXmpTagString("Xmp.audio.ChannelType",      data);
                setXmpTagString("Xmp.xmpDM.audioChannelType", data);
            }

            setXmpTagString("Xmp.audio.Format",
                QString::fromUtf8(av_get_sample_fmt_name((AVSampleFormat)codec->format)));

            // See XMP Dynamic Media properties from Adobe.
            // Audio Sample type is a limited untranslated string choice depending of amount of audio samples

            data = s_convertFFMpegFormatToXMP(codec->format);

            if (!data.isEmpty())
            {
                setXmpTagString("Xmp.audio.SampleType",      data);
                setXmpTagString("Xmp.xmpDM.audioSampleType", data);
            }

            // --------------

            MetaDataMap ameta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata);

            qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg audio stream metadata entries :";
            qCDebug(DIGIKAM_METAENGINE_LOG) << ameta;
            qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------";

            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("language"),                              // Generic.
                                       ameta,
                                       QStringList() << QLatin1String("Xmp.audio.TrackLang"));

            // --------------
/*
            data = s_setXmpTagStringFromEntry(this,
                                              QStringList() << QLatin1String("creation_time"),                  // Generic.
                                              ameta);

            if (!data.isEmpty())
            {
                QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
                setXmpTagString("Xmp.audio.TrackCreateDate",
                                QString::number(s_secondsSinceJanuary1904(dt)));
            }
*/
            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("handler_name"),                          // Generic.
                                       ameta,
                                       QStringList() << QLatin1String("Xmp.audio.HandlerDescription"));
        }

        // -----------------------------------------
        // Video stream parsing
        // -----------------------------------------

        if (!vstream && (codec->codec_type == AVMEDIA_TYPE_VIDEO))
        {
            vstream = true;

            setXmpTagString("Xmp.video.Codec",
                 QString::fromUtf8(cname));

            setXmpTagString("Xmp.video.CodecDescription",
                 QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name));

            setXmpTagString("Xmp.video.Format",
                 QString::fromUtf8(av_get_pix_fmt_name((AVPixelFormat)codec->format)));

            // Store in this tag the full description off FFMPEG video color space.

            setXmpTagString("Xmp.video.ColorMode",
                 QString::fromUtf8(av_color_space_name((AVColorSpace)codec->color_space)));

            VIDEOCOLORMODEL cm = VIDEOCOLORMODEL_OTHER;

            switch (codec->color_space)
            {
                case AVCOL_SPC_RGB:
                {
                    cm = VIDEOCOLORMODEL_SRGB;
                    break;
                }

                case AVCOL_SPC_BT470BG:
                case AVCOL_SPC_SMPTE170M:
                case AVCOL_SPC_SMPTE240M:
                {
                    cm = VIDEOCOLORMODEL_BT601;
                    break;
                }

                case AVCOL_SPC_BT709:
                {
                    cm = VIDEOCOLORMODEL_BT709;
                    break;
                }

                case AVCOL_SPC_UNSPECIFIED:
                case AVCOL_SPC_RESERVED:
                case AVCOL_SPC_NB:
                {
                    cm = VIDEOCOLORMODEL_UNKNOWN;
                    break;
                }

                default:
                {
                    break;
                }
            }

            // See XMP Dynamic Media properties from Adobe.
            // Video Color Space is a limited untranslated string choice depending of video color space value.

            data = videoColorModelToString(cm);

            if (!data.isEmpty())
            {
                setXmpTagString("Xmp.video.ColorSpace",      data);
                setXmpTagString("Xmp.xmpDM.videoColorSpace", data);
            }

            // ----------

            QString fo;

            switch (codec->field_order)
            {
                case AV_FIELD_PROGRESSIVE:
                {
                    fo = QLatin1String("Progressive");
                    break;
                }

                case AV_FIELD_TT:                       // Top coded first, top displayed first
                case AV_FIELD_BT:                       // Bottom coded first, top displayed first
                {
                    fo = QLatin1String("Upper");
                    break;
                }

                case AV_FIELD_BB:                       // Bottom coded first, bottom displayed first
                case AV_FIELD_TB:                       // Top coded first, bottom displayed first
                {
                    fo = QLatin1String("Lower");
                    break;
                }

                default:
                {
                    break;
                }
            }

            if (!fo.isEmpty())
            {
                setXmpTagString("Xmp.xmpDM.FieldOrder", fo);
            }

            // ----------

            QString aspectRatio;
            double frameRate = -1.0;

            if      (codec->sample_aspect_ratio.num != 0)    // Check if undefined by ffmpeg
            {
                AVRational displayAspectRatio;

                av_reduce(&displayAspectRatio.num, &displayAspectRatio.den,
                          codec->width  * (int64_t)codec->sample_aspect_ratio.num,
                          codec->height * (int64_t)codec->sample_aspect_ratio.den,
                          1024 * 1024);

                aspectRatio = QString::fromLatin1("%1/%2").arg(displayAspectRatio.num)
                                                          .arg(displayAspectRatio.den);
            }
            else if (codec->height)
            {
                aspectRatio = QString::fromLatin1("%1/%2").arg(codec->width)
                                                          .arg(codec->height);
            }

            if (stream->avg_frame_rate.den)
            {
                frameRate = (double)stream->avg_frame_rate.num / (double)stream->avg_frame_rate.den;
            }

            setXmpTagString("Xmp.video.Width",
                QString::number(codec->width));
            setXmpTagString("Xmp.video.FrameWidth",
                QString::number(codec->width));
            setXmpTagString("Xmp.video.SourceImageWidth",
                QString::number(codec->width));

            setXmpTagString("Xmp.video.Height",
                QString::number(codec->height));
            setXmpTagString("Xmp.video.FrameHeight",
                QString::number(codec->height));
            setXmpTagString("Xmp.video.SourceImageHeight",
                QString::number(codec->height));

            setXmpTagString("Xmp.video.FrameSize",
                QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height));
            setXmpTagString("Xmp.xmpDM.videoFrameSize",
                QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height));

            // Backport size in Exif and Iptc

            setItemDimensions(QSize(codec->width, codec->height));

            if (!aspectRatio.isEmpty())
            {
                setXmpTagString("Xmp.video.AspectRatio",           aspectRatio);
                setXmpTagString("Xmp.xmpDM.videoPixelAspectRatio", aspectRatio);
            }

            if (frameRate != -1.0)
            {
                setXmpTagString("Xmp.video.FrameRate", QString::number(frameRate));

                // See XMP Dynamic Media properties from Adobe.
                // Video Color Space is a limited untranslated string choice depending of video frame rate.
                // https://documentation.apple.com/en/finalcutpro/usermanual/index.html#chapter=D%26section=4%26tasks=true

                data = QLatin1String("Other");

                if      (frameRate == 24.0)
                {
                    data = QLatin1String("24");
                }
                else if (
                         (frameRate == 23.98) || (frameRate == 29.97) ||
                         (frameRate == 30.0)  || (frameRate == 59.94)
                        )
                {
                    data = QLatin1String("NTSC");
                }
                else if (frameRate == 25.0 || frameRate == 50.0)
                {
                    data = QLatin1String("PAL");
                }

                setXmpTagString("Xmp.xmpDM.videoFrameRate", data);
            }

            setXmpTagString("Xmp.video.BitDepth", QString::number(codec->bits_per_coded_sample));

            // See XMP Dynamic Media properties from Adobe.
            // Video Pixel Depth is a limited untranslated string choice depending of amount of samples format.

            data = s_convertFFMpegFormatToXMP(codec->format);

            if (!data.isEmpty())
            {
                setXmpTagString("Xmp.xmpDM.videoPixelDepth", data);
            }

            // -----------------------------------------

            MetaDataMap vmeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata);

            qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg video stream metadata entries :";
            qCDebug(DIGIKAM_METAENGINE_LOG) << vmeta;
            qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------";

            // --------------

#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 29, 100)

            const AVPacketSideData* const sd = av_packet_side_data_get(codec->coded_side_data,
                                                                       codec->nb_coded_side_data,
                                                                       AV_PKT_DATA_DISPLAYMATRIX);
            quint8* const sideData = sd ? sd->data : nullptr;

#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 18, 0)

            quint8* const sideData = reinterpret_cast<quint8*>(av_stream_get_side_data(stream,
                                                                                       AV_PKT_DATA_DISPLAYMATRIX,
                                                                                       nullptr));
#endif

#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 18, 0)

            if (sideData)
            {
                double r = av_display_rotation_get(reinterpret_cast<qint32*>(sideData));

                if (!qIsNaN(r))
                {
                    r   -= 360 * qFloor(r / 360 + 0.9 / 360);
                    data = QString::number(360 - r);
                }
            }

#else

            data = s_setXmpTagStringFromEntry(this,
                                              QStringList() << QLatin1String("rotate"),                         // Generic.
                                              vmeta);

#endif

            if (!data.isEmpty())
            {
                bool b               = false;
                int val              = data.toInt(&b);
                ImageOrientation ori = ORIENTATION_UNSPECIFIED;

                if (b)
                {
                    switch (val)
                    {
                        case 0:
                        {
                            ori = ORIENTATION_NORMAL;
                            break;
                        }

                        case 90:
                        {
                            ori = ORIENTATION_ROT_90;
                            break;
                        }

                        case 180:
                        {
                            ori = ORIENTATION_ROT_180;
                            break;
                        }

                        case 270:
                        {
                            ori = ORIENTATION_ROT_270;
                            break;
                        }

                        default:
                        {
                            break;
                        }
                    }

                    setXmpTagString("Xmp.video.Orientation", QString::number(ori));

                    // Backport orientation in Exif

                    setItemOrientation(ori);
                }
            }

            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("language")                               // Generic.
                                                     << QLatin1String("ILNG")                                   // RIFF files.
                                                     << QLatin1String("LANG"),                                  // RIFF files.
                                       vmeta,
                                       QStringList() << QLatin1String("Xmp.video.Language"));

            // --------------
/*
            data = s_setXmpTagStringFromEntry(this,
                                              QStringList() << QLatin1String("creation_time")                   // Generic.
                                                            << QLatin1String("_STATISTICS_WRITING_DATE_UTC"),   // MKV files.
                                              vmeta);

            if (!data.isEmpty())
            {
                QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
                setXmpTagString("Xmp.video.TrackCreateDate",
                                QString::number(s_secondsSinceJanuary1904(dt)));

                setXmpTagString("Xmp.xmpDM.shotDate", dt.toString(QLatin1String("yyyy-MM-ddThh:mm:ss")));
            }
*/
            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("handler_name"),                          // Generic.
                                       vmeta,
                                       QStringList() << QLatin1String("Xmp.video.HandlerDescription"));

            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("TVER")                                   // RIFF files.
                                                     << QLatin1String("_STATISTICS_WRITING_APP"),               // MKV files.
                                       vmeta,
                                       QStringList() << QLatin1String("Xmp.video.SoftwareVersion"));
        }

        // -----------------------------------------
        // Subtitle stream parsing
        // -----------------------------------------

        if (!sstream && (codec->codec_type == AVMEDIA_TYPE_SUBTITLE))
        {
            sstream = true;

            setXmpTagString("Xmp.video.SubTCodec",
                QString::fromUtf8(cname));
            setXmpTagString("Xmp.video.SubTCodecInfo",
                QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name));

            // -----------------------------------------

            MetaDataMap smeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata);

            qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg subtitle stream metadata entries :";
            qCDebug(DIGIKAM_METAENGINE_LOG) << smeta;
            qCDebug(DIGIKAM_METAENGINE_LOG) << "--------------------------------------------";

            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("subtitle")                               // Generic.
                                                     << QLatin1String("title"),                                 // Generic.
                                       smeta,
                                       QStringList() << QLatin1String("Xmp.video.Subtitle"));

            // --------------

            s_setXmpTagStringFromEntry(this,
                                       QStringList() << QLatin1String("language"),                              // Generic.
                                       smeta,
                                       QStringList() << QLatin1String("Xmp.video.SubTLang"));
        }
    }

    // -----------------------------------------
    // Root container parsing
    // -----------------------------------------

    MetaDataMap rmeta = s_extractFFMpegMetadataEntriesFromDictionary(fmt_ctx->metadata);

    qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg root container metadata entries :";
    qCDebug(DIGIKAM_METAENGINE_LOG) << rmeta;
    qCDebug(DIGIKAM_METAENGINE_LOG) << "------------------------------------------";

    // ----------------------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("major_brand"),                                   // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.MajorBrand"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("compatible_brands"),                             // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.CompatibleBrands"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("minor_version"),                                 // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.MinorVersion"));

    // --------------

    data = s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("keywords")                                       // Generic.
                                             << QLatin1String("IMIT")                                           // RIFF files.
                                             << QLatin1String("KEYWORDS")                                       // MKV files.
                                             << QLatin1String("com.apple.quicktime.keywords"),                  // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.InfoText"));

    if (!data.isEmpty())
    {
        QStringList keywords = s_keywordsSeparation(data);

        if (!keywords.isEmpty())
        {
            setXmpKeywords(keywords);
            setIptcKeywords(QStringList(), keywords);
        }
    }

    // --------------

    data = s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("category")                                       // Generic.
                                             << QLatin1String("ISBJ")                                           // RIFF files.
                                             << QLatin1String("SUBJECT"),                                       // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Subject"));

    if (!data.isEmpty())
    {
        QStringList categories = s_keywordsSeparation(data);

        if (!categories.isEmpty())
        {
            setXmpSubCategories(categories);
            setIptcSubCategories(QStringList(), categories);
        }
    }

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("premiere_version")                               // Generic.
                                             << QLatin1String("quicktime_version")                              // Generic.
                                             << QLatin1String("ISFT")                                           // Riff files
                                             << QLatin1String("com.apple.quicktime.software"),                  // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.SoftwareVersion"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("firmware")                                       // Generic.
                                             << QLatin1String("com.apple.proapps.serialno"),                    // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.FirmwareVersion"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("composer")                                       // Generic.
                                             << QLatin1String("COMPOSER"),                                      // MKV files
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Composer")
                                             << QLatin1String("Xmp.xmpDM.composer"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("com.apple.quicktime.displayname"),               // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Name"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("playback_requirements"),                         // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Requirements"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("lyrics"),                                        // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Lyrics"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("filename"),                                      // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.FileName"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("disk"),                                          // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.xmpDM.discNumber"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("performers"),                                    // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Performers"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("producer")                                       // Generic.
                                             << QLatin1String("PRODUCER")                                       // MKV files.
                                             << QLatin1String("com.apple.quicktime.producer"),                  // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Producer"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("artist")                                         // Generic.
                                             << QLatin1String("album_artist")                                   // Generic.
                                             << QLatin1String("original_artist")                                // Generic.
                                             << QLatin1String("com.apple.quicktime.artist")                     // QT files.
                                             << QLatin1String("IART")                                           // RIFF files.
                                             << QLatin1String("ARTIST")                                         // MKV files.
                                             << QLatin1String("author")                                         // Generic.
                                             << QLatin1String("com.apple.quicktime.author"),                    // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Artist")
                                             << QLatin1String("Xmp.xmpDM.artist"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("director")                                       // Generic.
                                             << QLatin1String("DIRC")                                           // RIFF files.
                                             << QLatin1String("DIRECTOR")                                       // MKV files.
                                             << QLatin1String("com.apple.quicktime.director"),                  // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Director")
                                             << QLatin1String("Xmp.xmpDM.director"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("media_type")                                     // Generic.
                                             << QLatin1String("IMED")                                           // RIFF files.
                                             << QLatin1String("ORIGINAL_MEDIA_TYPE"),                           // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Medium"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("grouping"),                                      // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Grouping"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("BPS"),                                           // MKV files
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.MaxBitRate"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ISRC"),                                          // MKV files
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ISRCCode"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("CONTENT_TYPE"),                                  // MKV files
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ExtendedContentDescription"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("FPS"),                                           // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.videoFrameRate")
                                             << QLatin1String("Xmp.xmpDM.FrameRate"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("encoder")                                        // Generic.
                                             << QLatin1String("ENCODER"),                                       // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Encoder"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("com.apple.proapps.clipID"),                      // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.FileID"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("original_source")                                // Generic.
                                             << QLatin1String("ISRC")                                           // Riff files
                                             << QLatin1String("com.apple.proapps.cameraName"),                  // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Source"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("original_format")                                // Generic.
                                             << QLatin1String("com.apple.proapps.originalFormat"),              // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Format"));

    // --------------

    data = s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("rating")                                         // Generic.
                                             << QLatin1String("IRTD")                                           // RIFF files.
                                             << QLatin1String("RATE")                                           // RIFF files.
                                             << QLatin1String("RATING")                                         // MKV files.
                                             << QLatin1String("com.apple.quicktime.rating.user"),               // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Rating")
                                             << QLatin1String("Xmp.video.Rate"));

    if (!data.isEmpty())
    {
        // Backport rating in Exif and Iptc

        bool b     = false;
        int rating = data.toInt(&b);

        if (b)
        {
            setItemRating(rating);
        }
    }

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("make")                                           // Generic.
                                             << QLatin1String("com.apple.quicktime.make"),                      // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Make"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("model")                                          // Generic.
                                             << QLatin1String("com.apple.quicktime.model"),                     // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Model")
                                             << QLatin1String("Xmp.xmpDM.cameraModel"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("URL")                                            // Generic.
                                             << QLatin1String("TURL"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.URL"));


    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("title")                                          // Generic.
                                             << QLatin1String("INAM")                                           // RIFF files.
                                             << QLatin1String("TITL")                                           // RIFF files.
                                             << QLatin1String("TITLE")                                          // MKV files.
                                             << QLatin1String("com.apple.quicktime.title"),                     // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Title")
                                             << QLatin1String("Xmp.xmpDM.shotName"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("copyright")                                      // Generic.
                                             << QLatin1String("ICOP")                                           // RIFF files.
                                             << QLatin1String("COPYRIGHT")                                      // MKV files.
                                             << QLatin1String("com.apple.quicktime.copyright"),                 // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Copyright")
                                             << QLatin1String("Xmp.xmpDM.copyright"));

    // --------------

    data = s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("comment")                                        // Generic.
                                             << QLatin1String("description")                                    // Generic.
                                             << QLatin1String("CMNT")                                           // Riff Files.
                                             << QLatin1String("COMN")                                           // Riff Files.
                                             << QLatin1String("ICMT")                                           // Riff Files.
                                             << QLatin1String("COMMENT")                                        // MKV Files.
                                             << QLatin1String("DESCRIPTION")                                    // MKV Files.
                                             << QLatin1String("com.apple.quicktime.description"),               // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Comment")
                                             << QLatin1String("Xmp.xmpDM.logComment"));
/*
    if (!data.isEmpty())
    {
        // Backport comment in Exif and Iptc

        CaptionsMap capMap;
        MetaEngine::AltLangMap comMap;
        comMap.insert(QLatin1String("x-default"), data);
        capMap.setData(comMap, MetaEngine::AltLangMap(), QString(), MetaEngine::AltLangMap());

        setItemComments(capMap);
    }
*/
    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("synopsis")                                       // Generic.
                                             << QLatin1String("SUMMARY")                                        // MKV files.
                                             << QLatin1String("SYNOPSIS"),                                      // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Information"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("lyrics")                                         // Generic.
                                             << QLatin1String("LYRICS"),                                        // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.xmpDM.lyrics"));

    // --------------

    for (int i = 1 ; i <= 9 ; ++i)
    {
        s_setXmpTagStringFromEntry(this,
                               QStringList() << QString::fromLatin1("IAS%1").arg(i),                            // RIFF files.
                               rmeta,
                               QStringList() << QString::fromLatin1("Xmp.video.Edit%1").arg(i));
    }

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("encoded_by")                                     // Generic.
                                             << QLatin1String("CODE")                                           // RIFF files.
                                             << QLatin1String("IECN")                                           // RIFF files.
                                             << QLatin1String("ENCODED_BY"),                                    // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.EncodedBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("DISP"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.SchemeTitle"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("AGES")                                           // RIFF files.
                                             << QLatin1String("ICRA")                                           // MKV files.
                                             << QLatin1String("LAW_RATING"),                                    // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Rated"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IBSU"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.BaseURL"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICAS"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.DefaultStream"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICDS"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.CostumeDesigner"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICMS"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Commissioned"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICNM"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Cinematographer"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICNT"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Country"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IARL"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ArchivalLocation"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICRP"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Cropped"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IDIM"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Dimensions"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IDPI"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.DotsPerInch"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IDST")                                           // RIFF files.
                                             << QLatin1String("DISTRIBUTED_BY"),                                // MKV files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.DistributedBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IEDT"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.EditedBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IENG"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Engineer")
                                             << QLatin1String("Xmp.xmpDM.engineer"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IKEY"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.PerformerKeywords"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ILGT"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Lightness"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ILGU"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.LogoURL"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ILIU"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.LogoIconURL"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IMBI"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.InfoBannerImage"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IMBU"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.InfoBannerURL"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IMIU"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.InfoURL"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IMUS"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.MusicBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IPDS"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ProductionDesigner"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IPLT"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.NumOfColors"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IPRD"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Product"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IPRO"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ProducedBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IRIP"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.RippedBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ISGN"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.SecondaryGenre"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ISHP"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Sharpness"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ISRF"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.SourceForm"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ISTD"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ProductionStudio"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ISTR")                                           // RIFF files.
                                             << QLatin1String("STAR"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Starring"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ITCH"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Technician"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IWMU"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.WatermarkURL"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("IWRI"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.WrittenBy"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("PRT1"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Part"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("PRT2"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.NumOfParts"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("STAT"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Statistics"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("TAPE"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.TapeName"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("TCDO"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.EndTimecode"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("TCOD"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.StartTimecode"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("TLEN"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Length"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("TORG"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Organization"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("VMAJ"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.VegasVersionMajor"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("VMIN"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.VegasVersionMinor"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("LOCA"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.LocationInfo")
                                             << QLatin1String("Xmp.xmpDM.shotLocation"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("album")                                          // Generic.
                                             << QLatin1String("com.apple.quicktime.album"),                     // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Album")
                                             << QLatin1String("Xmp.xmpDM.album"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("genre")                                          // Generic.
                                             << QLatin1String("GENR")                                           // RIFF files.
                                             << QLatin1String("IGNR")                                           // RIFF files.
                                             << QLatin1String("GENRE")                                          // MKV files.
                                             << QLatin1String("com.apple.quicktime.genre"),                     // QT files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Genre")
                                             << QLatin1String("Xmp.xmpDM.genre"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("track")                                          // Generic.
                                             << QLatin1String("TRCK"),                                          // RIFF files.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.TrackNumber")
                                             << QLatin1String("Xmp.xmpDM.trackNumber"));

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("year")                                           // Generic.
                                             << QLatin1String("YEAR")                                           // RIFF files.
                                             << QLatin1String("com.apple.quicktime.year"),
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.Year"));

    // --------------
/*
    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("ICRD")                                           // Riff files
                                             << QLatin1String("DATE_DIGITIZED"),                                // MKV files
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.DateTimeDigitized"));

    // --------------

    QStringList videoDateTimeOriginal;
    videoDateTimeOriginal                    << QLatin1String("creation_time")                                  // Generic.
                                             << QLatin1String("DTIM")                                           // RIFF files.
                                             << QLatin1String("DATE_RECORDED")                                  // MKV files.
                                             << QLatin1String("com.apple.quicktime.creationdate");              // QT files.

    if (rmeta.contains(QLatin1String("creation_time")))
    {
        if      (rmeta.contains(QLatin1String("com.apple.quicktime.creationdate")))
        {
            if (rmeta.value(QLatin1String("com.apple.quicktime.make")) == QLatin1String("Apple"))
            {
                QString dateStr         = rmeta.value(QLatin1String("com.apple.quicktime.creationdate"));
                QDateTime creationdDate = QDateTime::fromString(dateStr, Qt::ISODate);

                if (creationdDate.isValid() && (creationdDate.timeSpec() == Qt::OffsetFromUTC))
                {
                    dateStr = asDateTimeLocal(creationdDate).toString(Qt::ISODate);
                    rmeta.insert(QLatin1String("com.apple.quicktime.creationdate"), dateStr);
                }
            }

            videoDateTimeOriginal.prepend(videoDateTimeOriginal.takeLast());
        }
        else if (
                 !((rmeta.contains(QLatin1String("com.android.model")))                                        || ///< Android
                   (rmeta.contains(QLatin1String("com.android.version")))                                      || ///< Android
                   (rmeta.contains(QLatin1String("com.android.capture.fps")))                                  || ///< Android
                   (rmeta.contains(QLatin1String("com.android.manufacturer")))                                 || ///< Android
                   (rmeta.value(QLatin1String("com.apple.quicktime.make")) == QLatin1String("Canon"))          || ///< Canon
                   (rmeta.value(QLatin1String("compatible_brands"))        == QLatin1String("isom3gp4"))       ||
                   (rmeta.value(QLatin1String("compatible_brands"))        == QLatin1String("mp42avc1niko"))   || ///< Nikon
                   (rmeta.value(QLatin1String("compatible_brands"))        == QLatin1String("mp42avc1CAEP"))   || ///< Canon
                   (rmeta.value(QLatin1String("compatible_brands"))        == QLatin1String("isomiso2mp41"))   ||
                   (rmeta.value(QLatin1String("compatible_brands"))        == QLatin1String("XAVCmp42nrasiso6"))) ///< Casio
                 )
        {
            if (rmeta[QLatin1String("creation_time")].endsWith(QLatin1Char('Z')))
            {
                rmeta[QLatin1String("creation_time")].chop(1);
            }
        }
    }

    data = s_setXmpTagStringFromEntry(this,
                                      videoDateTimeOriginal,
                                      rmeta,
                                      QStringList() << QLatin1String("Xmp.video.DateTimeOriginal"));

    if (!data.isEmpty())
    {
        // Backport date in Xmp.

        QString exifDateTimeFormat = QLatin1String("yyyy-MM-dd hh:mm:ss");
        QString xmpDateTimeFormat  = QLatin1String("yyyy-MM-ddThh:mm:ss");
        QDateTime dateTime         = QDateTime::fromString(data, Qt::ISODate).toLocalTime();

        setExifTagString("Exif.Photo.DateTimeDigitized", dateTime.toString(exifDateTimeFormat));
        setExifTagString("Exif.Photo.DateTimeOriginal", dateTime.toString(exifDateTimeFormat));
        setExifTagString("Exif.Image.DateTime", dateTime.toString(exifDateTimeFormat));

        setXmpTagString("Xmp.exif.DateTimeDigitized", dateTime.toString(xmpDateTimeFormat));
        setXmpTagString("Xmp.exif.DateTimeOriginal", dateTime.toString(xmpDateTimeFormat));
        setXmpTagString("Xmp.xmp.CreateDate", dateTime.toString(xmpDateTimeFormat));
    }

    // --------------

    s_setXmpTagStringFromEntry(this,
                               QStringList() << QLatin1String("edit_date"),                                     // Generic.
                               rmeta,
                               QStringList() << QLatin1String("Xmp.video.ModificationDate")
                                             << QLatin1String("Xmp.xmpDM.videoModDate"));

    // --------------

    data = s_setXmpTagStringFromEntry(this,
                                      QStringList() << QLatin1String("date")                                    // Generic.
                                                    << QLatin1String("DATE_RELEASED"),                          // MKV files.
                                      rmeta);

    if (!data.isEmpty())
    {
        QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
        setXmpTagString("Xmp.video.MediaCreateDate",
                        QString::number(s_secondsSinceJanuary1904(dt)));
    }
*/
    // --------------

    // GPS info as string. ex: "+44.8511-000.6229/"
    // Defined in ISO 6709:2008.
    // Notes: altitude can be passed as 3rd values.
    //        each value is separated from others by '-' or '+'.
    //        '/' is always the terminaison character.

    data = s_setXmpTagStringFromEntry(this,
                                      QStringList() << QLatin1String("location")                                // Generic.
                                                    << QLatin1String("RECORDING_LOCATION")                      // MKV files.
                                                    << QLatin1String("com.apple.quicktime.location.ISO6709"),   // QT files.
                                      rmeta,
                                      QStringList() << QLatin1String("Xmp.video.GPSCoordinates")
                                                    << QLatin1String("Xmp.xmpDM.shotLocation"));

    if (!data.isEmpty())
    {
        // Backport location to Exif.

        QList<int> digits;

        for (int i = 0 ; i < data.length() ; ++i)
        {
            QChar c = data[i];

            if ((c == QLatin1Char('+')) || (c == QLatin1Char('-')) || (c == QLatin1Char('/')))
            {
                digits << i;
            }
        }

        QString coord;
        double lattitude = 0.0;
        double longitude = 0.0;
        double altitude  = 0.0;
        bool b1          = false;
        bool b2          = false;
        bool b3          = false;

        if (digits.size() > 1)
        {
            coord     = data.mid(digits[0], digits[1] - digits[0]);
            lattitude = coord.toDouble(&b1);
        }

        if (digits.size() > 2)
        {
            coord     = data.mid(digits[1], digits[2] - digits[1]);
            longitude = coord.toDouble(&b2);
        }

        if (digits.size() > 3)
        {
            coord    = data.mid(digits[2], digits[3] - digits[2]);
            altitude = coord.toDouble(&b3);
        }

        if (b1 && b2)
        {
            if (b3)
            {
                // All GPS values are available.

                setGPSInfo(altitude, lattitude, longitude);

                setXmpTagString("Xmp.video.GPSAltitude",
                                getXmpTagString("Xmp.exif.GPSAltitude"));
                setXmpTagString("Xmp.exif.GPSAltitude",
                                getXmpTagString("Xmp.exif.GPSAltitude"));
            }
            else
            {
                // No altitude available.

                double* alt = nullptr;
                setGPSInfo(alt, lattitude, longitude);
            }

            setXmpTagString("Xmp.video.GPSLatitude",
                            getXmpTagString("Xmp.exif.GPSLatitude"));
            setXmpTagString("Xmp.video.GPSLongitude",
                            getXmpTagString("Xmp.exif.GPSLongitude"));
            setXmpTagString("Xmp.video.GPSMapDatum",
                            getXmpTagString("Xmp.exif.GPSMapDatum"));
            setXmpTagString("Xmp.video.GPSVersionID",
                            getXmpTagString("Xmp.exif.GPSVersionID"));

            setXmpTagString("Xmp.exif.GPSLatitude",
                            getXmpTagString("Xmp.exif.GPSLatitude"));
            setXmpTagString("Xmp.exif.GPSLongitude",
                            getXmpTagString("Xmp.exif.GPSLongitude"));
            setXmpTagString("Xmp.exif.GPSMapDatum",
                            getXmpTagString("Xmp.exif.GPSMapDatum"));
            setXmpTagString("Xmp.exif.GPSVersionID",
                            getXmpTagString("Xmp.exif.GPSVersionID"));
        }
    }

    avformat_close_input(&fmt_ctx);

    QFileInfo fi(filePath);

    if (getXmpTagString("Xmp.video.FileName").isNull())
    {
        setXmpTagString("Xmp.video.FileName", fi.fileName());
    }

    if (getXmpTagString("Xmp.video.FileSize").isNull())
    {
        setXmpTagString("Xmp.video.FileSize", QString::number(fi.size() / (1024*1024)));
    }

    if (getXmpTagString("Xmp.video.FileType").isNull())
    {
        setXmpTagString("Xmp.video.FileType", fi.suffix());
    }

    if (getXmpTagString("Xmp.video.MimeType").isNull())
    {
        setXmpTagString("Xmp.video.MimeType", QMimeDatabase().mimeTypeForFile(filePath).name());
    }

    return true;

#else

    Q_UNUSED(filePath);

    return false;

#endif

}

QString DMetadata::videoColorModelToString(VIDEOCOLORMODEL videoColorModel)
{
    QString cs;

    switch (videoColorModel)
    {
        case VIDEOCOLORMODEL_SRGB:
        {
            cs = QLatin1String("sRGB");
            break;
        }

        case VIDEOCOLORMODEL_BT601:
        {
            cs = QLatin1String("CCIR-601");
            break;
        }

        case VIDEOCOLORMODEL_BT709:
        {
            cs = QLatin1String("CCIR-709");
            break;
        }

        case VIDEOCOLORMODEL_OTHER:
        {
            cs = QLatin1String("Other");
            break;
        }

        default: // VIDEOCOLORMODEL_UNKNOWN
        {
            break;
        }
    }

    return cs;
}

VideoInfoContainer DMetadata::getVideoInformation() const
{
    VideoInfoContainer videoInfo;

    if (hasXmp())
    {
        if (videoInfo.aspectRatio.isEmpty())
        {
            videoInfo.aspectRatio = getMetadataField(MetadataInfo::AspectRatio).toString();
        }

        if (videoInfo.audioBitRate.isEmpty())
        {
            videoInfo.audioBitRate = getXmpTagString("Xmp.audio.SampleRate");
        }

        if (videoInfo.audioChannelType.isEmpty())
        {
            videoInfo.audioChannelType = getXmpTagString("Xmp.audio.ChannelType");
        }

        if (videoInfo.audioCodec.isEmpty())
        {
            videoInfo.audioCodec = getXmpTagString("Xmp.audio.Codec");
        }

        if (videoInfo.duration.isEmpty())
        {
            videoInfo.duration = getXmpTagString("Xmp.video.duration");
        }

        if (videoInfo.frameRate.isEmpty())
        {
            videoInfo.frameRate = getXmpTagString("Xmp.video.FrameRate");
        }

        if (videoInfo.videoCodec.isEmpty())
        {
            videoInfo.videoCodec = getXmpTagString("Xmp.video.Codec");
        }
    }

    return videoInfo;
}

} // namespace Digikam
