exiftool-vendored.pl 12.62.0 → 12.67.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 (56) hide show
  1. package/bin/Changes +96 -1
  2. package/bin/MANIFEST +4 -0
  3. package/bin/META.json +5 -3
  4. package/bin/META.yml +5 -3
  5. package/bin/Makefile.PL +7 -1
  6. package/bin/README +50 -46
  7. package/bin/config_files/guano.config +161 -0
  8. package/bin/exiftool +116 -79
  9. package/bin/lib/Image/ExifTool/7Z.pm +793 -0
  10. package/bin/lib/Image/ExifTool/Apple.pm +19 -8
  11. package/bin/lib/Image/ExifTool/BigTIFF.pm +8 -1
  12. package/bin/lib/Image/ExifTool/Canon.pm +33 -12
  13. package/bin/lib/Image/ExifTool/CanonRaw.pm +4 -4
  14. package/bin/lib/Image/ExifTool/CanonVRD.pm +4 -1
  15. package/bin/lib/Image/ExifTool/Exif.pm +31 -14
  16. package/bin/lib/Image/ExifTool/FlashPix.pm +8 -2
  17. package/bin/lib/Image/ExifTool/FujiFilm.pm +6 -3
  18. package/bin/lib/Image/ExifTool/GPS.pm +5 -2
  19. package/bin/lib/Image/ExifTool/Geotag.pm +5 -2
  20. package/bin/lib/Image/ExifTool/Jpeg2000.pm +226 -28
  21. package/bin/lib/Image/ExifTool/Lang/fr.pm +1467 -202
  22. package/bin/lib/Image/ExifTool/MPF.pm +2 -1
  23. package/bin/lib/Image/ExifTool/Matroska.pm +16 -1
  24. package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
  25. package/bin/lib/Image/ExifTool/Nikon.pm +948 -31
  26. package/bin/lib/Image/ExifTool/NikonCustom.pm +874 -63
  27. package/bin/lib/Image/ExifTool/PDF.pm +23 -5
  28. package/bin/lib/Image/ExifTool/PLIST.pm +8 -1
  29. package/bin/lib/Image/ExifTool/PLUS.pm +19 -4
  30. package/bin/lib/Image/ExifTool/PNG.pm +6 -6
  31. package/bin/lib/Image/ExifTool/Pentax.pm +3 -1
  32. package/bin/lib/Image/ExifTool/PhaseOne.pm +5 -5
  33. package/bin/lib/Image/ExifTool/QuickTime.pm +91 -30
  34. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +20 -19
  35. package/bin/lib/Image/ExifTool/README +2 -2
  36. package/bin/lib/Image/ExifTool/RIFF.pm +11 -9
  37. package/bin/lib/Image/ExifTool/Samsung.pm +227 -227
  38. package/bin/lib/Image/ExifTool/Shortcuts.pm +2 -1
  39. package/bin/lib/Image/ExifTool/SigmaRaw.pm +4 -4
  40. package/bin/lib/Image/ExifTool/Sony.pm +229 -30
  41. package/bin/lib/Image/ExifTool/TagLookup.pm +4758 -4633
  42. package/bin/lib/Image/ExifTool/TagNames.pod +715 -23
  43. package/bin/lib/Image/ExifTool/Validate.pm +17 -1
  44. package/bin/lib/Image/ExifTool/WriteExif.pl +9 -7
  45. package/bin/lib/Image/ExifTool/WriteQuickTime.pl +21 -9
  46. package/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
  47. package/bin/lib/Image/ExifTool/Writer.pl +36 -12
  48. package/bin/lib/Image/ExifTool/XMP.pm +14 -2
  49. package/bin/lib/Image/ExifTool/XMP2.pl +33 -1
  50. package/bin/lib/Image/ExifTool/XMPStruct.pl +96 -28
  51. package/bin/lib/Image/ExifTool/ZIP.pm +5 -5
  52. package/bin/lib/Image/ExifTool.pm +184 -132
  53. package/bin/lib/Image/ExifTool.pod +124 -58
  54. package/bin/perl-Image-ExifTool.spec +44 -44
  55. package/bin/pp_build_exe.args +7 -4
  56. 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.56';
