exiftool-vendored.pl 12.56.0 → 12.62.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 (65) hide show
  1. package/bin/Changes +115 -5
  2. package/bin/LICENSE +674 -0
  3. package/bin/MANIFEST +11 -0
  4. package/bin/META.json +1 -1
  5. package/bin/META.yml +1 -1
  6. package/bin/README +45 -44
  7. package/bin/config_files/example.config +1 -0
  8. package/bin/config_files/rotate_regions.config +1 -1
  9. package/bin/exiftool +262 -160
  10. package/bin/lib/Image/ExifTool/AIFF.pm +2 -2
  11. package/bin/lib/Image/ExifTool/APE.pm +2 -2
  12. package/bin/lib/Image/ExifTool/BMP.pm +0 -1
  13. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +23 -19
  14. package/bin/lib/Image/ExifTool/Canon.pm +26 -6
  15. package/bin/lib/Image/ExifTool/CanonRaw.pm +5 -1
  16. package/bin/lib/Image/ExifTool/DJI.pm +28 -2
  17. package/bin/lib/Image/ExifTool/Exif.pm +77 -19
  18. package/bin/lib/Image/ExifTool/FlashPix.pm +33 -10
  19. package/bin/lib/Image/ExifTool/FujiFilm.pm +7 -3
  20. package/bin/lib/Image/ExifTool/GPS.pm +7 -2
  21. package/bin/lib/Image/ExifTool/Geotag.pm +30 -7
  22. package/bin/lib/Image/ExifTool/JPEG.pm +14 -2
  23. package/bin/lib/Image/ExifTool/Jpeg2000.pm +36 -11
  24. package/bin/lib/Image/ExifTool/LIF.pm +10 -2
  25. package/bin/lib/Image/ExifTool/LNK.pm +5 -4
  26. package/bin/lib/Image/ExifTool/MIE.pm +3 -3
  27. package/bin/lib/Image/ExifTool/MPEG.pm +2 -2
  28. package/bin/lib/Image/ExifTool/MakerNotes.pm +3 -2
  29. package/bin/lib/Image/ExifTool/Minolta.pm +6 -7
  30. package/bin/lib/Image/ExifTool/MinoltaRaw.pm +2 -1
  31. package/bin/lib/Image/ExifTool/Nikon.pm +1005 -909
  32. package/bin/lib/Image/ExifTool/NikonCustom.pm +2 -2
  33. package/bin/lib/Image/ExifTool/NikonSettings.pm +1 -1
  34. package/bin/lib/Image/ExifTool/Olympus.pm +88 -6
  35. package/bin/lib/Image/ExifTool/PDF.pm +17 -8
  36. package/bin/lib/Image/ExifTool/PNG.pm +10 -2
  37. package/bin/lib/Image/ExifTool/PanasonicRaw.pm +27 -1
  38. package/bin/lib/Image/ExifTool/Pentax.pm +8 -5
  39. package/bin/lib/Image/ExifTool/PhaseOne.pm +14 -1
  40. package/bin/lib/Image/ExifTool/Photoshop.pm +38 -7
  41. package/bin/lib/Image/ExifTool/QuickTime.pm +48 -14
  42. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +91 -27
  43. package/bin/lib/Image/ExifTool/README +19 -2
  44. package/bin/lib/Image/ExifTool/RIFF.pm +34 -13
  45. package/bin/lib/Image/ExifTool/Rawzor.pm +2 -2
  46. package/bin/lib/Image/ExifTool/Ricoh.pm +2 -1
  47. package/bin/lib/Image/ExifTool/Sigma.pm +5 -4
  48. package/bin/lib/Image/ExifTool/SigmaRaw.pm +9 -3
  49. package/bin/lib/Image/ExifTool/Sony.pm +39 -10
  50. package/bin/lib/Image/ExifTool/TagLookup.pm +4687 -4628
  51. package/bin/lib/Image/ExifTool/TagNames.pod +338 -117
  52. package/bin/lib/Image/ExifTool/Validate.pm +5 -5
  53. package/bin/lib/Image/ExifTool/WPG.pm +296 -0
  54. package/bin/lib/Image/ExifTool/WriteExif.pl +42 -0
  55. package/bin/lib/Image/ExifTool/WritePDF.pl +7 -8
  56. package/bin/lib/Image/ExifTool/WriteXMP.pl +1 -1
  57. package/bin/lib/Image/ExifTool/Writer.pl +162 -40
  58. package/bin/lib/Image/ExifTool/XMP.pm +35 -8
  59. package/bin/lib/Image/ExifTool/XMP2.pl +2 -1
  60. package/bin/lib/Image/ExifTool/ZIP.pm +159 -41
  61. package/bin/lib/Image/ExifTool.pm +286 -65
  62. package/bin/lib/Image/ExifTool.pod +95 -51
  63. package/bin/perl-Image-ExifTool.spec +44 -43
  64. package/bin/pp_build_exe.args +5 -4
  65. package/package.json +3 -3
@@ -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.83';
50
+ $VERSION = '2.85';
51
51
 
52
52
  sub ProcessMOV($$;$);
53
53
  sub ProcessKeys($$$);
@@ -419,6 +419,18 @@ my %channelLabel = (
419
419
  0x1ffff => 'Discrete_65535',
420
420
  );
421
421
 
