exiftool-vendored.exe 12.28.0 → 12.34.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 (49) hide show
  1. package/bin/exiftool_files/Changes +108 -5
  2. package/bin/exiftool_files/README +2 -2
  3. package/bin/exiftool_files/arg_files/xmp2exif.args +2 -1
  4. package/bin/exiftool_files/config_files/example.config +1 -1
  5. package/bin/exiftool_files/exiftool.pl +40 -26
  6. package/bin/exiftool_files/fmt_files/gpx.fmt +1 -1
  7. package/bin/exiftool_files/fmt_files/gpx_wpt.fmt +1 -1
  8. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +16 -3
  9. package/bin/exiftool_files/lib/Image/ExifTool/CBOR.pm +331 -0
  10. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +35 -5
  11. package/bin/exiftool_files/lib/Image/ExifTool/Charset.pm +2 -0
  12. package/bin/exiftool_files/lib/Image/ExifTool/DPX.pm +13 -2
  13. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +98 -4
  14. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +1 -0
  15. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +13 -2
  16. package/bin/exiftool_files/lib/Image/ExifTool/GoPro.pm +16 -1
  17. package/bin/exiftool_files/lib/Image/ExifTool/ICC_Profile.pm +96 -4
  18. package/bin/exiftool_files/lib/Image/ExifTool/ID3.pm +15 -3
  19. package/bin/exiftool_files/lib/Image/ExifTool/JSON.pm +7 -3
  20. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +60 -26
  21. package/bin/exiftool_files/lib/Image/ExifTool/Lang/nl.pm +60 -59
  22. package/bin/exiftool_files/lib/Image/ExifTool/M2TS.pm +81 -14
  23. package/bin/exiftool_files/lib/Image/ExifTool/MacOS.pm +2 -2
  24. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +12 -3
  25. package/bin/exiftool_files/lib/Image/ExifTool/NikonSettings.pm +10 -2
  26. package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +8 -1
  27. package/bin/exiftool_files/lib/Image/ExifTool/Other.pm +93 -0
  28. package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +11 -12
  29. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +7 -6
  30. package/bin/exiftool_files/lib/Image/ExifTool/Panasonic.pm +2 -2
  31. package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +2 -1
  32. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +67 -9
  33. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +133 -119
  34. package/bin/exiftool_files/lib/Image/ExifTool/README +9 -2
  35. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +6 -1
  36. package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +47 -10
  37. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +80 -32
  38. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +139 -4
  39. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +224 -30
  40. package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +1 -0
  41. package/bin/exiftool_files/lib/Image/ExifTool/WritePNG.pl +2 -0
  42. package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +17 -3
  43. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +43 -0
  44. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +21 -8
  45. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +4 -1
  46. package/bin/exiftool_files/lib/Image/ExifTool/XMPStruct.pl +3 -1
  47. package/bin/exiftool_files/lib/Image/ExifTool.pm +8892 -8839
  48. package/bin/exiftool_files/lib/Image/ExifTool.pod +24 -15
  49. package/package.json +3 -3
@@ -25,6 +25,7 @@ sub Process_mebx($$$);
25
25
  sub ProcessFreeGPS($$$);
26
26
  sub ProcessFreeGPS2($$$);
27
27
  sub Process360Fly($$$);
28
+ sub ProcessFMAS($$$);
28
29
 
29
30
  # QuickTime data types that have ExifTool equivalents
30
31
  # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
@@ -98,15 +99,15 @@ my %insvLimit = (
98
99
  The tags below are extracted from timed metadata in QuickTime and other
99
100
  formats of video files when the ExtractEmbedded option is used. Although
100
101
  most of these tags are combined into the single table below, ExifTool
101
- currently reads 53 different formats of timed GPS metadata from video files.
102
+ currently reads 57 different formats of timed GPS metadata from video files.
102
103
  },
103
104
  VARS => { NO_ID => 1 },
104
105
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
105
106
  GPSLongitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")' },
106
107
  GPSAltitude => { PrintConv => '(sprintf("%.4f", $val) + 0) . " m"' }, # round to 4 decimals
107
- GPSSpeed => { PrintConv => 'sprintf("%.4f", $val) + 0' }, # round to 4 decimals
108
+ GPSSpeed => { PrintConv => 'sprintf("%.4f", $val) + 0', Notes => 'in km/h unless GPSSpeedRef says otherwise' },
108
109
  GPSSpeedRef => { PrintConv => { K => 'km/h', M => 'mph', N => 'knots' } },
109
- GPSTrack => { PrintConv => 'sprintf("%.4f", $val) + 0' }, # round to 4 decimals
110
+ GPSTrack => { PrintConv => 'sprintf("%.4f", $val) + 0', Notes => 'relative to true north unless GPSTrackRef says otherwise' },
110
111
  GPSTrackRef => { PrintConv => { M => 'Magnetic North', T => 'True North' } },