24
+ $VERSION = '1.57';
25
25
 
26
26
  sub FetchObject($$$$);
27
27
  sub ExtractObject($$;$$);
@@ -109,7 +109,18 @@ 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 => {
@@ -1754,6 +1765,7 @@ sub ExpandArray($)
1754
1765
  # 4) nesting depth, 5) dictionary capture type
1755
1766
  sub ProcessDict($$$$;$$)
1756
1767
  {
1768
+ local $_;
1757
1769
  my ($et, $tagTablePtr, $dict, $xref, $nesting, $type) = @_;
1758
1770
  my $verbose = $et->Options('Verbose');
1759
1771
  my $unknown = $$tagTablePtr{EXTRACT_UNKNOWN};
@@ -2017,10 +2029,16 @@ sub ProcessDict($$$$;$$)
2017
2029
  }
2018
2030
  if ($$tagInfo{List} and not $$et{OPTIONS}{NoPDFList}) {
2019
2031
  # separate tokens in comma or whitespace delimited lists
2020
- my @values = ($val =~ /,/) ? split /,+\s*/, $val : split ' ', $val;
2021
- foreach $val (@values) {
2022
- $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 = ' ';
2023
2039
  }
2040
+ my @values = split $split, $val;
2041
+ $et->FoundTag($tagInfo, $_) foreach @values;
2024
2042
  } else {
2025
2043
  # a simple tag value
2026
2044
  $et->FoundTag($tagInfo, $val);
@@ -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';
@@ -14,7 +14,7 @@ use strict;
14
14
  use vars qw($VERSION);
15
15
  use Image::ExifTool::XMP;
16
16
 
17
- $VERSION = '1.00';
17
+ $VERSION = '1.02';
18
18
 
19
19
  sub ValidateMediaSummary($);
20
20
 
@@ -2311,12 +2311,13 @@ my %mediaMatrix = (
2311
2311
  GROUPS => { 0 => 'XMP', 1 => 'XMP-plus', 2 => 'Author' },
2312
2312
  NAMESPACE => 'plus',
2313
2313
  NOTES => q{
2314
- PLUS (Picture Licensing Universal System) License Data Format 1.2.1 XMP
2314
+ PLUS (Picture Licensing Universal System) License Data Format 2.0.1 XMP
2315
2315
  tags. Note that all controlled-vocabulary tags in this table (ie. tags with
2316
2316
  a fixed set of values) have raw values which begin with
2317
2317
  "http://ns.useplus.org/ldf/vocab/", but to reduce clutter this prefix has
2318
- been removed from the values shown below. See L<http://ns.useplus.org/> for
2319
- the complete specification.
2318
+ been removed from the values shown below, and from the values read and
2319
+ written with the -n option. See L<http://ns.useplus.org/> for the complete
2320
+ specification.
2320
2321
  },
2321
2322
  Version => { Name => 'PLUSVersion' },
2322
2323
  Licensee => {
@@ -2529,6 +2530,20 @@ my %mediaMatrix = (
2529
2530
  Custom8 => { List => 'Bag', Writable => 'lang-alt' },
2530
2531
  Custom9 => { List => 'Bag', Writable => 'lang-alt' },
2531
2532
  Custom10 => { List => 'Bag', Writable => 'lang-alt' },
2533
+ DataMining => {
2534
+ %plusVocab,
2535
+ PrintConv => {
2536
+ 'DMI-UNSPECIFIED' => 'Unspecified - no prohibition defined',
2537
+ 'DMI-ALLOWED' => 'Allowed',
2538
+ 'DMI-PROHIBITED-AIMLTRAINING' => 'Prohibited for AI/ML training',
2539
+ 'DMI-PROHIBITED-GENAIMLTRAINING' => 'Prohibited for Generative AI/ML training',
2540
+ 'DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING' => 'Prohibited except for search engine indexing',
2541
+ 'DMI-PROHIBITED' => 'Prohibited',
2542
+ 'DMI-PROHIBITED-SEECONSTRAINT' => 'Prohibited, see plus:OtherConstraints',
2543
+ 'DMI-PROHIBITED-SEEEMBEDDEDRIGHTSEXPR' => 'Prohibited, see iptcExt:EmbdEncRightsExpr',
2544
+ 'DMI-PROHIBITED-SEELINKEDRIGHTSEXPR' => 'Prohibited, see iptcExt:LinkedEncRightsExpr',
2545
+ },
2546
+ },
2532
2547
  );
2533
2548
 
2534
2549
  #------------------------------------------------------------------------------
@@ -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);
@@ -58,7 +58,7 @@ use Image::ExifTool::Exif;
58
58
  use Image::ExifTool::GPS;
59
59
  use Image::ExifTool::HP;
60
60
 
61
- $VERSION = '3.42';
61
+ $VERSION = '3.43';
62
62
 
63
63
  sub CryptShutterCount($$);
64
64
  sub PrintFilter($$$);
@@ -175,6 +175,7 @@ sub DecodeAFPoints($$$$;$);
175
175
  '4 2' => 'smc PENTAX-FA 80-320mm F4.5-5.6',
176
176
  '4 3' => 'smc PENTAX-FA 43mm F1.9 Limited',
177
177
  '4 6' => 'smc PENTAX-FA 35-80mm F4-5.6',
178
+ '4 7' => 'Irix 45mm F1.4', #27
178
179
  '4 8' => 'Irix 150mm F2.8 Macro', #exiv2 issue 1084
179
180
  '4 9' => 'Irix 11mm F4 Firefly', #27
180
181
  '4 10' => 'Irix 15mm F2.4', #27
@@ -265,6 +266,7 @@ sub DecodeAFPoints($$$$;$);
265
266
  '6 14' => 'smc PENTAX-FA* Macro 200mm F4 ED[IF]',
266
267
  '7 0' => 'smc PENTAX-DA 21mm F3.2 AL Limited', #13
267
268
  '7 58' => 'smc PENTAX-D FA Macro 100mm F2.8 WR', #PH - this bit of information cost me $600 ;)
269
+ # '7 58' also 'HD PENTAX-D FA MACRO 100mm F2.8 ED AW' (ref 27)
268
270
  '7 75' => 'Tamron SP AF 70-200mm F2.8 Di LD [IF] Macro (A001)', #(Anton Bondar)
269
271
  '7 201' => 'smc Pentax-DA L 50-200mm F4-5.6 ED WR', #(Bruce Rusk)
270
272
  '7 202' => 'smc PENTAX-DA L 18-55mm F3.5-5.6 AL WR', #29
@@ -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.85';
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
@@ -1006,7 +1010,8 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
1006
1010
  0 => 'Monoscopic',
1007
1011
  1 => 'Stereoscopic Top-Bottom',
1008
1012
  2 => 'Stereoscopic Left-Right',
1009
- 3 => 'Stereoscopic Stereo-Custom', # (provisional in spec as of 2017-10-10)
1013
+ 3 => 'Stereoscopic Stereo-Custom',
1014
+ 4 => 'Stereoscopic Right-Left',
1010
1015
  },
1011
1016
  },
1012
1017
  sv3d => {
@@ -2070,8 +2075,8 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2070
2075
  ValueConv => 'substr($val, 4)',
2071
2076
  ValueConvInv => '"\0\0\0\x01$val"',
2072
2077
  },
2073
- # hmtp - "\0\0\0\x01" followed by 408 bytes of zero
2074
- # 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
2075
2080
  # ---- GoPro ---- (ref PH)
2076
2081
  GoPr => 'GoProType', # (Hero3+)
2077
2082
  FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
@@ -2867,6 +2872,25 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2867
2872
  Name => 'AV1Configuration',
2868
2873
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
2869
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
2870
2894
  );
