exiftool_vendored 12.17.1 → 12.32.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +225 -1
  3. data/bin/MANIFEST +23 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +45 -43
  7. data/bin/arg_files/xmp2exif.args +2 -1
  8. data/bin/config_files/acdsee.config +193 -6
  9. data/bin/config_files/convert_regions.config +25 -14
  10. data/bin/config_files/cuepointlist.config +70 -0
  11. data/bin/config_files/example.config +2 -9
  12. data/bin/exiftool +142 -87
  13. data/bin/fmt_files/gpx.fmt +2 -2
  14. data/bin/fmt_files/gpx_wpt.fmt +2 -2
  15. data/bin/fmt_files/kml.fmt +1 -1
  16. data/bin/fmt_files/kml_track.fmt +1 -1
  17. data/bin/lib/Image/ExifTool/Apple.pm +3 -2
  18. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +30 -12
  19. data/bin/lib/Image/ExifTool/CBOR.pm +277 -0
  20. data/bin/lib/Image/ExifTool/Canon.pm +49 -18
  21. data/bin/lib/Image/ExifTool/DJI.pm +6 -6
  22. data/bin/lib/Image/ExifTool/DPX.pm +13 -2
  23. data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
  24. data/bin/lib/Image/ExifTool/Exif.pm +28 -11
  25. data/bin/lib/Image/ExifTool/FITS.pm +13 -2
  26. data/bin/lib/Image/ExifTool/FlashPix.pm +35 -10
  27. data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
  28. data/bin/lib/Image/ExifTool/GPS.pm +22 -11
  29. data/bin/lib/Image/ExifTool/Geotag.pm +13 -2
  30. data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
  31. data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
  32. data/bin/lib/Image/ExifTool/ID3.pm +15 -3
  33. data/bin/lib/Image/ExifTool/JPEG.pm +74 -4
  34. data/bin/lib/Image/ExifTool/JSON.pm +27 -4
  35. data/bin/lib/Image/ExifTool/Jpeg2000.pm +393 -16
  36. data/bin/lib/Image/ExifTool/LIF.pm +153 -0
  37. data/bin/lib/Image/ExifTool/Lang/nl.pm +60 -59
  38. data/bin/lib/Image/ExifTool/M2TS.pm +137 -5
  39. data/bin/lib/Image/ExifTool/MIE.pm +4 -3
  40. data/bin/lib/Image/ExifTool/MRC.pm +341 -0
  41. data/bin/lib/Image/ExifTool/MWG.pm +3 -3
  42. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  43. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  44. data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
  45. data/bin/lib/Image/ExifTool/Nikon.pm +19 -8
  46. data/bin/lib/Image/ExifTool/NikonSettings.pm +28 -11
  47. data/bin/lib/Image/ExifTool/Olympus.pm +6 -3
  48. data/bin/lib/Image/ExifTool/Other.pm +93 -0
  49. data/bin/lib/Image/ExifTool/PDF.pm +9 -12
  50. data/bin/lib/Image/ExifTool/PNG.pm +8 -7
  51. data/bin/lib/Image/ExifTool/Panasonic.pm +28 -3
  52. data/bin/lib/Image/ExifTool/Pentax.pm +28 -5
  53. data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
  54. data/bin/lib/Image/ExifTool/Photoshop.pm +6 -0
  55. data/bin/lib/Image/ExifTool/QuickTime.pm +247 -88
  56. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +283 -141
  57. data/bin/lib/Image/ExifTool/README +3 -0
  58. data/bin/lib/Image/ExifTool/RIFF.pm +89 -12
  59. data/bin/lib/Image/ExifTool/Samsung.pm +48 -10
  60. data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
  61. data/bin/lib/Image/ExifTool/Sony.pm +237 -78
  62. data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
  63. data/bin/lib/Image/ExifTool/TagLookup.pm +4125 -4028
  64. data/bin/lib/Image/ExifTool/TagNames.pod +644 -286
  65. data/bin/lib/Image/ExifTool/Torrent.pm +18 -11
  66. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
  67. data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  68. data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
  69. data/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
  70. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
  71. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +55 -21
  72. data/bin/lib/Image/ExifTool/WriteXMP.pl +7 -3
  73. data/bin/lib/Image/ExifTool/Writer.pl +47 -10
  74. data/bin/lib/Image/ExifTool/XMP.pm +39 -14
  75. data/bin/lib/Image/ExifTool/XMP2.pl +2 -1
  76. data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  77. data/bin/lib/Image/ExifTool/ZISRAW.pm +121 -2
  78. data/bin/lib/Image/ExifTool.pm +223 -72
  79. data/bin/lib/Image/ExifTool.pod +114 -93
  80. data/bin/perl-Image-ExifTool.spec +43 -42
  81. data/lib/exiftool_vendored/version.rb +1 -1
  82. metadata +28 -13