422
+ my %qtFlags = ( #12
423
+ 0 => 'undef', 22 => 'unsigned int', 71 => 'float[2] size',
424
+ 1 => 'UTF-8', 23 => 'float', 72 => 'float[4] rect',
425
+ 2 => 'UTF-16', 24 => 'double', 74 => 'int64s',
426
+ 3 => 'ShiftJIS', 27 => 'BMP', 75 => 'int8u',
427
+ 4 => 'UTF-8 sort', 28 => 'QT atom', 76 => 'int16u',
428
+ 5 => 'UTF-16 sort', 65 => 'int8s', 77 => 'int32u',
429
+ 13 => 'JPEG', 66 => 'int16s', 78 => 'int64u',
430
+ 14 => 'PNG', 67 => 'int32s', 79 => 'double[3][3]',
431
+ 21 => 'signed int', 70 => 'float[2] point',
432
+ );
433
+
422
434
  # properties which don't get inherited from the parent
423
435
  my %dontInherit = (
424
436
  ispe => 1, # size of parent may be different
@@ -435,15 +447,16 @@ my %dupDirOK = ( ipco => 1, '----' => 1 );
435
447
  my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
436
448
  stsc => 'stbl', stts => 'stbl' );
437
449
 
450
+ # atoms required for generating ImageDataMD5
451
+ my %md5Box = ( vide => { %eeStd }, soun => { %eeStd } );
452
+
438
453
  # boxes and their containers for the various handler types that we want to save
439
454
  # when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
440
455
  # used, but others have been checked against all available sample files and may be
441
456
  # useful in the future if the names are used for different boxes on other locations)