2871
2895
 
2872
2896
  # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
@@ -2901,8 +2925,8 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2901
2925
  1 => 'BT.709',
2902
2926
  2 => 'Unspecified',
2903
2927
  3 => 'For future use (3)',
2904
- 4 => 'BT.470 System M (historical)',
2905
- 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)
2906
2930
  6 => 'BT.601',
2907
2931
  7 => 'SMPTE 240 M',
2908
2932
  8 => 'Linear',
@@ -3128,6 +3152,16 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
3128
3152
  },
3129
3153
  );
3130
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
+
3131
3165
  %Image::ExifTool::QuickTime::ItemRef = (
3132
3166
  PROCESS_PROC => \&ProcessMOV,
3133
3167
  WRITE_PROC => \&WriteQuickTime,
@@ -3363,8 +3397,9 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
3363
3397
  },
3364
3398
  albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3365
3399
  apID => 'AppleStoreAccount',
3366
- atID => { #10 (or TV series)
3367
- 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)
3368
3403
  Format => 'int32u',
3369
3404
  Writable => 'int32s', #27
3370
3405
  },
@@ -3375,6 +3410,7 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
3375
3410
  Format => 'int32u',
3376
3411
  Writable => 'int32s', #27
3377
3412
  },
3413
+ cmID => 'ComposerID', #28 (need sample to get format)
3378
3414
  cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3379
