1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2019-02-26
7  * Description : item metadata interface - video helpers.
8  *
9  * References  :
10  *
11  * FFMpeg metadata review: https://wiki.multimedia.cx/index.php/FFmpeg_Metadata
12  * FFMpeg MP4 parser     : https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mov.c#L298
13  * Exiv2 XMP video       : https://github.com/Exiv2/exiv2/blob/master/src/properties.cpp#L1331
14  * Exiv2 RIFF tags       : https://github.com/Exiv2/exiv2/blob/master/src/riffvideo.cpp#L83
15  * Apple metadata desc   : https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html
16  * Matroska metadata desc: https://matroska.org/technical/specs/tagging/index.html
17  * FFMpeg metadata writer: https://github.com/kritzikratzi/ofxAvCodec/blob/master/src/ofxAvUtils.cpp#L61
18  *
19  * FFMpeg tags names origin:
20  *
21  * Generic    : common tags generated by FFMpeg codecs.
22  * RIFF files : Resource Interchange File Format tags (as AVI).
23  * MKV files  : Matroska container tags.
24  * QT files   : Quicktime container tags (Apple).
25  *
26  * Copyright (C) 2019-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
27  *
28  * This program is free software; you can redistribute it
29  * and/or modify it under the terms of the GNU General
30  * Public License as published by the Free Software Foundation;
31  * either version 2, or (at your option)
32  * any later version.
33  *
34  * This program is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37  * GNU General Public License for more details.
38  *
39  * ============================================================ */
40 
41 #include "dmetadata.h"
42 
43 // C Ansi includes
44 
45 #include <stdint.h>
46 
47 // Qt includes
48 
49 #include <QDateTime>
50 #include <QFileInfo>
51 #include <QMimeDatabase>
52 #include <QStringList>
53 
54 // KDE includes
55 
56 #include <klocalizedstring.h>
57 
58 // Local includes
59 
60 #include "captionvalues.h"
61 #include "digikam_debug.h"
62 #include "digikam_config.h"
63 
64 #ifdef HAVE_MEDIAPLAYER
65 
66 // Libav includes
67 
68 extern "C"
69 {
70 #include <libavformat/avformat.h>
71 #include <libavutil/dict.h>
72 #include <libavutil/pixdesc.h>
73 #include <libavcodec/avcodec.h>
74 }
75 
76 #endif
77 
78 namespace Digikam
79 {
80 
81 /**
82  * Search first occurrence of string in 'map' with keys given by 'lst'.
83  * Return the string match.
84  * If 'xmpTags' is not empty, register XMP tags value with string.
85  */
s_setXmpTagStringFromEntry(DMetadata * const meta,const QStringList & lst,const DMetadata::MetaDataMap & map,const QStringList & xmpTags=QStringList ())86 QString s_setXmpTagStringFromEntry(DMetadata* const meta,
87                                    const QStringList& lst,
88                                    const DMetadata::MetaDataMap& map,
89                                    const QStringList& xmpTags = QStringList())
90 {
91     foreach (const QString& tag, lst)
92     {
93         DMetadata::MetaDataMap::const_iterator it = map.find(tag);
94 
95         if (it != map.end())
96         {
97             if (meta &&                     // Protection.
98                 !xmpTags.isEmpty())         // If xmpTags is empty, we only return the matching value from the map.
99             {
100                 foreach (const QString& tag2, xmpTags)
101                 {
102                     // Only register the tag value if it doesn't exists yet.
103 
104                     if (meta->getXmpTagString(tag2.toLatin1().data()).isNull())
105                     {
106                         meta->setXmpTagString(tag2.toLatin1().data(), it.value());
107                     }
108                 }
109             }
110 
111             return it.value();
112         }
113     }
114 
115     return QString();
116 }
117 
s_keywordsSeparation(const QString & data)118 QStringList s_keywordsSeparation(const QString& data)
119 {
120     QStringList keywords = data.split(QLatin1Char('/'));
121 
122     if (keywords.isEmpty())
123     {
124         keywords = data.split(QLatin1Char(','));
125 
126         if (keywords.isEmpty())
127         {
128             keywords = data.split(QLatin1Char(' '));
129         }
130     }
131 
132     return keywords;
133 }
134 
s_secondsSinceJanuary1904(const QDateTime & dt)135 qint64 s_secondsSinceJanuary1904(const QDateTime& dt)
136 {
137     QDateTime dt1904(QDate(1904, 1, 1), QTime(0, 0, 0));
138 
139     return dt1904.secsTo(dt);
140 }
141 
142 #ifdef HAVE_MEDIAPLAYER
143 
s_convertFFMpegFormatToXMP(int format)144 QString s_convertFFMpegFormatToXMP(int format)
145 {
146     QString data;
147 
148     switch (format)
149     {
150         case AV_SAMPLE_FMT_U8:
151         case AV_SAMPLE_FMT_U8P:
152         {
153             data = QLatin1String("8Int");
154             break;
155         }
156 
157         case AV_SAMPLE_FMT_S16:
158         case AV_SAMPLE_FMT_S16P:
159         {
160             data = QLatin1String("16Int");
161             break;
162         }
163 
164         case AV_SAMPLE_FMT_S32:
165         case AV_SAMPLE_FMT_S32P:
166         {
167             data = QLatin1String("32Int");
168             break;
169         }
170 
171         case AV_SAMPLE_FMT_FLT:
172         case AV_SAMPLE_FMT_FLTP:
173         {
174             data = QLatin1String("32Float");
175             break;
176         }
177 
178         case AV_SAMPLE_FMT_DBL:     // Not supported by XMP spec.
179         case AV_SAMPLE_FMT_DBLP:    // Not supported by XMP spec.
180         case AV_SAMPLE_FMT_S64:     // Not supported by XMP spec.
181         case AV_SAMPLE_FMT_S64P:    // Not supported by XMP spec.
182         case AV_SAMPLE_FMT_NONE:
183         case AV_SAMPLE_FMT_NB:
184         default:
185         {
186             data = QLatin1String("Other");
187             break;
188         }
189 
190         // NOTE: where are 'Compressed' and 'Packed' type from XMP spec into FFMPEG ?
191     }
192 
193     return data;
194 }
195 
s_extractFFMpegMetadataEntriesFromDictionary(AVDictionary * const dict)196 DMetadata::MetaDataMap s_extractFFMpegMetadataEntriesFromDictionary(AVDictionary* const dict)
197 {
198     AVDictionaryEntry* entry = nullptr;
199     DMetadata::MetaDataMap meta;
200 
201     do
202     {
203         entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX);
204 
205         if (entry)
206         {
207             QString entryValue = QString::fromUtf8(entry->value);
208 
209             if (QString::fromUtf8(entry->key) == QLatin1String("creation_time"))
210             {
211                 if (QDateTime::fromString(entryValue, Qt::ISODate).toMSecsSinceEpoch() == 0)
212                 {
213                     continue;
214                 }
215             }
216 
217             meta.insert(QString::fromUtf8(entry->key), entryValue);
218         }
219     }
220     while (entry);
221 
222     return meta;
223 }
224 
225 #endif
226 
loadUsingFFmpeg(const QString & filePath)227 bool DMetadata::loadUsingFFmpeg(const QString& filePath)
228 {
229 
230 #ifdef HAVE_MEDIAPLAYER
231 
232     qCDebug(DIGIKAM_METAENGINE_LOG) << "Parse metadada with FFMpeg:" << filePath;
233 
234 #if LIBAVFORMAT_VERSION_MAJOR < 58
235 
236     av_register_all();
237 
238 #endif
239 
240     AVFormatContext* fmt_ctx = avformat_alloc_context();
241     int ret                  = avformat_open_input(&fmt_ctx, filePath.toUtf8().data(), nullptr, nullptr);
242 
243     if (ret < 0)
244     {
245         qCDebug(DIGIKAM_METAENGINE_LOG) << "avformat_open_input error: " << ret;
246 
247         return false;
248     }
249 
250     ret = avformat_find_stream_info(fmt_ctx, nullptr);
251 
252     if (ret < 0)
253     {
254         qCDebug(DIGIKAM_METAENGINE_LOG) << "avform_find_stream_info error: " << ret;
255 
256         return false;
257     }
258 
259     QString data;
260 
261     setXmpTagString("Xmp.video.duration",
262         QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE)));
263     setXmpTagString("Xmp.xmpDM.duration",
264         QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE)));
265 
266     if (fmt_ctx->bit_rate > 0)
267     {
268         setXmpTagString("Xmp.video.MaxBitRate", QString::number(fmt_ctx->bit_rate));
269     }
270 
271     setXmpTagString("Xmp.video.StreamCount",
272         QString::number(fmt_ctx->nb_streams));
273 
274     // To only register one video, one audio stream, and one subtitle stream in XMP metadata.
275 
276     bool vstream = false;
277     bool astream = false;
278     bool sstream = false;
279 
280     for (uint i = 0 ; i < fmt_ctx->nb_streams ; ++i)
281     {
282         const AVStream* const stream   = fmt_ctx->streams[i];
283 
284         if (!stream)
285         {
286             continue;
287         }
288 
289         AVCodecParameters* const codec = stream->codecpar;
290 
291         if (!codec)
292         {
293             continue;
294         }
295 
296         const char* cname              = avcodec_get_name(codec->codec_id);
297 
298         if (QLatin1String(cname) == QLatin1String("none"))
299         {
300             if      (codec->codec_type == AVMEDIA_TYPE_AUDIO)
301             {
302                 setXmpTagString("Xmp.audio.Codec",
303                     QString::fromUtf8(cname));
304             }
305             else if (codec->codec_type == AVMEDIA_TYPE_VIDEO)
306             {
307                 setXmpTagString("Xmp.video.Codec",
308                     QString::fromUtf8(cname));
309             }
310 
311             continue;
312         }
313 
314         // -----------------------------------------
315         // Audio stream parsing
316         // -----------------------------------------
317 
318         if (!astream && (codec->codec_type == AVMEDIA_TYPE_AUDIO))
319         {
320             astream = true;
321 
322             setXmpTagString("Xmp.audio.Codec",
323                 QString::fromUtf8(cname));
324 
325             setXmpTagString("Xmp.audio.CodecDescription",
326                 QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name));
327 
328             setXmpTagString("Xmp.audio.SampleRate",
329                 QString::number(codec->sample_rate));
330             setXmpTagString("Xmp.xmpDM.audioSampleRate",
331                 QString::number(codec->sample_rate));
332 
333             // See XMP Dynamic Media properties from Adobe.
334             // Audio Channel type is a limited untranslated string choice depending of amount of audio channels
335 
336             data = QString();
337 
338             switch (codec->channels)
339             {
340                 case 0:
341                 {
342                     break;
343                 }
344 
345                 case 1:
346                 {
347                     data = QLatin1String("Mono");
348                     break;
349                 }
350 
351                 case 2:
352                 {
353                     data = QLatin1String("Stereo");
354                     break;
355                 }
356 
357                 case 6:
358                 {
359                     data = QLatin1String("5.1");
360                     break;
361                 }
362 
363                 case 8:
364                 {
365                     data = QLatin1String("7.1");
366                     break;
367                 }
368 
369                 case 16:
370                 {
371                     data = QLatin1String("16 Channel");
372                     break;
373                 }
374 
375                 default:
376                 {
377                     data = QLatin1String("Other");
378                     break;
379                 }
380             }
381 
382             if (!data.isEmpty())
383             {
384                 setXmpTagString("Xmp.audio.ChannelType",      data);
385                 setXmpTagString("Xmp.xmpDM.audioChannelType", data);
386             }
387 
388             setXmpTagString("Xmp.audio.Format",
389                 QString::fromUtf8(av_get_sample_fmt_name((AVSampleFormat)codec->format)));
390 
391             // See XMP Dynamic Media properties from Adobe.
392             // Audio Sample type is a limited untranslated string choice depending of amount of audio samples
393 
394             data = s_convertFFMpegFormatToXMP(codec->format);
395 
396             if (!data.isEmpty())
397             {
398                 setXmpTagString("Xmp.audio.SampleType",      data);
399                 setXmpTagString("Xmp.xmpDM.audioSampleType", data);
400             }
401 
402             // --------------
403 
404             MetaDataMap ameta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata);
405 
406             qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg audio stream metadata entries :";
407             qCDebug(DIGIKAM_METAENGINE_LOG) << ameta;
408             qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------";
409 
410             // --------------
411 
412             s_setXmpTagStringFromEntry(this,
413                                        QStringList() << QLatin1String("language"),                              // Generic.
414                                        ameta,
415                                        QStringList() << QLatin1String("Xmp.audio.TrackLang"));
416 
417             // --------------
418 
419             data = s_setXmpTagStringFromEntry(this,
420                                               QStringList() << QLatin1String("creation_time"),                  // Generic.
421                                               ameta);
422 
423             if (!data.isEmpty())
424             {
425                 QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
426                 setXmpTagString("Xmp.audio.TrackCreateDate",
427                                 QString::number(s_secondsSinceJanuary1904(dt)));
428             }
429 
430             // --------------
431 
432             s_setXmpTagStringFromEntry(this,
433                                        QStringList() << QLatin1String("handler_name"),                          // Generic.
434                                        ameta,
435                                        QStringList() << QLatin1String("Xmp.audio.HandlerDescription"));
436         }
437 
438         // -----------------------------------------
439         // Video stream parsing
440         // -----------------------------------------
441 
442         if (!vstream && (codec->codec_type == AVMEDIA_TYPE_VIDEO))
443         {
444             vstream = true;
445 
446             setXmpTagString("Xmp.video.Codec",
447                  QString::fromUtf8(cname));
448 
449             setXmpTagString("Xmp.video.CodecDescription",
450                  QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name));
451 
452             setXmpTagString("Xmp.video.Format",
453                  QString::fromUtf8(av_get_pix_fmt_name((AVPixelFormat)codec->format)));
454 
455             // Store in this tag the full description off FFMPEG video color space.
456 
457             setXmpTagString("Xmp.video.ColorMode",
458                  QString::fromUtf8(av_color_space_name((AVColorSpace)codec->color_space)));
459 
460             VIDEOCOLORMODEL cm = VIDEOCOLORMODEL_OTHER;
461 
462             switch (codec->color_space)
463             {
464                 case AVCOL_SPC_RGB:
465                 {
466                     cm = VIDEOCOLORMODEL_SRGB;
467                     break;
468                 }
469 
470                 case AVCOL_SPC_BT470BG:
471                 case AVCOL_SPC_SMPTE170M:
472                 case AVCOL_SPC_SMPTE240M:
473                 {
474                     cm = VIDEOCOLORMODEL_BT601;
475                     break;
476                 }
477 
478                 case AVCOL_SPC_BT709:
479                 {
480                     cm = VIDEOCOLORMODEL_BT709;
481                     break;
482                 }
483 
484                 case AVCOL_SPC_UNSPECIFIED:
485                 case AVCOL_SPC_RESERVED:
486                 case AVCOL_SPC_NB:
487                 {
488                     cm = VIDEOCOLORMODEL_UNKNOWN;
489                     break;
490                 }
491 
492                 default:
493                 {
494                     break;
495                 }
496             }
497 
498             // See XMP Dynamic Media properties from Adobe.
499             // Video Color Space is a limited untranslated string choice depending of video color space value.
500 
501             data = videoColorModelToString(cm);
502 
503             if (!data.isEmpty())
504             {
505                 setXmpTagString("Xmp.video.ColorSpace",      data);
506                 setXmpTagString("Xmp.xmpDM.videoColorSpace", data);
507             }
508 
509             // ----------
510 
511             QString fo;
512 
513             switch (codec->field_order)
514             {
515                 case AV_FIELD_PROGRESSIVE:
516                 {
517                     fo = QLatin1String("Progressive");
518                     break;
519                 }
520 
521                 case AV_FIELD_TT:                       // Top coded first, top displayed first
522                 case AV_FIELD_BT:                       // Bottom coded first, top displayed first
523                 {
524                     fo = QLatin1String("Upper");
525                     break;
526                 }
527 
528                 case AV_FIELD_BB:                       // Bottom coded first, bottom displayed first
529                 case AV_FIELD_TB:                       // Top coded first, bottom displayed first
530                 {
531                     fo = QLatin1String("Lower");
532                     break;
533                 }
534 
535                 default:
536                 {
537                     break;
538                 }
539             }
540 
541             if (!fo.isEmpty())
542             {
543                 setXmpTagString("Xmp.xmpDM.FieldOrder", fo);
544             }
545 
546             // ----------
547 
548             QString aspectRatio;
549             double frameRate = -1.0;
550 
551             if      (codec->sample_aspect_ratio.num != 0)    // Check if undefined by ffmpeg
552             {
553                 AVRational displayAspectRatio;
554 
555                 av_reduce(&displayAspectRatio.num, &displayAspectRatio.den,
556                           codec->width  * (int64_t)codec->sample_aspect_ratio.num,
557                           codec->height * (int64_t)codec->sample_aspect_ratio.den,
558                           1024 * 1024);
559 
560                 aspectRatio = QString::fromLatin1("%1/%2").arg(displayAspectRatio.num)
561                                                           .arg(displayAspectRatio.den);
562             }
563             else if (codec->height)
564             {
565                 aspectRatio = QString::fromLatin1("%1/%2").arg(codec->width)
566                                                           .arg(codec->height);
567             }
568 
569             if (stream->avg_frame_rate.den)
570             {
571                 frameRate = (double)stream->avg_frame_rate.num / (double)stream->avg_frame_rate.den;
572             }
573 
574             setXmpTagString("Xmp.video.Width",
575                 QString::number(codec->width));
576             setXmpTagString("Xmp.video.FrameWidth",
577                 QString::number(codec->width));
578             setXmpTagString("Xmp.video.SourceImageWidth",
579                 QString::number(codec->width));
580 
581             setXmpTagString("Xmp.video.Height",
582                 QString::number(codec->height));
583             setXmpTagString("Xmp.video.FrameHeight",
584                 QString::number(codec->height));
585             setXmpTagString("Xmp.video.SourceImageHeight",
586                 QString::number(codec->height));
587 
588             setXmpTagString("Xmp.video.FrameSize",
589                 QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height));
590             setXmpTagString("Xmp.xmpDM.videoFrameSize",
591                 QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height));
592 
593             // Backport size in Exif and Iptc
594 
595             setItemDimensions(QSize(codec->width, codec->height));
596 
597             if (!aspectRatio.isEmpty())
598             {
599                 setXmpTagString("Xmp.video.AspectRatio",           aspectRatio);
600                 setXmpTagString("Xmp.xmpDM.videoPixelAspectRatio", aspectRatio);
601             }
602 
603             if (frameRate != -1.0)
604             {
605                 setXmpTagString("Xmp.video.FrameRate", QString::number(frameRate));
606 
607                 // See XMP Dynamic Media properties from Adobe.
608                 // Video Color Space is a limited untranslated string choice depending of video frame rate.
609                 // https://documentation.apple.com/en/finalcutpro/usermanual/index.html#chapter=D%26section=4%26tasks=true
610 
611                 data = QLatin1String("Other");
612 
613                 if      (frameRate == 24.0)
614                 {
615                     data = QLatin1String("24");
616                 }
617                 else if ((frameRate == 23.98) || (frameRate == 29.97) ||
618                          (frameRate == 30.0)  || (frameRate == 59.94))
619                 {
620                     data = QLatin1String("NTSC");
621                 }
622                 else if (frameRate == 25.0 || frameRate == 50.0)
623                 {
624                     data = QLatin1String("PAL");
625                 }
626 
627                 setXmpTagString("Xmp.xmpDM.videoFrameRate", data);
628             }
629 
630             setXmpTagString("Xmp.video.BitDepth", QString::number(codec->bits_per_coded_sample));
631 
632             // See XMP Dynamic Media properties from Adobe.
633             // Video Pixel Depth is a limited untranslated string choice depending of amount of samples format.
634 
635             data = s_convertFFMpegFormatToXMP(codec->format);
636 
637             if (!data.isEmpty())
638             {
639                 setXmpTagString("Xmp.xmpDM.videoPixelDepth", data);
640             }
641 
642             // -----------------------------------------
643 
644             MetaDataMap vmeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata);
645 
646             qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg video stream metadata entries :";
647             qCDebug(DIGIKAM_METAENGINE_LOG) << vmeta;
648             qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------";
649 
650             // --------------
651 
652             data = s_setXmpTagStringFromEntry(this,
653                                               QStringList() << QLatin1String("rotate"),                         // Generic.
654                                               vmeta);
655 
656             if (!data.isEmpty())
657             {
658                 bool b               = false;
659                 int val              = data.toInt(&b);
660                 ImageOrientation ori = ORIENTATION_UNSPECIFIED;
661 
662                 if (b)
663                 {
664                     switch (val)
665                     {
666                         case 0:
667                         {
668                             ori = ORIENTATION_NORMAL;
669                             break;
670                         }
671 
672                         case 90:
673                         {
674                             ori = ORIENTATION_ROT_90;
675                             break;
676                         }
677 
678                         case 180:
679                         {
680                             ori = ORIENTATION_ROT_180;
681                             break;
682                         }
683 
684                         case 270:
685                         {
686                             ori = ORIENTATION_ROT_270;
687                             break;
688                         }
689 
690                         default:
691                         {
692                             break;
693                         }
694                     }
695 
696                     setXmpTagString("Xmp.video.Orientation", QString::number(ori));
697 
698                     // Backport orientation in Exif
699 
700                     setItemOrientation(ori);
701                 }
702             }
703 
704             // --------------
705 
706             s_setXmpTagStringFromEntry(this,
707                                        QStringList() << QLatin1String("language")                               // Generic.
708                                                      << QLatin1String("ILNG")                                   // RIFF files.
709                                                      << QLatin1String("LANG"),                                  // RIFF files.
710                                        vmeta,
711                                        QStringList() << QLatin1String("Xmp.video.Language"));
712 
713             // --------------
714 
715             data = s_setXmpTagStringFromEntry(this,
716                                               QStringList() << QLatin1String("creation_time")                   // Generic.
717                                                             << QLatin1String("_STATISTICS_WRITING_DATE_UTC"),   // MKV files.
718                                               vmeta);
719 
720             if (!data.isEmpty())
721             {
722                 QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
723                 setXmpTagString("Xmp.video.TrackCreateDate",
724                                 QString::number(s_secondsSinceJanuary1904(dt)));
725 
726                 setXmpTagString("Xmp.xmpDM.shotDate", dt.toString());
727             }
728 
729             // --------------
730 
731             s_setXmpTagStringFromEntry(this,
732                                        QStringList() << QLatin1String("handler_name"),                          // Generic.
733                                        vmeta,
734                                        QStringList() << QLatin1String("Xmp.video.HandlerDescription"));
735 
736             // --------------
737 
738             s_setXmpTagStringFromEntry(this,
739                                        QStringList() << QLatin1String("TVER")                                   // RIFF files.
740                                                      << QLatin1String("_STATISTICS_WRITING_APP"),               // MKV files.
741                                        vmeta,
742                                        QStringList() << QLatin1String("Xmp.video.SoftwareVersion"));
743         }
744 
745         // -----------------------------------------
746         // Subtitle stream parsing
747         // -----------------------------------------
748 
749         if (!sstream && (codec->codec_type == AVMEDIA_TYPE_SUBTITLE))
750         {
751             sstream = true;
752 
753             setXmpTagString("Xmp.video.SubTCodec",
754                 QString::fromUtf8(cname));
755             setXmpTagString("Xmp.video.SubTCodecInfo",
756                 QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name));
757 
758             // -----------------------------------------
759 
760             MetaDataMap smeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata);
761 
762             qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg subtitle stream metadata entries :";
763             qCDebug(DIGIKAM_METAENGINE_LOG) << smeta;
764             qCDebug(DIGIKAM_METAENGINE_LOG) << "--------------------------------------------";
765 
766             // --------------
767 
768             s_setXmpTagStringFromEntry(this,
769                                        QStringList() << QLatin1String("subtitle")                               // Generic.
770                                                      << QLatin1String("title"),                                 // Generic.
771                                        smeta,
772                                        QStringList() << QLatin1String("Xmp.video.Subtitle"));
773 
774             // --------------
775 
776             s_setXmpTagStringFromEntry(this,
777                                        QStringList() << QLatin1String("language"),                              // Generic.
778                                        smeta,
779                                        QStringList() << QLatin1String("Xmp.video.SubTLang"));
780         }
781     }
782 
783     // -----------------------------------------
784     // Root container parsing
785     // -----------------------------------------
786 
787     MetaDataMap rmeta = s_extractFFMpegMetadataEntriesFromDictionary(fmt_ctx->metadata);
788 
789     qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg root container metadata entries :";
790     qCDebug(DIGIKAM_METAENGINE_LOG) << rmeta;
791     qCDebug(DIGIKAM_METAENGINE_LOG) << "------------------------------------------";
792 
793     // ----------------------------
794 
795     s_setXmpTagStringFromEntry(this,
796                                QStringList() << QLatin1String("major_brand"),                                   // Generic.
797                                rmeta,
798                                QStringList() << QLatin1String("Xmp.video.MajorBrand"));
799 
800     // --------------
801 
802     s_setXmpTagStringFromEntry(this,
803                                QStringList() << QLatin1String("compatible_brands"),                             // Generic.
804                                rmeta,
805                                QStringList() << QLatin1String("Xmp.video.CompatibleBrands"));
806 
807     // --------------
808 
809     s_setXmpTagStringFromEntry(this,
810                                QStringList() << QLatin1String("minor_version"),                                 // Generic.
811                                rmeta,
812                                QStringList() << QLatin1String("Xmp.video.MinorVersion"));
813 
814     // --------------
815 
816     data = s_setXmpTagStringFromEntry(this,
817                                QStringList() << QLatin1String("keywords")                                       // Generic.
818                                              << QLatin1String("IMIT")                                           // RIFF files.
819                                              << QLatin1String("KEYWORDS")                                       // MKV files.
820                                              << QLatin1String("com.apple.quicktime.keywords"),                  // QT files.
821                                rmeta,
822                                QStringList() << QLatin1String("Xmp.video.InfoText"));
823 
824     if (!data.isEmpty())
825     {
826         QStringList keywords = s_keywordsSeparation(data);
827 
828         if (!keywords.isEmpty())
829         {
830             setXmpKeywords(keywords);
831             setIptcKeywords(QStringList(), keywords);
832         }
833     }
834 
835     // --------------
836 
837     data = s_setXmpTagStringFromEntry(this,
838                                QStringList() << QLatin1String("category")                                       // Generic.
839                                              << QLatin1String("ISBJ")                                           // RIFF files.
840                                              << QLatin1String("SUBJECT"),                                       // MKV files.
841                                rmeta,
842                                QStringList() << QLatin1String("Xmp.video.Subject"));
843 
844     if (!data.isEmpty())
845     {
846         QStringList categories = s_keywordsSeparation(data);
847 
848         if (!categories.isEmpty())
849         {
850             setXmpSubCategories(categories);
851             setIptcSubCategories(QStringList(), categories);
852         }
853     }
854 
855     // --------------
856 
857     s_setXmpTagStringFromEntry(this,
858                                QStringList() << QLatin1String("premiere_version")                               // Generic.
859                                              << QLatin1String("quicktime_version")                              // Generic.
860                                              << QLatin1String("ISFT")                                           // Riff files
861                                              << QLatin1String("com.apple.quicktime.software"),                  // QT files.
862                                rmeta,
863                                QStringList() << QLatin1String("Xmp.video.SoftwareVersion"));
864 
865     // --------------
866 
867     s_setXmpTagStringFromEntry(this,
868                                QStringList() << QLatin1String("firmware")                                       // Generic.
869                                              << QLatin1String("com.apple.proapps.serialno"),                    // QT files.
870                                rmeta,
871                                QStringList() << QLatin1String("Xmp.video.FirmwareVersion"));
872 
873     // --------------
874 
875     s_setXmpTagStringFromEntry(this,
876                                QStringList() << QLatin1String("composer")                                       // Generic.
877                                              << QLatin1String("COMPOSER"),                                      // MKV files
878                                rmeta,
879                                QStringList() << QLatin1String("Xmp.video.Composer")
880                                              << QLatin1String("Xmp.xmpDM.composer"));
881 
882     // --------------
883 
884     s_setXmpTagStringFromEntry(this,
885                                QStringList() << QLatin1String("com.apple.quicktime.displayname"),               // QT files.
886                                rmeta,
887                                QStringList() << QLatin1String("Xmp.video.Name"));
888 
889     // --------------
890 
891     s_setXmpTagStringFromEntry(this,
892                                QStringList() << QLatin1String("playback_requirements"),                         // Generic.
893                                rmeta,
894                                QStringList() << QLatin1String("Xmp.video.Requirements"));
895 
896     // --------------
897 
898     s_setXmpTagStringFromEntry(this,
899                                QStringList() << QLatin1String("lyrics"),                                        // Generic.
900                                rmeta,
901                                QStringList() << QLatin1String("Xmp.video.Lyrics"));
902 
903     // --------------
904 
905     s_setXmpTagStringFromEntry(this,
906                                QStringList() << QLatin1String("filename"),                                      // Generic.
907                                rmeta,
908                                QStringList() << QLatin1String("Xmp.video.FileName"));
909 
910     // --------------
911 
912     s_setXmpTagStringFromEntry(this,
913                                QStringList() << QLatin1String("disk"),                                          // Generic.
914                                rmeta,
915                                QStringList() << QLatin1String("Xmp.xmpDM.discNumber"));
916 
917     // --------------
918 
919     s_setXmpTagStringFromEntry(this,
920                                QStringList() << QLatin1String("performers"),                                    // Generic.
921                                rmeta,
922                                QStringList() << QLatin1String("Xmp.video.Performers"));
923 
924     // --------------
925 
926     s_setXmpTagStringFromEntry(this,
927                                QStringList() << QLatin1String("producer")                                       // Generic.
928                                              << QLatin1String("PRODUCER")                                       // MKV files.
929                                              << QLatin1String("com.apple.quicktime.producer"),                  // QT files.
930                                rmeta,
931                                QStringList() << QLatin1String("Xmp.video.Producer"));
932 
933     // --------------
934 
935     s_setXmpTagStringFromEntry(this,
936                                QStringList() << QLatin1String("artist")                                         // Generic.
937                                              << QLatin1String("album_artist")                                   // Generic.
938                                              << QLatin1String("original_artist")                                // Generic.
939                                              << QLatin1String("com.apple.quicktime.artist")                     // QT files.
940                                              << QLatin1String("IART")                                           // RIFF files.
941                                              << QLatin1String("ARTIST")                                         // MKV files.
942                                              << QLatin1String("author")                                         // Generic.
943                                              << QLatin1String("com.apple.quicktime.author"),                    // QT files.
944                                rmeta,
945                                QStringList() << QLatin1String("Xmp.video.Artist")
946                                              << QLatin1String("Xmp.xmpDM.artist"));
947 
948     // --------------
949 
950     s_setXmpTagStringFromEntry(this,
951                                QStringList() << QLatin1String("director")                                       // Generic.
952                                              << QLatin1String("DIRC")                                           // RIFF files.
953                                              << QLatin1String("DIRECTOR")                                       // MKV files.
954                                              << QLatin1String("com.apple.quicktime.director"),                  // QT files.
955                                rmeta,
956                                QStringList() << QLatin1String("Xmp.video.Director")
957                                              << QLatin1String("Xmp.xmpDM.director"));
958 
959     // --------------
960 
961     s_setXmpTagStringFromEntry(this,
962                                QStringList() << QLatin1String("media_type")                                     // Generic.
963                                              << QLatin1String("IMED")                                           // RIFF files.
964                                              << QLatin1String("ORIGINAL_MEDIA_TYPE"),                           // MKV files.
965                                rmeta,
966                                QStringList() << QLatin1String("Xmp.video.Medium"));
967 
968     // --------------
969 
970     s_setXmpTagStringFromEntry(this,
971                                QStringList() << QLatin1String("grouping"),                                      // Generic.
972                                rmeta,
973                                QStringList() << QLatin1String("Xmp.video.Grouping"));
974 
975     // --------------
976 
977     s_setXmpTagStringFromEntry(this,
978                                QStringList() << QLatin1String("BPS"),                                           // MKV files
979                                rmeta,
980                                QStringList() << QLatin1String("Xmp.video.MaxBitRate"));
981 
982     // --------------
983 
984     s_setXmpTagStringFromEntry(this,
985                                QStringList() << QLatin1String("ISRC"),                                          // MKV files
986                                rmeta,
987                                QStringList() << QLatin1String("Xmp.video.ISRCCode"));
988 
989     // --------------
990 
991     s_setXmpTagStringFromEntry(this,
992                                QStringList() << QLatin1String("CONTENT_TYPE"),                                  // MKV files
993                                rmeta,
994                                QStringList() << QLatin1String("Xmp.video.ExtendedContentDescription"));
995 
996     // --------------
997 
998     s_setXmpTagStringFromEntry(this,
999                                QStringList() << QLatin1String("FPS"),                                           // MKV files.
1000                                rmeta,
1001                                QStringList() << QLatin1String("Xmp.video.videoFrameRate")
1002                                              << QLatin1String("Xmp.xmpDM.FrameRate"));
1003 
1004     // --------------
1005 
1006     s_setXmpTagStringFromEntry(this,
1007                                QStringList() << QLatin1String("encoder")                                        // Generic.
1008                                              << QLatin1String("ENCODER"),                                       // MKV files.
1009                                rmeta,
1010                                QStringList() << QLatin1String("Xmp.video.Encoder"));
1011 
1012     // --------------
1013 
1014     s_setXmpTagStringFromEntry(this,
1015                                QStringList() << QLatin1String("com.apple.proapps.clipID"),                      // QT files.
1016                                rmeta,
1017                                QStringList() << QLatin1String("Xmp.video.FileID"));
1018 
1019     // --------------
1020 
1021     s_setXmpTagStringFromEntry(this,
1022                                QStringList() << QLatin1String("original_source")                                // Generic.
1023                                              << QLatin1String("ISRC")                                           // Riff files
1024                                              << QLatin1String("com.apple.proapps.cameraName"),                  // QT files.
1025                                rmeta,
1026                                QStringList() << QLatin1String("Xmp.video.Source"));
1027 
1028     // --------------
1029 
1030     s_setXmpTagStringFromEntry(this,
1031                                QStringList() << QLatin1String("original_format")                                // Generic.
1032                                              << QLatin1String("com.apple.proapps.originalFormat"),              // QT files.
1033                                rmeta,
1034                                QStringList() << QLatin1String("Xmp.video.Format"));
1035 
1036     // --------------
1037 
1038     data = s_setXmpTagStringFromEntry(this,
1039                                QStringList() << QLatin1String("rating")                                         // Generic.
1040                                              << QLatin1String("IRTD")                                           // RIFF files.
1041                                              << QLatin1String("RATE")                                           // RIFF files.
1042                                              << QLatin1String("RATING")                                         // MKV files.
1043                                              << QLatin1String("com.apple.quicktime.rating.user"),               // QT files.
1044                                rmeta,
1045                                QStringList() << QLatin1String("Xmp.video.Rating")
1046                                              << QLatin1String("Xmp.video.Rate"));
1047 
1048     if (!data.isEmpty())
1049     {
1050         // Backport rating in Exif and Iptc
1051 
1052         bool b     = false;
1053         int rating = data.toInt(&b);
1054 
1055         if (b)
1056         {
1057             setItemRating(rating);
1058         }
1059     }
1060 
1061     // --------------
1062 
1063     s_setXmpTagStringFromEntry(this,
1064                                QStringList() << QLatin1String("make")                                           // Generic.
1065                                              << QLatin1String("com.apple.quicktime.make"),                      // QT files.
1066                                rmeta,
1067                                QStringList() << QLatin1String("Xmp.video.Make"));
1068 
1069     // --------------
1070 
1071     s_setXmpTagStringFromEntry(this,
1072                                QStringList() << QLatin1String("model")                                          // Generic.
1073                                              << QLatin1String("com.apple.quicktime.model"),                     // QT files.
1074                                rmeta,
1075                                QStringList() << QLatin1String("Xmp.video.Model")
1076                                              << QLatin1String("Xmp.xmpDM.cameraModel"));
1077 
1078     // --------------
1079 
1080     s_setXmpTagStringFromEntry(this,
1081                                QStringList() << QLatin1String("URL")                                            // Generic.
1082                                              << QLatin1String("TURL"),                                          // RIFF files.
1083                                rmeta,
1084                                QStringList() << QLatin1String("Xmp.video.URL"));
1085 
1086 
1087     // --------------
1088 
1089     s_setXmpTagStringFromEntry(this,
1090                                QStringList() << QLatin1String("title")                                          // Generic.
1091                                              << QLatin1String("INAM")                                           // RIFF files.
1092                                              << QLatin1String("TITL")                                           // RIFF files.
1093                                              << QLatin1String("TITLE")                                          // MKV files.
1094                                              << QLatin1String("com.apple.quicktime.title"),                     // QT files.
1095                                rmeta,
1096                                QStringList() << QLatin1String("Xmp.video.Title")
1097                                              << QLatin1String("Xmp.xmpDM.shotName"));
1098 
1099     // --------------
1100 
1101     s_setXmpTagStringFromEntry(this,
1102                                QStringList() << QLatin1String("copyright")                                      // Generic.
1103                                              << QLatin1String("ICOP")                                           // RIFF files.
1104                                              << QLatin1String("COPYRIGHT")                                      // MKV files.
1105                                              << QLatin1String("com.apple.quicktime.copyright"),                 // QT files.
1106                                rmeta,
1107                                QStringList() << QLatin1String("Xmp.video.Copyright")
1108                                              << QLatin1String("Xmp.xmpDM.copyright"));
1109 
1110     // --------------
1111 
1112     data = s_setXmpTagStringFromEntry(this,
1113                                QStringList() << QLatin1String("comment")                                        // Generic.
1114                                              << QLatin1String("description")                                    // Generic.
1115                                              << QLatin1String("CMNT")                                           // Riff Files.
1116                                              << QLatin1String("COMN")                                           // Riff Files.
1117                                              << QLatin1String("ICMT")                                           // Riff Files.
1118                                              << QLatin1String("COMMENT")                                        // MKV Files.
1119                                              << QLatin1String("DESCRIPTION")                                    // MKV Files.
1120                                              << QLatin1String("com.apple.quicktime.description"),               // QT files.
1121                                rmeta,
1122                                QStringList() << QLatin1String("Xmp.video.Comment")
1123                                              << QLatin1String("Xmp.xmpDM.logComment"));
1124 
1125     if (!data.isEmpty())
1126     {
1127         // Backport comment in Exif and Iptc
1128 
1129         CaptionsMap capMap;
1130         MetaEngine::AltLangMap comMap;
1131         comMap.insert(QLatin1String("x-default"), data);
1132         capMap.setData(comMap, MetaEngine::AltLangMap(), QString(), MetaEngine::AltLangMap());
1133 
1134         setItemComments(capMap);
1135     }
1136 
1137     // --------------
1138 
1139     s_setXmpTagStringFromEntry(this,
1140                                QStringList() << QLatin1String("synopsis")                                       // Generic.
1141                                              << QLatin1String("SUMMARY")                                        // MKV files.
1142                                              << QLatin1String("SYNOPSIS"),                                      // MKV files.
1143                                rmeta,
1144                                QStringList() << QLatin1String("Xmp.video.Information"));
1145 
1146     // --------------
1147 
1148     s_setXmpTagStringFromEntry(this,
1149                                QStringList() << QLatin1String("lyrics")                                         // Generic.
1150                                              << QLatin1String("LYRICS"),                                        // MKV files.
1151                                rmeta,
1152                                QStringList() << QLatin1String("Xmp.xmpDM.lyrics"));
1153 
1154     // --------------
1155 
1156     for (int i = 1 ; i <= 9 ; ++i)
1157     {
1158         s_setXmpTagStringFromEntry(this,
1159                                QStringList() << QString::fromLatin1("IAS%1").arg(i),                            // RIFF files.
1160                                rmeta,
1161                                QStringList() << QString::fromLatin1("Xmp.video.Edit%1").arg(i));
1162     }
1163 
1164     // --------------
1165 
1166     s_setXmpTagStringFromEntry(this,
1167                                QStringList() << QLatin1String("encoded_by")                                     // Generic.
1168                                              << QLatin1String("CODE")                                           // RIFF files.
1169                                              << QLatin1String("IECN")                                           // RIFF files.
1170                                              << QLatin1String("ENCODED_BY"),                                    // MKV files.
1171                                rmeta,
1172                                QStringList() << QLatin1String("Xmp.video.EncodedBy"));
1173 
1174     // --------------
1175 
1176     s_setXmpTagStringFromEntry(this,
1177                                QStringList() << QLatin1String("DISP"),                                          // RIFF files.
1178                                rmeta,
1179                                QStringList() << QLatin1String("Xmp.video.SchemeTitle"));
1180 
1181     // --------------
1182 
1183     s_setXmpTagStringFromEntry(this,
1184                                QStringList() << QLatin1String("AGES")                                           // RIFF files.
1185                                              << QLatin1String("ICRA")                                           // MKV files.
1186                                              << QLatin1String("LAW_RATING"),                                    // MKV files.
1187                                rmeta,
1188                                QStringList() << QLatin1String("Xmp.video.Rated"));
1189 
1190     // --------------
1191 
1192     s_setXmpTagStringFromEntry(this,
1193                                QStringList() << QLatin1String("IBSU"),                                          // RIFF files.
1194                                rmeta,
1195                                QStringList() << QLatin1String("Xmp.video.BaseURL"));
1196 
1197     // --------------
1198 
1199     s_setXmpTagStringFromEntry(this,
1200                                QStringList() << QLatin1String("ICAS"),                                          // RIFF files.
1201                                rmeta,
1202                                QStringList() << QLatin1String("Xmp.video.DefaultStream"));
1203 
1204     // --------------
1205 
1206     s_setXmpTagStringFromEntry(this,
1207                                QStringList() << QLatin1String("ICDS"),                                          // RIFF files.
1208                                rmeta,
1209                                QStringList() << QLatin1String("Xmp.video.CostumeDesigner"));
1210 
1211     // --------------
1212 
1213     s_setXmpTagStringFromEntry(this,
1214                                QStringList() << QLatin1String("ICMS"),                                          // RIFF files.
1215                                rmeta,
1216                                QStringList() << QLatin1String("Xmp.video.Commissioned"));
1217 
1218     // --------------
1219 
1220     s_setXmpTagStringFromEntry(this,
1221                                QStringList() << QLatin1String("ICNM"),                                          // RIFF files.
1222                                rmeta,
1223                                QStringList() << QLatin1String("Xmp.video.Cinematographer"));
1224 
1225     // --------------
1226 
1227     s_setXmpTagStringFromEntry(this,
1228                                QStringList() << QLatin1String("ICNT"),                                          // RIFF files.
1229                                rmeta,
1230                                QStringList() << QLatin1String("Xmp.video.Country"));
1231 
1232     // --------------
1233 
1234     s_setXmpTagStringFromEntry(this,
1235                                QStringList() << QLatin1String("IARL"),                                          // RIFF files.
1236                                rmeta,
1237                                QStringList() << QLatin1String("Xmp.video.ArchivalLocation"));
1238 
1239     // --------------
1240 
1241     s_setXmpTagStringFromEntry(this,
1242                                QStringList() << QLatin1String("ICRP"),                                          // RIFF files.
1243                                rmeta,
1244                                QStringList() << QLatin1String("Xmp.video.Cropped"));
1245 
1246     // --------------
1247 
1248     s_setXmpTagStringFromEntry(this,
1249                                QStringList() << QLatin1String("IDIM"),                                          // RIFF files.
1250                                rmeta,
1251                                QStringList() << QLatin1String("Xmp.video.Dimensions"));
1252 
1253     // --------------
1254 
1255     s_setXmpTagStringFromEntry(this,
1256                                QStringList() << QLatin1String("IDPI"),                                          // RIFF files.
1257                                rmeta,
1258                                QStringList() << QLatin1String("Xmp.video.DotsPerInch"));
1259 
1260     // --------------
1261 
1262     s_setXmpTagStringFromEntry(this,
1263                                QStringList() << QLatin1String("IDST")                                           // RIFF files.
1264                                              << QLatin1String("DISTRIBUTED_BY"),                                // MKV files.
1265                                rmeta,
1266                                QStringList() << QLatin1String("Xmp.video.DistributedBy"));
1267 
1268     // --------------
1269 
1270     s_setXmpTagStringFromEntry(this,
1271                                QStringList() << QLatin1String("IEDT"),                                          // RIFF files.
1272                                rmeta,
1273                                QStringList() << QLatin1String("Xmp.video.EditedBy"));
1274 
1275     // --------------
1276 
1277     s_setXmpTagStringFromEntry(this,
1278                                QStringList() << QLatin1String("IENG"),                                          // RIFF files.
1279                                rmeta,
1280                                QStringList() << QLatin1String("Xmp.video.Engineer")
1281                                              << QLatin1String("Xmp.xmpDM.engineer"));
1282 
1283     // --------------
1284 
1285     s_setXmpTagStringFromEntry(this,
1286                                QStringList() << QLatin1String("IKEY"),                                          // RIFF files.
1287                                rmeta,
1288                                QStringList() << QLatin1String("Xmp.video.PerformerKeywords"));
1289 
1290     // --------------
1291 
1292     s_setXmpTagStringFromEntry(this,
1293                                QStringList() << QLatin1String("ILGT"),                                          // RIFF files.
1294                                rmeta,
1295                                QStringList() << QLatin1String("Xmp.video.Lightness"));
1296 
1297     // --------------
1298 
1299     s_setXmpTagStringFromEntry(this,
1300                                QStringList() << QLatin1String("ILGU"),                                          // RIFF files.
1301                                rmeta,
1302                                QStringList() << QLatin1String("Xmp.video.LogoURL"));
1303 
1304     // --------------
1305 
1306     s_setXmpTagStringFromEntry(this,
1307                                QStringList() << QLatin1String("ILIU"),                                          // RIFF files.
1308                                rmeta,
1309                                QStringList() << QLatin1String("Xmp.video.LogoIconURL"));
1310 
1311     // --------------
1312 
1313     s_setXmpTagStringFromEntry(this,
1314                                QStringList() << QLatin1String("IMBI"),                                          // RIFF files.
1315                                rmeta,
1316                                QStringList() << QLatin1String("Xmp.video.InfoBannerImage"));
1317 
1318     // --------------
1319 
1320     s_setXmpTagStringFromEntry(this,
1321                                QStringList() << QLatin1String("IMBU"),                                          // RIFF files.
1322                                rmeta,
1323                                QStringList() << QLatin1String("Xmp.video.InfoBannerURL"));
1324 
1325     // --------------
1326 
1327     s_setXmpTagStringFromEntry(this,
1328                                QStringList() << QLatin1String("IMIU"),                                          // RIFF files.
1329                                rmeta,
1330                                QStringList() << QLatin1String("Xmp.video.InfoURL"));
1331 
1332     // --------------
1333 
1334     s_setXmpTagStringFromEntry(this,
1335                                QStringList() << QLatin1String("IMUS"),                                          // RIFF files.
1336                                rmeta,
1337                                QStringList() << QLatin1String("Xmp.video.MusicBy"));
1338 
1339     // --------------
1340 
1341     s_setXmpTagStringFromEntry(this,
1342                                QStringList() << QLatin1String("IPDS"),                                          // RIFF files.
1343                                rmeta,
1344                                QStringList() << QLatin1String("Xmp.video.ProductionDesigner"));
1345 
1346     // --------------
1347 
1348     s_setXmpTagStringFromEntry(this,
1349                                QStringList() << QLatin1String("IPLT"),                                          // RIFF files.
1350                                rmeta,
1351                                QStringList() << QLatin1String("Xmp.video.NumOfColors"));
1352 
1353     // --------------
1354 
1355     s_setXmpTagStringFromEntry(this,
1356                                QStringList() << QLatin1String("IPRD"),                                          // RIFF files.
1357                                rmeta,
1358                                QStringList() << QLatin1String("Xmp.video.Product"));
1359 
1360     // --------------
1361 
1362     s_setXmpTagStringFromEntry(this,
1363                                QStringList() << QLatin1String("IPRO"),                                          // RIFF files.
1364                                rmeta,
1365                                QStringList() << QLatin1String("Xmp.video.ProducedBy"));
1366 
1367     // --------------
1368 
1369     s_setXmpTagStringFromEntry(this,
1370                                QStringList() << QLatin1String("IRIP"),                                          // RIFF files.
1371                                rmeta,
1372                                QStringList() << QLatin1String("Xmp.video.RippedBy"));
1373 
1374     // --------------
1375 
1376     s_setXmpTagStringFromEntry(this,
1377                                QStringList() << QLatin1String("ISGN"),                                          // RIFF files.
1378                                rmeta,
1379                                QStringList() << QLatin1String("Xmp.video.SecondaryGenre"));
1380 
1381     // --------------
1382 
1383     s_setXmpTagStringFromEntry(this,
1384                                QStringList() << QLatin1String("ISHP"),                                          // RIFF files.
1385                                rmeta,
1386                                QStringList() << QLatin1String("Xmp.video.Sharpness"));
1387 
1388     // --------------
1389 
1390     s_setXmpTagStringFromEntry(this,
1391                                QStringList() << QLatin1String("ISRF"),                                          // RIFF files.
1392                                rmeta,
1393                                QStringList() << QLatin1String("Xmp.video.SourceForm"));
1394 
1395     // --------------
1396 
1397     s_setXmpTagStringFromEntry(this,
1398                                QStringList() << QLatin1String("ISTD"),                                          // RIFF files.
1399                                rmeta,
1400                                QStringList() << QLatin1String("Xmp.video.ProductionStudio"));
1401 
1402     // --------------
1403 
1404     s_setXmpTagStringFromEntry(this,
1405                                QStringList() << QLatin1String("ISTR")                                           // RIFF files.
1406                                              << QLatin1String("STAR"),                                          // RIFF files.
1407                                rmeta,
1408                                QStringList() << QLatin1String("Xmp.video.Starring"));
1409 
1410     // --------------
1411 
1412     s_setXmpTagStringFromEntry(this,
1413                                QStringList() << QLatin1String("ITCH"),                                          // RIFF files.
1414                                rmeta,
1415                                QStringList() << QLatin1String("Xmp.video.Technician"));
1416 
1417     // --------------
1418 
1419     s_setXmpTagStringFromEntry(this,
1420                                QStringList() << QLatin1String("IWMU"),                                          // RIFF files.
1421                                rmeta,
1422                                QStringList() << QLatin1String("Xmp.video.WatermarkURL"));
1423 
1424     // --------------
1425 
1426     s_setXmpTagStringFromEntry(this,
1427                                QStringList() << QLatin1String("IWRI"),                                          // RIFF files.
1428                                rmeta,
1429                                QStringList() << QLatin1String("Xmp.video.WrittenBy"));
1430 
1431     // --------------
1432 
1433     s_setXmpTagStringFromEntry(this,
1434                                QStringList() << QLatin1String("PRT1"),                                          // RIFF files.
1435                                rmeta,
1436                                QStringList() << QLatin1String("Xmp.video.Part"));
1437 
1438     // --------------
1439 
1440     s_setXmpTagStringFromEntry(this,
1441                                QStringList() << QLatin1String("PRT2"),                                          // RIFF files.
1442                                rmeta,
1443                                QStringList() << QLatin1String("Xmp.video.NumOfParts"));
1444 
1445     // --------------
1446 
1447     s_setXmpTagStringFromEntry(this,
1448                                QStringList() << QLatin1String("STAT"),                                          // RIFF files.
1449                                rmeta,
1450                                QStringList() << QLatin1String("Xmp.video.Statistics"));
1451 
1452     // --------------
1453 
1454     s_setXmpTagStringFromEntry(this,
1455                                QStringList() << QLatin1String("TAPE"),                                          // RIFF files.
1456                                rmeta,
1457                                QStringList() << QLatin1String("Xmp.video.TapeName"));
1458 
1459     // --------------
1460 
1461     s_setXmpTagStringFromEntry(this,
1462                                QStringList() << QLatin1String("TCDO"),                                          // RIFF files.
1463                                rmeta,
1464                                QStringList() << QLatin1String("Xmp.video.EndTimecode"));
1465 
1466     // --------------
1467 
1468     s_setXmpTagStringFromEntry(this,
1469                                QStringList() << QLatin1String("TCOD"),                                          // RIFF files.
1470                                rmeta,
1471                                QStringList() << QLatin1String("Xmp.video.StartTimecode"));
1472 
1473     // --------------
1474 
1475     s_setXmpTagStringFromEntry(this,
1476                                QStringList() << QLatin1String("TLEN"),                                          // RIFF files.
1477                                rmeta,
1478                                QStringList() << QLatin1String("Xmp.video.Length"));
1479 
1480     // --------------
1481 
1482     s_setXmpTagStringFromEntry(this,
1483                                QStringList() << QLatin1String("TORG"),                                          // RIFF files.
1484                                rmeta,
1485                                QStringList() << QLatin1String("Xmp.video.Organization"));
1486 
1487     // --------------
1488 
1489     s_setXmpTagStringFromEntry(this,
1490                                QStringList() << QLatin1String("VMAJ"),                                          // RIFF files.
1491                                rmeta,
1492                                QStringList() << QLatin1String("Xmp.video.VegasVersionMajor"));
1493 
1494     // --------------
1495 
1496     s_setXmpTagStringFromEntry(this,
1497                                QStringList() << QLatin1String("VMIN"),                                          // RIFF files.
1498                                rmeta,
1499                                QStringList() << QLatin1String("Xmp.video.VegasVersionMinor"));
1500 
1501     // --------------
1502 
1503     s_setXmpTagStringFromEntry(this,
1504                                QStringList() << QLatin1String("LOCA"),                                          // RIFF files.
1505                                rmeta,
1506                                QStringList() << QLatin1String("Xmp.video.LocationInfo")
1507                                              << QLatin1String("Xmp.xmpDM.shotLocation"));
1508 
1509     // --------------
1510 
1511     s_setXmpTagStringFromEntry(this,
1512                                QStringList() << QLatin1String("album")                                          // Generic.
1513                                              << QLatin1String("com.apple.quicktime.album"),                     // QT files.
1514                                rmeta,
1515                                QStringList() << QLatin1String("Xmp.video.Album")
1516                                              << QLatin1String("Xmp.xmpDM.album"));
1517 
1518     // --------------
1519 
1520     s_setXmpTagStringFromEntry(this,
1521                                QStringList() << QLatin1String("genre")                                          // Generic.
1522                                              << QLatin1String("GENR")                                           // RIFF files.
1523                                              << QLatin1String("IGNR")                                           // RIFF files.
1524                                              << QLatin1String("GENRE")                                          // MKV files.
1525                                              << QLatin1String("com.apple.quicktime.genre"),                     // QT files.
1526                                rmeta,
1527                                QStringList() << QLatin1String("Xmp.video.Genre")
1528                                              << QLatin1String("Xmp.xmpDM.genre"));
1529 
1530     // --------------
1531 
1532     s_setXmpTagStringFromEntry(this,
1533                                QStringList() << QLatin1String("track")                                          // Generic.
1534                                              << QLatin1String("TRCK"),                                          // RIFF files.
1535                                rmeta,
1536                                QStringList() << QLatin1String("Xmp.video.TrackNumber")
1537                                              << QLatin1String("Xmp.xmpDM.trackNumber"));
1538 
1539     // --------------
1540 
1541     s_setXmpTagStringFromEntry(this,
1542                                QStringList() << QLatin1String("year")                                           // Generic.
1543                                              << QLatin1String("YEAR")                                           // RIFF files.
1544                                              << QLatin1String("com.apple.quicktime.year"),
1545                                rmeta,
1546                                QStringList() << QLatin1String("Xmp.video.Year"));
1547 
1548     // --------------
1549 
1550     s_setXmpTagStringFromEntry(this,
1551                                QStringList() << QLatin1String("ICRD")                                           // Riff files
1552                                              << QLatin1String("DATE_DIGITIZED"),                                // MKV files
1553                                rmeta,
1554                                QStringList() << QLatin1String("Xmp.video.DateTimeDigitized"));
1555 
1556     // --------------
1557 
1558     QStringList videoDateTimeOriginal;
1559     videoDateTimeOriginal                    << QLatin1String("creation_time")                                  // Generic.
1560                                              << QLatin1String("DTIM")                                           // RIFF files.
1561                                              << QLatin1String("DATE_RECORDED")                                  // MKV files.
1562                                              << QLatin1String("com.apple.quicktime.creationdate");              // QT files.
1563 
1564     if (rmeta.contains(QLatin1String("creation_time")))
1565     {
1566         if      (rmeta.contains(QLatin1String("com.apple.quicktime.creationdate")))
1567         {
1568             videoDateTimeOriginal.prepend(videoDateTimeOriginal.takeLast());
1569         }
1570         else if (!rmeta.contains(QLatin1String("com.android.version")))
1571         {
1572             if (rmeta[QLatin1String("creation_time")].endsWith(QLatin1Char('Z')))
1573             {
1574                 rmeta[QLatin1String("creation_time")].chop(1);
1575             }
1576         }
1577     }
1578 
1579     data = s_setXmpTagStringFromEntry(this,
1580                                       videoDateTimeOriginal,
1581                                       rmeta,
1582                                       QStringList() << QLatin1String("Xmp.video.DateTimeOriginal"));
1583 
1584     if (!data.isEmpty())
1585     {
1586         // Backport date in Exif and Iptc.
1587 
1588         QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
1589         setImageDateTime(dt, true);
1590     }
1591 
1592     // --------------
1593 
1594     s_setXmpTagStringFromEntry(this,
1595                                QStringList() << QLatin1String("edit_date"),                                     // Generic.
1596                                rmeta,
1597                                QStringList() << QLatin1String("Xmp.video.ModificationDate")
1598                                              << QLatin1String("Xmp.xmpDM.videoModDate"));
1599 
1600     // --------------
1601 
1602     data = s_setXmpTagStringFromEntry(this,
1603                                       QStringList() << QLatin1String("date")                                    // Generic.
1604                                                     << QLatin1String("DATE_RELEASED"),                          // MKV files.
1605                                       rmeta);
1606 
1607     if (!data.isEmpty())
1608     {
1609         QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime();
1610         setXmpTagString("Xmp.video.MediaCreateDate",
1611                         QString::number(s_secondsSinceJanuary1904(dt)));
1612     }
1613 
1614     // --------------
1615 
1616     // GPS info as string. ex: "+44.8511-000.6229/"
1617     // Defined in ISO 6709:2008.
1618     // Notes: altitude can be passed as 3rd values.
1619     //        each value is separated from others by '-' or '+'.
1620     //        '/' is always the terminaison character.
1621 
1622     data = s_setXmpTagStringFromEntry(this,
1623                                       QStringList() << QLatin1String("location")                                // Generic.
1624                                                     << QLatin1String("RECORDING_LOCATION")                      // MKV files.
1625                                                     << QLatin1String("com.apple.quicktime.location.ISO6709"),   // QT files.
1626                                       rmeta,
1627                                       QStringList() << QLatin1String("Xmp.video.GPSCoordinates")
1628                                                     << QLatin1String("Xmp.xmpDM.shotLocation"));
1629 
1630     if (!data.isEmpty())
1631     {
1632         // Backport location to Exif.
1633 
1634         QList<int> digits;
1635 
1636         for (int i = 0 ; i < data.length() ; ++i)
1637         {
1638             QChar c = data[i];
1639 
1640             if ((c == QLatin1Char('+')) || (c == QLatin1Char('-')) || (c == QLatin1Char('/')))
1641             {
1642                 digits << i;
1643             }
1644         }
1645 
1646         QString coord;
1647         double lattitude = 0.0;
1648         double longitude = 0.0;
1649         double altitude  = 0.0;
1650         bool b1          = false;
1651         bool b2          = false;
1652         bool b3          = false;
1653 
1654         if (digits.size() > 1)
1655         {
1656             coord     = data.mid(digits[0], digits[1] - digits[0]);
1657             lattitude = coord.toDouble(&b1);
1658         }
1659 
1660         if (digits.size() > 2)
1661         {
1662             coord     = data.mid(digits[1], digits[2] - digits[1]);
1663             longitude = coord.toDouble(&b2);
1664         }
1665 
1666         if (digits.size() > 3)
1667         {
1668             coord    = data.mid(digits[2], digits[3] - digits[2]);
1669             altitude = coord.toDouble(&b3);
1670         }
1671 
1672         if (b1 && b2)
1673         {
1674             if (b3)
1675             {
1676                 // All GPS values are available.
1677 
1678                 setGPSInfo(altitude, lattitude, longitude);
1679 
1680                 setXmpTagString("Xmp.video.GPSAltitude",
1681                                 getXmpTagString("Xmp.exif.GPSAltitude"));
1682                 setXmpTagString("Xmp.exif.GPSAltitude",
1683                                 getXmpTagString("Xmp.exif.GPSAltitude"));
1684             }
1685             else
1686             {
1687                 // No altitude available.
1688 
1689                 double* alt = nullptr;
1690                 setGPSInfo(alt, lattitude, longitude);
1691             }
1692 
1693             setXmpTagString("Xmp.video.GPSLatitude",
1694                             getXmpTagString("Xmp.exif.GPSLatitude"));
1695             setXmpTagString("Xmp.video.GPSLongitude",
1696                             getXmpTagString("Xmp.exif.GPSLongitude"));
1697             setXmpTagString("Xmp.video.GPSMapDatum",
1698                             getXmpTagString("Xmp.exif.GPSMapDatum"));
1699             setXmpTagString("Xmp.video.GPSVersionID",
1700                             getXmpTagString("Xmp.exif.GPSVersionID"));
1701 
1702             setXmpTagString("Xmp.exif.GPSLatitude",
1703                             getXmpTagString("Xmp.exif.GPSLatitude"));
1704             setXmpTagString("Xmp.exif.GPSLongitude",
1705                             getXmpTagString("Xmp.exif.GPSLongitude"));
1706             setXmpTagString("Xmp.exif.GPSMapDatum",
1707                             getXmpTagString("Xmp.exif.GPSMapDatum"));
1708             setXmpTagString("Xmp.exif.GPSVersionID",
1709                             getXmpTagString("Xmp.exif.GPSVersionID"));
1710         }
1711     }
1712 
1713     avformat_close_input(&fmt_ctx);
1714 
1715     QFileInfo fi(filePath);
1716 
1717     if (getXmpTagString("Xmp.video.FileName").isNull())
1718     {
1719         setXmpTagString("Xmp.video.FileName", fi.fileName());
1720     }
1721 
1722     if (getXmpTagString("Xmp.video.FileSize").isNull())
1723     {
1724         setXmpTagString("Xmp.video.FileSize", QString::number(fi.size() / (1024*1024)));
1725     }
1726 
1727     if (getXmpTagString("Xmp.video.FileType").isNull())
1728     {
1729         setXmpTagString("Xmp.video.FileType", fi.suffix());
1730     }
1731 
1732     if (getXmpTagString("Xmp.video.MimeType").isNull())
1733     {
1734         setXmpTagString("Xmp.video.MimeType", QMimeDatabase().mimeTypeForFile(filePath).name());
1735     }
1736 
1737     return true;
1738 
1739 #else
1740 
1741     Q_UNUSED(filePath);
1742 
1743     return false;
1744 
1745 #endif
1746 
1747 }
1748 
videoColorModelToString(VIDEOCOLORMODEL videoColorModel)1749 QString DMetadata::videoColorModelToString(VIDEOCOLORMODEL videoColorModel)
1750 {
1751     QString cs;
1752 
1753     switch (videoColorModel)
1754     {
1755         case VIDEOCOLORMODEL_SRGB:
1756         {
1757             cs = QLatin1String("sRGB");
1758             break;
1759         }
1760 
1761         case VIDEOCOLORMODEL_BT601:
1762         {
1763             cs = QLatin1String("CCIR-601");
1764             break;
1765         }
1766 
1767         case VIDEOCOLORMODEL_BT709:
1768         {
1769             cs = QLatin1String("CCIR-709");
1770             break;
1771         }
1772 
1773         case VIDEOCOLORMODEL_OTHER:
1774         {
1775             cs = QLatin1String("Other");
1776             break;
1777         }
1778 
1779         default: // VIDEOCOLORMODEL_UNKNOWN
1780         {
1781             break;
1782         }
1783     }
1784 
1785     return cs;
1786 }
1787 
getVideoInformation() const1788 VideoInfoContainer DMetadata::getVideoInformation() const
1789 {
1790     VideoInfoContainer videoInfo;
1791 
1792     if (hasXmp())
1793     {
1794         if (videoInfo.aspectRatio.isEmpty())
1795         {
1796             videoInfo.aspectRatio = getMetadataField(MetadataInfo::AspectRatio).toString();
1797         }
1798 
1799         if (videoInfo.audioBitRate.isEmpty())
1800         {
1801             videoInfo.audioBitRate = getXmpTagString("Xmp.audio.SampleRate");
1802         }
1803 
1804         if (videoInfo.audioChannelType.isEmpty())
1805         {
1806             videoInfo.audioChannelType = getXmpTagString("Xmp.audio.ChannelType");
1807         }
1808 
1809         if (videoInfo.audioCodec.isEmpty())
1810         {
1811             videoInfo.audioCodec = getXmpTagString("Xmp.audio.Codec");
1812         }
1813 
1814         if (videoInfo.duration.isEmpty())
1815         {
1816             videoInfo.duration = getXmpTagString("Xmp.video.duration");
1817         }
1818 
1819         if (videoInfo.frameRate.isEmpty())
1820         {
1821             videoInfo.frameRate = getXmpTagString("Xmp.video.FrameRate");
1822         }
1823 
1824         if (videoInfo.videoCodec.isEmpty())
1825         {
1826             videoInfo.videoCodec = getXmpTagString("Xmp.video.Codec");
1827         }
1828     }
1829 
1830     return videoInfo;
1831 }
1832 
1833 } // namespace Digikam
1834