@@ -47,7 +47,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
47
47
  use Image::ExifTool::Exif;
48
48
  use Image::ExifTool::GPS;
49
49
 
50
- $VERSION = '2.57';
50
+ $VERSION = '2.69';
51
51
 
52
52
  sub ProcessMOV($$;$);
53
53
  sub ProcessKeys($$$);
@@ -65,6 +65,7 @@ sub Process_gsen($$$);
65
65
  sub ProcessRIFFTrailer($$$);
66
66
  sub ProcessTTAD($$$);
67
67
  sub ProcessNMEA($$$);
68
+ sub ProcessGPSLog($$$);
68
69
  sub SaveMetaKeys($$$);
69
70
  # ++^^^^^^^^^^^^++
70
71
  sub ParseItemLocation($$);
@@ -160,8 +161,15 @@ my %ftypLookup = (
160
161
  'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
161
162
  'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
162
163
  'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
163
- 'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4
164
- 'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4
164
+ 'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
165
+ 'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
166
+ 'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
167
+ 'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
168
+ 'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
169
+ 'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
170
+ 'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
171
+ 'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
172
+ 'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
165
173
  'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
166
174
  'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
167
175
  'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
@@ -221,9 +229,10 @@ my %timeInfo = (
221
229
  Writable => 1,
222
230
  Permanent => 1,
223
231
  DelValue => 0,
224
- # It is not uncommon for brain-dead software to use the wrong time zero,
225
- # so assume a time zero of Jan 1, 1970 if the date is before this
232
+ # It is not uncommon for brain-dead software to use the wrong time zero, it should be
233
+ # Jan 1, 1904, so assume a time zero of Jan 1, 1970 if the date is before this
226
234
  # Note: This value will be in UTC if generated by a system that is aware of the time zone
235
+ # (also note: this code is duplicated for the CreateDate tag)
227
236
  RawConv => q{
228
237
  my $offset = (66 * 365 + 17) * 24 * 3600;
229
238
  return $val - $offset if $val >= $offset or $$self{OPTIONS}{QuickTimeUTC};
@@ -243,7 +252,11 @@ my %timeInfo = (
243
252
  },
244
253
  # (all CR3 files store UTC times - PH)
245
254
  ValueConv => 'ConvertUnixTime($val, $self->Options("QuickTimeUTC") || $$self{FileType} eq "CR3")',
246
- ValueConvInv => 'GetUnixTime($val, $self->Options("QuickTimeUTC")) + (66 * 365 + 17) * 24 * 3600',
255
+ ValueConvInv => q{
256
+ $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
257
+ return undef unless defined $val;
258
+ return $val + (66 * 365 + 17) * 24 * 3600;
259
+ },
247
260
  PrintConv => '$self->ConvertDateTime($val)',
248
261
  PrintConvInv => '$self->InverseDateTime($val)',
249
262
  # (can't put Groups here because they aren't constant!)
@@ -258,11 +271,15 @@ my %unknownInfo = (
258
271
  Unknown => 1,
259
272
  ValueConv => '$val =~ /^([\x20-\x7e]*)\0*$/ ? $1 : \$val',
260
273
  );
274
+
275
+ # multi-language text with 6-byte header
276
+ my %langText = ( IText => 6 );
277
+
261
278
  # parsing for most of the 3gp udta language text boxes
262
- my %langText = (
279
+ my %langText3gp = (
263
280
  Notes => 'used in 3gp videos',
264
- IText => 6,
265
281
  Avoid => 1,
282
+ IText => 6,
266
283
  );
267
284
 
268
285
  # 4-character Vendor ID codes (ref PH)
@@ -670,6 +687,15 @@ my %eeBox2 = (
670
687
  udat => { #PH (GPS NMEA-format log written by Datakam Player software)
671
688
  Name => 'GPSLog',
672
689
  Binary => 1, # (actually ASCII, but very lengthy)
690
+ Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
691
+ RawConv => q{
692
+ $val =~ s/\0+$//; # remove trailing nulls
693
+ if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
694
+ my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
695
+ Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
696
+ }
697
+ return $val;
698
+ },
673
699
  },
674
700
  # meta - proprietary XML information written by some Flip cameras - PH
675
701
  # beam - 16 bytes found in an iPhone video
@@ -708,6 +734,11 @@ my %eeBox2 = (
708
734
  Unknown => 1,
709
735
  Binary => 1,
710
736
  },
737
+ sefd => {
738
+ Name => 'SamsungTrailer',
739
+ SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
740
+ },
741
+ # 'samn'? - seen in Vantrue N2S sample video
711
742
  );
712
743
 
713
744
  # MPEG-4 'ftyp' atom
@@ -1169,6 +1200,15 @@ my %eeBox2 = (
1169
1200
  Name => 'CreateDate',
1170
1201
  Groups => { 2 => 'Time' },
1171
1202
  %timeInfo,
1203
+ RawConv => q{
1204
+ my $offset = (66 * 365 + 17) * 24 * 3600;
1205
+ if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1206
+ $val -= $offset;
1207
+ } elsif ($val and not $$self{IsWriting}) {
1208
+ $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
1209
+ }
1210
+ return $$self{CreateDate} = $val;
1211
+ },
1172
1212
  # this is int64u if MovieHeaderVersion == 1 (ref 13)
1173
1213
  Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1174
1214
  },
@@ -1545,47 +1585,43 @@ my %eeBox2 = (
1545
1585
  # the following are 3gp tags, references:
1546
1586
  # http://atomicparsley.sourceforge.net
1547
1587
  # http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/
1548
- # (note that all %langText tags are Avoid => 1)
1549
- cprt => { Name => 'Copyright', %langText, Groups => { 2 => 'Author' } },
1550
- auth => { Name => 'Author', %langText, Groups => { 2 => 'Author' } },
1551
- titl => { Name => 'Title', %langText },
1552
- dscp => { Name => 'Description',%langText },
1553
- perf => { Name => 'Performer', %langText },
1554
- gnre => { Name => 'Genre', %langText },
1555
- albm => { Name => 'Album', %langText },
1556
- coll => { Name => 'CollectionName', %langText }, #17
1588
+ # (note that all %langText3gp tags are Avoid => 1)
1589
+ cprt => { Name => 'Copyright', %langText3gp, Groups => { 2 => 'Author' } },
1590
+ auth => { Name => 'Author', %langText3gp, Groups => { 2 => 'Author' } },
1591
+ titl => { Name => 'Title', %langText3gp },
1592
+ dscp => { Name => 'Description',%langText3gp },
1593
+ perf => { Name => 'Performer', %langText3gp },
1594
+ gnre => { Name => 'Genre', %langText3gp },
1595
+ albm => { Name => 'Album', %langText3gp },
1596
+ coll => { Name => 'CollectionName', %langText3gp }, #17
1557
1597
  rtng => {
1558
1598
  Name => 'Rating',
1599
+ Writable => 'undef',
1600
+ Avoid => 1,
1559
1601
  # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1560
- RawConv => q{
1561
- return '<err>' unless length $val >= 14;
1562
- my $str = 'Entity=' . substr($val,4,4) . ' Criteria=' . substr($val,8,4);
1563
- $str =~ tr/\0-\x1f\x7f-\xff//d; # remove unprintable characters
1564
- my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 12));
1565
- $lang = $lang ? "($lang) " : '';
1566
- $val = substr($val, 14);
1567
- $val = $self->Decode($val, 'UCS2') if $val =~ /^\xfe\xff/;
1568
- return $lang . $str . ' ' . $val;
1569
- },
1602
+ IText => 14, # (14 bytes before string)
1603
+ Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1604
+ ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1605
+ ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1570
1606
  },
1571
1607
  clsf => {
1572
1608
  Name => 'Classification',
1609
+ Writable => 'undef',
1610
+ Avoid => 1,
1573
1611
  # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1574
- RawConv => q{
1575
- return '<err>' unless length $val >= 12;
1576
- my $str = 'Entity=' . substr($val,4,4) . ' Index=' . Get16u(\$val,8);
1577
- $str =~ tr/\0-\x1f\x7f-\xff//d; # remove unprintable characters
1578
- my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 10));
1579
- $lang = $lang ? "($lang) " : '';
1580
- $val = substr($val, 12);
1581
- $val = $self->Decode($val, 'UCS2') if $val =~ /^\xfe\xff/;
1582
- return $lang . $str . ' ' . $val;
1583
- },
1612
+ IText => 12,
1613
+ Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1614
+ ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1615
+ ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1584
1616
  },