111
112
  GPSDateTime => {
112
113
  Groups => { 2 => 'Time' },
@@ -181,6 +182,13 @@ my %insvLimit = (
181
182
  TagTable => 'Image::ExifTool::QuickTime::Stream',
182
183
  ProcessProc => \&Process_text,
183
184
  },
185
+ },{
186
+ Name => 'gpmd_FMAS', # Vantrue N2S binary format
187
+ Condition => '$$valPt =~ /^FMAS\0\0\0\0/',
188
+ SubDirectory => {
189
+ TagTable => 'Image::ExifTool::QuickTime::Stream',
190
+ ProcessProc => \&ProcessFMAS,
191
+ },
184
192
  },{
185
193
  Name => 'gpmd_GoPro',
186
194
  SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
@@ -901,14 +909,8 @@ sub Process_text($$$)
901
909
  $tags{GPSDateTime} = $dateTime;
902
910
  $tags{GPSLatitude} = (($4 || 0) + $5/60) * ($6 eq 'N' ? 1 : -1);
903
911
  $tags{GPSLongitude} = (($7 || 0) + $8/60) * ($9 eq 'E' ? 1 : -1);
904
- if (length $10) {
905
- $tags{GPSSpeed} = $10 * $knotsToKph;
906
- $tags{GPSSpeedRef} = 'K';
907
- }
908
- if (length $11) {
909
- $tags{GPSTrack} = $11;
910
- $tags{GPSTrackRef} = 'T';
911
- }
912
+ $tags{GPSSpeed} = $10 * $knotsToKph if length $10;
913
+ $tags{GPSTrack} = $11 if length $11;
912
914
  } elsif ($tag =~ /^[A-Z]{2}GGA$/ and $dat =~ /^,(\d{2})(\d{2})(\d+(?:\.\d*)?),(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),[1-6]?,(\d+)?,(\.\d+|\d+\.?\d*)?,(-?\d+\.?\d*)?,M?/s) {
913
915
  my $time = "$1:$2:$3";
914
916
  if ($$et{LastTime}) {
@@ -988,10 +990,7 @@ sub Process_text($$$)
988
990
  $val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$dataPt, 0x39, 5)));
989
991
  $tags{GPSAltitude} = $val + 0 if $val =~ /^[-+]\d+$/;
990
992
  $val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$dataPt, 0x3e, 3)));
991
- if ($val =~ /^\d+$/) {
992
- $tags{GPSSpeed} = $val + 0;
993
- $tags{GPSSpeedRef} = 'K';
994
- }
993
+ $tags{GPSSpeed} = $val + 0 if $val =~ /^\d+$/;
995
994
  if ($$dataPt =~ /^\0\0..\xaa\xaa/s) { # (BlueSkySea)
996
995
  $val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$dataPt, 0xad, 12)));
997
996
  # the first X,Y,Z accelerometer readings from the AccelerometerData
@@ -1022,10 +1021,7 @@ sub Process_text($$$)
1022
1021
  $tags{GPSLatitude} = $2;
1023
1022
  $tags{GPSLongitude} = $1;
1024
1023
  $tags{GPSAltitude} = $1 if $$dataPt =~ /,\s*H\s+([-+]?\d+\.?\d*)m/;
1025
- if ($$dataPt =~ /,\s*H.S\s+([-+]?\d+\.?\d*)/) {
1026
- $tags{GPSSpeed} = $1 * $mpsToKph;
1027
- $tags{GPSSpeedRef} = 'K';
1028
- }
1024
+ $tags{GPSSpeed} = $1 * $mpsToKph if $$dataPt =~ /,\s*H.S\s+([-+]?\d+\.?\d*)/;
1029
1025
  $tags{Distance} = $1 * $mpsToKph if $$dataPt =~ /,\s*D\s+(\d+\.?\d*)m/;
1030
1026
  $tags{VerticalSpeed} = $1 if $$dataPt =~ /,\s*V.S\s+([-+]?\d+\.?\d*)/;
1031
1027
  $tags{FNumber} = $1 if $$dataPt =~ /\bF\/(\d+\.?\d*)/;
@@ -1086,14 +1082,8 @@ sub Process_text($$$)
1086
1082
  $tags{GPSDateTime} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $year, $14, $13, $1, $2, $3);
1087
1083
  $tags{GPSLatitude} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
1088
1084
  $tags{GPSLongitude} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
1089
- if (length $11) {
1090
- $tags{GPSSpeed} = $11 * $knotsToKph;
1091
- $tags{GPSSpeedRef} = 'K';
1092
- }
1093
- if (length $12) {
1094
- $tags{GPSTrack} = $12;
1095
- $tags{GPSTrackRef} = 'T';
1096
- }
1085
+ $tags{GPSSpeed} = $11 * $knotsToKph if length $11;
1086
+ $tags{GPSTrack} = $12 if length $12;
1097
1087
  }
1098
1088
  $tags{GSensor} = $1 if $$dataPt =~ /\bgsensori,(.*?)(;|$)/;
1099
1089
  $tags{Car} = $1 if $$dataPt =~ /\bCAR,(.*?)(;|$)/;