3415
  dscp => { Name => 'Description', Avoid => 1 },
3380
3416
  desc => { Name => 'Description', Avoid => 1 }, #7
@@ -6067,10 +6103,10 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
6067
6103
  PrintConv => { 0 => 'No', 1 => 'Yes' },
6068
6104
  },
6069
6105
  perf => 'Performer',
6070
- plID => { #10 (or TV season)
6071
- Name => 'PlayListID',
6072
- Format => 'int8u', # actually int64u, but split it up
6073
- Count => 8,
6106
+ plID => {
6107
+ # (ref 10 called this PlayListID or TVSeason)
6108
+ Name => 'AlbumID', #28
6109
+ Format => 'int64u',
6074
6110
  Writable => 'int32s', #27
6075
6111
  },
6076
6112
  purd => 'PurchaseDate', #7
@@ -6532,6 +6568,7 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
6532
6568
  'rating.user' => 'UserRating', # (Canon ELPH 510 HS)
6533
6569
  'collection.user' => 'UserCollection', #22
6534
6570
  'Encoded_With' => 'EncodedWith',
6571
+ 'content.identifier' => 'ContentIdentifier', #forum14874
6535
6572
  #
6536
6573
  # the following tags aren't in the com.apple.quicktime namespace:
6537
6574
  #
@@ -7187,7 +7224,7 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
7187
7224
  #
7188
7225
  # AudioFormat Offset Child atoms
7189
7226
  # ----------- ------ ----------------
7190
- # 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)
7191
7228
  # in24 52 wave, chan
7192
7229
  # "ms\0\x11" 52 wave
7193
7230
  # sowt 52 chan
@@ -7220,11 +7257,14 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
7220
7257
  chan => {
7221
7258
  Name => 'AudioChannelLayout',
7222
7259
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7223
- }
7260
+ },
7261
+ SA3D => { # written by Garmin VIRB360
7262
+ Name => 'SpatialAudio',
7263
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SpatialAudio' },
7264
+ },
7224
7265
  # alac - 28 bytes
7225
7266
  # adrm - AAX DRM atom? 148 bytes
7226
7267
  # aabd - AAX unknown 17kB (contains 'aavd' strings)
7227
- # SA3D - written by Garmin VIRB360
7228
7268
  );