1585
1617
  kywd => {
1586
1618
  Name => 'Keywords',
1587
- # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings)
1619
+ # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1620
+ # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1621
+ Notes => "not writable because Apple doesn't follow the 3gp specification",
1588
1622
  RawConv => q{
1623
+ my $sep = $self->Options('ListSep');
1624
+ return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
1589
1625
  return '<err>' unless length $val >= 7;
1590
1626
  my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1591
1627
  $lang = $lang ? "($lang) " : '';
@@ -1601,27 +1637,30 @@ my %eeBox2 = (
1601
1637
  push @vals, $v;
1602
1638
  $pos += $len;
1603
1639
  }
1604
- my $sep = $self->Options('ListSep');
1605
1640
  return $lang . join($sep, @vals);
1606
1641
  },
1607
1642
  },
1608
1643
  loci => {
1609
1644
  Name => 'LocationInformation',
1610
1645
  Groups => { 2 => 'Location' },
1646
+ Writable => 'undef',
1647
+ IText => 6,
1648
+ Avoid => 1,
1649
+ NoDecode => 1, # (we'll decode the data ourself)
1650
+ Notes => q{
1651
+ string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1652
+ Notes=XXX", used in 3gp videos
1653
+ },
1611
1654
  # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1612
1655
  # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1613
1656
  RawConv => q{
1614
- return '<err>' unless length $val >= 6;
1615
- my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1616
- $lang = $lang ? "($lang) " : '';
1617
- $val = substr($val, 6);
1618
1657
  my $str;
1619
1658
  if ($val =~ /^\xfe\xff/) {
1620
1659
  $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '<err>';
1621
1660
  $str = $self->Decode($1, 'UCS2');
1622
1661
  } else {
1623
1662
  $val =~ s/^(.*?)\0//s or return '<err>';
1624
- $str = $1;
1663
+ $str = $self->Decode($1, 'UTF8');
1625
1664
  }
1626
1665
  $str = '(none)' unless length $str;
1627
1666
  return '<err>' if length $val < 13;
@@ -1636,27 +1675,52 @@ my %eeBox2 = (
1636
1675
  if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1637
1676
  $str .= ' Body=' . $self->Decode($1, 'UCS2');
1638
1677
  } elsif ($val =~ s/^(.*?)\0//s) {
1639
- $str .= " Body=$1";
1678
+ $str .= ' Body=' . $self->Decode($1, 'UTF8');
1640
1679
  }
1641
1680
  if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1642
1681
  $str .= ' Notes=' . $self->Decode($1, 'UCS2');
1643
1682
  } elsif ($val =~ s/^(.*?)\0//s) {
1644
- $str .= " Notes=$1";
1683
+ $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1645
1684
  }
1646
- return $lang . $str;
1685
+ return $str;
1686
+ },
1687
+ RawConvInv => q{
1688
+ my ($role, $lat, $lon, $alt, $body, $note);
1689
+ $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1690
+ $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1691
+ $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1692
+ $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1693
+ $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1694
+ $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1695
+ $val = '' if $val eq '(none)';
1696
+ $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1697
+ return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1698
+ SetFixed32s(defined $lon ? $lon : 999) .
1699
+ SetFixed32s(defined $lat ? $lat : 999) .
1700
+ SetFixed32s(defined $alt ? $alt : 0) .
1701
+ $self->Encode($body) . "\0" .
1702
+ $self->Encode($note) . "\0";
1647
1703
  },