@@ -1230,7 +1220,10 @@ sub ProcessSamples($)
1230
1220
  $et->VPrint(1, "${hdr}, Sample ".($i+1).' of '.scalar(@$start)." ($size bytes)\n");
1231
1221
  $et->VerboseDump(\$buff, Addr => $$start[$i]);
1232
1222
  }
1233
- if ($type eq 'text') {
1223
+ if ($type eq 'text' or
1224
+ # (PNDM is normally 'text', but was sbtl/tx3g in concatenated Garmin sample output_3videos.mp4)
1225
+ ($type eq 'sbtl' and $metaFormat eq 'tx3g' and $buff =~ /^..PNDM/s))
1226
+ {
1234
1227
 
1235
1228
  FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
1236
1229
  unless ($buff =~ /^\$BEGIN/) {
@@ -1265,8 +1258,7 @@ sub ProcessSamples($)
1265
1258
  next if length($buff) < 20 + $n;
1266
1259
  $et->HandleTag($tagTbl, GPSLatitude => Get32s(\$buff, 12+$n) * 180/0x80000000);
1267
1260
  $et->HandleTag($tagTbl, GPSLongitude => Get32s(\$buff, 16+$n) * 180/0x80000000);
1268
- $et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, 8+$n) * $mphToKph);
1269
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1261
+ $et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, 8+$n) * $mphToKph);
1270
1262
  SetGPSDateTime($et, $tagTbl, $time[$i]);
1271
1263
  next; # all done (don't store/process as text)
1272
1264
  }
@@ -1339,6 +1331,19 @@ sub ProcessSamples($)
1339
1331
  $$et{HandlerType} = $$et{HanderDesc} = '';
1340
1332
  }
1341
1333
 
1334
+ #------------------------------------------------------------------------------
1335
+ # Convert latitude/longitude from DDDMM.MMMM format to decimal degrees
1336
+ # Inputs: 0) latitude, 1) longitude
1337
+ # Returns: lat/lon are changed in place
1338
+ # (note: this method works fine for negative coordinates)
1339
+ sub ConvertLatLon($$)
1340
+ {
1341
+ my $deg = int($_[0] / 100); # latitude
1342
+ $_[0] = $deg + ($_[0] - $deg * 100) / 60;
1343
+ $deg = int($_[1] / 100); # longitude
1344
+ $_[1] = $deg + ($_[1] - $deg * 100) / 60;
1345
+ }
1346
+
1342
1347
  #------------------------------------------------------------------------------
1343
1348
  # Process "freeGPS " data blocks referenced by a 'gps ' (GPSDataList) atom
1344
1349
  # Inputs: 0) ExifTool ref, 1) dirInfo ref {DataPt,SampleTime,SampleDuration}, 2) tagTable ref
@@ -1350,7 +1355,7 @@ sub ProcessFreeGPS($$$)
1350
1355
  my ($et, $dirInfo, $tagTbl) = @_;
1351
1356
  my $dataPt = $$dirInfo{DataPt};
1352
1357
  my $dirLen = length $$dataPt;
1353
- my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl);
1358
+ my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl, $ddd);
1354
1359
  my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, @acc, @xtra);
1355
1360
 
1356
1361
  return 0 if $dirLen < 92;
@@ -1475,7 +1480,7 @@ sub ProcessFreeGPS($$$)
1475
1480
  $lat = GetFloat($dataPt, 0x1c);
1476
1481
  $lon = GetFloat($dataPt, 0x20);
1477
1482
  $et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
1478
- $et->WarnOnce('GPSLatitude/Longitude encoding is not yet known, so these will be wrong');
1483
+ $et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1479
1484
  $lat = abs $lat;
1480
1485
  $lon = abs $lon;
1481
1486
  $spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
@@ -1505,6 +1510,23 @@ sub ProcessFreeGPS($$$)
1505
1510
  $trk -= 360 if $trk >= 360;
1506
1511
  SetByteOrder('MM');
1507
1512
 
