exiftool_vendored 12.17.1 → 12.32.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.
- checksums.yaml +4 -4
- data/bin/Changes +225 -1
- data/bin/MANIFEST +23 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +45 -43
- data/bin/arg_files/xmp2exif.args +2 -1
- data/bin/config_files/acdsee.config +193 -6
- data/bin/config_files/convert_regions.config +25 -14
- data/bin/config_files/cuepointlist.config +70 -0
- data/bin/config_files/example.config +2 -9
- data/bin/exiftool +142 -87
- data/bin/fmt_files/gpx.fmt +2 -2
- data/bin/fmt_files/gpx_wpt.fmt +2 -2
- data/bin/fmt_files/kml.fmt +1 -1
- data/bin/fmt_files/kml_track.fmt +1 -1
- data/bin/lib/Image/ExifTool/Apple.pm +3 -2
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +30 -12
- data/bin/lib/Image/ExifTool/CBOR.pm +277 -0
- data/bin/lib/Image/ExifTool/Canon.pm +49 -18
- data/bin/lib/Image/ExifTool/DJI.pm +6 -6
- data/bin/lib/Image/ExifTool/DPX.pm +13 -2
- data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
- data/bin/lib/Image/ExifTool/Exif.pm +28 -11
- data/bin/lib/Image/ExifTool/FITS.pm +13 -2
- data/bin/lib/Image/ExifTool/FlashPix.pm +35 -10
- data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
- data/bin/lib/Image/ExifTool/GPS.pm +22 -11
- data/bin/lib/Image/ExifTool/Geotag.pm +13 -2
- data/bin/lib/Image/ExifTool/GoPro.pm +16 -1
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
- data/bin/lib/Image/ExifTool/ID3.pm +15 -3
- data/bin/lib/Image/ExifTool/JPEG.pm +74 -4
- data/bin/lib/Image/ExifTool/JSON.pm +27 -4
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +393 -16
- data/bin/lib/Image/ExifTool/LIF.pm +153 -0
- data/bin/lib/Image/ExifTool/Lang/nl.pm +60 -59
- data/bin/lib/Image/ExifTool/M2TS.pm +137 -5
- data/bin/lib/Image/ExifTool/MIE.pm +4 -3
- data/bin/lib/Image/ExifTool/MRC.pm +341 -0
- data/bin/lib/Image/ExifTool/MWG.pm +3 -3
- data/bin/lib/Image/ExifTool/MXF.pm +1 -1
- data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
- data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
- data/bin/lib/Image/ExifTool/Nikon.pm +19 -8
- data/bin/lib/Image/ExifTool/NikonSettings.pm +28 -11
- data/bin/lib/Image/ExifTool/Olympus.pm +6 -3
- data/bin/lib/Image/ExifTool/Other.pm +93 -0
- data/bin/lib/Image/ExifTool/PDF.pm +9 -12
- data/bin/lib/Image/ExifTool/PNG.pm +8 -7
- data/bin/lib/Image/ExifTool/Panasonic.pm +28 -3
- data/bin/lib/Image/ExifTool/Pentax.pm +28 -5
- data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
- data/bin/lib/Image/ExifTool/Photoshop.pm +6 -0
- data/bin/lib/Image/ExifTool/QuickTime.pm +247 -88
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +283 -141
- data/bin/lib/Image/ExifTool/README +3 -0
- data/bin/lib/Image/ExifTool/RIFF.pm +89 -12
- data/bin/lib/Image/ExifTool/Samsung.pm +48 -10
- data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
- data/bin/lib/Image/ExifTool/Sony.pm +237 -78
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
- data/bin/lib/Image/ExifTool/TagLookup.pm +4125 -4028
- data/bin/lib/Image/ExifTool/TagNames.pod +644 -286
- data/bin/lib/Image/ExifTool/Torrent.pm +18 -11
- data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
- data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
- data/bin/lib/Image/ExifTool/WritePDF.pl +1 -0
- data/bin/lib/Image/ExifTool/WritePNG.pl +2 -0
- data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +55 -21
- data/bin/lib/Image/ExifTool/WriteXMP.pl +7 -3
- data/bin/lib/Image/ExifTool/Writer.pl +47 -10
- data/bin/lib/Image/ExifTool/XMP.pm +39 -14
- data/bin/lib/Image/ExifTool/XMP2.pl +2 -1
- data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
- data/bin/lib/Image/ExifTool/ZISRAW.pm +121 -2
- data/bin/lib/Image/ExifTool.pm +223 -72
- data/bin/lib/Image/ExifTool.pod +114 -93
- data/bin/perl-Image-ExifTool.spec +43 -42
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +28 -13
|
@@ -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)
|
|
@@ -65,8 +66,9 @@ my @dateMax = ( 24, 59, 59, 2200, 12, 31 );
|
|
|
65
66
|
my $gpsBlockSize = 0x8000;
|
|
66
67
|
|
|
67
68
|
# conversion factors
|
|
68
|
-
my $knotsToKph = 1.852;
|
|
69
|
-
my $mpsToKph = 3.6;
|
|
69
|
+
my $knotsToKph = 1.852; # knots --> km/h
|
|
70
|
+
my $mpsToKph = 3.6; # m/s --> km/h
|
|
71
|
+
my $mphToKph = 1.60934; # mph --> km/h
|
|
70
72
|
|
|
71
73
|
# handler types to process based on MetaFormat/OtherFormat
|
|
72
74
|
my %processByMetaFormat = (
|
|
@@ -94,18 +96,18 @@ my %insvLimit = (
|
|
|
94
96
|
%Image::ExifTool::QuickTime::Stream = (
|
|
95
97
|
GROUPS => { 2 => 'Location' },
|
|
96
98
|
NOTES => q{
|
|
97
|
-
|
|
98
|
-
the ExtractEmbedded option is used. Although
|
|
99
|
-
combined into the single table below, ExifTool
|
|
100
|
-
formats of timed GPS metadata from video files.
|
|
99
|
+
The tags below are extracted from timed metadata in QuickTime and other
|
|
100
|
+
formats of video files when the ExtractEmbedded option is used. Although
|
|
101
|
+
most of these tags are combined into the single table below, ExifTool
|
|
102
|
+
currently reads 57 different formats of timed GPS metadata from video files.
|
|
101
103
|
},
|
|
102
104
|
VARS => { NO_ID => 1 },
|
|
103
105
|
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
|
104
106
|
GPSLongitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")' },
|
|
105
107
|
GPSAltitude => { PrintConv => '(sprintf("%.4f", $val) + 0) . " m"' }, # round to 4 decimals
|
|
106
|
-
GPSSpeed => { PrintConv => 'sprintf("%.4f", $val) + 0'
|
|
108
|
+
GPSSpeed => { PrintConv => 'sprintf("%.4f", $val) + 0', Notes => 'in km/h unless GPSSpeedRef says otherwise' },
|
|
107
109
|
GPSSpeedRef => { PrintConv => { K => 'km/h', M => 'mph', N => 'knots' } },
|
|
108
|
-
GPSTrack => { PrintConv => 'sprintf("%.4f", $val) + 0'
|
|
110
|
+
GPSTrack => { PrintConv => 'sprintf("%.4f", $val) + 0', Notes => 'true north unless GPSTrackRef says otherwise' },
|
|
109
111
|
GPSTrackRef => { PrintConv => { M => 'Magnetic North', T => 'True North' } },
|
|
110
112
|
GPSDateTime => {
|
|
111
113
|
Groups => { 2 => 'Time' },
|
|
@@ -139,6 +141,7 @@ my %insvLimit = (
|
|
|
139
141
|
SampleTime => { Groups => { 2 => 'Video' }, PrintConv => 'ConvertDuration($val)', Notes => 'sample decoding time' },
|
|
140
142
|
SampleDuration=>{ Groups => { 2 => 'Video' }, PrintConv => 'ConvertDuration($val)' },
|
|
141
143
|
UserLabel => { Groups => { 2 => 'Other' } },
|
|
144
|
+
KiloCalories => { Groups => { 2 => 'Other' } },
|
|
142
145
|
SampleDateTime => {
|
|
143
146
|
Groups => { 2 => 'Time' },
|
|
144
147
|
ValueConv => q{
|
|
@@ -166,15 +169,29 @@ my %insvLimit = (
|
|
|
166
169
|
},
|
|
167
170
|
},
|
|
168
171
|
gpmd => [{
|
|
169
|
-
Name => '
|
|
170
|
-
Condition => '$$valPt
|
|
171
|
-
SubDirectory => {
|
|
172
|
+
Name => 'gpmd_Kingslim', # Kingslim D4 dashcam
|
|
173
|
+
Condition => '$$valPt =~ /^.{21}\0\0\0A[NS][EW]/s',
|
|
174
|
+
SubDirectory => {
|
|
175
|
+
TagTable => 'Image::ExifTool::QuickTime::Stream',
|
|
176
|
+
ProcessProc => \&ProcessFreeGPS,
|
|
177
|
+
},
|
|
172
178
|
},{
|
|
173
179
|
Name => 'gpmd_Rove', # Rove Stealth 4K encrypted text
|
|
180
|
+
Condition => '$$valPt =~ /^\0\0\xf2\xe1\xf0\xeeTT/',
|
|
174
181
|
SubDirectory => {
|
|
175
182
|
TagTable => 'Image::ExifTool::QuickTime::Stream',
|
|
176
183
|
ProcessProc => \&Process_text,
|
|
177
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
|
+
},
|
|
192
|
+
},{
|
|
193
|
+
Name => 'gpmd_GoPro',
|
|
194
|
+
SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
|
|
178
195
|
}],
|
|
179
196
|
fdsc => {
|
|
180
197
|
Name => 'fdsc',
|
|
@@ -406,7 +423,14 @@ my %insvLimit = (
|
|
|
406
423
|
Groups => { 2 => 'Time' },
|
|
407
424
|
Format => 'double',
|
|
408
425
|
RawConv => '$$self{FoundGPSDateTime} = 1; $val',
|
|
426
|
+
# by the specification, this should use the GPS epoch of Jan 6, 1980,
|
|
427
|
+
# but I have samples which use the Unix epoch of Jan 1, 1970, so convert
|
|
428
|
+
# to the Unix Epoch only if it doesn't match the CreateDate within 5 years
|
|
409
429
|
ValueConv => q{
|
|
430
|
+
my $offset = 315964800;
|
|
431
|
+
if ($$self{CreateDate} and $$self{CreateDate} - $val > 24 * 3600 * 365 * 5) {
|
|
432
|
+
$val += $offset;
|
|
433
|
+
}
|
|
410
434
|
my $str = ConvertUnixTime($val);
|
|
411
435
|
my $frac = $val - int($val);
|
|
412
436
|
if ($frac != 0) {
|
|
@@ -885,14 +909,8 @@ sub Process_text($$$)
|
|
|
885
909
|
$tags{GPSDateTime} = $dateTime;
|
|
886
910
|
$tags{GPSLatitude} = (($4 || 0) + $5/60) * ($6 eq 'N' ? 1 : -1);
|
|
887
911
|
$tags{GPSLongitude} = (($7 || 0) + $8/60) * ($9 eq 'E' ? 1 : -1);
|
|
888
|
-
if
|
|
889
|
-
|
|
890
|
-
$tags{GPSSpeedRef} = 'K';
|
|
891
|
-
}
|
|
892
|
-
if (length $11) {
|
|
893
|
-
$tags{GPSTrack} = $11;
|
|
894
|
-
$tags{GPSTrackRef} = 'T';
|
|
895
|
-
}
|
|
912
|
+
$tags{GPSSpeed} = $10 * $knotsToKph if length $10;
|
|
913
|
+
$tags{GPSTrack} = $11 if length $11;
|
|
896
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) {
|
|
897
915
|
my $time = "$1:$2:$3";
|
|
898
916
|
if ($$et{LastTime}) {
|
|
@@ -972,10 +990,7 @@ sub Process_text($$$)
|
|
|
972
990
|
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$dataPt, 0x39, 5)));
|
|
973
991
|
$tags{GPSAltitude} = $val + 0 if $val =~ /^[-+]\d+$/;
|
|
974
992
|
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$dataPt, 0x3e, 3)));
|
|
975
|
-
if
|
|
976
|
-
$tags{GPSSpeed} = $val + 0;
|
|
977
|
-
$tags{GPSSpeedRef} = 'K';
|
|
978
|
-
}
|
|
993
|
+
$tags{GPSSpeed} = $val + 0 if $val =~ /^\d+$/;
|
|
979
994
|
if ($$dataPt =~ /^\0\0..\xaa\xaa/s) { # (BlueSkySea)
|
|
980
995
|
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$dataPt, 0xad, 12)));
|
|
981
996
|
# the first X,Y,Z accelerometer readings from the AccelerometerData
|
|
@@ -1006,10 +1021,7 @@ sub Process_text($$$)
|
|
|
1006
1021
|
$tags{GPSLatitude} = $2;
|
|
1007
1022
|
$tags{GPSLongitude} = $1;
|
|
1008
1023
|
$tags{GPSAltitude} = $1 if $$dataPt =~ /,\s*H\s+([-+]?\d+\.?\d*)m/;
|
|
1009
|
-
if
|
|
1010
|
-
$tags{GPSSpeed} = $1 * $mpsToKph;
|
|
1011
|
-
$tags{GPSSpeedRef} = 'K';
|
|
1012
|
-
}
|
|
1024
|
+
$tags{GPSSpeed} = $1 * $mpsToKph if $$dataPt =~ /,\s*H.S\s+([-+]?\d+\.?\d*)/;
|
|
1013
1025
|
$tags{Distance} = $1 * $mpsToKph if $$dataPt =~ /,\s*D\s+(\d+\.?\d*)m/;
|
|
1014
1026
|
$tags{VerticalSpeed} = $1 if $$dataPt =~ /,\s*V.S\s+([-+]?\d+\.?\d*)/;
|
|
1015
1027
|
$tags{FNumber} = $1 if $$dataPt =~ /\bF\/(\d+\.?\d*)/;
|
|
@@ -1070,14 +1082,8 @@ sub Process_text($$$)
|
|
|
1070
1082
|
$tags{GPSDateTime} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $year, $14, $13, $1, $2, $3);
|
|
1071
1083
|
$tags{GPSLatitude} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
|
|
1072
1084
|
$tags{GPSLongitude} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
|
|
1073
|
-
if
|
|
1074
|
-
|
|
1075
|
-
$tags{GPSSpeedRef} = 'K';
|
|
1076
|
-
}
|
|
1077
|
-
if (length $12) {
|
|
1078
|
-
$tags{GPSTrack} = $12;
|
|
1079
|
-
$tags{GPSTrackRef} = 'T';
|
|
1080
|
-
}
|
|
1085
|
+
$tags{GPSSpeed} = $11 * $knotsToKph if length $11;
|
|
1086
|
+
$tags{GPSTrack} = $12 if length $12;
|
|
1081
1087
|
}
|
|
1082
1088
|
$tags{GSensor} = $1 if $$dataPt =~ /\bgsensori,(.*?)(;|$)/;
|
|
1083
1089
|
$tags{Car} = $1 if $$dataPt =~ /\bCAR,(.*?)(;|$)/;
|
|
@@ -1214,7 +1220,10 @@ sub ProcessSamples($)
|
|
|
1214
1220
|
$et->VPrint(1, "${hdr}, Sample ".($i+1).' of '.scalar(@$start)." ($size bytes)\n");
|
|
1215
1221
|
$et->VerboseDump(\$buff, Addr => $$start[$i]);
|
|
1216
1222
|
}
|
|
1217
|
-
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
|
+
{
|
|
1218
1227
|
|
|
1219
1228
|
FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
|
|
1220
1229
|
unless ($buff =~ /^\$BEGIN/) {
|
|
@@ -1249,8 +1258,7 @@ sub ProcessSamples($)
|
|
|
1249
1258
|
next if length($buff) < 20 + $n;
|
|
1250
1259
|
$et->HandleTag($tagTbl, GPSLatitude => Get32s(\$buff, 12+$n) * 180/0x80000000);
|
|
1251
1260
|
$et->HandleTag($tagTbl, GPSLongitude => Get32s(\$buff, 16+$n) * 180/0x80000000);
|
|
1252
|
-
$et->HandleTag($tagTbl, GPSSpeed
|
|
1253
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'M');
|
|
1261
|
+
$et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, 8+$n) * $mphToKph);
|
|
1254
1262
|
SetGPSDateTime($et, $tagTbl, $time[$i]);
|
|
1255
1263
|
next; # all done (don't store/process as text)
|
|
1256
1264
|
}
|
|
@@ -1323,6 +1331,19 @@ sub ProcessSamples($)
|
|
|
1323
1331
|
$$et{HandlerType} = $$et{HanderDesc} = '';
|
|
1324
1332
|
}
|
|
1325
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
|
+
|
|
1326
1347
|
#------------------------------------------------------------------------------
|
|
1327
1348
|
# Process "freeGPS " data blocks referenced by a 'gps ' (GPSDataList) atom
|
|
1328
1349
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref {DataPt,SampleTime,SampleDuration}, 2) tagTable ref
|
|
@@ -1334,7 +1355,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1334
1355
|
my ($et, $dirInfo, $tagTbl) = @_;
|
|
1335
1356
|
my $dataPt = $$dirInfo{DataPt};
|
|
1336
1357
|
my $dirLen = length $$dataPt;
|
|
1337
|
-
my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl);
|
|
1358
|
+
my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl, $ddd);
|
|
1338
1359
|
my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, @acc, @xtra);
|
|
1339
1360
|
|
|
1340
1361
|
return 0 if $dirLen < 92;
|
|
@@ -1424,7 +1445,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1424
1445
|
map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
|
|
1425
1446
|
}
|
|
1426
1447
|
|
|
1427
|
-
} elsif ($$dataPt =~ /^.{
|
|
1448
|
+
} elsif ($$dataPt =~ /^.{37}\0\0\0A([NS])([EW])/s) {
|
|
1428
1449
|
|
|
1429
1450
|
# decode freeGPS from ViofoA119v3 dashcam (similar to Novatek GPS format)
|
|
1430
1451
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
|
@@ -1441,6 +1462,34 @@ sub ProcessFreeGPS($$$)
|
|
|
1441
1462
|
$trk = GetFloat($dataPt, 0x38);
|
|
1442
1463
|
SetByteOrder('MM');
|
|
1443
1464
|
|
|
1465
|
+
} elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
|
|
1466
|
+
|
|
1467
|
+
# also decode 'gpmd' chunk from Kingslim D4 dashcam videos
|
|
1468
|
+
# 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
|
|
1469
|
+
# 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
|
|
1470
|
+
# 0020: 15 70 fe c5 29 5c c3 41 ae c7 af 42 00 00 d1 be [.p..)\.A...B....]
|
|
1471
|
+
# 0030: 00 00 80 3b 00 00 2c 3e 00 00 00 00 00 00 00 00 [...;..,>........]
|
|
1472
|
+
# 0040: 00 00 00 00 00 00 00 00 00 00 00 00 26 26 26 26 [............&&&&]
|
|
1473
|
+
# 0050: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
|
|
1474
|
+
# 0060: 01 00 00 00 23 23 23 23 75 00 00 00 c0 22 20 20 [....####u...." ]
|
|
1475
|
+
# 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
|
|
1476
|
+
($latRef, $lonRef) = ($1, $2);
|
|
1477
|
+
($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
|
|
1478
|
+
SetByteOrder('II');
|
|
1479
|
+
# lat/lon aren't decoded properly, but spd,trk,acc are
|
|
1480
|
+
$lat = GetFloat($dataPt, 0x1c);
|
|
1481
|
+
$lon = GetFloat($dataPt, 0x20);
|
|
1482
|
+
$et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
|
|
1483
|
+
$et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
|
|
1484
|
+
$lat = abs $lat;
|
|
1485
|
+
$lon = abs $lon;
|
|
1486
|
+
$spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
|
|
1487
|
+
$trk = GetFloat($dataPt, 0x28);
|
|
1488
|
+
$acc[0] = GetFloat($dataPt, 0x2c);
|
|
1489
|
+
$acc[1] = GetFloat($dataPt, 0x30);
|
|
1490
|
+
$acc[2] = GetFloat($dataPt, 0x34);
|
|
1491
|
+
SetByteOrder('MM');
|
|
1492
|
+
|
|
1444
1493
|
} elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
|
|
1445
1494
|
|
|
1446
1495
|
# decode freeGPS from Akaso dashcam
|
|
@@ -1461,6 +1510,23 @@ sub ProcessFreeGPS($$$)
|
|
|
1461
1510
|
$trk -= 360 if $trk >= 360;
|
|
1462
1511
|
SetByteOrder('MM');
|
|
1463
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
|
+
|
|
1464
1530
|
} elsif ($$dataPt =~ /^.{16}YndAkasoCar/s) {
|
|
1465
1531
|
|
|
1466
1532
|
# Akaso V1 dascham
|
|
@@ -1479,13 +1545,17 @@ sub ProcessFreeGPS($$$)
|
|
|
1479
1545
|
return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
|
|
1480
1546
|
($lonRef eq 'E' or $lonRef eq 'W');
|
|
1481
1547
|
|
|
1482
|
-
$et->WarnOnce(
|
|
1548
|
+
$et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
|
|
1483
1549
|
# (see https://exiftool.org/forum/index.php?topic=11320.0)
|
|
1484
|
-
return 1;
|
|
1485
1550
|
|
|
1486
1551
|
SetByteOrder('II');
|
|
1552
|
+
|
|
1553
|
+
$spd = GetFloat($dataPt, 0x60);
|
|
1554
|
+
$trk = GetFloat($dataPt, 0x64) + 180; # (why is this off by 180?)
|
|
1487
1555
|
$lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
|
|
1488
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
|
+
|
|
1489
1559
|
SetByteOrder('MM');
|
|
1490
1560
|
#my $serialNum = substr($$dataPt, 0x68, 20);
|
|
1491
1561
|
|
|
@@ -1549,10 +1619,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1549
1619
|
#
|
|
1550
1620
|
FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
|
|
1551
1621
|
# lat/long are in DDDMM.MMMM format
|
|
1552
|
-
|
|
1553
|
-
$lat = $deg + ($lat - $deg * 100) / 60;
|
|
1554
|
-
$deg = int($lon / 100);
|
|
1555
|
-
$lon = $deg + ($lon - $deg * 100) / 60;
|
|
1622
|
+
ConvertLatLon($lat, $lon) unless $ddd;
|
|
1556
1623
|
$sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
|
|
1557
1624
|
if (defined $yr) {
|
|
1558
1625
|
my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
|
|
@@ -1564,14 +1631,8 @@ sub ProcessFreeGPS($$$)
|
|
|
1564
1631
|
$et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
|
|
1565
1632
|
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
|
|
1566
1633
|
$et->HandleTag($tagTbl, GPSAltitude => $alt) if defined $alt;
|
|
1567
|
-
if
|
|
1568
|
-
|
|
1569
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
1570
|
-
}
|
|
1571
|
-
if (defined $trk) {
|
|
1572
|
-
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
1573
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
1574
|
-
}
|
|
1634
|
+
$et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd;
|
|
1635
|
+
$et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
|
|
1575
1636
|
while (@xtra) {
|
|
1576
1637
|
my $tag = shift @xtra;
|
|
1577
1638
|
$et->HandleTag($tagTbl, $tag => shift @xtra);
|
|
@@ -1690,9 +1751,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1690
1751
|
$et->HandleTag($tagTbl, GPSLatitude => Get32s(\$b, 0x10) / 1e7);
|
|
1691
1752
|
$et->HandleTag($tagTbl, GPSLongitude => Get32s(\$b, 0x18) / 1e7);
|
|
1692
1753
|
$et->HandleTag($tagTbl, GPSSpeed => Get32s(\$b, 0x20) / 100 * $mpsToKph);
|
|
1693
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
1694
1754
|
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
1695
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
1696
1755
|
$et->HandleTag($tagTbl, GPSAltitude => Get32s(\$b, 0x28) / 1000);
|
|
1697
1756
|
$lastRecPos = $recPos;
|
|
1698
1757
|
$foundNew = 1;
|
|
@@ -1802,6 +1861,27 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1802
1861
|
$spd = GetFloat($dataPt, 0x50);
|
|
1803
1862
|
$trk = GetFloat($dataPt, 0x54);
|
|
1804
1863
|
|
|
1864
|
+
} elsif ($$dataPt =~ /^.{16}A([NS])([EW])\0/s) {
|
|
1865
|
+
|
|
1866
|
+
# INNOVV MP4 video (same format as INNOVV TS)
|
|
1867
|
+
while ($$dataPt =~ /(A[NS][EW]\0.{28})/g) {
|
|
1868
|
+
my $dat = $1;
|
|
1869
|
+
$lat = abs(GetFloat(\$dat, 4)); # (abs just to be safe)
|
|
1870
|
+
$lon = abs(GetFloat(\$dat, 8)); # (abs just to be safe)
|
|
1871
|
+
$spd = GetFloat(\$dat, 12) * $knotsToKph;
|
|
1872
|
+
$trk = GetFloat(\$dat, 16);
|
|
1873
|
+
@acc = unpack('x20V3', $dat);
|
|
1874
|
+
map { $_ = $_ - 4294967296 if $_ >= 0x80000000 } @acc;
|
|
1875
|
+
ConvertLatLon($lat, $lon);
|
|
1876
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
1877
|
+
$et->HandleTag($tagTbl, GPSLatitude => $lat * (substr($dat,1,1) eq 'S' ? -1 : 1));
|
|
1878
|
+
$et->HandleTag($tagTbl, GPSLongitude => $lon * (substr($dat,2,1) eq 'W' ? -1 : 1));
|
|
1879
|
+
$et->HandleTag($tagTbl, GPSSpeed => $spd);
|
|
1880
|
+
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
1881
|
+
$et->HandleTag($tagTbl, Accelerometer => "@acc");
|
|
1882
|
+
}
|
|
1883
|
+
return 1;
|
|
1884
|
+
|
|
1805
1885
|
} else {
|
|
1806
1886
|
|
|
1807
1887
|
# (look for binary GPS as stored by NextBase 512G, ref PH)
|
|
@@ -1843,9 +1923,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1843
1923
|
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
|
1844
1924
|
$et->HandleTag($tagTbl, GPSLongitude => $lon);
|
|
1845
1925
|
$et->HandleTag($tagTbl, GPSSpeed => $spd / 100 * $mpsToKph);
|
|
1846
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
1847
1926
|
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
1848
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
1849
1927
|
last if $pos += 0x20 > length($$dataPt) - 0x1e;
|
|
1850
1928
|
}
|
|
1851
1929
|
return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
|
|
@@ -1858,23 +1936,12 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1858
1936
|
$yr += 2000 if $yr < 2000;
|
|
1859
1937
|
my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $yr, $mon, $day, $hr, $min, $sec);
|
|
1860
1938
|
# convert from DDMM.MMMMMM to DD.DDDDDD format if necessary
|
|
1861
|
-
unless
|
|
1862
|
-
my $deg = int($lat / 100);
|
|
1863
|
-
$lat = $deg + ($lat - $deg * 100) / 60;
|
|
1864
|
-
$deg = int($lon / 100);
|
|
1865
|
-
$lon = $deg + ($lon - $deg * 100) / 60;
|
|
1866
|
-
}
|
|
1939
|
+
ConvertLatLon($lat, $lon) unless $ddd;
|
|
1867
1940
|
$et->HandleTag($tagTbl, GPSDateTime => $time);
|
|
1868
1941
|
$et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
|
|
1869
1942
|
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
|
|
1870
|
-
if
|
|
1871
|
-
|
|
1872
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
1873
|
-
}
|
|
1874
|
-
if (defined $trk) {
|
|
1875
|
-
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
1876
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
1877
|
-
}
|
|
1943
|
+
$et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd; # (now in km/h)
|
|
1944
|
+
$et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
|
|
1878
1945
|
if (defined $alt) {
|
|
1879
1946
|
$et->HandleTag($tagTbl, GPSAltitude => $alt);
|
|
1880
1947
|
}
|
|
@@ -1882,6 +1949,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1882
1949
|
return 1;
|
|
1883
1950
|
}
|
|
1884
1951
|
|
|
1952
|
+
|
|
1885
1953
|
#------------------------------------------------------------------------------
|
|
1886
1954
|
# Extract embedded information referenced from a track
|
|
1887
1955
|
# Inputs: 0) ExifTool ref, 1) tag name, 2) data ref
|
|
@@ -1960,22 +2028,18 @@ sub ParseTag($$$)
|
|
|
1960
2028
|
while ($pos + 36 < $dataLen) {
|
|
1961
2029
|
my $dat = substr($$dataPt, $pos, 36);
|
|
1962
2030
|
last if $dat eq "\x0" x 36;
|
|
1963
|
-
my @a = unpack '
|
|
2031
|
+
my @a = unpack 'VVVVaVaV', $dat;
|
|
1964
2032
|
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
1965
2033
|
# 0=1, 1=1, 2=secs, 3=?
|
|
1966
2034
|
SetGPSDateTime($et, $tagTbl, $a[2]);
|
|
1967
2035
|
my $lat = $a[5] / 1e3;
|
|
1968
2036
|
my $lon = $a[7] / 1e3;
|
|
1969
|
-
|
|
1970
|
-
$lat =
|
|
1971
|
-
$
|
|
1972
|
-
$lon = $deg + ($lon - $deg * 100) / 60;
|
|
1973
|
-
$lat = -$lat if $a[4] eq 'S';
|
|
1974
|
-
$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';
|
|
1975
2040
|
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
|
1976
2041
|
$et->HandleTag($tagTbl, GPSLongitude => $lon);
|
|
1977
|
-
$et->HandleTag($tagTbl, GPSSpeed
|
|
1978
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
2042
|
+
$et->HandleTag($tagTbl, GPSSpeed => $a[3] / 1e3);
|
|
1979
2043
|
$pos += 36;
|
|
1980
2044
|
}
|
|
1981
2045
|
SetByteOrder('MM');
|
|
@@ -2037,9 +2101,9 @@ sub Process_mebx($$$)
|
|
|
2037
2101
|
|
|
2038
2102
|
# parse using information from 'keys' table (eg. Apple iPhone7+ hevc 'Core Media Data Handler')
|
|
2039
2103
|
$et->VerboseDir('mebx', undef, length $$dataPt);
|
|
2040
|
-
my $pos
|
|
2041
|
-
|
|
2042
|
-
|
|
2104
|
+
my ($pos, $len);
|
|
2105
|
+
for ($pos=0; $pos+8<length($$dataPt); $pos+=$len) {
|
|
2106
|
+
$len = Get32u($dataPt, $pos);
|
|
2043
2107
|
last if $len < 8 or $pos + $len > length $$dataPt;
|
|
2044
2108
|
my $id = substr($$dataPt, $pos+4, 4);
|
|
2045
2109
|
my $info = $$ee{'keys'}{$id};
|
|
@@ -2062,7 +2126,6 @@ sub Process_mebx($$$)
|
|
|
2062
2126
|
} else {
|
|
2063
2127
|
$et->WarnOnce('No key information for mebx ID ' . PrintableTagID($id,1));
|
|
2064
2128
|
}
|
|
2065
|
-
$pos += $len;
|
|
2066
2129
|
}
|
|
2067
2130
|
return 1;
|
|
2068
2131
|
}
|
|
@@ -2118,20 +2181,14 @@ sub Process_gps0($$$)
|
|
|
2118
2181
|
my $lat = GetDouble($dataPt, $pos);
|
|
2119
2182
|
my $lon = GetDouble($dataPt, $pos+8);
|
|
2120
2183
|
next if abs($lat) > 9000 or abs($lon) > 18000;
|
|
2121
|
-
|
|
2122
|
-
my $deg = int($lat / 100);
|
|
2123
|
-
$lat = $deg + ($lat - $deg * 100) / 60;
|
|
2124
|
-
$deg = int($lon / 100);
|
|
2125
|
-
$lon = $deg + ($lon - $deg * 100) / 60;
|
|
2184
|
+
ConvertLatLon($lat, $lon);
|
|
2126
2185
|
my @a = unpack('C*', substr($$dataPt, $pos+22, 6)); # unpack date/time
|
|
2127
2186
|
$a[0] += 2000;
|
|
2128
2187
|
$et->HandleTag($tagTbl, GPSDateTime => sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ", @a));
|
|
2129
2188
|
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
|
2130
2189
|
$et->HandleTag($tagTbl, GPSLongitude => $lon);
|
|
2131
2190
|
$et->HandleTag($tagTbl, GPSSpeed => Get16u($dataPt, $pos+0x14));
|
|
2132
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
2133
2191
|
$et->HandleTag($tagTbl, GPSTrack => Get8u($dataPt, $pos+0x1c) * 2); # (NC)
|
|
2134
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
2135
2192
|
$et->HandleTag($tagTbl, GPSAltitude => Get32s($dataPt, $pos + 0x10));
|
|
2136
2193
|
# yet to be decoded:
|
|
2137
2194
|
# 0x1d - int8u[3] seen: "1 1 0"
|
|
@@ -2220,10 +2277,7 @@ sub ProcessRIFFTrailer($$$)
|
|
|
2220
2277
|
my $lat = GetDouble(\$buff, $pos+4);
|
|
2221
2278
|
my $lon = GetDouble(\$buff, $pos+12);
|
|
2222
2279
|
$et->Warn('Bad gps0 record') and last if abs($lat) > 9000 or abs($lon) > 18000;
|
|
2223
|
-
|
|
2224
|
-
$lat = $deg + ($lat - $deg * 100) / 60;
|
|
2225
|
-
$deg = int($lon / 100);
|
|
2226
|
-
$lon = $deg + ($lon - $deg * 100) / 60;
|
|
2280
|
+
ConvertLatLon($lat, $lon);
|
|
2227
2281
|
$lat = -$lat if Get8u(\$buff, $pos+0x21) == 2; # wild guess
|
|
2228
2282
|
$lon = -$lon if Get8u(\$buff, $pos+0x22) == 2; # wild guess
|
|
2229
2283
|
my @a = unpack('C*', substr($buff, $pos+26, 6)); # unpack date/time
|
|
@@ -2233,9 +2287,7 @@ sub ProcessRIFFTrailer($$$)
|
|
|
2233
2287
|
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
|
2234
2288
|
$et->HandleTag($tagTbl, GPSLongitude => $lon);
|
|
2235
2289
|
$et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, $pos+0x18) * $knotsToKph);
|
|
2236
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
2237
2290
|
$et->HandleTag($tagTbl, GPSTrack => Get8u(\$buff, $pos+0x20) * 2);
|
|
2238
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
2239
2291
|
}
|
|
2240
2292
|
} elsif ($tag eq 'gsen') {
|
|
2241
2293
|
# (similar to record decoded in Process_gsen)
|
|
@@ -2272,39 +2324,91 @@ sub ProcessNMEA($$$)
|
|
|
2272
2324
|
{
|
|
2273
2325
|
my ($et, $dirInfo, $tagTbl) = @_;
|
|
2274
2326
|
my $dataPt = $$dirInfo{DataPt};
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
my ($
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
2327
|
+
my ($rtnVal, %fix);
|
|
2328
|
+
# parse only RMC and GGA sentence [with leading timecode] for now
|
|
2329
|
+
for (;;) {
|
|
2330
|
+
my ($tc, $type, $tim);
|
|
2331
|
+
if ($$dataPt =~ /(?:\[(\d+)\])?\$[A-Z]{2}(RMC|GGA),(\d{2}\d{2}\d+(\.\d*)?),/g) {
|
|
2332
|
+
($tc, $type, $tim) = ($1, $2, $3);
|
|
2333
|
+
}
|
|
2334
|
+
# write out last fix now if complete
|
|
2335
|
+
# (use the GPS timestamps because they may be different for the same timecode)
|
|
2336
|
+
if ($fix{tim} and (not $tim or $fix{tim} != $tim)) {
|
|
2337
|
+
if ($fix{dat} and defined $fix{lat} and defined $fix{lon}) {
|
|
2338
|
+
my $sampleTime;
|
|
2339
|
+
$sampleTime = ($fix{tc} - $$et{StartTime}) / 1000 if $fix{tc} and $$et{StartTime};
|
|
2340
|
+
FoundSomething($et, $tagTbl, $sampleTime);
|
|
2341
|
+
$et->HandleTag($tagTbl, GPSDateTime => $fix{dat});
|
|
2342
|
+
$et->HandleTag($tagTbl, GPSLatitude => $fix{lat});
|
|
2343
|
+
$et->HandleTag($tagTbl, GPSLongitude => $fix{lon});
|
|
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};
|
|
2349
|
+
}
|
|
2350
|
+
undef %fix;
|
|
2300
2351
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2352
|
+
$fix{tim} = $tim or last;
|
|
2353
|
+
my $pos = pos($$dataPt);
|
|
2354
|
+
pos($$dataPt) = $pos - length($tim) - 1; # rewind to re-parse time
|
|
2355
|
+
# (parsing of NMEA strings copied from Geotag.pm)
|
|
2356
|
+
if ($type eq 'RMC' and
|
|
2357
|
+
$$dataPt =~ /\G(\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+)/g)
|
|
2358
|
+
{
|
|
2359
|
+
my $year = $15 + ($15 >= 70 ? 1900 : 2000);
|
|
2360
|
+
$fix{tc} = $tc; # use timecode of RMC sentence
|
|
2361
|
+
$fix{dat} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$year,$14,$13,$1,$2,$3);
|
|
2362
|
+
$fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
|
|
2363
|
+
$fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
|
|
2364
|
+
$fix{spd} = $11 if length $11;
|
|
2365
|
+
$fix{trk} = $12 if length $12;
|
|
2366
|
+
} elsif ($type eq 'GGA' and
|
|
2367
|
+
$$dataPt =~ /\G(\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?/g)
|
|
2368
|
+
{
|
|
2369
|
+
$fix{lat} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
|
|
2370
|
+
$fix{lon} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
|
|
2371
|
+
@fix{qw(nsats hdop alt)} = ($11,$12,$13);
|
|
2372
|
+
} else {
|
|
2373
|
+
pos($$dataPt) = $pos; # continue searching from our last match
|
|
2304
2374
|
}
|
|
2305
2375
|
}
|
|
2306
2376
|
delete $$et{DOC_NUM};
|
|
2307
|
-
return
|
|
2377
|
+
return $rtnVal;
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
#------------------------------------------------------------------------------
|
|
2381
|
+
# Process 'gps ' or 'udat' atom possibly containing NMEA (ref PH)
|
|
2382
|
+
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
|
2383
|
+
# Returns: 1 on success
|
|
2384
|
+
sub ProcessGPSLog($$$)
|
|
2385
|
+
{
|
|
2386
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
|
2387
|
+
my $dataPt = $$dirInfo{DataPt};
|
|
2388
|
+
my ($rtnVal, @a);
|
|
2389
|
+
|
|
2390
|
+
# try NMEA format first
|
|
2391
|
+
return 1 if ProcessNMEA($et,$dirInfo,$tagTbl);
|
|
2392
|
+
|
|
2393
|
+
# DENVER ACG-8050WMK2 format looks like this:
|
|
2394
|
+
# 210318073213[1][N][52200970][E][006362321][+00152][100][00140][C000000]+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000+000
|
|
2395
|
+
# YYMMDDHHMMSS A? NS lat EW lon alt kph dir kCal accel
|
|
2396
|
+
while ($$dataPt =~ /\b(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\[1\]\[([NS])\]\[(\d{8})\]\[([EW])\]\[(\d{9})\]\[([-+]?\d*)\]\[(\d*)\]\[(\d*)\]\[C?(\d*)\](([-+]\d{3})+)/g) {
|
|
2397
|
+
my $lat = substr( $8,0,2) + substr( $8,2) / 600000;
|
|
2398
|
+
my $lon = substr($10,0,3) + substr($10,3) / 600000;
|
|
2399
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
2400
|
+
$et->HandleTag($tagTbl, GPSDateTime => "20$1:$2:$3 $4:$5:$6Z");
|
|
2401
|
+
$et->HandleTag($tagTbl, GPSLatitude => $lat * ($7 eq 'S' ? -1 : 1));
|
|
2402
|
+
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($9 eq 'W' ? -1 : 1));
|
|
2403
|
+
$et->HandleTag($tagTbl, GPSAltitude => $11 / 10) if length $11;
|
|
2404
|
+
$et->HandleTag($tagTbl, GPSSpeed => $12 + 0) if length $12;
|
|
2405
|
+
$et->HandleTag($tagTbl, GPSTrack => $13 + 0) if length $13;
|
|
2406
|
+
$et->HandleTag($tagTbl, KiloCalories => $14 / 10) if length $14;
|
|
2407
|
+
$et->HandleTag($tagTbl, Accelerometer=> $15) if length $15;
|
|
2408
|
+
$rtnVal = 1;
|
|
2409
|
+
}
|
|
2410
|
+
delete $$et{DOC_NUM};
|
|
2411
|
+
return $rtnVal;
|
|
2308
2412
|
}
|
|
2309
2413
|
|
|
2310
2414
|
#------------------------------------------------------------------------------
|
|
@@ -2395,9 +2499,7 @@ sub ProcessTTAD($$$)
|
|
|
2395
2499
|
$et->HandleTag($tagTbl, GPSLongitude => GetDouble($dataPt, $pos+0x24));
|
|
2396
2500
|
$et->HandleTag($tagTbl, GPSAltitude => GetDouble($dataPt, $pos+0x14));
|
|
2397
2501
|
$et->HandleTag($tagTbl, GPSSpeed => GetDouble($dataPt, $pos+0x0c) * $mpsToKph);
|
|
2398
|
-
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
|
2399
2502
|
$et->HandleTag($tagTbl, GPSTrack => GetDouble($dataPt, $pos+0x30));
|
|
2400
|
-
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
|
2401
2503
|
if ($unknown) {
|
|
2402
2504
|
my @a = map { GetDouble($dataPt, $pos+0x38+8*$_) } 0..2;
|
|
2403
2505
|
$et->HandleTag($tagTbl, Unknown03 => "@a");
|
|
@@ -2517,21 +2619,26 @@ sub ProcessInsta360($;$)
|
|
|
2517
2619
|
my $tmp = substr($buff, $p, $dlen);
|
|
2518
2620
|
my @a = unpack('VVvaa8aa8aa8a8a8', $tmp);
|
|
2519
2621
|
next unless $a[3] eq 'A'; # (ignore void fixes)
|
|
2520
|
-
|
|
2521
|
-
|
|
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
|
+
}
|
|
2522
2631
|
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
2523
2632
|
$a[$_] = GetDouble(\$a[$_], 0) foreach 4,6,8,9,10;
|
|
2524
2633
|
$a[4] = -abs($a[4]) if $a[5] eq 'S'; # (abs just in case it was already signed)
|
|
2525
|
-
$a[6] = -abs($a[6]) if $a[7]
|
|
2526
|
-
$et->HandleTag($tagTbl, GPSDateTime
|
|
2527
|
-
$et->HandleTag($tagTbl, GPSLatitude
|
|
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]);
|
|
2528
2637
|
$et->HandleTag($tagTbl, GPSLongitude => $a[6]);
|
|
2529
|
-
$et->HandleTag($tagTbl, GPSSpeed
|
|
2530
|
-
$et->HandleTag($tagTbl,
|
|
2531
|
-
$et->HandleTag($tagTbl,
|
|
2532
|
-
$et->HandleTag($tagTbl,
|
|
2533
|
-
$et->HandleTag($tagTbl, GPSAltitude => $a[10]);
|
|
2534
|
-
$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)
|
|
2535
2642
|
}
|
|
2536
2643
|
}
|
|
2537
2644
|
} elsif ($id == 0x101) {
|
|
@@ -2587,6 +2694,41 @@ sub Process360Fly($$$)
|
|
|
2587
2694
|
return 1;
|
|
2588
2695
|
}
|
|
2589
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
|
+
|
|
2590
2732
|
#------------------------------------------------------------------------------
|
|
2591
2733
|
# Scan media data for "freeGPS" metadata if not found already (ref PH)
|
|
2592
2734
|
# Inputs: 0) ExifTool ref
|
|
@@ -2660,7 +2802,7 @@ sub ScanMediaData($)
|
|
|
2660
2802
|
$buf2 = substr($buff, $len);
|
|
2661
2803
|
}
|
|
2662
2804
|
if ($tagTbl) {
|
|
2663
|
-
$$et{DOC_NUM} = 0;
|
|
2805
|
+
$$et{DOC_NUM} = 0; # reset DOC_NUM after extracting embedded metadata
|
|
2664
2806
|
$et->VPrint(0, "--------------------------\n");
|
|
2665
2807
|
SetByteOrder($oldByteOrder);
|
|
2666
2808
|
$$et{INDENT} = substr $$et{INDENT}, 0, -2;
|