exiftool_vendored 12.68.0 → 12.72.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +98 -15
  3. data/bin/MANIFEST +5 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +10 -10
  7. data/bin/exiftool +32 -25
  8. data/bin/lib/Image/ExifTool/AAC.pm +175 -0
  9. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +2 -1
  10. data/bin/lib/Image/ExifTool/CBOR.pm +18 -2
  11. data/bin/lib/Image/ExifTool/Canon.pm +87 -20
  12. data/bin/lib/Image/ExifTool/DJI.pm +3 -2
  13. data/bin/lib/Image/ExifTool/DNG.pm +25 -2
  14. data/bin/lib/Image/ExifTool/EXE.pm +54 -6
  15. data/bin/lib/Image/ExifTool/Exif.pm +204 -22
  16. data/bin/lib/Image/ExifTool/FujiFilm.pm +145 -20
  17. data/bin/lib/Image/ExifTool/GIF.pm +5 -1
  18. data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
  19. data/bin/lib/Image/ExifTool/ID3.pm +76 -10
  20. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  21. data/bin/lib/Image/ExifTool/JPEG.pm +1 -1
  22. data/bin/lib/Image/ExifTool/JSON.pm +4 -1
  23. data/bin/lib/Image/ExifTool/Jpeg2000.pm +30 -15
  24. data/bin/lib/Image/ExifTool/M2TS.pm +21 -16
  25. data/bin/lib/Image/ExifTool/MakerNotes.pm +2 -2
  26. data/bin/lib/Image/ExifTool/Nikon.pm +213 -105
  27. data/bin/lib/Image/ExifTool/Olympus.pm +7 -1
  28. data/bin/lib/Image/ExifTool/PNG.pm +8 -13
  29. data/bin/lib/Image/ExifTool/Panasonic.pm +15 -2
  30. data/bin/lib/Image/ExifTool/Pentax.pm +15 -6
  31. data/bin/lib/Image/ExifTool/PhotoMechanic.pm +2 -2
  32. data/bin/lib/Image/ExifTool/QuickTime.pm +60 -14
  33. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +59 -11
  34. data/bin/lib/Image/ExifTool/README +14 -5
  35. data/bin/lib/Image/ExifTool/RIFF.pm +60 -10
  36. data/bin/lib/Image/ExifTool/Ricoh.pm +109 -1
  37. data/bin/lib/Image/ExifTool/Samsung.pm +3 -2
  38. data/bin/lib/Image/ExifTool/Sony.pm +177 -37
  39. data/bin/lib/Image/ExifTool/TagLookup.pm +6971 -6714
  40. data/bin/lib/Image/ExifTool/TagNames.pod +957 -372
  41. data/bin/lib/Image/ExifTool/Text.pm +4 -5
  42. data/bin/lib/Image/ExifTool/Validate.pm +23 -20
  43. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +2 -2
  44. data/bin/lib/Image/ExifTool/WriteExif.pl +33 -8
  45. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +1 -0
  46. data/bin/lib/Image/ExifTool/WriteRIFF.pl +31 -6
  47. data/bin/lib/Image/ExifTool/Writer.pl +121 -28
  48. data/bin/lib/Image/ExifTool/XMP.pm +67 -2
  49. data/bin/lib/Image/ExifTool/XMP2.pl +35 -0
  50. data/bin/lib/Image/ExifTool.pm +94 -43
  51. data/bin/lib/Image/ExifTool.pod +28 -17
  52. data/bin/perl-Image-ExifTool.spec +9 -9
  53. data/lib/exiftool_vendored/version.rb +1 -1
  54. metadata +3 -2
@@ -37,7 +37,7 @@ use vars qw($VERSION %leicaLensTypes);
37
37
  use Image::ExifTool qw(:DataAccess :Utils);
38
38
  use Image::ExifTool::Exif;
39
39
 
40
- $VERSION = '2.19';
40
+ $VERSION = '2.21';
41
41
 
42
42
  sub ProcessLeicaLEIC($$$);
43
43
  sub WhiteBalanceConv($;$$);
@@ -1991,6 +1991,15 @@ my %shootingMode = (
1991
1991
  },
1992
1992
  PrintConvInv => '$_=$val; tr/A-Z0-9//dc; s/(.{3})(19|20)/$1/; $_',
1993
1993
  },
1994
+ 0x05ff => {
1995
+ Name => 'CameraIFD', # (Leica Q3)
1996
+ Condition => '$$valPt =~ /^(II\x2a\0\x08\0\0\0|MM\0\x2a\0\0\0\x08)/',
1997
+ SubDirectory => {
1998
+ TagTable => 'Image::ExifTool::PanasonicRaw::CameraIFD',
1999
+ Base => '$start',
2000
+ ProcessProc => \&Image::ExifTool::ProcessTIFF,
2001
+ },
2002
+ },
1994
2003
  );