1513
+ } elsif ($$dataPt =~ /^.{60}4W`b]S</s and length($$dataPt) >= 140) {
1514
+
1515
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 01 00 00 [..@.freeGPS ....]
1516
+ # 0010: 5a 58 53 42 4e 58 59 53 00 00 00 00 00 00 00 00 [ZXSBNXYS........]
1517
+ # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1518
+ # 0030: 00 00 00 00 00 00 00 00 00 00 00 00 34 57 60 62 [............4W`b]
1519
+ # 0040: 5d 53 3c 41 44 45 41 41 42 3e 40 40 3c 51 3c 45 []S<ADEAAB>@@<Q<E]
1520
+ # 0050: 41 40 43 3e 41 47 49 48 44 3c 5e 3c 40 41 46 43 [A@C>AGIHD<^<@AFC]
1521
+ # 0060: 42 3e 49 49 40 42 45 3c 55 3c 45 47 3e 45 43 41 [B>II@BE<U<EG>ECA]
1522
+ # decipher $GPRMC by subtracting 16 from each character value
1523
+ $_ = pack 'C*', map { $_>=16 and $_-=16 } unpack('x60C80', $$dataPt);
1524
+ return 0 unless /[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?\d{1,2}\.\d+),([NS]),(\d*?\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/;
1525
+ ($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
1526
+ $yr += ($yr >= 70 ? 1900 : 2000);
1527
+ $spd = $9 * $knotsToKph if length $9;
1528
+ $trk = $10 if length $10;
1529
+
1508
1530
  } elsif ($$dataPt =~ /^.{16}YndAkasoCar/s) {
1509
1531
 
1510
1532
  # Akaso V1 dascham
@@ -1523,13 +1545,17 @@ sub ProcessFreeGPS($$$)
1523
1545
  return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
1524
1546
  ($lonRef eq 'E' or $lonRef eq 'W');
1525
1547
 
1526
- $et->WarnOnce("Can't yet decrypt Akaso V1 timed GPS", 1);
1548
+ $et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1527
1549
  # (see https://exiftool.org/forum/index.php?topic=11320.0)
1528
- return 1;
1529
1550
 
1530
1551
  SetByteOrder('II');
1552
+
1553
+ $spd = GetFloat($dataPt, 0x60);
1554
+ $trk = GetFloat($dataPt, 0x64) + 180; # (why is this off by 180?)
1531
1555
  $lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
1532
1556
  $lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
1557
+ $ddd = 1; # don't convert until we know what the format is
1558
+
1533
1559
  SetByteOrder('MM');
1534
1560
  #my $serialNum = substr($$dataPt, 0x68, 20);
1535
1561
 
@@ -1593,10 +1619,7 @@ sub ProcessFreeGPS($$$)
1593
1619
  #
1594
1620
  FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
1595
1621
  # lat/long are in DDDMM.MMMM format
1596
- my $deg = int($lat / 100);
1597
- $lat = $deg + ($lat - $deg * 100) / 60;
1598
- $deg = int($lon / 100);
1599
- $lon = $deg + ($lon - $deg * 100) / 60;
1622
+ ConvertLatLon($lat, $lon) unless $ddd;
1600
1623
  $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
1601
1624
  if (defined $yr) {
1602
1625
  my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
@@ -1608,14 +1631,8 @@ sub ProcessFreeGPS($$$)
1608
1631
  $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
1609
1632
  $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
1610
1633
  $et->HandleTag($tagTbl, GPSAltitude => $alt) if defined $alt;
1611
- if (defined $spd) {
1612
- $et->HandleTag($tagTbl, GPSSpeed => $spd);
1613
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1614
- }
1615
- if (defined $trk) {
1616
- $et->HandleTag($tagTbl, GPSTrack => $trk);
1617
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
1618
- }
1634
+ $et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd;
1635
+ $et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
1619
1636
  while (@xtra) {
1620
1637
  my $tag = shift @xtra;
1621
1638
  $et->HandleTag($tagTbl, $tag => shift @xtra);
@@ -1734,9 +1751,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1734
1751
  $et->HandleTag($tagTbl, GPSLatitude => Get32s(\$b, 0x10) / 1e7);
1735
1752
  $et->HandleTag($tagTbl, GPSLongitude => Get32s(\$b, 0x18) / 1e7);
1736
1753
  $et->HandleTag($tagTbl, GPSSpeed => Get32s(\$b, 0x20) / 100 * $mpsToKph);
1737
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1738
1754
  $et->HandleTag($tagTbl, GPSTrack => $trk);
1739
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
1740
1755
  $et->HandleTag($tagTbl, GPSAltitude => Get32s(\$b, 0x28) / 1000);
1741
1756
  $lastRecPos = $recPos;
1742
1757
  $foundNew = 1;
@@ -1857,17 +1872,12 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1857
1872
  $trk = GetFloat(\$dat, 16);
1858
1873
  @acc = unpack('x20V3', $dat);
1859
1874
  map { $_ = $_ - 4294967296 if $_ >= 0x80000000 } @acc;
1860
- my $deg = int($lat / 100);
1861
- $lat = $deg + ($lat - $deg * 100) / 60;
1862
- $deg = int($lon / 100);
1863
- $lon = $deg + ($lon - $deg * 100) / 60;
1875
+ ConvertLatLon($lat, $lon);
1864
1876
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1865
1877
  $et->HandleTag($tagTbl, GPSLatitude => $lat * (substr($dat,1,1) eq 'S' ? -1 : 1));
1866
1878
  $et->HandleTag($tagTbl, GPSLongitude => $lon * (substr($dat,2,1) eq 'W' ? -1 : 1));
1867
1879
  $et->HandleTag($tagTbl, GPSSpeed => $spd);
1868
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1869
1880
  $et->HandleTag($tagTbl, GPSTrack => $trk);
1870
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
1871
1881
  $et->HandleTag($tagTbl, Accelerometer => "@acc");
1872
1882
  }
1873
1883
  return 1;
@@ -1913,9 +1923,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1913
1923
  $et->HandleTag($tagTbl, GPSLatitude => $lat);
1914
1924
  $et->HandleTag($tagTbl, GPSLongitude => $lon);
1915
1925
  $et->HandleTag($tagTbl, GPSSpeed => $spd / 100 * $mpsToKph);
1916
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1917
1926
  $et->HandleTag($tagTbl, GPSTrack => $trk);
1918
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
1919
1927
  last if $pos += 0x20 > length($$dataPt) - 0x1e;
1920
1928
  }
1921
1929
  return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
@@ -1928,23 +1936,12 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1928
1936
  $yr += 2000 if $yr < 2000;
1929
1937
  my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $yr, $mon, $day, $hr, $min, $sec);
1930
1938
  # convert from DDMM.MMMMMM to DD.DDDDDD format if necessary
1931
- unless ($ddd) {
1932
- my $deg = int($lat / 100);
1933
- $lat = $deg + ($lat - $deg * 100) / 60;
1934
- $deg = int($lon / 100);
1935
- $lon = $deg + ($lon - $deg * 100) / 60;
1936
- }
1939
+ ConvertLatLon($lat, $lon) unless $ddd;
1937
1940
  $et->HandleTag($tagTbl, GPSDateTime => $time);
1938
1941
  $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
1939
1942
  $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
1940
- if (defined $spd) {
1941
- $et->HandleTag($tagTbl, GPSSpeed => $spd); # (now in km/h)
1942
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1943
- }
1944
- if (defined $trk) {
1945
- $et->HandleTag($tagTbl, GPSTrack => $trk);
1946
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
1947
- }
1943
+ $et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd; # (now in km/h)
1944
+ $et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
1948
1945
  if (defined $alt) {
1949
1946
  $et->HandleTag($tagTbl, GPSAltitude => $alt);
1950
1947
  }
@@ -1952,6 +1949,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1952
1949
  return 1;
1953
1950
  }
