exiftool-vendored.pl 12.60.0 → 12.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/bin/Changes +110 -0
  2. package/bin/LICENSE +674 -0
  3. package/bin/MANIFEST +11 -0
  4. package/bin/META.json +5 -3
  5. package/bin/META.yml +5 -3
  6. package/bin/Makefile.PL +7 -1
  7. package/bin/README +50 -45
  8. package/bin/config_files/guano.config +161 -0
  9. package/bin/exiftool +163 -103
  10. package/bin/lib/Image/ExifTool/7Z.pm +793 -0
  11. package/bin/lib/Image/ExifTool/Apple.pm +14 -7
  12. package/bin/lib/Image/ExifTool/BMP.pm +0 -1
  13. package/bin/lib/Image/ExifTool/BigTIFF.pm +8 -1
  14. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +4 -4
  15. package/bin/lib/Image/ExifTool/Canon.pm +4 -1
  16. package/bin/lib/Image/ExifTool/CanonRaw.pm +4 -4
  17. package/bin/lib/Image/ExifTool/CanonVRD.pm +4 -1
  18. package/bin/lib/Image/ExifTool/Exif.pm +31 -14
  19. package/bin/lib/Image/ExifTool/FlashPix.pm +9 -2
  20. package/bin/lib/Image/ExifTool/FujiFilm.pm +3 -3
  21. package/bin/lib/Image/ExifTool/GPS.pm +5 -2
  22. package/bin/lib/Image/ExifTool/Geotag.pm +4 -1
  23. package/bin/lib/Image/ExifTool/Jpeg2000.pm +243 -20
  24. package/bin/lib/Image/ExifTool/Lang/fr.pm +1467 -202
  25. package/bin/lib/Image/ExifTool/MPF.pm +2 -1
  26. package/bin/lib/Image/ExifTool/Matroska.pm +16 -1
  27. package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
  28. package/bin/lib/Image/ExifTool/Nikon.pm +941 -33
  29. package/bin/lib/Image/ExifTool/NikonCustom.pm +874 -63
  30. package/bin/lib/Image/ExifTool/PDF.pm +39 -12
  31. package/bin/lib/Image/ExifTool/PLIST.pm +8 -1
  32. package/bin/lib/Image/ExifTool/PNG.pm +6 -6
  33. package/bin/lib/Image/ExifTool/PhaseOne.pm +5 -5
  34. package/bin/lib/Image/ExifTool/QuickTime.pm +96 -32
  35. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +68 -37
  36. package/bin/lib/Image/ExifTool/README +2 -2
  37. package/bin/lib/Image/ExifTool/RIFF.pm +11 -9
  38. package/bin/lib/Image/ExifTool/Samsung.pm +227 -227
  39. package/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
  40. package/bin/lib/Image/ExifTool/SigmaRaw.pm +4 -4
  41. package/bin/lib/Image/ExifTool/Sony.pm +237 -32
  42. package/bin/lib/Image/ExifTool/TagLookup.pm +4762 -4629
  43. package/bin/lib/Image/ExifTool/TagNames.pod +737 -20
  44. package/bin/lib/Image/ExifTool/Validate.pm +17 -1
  45. package/bin/lib/Image/ExifTool/WPG.pm +296 -0
  46. package/bin/lib/Image/ExifTool/WriteExif.pl +9 -7
  47. package/bin/lib/Image/ExifTool/WritePDF.pl +7 -8
  48. package/bin/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
  49. package/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
  50. package/bin/lib/Image/ExifTool/Writer.pl +47 -16
  51. package/bin/lib/Image/ExifTool/XMP.pm +30 -6
  52. package/bin/lib/Image/ExifTool/XMP2.pl +32 -0
  53. package/bin/lib/Image/ExifTool/XMPStruct.pl +96 -28
  54. package/bin/lib/Image/ExifTool/ZIP.pm +159 -41
  55. package/bin/lib/Image/ExifTool.pm +280 -164
  56. package/bin/lib/Image/ExifTool.pod +117 -52
  57. package/bin/perl-Image-ExifTool.spec +44 -43
  58. package/bin/pp_build_exe.args +8 -4
  59. package/package.json +3 -3
@@ -21,7 +21,7 @@ use vars qw($VERSION $AUTOLOAD $lastFetched);
21
21
  use Image::ExifTool qw(:DataAccess :Utils);
22
22
  require Exporter;
23
23
 
24
- $VERSION = '1.55';
24
+ $VERSION = '1.57';
25
25
 
26
26
  sub FetchObject($$$$);
