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