1954
1951
 
1952
+
1955
1953
  #------------------------------------------------------------------------------
1956
1954
  # Extract embedded information referenced from a track
1957
1955
  # Inputs: 0) ExifTool ref, 1) tag name, 2) data ref
@@ -2036,16 +2034,12 @@ sub ParseTag($$$)
2036
2034
  SetGPSDateTime($et, $tagTbl, $a[2]);
2037
2035
  my $lat = $a[5] / 1e3;
2038
2036
  my $lon = $a[7] / 1e3;
2039
- my $deg = int($lat / 100);
2040
- $lat = $deg + ($lat - $deg * 100) / 60;
2041
- $deg = int($lon / 100);
2042
- $lon = $deg + ($lon - $deg * 100) / 60;
2043
- $lat = -$lat if $a[4] eq 'S';
2044
- $lon = -$lon if $a[6] eq 'W';
2037
+ ConvertLatLon($lat, $lon);
2038
+ $lat = -abs($lat) if $a[4] eq 'S';
2039
+ $lon = -abs($lon) if $a[6] eq 'W';
2045
2040
  $et->HandleTag($tagTbl, GPSLatitude => $lat);
2046
2041
  $et->HandleTag($tagTbl, GPSLongitude => $lon);
2047
- $et->HandleTag($tagTbl, GPSSpeed => $a[3] / 1e3);
2048
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2042
+ $et->HandleTag($tagTbl, GPSSpeed => $a[3] / 1e3);
2049
2043
  $pos += 36;
2050
2044
  }
2051
2045
  SetByteOrder('MM');
@@ -2107,9 +2101,9 @@ sub Process_mebx($$$)
2107
2101
 
2108
2102
  # parse using information from 'keys' table (eg. Apple iPhone7+ hevc 'Core Media Data Handler')
2109
2103
  $et->VerboseDir('mebx', undef, length $$dataPt);
2110
- my $pos = 0;
2111
- while ($pos + 8 < length $$dataPt) {
2112
- my $len = Get32u($dataPt, $pos);
2104
+ my ($pos, $len);
2105
+ for ($pos=0; $pos+8<length($$dataPt); $pos+=$len) {
2106
+ $len = Get32u($dataPt, $pos);
2113
2107
  last if $len < 8 or $pos + $len > length $$dataPt;
2114
2108
  my $id = substr($$dataPt, $pos+4, 4);
2115
2109
  my $info = $$ee{'keys'}{$id};
@@ -2132,7 +2126,6 @@ sub Process_mebx($$$)
2132
2126
  } else {
2133
2127
  $et->WarnOnce('No key information for mebx ID ' . PrintableTagID($id,1));
2134
2128
  }
2135
- $pos += $len;
2136
2129
  }
2137
2130
  return 1;
2138
2131
  }
@@ -2188,20 +2181,14 @@ sub Process_gps0($$$)
2188
2181
  my $lat = GetDouble($dataPt, $pos);
2189
2182
  my $lon = GetDouble($dataPt, $pos+8);
2190
2183
  next if abs($lat) > 9000 or abs($lon) > 18000;
