exiftool_vendored 12.42.0 → 12.50.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +189 -6
  3. data/bin/MANIFEST +12 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +45 -44
  7. data/bin/config_files/acdsee.config +2 -1
  8. data/bin/config_files/frameCount.config +56 -0
  9. data/bin/config_files/tiff_version.config +1 -1
  10. data/bin/exiftool +113 -95
  11. data/bin/fmt_files/gpx.fmt +3 -0
  12. data/bin/fmt_files/gpx_wpt.fmt +3 -0
  13. data/bin/lib/Image/ExifTool/Apple.pm +16 -3
  14. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +23 -12
  15. data/bin/lib/Image/ExifTool/Canon.pm +66 -37
  16. data/bin/lib/Image/ExifTool/CanonRaw.pm +8 -1
  17. data/bin/lib/Image/ExifTool/CanonVRD.pm +7 -8
  18. data/bin/lib/Image/ExifTool/DJI.pm +2 -1
  19. data/bin/lib/Image/ExifTool/DarwinCore.pm +13 -1
  20. data/bin/lib/Image/ExifTool/EXE.pm +9 -1
  21. data/bin/lib/Image/ExifTool/Exif.pm +17 -12
  22. data/bin/lib/Image/ExifTool/FLAC.pm +17 -3
  23. data/bin/lib/Image/ExifTool/FLIR.pm +4 -3
  24. data/bin/lib/Image/ExifTool/FlashPix.pm +26 -3
  25. data/bin/lib/Image/ExifTool/FujiFilm.pm +51 -4
  26. data/bin/lib/Image/ExifTool/GPS.pm +21 -1
  27. data/bin/lib/Image/ExifTool/Geotag.pm +25 -5
  28. data/bin/lib/Image/ExifTool/ICC_Profile.pm +3 -2
  29. data/bin/lib/Image/ExifTool/ICO.pm +143 -0
  30. data/bin/lib/Image/ExifTool/ID3.pm +6 -6
  31. data/bin/lib/Image/ExifTool/IPTC.pm +5 -1
  32. data/bin/lib/Image/ExifTool/LNK.pm +5 -2
  33. data/bin/lib/Image/ExifTool/M2TS.pm +98 -8
  34. data/bin/lib/Image/ExifTool/MIE.pm +9 -3
  35. data/bin/lib/Image/ExifTool/MISB.pm +494 -0
  36. data/bin/lib/Image/ExifTool/MakerNotes.pm +3 -1
  37. data/bin/lib/Image/ExifTool/Matroska.pm +24 -16
  38. data/bin/lib/Image/ExifTool/Motorola.pm +8 -2
  39. data/bin/lib/Image/ExifTool/Nikon.pm +288 -122
  40. data/bin/lib/Image/ExifTool/NikonSettings.pm +5 -3
  41. data/bin/lib/Image/ExifTool/Olympus.pm +3 -2
  42. data/bin/lib/Image/ExifTool/Panasonic.pm +21 -4
  43. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +25 -5
  44. data/bin/lib/Image/ExifTool/Parrot.pm +96 -2
  45. data/bin/lib/Image/ExifTool/Pentax.pm +7 -2
  46. data/bin/lib/Image/ExifTool/Photoshop.pm +29 -3
  47. data/bin/lib/Image/ExifTool/QuickTime.pm +163 -13
  48. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +119 -13
  49. data/bin/lib/Image/ExifTool/README +13 -3
  50. data/bin/lib/Image/ExifTool/RIFF.pm +106 -9
  51. data/bin/lib/Image/ExifTool/Samsung.pm +2 -2
  52. data/bin/lib/Image/ExifTool/Sigma.pm +27 -1
  53. data/bin/lib/Image/ExifTool/SigmaRaw.pm +37 -13
  54. data/bin/lib/Image/ExifTool/Sony.pm +71 -43
  55. data/bin/lib/Image/ExifTool/TagInfoXML.pm +3 -1
  56. data/bin/lib/Image/ExifTool/TagLookup.pm +4737 -4517
  57. data/bin/lib/Image/ExifTool/TagNames.pod +1837 -1417
  58. data/bin/lib/Image/ExifTool/Text.pm +3 -4
  59. data/bin/lib/Image/ExifTool/Torrent.pm +2 -3
  60. data/bin/lib/Image/ExifTool/Validate.pm +3 -3
  61. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +7 -0
  62. data/bin/lib/Image/ExifTool/WriteExif.pl +100 -23
  63. data/bin/lib/Image/ExifTool/WriteIPTC.pl +2 -6
  64. data/bin/lib/Image/ExifTool/WritePhotoshop.pl +5 -5
  65. data/bin/lib/Image/ExifTool/WriteRIFF.pl +359 -0
  66. data/bin/lib/Image/ExifTool/Writer.pl +13 -5
  67. data/bin/lib/Image/ExifTool/XMP.pm +78 -59
  68. data/bin/lib/Image/ExifTool/XMP2.pl +19 -4
  69. data/bin/lib/Image/ExifTool.pm +111 -24
  70. data/bin/lib/Image/ExifTool.pod +83 -69
  71. data/bin/perl-Image-ExifTool.spec +43 -43
  72. data/lib/exiftool_vendored/version.rb +1 -1
  73. metadata +9 -4