27
27
  sub ExtractObject($$;$$);
@@ -41,7 +41,7 @@ my $cryptStream; # flag that streams are encrypted
41
41
  my $lastOffset; # last fetched object offset
42
42
  my %streamObjs; # hash of stream objects
43
43
  my %fetched; # dicts fetched in verbose mode (to avoid cyclical recursion)
44
- my $pdfVer; # version of PDF file being processed
44
+ my $pdfVer; # version of PDF file being processed (from header)
45
45
 
46
46
  # filters supported in DecodeStream()
47
47
  my %supportedFilter = (
@@ -109,12 +109,24 @@ my %supportedFilter = (
109
109
  Title => { },
110
110
  Author => { Groups => { 2 => 'Author' } },
111
111
  Subject => { },
112
- Keywords => { List => 'string' }, # this is a string list
112
+ Keywords => {
113
+ List => 'string', # this is a string list
114
+ Notes => q{
115
+ stored as a string but treated as a comma- or semicolon-separated list of
116
+ items when reading if the string contains commas or semicolons, whichever is
117
+ more numerous, otherwise it is treated a space-separated list of items.
118
+ Written as a comma-separated list. The list behaviour may be defeated by
119
+ setting the API NoPDFList option. Note that the corresponding
120
+ XMP-pdf:Keywords tag is not treated as a list, so the NoPDFList option
121
+ should be used when copying between these two.
122
+ },
123
+ },
113
124
  Creator => { },
114
125
  Producer => { },
115
126
  CreationDate => {
116
127
  Name => 'CreateDate',
117
128
  Writable => 'date',
129
+ PDF2 => 1, # not deprecated in PDF 2.0
118
130
  Groups => { 2 => 'Time' },
119
131
  Shift => 'Time',
120
132
  PrintConv => '$self->ConvertDateTime($val)',
@@ -123,6 +135,7 @@ my %supportedFilter = (
123
135
  ModDate => {
124
136
  Name => 'ModifyDate',
125
137
  Writable => 'date',
138
+ PDF2 => 1, # not deprecated in PDF 2.0
126
139
  Groups => { 2 => 'Time' },
127
140
  Shift => 'Time',
128
141
  PrintConv => '$self->ConvertDateTime($val)',
@@ -168,7 +181,10 @@ my %supportedFilter = (
168
181
  Lang => 'Language',
169
182
  PageLayout => { },
170
183
  PageMode => { },
171
- Version => 'PDFVersion',
184
+ Version => {
185
+ Name => 'PDFVersion',
186
+ RawConv => '$$self{PDFVersion} = $val if $$self{PDFVersion} < $val; $val',
187
+ },
172
188
  );
173
189
 
174
190
  # tags extracted from the PDF Encrypt dictionary
@@ -1749,12 +1765,13 @@ sub ExpandArray($)
1749
1765
  # 4) nesting depth, 5) dictionary capture type
1750
1766
  sub ProcessDict($$$$;$$)
1751
1767
  {
1768
+ local $_;
1752
1769
  my ($et, $tagTablePtr, $dict, $xref, $nesting, $type) = @_;
1753
1770
  my $verbose = $et->Options('Verbose');
1754
1771
  my $unknown = $$tagTablePtr{EXTRACT_UNKNOWN};
1755
1772
  my $embedded = (defined $unknown and not $unknown and $et->Options('ExtractEmbedded'));
1756
1773
  my @tags = @{$$dict{_tags}};
1757
- my ($next, %join);
1774
+ my ($next, %join, $validInfo);
1758
1775
  my $index = 0;
1759
1776
 
1760
1777
  $nesting = ($nesting || 0) + 1;
@@ -1775,6 +1792,7 @@ sub ProcessDict($$$$;$$)
1775
1792
  last;
1776
1793
  }
1777
1794
  }
1795
+ $validInfo = ($et->Options('Validate') and $tagTablePtr eq \%Image::ExifTool::PDF::Info);
1778
1796
  #
1779
1797
  # extract information from all tags in the dictionary
1780
1798
  #
@@ -1810,6 +1828,10 @@ sub ProcessDict($$$$;$$)
1810
1828
  $isSubDoc = 1; # treat as a sub-document
1811
1829
  }
1812
1830
  }
1831
+ if ($validInfo and $$et{PDFVersion} >= 2.0 and (not $tagInfo or not $$tagInfo{PDF2})) {
1832
+ my $name = $tagInfo ? ":$$tagInfo{Name}" : " Info tag '${tag}'";
1833
+ $et->Warn("PDF$name is deprecated in PDF 2.0");
1834
+ }
1813
1835
  if ($verbose) {
1814
1836
  my ($val2, $extra);
1815
1837
  if (ref $val eq 'SCALAR') {
@@ -2007,10 +2029,16 @@ sub ProcessDict($$$$;$$)
2007
2029
  }
2008
2030
  if ($$tagInfo{List} and not $$et{OPTIONS}{NoPDFList}) {
2009
2031
  # separate tokens in comma or whitespace delimited lists
2010
- my @values = ($val =~ /,/) ? split /,+\s*/, $val : split ' ', $val;
2011
- foreach $val (@values) {
2012
- $et->FoundTag($tagInfo, $val);
2032
+ my $comma = $val =~ tr/,/,/;
2033
+ my $semi = $val =~ tr/;/;/;
2034
+ my $split;
2035
+ if ($comma or $semi) {
2036
+ $split = $comma > $semi ? ',+\\s*' : ';+\\s*';
2037
+ } else {
2038
+ $split = ' ';
2013
2039
  }
2040
+ my @values = split $split, $val;
2041
+ $et->FoundTag($tagInfo, $_) foreach @values;
2014
2042
  } else {
2015
2043
  # a simple tag value
2016
2044
  $et->FoundTag($tagInfo, $val);
@@ -2118,9 +2146,8 @@ sub ReadPDF($$)
2118
2146
  $raf->Read($buff, 1024) >= 8 or return 0;
2119
2147
  $buff =~ /^(\s*)%PDF-(\d+\.\d+)/ or return 0;
2120
2148
  $$et{PDFBase} = length $1 and $et->Warn('PDF header is not at start of file',1);
2121
- $pdfVer = $2;
2149
+ $pdfVer = $$et{PDFVersion} = $2;
2122
2150
  $et->SetFileType(); # set the FileType tag
2123
- $et->Warn("The PDF $pdfVer specification is held hostage by the ISO") if $pdfVer >= 2.0;
2124
2151
  # store PDFVersion tag
2125
2152
  my $tagTablePtr = GetTagTable('Image::ExifTool::PDF::Root');
2126
2153
  $et->HandleTag($tagTablePtr, 'Version', $pdfVer);
@@ -2384,8 +2411,8 @@ This module is loaded automatically by Image::ExifTool when required.
2384
2411
  This code reads meta information from PDF (Adobe Portable Document Format)
2385
2412
  files. It supports object streams introduced in PDF-1.5 but only with a
2386
2413
  limited set of Filter and Predictor algorithms, however all standard
2387
- encryption methods through PDF-1.7 extension level 3 are supported,
2388
- including AESV2 (AES-128) and AESV3 (AES-256).
2414
+ encryption methods through PDF-2.0 are supported, including AESV2 (AES-128)
2415
+ and AESV3 (AES-256).
2389
2416
 
2390
2417
  =head1 AUTHOR
2391
2418
 
@@ -21,7 +21,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
21
21
  use Image::ExifTool::XMP;
22
22
  use Image::ExifTool::GPS;
23
23
 
24
- $VERSION = '1.10';
24
+ $VERSION = '1.11';
25
25
 
26
26
  sub ExtractObject($$;$);
27
27
  sub Get24u($$);
@@ -288,6 +288,12 @@ sub ExtractObject($$;$)
288
288
  my $obj = ExtractObject($et, $plistInfo, $tag);
289
289
  next if not defined $obj;
290
290
  unless ($tagTablePtr) {
291
+ # make sure this is a valid structure field name
292
+ if (not defined $key or $key !~ /^[-_a-zA-Z0-9]+$/) {
293
+ $key = "Tag$i"; # (generate fake tag name if it had illegal characters)
294
+ } elsif ($key !~ /^[_a-zA-Z]/) {
295
+ $key = "_$key"; # (must begin with alpha or underline)
296
+ }
291
297
  $$val{$key} = $obj if defined $obj;
292
298
  next;
293
299
  }
@@ -298,6 +304,7 @@ sub ExtractObject($$;$)
298
304
  my $name = $tag;
299
305
  $name =~ s/([^A-Za-z])([a-z])/$1\u$2/g; # capitalize words
300
306
  $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
307
+ $name = "Tag$name" if length($name) < 2 or $name =~ /^[-0-9]/;
301
308
  $tagInfo = { Name => ucfirst($name), List => 1 };
302
309
  if ($$plistInfo{DateFormat}) {
303
310
  $$tagInfo{Groups}{2} = 'Time';
@@ -36,7 +36,7 @@ use strict;
36
36
  use vars qw($VERSION $AUTOLOAD %stdCase);
37
37
  use Image::ExifTool qw(:DataAccess :Utils);
38
38
 
39
- $VERSION = '1.63';
39
+ $VERSION = '1.64';
40
40
 
41
41
  sub ProcessPNG_tEXt($$$);
42
42
  sub ProcessPNG_iTXt($$$);
@@ -1374,7 +1374,7 @@ sub ProcessPNG($$)
1374
1374
  my $datCount = 0;
1375
1375
  my $datBytes = 0;
1376
1376
  my $fastScan = $et->Options('FastScan');
1377
- my $md5 = $$et{ImageDataMD5};
1377
+ my $hash = $$et{ImageDataHash};
1378
1378
  my ($n, $sig, $err, $hbuf, $dbuf, $cbuf);
1379
1379
  my ($wasHdr, $wasEnd, $wasDat, $doTxt, @txtOffset);
1380
1380
 
@@ -1454,7 +1454,7 @@ sub ProcessPNG($$)
1454
1454
  if ($datCount and $chunk ne $datChunk) {
1455
1455
  my $s = $datCount > 1 ? 's' : '';
1456
1456
  print $out "$fileType $datChunk ($datCount chunk$s, total $datBytes bytes)\n";
1457
- print $out "$$et{INDENT}(ImageDataMD5: $datBytes bytes of $datChunk data)\n" if $md5;
1457
+ print $out "$$et{INDENT}(ImageDataHash: $datBytes bytes of $datChunk data)\n" if $hash;
1458
1458
  $datCount = $datBytes = 0;
1459
1459
  }
1460
1460
  }
@@ -1541,8 +1541,8 @@ sub ProcessPNG($$)
1541
1541
  }
1542
1542
  # skip over data chunks if possible/necessary
1543
1543
  } elsif (not $validate or $len > $chunkSizeLimit) {
1544
- if ($md5) {
1545
- $et->ImageDataMD5($raf, $len);
1544
+ if ($hash) {
1545
+ $et->ImageDataHash($raf, $len);
1546
1546
  $raf->Read($cbuf, 4) == 4 or $et->Warn('Truncated data'), last;
1547
1547
  } else {
1548
1548
  $raf->Seek($len + 4, 1) or $et->Warn('Seek error'), last;
@@ -1565,7 +1565,7 @@ sub ProcessPNG($$)
1565
1565
  $et->Warn("Corrupted $fileType image") unless $wasEnd;
1566
1566
  last;
1567
1567
  }
1568
- $md5->add($dbuf) if $md5 and $datChunk; # add to MD5 if necessary
1568
+ $hash->add($dbuf) if $hash and $datChunk; # add to hash if necessary
1569
1569
  if ($verbose or $validate or ($outfile and not $fastScan)) {
1570
1570
  # check CRC when in verbose mode (since we don't care about speed)
1571
1571
  my $crc = CalculateCRC(\$hbuf, undef, 4);
@@ -15,7 +15,7 @@ use vars qw($VERSION);
15
15
  use Image::ExifTool qw(:DataAccess :Utils);
16
16
  use Image::ExifTool::Exif;
17
17
 
18
- $VERSION = '1.08';
18
+ $VERSION = '1.09';
19
19
 
20
20
  sub WritePhaseOne($$$);
21
21
  sub ProcessPhaseOne($$$);
@@ -585,7 +585,7 @@ sub ProcessPhaseOne($$$)
585
585
  my $dirLen = $$dirInfo{DirLen} || $$dirInfo{DataLen} - $dirStart;
586
586
  my $binary = $et->Options('Binary');
587
587
  my $verbose = $et->Options('Verbose');
588
- my $md5 = $$et{ImageDataMD5};
588
+ my $hash = $$et{ImageDataHash};
589
589
  my $htmlDump = $$et{HTML_DUMP};
590
590
 
591
591
  return 0 if $dirLen < 12;
@@ -678,16 +678,16 @@ sub ProcessPhaseOne($$$)
678
678
  }
679
679
  }
680
680
  }
681
- if ($md5 and $tagInfo and $$tagInfo{IsImageData}) {
681
+ if ($hash and $tagInfo and $$tagInfo{IsImageData}) {
682
682
  my ($pos, $len) = ($valuePtr, $size);
683
683
  while ($len) {
684
684
  my $n = $len > 65536 ? 65536 : $len;
685
685
  my $tmp = substr($$dataPt, $pos, $n);
686
- $md5->add($tmp);
686
+ $hash->add($tmp);
687
687
  $len -= $n;
688
688
  $pos += $n;
689
689
  }
690
- $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $size bytes of PhaseOne:$$tagInfo{Name})\n");
690
+ $et->VPrint(0, "$$et{INDENT}(ImageDataHash: $size bytes of PhaseOne:$$tagInfo{Name})\n");
691
691
  }
692
692
  my %parms = (
693
693
  DirName => $ifdType,
@@ -37,6 +37,7 @@
37
37
  # 25) https://cconcolato.github.io/mp4ra/atoms.html
38
38
  # 26) https://github.com/SamsungVR/android_upload_sdk/blob/master/SDKLib/src/main/java/com/samsung/msca/samsungvr/sdk/UserVideo.java
39
39
  # 27) https://exiftool.org/forum/index.php?topic=11517.0
40
+ # 28) https://docs.mp3tag.de/mapping/
40
41
  #------------------------------------------------------------------------------
41
42
 
42
43
  package Image::ExifTool::QuickTime;
@@ -47,7 +48,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
47
48
  use Image::ExifTool::Exif;
48
49
  use Image::ExifTool::GPS;
49
50
 
50
- $VERSION = '2.84';
51
+ $VERSION = '2.87';
51
52
 
52
53
  sub ProcessMOV($$;$);
53
54
  sub ProcessKeys($$$);
@@ -224,6 +225,9 @@ my %ftypLookup = (
224
225
  'crx ' => 'Canon Raw (.CRX)', #PH (CR3 or CRM; use Canon CompressorVersion to decide)
225
226
  );
226
227
 
228
+ # use extension to determine file type
229
+ my %useExt = ( GLV => 'MP4' );
230
+
227
231
  # information for int32u date/time tags (time zero is Jan 1, 1904)
228
232
  my %timeInfo = (
229
233
  Notes => 'converted from UTC to local time if the QuickTimeUTC option is set',
@@ -447,8 +451,8 @@ my %dupDirOK = ( ipco => 1, '----' => 1 );
447
451
  my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
448
452
  stsc => 'stbl', stts => 'stbl' );
449
453
 
450
- # atoms required for generating ImageDataMD5
451
- my %md5Box = ( vide => { %eeStd }, soun => { %eeStd } );
454
+ # atoms required for generating ImageDataHash
455
+ my %hashBox = ( vide => { %eeStd }, soun => { %eeStd } );
452
456
 
453
457
  # boxes and their containers for the various handler types that we want to save
454
458
  # when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
@@ -470,6 +474,9 @@ my %eeBox2 = (
470
474
  vide => { avcC => 'stsd' }, # (parses H264 video stream)
471
475
  );
472
476
 
477
+ # image types in AVIF and HEIC files
478
+ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
479
+
473
480
  # QuickTime atoms
474
481
  %Image::ExifTool::QuickTime::Main = (
475
482
  PROCESS_PROC => \&ProcessMOV,
@@ -1003,7 +1010,8 @@ my %eeBox2 = (
1003
1010
  0 => 'Monoscopic',
1004
1011
  1 => 'Stereoscopic Top-Bottom',
1005
1012
  2 => 'Stereoscopic Left-Right',
1006
- 3 => 'Stereoscopic Stereo-Custom', # (provisional in spec as of 2017-10-10)
1013
+ 3 => 'Stereoscopic Stereo-Custom',
1014
+ 4 => 'Stereoscopic Right-Left',
1007
1015
  },
1008
1016
  },
1009
1017
  sv3d => {
@@ -2067,8 +2075,8 @@ my %eeBox2 = (
2067
2075
  ValueConv => 'substr($val, 4)',
2068
2076
  ValueConvInv => '"\0\0\0\x01$val"',
2069
2077
  },
2070
- # hmtp - "\0\0\0\x01" followed by 408 bytes of zero
2071
- # vrin - "\0\0\0\x01" followed by 8 bytes of zero
2078
+ # hmtp - 412 bytes: "\0\0\0\x01" then maybe "\0\0\0\x64" and the rest zeros
2079
+ # vrin - 12 bytes: "\0\0\0\x01" followed by 8 bytes of zero
2072
2080
  # ---- GoPro ---- (ref PH)
2073
2081
  GoPr => 'GoProType', # (Hero3+)
2074
2082
  FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
@@ -2864,6 +2872,25 @@ my %eeBox2 = (
2864
2872
  Name => 'AV1Configuration',
2865
2873
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
2866
2874
  },
2875
+ clli => {
2876
+ Name => 'ContentLightLevel',
2877
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ContentLightLevel' },
2878
+ },
2879
+ # ref https://nokiatech.github.io/heif/technical.html
2880
+ # cclv - Content Color Volume
2881
+ # mdcv - Mastering Display Color Volume
2882
+ # rrtp - Required reference types
2883
+ # crtt - Creation time information
2884
+ # mdft - Modification time information
2885
+ # udes - User description
2886
+ # altt - Accessibility text
2887
+ # aebr - Auto exposure information
2888
+ # wbbr - White balance information
2889
+ # fobr - Focus information
2890
+ # afbr - Flash exposure information
2891
+ # dobr - Depth of field information
2892
+ # pano - Panorama information
2893
+ # iscl - Image Scaling
2867
2894
  );
2868
2895
 
2869
2896
  # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
@@ -2884,7 +2911,7 @@ my %eeBox2 = (
2884
2911
  7 => 'SMPTE 240',
2885
2912
  8 => 'Generic film (color filters using illuminant C)',
2886
2913
  9 => 'BT.2020, BT.2100',
2887
- 10 => 'SMPTE 428 (CIE 1921 XYZ)',
2914
+ 10 => 'SMPTE 428 (CIE 1931 XYZ)', #forum14766
2888
2915
  11 => 'SMPTE RP 431-2',
2889
2916
  12 => 'SMPTE EG 432-1',
2890
2917
  22 => 'EBU Tech. 3213-E',
@@ -2898,8 +2925,8 @@ my %eeBox2 = (
2898
2925
  1 => 'BT.709',
2899
2926
  2 => 'Unspecified',
2900
2927
  3 => 'For future use (3)',
2901
- 4 => 'BT.470 System M (historical)',
2902
- 5 => 'BT.470 System B, G (historical)',
2928
+ 4 => 'BT.470 System M (historical)', # Gamma 2.2? (ref forum14960)
2929
+ 5 => 'BT.470 System B, G (historical)', # Gamma 2.8? (ref forum14960)
2903
2930
  6 => 'BT.601',
2904
2931
  7 => 'SMPTE 240 M',
2905
2932
  8 => 'Linear',
@@ -3125,6 +3152,16 @@ my %eeBox2 = (
3125
3152
  },
3126
3153
  );
3127
3154
 
3155
+ # ref https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/MPEG4Writer.cpp
3156
+ %Image::ExifTool::QuickTime::ContentLightLevel = (
3157
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3158
+ GROUPS => { 2 => 'Video' },
3159
+ FIRST_ENTRY => 0,
3160
+ FORMAT => 'int16u',
3161
+ 0 => 'MaxContentLightLevel',
3162
+ 1 => 'MaxPicAverageLightLevel',
3163
+ );
3164
+
3128
3165
  %Image::ExifTool::QuickTime::ItemRef = (
3129
3166
  PROCESS_PROC => \&ProcessMOV,
3130
3167
  WRITE_PROC => \&WriteQuickTime,
@@ -3360,8 +3397,9 @@ my %eeBox2 = (
3360
3397
  },
3361
3398
  albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3362
3399
  apID => 'AppleStoreAccount',
3363
- atID => { #10 (or TV series)
3364
- Name => 'AlbumTitleID',
3400
+ atID => {
3401
+ # (ref 10 called this AlbumTitleID or TVSeries)
3402
+ Name => 'ArtistID', #28 (or Track ID ref https://gist.github.com/maf654321/2b44c7b15d798f0c52ee)
3365
3403
  Format => 'int32u',
3366
3404
  Writable => 'int32s', #27
3367
3405
  },
@@ -3372,6 +3410,7 @@ my %eeBox2 = (
3372
3410
  Format => 'int32u',
3373
3411
  Writable => 'int32s', #27
3374
3412
  },
3413
+ cmID => 'ComposerID', #28 (need sample to get format)
3375
3414
  cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3376
3415
  dscp => { Name => 'Description', Avoid => 1 },
3377
3416
  desc => { Name => 'Description', Avoid => 1 }, #7
@@ -6064,10 +6103,10 @@ my %eeBox2 = (
6064
6103
  PrintConv => { 0 => 'No', 1 => 'Yes' },
6065
6104
  },
6066
6105
  perf => 'Performer',
6067
- plID => { #10 (or TV season)
6068
- Name => 'PlayListID',
6069
- Format => 'int8u', # actually int64u, but split it up
6070
- Count => 8,
6106
+ plID => {
6107
+ # (ref 10 called this PlayListID or TVSeason)
6108
+ Name => 'AlbumID', #28
6109
+ Format => 'int64u',
6071
6110
  Writable => 'int32s', #27
6072
6111
  },
6073
6112
  purd => 'PurchaseDate', #7
@@ -6529,6 +6568,7 @@ my %eeBox2 = (
6529
6568
  'rating.user' => 'UserRating', # (Canon ELPH 510 HS)
6530
6569
  'collection.user' => 'UserCollection', #22
6531
6570
  'Encoded_With' => 'EncodedWith',
6571
+ 'content.identifier' => 'ContentIdentifier', #forum14874
6532
6572
  #
6533
6573
  # the following tags aren't in the com.apple.quicktime namespace:
6534
6574
  #
@@ -7184,7 +7224,7 @@ my %eeBox2 = (
7184
7224
  #
7185
7225
  # AudioFormat Offset Child atoms
7186
7226
  # ----------- ------ ----------------
7187
- # mp4a 52 * wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max)
7227
+ # mp4a 52 * wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max and Garmin VIRB 360)
7188
7228
  # in24 52 wave, chan
7189
7229
  # "ms\0\x11" 52 wave
7190
7230
  # sowt 52 chan
@@ -7217,11 +7257,14 @@ my %eeBox2 = (
7217
7257
  chan => {
7218
7258
  Name => 'AudioChannelLayout',
7219
7259
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7220
- }
7260
+ },
7261
+ SA3D => { # written by Garmin VIRB360
7262
+ Name => 'SpatialAudio',
7263
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SpatialAudio' },
7264
+ },
7221
7265
  # alac - 28 bytes
7222
7266
  # adrm - AAX DRM atom? 148 bytes
7223
7267
  # aabd - AAX unknown 17kB (contains 'aavd' strings)
7224
- # SA3D - written by Garmin VIRB360
7225
7268
  );
7226
7269
 
7227
7270
  # AMR decode config box (ref 3)
@@ -7569,6 +7612,20 @@ my %eeBox2 = (
7569
7612
  # (arbitrarily decode only first 8 channels)
7570
7613
  );
7571
7614
 
7615
+ # spatial audio (ref https://github.com/google/spatial-media/blob/master/docs/spatial-audio-rfc.md)
7616
+ %Image::ExifTool::QuickTime::SpatialAudio = (
7617
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7618
+ GROUPS => { 2 => 'Audio' },
7619
+ NOTES => 'Spatial Audio tags.',
7620
+ 0 => 'SpatialAudioVersion',
7621
+ 1 => { Name => 'AmbisonicType', PrintConv => { 0 => 'Periphonic' } },
7622
+ 2 => { Name => 'AmbisonicOrder', Format => 'int32u' },
7623
+ 6 => { Name => 'AmbisonicChannelOrdering', PrintConv => { 0 => 'ACN' } },
7624
+ 7 => { Name => 'AmbisonicNormalization', PrintConv => { 0 => 'SN3D' } },
7625
+ 8 => { Name => 'AmbisonicChannels', Format => 'int32u' },
7626
+ 12 => { Name => 'AmbisonicChannelMap', Format => 'int32u[$val{8}]' },
7627
+ );
7628
+
7572
7629
  # scheme type atom
7573
7630
  # ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
7574
7631
  %Image::ExifTool::QuickTime::SchemeType = (
@@ -8784,15 +8841,15 @@ sub HandleItemInfo($)
8784
8841
  $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
8785
8842
  }
8786
8843
  }
8787
- # do MD5 checksum of AVIF "av01" image data
8788
- if ($type eq 'av01' and $$et{ImageDataMD5}) {
8789
- my $md5 = $$et{ImageDataMD5};
8844
+ # do hash of AVIF "av01" and HEIC image data
8845
+ if ($isImageData{$type} and $$et{ImageDataHash}) {
8846
+ my $hash = $$et{ImageDataHash};
8790
8847
  my $tot = 0;
8791
8848
  foreach $extent (@{$$item{Extents}}) {
8792
- $raf->Seek($$extent[1] + $base, 0) or $et->Warn('Seek error in av01 image data'), last;
8793
- $tot += $et->ImageDataMD5($raf, $$extent[2], 'av01 image', 1);
8849
+ $raf->Seek($$extent[1] + $base, 0) or $et->Warn("Seek error in $type image data"), last;
8850
+ $tot += $et->ImageDataHash($raf, $$extent[2], "$type image", 1);
8794
8851
  }
8795
- $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of av01 data)\n") if $tot;
8852
+ $et->VPrint(0, "$$et{INDENT}(ImageDataHash: $tot bytes of $type data)\n") if $tot;
8796
8853
  }
8797
8854
  next unless $name;
8798
8855
  # assemble the data for this item
@@ -9282,6 +9339,9 @@ sub ProcessMOV($$;$)
9282
9339
  }
9283
9340
  }
9284
9341
  $fileType or $fileType = 'MP4'; # default to MP4
9342
+ # set file type from extension if appropriate
9343
+ my $ext = $$et{FILE_EXT};
9344
+ $fileType = $ext if $ext and $useExt{$ext} and $fileType eq $useExt{$ext};
9285
9345
  $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
9286
9346
  # temporarily set ExtractEmbedded option for CRX files
9287
9347
  $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
@@ -9296,8 +9356,8 @@ sub ProcessMOV($$;$)
9296
9356
  $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9297
9357
 
9298
9358
  my $ee = $$et{OPTIONS}{ExtractEmbedded};
9299
- my $md5 = $$et{ImageDataMD5};
9300
- if ($ee or $md5) {
9359
+ my $hash = $$et{ImageDataHash};
9360
+ if ($ee or $hash) {
9301
9361
  $unkOpt = $$et{OPTIONS}{Unknown};
9302
9362
  require 'Image/ExifTool/QuickTimeStream.pl';
9303
9363
  }
@@ -9379,7 +9439,7 @@ sub ProcessMOV($$;$)
9379
9439
  # set flag to store additional information for ExtractEmbedded option
9380
9440
  my $handlerType = $$et{HandlerType};
9381
9441
  if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
9382
- if ($ee or $md5) {
9442
+ if ($ee or $hash) {
9383
9443
  # (there is another 'gps ' box with a track log that doesn't contain offsets)
9384
9444
  if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
9385
9445
  $eeTag = 1;
@@ -9391,7 +9451,7 @@ sub ProcessMOV($$;$)
9391
9451
  } elsif ($ee and $ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
9392
9452
  $eeTag = 1;
9393
9453
  $$et{OPTIONS}{Unknown} = 1;
9394
- } elsif ($md5 and $md5Box{$handlerType} and $md5Box{$handlerType}{$tag}) {
9454
+ } elsif ($hash and $hashBox{$handlerType} and $hashBox{$handlerType}{$tag}) {
9395
9455
  $eeTag = 1;
9396
9456
  $$et{OPTIONS}{Unknown} = 1;
9397
9457
  }
@@ -9463,13 +9523,17 @@ sub ProcessMOV($$;$)
9463
9523
  my $items = $$et{ItemInfo};
9464
9524
  my ($id, $prop, $docNum, $lowest);
9465
9525
  my $primary = $$et{PrimaryItem} || 0;
9466
- ItemID: foreach $id (keys %$items) {
9526
+ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
9467
9527
  next unless $$items{$id}{Association};
9468
9528
  my $item = $$items{$id};
9469
9529
  foreach $prop (@{$$item{Association}}) {
9470
9530
  next unless $prop == $index;
9471
9531
  if ($id == $primary or (not $dontInherit{$tag} and
9472
- (not $$item{RefersTo} or $$item{RefersTo}{$primary})))
9532
+ (($$item{RefersTo} and $$item{RefersTo}{$primary}) or
9533
+ # hack: assume Item 1 is from the main image (eg. hvc1 data)
9534
+ # to hack the case where the primary item (ie. main image)
9535
+ # doesn't directly reference this property
9536
+ (not $$item{RefersTo} and $id == 1))))
9473
9537
  {
9474
9538
  # this is associated with the primary item or an item describing
9475
9539
  # the primary item, so consider this part of the main document
@@ -9480,7 +9544,7 @@ ItemID: foreach $id (keys %$items) {
9480
9544
  # this property is already associated with an item that has
9481
9545
  # an ExifTool document number, so use the lowest associated DocNum
9482
9546
  $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
9483
- } elsif (not defined $lowest or $lowest > $id) {
9547
+ } else {
9484
9548
  # keep track of the lowest associated item ID
9485
9549
  $lowest = $id;
9486
9550
  }
@@ -9626,7 +9690,7 @@ ItemID: foreach $id (keys %$items) {
9626
9690
  }
9627
9691
  if ($tag eq 'stbl') {
9628
9692
  # process sample data when exiting SampleTable box if extracting embedded
9629
- ProcessSamples($et) if $ee or $md5;
9693
+ ProcessSamples($et) if $ee or $hash;
9630
9694
  } elsif ($tag eq 'minf') {
9631
9695
  $$et{HandlerType} = ''; # reset handler type at end of media info box
9632
9696
  }