2191
- # (note: this method works fine for negative coordinates)
2192
- my $deg = int($lat / 100);
2193
- $lat = $deg + ($lat - $deg * 100) / 60;
2194
- $deg = int($lon / 100);
2195
- $lon = $deg + ($lon - $deg * 100) / 60;
2184
+ ConvertLatLon($lat, $lon);
2196
2185
  my @a = unpack('C*', substr($$dataPt, $pos+22, 6)); # unpack date/time
2197
2186
  $a[0] += 2000;
2198
2187
  $et->HandleTag($tagTbl, GPSDateTime => sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ", @a));
2199
2188
  $et->HandleTag($tagTbl, GPSLatitude => $lat);
2200
2189
  $et->HandleTag($tagTbl, GPSLongitude => $lon);
2201
2190
  $et->HandleTag($tagTbl, GPSSpeed => Get16u($dataPt, $pos+0x14));
2202
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2203
2191
  $et->HandleTag($tagTbl, GPSTrack => Get8u($dataPt, $pos+0x1c) * 2); # (NC)
2204
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2205
2192
  $et->HandleTag($tagTbl, GPSAltitude => Get32s($dataPt, $pos + 0x10));
2206
2193
  # yet to be decoded:
2207
2194
  # 0x1d - int8u[3] seen: "1 1 0"
@@ -2290,10 +2277,7 @@ sub ProcessRIFFTrailer($$$)
2290
2277
  my $lat = GetDouble(\$buff, $pos+4);
2291
2278
  my $lon = GetDouble(\$buff, $pos+12);
2292
2279
  $et->Warn('Bad gps0 record') and last if abs($lat) > 9000 or abs($lon) > 18000;
2293
- my $deg = int($lat / 100);
2294
- $lat = $deg + ($lat - $deg * 100) / 60;
2295
- $deg = int($lon / 100);
2296
- $lon = $deg + ($lon - $deg * 100) / 60;
2280
+ ConvertLatLon($lat, $lon);
2297
2281
  $lat = -$lat if Get8u(\$buff, $pos+0x21) == 2; # wild guess
2298
2282
  $lon = -$lon if Get8u(\$buff, $pos+0x22) == 2; # wild guess
2299
2283
  my @a = unpack('C*', substr($buff, $pos+26, 6)); # unpack date/time
@@ -2303,9 +2287,7 @@ sub ProcessRIFFTrailer($$$)
2303
2287
  $et->HandleTag($tagTbl, GPSLatitude => $lat);
2304
2288
  $et->HandleTag($tagTbl, GPSLongitude => $lon);
2305
2289
  $et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, $pos+0x18) * $knotsToKph);
2306
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2307
2290
  $et->HandleTag($tagTbl, GPSTrack => Get8u(\$buff, $pos+0x20) * 2);
2308
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2309
2291
  }
2310
2292
  } elsif ($tag eq 'gsen') {
2311
2293
  # (similar to record decoded in Process_gsen)
@@ -2359,17 +2341,11 @@ sub ProcessNMEA($$$)
2359
2341
  $et->HandleTag($tagTbl, GPSDateTime => $fix{dat});
2360
2342
  $et->HandleTag($tagTbl, GPSLatitude => $fix{lat});
2361
2343
  $et->HandleTag($tagTbl, GPSLongitude => $fix{lon});
2362
- if (defined $fix{spd}) {
2363
- $et->HandleTag($tagTbl, GPSSpeed => $fix{spd} * $knotsToKph);
2364
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2365
- }
2366
- if (defined $fix{trk}) {
2367
- $et->HandleTag($tagTbl, GPSTrack => $fix{trk});
2368
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2369
- }
2370
- $et->HandleTag($tagTbl, GPSAltitude => $fix{alt}) if defined $fix{alt};
2371
- $et->HandleTag($tagTbl, GPSSatellites => $fix{nsats}+0) if defined $fix{nsats};
2372
- $et->HandleTag($tagTbl, GPSDOP => $fix{hdop}) if defined $fix{hdop};
2344
+ $et->HandleTag($tagTbl, GPSSpeed => $fix{spd} * $knotsToKph) if defined $fix{spd};
2345
+ $et->HandleTag($tagTbl, GPSTrack => $fix{trk}) if defined $fix{trk};
2346
+ $et->HandleTag($tagTbl, GPSAltitude => $fix{alt}) if defined $fix{alt};
2347
+ $et->HandleTag($tagTbl, GPSSatellites=> $fix{nsats}+0) if defined $fix{nsats};
2348
+ $et->HandleTag($tagTbl, GPSDOP => $fix{hdop}) if defined $fix{hdop};
2373
2349
  }
2374
2350
  undef %fix;
2375
2351
  }
@@ -2523,9 +2499,7 @@ sub ProcessTTAD($$$)
2523
2499
  $et->HandleTag($tagTbl, GPSLongitude => GetDouble($dataPt, $pos+0x24));
2524
2500
  $et->HandleTag($tagTbl, GPSAltitude => GetDouble($dataPt, $pos+0x14));