@@ -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.74';
50
+ $VERSION = '2.80';
51
51
 
52
52
  sub ProcessMOV($$;$);
53
53
  sub ProcessKeys($$$);
@@ -66,6 +66,7 @@ sub ProcessRIFFTrailer($$$);
66
66
  sub ProcessTTAD($$$);
67
67
  sub ProcessNMEA($$$);
68
68
  sub ProcessGPSLog($$$);
69
+ sub ProcessGarminGPS($$$);
69
70
  sub SaveMetaKeys($$$);
70
71
  # ++^^^^^^^^^^^^++
71
72
  sub ParseItemLocation($$);
@@ -521,6 +522,12 @@ my %eeBox2 = (
521
522
  return substr($val, 12, $len);
522
523
  },
523
524
  },
525
+ {
526
+ Name => 'SkipInfo', # (found in 70mai Pro Plus+ MP4 videos)
527
+ # (look for something that looks like a QuickTime atom header)
528
+ Condition => '$$valPt =~ /^\0[\0-\x04]..[a-zA-Z ]{4}/s',
529
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SkipInfo' },
530
+ },
524
531
  { Name => 'Skip', Unknown => 1, Binary => 1 },
525
532
  ],
526
533
  wide => { Unknown => 1, Binary => 1 },
@@ -556,12 +563,16 @@ my %eeBox2 = (
556
563
  mdat => { Name => 'MediaData', Unknown => 1, Binary => 1 },
557
564
  'mdat-size' => {
558
565
  Name => 'MediaDataSize',
566
+ RawConv => '$$self{MediaDataSize} = $val',
559
567
  Notes => q{
560
568
  not a real tag ID, this tag represents the size of the 'mdat' data in bytes
561
569
  and is used in the AvgBitrate calculation
562
570
  },
563
571
  },
564
- 'mdat-offset' => 'MediaDataOffset',
572
+ 'mdat-offset' => {
573
+ Name => 'MediaDataOffset',
574
+ RawConv => '$$self{MediaDataOffset} = $val',
575
+ },
565
576
  junk => { Unknown => 1, Binary => 1 }, #8
566
577
  uuid => [
567
578
  { #9 (MP4 files)
@@ -754,6 +765,19 @@ my %eeBox2 = (
754
765
  # 'samn'? - seen in Vantrue N2S sample video
755
766
  );
756
767
 
768
+ # stuff seen in 'skip' atom (70mai Pro Plus+ MP4 videos)
769
+ %Image::ExifTool::QuickTime::SkipInfo = (
770
+ PROCESS_PROC => \&ProcessMOV,
771
+ GROUPS => { 2 => 'Video' },
772
+ 'ver ' => 'Version',
773
+ # tima - int32u: seen 0x3c
774
+ thma => {
775
+ Name => 'ThumbnailImage',
776
+ Groups => { 2 => 'Preview' },
777
+ Binary => 1,
778
+ },
779
+ );
780
+
757
781
  # MPEG-4 'ftyp' atom
758
782
  # (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
759
783
  %Image::ExifTool::QuickTime::FileType = (
@@ -897,7 +921,7 @@ my %eeBox2 = (
897
921
  },
898
922
  colr => {
899
923
  Name => 'ColorRepresentation',
900
- ValueConv => 'join(" ", substr($val,0,4), unpack("x4n*",$val))',
924
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
901
925
  },
902
926
  pasp => {
903
927
  Name => 'PixelAspectRatio',
@@ -1118,6 +1142,23 @@ my %eeBox2 = (
1118
1142
  Start => 16,
1119
1143
  },
1120
1144
  },
1145
+ {
1146
+ Name => 'GarminGPS',
1147
+ Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/ and $$self{OPTIONS}{ExtractEmbedded}',
1148
+ SubDirectory => {
1149
+ TagTable => 'Image::ExifTool::QuickTime::Stream',
1150
+ ProcessProc => \&ProcessGarminGPS,
1151
+ },
1152
+ },
1153
+ {
1154
+ Name => 'GarminGPS',
1155
+ Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/',
1156
+ Notes => 'Garmin GPS sensor data',
1157
+ RawConv => q{
1158
+ $self->WarnOnce('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1159
+ return \$val;
1160
+ },
1161
+ },
1121
1162
  {
1122
1163
  Name => 'UUID-Unknown',
1123
1164
  %unknownInfo,
@@ -2732,7 +2773,7 @@ my %eeBox2 = (
2732
2773
  },
2733
2774
  },{
2734
2775
  Name => 'ColorRepresentation',
2735
- ValueConv => 'join(" ", substr($val,0,4), unpack("x4n*",$val))',
2776
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
2736
2777
  }],
2737
2778
  irot => {
2738
2779
  Name => 'Rotation',
@@ -2792,6 +2833,78 @@ my %eeBox2 = (
2792
2833
  },
2793
2834
  );
2794
2835
 
2836
+ # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
2837
+ %Image::ExifTool::QuickTime::ColorRep = (
2838
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2839
+ GROUPS => { 2 => 'Video' },
2840
+ FIRST_ENTRY => 0,
2841
+ 0 => { Name => 'ColorProfiles', Format => 'undef[4]' },
2842
+ 4 => {
2843
+ Name => 'ColorPrimaries',
2844
+ Format => 'int16u',
2845
+ PrintConv => {
2846
+ 1 => 'BT.709',
2847
+ 2 => 'Unspecified',
2848
+ 4 => 'BT.470 System M (historical)',
2849
+ 5 => 'BT.470 System B, G (historical)',
2850
+ 6 => 'BT.601',
2851
+ 7 => 'SMPTE 240',
2852
+ 8 => 'Generic film (color filters using illuminant C)',
2853
+ 9 => 'BT.2020, BT.2100',
2854
+ 10 => 'SMPTE 428 (CIE 1921 XYZ)',
2855
+ 11 => 'SMPTE RP 431-2',
2856
+ 12 => 'SMPTE EG 432-1',
2857
+ 22 => 'EBU Tech. 3213-E',
2858
+ },
2859
+ },
2860
+ 6 => {
2861
+ Name => 'TransferCharacteristics',
2862
+ Format => 'int16u',
2863
+ PrintConv => {
2864
+ 0 => 'For future use (0)',
2865
+ 1 => 'BT.709',
2866
+ 2 => 'Unspecified',
2867
+ 3 => 'For future use (3)',
2868
+ 4 => 'BT.470 System M (historical)',
2869
+ 5 => 'BT.470 System B, G (historical)',
2870
+ 6 => 'BT.601',
2871
+ 7 => 'SMPTE 240 M',
2872
+ 8 => 'Linear',
2873
+ 9 => 'Logarithmic (100 : 1 range)',
2874
+ 10 => 'Logarithmic (100 * Sqrt(10) : 1 range)',
2875
+ 11 => 'IEC 61966-2-4',
2876
+ 12 => 'BT.1361',
2877
+ 13 => 'sRGB or sYCC',
2878
+ 14 => 'BT.2020 10-bit systems',
2879
+ 15 => 'BT.2020 12-bit systems',
2880
+ 16 => 'SMPTE ST 2084, ITU BT.2100 PQ',
2881
+ 17 => 'SMPTE ST 428',
2882
+ 18 => 'BT.2100 HLG, ARIB STD-B67',
2883
+ },
2884
+ },
2885
+ 8 => {
2886
+ Name => 'MatrixCoefficients',
2887
+ Format => 'int16u',
2888
+ PrintConv => {
2889
+ 0 => 'Identity matrix',
2890
+ 1 => 'BT.709',
2891
+ 2 => 'Unspecified',
2892
+ 3 => 'For future use (3)',
2893
+ 4 => 'US FCC 73.628',
2894
+ 5 => 'BT.470 System B, G (historical)',
2895
+ 6 => 'BT.601',
2896
+ 7 => 'SMPTE 240 M',
2897
+ 8 => 'YCgCo',
2898
+ 9 => 'BT.2020 non-constant luminance, BT.2100 YCbCr',
2899
+ 10 => 'BT.2020 constant luminance',
2900
+ 11 => 'SMPTE ST 2085 YDzDx',
2901
+ 12 => 'Chromaticity-derived non-constant luminance',
2902
+ 13 => 'Chromaticity-derived constant luminance',
2903
+ 14 => 'BT.2100 ICtCp',
2904
+ },
2905
+ },
2906
+ );
2907
+
2795
2908
  # HEVC configuration (ref https://github.com/MPEGGroup/isobmff/blob/master/IsoLib/libisomediafile/src/HEVCConfigAtom.c)
2796
2909
  %Image::ExifTool::QuickTime::HEVCConfig = (
2797
2910
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
@@ -7516,6 +7629,12 @@ my %eeBox2 = (
7516
7629
  Format => 'undef[4]',
7517
7630
  RawConv => '$$self{MetaFormat} = $val',
7518
7631
  },
7632
+ 8 => { # starts at 8 for MetaFormat eq 'camm', and 17 for 'mett'
7633
+ Name => 'MetaType',
7634
+ Format => 'undef[$size-8]',
7635
+ # may start at various locations!
7636
+ RawConv => '$$self{MetaType} = ($val=~/(application[^\0]+)/ ? $1 : undef)',
7637
+ },
7519
7638
  #
7520
7639
  # Observed offsets for child atoms of various MetaFormat types:
7521
7640
  #
@@ -7548,6 +7667,11 @@ my %eeBox2 = (
7548
7667
  Format => 'undef[4]',
7549
7668
  RawConv => '$$self{MetaFormat} = $val', # (yes, use MetaFormat for this too)
7550
7669
  },
7670
+ 24 => {
7671
+ Condition => '$$self{MetaFormat} eq "tmcd"',
7672
+ Name => 'PlaybackFrameRate', # (may differ from recorded FrameRate eg. ../pics/FujiFilmX-H1.mov)
7673
+ Format => 'rational64u',
7674
+ },
7551
7675
  #
7552
7676
  # Observed offsets for child atoms of various OtherFormat types:
7553
7677
  #
@@ -8000,9 +8124,12 @@ sub AUTOLOAD
8000
8124
  # Returns: 9-element rotation matrix as a string (with 0 x/y offsets)
8001
8125
  sub GetRotationMatrix($)
8002
8126
  {
8003
- my $ang = 3.1415926536 * shift() / 180;
8127
+ my $ang = 3.14159265358979323846264 * shift() / 180;
8004
8128
  my $cos = cos $ang;
8005
8129
  my $sin = sin $ang;
8130
+ # round to zero
8131
+ $cos = 0 if abs($cos) < 1e-12;
8132
+ $sin = 0 if abs($sin) < 1e-12;
8006
8133
  my $msn = -$sin;
8007
8134
  return "$cos $sin 0 $msn $cos 0 0 0 1";
8008
8135
  }
@@ -8583,7 +8710,7 @@ sub HandleItemInfo($)
8583
8710
  $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes)\n");
8584
8711
  }
8585
8712
  # get ExifTool name for this item
8586
- my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP' }->{$type} || '';
8713
+ my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage' }->{$type} || '';
8587
8714
  my ($warn, $extent);
8588
8715
  $warn = "Can't currently decode encoded $type metadata" if $$item{ContentEncoding};
8589
8716
  $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
@@ -8660,6 +8787,21 @@ sub HandleItemInfo($)
8660
8787
  }
8661
8788
  $subTable = GetTagTable('Image::ExifTool::Exif::Main');
8662
8789
  $proc = \&Image::ExifTool::ProcessTIFF;
8790
+ } elsif ($name eq 'PreviewImage') {
8791
+ # take a quick stab at determining the size of the image
8792
+ # (based on JPEG previews found in Fuji X-H2S HIF images)
8793
+ my $type = 'PreviewImage';
8794
+ if ($buff =~ /^.{556}\xff\xc0\0\x11.(.{4})/s) {
8795
+ my ($h, $w) = unpack('n2', $1);
8796
+ # (not sure if $h is ever the long dimension, but test it just in case)
8797
+ if ($w == 160 or $h == 160) {
8798
+ $type = 'ThumbnailImage';
8799
+ } elsif ($w == 1920 or $h == 1920) {
8800
+ $type = 'OtherImage'; # (large preview)
8801
+ } # (PreviewImage is 640x480)
8802
+ }
8803
+ $et->FoundTag($type => $buff);
8804
+ next;
8663
8805
  } else {
8664
8806
  $start = 0;
8665
8807
  $subTable = GetTagTable('Image::ExifTool::XMP::Main');
@@ -9100,12 +9242,13 @@ sub ProcessMOV($$;$)
9100
9242
  # temporarily set ExtractEmbedded option for CRX files
9101
9243
  $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
9102
9244
  } else {
9103
- $et->SetFileType(); # MOV
9245
+ $et->SetFileType(); # MOV
9104
9246
  }
9105
9247
  SetByteOrder('MM');
9106
9248
  $$et{PRIORITY_DIR} = 'XMP'; # have XMP take priority
9107
9249
  }