7229
7269
 
7230
7270
  # AMR decode config box (ref 3)
@@ -7572,6 +7612,20 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
7572
7612
  # (arbitrarily decode only first 8 channels)
7573
7613
  );
7574
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
+
7575
7629
  # scheme type atom
7576
7630
  # ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
7577
7631
  %Image::ExifTool::QuickTime::SchemeType = (
@@ -8787,15 +8841,15 @@ sub HandleItemInfo($)
8787
8841
  $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
8788
8842
  }
8789
8843
  }
8790
- # do MD5 checksum of AVIF "av01" and HEIC image data
8791
- if ($isImageData{$type} and $$et{ImageDataMD5}) {
8792
- 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};
8793
8847
  my $tot = 0;
8794
8848
  foreach $extent (@{$$item{Extents}}) {
8795
8849
  $raf->Seek($$extent[1] + $base, 0) or $et->Warn("Seek error in $type image data"), last;
8796
- $tot += $et->ImageDataMD5($raf, $$extent[2], "$type image", 1);
8850
+ $tot += $et->ImageDataHash($raf, $$extent[2], "$type image", 1);
8797
8851
  }
8798
- $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of $type data)\n") if $tot;
8852
+ $et->VPrint(0, "$$et{INDENT}(ImageDataHash: $tot bytes of $type data)\n") if $tot;
8799
8853
  }
8800
8854
  next unless $name;
8801
8855
  # assemble the data for this item
@@ -9285,6 +9339,9 @@ sub ProcessMOV($$;$)
9285
9339
  }
9286
9340
  }
9287
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};
9288
9345
  $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
9289
9346
  # temporarily set ExtractEmbedded option for CRX files
9290
9347
  $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
@@ -9299,8 +9356,8 @@ sub ProcessMOV($$;$)
9299
9356
  $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9300
9357
 
9301
9358
  my $ee = $$et{OPTIONS}{ExtractEmbedded};