2525
2501
  $et->HandleTag($tagTbl, GPSSpeed => GetDouble($dataPt, $pos+0x0c) * $mpsToKph);
2526
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2527
2502
  $et->HandleTag($tagTbl, GPSTrack => GetDouble($dataPt, $pos+0x30));
2528
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2529
2503
  if ($unknown) {
2530
2504
  my @a = map { GetDouble($dataPt, $pos+0x38+8*$_) } 0..2;
2531
2505
  $et->HandleTag($tagTbl, Unknown03 => "@a");
@@ -2645,21 +2619,26 @@ sub ProcessInsta360($;$)
2645
2619
  my $tmp = substr($buff, $p, $dlen);
2646
2620
  my @a = unpack('VVvaa8aa8aa8a8a8', $tmp);
2647
2621
  next unless $a[3] eq 'A'; # (ignore void fixes)
2648
- last unless ($a[5] eq 'N' or $a[5] eq 'S') and # (quick validation)
2649
- ($a[7] eq 'E' or $a[7] eq 'W');
2622
+ unless (($a[5] eq 'N' or $a[5] eq 'S') and # (quick validation)
2623
+ ($a[7] eq 'E' or $a[7] eq 'W' or
2624
+ # (odd, but I've seen "O" instead of "W". Perhaps
2625
+ # when the language is french? ie. "Ouest"?)
2626
+ $a[7] eq 'O'))
2627
+ {
2628
+ $et->Warn('Unrecognized INSV GPS format');
2629
+ last;
2630
+ }
2650
2631
  $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2651
2632
  $a[$_] = GetDouble(\$a[$_], 0) foreach 4,6,8,9,10;
2652
2633
  $a[4] = -abs($a[4]) if $a[5] eq 'S'; # (abs just in case it was already signed)
2653
- $a[6] = -abs($a[6]) if $a[7] eq 'W';
2654
- $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($a[0]) . 'Z');
2655
- $et->HandleTag($tagTbl, GPSLatitude => $a[4]);
2634
+ $a[6] = -abs($a[6]) if $a[7] ne 'E';
2635
+ $et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($a[0]) . 'Z');
2636
+ $et->HandleTag($tagTbl, GPSLatitude => $a[4]);
2656
2637
  $et->HandleTag($tagTbl, GPSLongitude => $a[6]);
2657
- $et->HandleTag($tagTbl, GPSSpeed => $a[8] * $mpsToKph);
2658
- $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
2659
- $et->HandleTag($tagTbl, GPSTrack => $a[9]);
2660
- $et->HandleTag($tagTbl, GPSTrackRef => 'T');
2661
- $et->HandleTag($tagTbl, GPSAltitude => $a[10]);
2662
- $et->HandleTag($tagTbl, Unknown02 => "@a[1,2]") if $unknown; # millisecond counter (https://exiftool.org/forum/index.php?topic=9884.msg65143#msg65143)
2638
+ $et->HandleTag($tagTbl, GPSSpeed => $a[8] * $mpsToKph);
2639
+ $et->HandleTag($tagTbl, GPSTrack => $a[9]);
2640
+ $et->HandleTag($tagTbl, GPSAltitude => $a[10]);
2641
+ $et->HandleTag($tagTbl, Unknown02 => "@a[1,2]") if $unknown; # millisecond counter (https://exiftool.org/forum/index.php?topic=9884.msg65143#msg65143)
2663
2642
  }
2664
2643
  }
2665
2644
  } elsif ($id == 0x101) {
@@ -2715,6 +2694,41 @@ sub Process360Fly($$$)
2715
2694
  return 1;
2716
2695
  }
2717
2696
 