9108
- $$raf{NoBuffer} = 1 if $et->Options('FastScan'); # disable buffering in FastScan mode
9250
+ my $fast = $$et{OPTIONS}{FastScan} || 0;
9251
+ $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9109
9252
 
9110
9253
  my $ee = $$et{OPTIONS}{ExtractEmbedded};
9111
9254
  if ($ee) {
@@ -9133,8 +9276,10 @@ sub ProcessMOV($$;$)
9133
9276
  $et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
9134
9277
  if ($$tagTablePtr{"$tag-size"}) {
9135
9278
  my $pos = $raf->Tell();
9136
- $raf->Seek(0, 2);
9137
- $et->HandleTag($tagTablePtr, "$tag-size", $raf->Tell() - $pos);
9279
+ unless ($fast) {
9280
+ $raf->Seek(0, 2);
9281
+ $et->HandleTag($tagTablePtr, "$tag-size", $raf->Tell() - $pos);
9282
+ }
9138
9283
  $et->HandleTag($tagTablePtr, "$tag-offset", $pos) if $$tagTablePtr{"$tag-offset"};
9139
9284
  }
9140
9285
  }
@@ -9237,6 +9382,8 @@ sub ProcessMOV($$;$)
9237
9382
  $et->HandleTag($tagTablePtr, "$tag-size", $size);