1995
2004
 
1996
2005
  # Leica type5 ShotInfo (ref PH) (X2)
@@ -2834,10 +2843,14 @@ sub ProcessLeicaTrailer($;$)
2834
2843
  my $val = Image::ExifTool::Exif::RebuildMakerNotes($et, \%dirInfo, $tagTablePtr);
2835
2844
  unless (defined $val) {
2836
2845
  $et->Warn('Error rebuilding maker notes (may be corrupt)') if $len > 4;
2837
- $val = $buff,
2846
+ $val = $buff;
2838
2847
  }
2839
2848
  my $key = $et->FoundTag($tagInfo, $val);
2840
2849
  $et->SetGroup($key, 'ExifIFD');
2850
+ if ($$et{MAKER_NOTE_FIXUP}) {
2851
+ $$et{TAG_EXTRA}{$key}{Fixup} = $$et{MAKER_NOTE_FIXUP};
2852
+ delete $$et{MAKER_NOTE_FIXUP};
2853
+ }
2841
2854
  }
2842
2855
  }
2843
2856
  SetByteOrder($oldOrder);
@@ -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.44';
61
+ $VERSION = '3.45';
62
62
 
63
63
  sub CryptShutterCount($$);
64
64
  sub PrintFilter($$$);
@@ -410,7 +410,8 @@ sub DecodeAFPoints($$$$;$);
410
410
  #
411
411
  # Ricoh lenses
412
412
  #
413
- '31 1' => 'GR Lens', #PH (GR III 28mm F2.8)
413
+ '31 1' => '18.3mm F2.8', #PH (GR III built-in)
414
+ '31 4' => '26.1mm F2.8', #PH (GR IIIx built-in)
414
415
  );
415
416
 
416
417
  # Pentax model ID codes - PH
