exiftool_vendored 12.18.0 → 12.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/Changes +236 -4
- 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 +152 -97
- 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 +31 -13
- data/bin/lib/Image/ExifTool/CBOR.pm +331 -0
- data/bin/lib/Image/ExifTool/Canon.pm +44 -19
- 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 +124 -13
- 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 +30 -5
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +395 -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 +3 -3
- data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
- data/bin/lib/Image/ExifTool/Nikon.pm +18 -5
- data/bin/lib/Image/ExifTool/NikonSettings.pm +19 -2
- data/bin/lib/Image/ExifTool/Olympus.pm +10 -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 +234 -75
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +283 -141
- data/bin/lib/Image/ExifTool/README +5 -2
- 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 +230 -69
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
- data/bin/lib/Image/ExifTool/TagLookup.pm +4145 -4029
- data/bin/lib/Image/ExifTool/TagNames.pod +671 -287
- 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 +45 -15
- data/bin/lib/Image/ExifTool/XMP2.pl +3 -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 +233 -81
- 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 => 'relative to 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;
|