9238
9383
  $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9239
9384
  }
9385
+ # stop processing at mdat/idat if -fast2 is used
9386
+ last if $fast > 1 and ($tag eq 'mdat' or $tag eq 'idat');
9240
9387
  # load values only if associated with a tag (or verbose) and not too big
9241
9388
  if ($size > 0x2000000) { # start to get worried above 32 MB
9242
9389
  # check for RIFF trailer (written by Auto-Vox dashcam)
@@ -9555,10 +9702,14 @@ ItemID: foreach $id (keys %$items) {
9555
9702
  require Image::ExifTool::Font;
9556
9703
  $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
9557
9704
  }
9705
+ } else {
9706
+ # for the default language code of 0x0000, use UTF-8 instead
9707
+ # of the CharsetQuickTime setting if obviously UTF8
9708
+ $enc = 'UTF8' if Image::ExifTool::IsUTF8(\$str) > 0;
9558
9709
  }
9559
9710
  # the spec says only "Macintosh text encoding", but
9560
9711
  # allow this to be configured by the user
9561
- $enc = $charsetQuickTime;
9712
+ $enc = $charsetQuickTime unless $enc;
9562
9713
  } else {
9563
9714
  # convert language code to ASCII (ignore read-only bit)
9564
9715
  $lang = UnpackLang($lang);
@@ -9599,8 +9750,7 @@ ItemID: foreach $id (keys %$items) {
9599
9750
  if (not ref $$vp and length($$vp) <= 65536 and $$vp =~ /[\x80-\xff]/) {
9600
9751
  # the encoding of this is not specified, so use CharsetQuickTime
9601
9752
  # unless the string is valid UTF-8
9602
- require Image::ExifTool::XMP;
9603
- my $enc = Image::ExifTool::XMP::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
9753
+ my $enc = Image::ExifTool::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
9604
9754
  $$vp = $et->Decode($$vp, $enc);
9605
9755
  }
9606
9756
  }
@@ -27,6 +27,8 @@ sub ProcessFreeGPS2($$$);
27
27
  sub Process360Fly($$$);
28
28
  sub ProcessFMAS($$$);
29
29
 
30
+ my $debug; # set to 1 for extra debugging messages
31
+
30
32
  # QuickTime data types that have ExifTool equivalents
31
33
  # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
32
34
  my %qtFmt = (
@@ -80,7 +82,7 @@ my %processByMetaFormat = (
80
82
 
81
83
  # data lengths for each INSV record type
82
84
  my %insvDataLen = (
83
- 0x300 => 56, # accelerometer
85
+ 0x300 => 0, # accelerometer (could be either 20 or 56 bytes)
84
86
  0x400 => 16, # exposure (ref 6)
85
87
  0x600 => 8, # timestamps (ref 6)
86
88
  0x700 => 53, # GPS
@@ -99,7 +101,7 @@ my %insvLimit = (
99
101
  The tags below are extracted from timed metadata in QuickTime and other
100
102
  formats of video files when the ExtractEmbedded option is used. Although
101
103
  most of these tags are combined into the single table below, ExifTool
102
- currently reads 59 different formats of timed GPS metadata from video files.
104
+ currently reads 62 different formats of timed GPS metadata from video files.
103
105
  },
104
106
  VARS => { NO_ID => 1 },
105
107
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -896,10 +898,15 @@ sub Process_text($$$)
896
898
  my $time = "$1:$2:$3";
897
899
  if ($$et{LastTime}) {
898
900
  if ($$et{LastTime} eq $time) {
901
+ # combine with the previous NMEA sentence
899
902
  $$et{DOC_NUM} = $$et{LastDoc};
900
903
  } elsif (%tags) {
904
+ # handle existing tags and start a new document
905
+ # (see https://exiftool.org/forum/index.php?msg=75422)
901
906
  HandleTextTags($et, $tagTbl, \%tags);
902
- $$et{DOC_NUM} = ++$$et{DOC_COUNT};
907
+ undef %tags;
908
+ # increment document number and update document count if necessary
909
+ $$et{DOC_COUNT} < ++$$et{DOC_NUM} and $$et{DOC_COUNT} = $$et{DOC_NUM};
903
910
  }
904
911
  }
905
912
  $$et{LastTime} = $time;
@@ -918,7 +925,8 @@ sub Process_text($$$)
918
925
  $$et{DOC_NUM} = $$et{LastDoc};
919
926
  } elsif (%tags) {
920
927
  HandleTextTags($et, $tagTbl, \%tags);
921
- $$et{DOC_NUM} = ++$$et{DOC_COUNT};
928
+ undef %tags;
929
+ $$et{DOC_COUNT} < ++$$et{DOC_NUM} and $$et{DOC_COUNT} = $$et{DOC_NUM};
922
930
  }
923
931
  }
924
932
  $$et{LastTime} = $time;
@@ -1416,6 +1424,7 @@ sub ProcessFreeGPS($$$)
1416
1424
  } elsif ($buf2 =~ /^.{173}([-+]\d{3})([-+]\d{3})([-+]\d{3})/s) { # (Azdome)
1417
1425
  @acc = ($1/100, $2/100, $3/100);
1418
1426
  }
1427
+ $debug and $et->FoundTag(GPSType => '1A');
1419
1428
 
1420
1429
  } elsif ($$dataPt =~ /^.{52}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/s) {
1421
1430
 
@@ -1444,6 +1453,7 @@ sub ProcessFreeGPS($$$)
1444
1453
  # change to signed integer and divide by 256
1445
1454
  map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
1446
1455
  }