2697
+ #------------------------------------------------------------------------------
2698
+ # Process GPS from Vantrue N2S dashcam
2699
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2700
+ # Returns: 1 on success
2701
+ sub ProcessFMAS($$$)
2702
+ {
2703
+ my ($et, $dirInfo, $tagTbl) = @_;
2704
+ my $dataPt = $$dirInfo{DataPt};
2705
+ return 0 unless $$dataPt =~ /^FMAS\0\0\0\0.{72}SAMM.{36}A/s and length($$dataPt) >= 160;
2706
+ $et->VerboseDir('FMAS', undef, length($$dataPt));
2707
+ # 0000: 46 4d 41 53 00 00 00 00 00 00 00 00 00 00 00 00 [FMAS............]
2708
+ # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
2709
+ # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
2710
+ # 0030: 02 08 01 08 06 08 02 04 07 02 06 00 00 00 00 00 [................]
2711
+ # 0040: 00 00 00 00 00 00 00 00 4f 46 4e 49 4d 4d 41 53 [........OFNIMMAS]
2712
+ # 0050: 53 41 4d 4d 01 00 00 00 00 00 00 00 00 00 00 00 [SAMM............]
2713
+ # 0060: e5 07 09 18 08 00 22 00 02 00 00 00 a1 82 8a bf [......".........]
2714
+ # 0070: 89 23 8e bd 0b 2c 30 bc 41 57 4e 51 16 00 a1 01 [.#...,0.AWNQ....]
2715
+ # 0080: 29 26 27 0c 4b 00 49 00 00 00 00 00 00 00 00 00 [)&'.K.I.........]
2716
+ # 0090: 00 00 00 00 00 00 00 00 00 52 00 00 00 00 00 00 [.........R......]
2717
+ my @a = unpack('x96vCCCCCCx16AAACCCvCCvvv',$$dataPt);
2718
+ SetByteOrder('II');
2719
+ my $acc = ReadValue($dataPt, 0x6c, 'float', 3); # (looks like Z comes first in my sample)
2720
+ my $lon = $a[10] + ($a[11] + $a[13]/6000) / 60; # (why zero byte at $a[12]?)
2721
+ my $lat = $a[14] + ($a[15] + $a[16]/6000) / 60;
2722
+ $et->HandleTag($tagTbl, GPSDateTime => sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d', @a[0..5]));
2723
+ $et->HandleTag($tagTbl, GPSLatitude => $lat * ($a[9] eq 'S' ? -1 : 1));
2724
+ $et->HandleTag($tagTbl, GPSLongitude => $lon * ($a[8] eq 'W' ? -1 : 1));
2725
+ $et->HandleTag($tagTbl, GPSSpeed => $a[17] * $mphToKph); # convert mph -> kph
2726
+ $et->HandleTag($tagTbl, GPSTrack => $a[18]);
2727
+ $et->HandleTag($tagTbl, Accelerometer=> $acc);
2728
+ SetByteOrder('MM');
2729
+ return 1;
2730
+ }
2731
+
2718
2732
  #------------------------------------------------------------------------------
2719
2733
  # Scan media data for "freeGPS" metadata if not found already (ref PH)
2720
2734
  # Inputs: 0) ExifTool ref
@@ -408,6 +408,9 @@ numerical, and generated automatically otherwise.
408
408
  to be evaluated. Expression may access $val and $et,
409
409
  and is evaluated only when reading.
410
410
 
411
+ 'iTXt' - [PNG TextualData tags only] flag to write tag as PNG
412
+ iTXt chunk even if it contains no special characters.
413
+
411
414
  'List' - flag indicating that duplicate entries of this tag
412
415
  are allowed, and will be accumulated in a list. Note that for
413
416
  XMP information, 3 different types of lists are supported and
@@ -447,6 +450,10 @@ numerical, and generated automatically otherwise.
447
450
  may be written if the tag already exists. By default, all
448
451
  MakerNotes tags are permanent unless otherwise specified.
449
452
 
453
+ 'PreservePadding' - [QuickTime only] flag to preserve the
454
+ original size of the QuickTime atom by padding with nulls when
455
+ writing with the QuickTimePad option.
456
+
450
457
  'PrintHex' - specifies that unknown PrintConv values should
451
458
  be printed in hex (eg. 'Unknown (0x1)'). Also causes
452
459
  numerical tag values to be printed in hex in the HTML tag name
@@ -471,9 +478,9 @@ numerical, and generated automatically otherwise.
471
478
  tags in IFD1 of JPEG images which default to priority 0.
472
479
 
473
480
  'Protected' - bit mask to protect tags from writing:
474
- Bit 0x01 indicates an 'unsafe' tag, which is not set via
481
+ Bit 0x01 indicates an 'Unsafe' tag, which is not set via
475
482
  SetNewValuesFromFile() unless specified explicitly.
476
- Bit 0x02 indicates a 'protected' tag, which should not be set
483
+ Bit 0x02 indicates a 'Protected' tag, which should not be set
477
484
  directly by the user.
478
485
 
479
486
  'PutFirst' - [EXIF only] flag to place this value before IFD0
@@ -30,7 +30,7 @@ use strict;
30
30
  use vars qw($VERSION);
31
31
  use Image::ExifTool qw(:DataAccess :Utils);
32
32
 
33
- $VERSION = '1.58';
33
+ $VERSION = '1.59';
34
34
 
35
35
  sub ConvertTimecode($);
36
36
  sub ProcessSGLT($$$);
@@ -527,6 +527,10 @@ my %code2charset = (
527
527
  Name => 'Text',
528
528
  Notes => 'streamed text, extracted when the ExtractEmbedded option is used',
529
529
  },
530
+ 'id3 ' => {
531
+ Name => 'ID3',
532
+ SubDirectory => { TagTable => 'Image::ExifTool::ID3::Main' },
533
+ },
530
534
  #
531
535
  # WebP-specific tags
532
536
  #
@@ -818,6 +822,7 @@ my %code2charset = (
818
822
  ILGT => 'Lightness',
819
823
  IMED => 'Medium',
820
824
  INAM => 'Title',
825
+ ITRK => 'TrackNumber',
821
826
  IPLT => 'NumColors',
822
827
  IPRD => 'Product',
823
828
  ISBJ => 'Subject',