@@ -1141,8 +1142,12 @@ my %binaryDataAttrs = (
1141
1142
  3 => 'Manual',
1142
1143
  4 => 'Super Macro', #JD
1143
1144
  5 => 'Pan Focus',
1144
- # 8 - seen for Ricoh GR III
1145
- # 9 - seen for Ricoh GR III
1145
+ 6 => 'Auto-area', # (GR III)
1146
+ 8 => 'Select', # (GR III)
1147
+ 9 => 'Pinpoint', # (GR III)
1148
+ 10 => 'Tracking', # (GR III)
1149
+ 11 => 'Continuous', # (GR III)
1150
+ 12 => 'Snap', # (GR III)
1146
1151
  16 => 'AF-S (Focus-priority)', #17
1147
1152
  17 => 'AF-C (Focus-priority)', #17
1148
1153
  18 => 'AF-A (Focus-priority)', #PH (educated guess)
@@ -1903,6 +1908,7 @@ my %binaryDataAttrs = (
1903
1908
  '0 28' => 'Quick Macro', # (Q)
1904
1909
  '0 29' => 'Forest', # (Q)
1905
1910
  '0 30' => 'Backlight Silhouette', # (Q)
1911
+ '0 32' => 'DOF', #PH (GR III)
1906
1912
  # AUTO PICT modes (auto-selected)
1907
1913
  '1 4' => 'Auto PICT (Standard)', #13
1908
1914
  '1 5' => 'Auto PICT (Portrait)', #7 (K100D)
@@ -2146,6 +2152,7 @@ my %binaryDataAttrs = (
2146
2152
  10 => 'Cross Processing', #31 (K-70)
2147
2153
  11 => 'Flat', #31 (K-70)
2148
2154
  # 256 - seen for GR III
2155
+ # 257 - seen for GR III
2149
2156
  # 262 - seen for GR III
2150
2157
  32768 => 'n/a',
2151
2158
  },
@@ -2584,8 +2591,10 @@ my %binaryDataAttrs = (
2584
2591
  PrintConv => {
2585
2592
  0 => 'Off',
2586
2593
  1 => 'On',
2587
- '0 2' => 'Off (0 2)', #PH (NC, GR III)
2588
- '1 2' => 'On (1 2)', #PH (NC, GR III)
2594
+ '0 0' => 'Off (Off)', #PH (GR III)
2595
+ '1 1' => 'On (On)', #PH (GR III)
2596
+ '0 2' => 'Off (Auto)', #PH (GR III)
2597
+ '1 2' => 'On (Auto)', #PH (GR III)
2589
2598
  },
2590
2599
  },
2591
2600
  0x008b => { #PH (LS465)
@@ -15,7 +15,7 @@ use Image::ExifTool::Exif;
15
15
  use Image::ExifTool::IPTC;
16
16
  use Image::ExifTool::XMP;
17
17
 
18
- $VERSION = '1.07';
18
+ $VERSION = '1.08';
19
19
 
20
20
  sub ProcessPhotoMechanic($$);
21
21
 
@@ -138,7 +138,7 @@ my %rawCropConv = (
138
138
  ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
139
139
  ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
140
140
  },
141
- CreatorIdentity => { },
141
+ CreatorIdentity => { List => 'Seq' },
142
142
  );
143
143
 
144
144
  #------------------------------------------------------------------------------
@@ -48,7 +48,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
48
48
  use Image::ExifTool::Exif;
49
49
  use Image::ExifTool::GPS;
50
50
 
51
- $VERSION = '2.88';
51
+ $VERSION = '2.91';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -638,6 +638,33 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
638
638
  Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/ and $$self{OPTIONS}{ExtractEmbedded}',
639
639
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Tags360Fly' },
640
640
  },
641
+ { #https://c2pa.org/specifications/
642
+ Name => 'JUMBF',
643
+ Condition => '$$valPt=~/^\xd8\xfe\xc3\xd6\x1b\x0e\x48\x3c\x92\x97\x58\x28\x87\x7e\xc4\x81.{4}manifest\0/s',
644
+ Deletable => 1,
645
+ SubDirectory => {
646
+ TagTable => 'Image::ExifTool::Jpeg2000::Main',
647
+ # 16 bytes uuid
648
+ # +4 bytes 0
649
+ # +9 bytes "manifest\0"
650
+ # +8 bytes absolute(!!!) offset to C2PA uuid "merkle\0" box
651
+ # =37 bytes total
652
+ Start => 37,
653
+ },
654
+ },
655
+ { #https://c2pa.org/specifications/ (NC)
656
+ Name => 'CBOR',
657
+ Condition => '$$valPt=~/^\xd8\xfe\xc3\xd6\x1b\x0e\x48\x3c\x92\x97\x58\x28\x87\x7e\xc4\x81.{4}merkle\0/s',
658
+ Deletable => 1, # (NC)
659
+ SubDirectory => {
660
+ TagTable => 'Image::ExifTool::CBOR::Main',
661
+ # 16 bytes uuid
662
+ # +4 bytes 0
663
+ # +7 bytes "merkle\0"
664
+ # =27 bytes total
665
+ Start => 27,
666
+ },
667
+ },
641
668
  {
642
669
  Name => 'SensorData',
643
670
  Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
@@ -2311,16 +2338,33 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2311
2338
  # RDT9 - only 16-byte header?
2312
2339
  # the boxes below all have a similar header (little-endian):
2313
2340
  # 0 int32u - number of records
2314
- # 4 ? - "1e 00"
2341
+ # 4 int32u - sample rate (Hz)
2315
2342
  # 6 int16u - record length in bytes
2316
- # 8 ? - "23 01 00 00 00 00 00 00"
2317
- # 16 - start of records (each record ends in an int64u timestamp "ts" in ns)
2318
- # RDTA - float[4],ts: "-0.31289672 -0.2245330 11.303817 0 775.780"
2319
- # RDTB - float[4],ts: "-0.04841613 -0.2166595 0.0724792 0 775.780"
2320
- # RDTC - float[4],ts: "27.60925 -27.10037 -13.27285 0 775.829"
2343
+ # 8 int16u - 0x0123 = little-endian, 0x3210 = big endian
2344
+ # 10 int16u[3] - all zeros
2345
+ # 16 - start of records (each record ends in an int64u timestamp "ts" in ns)
2346
+ RDTA => {
2347
+ Name => 'RicohRDTA',
2348
+ SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTA' },
2349
+ },
2350
+ RDTB => {
2351
+ Name => 'RicohRDTB',
2352
+ SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTB' },
2353
+ },
2354
+ RDTC => {
2355
+ Name => 'RicohRDTC',
2356
+ SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTC' },
2357
+ },
2321
2358
  # RDTD - int16s[3],ts: "353 -914 16354 0 775.829"
2322
- # RDTG - ts: "775.825"
2359
+ RDTG => {
2360
+ Name => 'RicohRDTG',
2361
+ SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTG' },
2362
+ },
2323
2363
  # RDTI - float[4],ts: "0.00165951 0.005770059 0.06838259 0.1744695 775.862"
2364
+ RDTL => {
2365
+ Name => 'RicohRDTL',
2366
+ SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTL' },
2367
+ },
2324
2368
  # ---- Samsung ----
2325
2369
  vndr => 'Vendor', #PH (Samsung PL70)
2326
2370
  SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
@@ -2344,7 +2388,7 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2344
2388
  # edli - 52 bytes all zero (Samsung WB30F)
2345
2389
  # @etc - 4 bytes all zero (Samsung WB30F)
2346
2390
  # saut - 4 bytes all zero (Samsung SM-N900T)
2347
- # smrd - string "TRUEBLUE" (Samsung SM-C101)
2391
+ # smrd - string "TRUEBLUE" (Samsung SM-C101, etc)
2348
2392
  # ---- TomTom Bandit Action Cam ----
2349
2393
  TTMD => {
2350
2394
  Name => 'TomTomMetaData',
@@ -2364,7 +2408,7 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
2364
2408
  # opax - 164 bytes unknown (center and affine arrays? ref 26)
2365
2409
  # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2366
2410
  # intv - 16 bytes all zero
2367
- # ---- Xaiomi ----
2411
+ # ---- Xiaomi ----
2368
2412
  mcvr => {
2369
2413
  Name => 'PreviewImage',
2370
2414
  Groups => { 2 => 'Preview' },
@@ -6576,11 +6620,13 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
6576
6620
  'collection.user' => 'UserCollection', #22
6577
6621
  'Encoded_With' => 'EncodedWith',
6578
6622
  'content.identifier' => 'ContentIdentifier', #forum14874
6623
+ 'encoder' => { }, # forum15418 (written by ffmpeg)
6579
6624
  #
6580
6625
  # the following tags aren't in the com.apple.quicktime namespace:
6581
6626
  #
6582
6627
  'com.apple.photos.captureMode' => 'CaptureMode',
6583
6628
  'com.android.version' => 'AndroidVersion',
6629
+ 'com.android.capture.fps' => 'AndroidCaptureFPS',
6584
6630
  #
6585
6631
  # also seen
6586
6632
  #
@@ -9502,9 +9548,9 @@ sub ProcessMOV($$;$)
9502
9548
  $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9503
9549
  }
9504
9550
  # stop processing at mdat/idat if -fast2 is used
9505
- last if $fast > 1 and ($tag eq 'mdat' or $tag eq 'idat');
9551
+ last if $fast > 1 and ($tag eq 'mdat' or ($tag eq 'idat' and $$et{FileType} ne 'HEIC'));
9506
9552
  # load values only if associated with a tag (or verbose) and not too big
9507
- if ($size > 0x2000000) { # start to get worried above 32 MB
9553
+ if ($size > 0x2000000) { # start to get worried above 32 MiB
9508
9554
  # check for RIFF trailer (written by Auto-Vox dashcam)
9509
9555
  if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
9510
9556
  $et->VPrint(0, "Found RIFF trailer");
@@ -9521,9 +9567,9 @@ sub ProcessMOV($$;$)
9521
9567
  if ($tagInfo and not $$tagInfo{Unknown} and not $eeTag) {
9522
9568
  my $t = PrintableTagID($tag,2);
9523
9569
  if ($size > 0x8000000) {
9524
- $et->Warn("Skipping '${t}' atom > 128 MB", 1);
9570
+ $et->Warn("Skipping '${t}' atom > 128 MiB", 1);
9525
9571
  } else {
9526
- $et->Warn("Skipping '${t}' atom > 32 MB", 2) or $ignore = 0;
9572
+ $et->Warn("Skipping '${t}' atom > 32 MiB", 2) or $ignore = 0;
9527
9573
  }
9528
9574
  }
9529
9575
  }
@@ -83,7 +83,8 @@ my %processByMetaFormat = (
83
83
 
84
84
  # data lengths for each INSV/INSP record type
85
85
  my %insvDataLen = (
86
- 0x200 => 0, # PreivewImage (any size) (a duplicate of PreviewImage in APP2 of INSP files)
86
+ 0x000 => 0, # directory table (any size)
87
+ 0x200 => 0, # PreviewImage (any size) (a duplicate of PreviewImage in APP2 of INSP files)
87
88
  0x300 => 0, # accelerometer (could be either 20 or 56 bytes)
88
89
  0x400 => 16, # exposure (ref 6)
89
90
  0x600 => 8, # timestamps (ref 6)
@@ -91,6 +92,8 @@ my %insvDataLen = (
91
92
  # 0x900 => 48, # ? (Insta360 X3)
92
93
  # 0xa00 => 5?, # ? (Insta360 ONE RS)
93
94
  # 0xb00 => 10, # ? (Insta360 X3)
95
+ # 0xd00 => 10, # ? (Insta360 Ace Pro)
96
+ # 0x1200 ? # ? (Insta360 Ace Pro)
94
97
  );
95
98
 
96
99
  # limit the default amount of data we read for some record types
@@ -106,7 +109,7 @@ my %insvLimit = (
106
109
  The tags below are extracted from timed metadata in QuickTime and other
107
110
  formats of video files when the ExtractEmbedded option is used. Although
108
111
  most of these tags are combined into the single table below, ExifTool
109
- currently reads 66 different formats of timed GPS metadata from video files.
112
+ currently reads 67 different formats of timed GPS metadata from video files.
110
113
  },
111
114
  VARS => { NO_ID => 1 },
112
115
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -2043,7 +2046,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2043
2046
  return 1;
2044
2047
 
2045
2048
  } elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
2046
-
2049
+
2047
2050
  $debug and $et->FoundTag(GPSType => '2G');
2048
2051
  # Vantrue N4 dashcam
2049
2052
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
@@ -2778,7 +2781,7 @@ sub ProcessInsta360($;$)
2778
2781
  my ($et, $dirInfo) = @_;
2779
2782
  my $raf = $$et{RAF};
2780
2783
  my $offset = $dirInfo ? $$dirInfo{Offset} || 0 : 0;
2781
- my $buff;
2784
+ my ($buff, $dirTable, $dirTablePos);
2782
2785
 
2783
2786
  return 0 unless $raf->Seek(-78-$offset, 2) and $raf->Read($buff, 78) == 78 and
2784
2787
  substr($buff,-32) eq "8db42d694ccc418790edff439fe026bf"; # check magic number
@@ -2868,7 +2871,19 @@ sub ProcessInsta360($;$)
2868
2871
  if ($len % $dlen and $id != 0x700) { # (have seen one 0x700 record which was expected format but not multiple of 53 bytes)
2869
2872
  $et->Warn(sprintf('Unexpected Insta360 record 0x%x length',$id));
2870
2873
  } elsif ($id == 0x200) {
2871
- $et->FoundTag(PreviewImage => $buff);
2874
+ # there are 4 types of record 0x200
2875
+ # 1. JPEG preview (starts with ff d8 ff e1)
2876
+ # 2. TIFF preview (starts with 01 00 00 00, then record length)
2877
+ # 3. Unknown 1 (starts with 00 00 00 01)
2878
+ # 4. Unknown 2 (starts with 00 00 01 34)
2879
+ if ($buff =~ /^\xff\xd8\xff/) {
2880
+ $et->FoundTag(PreviewImage => $buff);
2881
+ } elsif ($buff =~ /^\x01\0\0\0(.{4})\x01/s and unpack('V',$1) == $dlen) {
2882
+ my ($w, $h) = unpack('x16V2',$buff);
2883
+ # build the TIFF image (could the 1 at byte 9 be the SamplesPerPixel?)
2884
+ my $hdr = Image::ExifTool::MakeTiffHeader($w, $h, 1, 8);
2885
+ $et->FoundTag(PreviewTIFF => $hdr . substr($buff, 40));
2886
+ }
2872
2887
  } elsif ($id == 0x300) {
2873
2888
  for ($p=0; $p<$len; $p+=$dlen) {
2874
2889
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
@@ -2899,7 +2914,7 @@ sub ProcessInsta360($;$)
2899
2914
  my $tmp = substr($buff, $p, $dlen);
2900
2915
  my @a = unpack('VVvaa8aa8aa8a8a8', $tmp);
2901
2916
  unless (($a[5] eq 'N' or $a[5] eq 'S') and # (quick validation)
2902
- ($a[7] eq 'E' or $a[7] eq 'W' or
2917
+ ($a[7] eq 'E' or $a[7] eq 'W' or
2903
2918
  # (odd, but I've seen "O" instead of "W". Perhaps
2904
2919
  # when the language is french? ie. "Ouest"?)
2905
2920
  $a[7] eq 'O'))
@@ -2913,13 +2928,15 @@ sub ProcessInsta360($;$)
2913
2928
  $a[$_] = GetDouble(\$a[$_], 0) foreach 4,6,8,9,10;
2914
2929
  $a[4] = -abs($a[4]) if $a[5] eq 'S'; # (abs just in case it was already signed)
2915
2930
  $a[6] = -abs($a[6]) if $a[7] ne 'E';
2916
- $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($a[0]) . 'Z');
2931
+ my $ms = '';
2932
+ $a[2] and ($ms = sprintf('.%.3d', $a[2])) =~ s/0+$//;
2933
+ $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($a[0]) . $ms . 'Z');
2917
2934
  $et->HandleTag($tagTbl, GPSLatitude => $a[4]);
2918
2935
  $et->HandleTag($tagTbl, GPSLongitude => $a[6]);
2919
2936
  $et->HandleTag($tagTbl, GPSSpeed => $a[8] * $mpsToKph);
2920
2937
  $et->HandleTag($tagTbl, GPSTrack => $a[9]);
2921
2938
  $et->HandleTag($tagTbl, GPSAltitude => $a[10]);
2922
- $et->HandleTag($tagTbl, Unknown02 => "@a[1,2]") if $unknown; # millisecond counter (https://exiftool.org/forum/index.php?topic=9884.msg65143#msg65143)
2939
+ $et->HandleTag($tagTbl, Unknown02 => $a[1]) if $unknown;
2923
2940
  }
2924
2941
  }
2925
2942
  } elsif ($id == 0x101) {
@@ -2932,10 +2949,41 @@ sub ProcessInsta360($;$)
2932
2949
  $et->HandleTag($tagTablePtr, $t, $val);
2933
2950
  $p += 2 + $n;
2934
2951
  }
2952
+ } elsif ($id == 0x0) {
2953
+ last if not $len;
2954
+ # example directory table for record locations from Insta360AcePro MP4 video:
2955
+ # vv vv - record ID
2956
+ # vv vv vv vv - record size
2957
+ # vv vv vv vv - offset from start of footer
2958
+ # 00 00 00 00 00 00 00 00 00 00
2959
+ # 01 01 82 04 00 00 1b 45 62 00
2960
+ # 02 00 28 46 05 00 ed fe 5c 00
2961
+ # 03 00 40 aa 24 00 ed fe 34 00
2962
+ # 04 00 00 c1 01 00 ed fe 30 00
2963
+ # [...]
2964
+ unless ($dirTable) {
2965
+ $dirTable = $buff;
2966
+ $dirTablePos = 0;
2967
+ }
2935
2968
  }
2936
- ($epos -= 6) + $trailerLen < 0 and last; # step back to previous record
2937
- $raf->Seek($epos, 2) or last;
2938
- $raf->Read($buff, 6) == 6 or last;
2969
+ # step through directory table instead of sequential scanning if possible
2970
+ if ($dirTable) {
2971
+ undef $epos;
2972
+ for (;;) {
2973
+ last if $dirTablePos + 10 > length($dirTable);
2974
+ my ($id, $siz, $off) = unpack("x${dirTablePos}vVV", $dirTable);
2975
+ $dirTablePos += 10;
2976
+ if ($id and $siz and $off + $siz < $trailerLen) {
2977
+ $epos = $off + $siz - $trailerLen;
2978
+ last;
2979
+ }
2980
+ }
2981
+ last unless defined $epos;
2982
+ } else {
2983
+ ($epos -= 6) + $trailerLen < 0 and last; # step back to previous record
2984
+ }
2985
+ $raf->Seek($epos, 2) or last; # seek to start of next footer
2986
+ $raf->Read($buff, 6) == 6 or last; # read footer
2939
2987
  }
2940
2988
  $$et{DOC_NUM} = 0;
2941
2989
  SetByteOrder('MM');
@@ -343,9 +343,10 @@ numerical, and generated automatically otherwise.
343
343
  but applied automatically to individual tags. Value specifies
344
344
  pattern for split, or 1 for default pattern ',?\\s+'.
345
345
 
346
- 'Avoid' - avoid creating this tag if possible. This is only
347
- effective if another tag exists with the same name. Setting
348
- this flag also sets the default Priority to 0 for this tag.
346
+ 'Avoid' - avoid creating this tag when writing if possible.
347
+ This is only effective if another tag exists with the same
348
+ name. Setting this flag also sets the default Priority to 0
349
+ for this tag.
349
350
 
350
351
  'Binary' - set to 1 for binary data. This has the same effect
351
352
  as setting ValueConv to '\$val', but it it a bit cleaner and
@@ -615,9 +616,9 @@ numerical, and generated automatically otherwise.
615
616
  arguments: the value, a flag which is set for the inverse
616
617
  conversion, and a reference to the PrintConv hash, and returns
617
618
  the converted value or undef on error -- it may call warn() to
618
- return an error message. The lookup hash may also contain a
619
+ return an error message. The lookup hash may also contain a
619
620
  'Notes' entry which is used for documentation if the
620
- SeparateTable flag is set). In an expression, $self is a
621
+ SeparateTable flag is set). In an expression, $self is a
621
622
  reference to the current ExifTool object, $val is the Raw