1456
+ $debug and $et->FoundTag(GPSType => '1B');
1447
1457
 
1448
1458
  } elsif ($$dataPt =~ /^.{37}\0\0\0A([NS])([EW])/s) {
1449
1459
 
@@ -1454,13 +1464,27 @@ sub ProcessFreeGPS($$$)
1454
1464
  # 0030: f1 47 40 46 66 66 d2 41 85 eb 83 41 00 00 00 00 [.G@Fff.A...A....]
1455
1465
  ($latRef, $lonRef) = ($1, $2);
1456
1466
  ($hr,$min,$sec,$yr,$mon,$day) = unpack('x16V6', $$dataPt);
1457
- $yr += 2000;
1467
+ if ($yr < 2000) {
1468
+ $yr += 2000;
1469
+ } else {
1470
+ # Kenwood dashcam sometimes stores absolute year and local time
1471
+ # (but sometimes year since 2000 and UTC time in same video!)
1472
+ require Time::Local;
1473
+ my $time = Image::ExifTool::TimeLocal($sec,$min,$hr,$day,$mon-1,$yr-1900);
1474
+ ($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
1475
+ $yr += 1900;
1476
+ ++$mon;
1477
+ $et->WarnOnce('Converting GPSDateTime to UTC based on local time zone',1);
1478
+ }
1458
1479
  SetByteOrder('II');
1459
1480
  $lat = GetFloat($dataPt, 0x2c);
1460
1481
  $lon = GetFloat($dataPt, 0x30);
1461
1482
  $spd = GetFloat($dataPt, 0x34) * $knotsToKph; # (convert knots to km/h)
1462
1483
  $trk = GetFloat($dataPt, 0x38);
1484
+ @acc = unpack('x60V3', $$dataPt); # (may be all zeros if not valid)
1485
+ map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
1463
1486
  SetByteOrder('MM');
1487
+ $debug and $et->FoundTag(GPSType => '1C');
1464
1488
 
1465
1489
  } elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
1466
1490
 
@@ -1489,6 +1513,7 @@ sub ProcessFreeGPS($$$)
1489
1513
  $acc[1] = GetFloat($dataPt, 0x30);
1490
1514
  $acc[2] = GetFloat($dataPt, 0x34);
1491
1515
  SetByteOrder('MM');
1516
+ $debug and $et->FoundTag(GPSType => '1D');
1492
1517
 
1493
1518
  } elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