1648
1704
  },
1649
1705
  yrrc => {
1650
1706
  Name => 'Year',
1707
+ Writable => 'undef',
1651
1708
  Groups => { 2 => 'Time' },
1652
- RawConv => 'length($val) >= 6 ? Get16u(\$val,4) : "<err>"',
1709
+ Avoid => 1,
1710
+ Notes => 'used in 3gp videos',
1711
+ ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : "<err>"',
1712
+ ValueConvInv => 'pack("Nn",0,$val)',
1653
1713
  },
1654
1714
  urat => { #17
1655
1715
  Name => 'UserRating',
1656
- RawConv => q{
1716
+ Writable => 'undef',
1717
+ Notes => 'used in 3gp videos',
1718
+ Avoid => 1,
1719
+ ValueConv => q{
1657
1720
  return '<err>' unless length $val >= 8;
1658
- return Get8u(\$val, 7);
1721
+ unpack('x7C', $val);
1659
1722
  },
1723
+ ValueConvInv => 'pack("N2",0,$val)',
1660
1724
  },
1661
1725
  # tsel - TrackSelection (ref 17)
1662
1726
  # Apple tags (ref 16[dead] -- see ref 25 instead)
@@ -1988,7 +2052,11 @@ my %eeBox2 = (
1988
2052
  # ---- Microsoft ----
1989
2053
  Xtra => { #PH (microsoft)
1990
2054
  Name => 'MicrosoftXtra',
1991
- SubDirectory => { TagTable => 'Image::ExifTool::Microsoft::Xtra' },
2055
+ WriteGroup => 'Microsoft',
2056
+ SubDirectory => {
2057
+ DirName => 'Microsoft',
2058
+ TagTable => 'Image::ExifTool::Microsoft::Xtra',
2059
+ },
1992
2060
  },
1993
2061
  # ---- Minolta ----
1994
2062
  MMA0 => { #PH (DiMage 7Hi)
@@ -2037,7 +2105,7 @@ my %eeBox2 = (
2037
2105
  SubDirectory => { TagTable => 'Image::ExifTool::Olympus::thmb' },
2038
2106
  },{ #17 (format is in bytes 3-7)
2039
2107
  Name => 'ThumbnailImage',
2040
- Condition => '$$valPt =~ /^.{8}\xff\xd8\xff\xdb/s',
2108
+ Condition => '$$valPt =~ /^.{8}\xff\xd8\xff[\xdb\xe0]/s',
2041
2109
  Groups => { 2 => 'Preview' },
2042
2110
  RawConv => 'substr($val, 8)',
2043
2111
  Binary => 1,
@@ -2201,6 +2269,12 @@ my %eeBox2 = (
2201
2269
  # opax - 164 bytes unknown (center and affine arrays? ref 26)
2202
2270
  # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2203
2271
  # intv - 16 bytes all zero
2272
+ # ---- Xaiomi ----
2273
+ mcvr => {
2274
+ Name => 'PreviewImage',
2275
+ Groups => { 2 => 'Preview' },
2276
+ Binary => 1,
2277
+ },
2204
2278
  # ---- Unknown ----
2205
2279
  # CDET - 128 bytes (unknown origin)
2206
2280
  # mtyp - 4 bytes all zero (some drone video)
@@ -2638,6 +2712,7 @@ my %eeBox2 = (
2638
2712
  colr => [{
2639
2713
  Name => 'ICC_Profile',
2640
2714
  Condition => '$$valPt =~ /^(prof|rICC)/',
2715
+ Permanent => 0, # (in QuickTime, this writes a zero-length box instead of deleting)
2641
2716
  SubDirectory => {
2642
2717
  TagTable => 'Image::ExifTool::ICC_Profile::Main',
2643
2718
  Start => 4,
@@ -2994,9 +3069,9 @@ my %eeBox2 = (
2994
3069
  3-character ISO 639-2 language code and an optional ISO 3166-1 alpha 2
2995
3070
  country code to the tag name (eg. "ItemList:Title-fra" or
2996
3071
  "ItemList::Title-fra-FR"). When creating a new Meta box to contain the
2997
- ItemList directory, by default ExifTool does not specify a
2998
- L<Handler|Image::ExifTool::TagNames/QuickTime Handler Tags>, but the
2999
- API L<QuickTimeHandler|../ExifTool.html#QuickTimeHandler> option may be used to include an 'mdir' Handler box.
3072
+ ItemList directory, by default ExifTool adds an 'mdir' (Metadata) Handler
3073
+ box because Apple software may ignore ItemList tags otherwise, but the API
3074
+ L<QuickTimeHandler|../ExifTool.html#QuickTimeHandler> option may be set to 0 to avoid this.
3000
3075
  },
3001
3076
  # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
3002
3077
  # int8u and int16u. Multi-byte binary "data" tags are extracted as binary data.
@@ -6157,13 +6232,12 @@ my %eeBox2 = (
6157
6232
  PROCESS_PROC => \&ProcessKeys,
6158
6233
  WRITE_PROC => \&WriteKeys,
6159
6234
  CHECK_PROC => \&CheckQTValue,
6160
- VARS => { LONG_TAGS => 3 },
6235
+ VARS => { LONG_TAGS => 7 },
6161
6236
  WRITABLE => 1,
6162
6237
  # (not PREFERRED when writing)
6163
6238
  GROUPS => { 1 => 'Keys' },
6164
6239
  WRITE_GROUP => 'Keys',
6165
6240
  LANG_INFO => \&GetLangInfo,
6166
- FORMAT => 'string',
6167
6241
  NOTES => q{
6168
6242
  This directory contains a list of key names which are used to decode tags
6169
6243
  written by the "mdta" handler. Also in this table are a few tags found in
@@ -6229,6 +6303,10 @@ my %eeBox2 = (
6229
6303
  'location.ISO6709' => {
6230
6304
  Name => 'GPSCoordinates',
6231
6305
  Groups => { 2 => 'Location' },
6306
+ Notes => q{
6307
+ Google Photos may ignore this if the coorinates have more than 5 digits
6308
+ after the decimal
6309
+ },
6232
6310
  ValueConv => \&ConvertISO6709,
6233
6311
  ValueConvInv => \&ConvInvISO6709,
6234
6312
  PrintConv => \&PrintGPSCoordinates,
@@ -6265,6 +6343,11 @@ my %eeBox2 = (
6265
6343
  PrintConv => '$self->ConvertDateTime($val)',
6266
6344
  PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
6267
6345
  },
6346
+ 'location.accuracy.horizontal' => { Name => 'LocationAccuracyHorizontal' },
6347
+ 'live-photo.auto' => { Name => 'LivePhotoAuto', Writable => 'int8u' },
6348
+ 'live-photo.vitality-score' => { Name => 'LivePhotoVitalityScore', Writable => 'float' },
6349
+ 'live-photo.vitality-scoring-version' => { Name => 'LivePhotoVitalityScoringVersion', Writable => 'int64s' },
6350
+ 'apple.photos.variation-identifier' => { Name => 'ApplePhotosVariationIdentifier', Writable => 'int64s' },
6268
6351
  'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
6269
6352
  'direction.motion' => { Name => 'CameraMotion', Groups => { 2 => 'Location' } },
6270
6353
  'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
@@ -6300,11 +6383,15 @@ my %eeBox2 = (
6300
6383
  # com.divergentmedia.clipwrap.manufacturer ('Sony')
6301
6384
  # com.divergentmedia.clipwrap.originalDateTime ('2013/2/6 10:30:40+0200')
6302
6385
  #
6303
- # seen in timed metadata (mebx), and added dynamically to the table
6304
- # via SaveMetaKeys(). NOTE: these tags are not writable!
6386
+ # seen in timed metadata (mebx), and added dynamically to the table via SaveMetaKeys()
6387
+ # NOTE: these tags are not writable! (timed metadata cannot yet be written)
6305
6388
  #
6306
6389
  # (mdta)com.apple.quicktime.video-orientation (dtyp=66, int16s)
6307
- 'video-orientation' => { Name => 'VideoOrientation', Writable => 0 },
6390
+ 'video-orientation' => {
6391
+ Name => 'VideoOrientation',
6392
+ Writable => 0,
6393
+ PrintConv => \%Image::ExifTool::Exif::orientation, #PH (NC)
6394
+ },
6308
6395
  # (mdta)com.apple.quicktime.live-photo-info (dtyp=com.apple.quicktime.com.apple.quicktime.live-photo-info)
6309
6396
  'live-photo-info' => {
6310
6397
  Name => 'LivePhotoInfo',
@@ -6364,10 +6451,11 @@ my %eeBox2 = (
6364
6451
  # iTunes info ('----') atoms
6365
6452
  %Image::ExifTool::QuickTime::iTunesInfo = (
6366
6453
  PROCESS_PROC => \&ProcessMOV,
6367
- GROUPS => { 2 => 'Audio' },
6454
+ GROUPS => { 1 => 'iTunes', 2 => 'Audio' },
6368
6455
  NOTES => q{
6369
6456
  ExifTool will extract any iTunesInfo tags that exist, even if they are not
6370
- defined in this table.
6457
+ defined in this table. These tags belong to the family 1 "iTunes" group,
6458
+ and are not currently writable.
6371
6459
  },
6372
6460
  # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
6373
6461
  # I haven't been able to find any documentation on this.
@@ -6428,9 +6516,45 @@ my %eeBox2 = (
6428
6516
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::EncodingParams' },
6429
6517
  },
6430
6518
  # also heard about 'iTunPGAP', but I haven't seen a sample
6431
- DISCNUMBER => 'DiscNumber', #PH
6432
- TRACKNUMBER => 'TrackNumber', #PH
6433
- popularimeter => 'Popularimeter', #PH
6519
+ # all tags below were added based on samples I have seen - PH
6520
+ DISCNUMBER => 'DiscNumber',
6521
+ TRACKNUMBER => 'TrackNumber',
6522
+ ARTISTS => 'Artists',
6523
+ CATALOGNUMBER => 'CatalogNumber',
6524
+ RATING => 'Rating',
6525
+ MEDIA => 'Media',
6526
+ SCRIPT => 'Script', # character set? (seen 'Latn')
6527
+ BARCODE => 'Barcode',
6528
+ LABEL => 'Label',
6529
+ MOOD => 'Mood',
6530
+ popularimeter => 'Popularimeter',
6531
+ 'Dynamic Range (DR)'=> 'DynamicRange',
6532
+ initialkey => 'InitialKey',
6533
+ originalyear => 'OriginalYear',
6534
+ originaldate => 'OriginalDate',
6535
+ '~length' => 'Length', # play length? (ie. duration?)
6536
+ replaygain_track_gain=>'ReplayTrackGain',
6537
+ replaygain_track_peak=>'ReplayTrackPeak',
6538
+ 'Volume Level (ReplayGain)'=> 'ReplayVolumeLevel',
6539
+ 'Dynamic Range (R128)'=> 'DynamicRangeR128',
6540
+ 'Volume Level (R128)' => 'VolumeLevelR128',
6541
+ 'Peak Level (Sample)' => 'PeakLevelSample',
6542
+ 'Peak Level (R128)' => 'PeakLevelR128',
6543
+ # also seen (many from forum12777):
6544
+ # 'MusicBrainz Album Release Country'
6545
+ # 'MusicBrainz Album Type'
6546
+ # 'MusicBrainz Album Status'
6547
+ # 'MusicBrainz Track Id'
6548
+ # 'MusicBrainz Release Track Id'
6549
+ # 'MusicBrainz Album Id'
6550
+ # 'MusicBrainz Album Artist Id'
6551
+ # 'MusicBrainz Artist Id'
6552
+ # 'Acoustid Id' (sic)
6553
+ # 'Tool Version'
6554
+ # 'Tool Name'
6555
+ # 'ISRC'
6556
+ # 'HDCD'
6557
+ # 'Waveform'
6434
6558
  );
6435
6559
 
6436
6560
  # iTunes audio encoding parameters
@@ -7589,7 +7713,11 @@ my %eeBox2 = (
7589
7713
  8 => {
7590
7714
  Name => 'HandlerType',
7591
7715
  Format => 'undef[4]',
7592
- RawConv => '$$self{HandlerType} = $val unless $val eq "alis" or $val eq "url "; $val',
7716
+ RawConv => q{
7717
+ $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
7718
+ $$self{HasHandler}{$val} = 1; # remember all our handlers
7719
+ return $val;
7720
+ },
7593
7721
  PrintConvColumns => 2,
7594
7722
  PrintConv => {
7595
7723
  alis => 'Alias Data', #PH
@@ -7694,7 +7822,7 @@ my %eeBox2 = (
7694
7822
  $val =~ s/\0+$//; # remove trailing nulls
7695
7823
  if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
7696
7824
  my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
7697
- Image::ExifTool::QuickTime::ProcessNMEA($self, { DataPt => \$val }, $tagTbl);
7825
+ Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
7698
7826
  }
7699
7827
  return $val;
7700
7828
  },
@@ -8574,7 +8702,7 @@ sub QuickTimeFormat($$)
8574
8702
  my ($flags, $len) = @_;
8575
8703
  my $format;
8576
8704
  if ($flags == 0x15 or $flags == 0x16) {
8577
- $format = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
8705
+ $format = { 1=>'int8', 2=>'int16', 4=>'int32', 8=>'int64' }->{$len};
8578
8706
  $format .= $flags == 0x15 ? 's' : 'u' if $format;
8579
8707
  } elsif ($flags == 0x17) {
8580
8708
  $format = 'float';
@@ -8795,20 +8923,28 @@ sub ProcessKeys($$$)
8795
8923
  my $ns = substr($$dataPt, $pos + 4, 4);
8796
8924
  my $tag = substr($$dataPt, $pos + 8, $len - 8);
8797
8925
  $tag =~ s/\0.*//s; # truncate at null
8926
+ my $full = $tag;
8798
8927
  $tag =~ s/^com\.(apple\.quicktime\.)?// if $ns eq 'mdta'; # remove apple quicktime domain
8799
8928
  $tag = "Tag_$ns" unless $tag;
8800
- # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
8801
- my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
8802
- unless ($tagInfo) {
8803
- $tagInfo = $et->GetTagInfo($itemList, $tag);
8804
- unless ($tagInfo) {
8929
+ my $short = $tag;
8930
+ my $tagInfo;
8931
+ for (;;) {
8932
+ $tagInfo = $et->GetTagInfo($tagTablePtr, $tag) and last;
8933
+ # also try ItemList and UserData tables
8934
+ $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
8935
+ $tagInfo = $et->GetTagInfo($userData, $tag) and last;
8936
+ # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
8937
+ if ($tag =~ /^\w{3}\xa9$/) {
8938
+ $tag = pack('N', unpack('V', $tag));
8939
+ $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
8805
8940
  $tagInfo = $et->GetTagInfo($userData, $tag);
8806
- if (not $tagInfo and $tag =~ /^\w{3}\xa9$/) {
8807
- $tag = pack('N', unpack('V', $tag));
8808
- $tagInfo = $et->GetTagInfo($itemList, $tag);
8809
- $tagInfo or $tagInfo = $et->GetTagInfo($userData, $tag);
8810
- }
8941
+ last;
8811
8942
  }
8943
+ if ($tag eq $full) {
8944
+ $tag = $short;
8945
+ last;
8946
+ }
8947
+ $tag = $full;
8812
8948
  }
8813
8949
  my ($newInfo, $msg);
8814
8950
  if ($tagInfo) {
@@ -8849,6 +8985,7 @@ sub ProcessKeys($$$)
8849
8985
  delete $$itemList{$id};
8850
8986
  }
8851
8987
  if ($newInfo) {
8988
+ $$newInfo{KeysID} = $tag; # save original ID for use in family 7 group name
8852
8989
  AddTagToTable($itemList, $id, $newInfo);
8853
8990
  $msg or $msg = '';
8854
8991
  $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $tag$msg\n";
@@ -8927,6 +9064,7 @@ sub ProcessMOV($$;$)
8927
9064
  if ($raf->Read($buff, $size-8) == $size-8) {
8928
9065
  $raf->Seek(-($size-8), 1);
8929
9066
  my $type = substr($buff, 0, 4);
9067
+ $$et{save_ftyp} = $type;
8930
9068
  # see if we know the extension for this file type
8931
9069
  if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
8932
9070
  $fileType = $1;
@@ -9201,6 +9339,7 @@ ItemID: foreach $id (keys %$items) {
9201
9339
  Name => $name,
9202
9340
  Description => $desc,
9203
9341
  };
9342
+ $et->VPrint(0, $$et{INDENT}, "[adding QuickTime:$name]\n");
9204
9343
  AddTagToTable($tagTablePtr, $tag, $tagInfo);
9205
9344
  }
9206
9345
  # ignore 8-byte header
@@ -9212,9 +9351,9 @@ ItemID: foreach $id (keys %$items) {
9212
9351
  $val = \$buff;
9213
9352
  }
9214
9353
  }
9215
- undef %triplet;
9354
+ $$tagInfo{List} = 1; # (allow any of these tags to have multiple data elements)
9355
+ $et->VerboseInfo($tag, $tagInfo, Value => $val) if $verbose;
9216
9356
  } else {
9217
- undef %triplet if $tag eq 'mean';
9218
9357
  $triplet{$tag} = substr($val,4) if length($val) > 4;
9219
9358
  undef $tagInfo; # don't store this tag
9220
9359
  }
@@ -9298,7 +9437,13 @@ ItemID: foreach $id (keys %$items) {
9298
9437
  # (shouldn't be null terminated, but some software writes it anyway)
9299
9438
  $value =~ s/\0$// unless $$tagInfo{Binary};
9300
9439
  } else {
9301
- $format = QuickTimeFormat($flags, $len) unless $format;
9440
+ if (not $format) {
9441
+ $format = QuickTimeFormat($flags, $len);
9442
+ } elsif ($format =~ /^int\d+([us])$/) {
9443
+ # adjust integer to available length (but not int64)
9444
+ my $fmt = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
9445
+ $format = $fmt . $1 if defined $fmt;
9446
+ }
9302
9447
  if ($format) {
9303
9448
  $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
9304
9449
  } elsif (not $$tagInfo{ValueConv}) {
@@ -9350,9 +9495,9 @@ ItemID: foreach $id (keys %$items) {
9350
9495
  }
9351
9496
  for (;;) {
9352
9497
  my ($len, $lang);
9353
- if ($$tagInfo{IText} and $$tagInfo{IText} == 6) {
9354
- last if $pos + 6 > $size;
9355
- $pos += 4;
9498
+ if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
9499
+ last if $pos + $$tagInfo{IText} > $size;
9500
+ $pos += $$tagInfo{IText} - 2;
9356
9501
  $lang = unpack("x${pos}n", $val);
9357
9502
  $pos += 2;
9358
9503
  $len = $size - $pos;
@@ -9372,7 +9517,7 @@ ItemID: foreach $id (keys %$items) {
9372
9517
  # ignore any empty entries (or null padding) after the first
9373
9518
  next if not $len and $pos;
9374
9519
  my $str = substr($val, $pos, $len);
9375
- my $langInfo;
9520
+ my ($langInfo, $enc);
9376
9521
  if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
9377
9522
  # this is a Macintosh language code
9378
9523
  # a language code of 0 is Macintosh english, so treat as default
@@ -9389,15 +9534,22 @@ ItemID: foreach $id (keys %$items) {
9389
9534
  }
9390
9535
  # the spec says only "Macintosh text encoding", but
9391
9536
  # allow this to be configured by the user
9392
- $str = $et->Decode($str, $charsetQuickTime);
9537
+ $enc = $charsetQuickTime;
9393
9538
  } else {
9394
9539
  # convert language code to ASCII (ignore read-only bit)
9395
9540
  $lang = UnpackLang($lang);
9396
9541
  # may be either UTF-8 or UTF-16BE
9397
- my $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9542
+ $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
9543
+ }
9544
+ unless ($$tagInfo{NoDecode}) {
9398
9545
  $str = $et->Decode($str, $enc);
9546
+ $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
9547
+ }
9548
+ if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
9549
+ my $n = $$tagInfo{IText} - 6;
9550
+ # add back extra bytes (eg. 'rtng' box)
9551
+ $str = substr($val, $pos-$n-2, $n) . $str;
9399
9552
  }
9400
- $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
9401
9553
  $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
9402
9554
  $et->FoundTag($langInfo || $tagInfo, $str);
9403
9555
  $pos += $len;
@@ -9447,6 +9599,13 @@ ItemID: foreach $id (keys %$items) {
9447
9599
  ($size, $tag) = unpack('Na4', $buff);
9448
9600
  ++$index if defined $index;
9449
9601
  }
9602
+ # tweak file type based on track content ("iso*" and "dash" ftyp only)
9603
+ if ($topLevel and $$et{VALUE}{FileType} and $$et{VALUE}{FileType} eq 'MP4' and
9604
+ $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash)/ and
9605
+ $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
9606
+ {
9607
+ $et->OverrideFileType('M4A', 'audio/mp4');
9608
+ }
9450
9609
  # fill in missing defaults for alternate language tags
9451
9610
  # (the first language is taken as the default)
9452
9611
  if ($doDefaultLang and $$et{QTLang}) {