exiftool-vendored.exe 12.73.0 → 12.78.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.
- package/bin/exiftool_files/Changes +94 -6
- package/bin/exiftool_files/README +46 -45
- package/bin/exiftool_files/config_files/example.config +10 -2
- package/bin/exiftool_files/exiftool.pl +138 -87
- package/bin/exiftool_files/lib/File/RandomAccess.pm +31 -5
- package/bin/exiftool_files/lib/File/RandomAccess.pod +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/7Z.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/AFCP.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/BZZ.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +7 -7
- package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +17 -13
- package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +8 -2
- package/bin/exiftool_files/lib/Image/ExifTool/DICOM.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/DNG.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +54 -5
- package/bin/exiftool_files/lib/Image/ExifTool/FLIR.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Fixup.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +8 -3
- package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +5 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.pm +237 -0
- package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/HtmlDump.pm +7 -4
- package/bin/exiftool_files/lib/Image/ExifTool/ID3.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Import.pm +9 -6
- package/bin/exiftool_files/lib/Image/ExifTool/JSON.pm +11 -11
- package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +51 -12
- package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/MWG.pm +1 -0
- package/bin/exiftool_files/lib/Image/ExifTool/MacOS.pm +19 -4
- package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +5 -3
- package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Ogg.pm +4 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +3 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +59 -9
- package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/PostScript.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +223 -117
- package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +260 -242
- package/bin/exiftool_files/lib/Image/ExifTool/RSRC.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +4 -4
- package/bin/exiftool_files/lib/Image/ExifTool/Shift.pl +1 -2
- package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +3 -3
- package/bin/exiftool_files/lib/Image/ExifTool/TagInfoXML.pm +2 -2
- package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +85 -8
- package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +148 -8
- package/bin/exiftool_files/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +26 -23
- package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +1 -1
- package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +15 -2
- package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +4 -2
- package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +77 -52
- package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +2 -1
- package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +9 -0
- package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +6 -6
- package/bin/exiftool_files/lib/Image/ExifTool.pm +204 -63
- package/bin/exiftool_files/lib/Image/ExifTool.pod +118 -94
- package/package.json +5 -5
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
#
|
|
6
6
|
# Revisions: 2018-01-03 - P. Harvey Created
|
|
7
7
|
#
|
|
8
|
+
# Notes: Set API "Debug" option to generate GPSType tag
|
|
9
|
+
#
|
|
8
10
|
# References: 1) https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW130
|
|
9
11
|
# 2) http://sergei.nz/files/nvtk_mp42gpx.py
|
|
10
12
|
# 3) https://forum.flitsservice.nl/dashcam-info/dod-ls460w-gps-data-uit-mov-bestand-lezen-t87926.html
|
|
@@ -22,14 +24,12 @@ use Image::ExifTool::QuickTime;
|
|
|
22
24
|
sub Process_tx3g($$$);
|
|
23
25
|
sub Process_marl($$$);
|
|
24
26
|
sub Process_mebx($$$);
|
|
27
|
+
sub Process_text($$$;$);
|
|
25
28
|
sub ProcessFreeGPS($$$);
|
|
26
|
-
sub ProcessFreeGPS2($$$);
|
|
27
29
|
sub Process360Fly($$$);
|
|
28
30
|
sub ProcessFMAS($$$);
|
|
29
31
|
sub ProcessCAMM($$$);
|
|
30
32
|
|
|
31
|
-
my $debug; # set to 'tEST' (all caps) for extra debugging messages
|
|
32
|
-
|
|
33
33
|
# QuickTime data types that have ExifTool equivalents
|
|
34
34
|
# (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
|
|
35
35
|
my %qtFmt = (
|
|
@@ -109,7 +109,7 @@ my %insvLimit = (
|
|
|
109
109
|
The tags below are extracted from timed metadata in QuickTime and other
|
|
110
110
|
formats of video files when the ExtractEmbedded option is used. Although
|
|
111
111
|
most of these tags are combined into the single table below, ExifTool
|
|
112
|
-
currently reads
|
|
112
|
+
currently reads 71 different formats of timed GPS metadata from video files.
|
|
113
113
|
},
|
|
114
114
|
VARS => { NO_ID => 1 },
|
|
115
115
|
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
|
@@ -137,6 +137,7 @@ my %insvLimit = (
|
|
|
137
137
|
GPSDOP => { Description => 'GPS Dilution Of Precision' },
|
|
138
138
|
Distance => { PrintConv => '"$val m"' },
|
|
139
139
|
VerticalSpeed=> { PrintConv => '"$val m/s"' },
|
|
140
|
+
CameraModel => { Groups => { 2 => 'Camera' } },
|
|
140
141
|
FNumber => { PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)', Groups => { 2 => 'Camera' } },
|
|
141
142
|
ExposureTime => { PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)', Groups => { 2 => 'Camera' } },
|
|
142
143
|
ExposureCompensation => { PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)', Groups => { 2 => 'Camera' } },
|
|
@@ -848,11 +849,11 @@ sub FoundSomething($$;$$)
|
|
|
848
849
|
#------------------------------------------------------------------------------
|
|
849
850
|
# Approximate GPSDateTime value from sample time and CreateDate
|
|
850
851
|
# Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (s)
|
|
851
|
-
# 3) true if CreateDate is at end of video
|
|
852
|
+
# 3) true if CreateDate is at end of video, 4) flag if CreateDate is UTC
|
|
852
853
|
# Notes: Uses ExifTool CreateDateAtEnd as flag to subtract video duration
|
|
853
|
-
sub SetGPSDateTime(
|
|
854
|
+
sub SetGPSDateTime($$$;$)
|
|
854
855
|
{
|
|
855
|
-
my ($et, $tagTbl, $sampleTime) = @_;
|
|
856
|
+
my ($et, $tagTbl, $sampleTime, $isUTC) = @_;
|
|
856
857
|
my $value = $$et{VALUE};
|
|
857
858
|
if (defined $sampleTime and $$value{CreateDate}) {
|
|
858
859
|
$sampleTime += $$value{CreateDate}; # adjust sample time to seconds since the epoch
|
|
@@ -863,7 +864,9 @@ sub SetGPSDateTime($$$)
|
|
|
863
864
|
} else {
|
|
864
865
|
$et->WarnOnce('Approximating GPSDateTime as CreateDate + SampleTime', 1);
|
|
865
866
|
}
|
|
866
|
-
|
|
867
|
+
my $utc = $et->Options('QuickTimeUTC');
|
|
868
|
+
$utc = $isUTC unless defined $utc; # (allow QuickTimeUTC=0 to override $isUTC default)
|
|
869
|
+
unless ($utc) {
|
|
867
870
|
my $tzOff = $$et{tzOff}; # use previously calculated offset
|
|
868
871
|
unless (defined $tzOff) {
|
|
869
872
|
# adjust to UTC, assuming time is local
|
|
@@ -910,9 +913,14 @@ sub Process_text($$$;$)
|
|
|
910
913
|
$et->VerboseDir($dirName, undef, length($$dataPt));
|
|
911
914
|
}
|
|
912
915
|
|
|
913
|
-
while ($$dataPt =~ /\$(\w+)([
|
|
916
|
+
while ($$dataPt =~ /\$(\w+)([^\$\0]*)/g) {
|
|
914
917
|
my ($tag, $dat) = ($1, $2);
|
|
915
|
-
if ($tag =~ /^[A-Z]{2}RMC$/
|
|
918
|
+
if ($tag =~ /^[A-Z]{2}RMC$/) {
|
|
919
|
+
unless ($dat =~ /^,(\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+)/) {
|
|
920
|
+
$tags{Text} = defined $tags{Text} ? $tags{Text} . "\$$tag$dat" : "\$$tag$dat" unless $handled;
|
|
921
|
+
$dat =~ /^,\d+\.?\d*,V,/ and $$et{UnknownTextCount} = 0; # (allow any number of void fixes)
|
|
922
|
+
next;
|
|
923
|
+
}
|
|
916
924
|
my $time = "$1:$2:$3";
|
|
917
925
|
if ($$et{LastTime}) {
|
|
918
926
|
if ($$et{LastTime} eq $time) {
|
|
@@ -972,8 +980,8 @@ sub Process_text($$$;$)
|
|
|
972
980
|
} elsif ($tag eq 'BEGIN') {
|
|
973
981
|
$tags{Text} = $dat if length $dat;
|
|
974
982
|
$tags{done} = 1;
|
|
975
|
-
} elsif ($tag ne 'END') {
|
|
976
|
-
$tags{Text} = "\$$tag$dat"
|
|
983
|
+
} elsif ($tag ne 'END' and not $handled) {
|
|
984
|
+
$tags{Text} = defined $tags{Text} ? $tags{Text} . "\$$tag$dat" : "\$$tag$dat";
|
|
977
985
|
}
|
|
978
986
|
}
|
|
979
987
|
%tags and HandleTextTags($et, $tagTbl, \%tags), return;
|
|
@@ -1144,13 +1152,13 @@ sub ProcessSamples($)
|
|
|
1144
1152
|
{
|
|
1145
1153
|
my $et = shift;
|
|
1146
1154
|
my ($raf, $ee) = @$et{qw(RAF ee)};
|
|
1147
|
-
my ($i, $
|
|
1155
|
+
my ($i, $pos, $hdrLen, $hdrFmt, @time, @dur, $oldIndent, $hash);
|
|
1148
1156
|
my ($mdatOffset, $mdatSize); # (for range-checking samples when hash is done)
|
|
1149
1157
|
|
|
1150
1158
|
return unless $ee;
|
|
1151
1159
|
delete $$et{ee}; # use only once
|
|
1152
1160
|
|
|
1153
|
-
my $eeOpt = $et->Options('ExtractEmbedded');
|
|
1161
|
+
my $eeOpt = $et->Options('ExtractEmbedded') || 0;
|
|
1154
1162
|
my $type = $$et{HandlerType} || '';
|
|
1155
1163
|
if ($type eq 'vide') {
|
|
1156
1164
|
# only process specific types of video streams
|
|
@@ -1252,6 +1260,7 @@ Sample: for ($i=0; ; ) {
|
|
|
1252
1260
|
$hdrFmt = ($hdrLen == 4 ? 'N' : $hdrLen == 2 ? 'n' : 'C');
|
|
1253
1261
|
require Image::ExifTool::H264;
|
|
1254
1262
|
}
|
|
1263
|
+
|
|
1255
1264
|
# loop through all samples
|
|
1256
1265
|
for ($i=0; $i<@$start and $i<@$size; ++$i) {
|
|
1257
1266
|
|
|
@@ -1272,6 +1281,7 @@ Sample: for ($i=0; ; ) {
|
|
|
1272
1281
|
}
|
|
1273
1282
|
# read the sample data
|
|
1274
1283
|
$raf->Seek($$start[$i], 0) or $et->WarnOnce("Seek error in $type data"), next;
|
|
1284
|
+
my $buff;
|
|
1275
1285
|
my $n = $raf->Read($buff, $size);
|
|
1276
1286
|
unless ($n == $size) {
|
|
1277
1287
|
$et->WarnOnce("Error reading $type data");
|
|
@@ -1293,10 +1303,7 @@ Sample: for ($i=0; ; ) {
|
|
|
1293
1303
|
$pos += $hdrLen + $len;
|
|
1294
1304
|
last if $pos + $hdrLen >= length($buff);
|
|
1295
1305
|
}
|
|
1296
|
-
if
|
|
1297
|
-
my $eeOpt = $et->Options('ExtractEmbedded');
|
|
1298
|
-
last unless $eeOpt and $eeOpt > 2;
|
|
1299
|
-
}
|
|
1306
|
+
last if $$et{GotNAL06} and $eeOpt < 3;
|
|
1300
1307
|
next;
|
|
1301
1308
|
}
|
|
1302
1309
|
if ($verbose > 1) {
|
|
@@ -1344,11 +1351,12 @@ Sample: for ($i=0; ; ) {
|
|
|
1344
1351
|
$et->HandleTag($tagTbl, GPSLatitude => Get32s(\$buff, 12+$n) * 180/0x80000000);
|
|
1345
1352
|
$et->HandleTag($tagTbl, GPSLongitude => Get32s(\$buff, 16+$n) * 180/0x80000000);
|
|
1346
1353
|
$et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, 8+$n) * $mphToKph);
|
|
1347
|
-
SetGPSDateTime($et, $tagTbl, $time[$i]);
|
|
1354
|
+
SetGPSDateTime($et, $tagTbl, $time[$i], 1);
|
|
1348
1355
|
next; # all done (don't store/process as text)
|
|
1349
1356
|
}
|
|
1350
|
-
unless (defined $val) {
|
|
1351
|
-
|
|
1357
|
+
unless (defined $val or $buff =~ /\0[^\0]/) {
|
|
1358
|
+
# just store any other plain text
|
|
1359
|
+
$et->HandleTag($tagTbl, Text => $buff);
|
|
1352
1360
|
$handled = 1;
|
|
1353
1361
|
}
|
|
1354
1362
|
}
|
|
@@ -1382,7 +1390,10 @@ Sample: for ($i=0; ; ) {
|
|
|
1382
1390
|
} elsif ($type eq 'gps ') { # (ie. GPSDataList tag)
|
|
1383
1391
|
|
|
1384
1392
|
if ($buff =~ /^....freeGPS /s) {
|
|
1385
|
-
#
|
|
1393
|
+
# process by brute scan instead if ExtractEmbedded >= 3
|
|
1394
|
+
# (some videos don't reference all freeGPS info from 'gps ' table, eg. INNOV)
|
|
1395
|
+
last if $eeOpt >= 3;
|
|
1396
|
+
# decode "freeGPS " data (Novatek and others)
|
|
1386
1397
|
ProcessFreeGPS($et, {
|
|
1387
1398
|
DataPt => \$buff,
|
|
1388
1399
|
DataPos => $$start[$i],
|
|
@@ -1415,7 +1426,7 @@ Sample: for ($i=0; ; ) {
|
|
|
1415
1426
|
}
|
|
1416
1427
|
# clean up
|
|
1417
1428
|
$raf->Seek($tell, 0); # restore original file position
|
|
1418
|
-
$$et{DOC_NUM}
|
|
1429
|
+
delete $$et{DOC_NUM};
|
|
1419
1430
|
$$et{HandlerType} = $$et{HanderDesc} = '';
|
|
1420
1431
|
}
|
|
1421
1432
|
|
|
@@ -1433,11 +1444,10 @@ sub ConvertLatLon($$)
|
|
|
1433
1444
|
}
|
|
1434
1445
|
|
|
1435
1446
|
#------------------------------------------------------------------------------
|
|
1436
|
-
# Process "freeGPS " data blocks
|
|
1447
|
+
# Process "freeGPS " data blocks
|
|
1437
1448
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref {DataPt,SampleTime,SampleDuration}, 2) tagTable ref
|
|
1438
1449
|
# Returns: 1 on success (or 0 on unrecognized or "measurement-void" GPS data)
|
|
1439
1450
|
# Notes:
|
|
1440
|
-
# - also see ProcessFreeGPS2() below for processing of other types of freeGPS blocks
|
|
1441
1451
|
sub ProcessFreeGPS($$$)
|
|
1442
1452
|
{
|
|
1443
1453
|
my ($et, $dirInfo, $tagTbl) = @_;
|
|
@@ -1446,11 +1456,15 @@ sub ProcessFreeGPS($$$)
|
|
|
1446
1456
|
my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl, $ddd);
|
|
1447
1457
|
my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, @acc, @xtra);
|
|
1448
1458
|
|
|
1449
|
-
return 0 if $dirLen <
|
|
1459
|
+
return 0 if $dirLen < 82;
|
|
1460
|
+
|
|
1461
|
+
my $debug = $et->Options('Debug');
|
|
1462
|
+
my $oldOrder = GetByteOrder();
|
|
1463
|
+
SetByteOrder('II');
|
|
1450
1464
|
|
|
1451
1465
|
if (substr($$dataPt,18,8) eq "\xaa\xaa\xf2\xe1\xf0\xee\x54\x54") {
|
|
1452
1466
|
|
|
1453
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1467
|
+
$debug and $et->FoundTag(GPSType => 1);
|
|
1454
1468
|
# (this is very similar to the encrypted text format)
|
|
1455
1469
|
# decode encrypted ASCII-based GPS (DashCam Azdome GS63H, ref 5)
|
|
1456
1470
|
# header looks like this in my sample:
|
|
@@ -1515,12 +1529,19 @@ sub ProcessFreeGPS($$$)
|
|
|
1515
1529
|
|
|
1516
1530
|
} elsif ($$dataPt =~ /^.{52}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/s) {
|
|
1517
1531
|
|
|
1518
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1519
|
-
# decode NMEA-format GPS data (
|
|
1532
|
+
$debug and $et->FoundTag(GPSType => 2);
|
|
1533
|
+
# decode NMEA-format GPS data (Nextbase 512GW dashcam, ref PH)
|
|
1520
1534
|
# header looks like this in my sample:
|
|
1521
1535
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 40 01 00 00 [....freeGPS @...]
|
|
1522
1536
|
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1523
1537
|
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1538
|
+
# 0030: 00 00 00 00 32 30 31 38 30 39 31 39 31 30 30 39 [....201809191009]
|
|
1539
|
+
# 0040: 35 39 00 00 1c 01 00 00 06 00 00 00 ef ff ff ff [59..............]
|
|
1540
|
+
# 0050: 20 24 47 50 52 4d 43 2c 30 38 30 39 35 31 2e 30 [ $GPRMC,080951.0]
|
|
1541
|
+
# 0060: 30 30 2c 41 2c 35 32 30 37 2e 39 30 39 37 2c 4e [00,A,5207.9097,N]
|
|
1542
|
+
# 0070: 2c 30 30 35 30 35 2e 35 31 37 35 2c 45 2c 35 35 [,00505.5175,E,55]
|
|
1543
|
+
# 0080: 2e 31 31 2c 31 32 35 2e 35 38 2c 31 39 30 39 31 [.11,125.58,19091]
|
|
1544
|
+
# 0090: 38 2c 2c 2c 41 2a 35 39 0d 0a 00 00 00 00 00 00 [8,,,A*59........]
|
|
1524
1545
|
push @xtra, CameraDateTime => "$1:$2:$3 $4:$5:$6";
|
|
1525
1546
|
if ($$dataPt =~ /\$[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d+\.\d+),([NS]),(\d+\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/s) {
|
|
1526
1547
|
($lat,$latRef,$lon,$lonRef) = ($5,$6,$7,$8);
|
|
@@ -1544,7 +1565,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1544
1565
|
|
|
1545
1566
|
} elsif ($$dataPt =~ /^.{37}\0\0\0A([NS])([EW])/s) {
|
|
1546
1567
|
|
|
1547
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1568
|
+
$debug and $et->FoundTag(GPSType => 3);
|
|
1548
1569
|
# decode freeGPS from ViofoA119v3 dashcam (similar to Novatek GPS format)
|
|
1549
1570
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
|
1550
1571
|
# 0010: 05 00 00 00 2f 00 00 00 03 00 00 00 13 00 00 00 [..../...........]
|
|
@@ -1552,9 +1573,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1552
1573
|
# 0030: f1 47 40 46 66 66 d2 41 85 eb 83 41 00 00 00 00 [.G@Fff.A...A....]
|
|
1553
1574
|
($latRef, $lonRef) = ($1, $2);
|
|
1554
1575
|
($hr,$min,$sec,$yr,$mon,$day) = unpack('x16V6', $$dataPt);
|
|
1555
|
-
if ($yr
|
|
1556
|
-
$yr += 2000;
|
|
1557
|
-
} else {
|
|
1576
|
+
if ($yr >= 2000) {
|
|
1558
1577
|
# Kenwood dashcam sometimes stores absolute year and local time
|
|
1559
1578
|
# (but sometimes year since 2000 and UTC time in same video!)
|
|
1560
1579
|
require Time::Local;
|
|
@@ -1564,7 +1583,6 @@ sub ProcessFreeGPS($$$)
|
|
|
1564
1583
|
++$mon;
|
|
1565
1584
|
$et->WarnOnce('Converting GPSDateTime to UTC based on local time zone',1);
|
|
1566
1585
|
}
|
|
1567
|
-
SetByteOrder('II');
|
|
1568
1586
|
$lat = GetFloat($dataPt, 0x2c);
|
|
1569
1587
|
$lon = GetFloat($dataPt, 0x30);
|
|
1570
1588
|
$spd = GetFloat($dataPt, 0x34) * $knotsToKph; # (convert knots to km/h)
|
|
@@ -1575,11 +1593,10 @@ sub ProcessFreeGPS($$$)
|
|
|
1575
1593
|
@acc = unpack('V3', $tmp);
|
|
1576
1594
|
map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
|
|
1577
1595
|
}
|
|
1578
|
-
SetByteOrder('MM');
|
|
1579
1596
|
|
|
1580
1597
|
} elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
|
|
1581
1598
|
|
|
1582
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1599
|
+
$debug and $et->FoundTag(GPSType => 4);
|
|
1583
1600
|
# also decode 'gpmd' chunk from Kingslim D4 dashcam videos
|
|
1584
1601
|
# 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
|
|
1585
1602
|
# 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
|
|
@@ -1591,7 +1608,6 @@ sub ProcessFreeGPS($$$)
|
|
|
1591
1608
|
# 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
|
|
1592
1609
|
($latRef, $lonRef) = ($1, $2);
|
|
1593
1610
|
($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
|
|
1594
|
-
SetByteOrder('II');
|
|
1595
1611
|
# lat/lon aren't decoded properly, but spd,trk,acc are
|
|
1596
1612
|
$lat = GetFloat($dataPt, 0x1c);
|
|
1597
1613
|
$lon = GetFloat($dataPt, 0x20);
|
|
@@ -1604,11 +1620,10 @@ sub ProcessFreeGPS($$$)
|
|
|
1604
1620
|
$acc[0] = GetFloat($dataPt, 0x2c);
|
|
1605
1621
|
$acc[1] = GetFloat($dataPt, 0x30);
|
|
1606
1622
|
$acc[2] = GetFloat($dataPt, 0x34);
|
|
1607
|
-
SetByteOrder('MM');
|
|
1608
1623
|
|
|
1609
1624
|
} elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
|
|
1610
1625
|
|
|
1611
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1626
|
+
$debug and $et->FoundTag(GPSType => 5);
|
|
1612
1627
|
# decode freeGPS from Akaso dashcam
|
|
1613
1628
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 60 00 00 00 [....freeGPS `...]
|
|
1614
1629
|
# 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
|
|
@@ -1617,19 +1632,32 @@ sub ProcessFreeGPS($$$)
|
|
|
1617
1632
|
# 0040: 13 b3 ca 44 4e 00 00 00 29 92 fb 45 45 00 00 00 [...DN...)..EE...]
|
|
1618
1633
|
# 0050: d9 ee b4 41 ec d1 d3 42 e4 07 00 00 01 00 00 00 [...A...B........]
|
|
1619
1634
|
# 0060: 0c 00 00 00 01 00 00 00 05 00 00 00 00 00 00 00 [................]
|
|
1635
|
+
# (unknown dashcam, "Anticlock 2 2020_1125_1455_007.MOV"):
|
|
1636
|
+
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 68 00 00 00 [....freeGPS h...]
|
|
1637
|
+
# 0010: 32 30 31 33 30 33 32 35 41 00 00 00 00 00 00 00 [20130325A.......]
|
|
1638
|
+
# 0020: 41 70 72 20 20 36 20 32 30 31 36 2c 20 31 36 3a [Apr 6 2016, 16:]
|
|
1639
|
+
# 0030: 0e 00 00 00 38 00 00 00 22 00 00 00 41 00 00 00 [....8..."...A...]
|
|
1640
|
+
# 0040: 8a 63 24 45 53 00 00 00 9f e6 42 45 45 00 00 00 [.c$ES.....BEE...]
|
|
1641
|
+
# 0050: 59 c0 04 3f 52 b8 42 41 14 00 00 00 0b 00 00 00 [Y..?R.BA........]
|
|
1642
|
+
# 0060: 19 00 00 00 06 00 00 00 05 00 00 00 f6 ff ff ff [................]
|
|
1643
|
+
# 0070: 03 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1620
1644
|
($latRef, $lonRef) = ($1, $2);
|
|
1621
|
-
($hr, $min, $sec, $yr, $mon, $day) = unpack('
|
|
1622
|
-
SetByteOrder('II');
|
|
1645
|
+
($hr, $min, $sec, $yr, $mon, $day, @acc) = unpack('x48V3x28V6', $$dataPt);
|
|
1623
1646
|
$lat = GetFloat($dataPt, 0x40);
|
|
1624
1647
|
$lon = GetFloat($dataPt, 0x48);
|
|
1625
1648
|
$spd = GetFloat($dataPt, 0x50);
|
|
1626
|
-
$trk = GetFloat($dataPt, 0x54)
|
|
1627
|
-
|
|
1628
|
-
|
|
1649
|
+
$trk = GetFloat($dataPt, 0x54);
|
|
1650
|
+
if (substr($$dataPt, 16, 4) eq 'x.xx') {
|
|
1651
|
+
$trk += 180; # (why is this off by 180?)
|
|
1652
|
+
$trk -= 360 if $trk >= 360;
|
|
1653
|
+
undef @acc;
|
|
1654
|
+
} else {
|
|
1655
|
+
map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc; # (NC)
|
|
1656
|
+
}
|
|
1629
1657
|
|
|
1630
1658
|
} elsif ($$dataPt =~ /^.{60}4W`b]S</s and length($$dataPt) >= 140) {
|
|
1631
1659
|
|
|
1632
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1660
|
+
$debug and $et->FoundTag(GPSType => 6);
|
|
1633
1661
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 01 00 00 [..@.freeGPS ....]
|
|
1634
1662
|
# 0010: 5a 58 53 42 4e 58 59 53 00 00 00 00 00 00 00 00 [ZXSBNXYS........]
|
|
1635
1663
|
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
@@ -1639,7 +1667,10 @@ sub ProcessFreeGPS($$$)
|
|
|
1639
1667
|
# 0060: 42 3e 49 49 40 42 45 3c 55 3c 45 47 3e 45 43 41 [B>II@BE<U<EG>ECA]
|
|
1640
1668
|
# decipher $GPRMC by subtracting 16 from each character value
|
|
1641
1669
|
$_ = pack 'C*', map { $_>=16 and $_-=16 } unpack('x60C80', $$dataPt);
|
|
1642
|
-
|
|
1670
|
+
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+)/) {
|
|
1671
|
+
SetByteOrder($oldOrder);
|
|
1672
|
+
return 0;
|
|
1673
|
+
}
|
|
1643
1674
|
($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
|
|
1644
1675
|
$yr += ($yr >= 70 ? 1900 : 2000);
|
|
1645
1676
|
$spd = $9 * $knotsToKph if length $9;
|
|
@@ -1647,7 +1678,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1647
1678
|
|
|
1648
1679
|
} elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0{5}/s) {
|
|
1649
1680
|
|
|
1650
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1681
|
+
$debug and $et->FoundTag(GPSType => 7);
|
|
1651
1682
|
# Akaso V1 dascham
|
|
1652
1683
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
|
|
1653
1684
|
# 0010: 59 6e 64 41 6b 61 73 6f 43 61 72 00 00 00 00 00 [YndAkasoCar.....]
|
|
@@ -1671,27 +1702,20 @@ sub ProcessFreeGPS($$$)
|
|
|
1671
1702
|
# 0090: 0a 00 00 00 e5 07 00 00 0c 00 00 00 1c 00 00 00 [................]
|
|
1672
1703
|
($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
|
|
1673
1704
|
unpack('x48V6a1a1a1x1', $$dataPt);
|
|
1674
|
-
# ignore invalid fixes
|
|
1675
|
-
return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
|
|
1676
|
-
($lonRef eq 'E' or $lonRef eq 'W');
|
|
1677
1705
|
|
|
1678
1706
|
$et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
|
|
1679
1707
|
# (see https://exiftool.org/forum/index.php?topic=11320.0)
|
|
1680
1708
|
|
|
1681
|
-
SetByteOrder('II');
|
|
1682
|
-
|
|
1683
1709
|
$spd = GetFloat($dataPt, 0x60);
|
|
1684
1710
|
$trk = GetFloat($dataPt, 0x64) + 180; # (why is this off by 180?)
|
|
1685
1711
|
$lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
|
|
1686
1712
|
$lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
|
|
1687
1713
|
$ddd = 1; # don't convert until we know what the format is
|
|
1688
|
-
|
|
1689
|
-
SetByteOrder('MM');
|
|
1690
1714
|
#my $serialNum = substr($$dataPt, 0x68, 20);
|
|
1691
1715
|
|
|
1692
1716
|
} elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
|
|
1693
1717
|
|
|
1694
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1718
|
+
$debug and $et->FoundTag(GPSType => 8);
|
|
1695
1719
|
# EACHPAI dash cam
|
|
1696
1720
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 ac 00 00 00 [....freeGPS ....]
|
|
1697
1721
|
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
@@ -1705,6 +1729,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1705
1729
|
|
|
1706
1730
|
$et->WarnOnce("Can't yet decrypt EACHPAI timed GPS", 1);
|
|
1707
1731
|
# (see https://exiftool.org/forum/index.php?topic=5095.msg61266#msg61266)
|
|
1732
|
+
SetByteOrder($oldOrder);
|
|
1708
1733
|
return 1;
|
|
1709
1734
|
|
|
1710
1735
|
my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
|
|
@@ -1714,7 +1739,7 @@ sub ProcessFreeGPS($$$)
|
|
|
1714
1739
|
|
|
1715
1740
|
} elsif ($$dataPt =~ /^.{64}A([NS])([EW])\0/s) {
|
|
1716
1741
|
|
|
1717
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1742
|
+
$debug and $et->FoundTag(GPSType => 9);
|
|
1718
1743
|
# Vantrue S1 dashcam
|
|
1719
1744
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
|
|
1720
1745
|
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
@@ -1726,103 +1751,21 @@ sub ProcessFreeGPS($$$)
|
|
|
1726
1751
|
# 0070: 05 00 00 00 7f 00 00 00 07 01 00 00 00 00 00 00 [................]
|
|
1727
1752
|
($latRef, $lonRef) = ($1, $2);
|
|
1728
1753
|
($yr,$mon,$day,$hr,$min,$sec,@acc) = unpack('x68V6x20V3', $$dataPt);
|
|
1729
|
-
|
|
1730
|
-
|
|
1754
|
+
unless ($mon>=1 and $mon<=12 and $day>=1 and $day<=31) {
|
|
1755
|
+
SetByteOrder($oldOrder);
|
|
1756
|
+
return 0;
|
|
1757
|
+
}
|
|
1731
1758
|
# (not sure about acc scaling)
|
|
1732
1759
|
map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc;
|
|
1733
|
-
SetByteOrder('II');
|
|
1734
1760
|
$lon = GetFloat($dataPt, 0x5c);
|
|
1735
1761
|
$lat = GetFloat($dataPt, 0x60);
|
|
1736
1762
|
$spd = GetFloat($dataPt, 0x64) * $knotsToKph;
|
|
1737
1763
|
$trk = GetFloat($dataPt, 0x68);
|
|
1738
1764
|
$alt = GetFloat($dataPt, 0x6c);
|
|
1739
|
-
SetByteOrder('MM');
|
|
1740
|
-
|
|
1741
|
-
} else {
|
|
1742
|
-
|
|
1743
|
-
$debug and $et->FoundTag(GPSType => '1J');
|
|
1744
|
-
# decode binary GPS format (Viofo A119S, ref 2)
|
|
1745
|
-
# header looks like this in my sample:
|
|
1746
|
-
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
|
|
1747
|
-
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1748
|
-
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1749
|
-
# 0030: 10 00 00 00 2d 00 00 00 14 00 00 00 11 00 00 00 [....-...........]
|
|
1750
|
-
# 0040: 0c 00 00 00 1f 00 00 00 41 4e 45 00 5d 9a a9 45 [........ANE.]..E]
|
|
1751
|
-
# 0050: ab 1e e5 44 ec 51 f0 40 b8 5e a5 43 00 00 00 00 [...D.Q.@.^.C....]
|
|
1752
|
-
# (records are same structure as Type 3 Novatek GPS in ProcessFreeGPS2() below)
|
|
1753
|
-
($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef,$lat,$lon,$spd,$trk) =
|
|
1754
|
-
unpack('x48V6a1a1a1x1V4', $$dataPt);
|
|
1755
|
-
# ignore invalid fixes
|
|
1756
|
-
return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
|
|
1757
|
-
($lonRef eq 'E' or $lonRef eq 'W');
|
|
1758
|
-
($lat,$lon,$spd,$trk) = unpack 'f*', pack 'L*', $lat, $lon, $spd, $trk;
|
|
1759
|
-
# lat/lon also stored as doubles by Transcend Driver Pro 230 (ref PH)
|
|
1760
|
-
SetByteOrder('II');
|
|
1761
|
-
my ($lat2, $lon2, $alt2) = (
|
|
1762
|
-
GetDouble($dataPt, 0x70),
|
|
1763
|
-
GetDouble($dataPt, 0x80),
|
|
1764
|
-
# GetDouble($dataPt, 0x98), # (don't know what this is)
|
|
1765
|
-
GetDouble($dataPt,0xa0),
|
|
1766
|
-
# GetDouble($dataPt,0xa8)) # (don't know what this is)
|
|
1767
|
-
);
|
|
1768
|
-
if (abs($lat2-$lat) < 0.001 and abs($lon2-$lon) < 0.001) {
|
|
1769
|
-
$lat = $lat2;
|
|
1770
|
-
$lon = $lon2;
|
|
1771
|
-
$alt = $alt2;
|
|
1772
|
-
}
|
|
1773
|
-
SetByteOrder('MM');
|
|
1774
|
-
$yr += 2000 if $yr < 2000;
|
|
1775
|
-
$spd *= $knotsToKph; # convert speed to km/h
|
|
1776
|
-
# ($trk is not confirmed; may be GPSImageDirection, ref PH)
|
|
1777
|
-
}
|
|
1778
|
-
#
|
|
1779
|
-
# save tag values extracted by above code
|
|
1780
|
-
#
|
|
1781
|
-
FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
|
|
1782
|
-
$sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
|
|
1783
|
-
if (defined $yr) {
|
|
1784
|
-
my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
|
|
1785
|
-
$et->HandleTag($tagTbl, GPSDateTime => $time);
|
|
1786
|
-
} elsif (defined $hr) {
|
|
1787
|
-
my $time = sprintf('%.2d:%.2d:%sZ',$hr,$min,$sec);
|
|
1788
|
-
$et->HandleTag($tagTbl, GPSTimeStamp => $time);
|
|
1789
|
-
}
|
|
1790
|
-
if (defined $lat) {
|
|
1791
|
-
# lat/long are in DDDMM.MMMM format unless $ddd is set
|
|
1792
|
-
ConvertLatLon($lat, $lon) unless $ddd;
|
|
1793
|
-
$et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
|
|
1794
|
-
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
|
|
1795
|
-
}
|
|
1796
|
-
$et->HandleTag($tagTbl, GPSAltitude => $alt) if defined $alt;
|
|
1797
|
-
$et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd;
|
|
1798
|
-
$et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
|
|
1799
|
-
while (@xtra) {
|
|
1800
|
-
my $tag = shift @xtra;
|
|
1801
|
-
$et->HandleTag($tagTbl, $tag => shift @xtra);
|
|
1802
|
-
}
|
|
1803
|
-
$et->HandleTag($tagTbl, Accelerometer => \@acc) if @acc;
|
|
1804
|
-
return 1;
|
|
1805
|
-
}
|
|
1806
1765
|
|
|
1807
|
-
|
|
1808
|
-
# Process "freeGPS " data blocks _not_ referenced by a 'gps ' atom
|
|
1809
|
-
# Inputs: 0) ExifTool ref, 1) dirInfo ref {DataPt,DataPos,DirLen}, 2) tagTable ref
|
|
1810
|
-
# Returns: 1 on success
|
|
1811
|
-
# Notes:
|
|
1812
|
-
# - also see ProcessFreeGPS() above
|
|
1813
|
-
sub ProcessFreeGPS2($$$)
|
|
1814
|
-
{
|
|
1815
|
-
my ($et, $dirInfo, $tagTbl) = @_;
|
|
1816
|
-
my $dataPt = $$dirInfo{DataPt};
|
|
1817
|
-
my $dirLen = $$dirInfo{DirLen};
|
|
1818
|
-
my ($yr, $mon, $day, $hr, $min, $sec, $pos, @acc);
|
|
1819
|
-
my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, $ddd, $unk);
|
|
1820
|
-
|
|
1821
|
-
return 0 if $dirLen < 82; # minimum size of block with a single GPS record
|
|
1822
|
-
|
|
1823
|
-
if (substr($$dataPt,0x45,3) eq 'ATC') {
|
|
1766
|
+
} elsif (substr($$dataPt,0x45,3) eq 'ATC') {
|
|
1824
1767
|
|
|
1825
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1768
|
+
$debug and $et->FoundTag(GPSType => 10);
|
|
1826
1769
|
# header looks like this: (sample 1)
|
|
1827
1770
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 38 06 00 00 [....freeGPS 8...]
|
|
1828
1771
|
# 0010: 49 51 53 32 30 31 33 30 33 30 36 42 00 00 00 00 [IQS20130306B....]
|
|
@@ -1930,11 +1873,12 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1930
1873
|
}
|
|
1931
1874
|
# save position of most recent record (needed when parsing the next freeGPS block)
|
|
1932
1875
|
$$et{FreeGPS2}{RecentRecPos} = $lastRecPos;
|
|
1876
|
+
SetByteOrder($oldOrder);
|
|
1933
1877
|
return 1;
|
|
1934
1878
|
|
|
1935
|
-
} elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s) {
|
|
1879
|
+
} elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s and $dirLen >= 0x88) {
|
|
1936
1880
|
|
|
1937
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1881
|
+
$debug and $et->FoundTag(GPSType => 11);
|
|
1938
1882
|
# header looks like this in my sample:
|
|
1939
1883
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 08 01 00 00 [....freeGPS ....]
|
|
1940
1884
|
# 0010: 32 30 31 33 30 38 31 35 2e 30 31 00 00 00 00 00 [20130815.01.....]
|
|
@@ -1963,76 +1907,9 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
1963
1907
|
$spd = GetDouble($dataPt, 0x60) * $knotsToKph;
|
|
1964
1908
|
$trk = GetDouble($dataPt, 0x68);
|
|
1965
1909
|
|
|
1966
|
-
} elsif ($$dataPt =~ /^.{72}A([NS])([EW])/s) {
|
|
1967
|
-
|
|
1968
|
-
# Type 3 (Novatek GPS, ref 2): (in case it wasn't decoded via 'gps ' atom)
|
|
1969
|
-
# 0x30 - int32u hour
|
|
1970
|
-
# 0x34 - int32u minute
|
|
1971
|
-
# 0x38 - int32u second
|
|
1972
|
-
# 0x3c - int32u year - 2000
|
|
1973
|
-
# 0x40 - int32u month
|
|
1974
|
-
# 0x44 - int32u day
|
|
1975
|
-
# 0x48 - int8u GPS status ('A' or 'V')
|
|
1976
|
-
# 0x49 - int8u latitude ref ('N' or 'S')
|
|
1977
|
-
# 0x4a - int8u longitude ref ('E' or 'W')
|
|
1978
|
-
# 0x4b - 0
|
|
1979
|
-
# 0x4c - float latitude (DDMM.MMMMMM)
|
|
1980
|
-
# 0x50 - float longitude (DDMM.MMMMMM)
|
|
1981
|
-
# 0x54 - float speed (knots)
|
|
1982
|
-
# 0x58 - float heading (deg)
|
|
1983
|
-
# Type 3b, same as above for 0x30-0x4a (ref PH)
|
|
1984
|
-
# 0x4c - int32s latitude (decimal degrees * 1e7)
|
|
1985
|
-
# 0x50 - int32s longitude (decimal degrees * 1e7)
|
|
1986
|
-
# 0x54 - int32s speed (m/s * 100)
|
|
1987
|
-
# 0x58 - float altitude (m * 1000, NC)
|
|
1988
|
-
($latRef, $lonRef) = ($1, $2);
|
|
1989
|
-
($hr,$min,$sec,$yr,$mon,$day) = unpack('x48V6', $$dataPt);
|
|
1990
|
-
if (substr($$dataPt, 16, 3) eq 'IQS') {
|
|
1991
|
-
$debug and $et->FoundTag(GPSType => '2C');
|
|
1992
|
-
# Type 3b (ref PH)
|
|
1993
|
-
# header looks like this in my sample:
|
|
1994
|
-
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
|
|
1995
|
-
# 0010: 49 51 53 5f 41 37 5f 32 30 31 35 30 34 31 37 00 [IQS_A7_20150417.]
|
|
1996
|
-
# 0020: 4d 61 72 20 32 39 20 32 30 31 37 2c 20 31 36 3a [Mar 29 2017, 16:]
|
|
1997
|
-
$ddd = 1;
|
|
1998
|
-
$lat = abs Get32s($dataPt, 0x4c) / 1e7;
|
|
1999
|
-
$lon = abs Get32s($dataPt, 0x50) / 1e7;
|
|
2000
|
-
$spd = Get32s($dataPt, 0x54) / 100 * $mpsToKph;
|
|
2001
|
-
$alt = GetFloat($dataPt, 0x58) / 1000; # (NC)
|
|
2002
|
-
|
|
2003
|
-
} else {
|
|
2004
|
-
$debug and $et->FoundTag(GPSType => '2D');
|
|
2005
|
-
# Type 3 (ref 2)
|
|
2006
|
-
# (no sample with this format)
|
|
2007
|
-
$lat = GetFloat($dataPt, 0x4c);
|
|
2008
|
-
$lon = GetFloat($dataPt, 0x50);
|
|
2009
|
-
$spd = GetFloat($dataPt, 0x54) * $knotsToKph;
|
|
2010
|
-
$trk = GetFloat($dataPt, 0x58);
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
} elsif ($$dataPt =~ /^.{60}A\0.{6}([NS])\0.{6}([EW])\0/s and $dirLen >= 112) {
|
|
2014
|
-
|
|
2015
|
-
$debug and $et->FoundTag(GPSType => '2E');
|
|
2016
|
-
# header looks like this in my sample (unknown dashcam, "Anticlock 2 2020_1125_1455_007.MOV"):
|
|
2017
|
-
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 68 00 00 00 [....freeGPS h...]
|
|
2018
|
-
# 0010: 32 30 31 33 30 33 32 35 41 00 00 00 00 00 00 00 [20130325A.......]
|
|
2019
|
-
# 0020: 41 70 72 20 20 36 20 32 30 31 36 2c 20 31 36 3a [Apr 6 2016, 16:]
|
|
2020
|
-
# 0030: 0e 00 00 00 38 00 00 00 22 00 00 00 41 00 00 00 [....8..."...A...]
|
|
2021
|
-
# 0040: 8a 63 24 45 53 00 00 00 9f e6 42 45 45 00 00 00 [.c$ES.....BEE...]
|
|
2022
|
-
# 0050: 59 c0 04 3f 52 b8 42 41 14 00 00 00 0b 00 00 00 [Y..?R.BA........]
|
|
2023
|
-
# 0060: 19 00 00 00 06 00 00 00 05 00 00 00 f6 ff ff ff [................]
|
|
2024
|
-
# 0070: 03 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
2025
|
-
($latRef, $lonRef) = ($1, $2);
|
|
2026
|
-
($hr,$min,$sec,$yr,$mon,$day,@acc) = unpack('x48V3x28V6',$$dataPt);
|
|
2027
|
-
map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc; # (NC)
|
|
2028
|
-
$lat = GetFloat($dataPt, 0x40);
|
|
2029
|
-
$lon = GetFloat($dataPt, 0x48);
|
|
2030
|
-
$spd = GetFloat($dataPt, 0x50);
|
|
2031
|
-
$trk = GetFloat($dataPt, 0x54);
|
|
2032
|
-
|
|
2033
1910
|
} elsif ($$dataPt =~ /^.{16}A([NS])([EW])\0/s) {
|
|
2034
1911
|
|
|
2035
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1912
|
+
$debug and $et->FoundTag(GPSType => 12);
|
|
2036
1913
|
# INNOVV MP4 video (same format as INNOVV TS)
|
|
2037
1914
|
while ($$dataPt =~ /(A[NS][EW]\0.{28})/g) {
|
|
2038
1915
|
my $dat = $1;
|
|
@@ -2050,11 +1927,12 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
2050
1927
|
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
2051
1928
|
$et->HandleTag($tagTbl, Accelerometer => "@acc");
|
|
2052
1929
|
}
|
|
1930
|
+
SetByteOrder($oldOrder);
|
|
2053
1931
|
return 1;
|
|
2054
1932
|
|
|
2055
1933
|
} elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
|
|
2056
1934
|
|
|
2057
|
-
$debug and $et->FoundTag(GPSType =>
|
|
1935
|
+
$debug and $et->FoundTag(GPSType => 13);
|
|
2058
1936
|
# Vantrue N4 dashcam
|
|
2059
1937
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
|
2060
1938
|
# 0010: 0d 00 00 00 16 00 00 00 1e 00 00 00 41 00 00 00 [............A...]
|
|
@@ -2077,16 +1955,82 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
2077
1955
|
map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 1000 } @acc; # (NC)
|
|
2078
1956
|
# (not necessary to read RMC sentence because we already have it all)
|
|
2079
1957
|
|
|
2080
|
-
}
|
|
1958
|
+
} elsif ($$dataPt =~ /^.{72}A[NS][EW]\0/s) {
|
|
2081
1959
|
|
|
2082
|
-
|
|
2083
|
-
# (look for binary GPS as stored by NextBase 512G, ref PH)
|
|
1960
|
+
# decode binary GPS format (Viofo A119S, ref 2)
|
|
2084
1961
|
# header looks like this in my sample:
|
|
1962
|
+
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
|
|
1963
|
+
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1964
|
+
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
|
1965
|
+
# 0030: 10 00 00 00 2d 00 00 00 14 00 00 00 11 00 00 00 [....-...........]
|
|
1966
|
+
# 0040: 0c 00 00 00 1f 00 00 00 41 4e 45 00 5d 9a a9 45 [........ANE.]..E]
|
|
1967
|
+
# 0050: ab 1e e5 44 ec 51 f0 40 b8 5e a5 43 00 00 00 00 [...D.Q.@.^.C....]
|
|
1968
|
+
# (records are same structure as Type 3 Novatek GPS:)
|
|
1969
|
+
# Type 3 (Novatek GPS, ref 2):
|
|
1970
|
+
# 0x30 - int32u hour
|
|
1971
|
+
# 0x34 - int32u minute
|
|
1972
|
+
# 0x38 - int32u second
|
|
1973
|
+
# 0x3c - int32u year - 2000
|
|
1974
|
+
# 0x40 - int32u month
|
|
1975
|
+
# 0x44 - int32u day
|
|
1976
|
+
# 0x48 - int8u GPS status ('A' or 'V')
|
|
1977
|
+
# 0x49 - int8u latitude ref ('N' or 'S')
|
|
1978
|
+
# 0x4a - int8u longitude ref ('E' or 'W')
|
|
1979
|
+
# 0x4b - 0
|
|
1980
|
+
# 0x4c - float latitude (DDMM.MMMMMM)
|
|
1981
|
+
# 0x50 - float longitude (DDMM.MMMMMM)
|
|
1982
|
+
# 0x54 - float speed (knots)
|
|
1983
|
+
# 0x58 - float heading (deg)
|
|
1984
|
+
# Type 3b, same as above for 0x30-0x4a (ref PH)
|
|
1985
|
+
# 0x4c - int32s latitude (decimal degrees * 1e7)
|
|
1986
|
+
# 0x50 - int32s longitude (decimal degrees * 1e7)
|
|
1987
|
+
# 0x54 - int32s speed (m/s * 100)
|
|
1988
|
+
# 0x58 - float altitude (m * 1000, NC)
|
|
1989
|
+
($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
|
|
1990
|
+
unpack('x48V6a1a1a1x1V4', $$dataPt);
|
|
1991
|
+
if (substr($$dataPt, 16, 3) eq 'IQS') {
|
|
1992
|
+
$debug and $et->FoundTag(GPSType => 14);
|
|
1993
|
+
# Type 3b (ref PH)
|
|
1994
|
+
# header looks like this in my sample:
|
|
1995
|
+
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
|
|
1996
|
+
# 0010: 49 51 53 5f 41 37 5f 32 30 31 35 30 34 31 37 00 [IQS_A7_20150417.]
|
|
1997
|
+
# 0020: 4d 61 72 20 32 39 20 32 30 31 37 2c 20 31 36 3a [Mar 29 2017, 16:]
|
|
1998
|
+
$ddd = 1;
|
|
1999
|
+
$lat = abs Get32s($dataPt, 0x4c) / 1e7;
|
|
2000
|
+
$lon = abs Get32s($dataPt, 0x50) / 1e7;
|
|
2001
|
+
$spd = Get32s($dataPt, 0x54) / 100 * $mpsToKph;
|
|
2002
|
+
$alt = GetFloat($dataPt, 0x58) / 1000; # (NC)
|
|
2003
|
+
} else {
|
|
2004
|
+
$debug and $et->FoundTag(GPSType => 15);
|
|
2005
|
+
$lat = GetFloat($dataPt, 0x4c);
|
|
2006
|
+
$lon = GetFloat($dataPt, 0x50);
|
|
2007
|
+
$spd = GetFloat($dataPt, 0x54) * $knotsToKph;
|
|
2008
|
+
$trk = GetFloat($dataPt, 0x58);
|
|
2009
|
+
# ($trk is not confirmed; may be GPSImageDirection, ref PH)
|
|
2010
|
+
}
|
|
2011
|
+
if ($dirLen >= 0xb0) {
|
|
2012
|
+
# lat/lon also stored as doubles by Transcend Driver Pro 230 (ref PH)
|
|
2013
|
+
my ($lat2, $lon2) = ( GetDouble($dataPt, 0x70), GetDouble($dataPt, 0x80) );
|
|
2014
|
+
# (0xa0 is altitude, don't know what 0x98 and 0xa8 are)
|
|
2015
|
+
if (abs($lat2-$lat) < 0.001 and abs($lon2-$lon) < 0.001) {
|
|
2016
|
+
$lat = $lat2;
|
|
2017
|
+
$lon = $lon2;
|
|
2018
|
+
$alt = GetDouble($dataPt, 0xa0);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
} else {
|
|
2023
|
+
|
|
2024
|
+
$debug and $et->FoundTag(GPSType => 16);
|
|
2025
|
+
# (look for binary GPS as stored by Nextbase 512G, ref PH)
|
|
2085
2026
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 01 00 00 [....freeGPS x...]
|
|
2086
2027
|
# 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
|
|
2087
2028
|
# 0020: 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 [00000...........]
|
|
2088
|
-
|
|
2089
|
-
#
|
|
2029
|
+
# 0030: 24 53 02 79 d4 85 07 e2 0a 08 06 2a 01 d1 02 20 [$S.y.......*... ]
|
|
2030
|
+
# 0040: 14 98 ff ff 21 67 97 10 00 00 00 00 00 00 00 00 [....!g..........]
|
|
2031
|
+
# 0050: 24 53 02 a2 d4 42 07 e2 0a 08 06 2a 01 d2 02 20 [$S...B.....*... ]
|
|
2032
|
+
# 0060: 14 98 e3 ff 21 67 3b 10 00 00 00 00 00 00 00 00 [....!g;.........]
|
|
2033
|
+
# 32-byte record structure (big endian!):
|
|
2090
2034
|
# 0x30 - int16u unknown (seen: 0x24 0x53 = "$S")
|
|
2091
2035
|
# 0x32 - int16u speed (m/s * 100)
|
|
2092
2036
|
# 0x34 - int16s heading (deg * 100) (or GPSImgDirection?)
|
|
@@ -2101,8 +2045,9 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
2101
2045
|
# 0x43 - int32s longitude (decimal degrees * 1e7)
|
|
2102
2046
|
# 0x47 - int8u unknown (seen: 16)
|
|
2103
2047
|
# 0x48-0x4f - all zero
|
|
2048
|
+
my $pos;
|
|
2104
2049
|
for ($pos=0x32; ; ) {
|
|
2105
|
-
($spd,$trk,$yr,$mon,$day,$hr,$min,$sec,$
|
|
2050
|
+
($spd,$trk,$yr,$mon,$day,$hr,$min,$sec,$lat,$lon) = unpack "x${pos}nnnCCCCnx1NN", $$dataPt;
|
|
2106
2051
|
# validate record using date/time
|
|
2107
2052
|
last if $yr < 2000 or $yr > 2200 or
|
|
2108
2053
|
$mon < 1 or $mon > 12 or
|
|
@@ -2122,28 +2067,41 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
|
2122
2067
|
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
|
2123
2068
|
last if $pos += 0x20 > length($$dataPt) - 0x1e;
|
|
2124
2069
|
}
|
|
2070
|
+
SetByteOrder($oldOrder);
|
|
2125
2071
|
return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
|
|
2126
2072
|
}
|
|
2127
2073
|
#
|
|
2128
2074
|
# save tag values extracted by above code
|
|
2129
2075
|
#
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
$
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2076
|
+
SetByteOrder($oldOrder);
|
|
2077
|
+
return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
|
|
2078
|
+
FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
|
|
2079
|
+
$sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
|
|
2080
|
+
if (defined $yr) {
|
|
2081
|
+
$yr += 2000 if $yr < 2000;
|
|
2082
|
+
my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%sZ',$yr,$mon,$day,$hr,$min,$sec);
|
|
2083
|
+
$et->HandleTag($tagTbl, GPSDateTime => $time);
|
|
2084
|
+
} elsif (defined $hr) {
|
|
2085
|
+
my $time = sprintf('%.2d:%.2d:%sZ',$hr,$min,$sec);
|
|
2086
|
+
$et->HandleTag($tagTbl, GPSTimeStamp => $time);
|
|
2087
|
+
}
|
|
2088
|
+
if (defined $lat) {
|
|
2089
|
+
# lat/long are in DDDMM.MMMM format unless $ddd is set
|
|
2090
|
+
ConvertLatLon($lat, $lon) unless $ddd;
|
|
2091
|
+
$et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
|
|
2092
|
+
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
|
|
2093
|
+
}
|
|
2141
2094
|
$et->HandleTag($tagTbl, GPSAltitude => $alt) if defined $alt;
|
|
2095
|
+
$et->HandleTag($tagTbl, GPSSpeed => $spd) if defined $spd;
|
|
2096
|
+
$et->HandleTag($tagTbl, GPSTrack => $trk) if defined $trk;
|
|
2097
|
+
while (@xtra) {
|
|
2098
|
+
my $tag = shift @xtra;
|
|
2099
|
+
$et->HandleTag($tagTbl, $tag => shift @xtra);
|
|
2100
|
+
}
|
|
2142
2101
|
$et->HandleTag($tagTbl, Accelerometer => "@acc") if @acc;
|
|
2143
2102
|
return 1;
|
|
2144
2103
|
}
|
|
2145
2104
|
|
|
2146
|
-
|
|
2147
2105
|
#------------------------------------------------------------------------------
|
|
2148
2106
|
# Extract embedded information referenced from a track
|
|
2149
2107
|
# Inputs: 0) ExifTool ref, 1) tag name, 2) data ref
|
|
@@ -2433,6 +2391,68 @@ sub Process_gsen($$$)
|
|
|
2433
2391
|
return 1;
|
|
2434
2392
|
}
|
|
2435
2393
|
|
|
2394
|
+
#------------------------------------------------------------------------------
|
|
2395
|
+
# Process 'gdat' atom Base64-encoded JSON-format timed GPS used by Nextbase software (ref PH)
|
|
2396
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
|
2397
|
+
# Returns: 1 on success
|
|
2398
|
+
sub Process_gdat($$$)
|
|
2399
|
+
{
|
|
2400
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
|
2401
|
+
unless ($$et{OPTIONS}{ExtractEmbedded}) {
|
|
2402
|
+
$et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData',3);
|
|
2403
|
+
return 0;
|
|
2404
|
+
}
|
|
2405
|
+
my $dataPt = $$dirInfo{DataPt};
|
|
2406
|
+
require Image::ExifTool::XMP;
|
|
2407
|
+
$dataPt = Image::ExifTool::XMP::DecodeBase64($$dataPt);
|
|
2408
|
+
my (%dbase, $fix);
|
|
2409
|
+
require Image::ExifTool::Import;
|
|
2410
|
+
Image::ExifTool::Import::ReadJSON($dataPt, \%dbase);
|
|
2411
|
+
my $info = $dbase{'*'} or return 0;
|
|
2412
|
+
$et->HandleTag($tagTbl, CameraModel => $$info{cameraModel}) if $$info{cameraModel};
|
|
2413
|
+
my $gps = $$info{gpsData} or return 0;
|
|
2414
|
+
return 0 unless ref $gps eq 'ARRAY';
|
|
2415
|
+
foreach $fix (@$gps) {
|
|
2416
|
+
next unless ref $fix eq 'HASH' and $$fix{gpsStatus} and $$fix{gpsStatus} eq 'A';
|
|
2417
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
2418
|
+
if ($$fix{datetime}) {
|
|
2419
|
+
$$fix{datetime} =~ tr/-T/: /;
|
|
2420
|
+
$et->HandleTag($tagTbl, GPSDateTime => $$fix{datetime});
|
|
2421
|
+
}
|
|
2422
|
+
$et->HandleTag($tagTbl, GPSLatitude => $$fix{lat}) if defined $$fix{lat};
|
|
2423
|
+
$et->HandleTag($tagTbl, GPSLongitude => $$fix{lon}) if defined $$fix{lon};
|
|
2424
|
+
$et->HandleTag($tagTbl, GPSSpeed => $$fix{speed} * $mphToKph) if defined $$fix{speed};
|
|
2425
|
+
$et->HandleTag($tagTbl, GPSTrack => $$fix{bearing}) if defined $$fix{bearing};
|
|
2426
|
+
if (defined $$fix{xAcc} and defined $$fix{yAcc} and defined $$fix{zAcc}) {
|
|
2427
|
+
$et->HandleTag($tagTbl, Accelerometer => "$$fix{xAcc} $$fix{yAcc} $$fix{zAcc}");
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
delete $$et{DOC_NUM};
|
|
2431
|
+
return 1;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
#------------------------------------------------------------------------------
|
|
2435
|
+
# Extract GPS from Nextbase 'nbmt' atom
|
|
2436
|
+
# Inputs: 0) ExifTool ref, 1) data ref or dirInfo ref, 2) tag table ref
|
|
2437
|
+
# Returns: 1 on success
|
|
2438
|
+
sub Process_nbmt($$$)
|
|
2439
|
+
{
|
|
2440
|
+
my ($et, $dataPt, $tagTbl) = @_;
|
|
2441
|
+
|
|
2442
|
+
if ($$et{OPTIONS}{ExtractEmbedded}) {
|
|
2443
|
+
$$et{DOC_NUM} = $$et{DOC_COUNT} + 1;
|
|
2444
|
+
delete $$et{UnknownTextCount};
|
|
2445
|
+
delete $$et{NoMoreTextDecoding};
|
|
2446
|
+
Process_text($et, $dataPt, $tagTbl, 1);
|
|
2447
|
+
delete $$et{UnknownTextCount};
|
|
2448
|
+
delete $$et{NoMoreTextDecoding};
|
|
2449
|
+
delete $$et{DOC_NUM};
|
|
2450
|
+
} else {
|
|
2451
|
+
$et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData',3);
|
|
2452
|
+
}
|
|
2453
|
+
return 1;
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2436
2456
|
#------------------------------------------------------------------------------
|
|
2437
2457
|
# Process LIGOGPS JSON-format GPS from Yada RoadCam Pro 4K BT58189
|
|
2438
2458
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
|
@@ -2454,9 +2474,8 @@ sub ProcessLIGO_JSON($$$)
|
|
|
2454
2474
|
$et->VerboseDir('LIGO_JSON', undef, length($$dataPt));
|
|
2455
2475
|
while ($$dataPt =~ /LIGOGPSINFO (\{.*?\})/g) {
|
|
2456
2476
|
my $json = $1;
|
|
2457
|
-
my $raf = new File::RandomAccess(\$json);
|
|
2458
2477
|
my %dbase;
|
|
2459
|
-
Image::ExifTool::Import::ReadJSON(
|
|
2478
|
+
Image::ExifTool::Import::ReadJSON(\$json, \%dbase);
|
|
2460
2479
|
my $info = $dbase{'*'} or next;
|
|
2461
2480
|
# my sample contains the following JSON fields (in this order):
|
|
2462
2481
|
# Hour Minute Second Year Month Day (GPS UTC time)
|
|
@@ -2863,6 +2882,8 @@ sub ProcessTTAD($$$)
|
|
|
2863
2882
|
# Extract information from Insta360 trailer (INSV and INSP files) (ref PH)
|
|
2864
2883
|
# Inputs: 0) ExifTool ref, 1) Optional dirInfo ref for returning trailer info
|
|
2865
2884
|
# Returns: true on success
|
|
2885
|
+
# Notes: There looks to be some useful information by telemetry-parser, but
|
|
2886
|
+
# the code is cryptic: https://github.com/AdrianEddy/telemetry-parser
|
|
2866
2887
|
sub ProcessInsta360($;$)
|
|
2867
2888
|
{
|
|
2868
2889
|
local $_;
|
|
@@ -3073,7 +3094,7 @@ sub ProcessInsta360($;$)
|
|
|
3073
3094
|
$raf->Seek($epos, 2) or last; # seek to start of next footer
|
|
3074
3095
|
$raf->Read($buff, 6) == 6 or last; # read footer
|
|
3075
3096
|
}
|
|
3076
|
-
$$et{DOC_NUM}
|
|
3097
|
+
delete $$et{DOC_NUM};
|
|
3077
3098
|
SetByteOrder('MM');
|
|
3078
3099
|
delete $$et{SET_GROUP0};
|
|
3079
3100
|
delete $$et{SET_GROUP1};
|
|
@@ -3215,7 +3236,7 @@ sub ScanMediaData($)
|
|
|
3215
3236
|
{
|
|
3216
3237
|
my $et = shift;
|
|
3217
3238
|
my $raf = $$et{RAF} or return;
|
|
3218
|
-
my ($tagTbl, $
|
|
3239
|
+
my ($tagTbl, $verbose, $buff, $dataLen);
|
|
3219
3240
|
my ($pos, $buf2) = (0, '');
|
|
3220
3241
|
|
|
3221
3242
|
# don't rescan for freeGPS if we already found embedded metadata
|
|
@@ -3251,8 +3272,6 @@ sub ScanMediaData($)
|
|
|
3251
3272
|
# initialize variables for extracting metadata from this block
|
|
3252
3273
|
$tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
|
3253
3274
|
$verbose = $$et{OPTIONS}{Verbose};
|
|
3254
|
-
$oldByteOrder = GetByteOrder();
|
|
3255
|
-
SetByteOrder('II');
|
|
3256
3275
|
$et->VPrint(0, "---- Extract Embedded ----\n");
|
|
3257
3276
|
$$et{INDENT} .= '| ';
|
|
3258
3277
|
}
|
|
@@ -3275,15 +3294,14 @@ sub ScanMediaData($)
|
|
|
3275
3294
|
$et->VerboseDump(\$buff, DataPos => $pos + $dataPos);
|
|
3276
3295
|
}
|
|
3277
3296
|
my $dirInfo = { DataPt => \$buff, DataPos => $pos + $dataPos, DirLen => $len };
|
|
3278
|
-
|
|
3297
|
+
ProcessFreeGPS($et, $dirInfo, $tagTbl);
|
|
3279
3298
|
}
|
|
3280
3299
|
$pos += $len;
|
|
3281
3300
|
$buf2 = substr($buff, $len);
|
|
3282
3301
|
}
|
|
3283
3302
|
if ($tagTbl) {
|
|
3284
|
-
$$et{DOC_NUM}
|
|
3303
|
+
delete $$et{DOC_NUM}; # reset DOC_NUM after extracting embedded metadata
|
|
3285
3304
|
$et->VPrint(0, "--------------------------\n");
|
|
3286
|
-
SetByteOrder($oldByteOrder);
|
|
3287
3305
|
$$et{INDENT} = substr $$et{INDENT}, 0, -2;
|
|
3288
3306
|
}
|
|
3289
3307
|
# process Insta360 trailer if it exists
|