1494
1519
 
@@ -1509,6 +1534,7 @@ sub ProcessFreeGPS($$$)
1509
1534
  $trk = GetFloat($dataPt, 0x54) + 180; # (why is this off by 180?)
1510
1535
  $trk -= 360 if $trk >= 360;
1511
1536
  SetByteOrder('MM');
1537
+ $debug and $et->FoundTag(GPSType => '1E');
1512
1538
 
1513
1539
  } elsif ($$dataPt =~ /^.{60}4W`b]S</s and length($$dataPt) >= 140) {
1514
1540
 
@@ -1526,8 +1552,9 @@ sub ProcessFreeGPS($$$)
1526
1552
  $yr += ($yr >= 70 ? 1900 : 2000);
1527
1553
  $spd = $9 * $knotsToKph if length $9;
1528
1554
  $trk = $10 if length $10;
1555
+ $debug and $et->FoundTag(GPSType => '1F');
1529
1556
 
1530
- } elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0/s) {
1557
+ } elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0{5}/s) {
1531
1558
 
1532
1559
  # Akaso V1 dascham
1533
1560
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
@@ -1569,6 +1596,7 @@ sub ProcessFreeGPS($$$)
1569
1596
 
1570
1597
  SetByteOrder('MM');
1571
1598
  #my $serialNum = substr($$dataPt, 0x68, 20);
1599
+ $debug and $et->FoundTag(GPSType => '1G');
1572
1600
 
1573
1601
  } elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
1574
1602
 
@@ -1591,6 +1619,7 @@ sub ProcessFreeGPS($$$)
1591
1619
  # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
1592
1620
  substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
1593
1621
  # (other values are currently unknown)
1622
+ $debug and $et->FoundTag(GPSType => '1H');
1594
1623
 
1595
1624
  } elsif ($$dataPt =~ /^.{64}A([NS])([EW])\0/s) {
1596
1625
 
@@ -1616,6 +1645,7 @@ sub ProcessFreeGPS($$$)
1616
1645
  $trk = GetFloat($dataPt, 0x68);
1617
1646
  $alt = GetFloat($dataPt, 0x6c);
1618
1647
  SetByteOrder('MM');
1648
+ $debug and $et->FoundTag(GPSType => '1I');
1619
1649
 
1620
1650
  } else {
1621
1651
 
@@ -1624,6 +1654,9 @@ sub ProcessFreeGPS($$$)
1624
1654
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
1625
1655
  # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1626
1656
  # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1657
+ # 0030: 10 00 00 00 2d 00 00 00 14 00 00 00 11 00 00 00 [....-...........]
1658
+ # 0040: 0c 00 00 00 1f 00 00 00 41 4e 45 00 5d 9a a9 45 [........ANE.]..E]
1659
+ # 0050: ab 1e e5 44 ec 51 f0 40 b8 5e a5 43 00 00 00 00 [...D.Q.@.^.C....]
1627
1660
  # (records are same structure as Type 3 Novatek GPS in ProcessFreeGPS2() below)
1628
1661
  ($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef,$lat,$lon,$spd,$trk) =
1629
1662
  unpack('x48V6a1a1a1x1V4', $$dataPt);
@@ -1649,6 +1682,7 @@ sub ProcessFreeGPS($$$)
1649
1682
  $yr += 2000 if $yr < 2000;
1650
1683
  $spd *= $knotsToKph; # convert speed to km/h
1651
1684
  # ($trk is not confirmed; may be GPSImageDirection, ref PH)
1685
+ $debug and $et->FoundTag(GPSType => '1J');
1652
1686
  }
1653
1687
  #
1654
1688
  # save tag values extracted by above code
@@ -1802,6 +1836,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1802
1836
  }
1803
1837
  # save position of most recent record (needed when parsing the next freeGPS block)
1804
1838
  $$et{FreeGPS2}{RecentRecPos} = $lastRecPos;
1839
+ $debug and $et->FoundTag(GPSType => '2A');
1805
1840
  return 1;
1806
1841
 
1807
1842
  } elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s) {
@@ -1833,6 +1868,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1833
1868
  $lon = GetDouble($dataPt, 0x50);
1834
1869
  $spd = GetDouble($dataPt, 0x60) * $knotsToKph;
1835
1870
  $trk = GetDouble($dataPt, 0x68);
1871
+ $debug and $et->FoundTag(GPSType => '2B');
1836
1872
 
1837
1873
  } elsif ($$dataPt =~ /^.{72}A([NS])([EW])/s) {
1838
1874
 
@@ -1869,6 +1905,8 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1869
1905
  $lon = abs Get32s($dataPt, 0x50) / 1e7;
1870
1906
  $spd = Get32s($dataPt, 0x54) / 100 * $mpsToKph;
1871
1907
  $alt = GetFloat($dataPt, 0x58) / 1000; # (NC)
1908
+ $debug and $et->FoundTag(GPSType => '2C');
1909
+
1872
1910
  } else {
1873
1911
  # Type 3 (ref 2)
1874
1912
  # (no sample with this format)
@@ -1876,6 +1914,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1876
1914
  $lon = GetFloat($dataPt, 0x50);
1877
1915
  $spd = GetFloat($dataPt, 0x54) * $knotsToKph;
1878
1916
  $trk = GetFloat($dataPt, 0x58);
1917
+ $debug and $et->FoundTag(GPSType => '2D');
1879
1918
  }
1880
1919
 
1881
1920
  } elsif ($$dataPt =~ /^.{60}A\0.{6}([NS])\0.{6}([EW])\0/s and $dirLen >= 112) {
@@ -1896,6 +1935,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1896
1935
  $lon = GetFloat($dataPt, 0x48);
1897
1936
  $spd = GetFloat($dataPt, 0x50);
1898
1937
  $trk = GetFloat($dataPt, 0x54);
1938
+ $debug and $et->FoundTag(GPSType => '2E');
1899
1939
 
1900
1940
  } elsif ($$dataPt =~ /^.{16}A([NS])([EW])\0/s) {
1901
1941
 
@@ -1916,6 +1956,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1916
1956
  $et->HandleTag($tagTbl, GPSTrack => $trk);
1917
1957
  $et->HandleTag($tagTbl, Accelerometer => "@acc");
1918
1958
  }
1959
+ $debug and $et->FoundTag(GPSType => '2F');
1919
1960
  return 1;
1920
1961
 
1921
1962
  } else {
@@ -1962,6 +2003,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1962
2003
  $et->HandleTag($tagTbl, GPSTrack => $trk);
1963
2004
  last if $pos += 0x20 > length($$dataPt) - 0x1e;
1964
2005
  }
2006
+ $debug and $et->FoundTag(GPSType => '2G');
1965
2007
  return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
1966
2008
  }
1967
2009
  #
@@ -2530,7 +2572,7 @@ sub ProcessTTAD($$$)
2530
2572
  # (I think "5" may be the number of satellites. seen: 5,6,7 - PH)
2531
2573
  FoundSomething($et, $tagTbl, $sampleTime / 1000);
2532
2574
  my $t = GetDouble($dataPt, $pos);
2533
- $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($t,undef,3).'Z');
2575
+ $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($t,undef,3) . 'Z');
2534
2576
  $et->HandleTag($tagTbl, GPSLatitude => GetDouble($dataPt, $pos+0x1c));
2535
2577
  $et->HandleTag($tagTbl, GPSLongitude => GetDouble($dataPt, $pos+0x24));
2536
2578
  $et->HandleTag($tagTbl, GPSAltitude => GetDouble($dataPt, $pos+0x14));
@@ -2621,8 +2663,33 @@ sub ProcessInsta360($;$)
2621
2663
  if ($verbose) {
2622
2664
  $et->VPrint(0, sprintf("Insta360 Record 0x%x (offset 0x%x, %d bytes):\n", $id, $fileEnd + $epos, $len));
2623
2665
  }
2666
+ # there are 2 types of record 0x300:
2667
+ # 1. 56 byte records
2668
+ # 0000: 4a f7 02 00 00 00 00 00 00 00 00 00 00 1e e7 3f [J..............?]
2669
+ # 0010: 00 00 00 00 00 b2 ef bf 00 00 00 00 00 70 c1 bf [.............p..]
2670
+ # 0020: 00 00 00 e0 91 5c 8c bf 00 00 00 20 8f ff 87 bf [.....\..... ....]
2671
+ # 0030: 00 00 00 00 88 7f c9 bf
2672
+ # 2. 20 byte records
2673
+ # 0000: c1 d8 d9 0b 00 00 00 00 f5 83 14 80 df 7f fe 7f [................]
2674
+ # 0010: fe 7f 01 80
2675
+ if ($id == 0x300) {
2676
+ if ($len % 20 and not $len % 56) {
2677
+ $dlen = 56;
2678
+ } elsif ($len % 56 and not $len % 20) {
2679
+ $dlen = 20;
2680
+ } else {
2681
+ if ($raf->Read($buff, 20) == 20) {
2682
+ if (substr($buff, 16, 3) eq "\0\0\0") {
2683
+ $dlen = 56;
2684
+ } else {
2685
+ $dlen = 20;
2686
+ }
2687
+ }
2688
+ $raf->Seek($epos, 2) or last;
2689
+ }
2690
+ }
2624
2691
  # limit the number of records we read if necessary
2625
- if ($insvLimit{$id} and $len > $insvLimit{$id}[1] * $dlen and
2692
+ if ($dlen and $insvLimit{$id} and $len > $insvLimit{$id}[1] * $dlen and
2626
2693
  $et->Warn("Insta360 $insvLimit{$id}[0] data is huge. Processing only the first $insvLimit{$id}[1] records",2))
2627
2694
  {
2628
2695
  $len = $insvLimit{$id}[1] * $dlen;
@@ -2630,11 +2697,18 @@ sub ProcessInsta360($;$)
2630
2697
  $raf->Read($buff, $len) == $len or last;
2631
2698
  $et->VerboseDump(\$buff) if $verbose > 2;
2632
2699
  if ($dlen) {
2633
- $len % $dlen and $et->Warn(sprintf('Unexpected Insta360 record 0x%x length',$id)), last;
2634
- if ($id == 0x300) {
2700
+ if ($len % $dlen) {
2701
+ $et->Warn(sprintf('Unexpected Insta360 record 0x%x length',$id));
2702
+ } elsif ($id == 0x300) {
2635
2703
  for ($p=0; $p<$len; $p+=$dlen) {
2636
2704
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2637
- my @a = map { GetDouble(\$buff, $p + 8 * $_) } 1..6;
2705
+ my @a;
2706
+ if ($dlen == 56) {
2707
+ @a = map { GetDouble(\$buff, $p + 8 * $_) } 1..6;
2708
+ } else {
2709
+ @a = unpack("x${p}x8v6", $buff);
2710
+ map { $_ = ($_ - 0x8000) / 1000 } @a;
2711
+ }
2638
2712
  $et->HandleTag($tagTbl, TimeCode => sprintf('%.3f', Get64u(\$buff, $p) / 1000));
2639
2713
  $et->HandleTag($tagTbl, Accelerometer => "@a[0..2]"); # (NC)
2640
2714
  $et->HandleTag($tagTbl, AngularVelocity => "@a[3..5]"); # (NC)
@@ -2699,6 +2773,38 @@ sub ProcessInsta360($;$)
2699
2773
  return 1;
2700
2774
  }
2701
2775
 
2776
+ #------------------------------------------------------------------------------
2777
+ # Process Garmin GPS 'uuid' atom (ref PH)
2778
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2779
+ # Returns: 1 on success
2780
+ # Note: This format is used by the Garmin DriveAssist 51, but the DriveAssist 50
2781
+ # uses a completely different format. :(
2782
+ sub ProcessGarminGPS($$$)
2783
+ {
2784
+ my ($et, $dirInfo, $tagTbl) = @_;
2785
+ my $dataPt = $$dirInfo{DataPt};
2786
+ my $dataLen = length $$dataPt;
2787
+ my $pos = 33;
2788
+ my $epoch = (66 * 365 + 17) * 24 * 3600; # time is relative to Jan 1, 1904
2789
+ my $scl = 180 / (32768 * 65536); # scaling factor for lat/lon
2790
+ $et->VerboseDir('GarminGPS');
2791
+ while ($pos + 20 <= $dataLen) {
2792
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2793
+ my $time = Image::ExifTool::ConvertUnixTime(Get32u($dataPt, $pos) - $epoch) . 'Z';
2794
+ my $lat = Get32s($dataPt, $pos + 12) * $scl;
2795
+ my $lon = Get32s($dataPt, $pos + 16) * $scl;
2796
+ my $spd = Get16u($dataPt, $pos + 4); # (in mph)
2797
+ $et->HandleTag($tagTbl, 'GPSDateTime', $time);
2798
+ $et->HandleTag($tagTbl, 'GPSLatitude', $lat);
2799
+ $et->HandleTag($tagTbl, 'GPSLongitude', $lon);
2800
+ $et->HandleTag($tagTbl, 'GPSSpeed', $spd);
2801
+ $et->HandleTag($tagTbl, 'GPSSpeedRef', 'M');
2802
+ $pos += 20;
2803
+ }
2804
+ delete $$et{DOC_NUM};
2805
+ return 1;
2806
+ }
2807
+
2702
2808
  #------------------------------------------------------------------------------
2703
2809
  # Process 360Fly 'uuid' atom containing sensor data
2704
2810
  # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
@@ -2776,9 +2882,9 @@ sub ScanMediaData($)
2776
2882
  my ($pos, $buf2) = (0, '');
2777
2883
 
2778
2884
  # don't rescan for freeGPS if we already found embedded metadata
2779
- my $dataPos = $$et{VALUE}{MediaDataOffset};
2885
+ my $dataPos = $$et{MediaDataOffset};
2780
2886
  if ($dataPos and not $$et{DOC_COUNT}) {
2781
- $dataLen = $$et{VALUE}{MediaDataSize};
2887
+ $dataLen = $$et{MediaDataSize};
2782
2888
  if ($dataLen) {
2783
2889
  if ($raf->Seek($dataPos, 0)) {
2784
2890
  $$et{FreeGPS2} = { }; # initialize variable space for FreeGPS2()