442
457
  my %eeBox = (
443
458
  # (note: vide is only processed if specific atoms exist in the VideoSampleDesc)
444
- vide => { %eeStd,
445
- JPEG => 'stsd',
446
- },
459
+ vide => { %eeStd, JPEG => 'stsd' },
447
460
  text => { %eeStd },
448
461
  meta => { %eeStd },
449
462
  sbtl => { %eeStd },
@@ -457,6 +470,9 @@ my %eeBox2 = (
457
470
  vide => { avcC => 'stsd' }, # (parses H264 video stream)
458
471
  );
459
472
 
473
+ # image types in AVIF and HEIC files
474
+ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
475
+
460
476
  # QuickTime atoms
461
477
  %Image::ExifTool::QuickTime::Main = (
462
478
  PROCESS_PROC => \&ProcessMOV,
@@ -1159,7 +1175,10 @@ my %eeBox2 = (
1159
1175
  },
1160
1176
  {
1161
1177
  Name => 'GarminGPS',
1162
- Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/ and $$self{OPTIONS}{ExtractEmbedded}',
1178
+ Condition => q{
1179
+ $$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/ and
1180
+ $$self{OPTIONS}{ExtractEmbedded}
1181
+ },
1163
1182
  SubDirectory => {
1164
1183
  TagTable => 'Image::ExifTool::QuickTime::Stream',
1165
1184
  ProcessProc => \&ProcessGarminGPS,
@@ -1961,7 +1980,7 @@ my %eeBox2 = (
1961
1980
  Name => 'SanyoMOV',
1962
1981
  Condition => q{
1963
1982
  $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1964
- $self->{VALUE}->{FileType} eq "MOV"
1983
+ $$self{FileType} eq "MOV"
1965
1984
  },
1966
1985
  SubDirectory => {
1967
1986
  TagTable => 'Image::ExifTool::Sanyo::MOV',
@@ -1972,7 +1991,7 @@ my %eeBox2 = (
1972
1991
  Name => 'SanyoMP4',
1973
1992
  Condition => q{
1974
1993
  $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
1975
- $self->{VALUE}->{FileType} eq "MP4"
1994
+ $$self{FileType} eq "MP4"
1976
1995
  },
1977
1996
  SubDirectory => {
1978
1997
  TagTable => 'Image::ExifTool::Sanyo::MP4',
@@ -2868,7 +2887,7 @@ my %eeBox2 = (
2868
2887
  7 => 'SMPTE 240',
2869
2888
  8 => 'Generic film (color filters using illuminant C)',
2870
2889
  9 => 'BT.2020, BT.2100',
2871
- 10 => 'SMPTE 428 (CIE 1921 XYZ)',
2890
+ 10 => 'SMPTE 428 (CIE 1931 XYZ)', #forum14766
2872
2891
  11 => 'SMPTE RP 431-2',
2873
2892
  12 => 'SMPTE EG 432-1',
2874
2893
  22 => 'EBU Tech. 3213-E',
@@ -7146,7 +7165,7 @@ my %eeBox2 = (
7146
7165
  $$self{AudioFormat} = $val;
7147
7166
  return undef unless $val =~ /^[\w ]{4}$/i;
7148
7167
  # check for protected audio format
7149
- $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{VALUE}{FileType} eq 'M4A';
7168
+ $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{FileType} eq 'M4A';
7150
7169
  return $val;
7151
7170
  },
7152
7171
  # see this link for print conversions (not complete):
@@ -8768,6 +8787,16 @@ sub HandleItemInfo($)
8768
8787
  $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
8769
8788
  }
8770
8789
  }
8790
+ # do MD5 checksum of AVIF "av01" and HEIC image data
8791
+ if ($isImageData{$type} and $$et{ImageDataMD5}) {
8792
+ my $md5 = $$et{ImageDataMD5};
8793
+ my $tot = 0;
8794
+ foreach $extent (@{$$item{Extents}}) {
8795
+ $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);
8797
+ }
8798
+ $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $tot bytes of $type data)\n") if $tot;
8799
+ }
8771
8800
  next unless $name;
8772
8801
  # assemble the data for this item
8773
8802
  undef $buff;
@@ -9270,7 +9299,8 @@ sub ProcessMOV($$;$)
9270
9299
  $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9271
9300
 
9272
9301
  my $ee = $$et{OPTIONS}{ExtractEmbedded};
9273
- if ($ee) {
9302
+ my $md5 = $$et{ImageDataMD5};
9303
+ if ($ee or $md5) {
9274
9304
  $unkOpt = $$et{OPTIONS}{Unknown};
9275
9305
  require 'Image/ExifTool/QuickTimeStream.pl';
9276
9306
  }
@@ -9352,7 +9382,7 @@ sub ProcessMOV($$;$)
9352
9382
  # set flag to store additional information for ExtractEmbedded option
9353
9383
  my $handlerType = $$et{HandlerType};
9354
9384
  if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
9355
- if ($ee) {
9385
+ if ($ee or $md5) {
9356
9386
  # (there is another 'gps ' box with a track log that doesn't contain offsets)
9357
9387
  if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
9358
9388
  $eeTag = 1;
@@ -9364,6 +9394,9 @@ sub ProcessMOV($$;$)
9364
9394
  } elsif ($ee and $ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
9365
9395
  $eeTag = 1;
9366
9396
  $$et{OPTIONS}{Unknown} = 1;
9397
+ } elsif ($md5 and $md5Box{$handlerType} and $md5Box{$handlerType}{$tag}) {
9398
+ $eeTag = 1;
9399
+ $$et{OPTIONS}{Unknown} = 1;
9367
9400
  }
9368
9401
  my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9369
9402
 
@@ -9596,7 +9629,7 @@ ItemID: foreach $id (keys %$items) {
9596
9629
  }
9597
9630
  if ($tag eq 'stbl') {
9598
9631
  # process sample data when exiting SampleTable box if extracting embedded
9599
- ProcessSamples($et) if $ee;
9632
+ ProcessSamples($et) if $ee or $md5;
9600
9633
  } elsif ($tag eq 'minf') {
9601
9634
  $$et{HandlerType} = ''; # reset handler type at end of media info box
9602
9635
  }
@@ -9656,6 +9689,7 @@ ItemID: foreach $id (keys %$items) {
9656
9689
  }
9657
9690
  }
9658
9691
  $langInfo or $langInfo = $tagInfo;
9692
+ my $str = $qtFlags{$flags} ? " ($qtFlags{$flags})" : '';
9659
9693
  $et->VerboseInfo($tag, $langInfo,
9660
9694
  Value => ref $value ? $$value : $value,
9661
9695
  DataPt => \$val,
@@ -9664,7 +9698,7 @@ ItemID: foreach $id (keys %$items) {
9664
9698
  Size => $len,
9665
9699
  Format => $format,
9666
9700
  Index => $index,
9667
- Extra => sprintf(", Type='${type}', Flags=0x%x, Lang=0x%.4x",$flags,$lang),
9701
+ Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
9668
9702
  ) if $verbose;
9669
9703
  # use "Keys" in path instead of ItemList if this was defined by a Keys tag
9670
9704
  my $isKey = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
@@ -9793,7 +9827,7 @@ ItemID: foreach $id (keys %$items) {
9793
9827
  ++$index if defined $index;
9794
9828
  }
9795
9829
  # tweak file type based on track content ("iso*" and "dash" ftyp only)
9796
- if ($topLevel and $$et{VALUE}{FileType} and $$et{VALUE}{FileType} eq 'MP4' and
9830
+ if ($topLevel and $$et{FileType} and $$et{FileType} eq 'MP4' and
9797
9831
  $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash)/ and
9798
9832
  $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
9799
9833
  {
@@ -28,7 +28,7 @@ sub Process360Fly($$$);
28
28
  sub ProcessFMAS($$$);
29
29
  sub ProcessCAMM($$$);
30
30
 
31
- my $debug; # set to 1 for extra debugging messages
31
+ my $debug; # set to 'tEST' (all caps) for extra debugging messages
32
32
 
33
33
  # QuickTime data types that have ExifTool equivalents
34
34
  # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
@@ -89,6 +89,7 @@ my %insvDataLen = (
89
89
  0x600 => 8, # timestamps (ref 6)
90
90
  0x700 => 53, # GPS
91
91
  # 0x900 => 48, # ? (Insta360 X3)
92
+ # 0xa00 => 5?, # ? (Insta360 ONE RS)
92
93
  # 0xb00 => 10, # ? (Insta360 X3)
93
94
  );
94
95
 
@@ -599,6 +600,8 @@ my %insvLimit = (
599
600
  0x1a => 'Firmware',
600
601
  0x2a => {
601
602
  Name => 'Parameters',
603
+ # (see https://exiftool.org/forum/index.php?msg=78942)
604
+ Notes => 'number of lenses, 6-axis orientation of each lens, raw resolution',
602
605
  ValueConv => '$val =~ tr/_/ /; $val',
603
606
  },
604
607
  );
@@ -1125,23 +1128,38 @@ sub Process_text($$$;$)
1125
1128
  # Inputs: 0) ExifTool ref
1126
1129
  # Notes: Also accesses ExifTool RAF*, SET_GROUP1, HandlerType, MetaFormat,
1127
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
1128
1133
  sub ProcessSamples($)
1129
1134
  {
1130
1135
  my $et = shift;
1131
1136
  my ($raf, $ee) = @$et{qw(RAF ee)};
1132
- my ($i, $buff, $pos, $hdrLen, $hdrFmt, @time, @dur, $oldIndent);
1137
+ my ($i, $buff, $pos, $hdrLen, $hdrFmt, @time, @dur, $oldIndent, $md5);
1138
+ my ($mdatOffset, $mdatSize); # (for range-checking samples when MD5 is done)
1133
1139
 
1134
1140
  return unless $ee;
1135
1141
  delete $$et{ee}; # use only once
1136
1142
 
1137
- # only process specific types of video streams
1143
+ my $eeOpt = $et->Options('ExtractEmbedded');
1138
1144
  my $type = $$et{HandlerType} || '';
1139
1145
  if ($type eq 'vide') {
1140
- if ($$ee{avcC}) { $type = 'avcC' }
1141
- elsif ($$ee{JPEG}) { $type = 'JPEG' }
1142
- else { return }
1146
+ # only process specific types of video streams
1147
+ $md5 = $$et{ImageDataMD5};
1148
+ # only process specific video types if ExtractEmbedded was used
1149
+ # (otherwise we are only here to calculate the audio/video MD5)
1150
+ if ($eeOpt) {
1151
+ if ($$ee{avcC}) { $type = 'avcC' }
1152
+ elsif ($$ee{JPEG}) { $type = 'JPEG' }
1153
+ else { return unless $md5 }
1154
+ }
1155
+ } elsif ($type eq 'soun') {
1156
+ $md5 = $$et{ImageDataMD5};
1157
+ return unless $md5;
1158
+ } else {
1159
+ return unless $eeOpt; # (don't do MD5 on other types)
1143
1160
  }
1144
1161
 
1162
+ my $md5size = 0;
1145
1163
  my ($start, $size) = @$ee{qw(start size)};
1146
1164
  #
1147
1165
  # determine sample start offsets from chunk offsets (stco) and sample-to-chunk table (stsc),
@@ -1160,13 +1178,16 @@ sub ProcessSamples($)
1160
1178
  $timeDelta = shift @$stts;
1161
1179
  }
1162
1180
  my $ts = $$et{MediaTS} || 1;
1181
+ my @chunkSize; # total size of each chunk
1163
1182
  foreach $chunkStart (@$stco) {
1164
1183
  if ($iChunk >= $nextChunk and @$stsc) {
1165
1184
  ($startChunk, $samplesPerChunk, $descIdx) = @{shift @$stsc};
1166
1185
  $nextChunk = $$stsc[0][0] if @$stsc;
1167
1186
  }
1168
1187
  @$size < @$start + $samplesPerChunk and $et->WarnOnce('Sample size error'), last;
1188
+ last unless defined $chunkStart and length $chunkStart;
1169
1189
  my $sampleStart = $chunkStart;
1190
+ my $chunkSize = 0;
1170
1191
  Sample: for ($i=0; ; ) {
1171
1192
  push @$start, $sampleStart;
1172
1193
  if (defined $time) {
@@ -1184,12 +1205,19 @@ Sample: for ($i=0; ; ) {
1184
1205
  --$timeCount;
1185
1206
  }
1186
1207
  # (eventually should use the description indices: $descIdx)
1208
+ $chunkSize += $$size[$#$start];
1187
1209
  last if ++$i >= $samplesPerChunk;
1188
1210
  $sampleStart += $$size[$#$start];
1189
1211
  }
1212
+ push @chunkSize, $chunkSize;
1190
1213
  ++$iChunk;
1191
1214
  }
1192
1215
  @$start == @$size or $et->WarnOnce('Incorrect sample start/size count'), return;
1216
+ # process as chunks if we are only interested in calculating MD5
1217
+ if ($type eq 'soun' or $type eq 'vide') {
1218
+ $start = $stco;
1219
+ $size = \@chunkSize;
1220
+ }
1193
1221
  }
1194
1222
  #
1195
1223
  # extract and parse the sample data
@@ -1204,6 +1232,10 @@ Sample: for ($i=0; ; ) {
1204
1232
  $oldIndent = $$et{INDENT};
1205
1233
  $$et{INDENT} = '';
1206
1234
  }
1235
+ if ($md5) {
1236
+ $mdatSize = $$et{MediaDataSize};
1237
+ $mdatOffset = $$et{MediaDataOffset} if defined $mdatSize;
1238
+ }
1207
1239
  # get required information from avcC box if parsing video data
1208
1240
  if ($type eq 'avcC') {
1209
1241
  $hdrLen = (Get8u(\$$ee{avcC}, 4) & 0x03) + 1;
@@ -1217,10 +1249,29 @@ Sample: for ($i=0; ; ) {
1217
1249
  delete $$et{FoundGPSLatitude};
1218
1250
  delete $$et{FoundGPSDateTime};
1219
1251
 
1220
- # read the sample data
1252
+ # range check the sample data for MD5 if necessary
1221
1253
  my $size = $$size[$i];
1222
- next unless $raf->Seek($$start[$i], 0) and $raf->Read($buff, $size) == $size;
1223
-
1254
+ if (defined $mdatOffset) {
1255
+ if ($$start[$i] < $mdatOffset) {
1256
+ $et->Warn("Sample $i for '${type}' data is before start of mdat");
1257
+ } elsif ($$start[$i] + $size > $mdatOffset + $mdatSize) {
1258
+ $et->Warn("Sample $i for '${type}' data runs off end of mdat");
1259
+ $size = $mdatOffset + $mdatSize - $$start[$i];
1260
+ $size = 0 if $size < 0;
1261
+ }
1262
+ }
1263
+ # read the sample data
1264
+ $raf->Seek($$start[$i], 0) or $et->WarnOnce("Seek error in $type data"), next;
1265
+ my $n = $raf->Read($buff, $size);
1266
+ unless ($n == $size) {
1267
+ $et->WarnOnce("Error reading $type data");
1268
+ next unless $n;
1269
+ $size = $n;
1270
+ }
1271
+ if ($md5) {
1272
+ $md5->add($buff);
1273
+ $md5size += length $buff;
1274
+ }
1224
1275
  if ($type eq 'avcC') {
1225
1276
  next if length($buff) <= $hdrLen;
1226
1277
  # scan through all NAL units and send them to ParseH264Video()
@@ -1347,6 +1398,8 @@ Sample: for ($i=0; ; ) {
1347
1398
  SetGPSDateTime($et, $tagTbl, $time[$i]) if $$et{FoundGPSLatitude} and not $$et{FoundGPSDateTime};
1348
1399
  }
1349
1400
  if ($verbose) {
1401
+ my $str = $type eq 'soun' ? 'Audio' : 'Video';
1402
+ $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: $md5size bytes of $str data)\n") if $md5size;
1350
1403
  $$et{INDENT} = $oldIndent;
1351
1404
  $et->VPrint(0, "--------------------------\n");
1352
1405
  }
@@ -1423,16 +1476,15 @@ sub ProcessFreeGPS($$$)
1423
1476
  $et->VerboseDump(\$buf2);
1424
1477
  }
1425
1478
  # (extract longitude as 9 digits, not 8, ref PH)
1426
- return 0 unless $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})?/s;
1427
- ($yr,$mon,$day,$hr,$min,$sec,$lbl,$latRef,$lat,$lonRef,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8,$9/1e4,$10,$11/1e4,$12);
1428
- if (defined $spd) { # (Azdome)
1429
- $spd += 0; # remove leading 0's
1430
- } elsif ($buf2 =~ /^.{57}([-+]\d{4})(\d{3})/s) { # (EEEkit)
1431
- # $alt = $1 + 0; (doesn't look right for my sample, but the Ambarella A12 text has this)
1432
- $spd = $2 + 0;
1433
- }
1434
- $lbl =~ s/\0.*//s; $lbl =~ s/\s+$//; # truncate at null and remove trailing spaces
1435
- push @xtra, UserLabel => $lbl if length $lbl;
1479
+ if ($buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})?/s) {
1480
+ ($yr,$mon,$day,$hr,$min,$sec,$lbl,$latRef,$lat,$lonRef,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8,$9/1e4,$10,$11/1e4,$12);
1481
+ if (defined $spd) { # (Azdome)
1482
+ $spd += 0; # remove leading 0's
1483
+ } elsif ($buf2 =~ /^.{57}([-+]\d{4})(\d{3})/s) { # (EEEkit)
1484
+ # $alt = $1 + 0; (doesn't look right for my sample, but the Ambarella A12 text has this)
1485
+ $spd = $2 + 0;
1486
+ }
1487
+ }
1436
1488
  # extract accelerometer data (ref PH)
1437
1489
  if ($buf2 =~ /^.{65}(([-+]\d{3})([-+]\d{3})([-+]\d{3})([-+]\d{3})*)/s) {
1438
1490
  $_ = $1;
@@ -1440,7 +1492,15 @@ sub ProcessFreeGPS($$$)
1440
1492
  s/([-+])/ $1/g; s/^ //;
1441
1493
  push @xtra, AccelerometerData => $_;
1442
1494
  } elsif ($buf2 =~ /^.{173}([-+]\d{3})([-+]\d{3})([-+]\d{3})/s) { # (Azdome)
1495
+ # (Adzome may contain acc and date/time/label even if GPS doesn't exist)
1443
1496
  @acc = ($1/100, $2/100, $3/100);
1497
+ if (not defined $yr and $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})/s) {
1498
+ ($yr,$mon,$day,$hr,$min,$sec,$lbl) = ($1,$2,$3,$4,$5,$6,$7);
1499
+ }
1500
+ }
1501
+ if (defined $lbl) {
1502
+ $lbl =~ s/\0.*//s; $lbl =~ s/\s+$//; # truncate at null and remove trailing spaces
1503
+ push @xtra, UserLabel => $lbl if length $lbl;
1444
1504
  }
1445
1505
 
1446
1506
  } elsif ($$dataPt =~ /^.{52}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/s) {
@@ -1488,7 +1548,7 @@ sub ProcessFreeGPS($$$)
1488
1548
  # Kenwood dashcam sometimes stores absolute year and local time
1489
1549
  # (but sometimes year since 2000 and UTC time in same video!)
1490
1550
  require Time::Local;
1491
- my $time = Image::ExifTool::TimeLocal($sec,$min,$hr,$day,$mon-1,$yr-1900);
1551
+ my $time = Image::ExifTool::TimeLocal($sec,$min,$hr,$day,$mon-1,$yr);
1492
1552
  ($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
1493
1553
  $yr += 1900;
1494
1554
  ++$mon;
@@ -1709,8 +1769,6 @@ sub ProcessFreeGPS($$$)
1709
1769
  # save tag values extracted by above code
1710
1770
  #
1711
1771
  FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
1712
- # lat/long are in DDDMM.MMMM format
1713
- ConvertLatLon($lat, $lon) unless $ddd;
1714
1772
  $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
1715
1773
  if (defined $yr) {
1716
1774
  my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
@@ -1719,8 +1777,12 @@ sub ProcessFreeGPS($$$)
1719
1777
  my $time = sprintf('%.2d:%.2d:%sZ',$hr,$min,$sec);
1720
1778
  $et->HandleTag($tagTbl, GPSTimeStamp => $time);
1721
1779
  }
1722
- $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
1723
- $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
1780
+ if (defined $lat) {
1781
+ # lat/long are in DDDMM.MMMM format unless $ddd is set
1782
+ ConvertLatLon($lat, $lon) unless $ddd;
1783
+ $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
1784
+ $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
1785
+ }
1724
1786
  $et->HandleTag($tagTbl, GPSAltitude => $alt) if defined $alt;
1725
1787
  $et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd;
1726
1788
  $et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
@@ -2803,7 +2865,7 @@ sub ProcessInsta360($;$)
2803
2865
  $raf->Read($buff, $len) == $len or last;
2804
2866
  $et->VerboseDump(\$buff) if $verbose > 2;
2805
2867
  if ($dlen) {
2806
- if ($len % $dlen) {
2868
+ if ($len % $dlen and $id != 0x700) { # (have seen one 0x700 record which was expected format but not multiple of 53 bytes)
2807
2869
  $et->Warn(sprintf('Unexpected Insta360 record 0x%x length',$id));
2808
2870
  } elsif ($id == 0x200) {
2809
2871
  $et->FoundTag(PreviewImage => $buff);
@@ -2833,10 +2895,9 @@ sub ProcessInsta360($;$)
2833
2895
  $et->HandleTag($tagTbl, VideoTimeStamp => sprintf('%.3f', Get64u(\$buff, $p) / 1000));
2834
2896
  }
2835
2897
  } elsif ($id == 0x700) {
2836
- for ($p=0; $p<$len; $p+=$dlen) {
2898
+ for ($p=0; $p+$dlen<=$len; $p+=$dlen) {
2837
2899
  my $tmp = substr($buff, $p, $dlen);
2838
2900
  my @a = unpack('VVvaa8aa8aa8a8a8', $tmp);
2839
- next unless $a[3] eq 'A'; # (ignore void fixes)
2840
2901
  unless (($a[5] eq 'N' or $a[5] eq 'S') and # (quick validation)
2841
2902
  ($a[7] eq 'E' or $a[7] eq 'W' or
2842
2903
  # (odd, but I've seen "O" instead of "W". Perhaps
@@ -2846,6 +2907,7 @@ sub ProcessInsta360($;$)
2846
2907
  $et->Warn('Unrecognized INSV GPS format');
2847
2908
  last;
2848
2909
  }
2910
+ next unless $a[3] eq 'A'; # (ignore void fixes)
2849
2911
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2850
2912
  $a[$_] = GetDouble(\$a[$_], 0) foreach 4,6,8,9,10;
2851
2913
  $a[4] = -abs($a[4]) if $a[5] eq 'S'; # (abs just in case it was already signed)
@@ -2924,6 +2986,7 @@ sub ProcessGarminGPS($$$)
2924
2986
  my $epoch = (66 * 365 + 17) * 24 * 3600; # time is relative to Jan 1, 1904
2925
2987
  my $scl = 180 / (32768 * 65536); # scaling factor for lat/lon
2926
2988
  $et->VerboseDir('GarminGPS');
2989
+ $$et{SET_GROUP1} = 'Garmin';
2927
2990
  while ($pos + 20 <= $dataLen) {
2928
2991
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2929
2992
  my $time = Image::ExifTool::ConvertUnixTime(Get32u($dataPt, $pos) - $epoch) . 'Z';
@@ -2938,6 +3001,7 @@ sub ProcessGarminGPS($$$)
2938
3001
  $pos += 20;
2939
3002
  }
2940
3003
  delete $$et{DOC_NUM};
3004
+ delete $$et{SET_GROUP1};
2941
3005
  return 1;
2942
3006
  }
2943
3007
 
@@ -249,6 +249,13 @@ key:
249
249
  within this atom. Currently used only for Canon CNTH atom,
250
250
  which contains garbage after the first contained atom.
251
251
 
252
+ NIKON_OFFSETS [Nikon Encrypted tables only] Pointer to int32u NumberOffsets
253
+ for offset-type encrypted Nikon directories. When set,
254
+ directory and decryption lengths are calculated automatically.
255
+
256
+ ALLOW_REPROCESS Flag to allow reprocessing of another directory at this
257
+ same location in the file, bypassing recursion avoidance test.
258
+
252
259
  DATAMEMBER : BinaryData tables only. A reference to a list of sorted tag ID's
253
260
  which must be extracted as data members when writing. Must also list "var_"
254
261
  format tags and tags with Hook so offsets are properly calculated if the table
@@ -404,6 +411,10 @@ numerical, and generated automatically otherwise.
404
411
 
405
412
  'IsComposite' - flag set for Composite tags
406
413
 
414
+ 'IsImageData' - flag set if this is an image data offset to
415
+ be included in ImageDataMD5 calculation. Must have an
416
+ OffsetPair entry which is the ID of the corresponding size.
417
+
407
418
  'IsOffset' - flag set if the tag represents an offset to some
408
419
  data, and causes value will be adjusted to an absolute file
409
420
  offset. If set to 2, the offset base of the parent directory
@@ -465,6 +476,10 @@ numerical, and generated automatically otherwise.
465
476
  documentation, padded to the number of digits given by the
466
477
  PrintHex value.
467
478
 
479
+ 'PrintInt' - remove decimal part of tag ID in HTML tag name
480
+ documentation. (To avoid confusing ExifTool users because
481
+ the LensType decimal numbers are for internal use only.)
482
+
468
483
  'PrintSort' - causes PrintConv values to be sorted by value
469
484
  rather than key in the HTML tag name documentation.
470
485
 
@@ -713,7 +728,7 @@ numerical, and generated automatically otherwise.
713
728
  BitShift are applied before evaluating RawConv.
714
729
 
715
730
  BitShift : [Mask tags only] Bit shift for Mask-ed values. If not
716
- specified, set to the number of trailing bits in the Mask.
731
+ specified, set to the number of trailing zero bits in Mask.
717
732
  When reading, the value is shifted right by this number of
718
733
  bits after the Mask is applied.
719
734
 
@@ -1003,7 +1018,9 @@ numerical, and generated automatically otherwise.
1003
1018
  current Base. This is a Perl expression which may use
1004
1019
  $valuePtr to represent the location of the tag value in the
1005
1020
  file, or $val for the value itself. If not specified, a Start
1006
- of '$valuePtr' is assumed.
1021
+ of '$valuePtr' is assumed. Subdirectories in BinaryData may
1022
+ also use $dirStart to represent the offset of the current
1023
+ directory start relative to the start of the data block.
1007
1024
 
1008
1025
  OffsetPt : [EXIF directories only] If specified, this is a Perl
1009
1026
  expression that gives the position of a 32-bit word in the
@@ -30,7 +30,7 @@ use strict;
30
30
  use vars qw($VERSION $AUTOLOAD);
31
31
  use Image::ExifTool qw(:DataAccess :Utils);
32
32
 
33
- $VERSION = '1.61';
33
+ $VERSION = '1.64';
34
34
 
35
35
  sub ConvertTimecode($);
36
36
  sub ProcessSGLT($$$);
@@ -38,6 +38,13 @@ sub ProcessSLLT($$$);
38
38
  sub ProcessLucas($$$);
39
39
  sub WriteRIFF($$);
40
40
 
41
+ # RIFF chunks containing image data (to include in ImageDataMD5 digest)
42
+ my %isImageData = (
43
+ LIST_movi => 1, # (AVI: contains ##db, ##dc, ##wb)
44
+ data => 1, # (WAV)
45
+ 'VP8 '=>1, VP8L=>1, ANIM=>1, ANMF=>1, ALPH=>1, # (WebP)
46
+ );
47
+
41
48
  # recognized RIFF variants
42
49
  my %riffType = (
43
50
  'WAVE' => 'WAV', 'AVI ' => 'AVI', 'WEBP' => 'WEBP',
@@ -1522,7 +1529,7 @@ my %code2charset = (
1522
1529
  },
1523
1530
  # (can't calculate duration like this for compressed audio types)
1524
1531
  RawConv => q{
1525
- return undef if $$self{VALUE}{FileType} =~ /^(LA|OFR|PAC|WV)$/;
1532
+ return undef if $$self{FileType} =~ /^(LA|OFR|PAC|WV)$/;
1526
1533
  return(($val[0] and not ($val[2] or $val[3])) ? $val[1] / $val[0] : undef);
1527
1534
  },
1528
1535
  PrintConv => 'ConvertDuration($val)',
@@ -1980,6 +1987,7 @@ sub ProcessRIFF($$)
1980
1987
  my $unknown = $et->Options('Unknown');
1981
1988
  my $validate = $et->Options('Validate');
1982
1989
  my $ee = $et->Options('ExtractEmbedded');
1990
+ my $md5 = $$et{ImageDataMD5};
1983
1991
 
1984
1992
  # verify this is a valid RIFF file
1985
1993
  return 0 unless $raf->Read($buff, 12) == 12;
@@ -1996,7 +2004,7 @@ sub ProcessRIFF($$)
1996
2004
  $$raf{NoBuffer} = 1 if $et->Options('FastScan'); # disable buffering in FastScan mode
1997
2005
  $mime = $riffMimeType{$type} if $type;
1998
2006
  $et->SetFileType($type, $mime);
1999
- $$et{VALUE}{FileType} .= ' (RF64)' if $rf64;
2007
+ $$et{VALUE}{FileType} .= ' (RF64)' if $rf64 and $$et{VALUE}{FileType};
2000
2008
  $$et{RIFFStreamType} = ''; # initialize stream type
2001
2009
  $$et{RIFFStreamCodec} = []; # initialize codec array
2002
2010
  SetByteOrder('II');
@@ -2059,6 +2067,10 @@ sub ProcessRIFF($$)
2059
2067
  # (in LIST_movi chunk: ##db = uncompressed DIB, ##dc = compressed DIB, ##wb = audio data)
2060
2068
  if ($tagInfo or (($verbose or $unknown) and $tag !~ /^(data|idx1|LIST_movi|RIFF|\d{2}(db|dc|wb))$/)) {
2061
2069
  $raf->Read($buff, $len2) == $len2 or $err=1, last;
2070
+ if ($md5 and $isImageData{$tag}) {
2071
+ $md5->add($buff);
2072
+ $et->VPrint(0, "$$et{INDENT}(ImageDataMD5: '${tag}' chunk, $len2 bytes)\n");
2073
+ }
2062
2074
  my $setGroups;
2063
2075
  if ($tagInfo and ref $tagInfo eq 'HASH' and $$tagInfo{SetGroups}) {
2064
2076
  $setGroups = $$et{SET_GROUP0} = $$et{SET_GROUP1} = $$tagInfo{SetGroups};
@@ -2085,18 +2097,27 @@ sub ProcessRIFF($$)
2085
2097
  # extract information from remaining file as an embedded file
2086
2098
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2087
2099
  next; # (must not increment $pos)
2088
- } elsif ($tag eq 'LIST_movi' and $ee) {
2089
- next; # parse into movi chunk
2090
2100
  } else {
2091
- if ($len > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2092
- $et->Warn("Stopped parsing at large $tag chunk (LargeFileSupport not set)");
2093
- last;
2101
+ my $rewind;
2102
+ # do MD5 if required
2103
+ if ($md5 and $isImageData{$tag}) {
2104
+ $rewind = $raf->Tell();
2105
+ $et->ImageDataMD5($raf, $len2, "'${tag}' chunk");
2094
2106
  }
2095
- if ($validate and $len2) {
2096
- # (must actually try to read something after seeking to detect error)
2097
- $raf->Seek($len2-1, 1) and $raf->Read($buff, 1) == 1 or $err = 1, last;
2098
- } else {
2099
- $raf->Seek($len2, 1) or $err=1, last;
2107
+ if ($tag eq 'LIST_movi' and $ee) {
2108
+ $raf->Seek($rewind, 0) or $err = 1, last if $rewind;
2109
+ next; # parse into movi chunk
2110
+ } elsif (not $rewind) {
2111
+ if ($len > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2112
+ $et->Warn("Stopped parsing at large $tag chunk (LargeFileSupport not set)");
2113
+ last;
2114
+ }
2115
+ if ($validate and $len2) {
2116
+ # (must actually try to read something after seeking to detect error)
2117
+ $raf->Seek($len2-1, 1) and $raf->Read($buff, 1) == 1 or $err = 1, last;
2118
+ } else {
2119
+ $raf->Seek($len2, 1) or $err=1, last;
2120
+ }
2100
2121
  }
2101
2122
  }
2102
2123
  $pos += $len2;
@@ -14,7 +14,7 @@ use strict;
14
14
  use vars qw($VERSION);
15
15
  use Image::ExifTool qw(:DataAccess :Utils);
16
16
 
17
- $VERSION = '1.05';
17
+ $VERSION = '1.06';
18
18
 
19
19
  # currently support this version Rawzor images
20
20
  my $implementedRawzorVersion = 199; # (up to version 1.99)
@@ -138,7 +138,7 @@ sub ProcessRWZ($$)
138
138
  }
139
139
  # set OriginalFileType from FileType of original file
140
140
  # then change FileType and MIMEType to indicate a Rawzor image
141
- my $origFileType = $$et{VALUE}{FileType};
141
+ my $origFileType = $$et{FileType};
142
142
  if ($origFileType) {
143
143
  $et->HandleTag($tagTablePtr, OriginalFileType => $origFileType);
144
144
  $et->OverrideFileType('RWZ');
@@ -19,7 +19,7 @@ use vars qw($VERSION);
19
19
  use Image::ExifTool qw(:DataAccess :Utils);
20
20
  use Image::ExifTool::Exif;
21
21
 
22
- $VERSION = '1.35';
22
+ $VERSION = '1.36';
23
23
 
24
24
  sub ProcessRicohText($$$);
25
25
  sub ProcessRicohRMETA($$$);
@@ -949,6 +949,7 @@ sub ProcessRicohText($$$)
949
949
 
950
950
  my $data = substr($$dataPt, $dirStart, $dirLen);
951
951
  return 1 if $data =~ /^\0/; # blank Ricoh maker notes
952
+ $et->VerboseDir('RicohText', undef, $dirLen);
952
953
  # validate text maker notes
953
954
  unless ($data =~ /^(Rev|Rv)/) {
954
955
  $et->Warn('Bad Ricoh maker notes');