622
623
  value, and $tag is the tag key. The subroutine takes 2
623
624
  arguments: the Raw value and a reference to the current
@@ -893,6 +894,11 @@ numerical, and generated automatically otherwise.
893
894
  writing this tag. Only needed if tag can be written to
894
895
  groups other than the normal groups for this tag (very rare).
895
896
 
897
+ Deletable : [Writable SubDirectory's only] Overrides internal test for
898
+ metadata types with permanent directories (currently QuickTime
899
+ and Jpeg2000), allowing the tag containing these directories
900
+ to be deleted
901
+
896
902
  OffsetPair : Used in EXIF table to specify the tagID for the corresponding
897
903
  offset or length tag.
898
904
 
@@ -917,6 +923,9 @@ numerical, and generated automatically otherwise.
917
923
  FixedSize : [EXIF only] Hack to ignore value size and use this instead.
918
924
  Only valid if Format is also defined.
919
925
 
926
+ TruncateOK : [EXIF only] Hack to demote a truncated value in an IFD to a
927
+ minor error.
928
+
920
929
  Struct : [XMP tags only] Reference to structure hash for structured XMP
921
930
  tags. See "STRUCTURES" section below for more details. (For
922
931
  backward compatibility, this may be a name to an entry in
@@ -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.65';
33
+ $VERSION = '1.67';
34
34
 
35
35
  sub ConvertTimecode($);
36
36
  sub ProcessSGLT($$$);
@@ -500,6 +500,11 @@ my %code2charset = (
500
500
  Name => 'OldXMP',
501
501
  Binary => 1,
502
502
  },
503
+ C2PA => { #https://c2pa.org/specifications/
504
+ Name => 'JUMBF',
505
+ Deletable => 1,
506
+ SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::Main' },
507
+ },
503
508
  olym => {
504
509
  Name => 'Olym',
505
510
  SubDirectory => { TagTable => 'Image::ExifTool::Olympus::WAV' },
@@ -555,7 +560,7 @@ my %code2charset = (
555
560
  },
556
561
  },{ # (WebP) - have also seen with "Exif\0\0" header - PH
557
562
  Name => 'EXIF',
558
- Condition => '$$valPt =~ /^Exif\0\0(II\x2a\0|MM\0\x2a)/ and $self->Warn("Improper EXIF header",1)',
563
+ Condition => '$$valPt =~ /^Exif\0\0(II\x2a\0|MM\0\x2a)/ and ($self->Warn("Improper EXIF header",1) or 1)',
559
564
  SubDirectory => {
560
565
  TagTable => 'Image::ExifTool::Exif::Main',
561
566
  ProcessProc => \&Image::ExifTool::ProcessTIFF,
@@ -570,6 +575,12 @@ my %code2charset = (
570
575
  Notes => 'WebP files',
571
576
  SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
572
577
  },
578
+ "XMP\0" => {
579
+ Name => 'XMP',
580
+ Notes => 'incorrectly written WebP files',
581
+ Condition => '$self->Warn("Incorrect XMP tag ID", 1) or 1',
582
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
583
+ },
573
584
  ICCP => { #14 (WebP)
574
585
  Name => 'ICC_Profile',
575
586
  Notes => 'WebP files',
@@ -1063,7 +1074,16 @@ my %code2charset = (
1063
1074
  },
1064
1075
  1 => {
1065
1076
  Name => 'MaxDataRate',
1066
- PrintConv => 'sprintf("%.4g kB/s",$val / 1024)',
1077
+ Notes => q{
1078
+ converted using SI byte prefixes unles the API ByteUnit option is set to
1079
+ "Binary"
1080
+ },
1081
+ PrintConv => q{
1082
+ my ($unit, $div) = $self->Options('ByteUnit') eq 'Binary' ? ('KiB/s',1024) : ('kB/s',1000);
1083
+ my $tmp = $val / $div;
1084
+ $tmp > 9999 and $tmp /= $div, $unit =~ s/^./M/;
1085
+ sprintf('%.4g %s', $tmp, $unit);
1086
+ },
1067
1087
  },
1068
1088
  # 2 => 'PaddingGranularity',
1069
1089
  # 3 => 'Flags',
@@ -1983,7 +2003,7 @@ sub ProcessRIFF($$)
1983
2003
  {
1984
2004
  my ($et, $dirInfo) = @_;
1985
2005
  my $raf = $$dirInfo{RAF};
1986
- my ($buff, $buf2, $type, $mime, $err, $rf64);
2006
+ my ($buff, $buf2, $type, $mime, $err, $rf64, $moviEnd);
1987
2007
  my $verbose = $et->Options('Verbose');
1988
2008
  my $unknown = $et->Options('Unknown');
1989
2009
  my $validate = $et->Options('Validate');
@@ -2017,8 +2037,34 @@ sub ProcessRIFF($$)
2017
2037
  # Read chunks in RIFF image
2018
2038
  #
2019
2039
  for (;;) {
2040
+ if ($err) {
2041
+ last unless $moviEnd;
2042
+ # we arrived here because there was a problem parsing the movie data
2043
+ # so seek to the end to continue processing
2044
+ if ($moviEnd > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2045
+ $et->Warn('Possibly corrupt LIST_movi data');
2046
+ $et->Warn('Stopped parsing at large LIST_movi chunk (LargeFileSupport not set)');
2047
+ undef $err;
2048
+ last;
2049
+ }
2050
+ if ($validate) {
2051
+ # (must actually try to read something after seeking to detect error)
2052
+ $raf->Seek($moviEnd-1, 0) and $raf->Read($buff, 1) == 1 or last;
2053
+ } else {
2054
+ $raf->Seek($moviEnd, 0) or last;
2055
+ }
2056
+ $pos = $moviEnd;
2057
+ $et->Warn('Possibly corrupt LIST_movi data');
2058
+ undef $err;
2059
+ undef $moviEnd;
2060
+ }
2061
+ if ($moviEnd) {
2062
+ $pos > $moviEnd and $err = 1, next; # error if we parsed past the end of the movie data
2063
+ undef $moviEnd if $pos == $moviEnd; # parsed all movie data?
2064
+ }
2020
2065
  my $num = $raf->Read($buff, 8);
2021
2066
  if ($num < 8) {
2067
+ $moviEnd and $err = 1, next;
2022
2068
  $err = 1 if $num;
2023
2069
  $et->Warn('Incorrect RIFF chunk size' . " $pos vs. $riffEnd") if $validate and $pos != $riffEnd;
2024
2070
  last;
@@ -2029,7 +2075,7 @@ sub ProcessRIFF($$)
2029
2075
  $et->OverrideFileType('Extended WEBP',undef,'webp') if $tag eq 'VP8X' and $type eq 'WEBP';
2030
2076
  # special case: construct new tag name from specific LIST type
2031
2077
  if ($tag eq 'LIST') {
2032
- $raf->Read($buff, 4) == 4 or $err=1, last;
2078
+ $raf->Read($buff, 4) == 4 or $err=1, next;
2033
2079
  $pos += 4;
2034
2080
  $tag .= "_$buff";
2035
2081
  $len -= 4; # already read 4 bytes (the LIST type)
@@ -2038,6 +2084,7 @@ sub ProcessRIFF($$)
2038
2084
  }
2039
2085
  $et->VPrint(0, "RIFF '${tag}' chunk ($len bytes of data):\n");
2040
2086
  if ($len <= 0) {
2087
+ $moviEnd and $err = 1, next;
2041
2088
  if ($len < 0) {
2042
2089
  $et->Warn('Invalid chunk length');
2043
2090
  } elsif ($tag eq "\0\0\0\0") {
@@ -2068,7 +2115,7 @@ sub ProcessRIFF($$)
2068
2115
  my $tagInfo = $$tagTbl{$tag};
2069
2116
  # (in LIST_movi chunk: ##db = uncompressed DIB, ##dc = compressed DIB, ##wb = audio data)
2070
2117
  if ($tagInfo or (($verbose or $unknown) and $tag !~ /^(data|idx1|LIST_movi|RIFF|\d{2}(db|dc|wb))$/)) {
2071
- $raf->Read($buff, $len2) == $len2 or $err=1, last;
2118
+ $raf->Read($buff, $len2) == $len2 or $err=1, next;
2072
2119
  if ($hash and $isImageData{$tag}) {
2073
2120
  $hash->add($buff);
2074
2121
  $et->VPrint(0, "$$et{INDENT}(ImageDataHash: '${tag}' chunk, $len2 bytes)\n");
@@ -2094,7 +2141,7 @@ sub ProcessRIFF($$)
2094
2141
  $et->Warn('Incorrect RIFF chunk size') if $validate and $pos - 8 != $riffEnd;
2095
2142
  $riffEnd += $len2 + 8;
2096
2143
  # don't read into RIFF chunk (eg. concatenated video file)
2097
- $raf->Read($buff, 4) == 4 or $err=1, last; # (skip RIFF type word)
2144
+ $raf->Read($buff, 4) == 4 or $err=1, next; # (skip RIFF type word)
2098
2145
  $pos += 4;
2099
2146
  # extract information from remaining file as an embedded file
2100
2147
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
@@ -2107,18 +2154,21 @@ sub ProcessRIFF($$)
2107
2154
  $et->ImageDataHash($raf, $len2, "'${tag}' chunk");
2108
2155
  }
2109
2156
  if ($tag eq 'LIST_movi' and $ee) {
2110
- $raf->Seek($rewind, 0) or $err = 1, last if $rewind;
2157
+ $raf->Seek($rewind, 0) or $err = 1, next if $rewind;
2158
+ # save end-of-movie offset so we can seek there if we get errors parsing the movie data
2159
+ $moviEnd = $raf->Tell() + $len2;
2111
2160
  next; # parse into movi chunk
2112
2161
  } elsif (not $rewind) {
2113
2162
  if ($len > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2163
+ $tag =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/eg;
2114
2164
  $et->Warn("Stopped parsing at large $tag chunk (LargeFileSupport not set)");
2115
2165
  last;
2116
2166
  }
2117
2167
  if ($validate and $len2) {
2118
2168
  # (must actually try to read something after seeking to detect error)
2119
- $raf->Seek($len2-1, 1) and $raf->Read($buff, 1) == 1 or $err = 1, last;
2169
+ $raf->Seek($len2-1, 1) and $raf->Read($buff, 1) == 1 or $err = 1, next;
2120
2170
  } else {
2121
- $raf->Seek($len2, 1) or $err=1, last;
2171
+ $raf->Seek($len2, 1) or $err=1, next;
2122
2172
  }
2123
2173
  }
2124
2174
  }