9302
- my $md5 = $$et{ImageDataMD5};
9303
- if ($ee or $md5) {
9359
+ my $hash = $$et{ImageDataHash};
9360
+ if ($ee or $hash) {
9304
9361
  $unkOpt = $$et{OPTIONS}{Unknown};
9305
9362
  require 'Image/ExifTool/QuickTimeStream.pl';
9306
9363
  }
@@ -9382,7 +9439,7 @@ sub ProcessMOV($$;$)
9382
9439
  # set flag to store additional information for ExtractEmbedded option
9383
9440
  my $handlerType = $$et{HandlerType};
9384
9441
  if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
9385
- if ($ee or $md5) {
9442
+ if ($ee or $hash) {
9386
9443
  # (there is another 'gps ' box with a track log that doesn't contain offsets)
9387
9444
  if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
9388
9445
  $eeTag = 1;
@@ -9394,7 +9451,7 @@ sub ProcessMOV($$;$)
9394
9451
  } elsif ($ee and $ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
9395
9452
  $eeTag = 1;
9396
9453
  $$et{OPTIONS}{Unknown} = 1;
9397
- } elsif ($md5 and $md5Box{$handlerType} and $md5Box{$handlerType}{$tag}) {
9454
+ } elsif ($hash and $hashBox{$handlerType} and $hashBox{$handlerType}{$tag}) {
9398
9455
  $eeTag = 1;
9399
9456
  $$et{OPTIONS}{Unknown} = 1;
9400
9457
  }
@@ -9466,13 +9523,17 @@ sub ProcessMOV($$;$)
9466
9523
  my $items = $$et{ItemInfo};
9467
9524
  my ($id, $prop, $docNum, $lowest);
9468
9525
  my $primary = $$et{PrimaryItem} || 0;
9469
- ItemID: foreach $id (keys %$items) {
9526
+ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
9470
9527
  next unless $$items{$id}{Association};
9471
9528
  my $item = $$items{$id};
9472
9529
  foreach $prop (@{$$item{Association}}) {
9473
9530
  next unless $prop == $index;
9474
9531
  if ($id == $primary or (not $dontInherit{$tag} and
9475
- (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))))
9476
9537
  {
9477
9538
  # this is associated with the primary item or an item describing
9478
9539
  # the primary item, so consider this part of the main document
@@ -9483,7 +9544,7 @@ ItemID: foreach $id (keys %$items) {
9483
9544
  # this property is already associated with an item that has
9484
9545
  # an ExifTool document number, so use the lowest associated DocNum
9485
9546
  $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
9486
- } elsif (not defined $lowest or $lowest > $id) {
9547
+ } else {
9487
9548
  # keep track of the lowest associated item ID
9488
9549
  $lowest = $id;
9489
9550
  }
@@ -9629,7 +9690,7 @@ ItemID: foreach $id (keys %$items) {
9629
9690
  }
9630
9691
  if ($tag eq 'stbl') {
9631
9692
  # process sample data when exiting SampleTable box if extracting embedded
9632
- ProcessSamples($et) if $ee or $md5;
9693
+ ProcessSamples($et) if $ee or $hash;
9633
9694
  } elsif ($tag eq 'minf') {
9634
9695
  $$et{HandlerType} = ''; # reset handler type at end of media info box
9635
9696
  }
@@ -1128,14 +1128,14 @@ sub Process_text($$$;$)
1128
1128
  # Inputs: 0) ExifTool ref
1129
1129
  # Notes: Also accesses ExifTool RAF*, SET_GROUP1, HandlerType, MetaFormat,
1130
1130
  # ee*, and avcC elements (* = must exist)
1131
- # - may be called either due to ExtractEmbedded option, or ImageDataMD5 requested
1132
- # - MD5 includes only video and audio data
1131
+ # - may be called either due to ExtractEmbedded option, or ImageDataHash requested
1132
+ # - hash includes only video and audio data
1133
1133
  sub ProcessSamples($)
1134
1134
  {
1135
1135
  my $et = shift;
1136
1136
  my ($raf, $ee) = @$et{qw(RAF ee)};
1137
- my ($i, $buff, $pos, $hdrLen, $hdrFmt, @time, @dur, $oldIndent, $md5);
1138
- my ($mdatOffset, $mdatSize); # (for range-checking samples when MD5 is done)
1137
+ my ($i, $buff, $pos, $hdrLen, $hdrFmt, @time, @dur, $oldIndent, $hash);
1138
+ my ($mdatOffset, $mdatSize); # (for range-checking samples when hash is done)
1139
1139
 
1140
1140
  return unless $ee;
1141
1141
  delete $$et{ee}; # use only once
@@ -1144,22 +1144,22 @@ sub ProcessSamples($)
1144
1144
  my $type = $$et{HandlerType} || '';
1145
1145
  if ($type eq 'vide') {
1146
1146
  # only process specific types of video streams
1147
- $md5 = $$et{ImageDataMD5};
1147
+ $hash = $$et{ImageDataHash};
1148
1148
  # only process specific video types if ExtractEmbedded was used
1149
- # (otherwise we are only here to calculate the audio/video MD5)
1149
+ # (otherwise we are only here to calculate the audio/video hash)
1150
1150
  if ($eeOpt) {
1151
1151
  if ($$ee{avcC}) { $type = 'avcC' }
1152
1152
  elsif ($$ee{JPEG}) { $type = 'JPEG' }
1153
- else { return unless $md5 }
1153
+ else { return unless $hash }
1154
1154
  }
1155
1155
  } elsif ($type eq 'soun') {
1156
- $md5 = $$et{ImageDataMD5};
1157
- return unless $md5;
1156
+ $hash = $$et{ImageDataHash};
1157
+ return unless $hash;
1158
1158
  } else {
1159
- return unless $eeOpt; # (don't do MD5 on other types)
1159
+ return unless $eeOpt; # (don't do hash on other types)
1160
1160
  }
1161
1161
 
1162
- my $md5size = 0;
1162
+ my $hashSize = 0;
1163
1163
  my ($start, $size) = @$ee{qw(start size)};
1164
1164
  #
1165
1165
  # determine sample start offsets from chunk offsets (stco) and sample-to-chunk table (stsc),
@@ -1213,7 +1213,7 @@ Sample: for ($i=0; ; ) {
1213
1213
  ++$iChunk;
1214
1214
  }
1215
1215
  @$start == @$size or $et->WarnOnce('Incorrect sample start/size count'), return;
1216
- # process as chunks if we are only interested in calculating MD5
1216
+ # process as chunks if we are only interested in calculating hash
1217
1217
  if ($type eq 'soun' or $type eq 'vide') {
1218
1218
  $start = $stco;
1219
1219
  $size = \@chunkSize;
@@ -1232,7 +1232,7 @@ Sample: for ($i=0; ; ) {
1232
1232
  $oldIndent = $$et{INDENT};
1233
1233
  $$et{INDENT} = '';
1234
1234
  }
1235
- if ($md5) {
1235
+ if ($hash) {
1236
1236
  $mdatSize = $$et{MediaDataSize};
1237
1237
  $mdatOffset = $$et{MediaDataOffset} if defined $mdatSize;
1238
1238
  }
@@ -1249,7 +1249,7 @@ Sample: for ($i=0; ; ) {
1249
1249
  delete $$et{FoundGPSLatitude};
1250
1250
  delete $$et{FoundGPSDateTime};
1251
1251
 
1252
- # range check the sample data for MD5 if necessary
1252
+ # range check the sample data for hash if necessary
1253
1253
  my $size = $$size[$i];
1254
1254
  if (defined $mdatOffset) {
1255
1255
  if ($$start[$i] < $mdatOffset) {
@@ -1268,9 +1268,9 @@ Sample: for ($i=0; ; ) {
1268
1268
  next unless $n;
1269
1269
  $size = $n;
1270
1270
  }
1271
- if ($md5) {
1272
- $md5->add($buff);
1273
- $md5size += length $buff;
1271
+ if ($hash) {
1272
+ $hash->add($buff);
1273
+ $hashSize += length $buff;
1274
1274
  }
1275
1275
  if ($type eq 'avcC') {
1276
1276
  next if length($buff) <= $hdrLen;
@@ -1378,7 +1378,7 @@ Sample: for ($i=0; ; ) {
1378
1378
  DataPos => $$start[$i],
1379
1379
  SampleTime => $time[$i],
1380
1380
  SampleDuration => $dur[$i],
1381
- }, $tagTbl) ;
1381
+ }, $tagTbl);
1382
1382
  }
1383
1383
 
1384
1384
  } elsif ($$tagTbl{$type}) {
@@ -1399,7 +1399,7 @@ Sample: for ($i=0; ; ) {
1399
1399
  }
1400
1400
  if ($verbose) {
1401
1401
  my $str = $type eq 'soun' ? 'Audio' : 'Video';
1402
- $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $md5size bytes of $str data)\n") if $md5size;
1402
+ $et->VPrint(0, "$$et{INDENT}(ImageDataHash: $hashSize bytes of $str data)\n") if $hashSize;
1403
1403
  $$et{INDENT} = $oldIndent;
1404
1404
  $et->VPrint(0, "--------------------------\n");
1405
1405
  }
@@ -2904,6 +2904,7 @@ sub ProcessInsta360($;$)
2904
2904
  # when the language is french? ie. "Ouest"?)
2905
2905
  $a[7] eq 'O'))
2906
2906
  {
2907
+ next if $a[3] eq 'V'; # void fixes don't have N/S E/W
2907
2908
  $et->Warn('Unrecognized INSV GPS format');
2908
2909
  last;
2909
2910
  }