exiftool_vendored 13.06.0 → 13.08.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 +29 -3
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +6 -5
- data/bin/lib/Image/ExifTool/AIFF.pm +1 -1
- data/bin/lib/Image/ExifTool/APE.pm +1 -1
- data/bin/lib/Image/ExifTool/ASF.pm +1 -1
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +4 -3
- data/bin/lib/Image/ExifTool/Canon.pm +19 -1
- data/bin/lib/Image/ExifTool/DJI.pm +1 -1
- data/bin/lib/Image/ExifTool/Exif.pm +2 -2
- data/bin/lib/Image/ExifTool/FITS.pm +2 -2
- data/bin/lib/Image/ExifTool/FLIF.pm +2 -2
- data/bin/lib/Image/ExifTool/FlashPix.pm +11 -11
- data/bin/lib/Image/ExifTool/Font.pm +1 -1
- data/bin/lib/Image/ExifTool/HP.pm +1 -1
- data/bin/lib/Image/ExifTool/ID3.pm +3 -3
- data/bin/lib/Image/ExifTool/IPTC.pm +2 -2
- data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +6 -6
- data/bin/lib/Image/ExifTool/M2TS.pm +39 -9
- data/bin/lib/Image/ExifTool/MXF.pm +2 -2
- data/bin/lib/Image/ExifTool/Matroska.pm +1 -1
- data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
- data/bin/lib/Image/ExifTool/PDF.pm +15 -15
- data/bin/lib/Image/ExifTool/PLIST.pm +1 -1
- data/bin/lib/Image/ExifTool/PNG.pm +4 -4
- data/bin/lib/Image/ExifTool/Panasonic.pm +1 -1
- data/bin/lib/Image/ExifTool/PhaseOne.pm +3 -3
- data/bin/lib/Image/ExifTool/Photoshop.pm +63 -3
- data/bin/lib/Image/ExifTool/Protobuf.pm +4 -4
- data/bin/lib/Image/ExifTool/QuickTime.pm +23 -14
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +336 -91
- data/bin/lib/Image/ExifTool/README +4 -1
- data/bin/lib/Image/ExifTool/RIFF.pm +3 -3
- data/bin/lib/Image/ExifTool/RTF.pm +1 -1
- data/bin/lib/Image/ExifTool/Ricoh.pm +3 -3
- data/bin/lib/Image/ExifTool/Sony.pm +2 -2
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +4 -3
- data/bin/lib/Image/ExifTool/TagLookup.pm +6979 -6970
- data/bin/lib/Image/ExifTool/TagNames.pod +26 -5
- data/bin/lib/Image/ExifTool/VCard.pm +2 -2
- data/bin/lib/Image/ExifTool/Validate.pm +3 -3
- data/bin/lib/Image/ExifTool/WriteExif.pl +2 -2
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +4 -4
- data/bin/lib/Image/ExifTool/WriteXMP.pl +2 -2
- data/bin/lib/Image/ExifTool/Writer.pl +16 -16
- data/bin/lib/Image/ExifTool/XMP.pm +9 -9
- data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
- data/bin/lib/Image/ExifTool.pm +58 -57
- data/bin/lib/Image/ExifTool.pod +1 -1
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -2
@@ -94,6 +94,7 @@ my %insvDataLen = (
|
|
94
94
|
# 0xb00 => 10, # ? (Insta360 X3)
|
95
95
|
# 0xd00 => 10, # ? (Insta360 Ace Pro)
|
96
96
|
# 0x1200 ? # ? (Insta360 Ace Pro)
|
97
|
+
# 0x1600 ? # ? (?)
|
97
98
|
);
|
98
99
|
|
99
100
|
# limit the default amount of data we read for some record types
|
@@ -109,7 +110,7 @@ my %insvLimit = (
|
|
109
110
|
The tags below are extracted from timed metadata in QuickTime and other
|
110
111
|
formats of video files when the ExtractEmbedded option is used. Although
|
111
112
|
most of these tags are combined into the single table below, ExifTool
|
112
|
-
currently reads
|
113
|
+
currently reads 96 different types of timed GPS metadata from video files.
|
113
114
|
},
|
114
115
|
VARS => { NO_ID => 1 },
|
115
116
|
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
@@ -353,6 +354,8 @@ my %insvLimit = (
|
|
353
354
|
Unknown01 => { Unknown => 1 },
|
354
355
|
Unknown02 => { Unknown => 1 },
|
355
356
|
Unknown03 => { Unknown => 1 },
|
357
|
+
M => { Name => 'Unknown_M', Unknown => 1 }, # (from LIGOGPSINFO)
|
358
|
+
H => { Name => 'Unknown_H', Unknown => 1 }, # (from LIGOGPSINFO)
|
356
359
|
);
|
357
360
|
|
358
361
|
# tags found in 'camm' type 0 timed metadata (ref 4)
|
@@ -911,9 +914,9 @@ sub SetGPSDateTime($$$;$)
|
|
911
914
|
if ($$et{CreateDateAtEnd}) { # adjust if CreateDate is at end of video
|
912
915
|
return unless $$value{TimeScale} and $$value{Duration};
|
913
916
|
$sampleTime -= $$value{Duration} / $$value{TimeScale};
|
914
|
-
$et->
|
917
|
+
$et->Warn('Approximating GPSDateTime as CreateDate - Duration + SampleTime', 1);
|
915
918
|
} else {
|
916
|
-
$et->
|
919
|
+
$et->Warn('Approximating GPSDateTime as CreateDate + SampleTime', 1);
|
917
920
|
}
|
918
921
|
my $utc = $et->Options('QuickTimeUTC');
|
919
922
|
$utc = $isUTC unless defined $utc; # (allow QuickTimeUTC=0 to override $isUTC default)
|
@@ -1274,7 +1277,7 @@ sub ProcessSamples($)
|
|
1274
1277
|
($startChunk, $samplesPerChunk, $descIdx) = @{shift @$stsc};
|
1275
1278
|
$nextChunk = $$stsc[0][0] if @$stsc;
|
1276
1279
|
}
|
1277
|
-
@$size < @$start + $samplesPerChunk and $et->
|
1280
|
+
@$size < @$start + $samplesPerChunk and $et->Warn('Sample size error'), last;
|
1278
1281
|
last unless defined $chunkStart and length $chunkStart;
|
1279
1282
|
my $sampleStart = $chunkStart;
|
1280
1283
|
my $chunkSize = 0;
|
@@ -1302,7 +1305,7 @@ Sample: for ($i=0; ; ) {
|
|
1302
1305
|
push @chunkSize, $chunkSize;
|
1303
1306
|
++$iChunk;
|
1304
1307
|
}
|
1305
|
-
@$start == @$size or $et->
|
1308
|
+
@$start == @$size or $et->Warn('Incorrect sample start/size count'), return;
|
1306
1309
|
# process as chunks if we are only interested in calculating hash
|
1307
1310
|
if ($type eq 'soun' or $type eq 'vide') {
|
1308
1311
|
$start = $stco;
|
@@ -1332,7 +1335,7 @@ Sample: for ($i=0; ; ) {
|
|
1332
1335
|
$hdrFmt = ($hdrLen == 4 ? 'N' : $hdrLen == 2 ? 'n' : 'C');
|
1333
1336
|
require Image::ExifTool::H264;
|
1334
1337
|
}
|
1335
|
-
|
1338
|
+
|
1336
1339
|
# loop through all samples
|
1337
1340
|
for ($i=0; $i<@$start and $i<@$size; ++$i) {
|
1338
1341
|
|
@@ -1352,11 +1355,11 @@ Sample: for ($i=0; ; ) {
|
|
1352
1355
|
}
|
1353
1356
|
}
|
1354
1357
|
# read the sample data
|
1355
|
-
$raf->Seek($$start[$i], 0) or $et->
|
1358
|
+
$raf->Seek($$start[$i], 0) or $et->Warn("Seek error in $type data"), next;
|
1356
1359
|
my $buff;
|
1357
1360
|
my $n = $raf->Read($buff, $size);
|
1358
1361
|
unless ($n == $size) {
|
1359
|
-
$et->
|
1362
|
+
$et->Warn("Error reading $type data");
|
1360
1363
|
next unless $n;
|
1361
1364
|
$size = $n;
|
1362
1365
|
}
|
@@ -1560,7 +1563,7 @@ sub ProcessFreeGPS($$$)
|
|
1560
1563
|
my ($et, $dirInfo, $tagTbl) = @_;
|
1561
1564
|
my $dataPt = $$dirInfo{DataPt};
|
1562
1565
|
my $dirLen = length $$dataPt;
|
1563
|
-
my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl, $ddd);
|
1566
|
+
my ($yr, $mon, $day, $hr, $min, $sec, $ss, $stat, $lbl, $ddd, $done);
|
1564
1567
|
my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, @acc, @xtra);
|
1565
1568
|
|
1566
1569
|
return 0 if $dirLen < 82;
|
@@ -1699,7 +1702,7 @@ sub ProcessFreeGPS($$$)
|
|
1699
1702
|
($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
|
1700
1703
|
$yr += 1900;
|
1701
1704
|
++$mon;
|
1702
|
-
$et->
|
1705
|
+
$et->Warn('Converting GPSDateTime to UTC based on local time zone',1);
|
1703
1706
|
}
|
1704
1707
|
$lat = GetFloat($dataPt, 0x2c);
|
1705
1708
|
$lon = GetFloat($dataPt, 0x30);
|
@@ -1744,36 +1747,69 @@ sub ProcessFreeGPS($$$)
|
|
1744
1747
|
($lon = DecryptLucky($ln, $key)) =~ /^\d{1,5}\.\d+$/ or undef($lon), next;
|
1745
1748
|
last;
|
1746
1749
|
}
|
1747
|
-
$lon or $et->
|
1750
|
+
$lon or $et->Warn('Unknown encryption for latitude/longitude');
|
1748
1751
|
}
|
1749
1752
|
}
|
1750
1753
|
|
1751
|
-
} elsif ($$dataPt =~
|
1754
|
+
} elsif ($$dataPt =~ /^(.{16}|.{48}|.{80})LIGOGPSINFO\0/s and length($$dataPt) >= length($1) + 0x84) {
|
1752
1755
|
|
1753
1756
|
$debug and $et->FoundTag(GPSType => 5);
|
1754
|
-
|
1755
|
-
#
|
1756
|
-
#
|
1757
|
-
#
|
1758
|
-
#
|
1759
|
-
#
|
1760
|
-
#
|
1761
|
-
#
|
1762
|
-
#
|
1763
|
-
|
1764
|
-
|
1765
|
-
#
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1757
|
+
my $pos = length $1;
|
1758
|
+
# iiway s1 dual dash cam
|
1759
|
+
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
1760
|
+
# 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
|
1761
|
+
# 0020: 0a 00 00 00 23 23 23 23 6a 00 00 00 c0 20 20 20 [....####j.... ]
|
1762
|
+
# 0030: 20 f0 12 10 12 22 e1 0e 10 12 2f 90 10 13 02 f2 [ ...."..../.....]
|
1763
|
+
# ABASK A8 4K Dashcam (different scaling factor)
|
1764
|
+
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
1765
|
+
# 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
|
1766
|
+
# 0020: 00 00 00 00 23 23 23 23 69 00 00 00 c0 20 20 20 [....####i.... ]
|
1767
|
+
# 0030: 20 f0 12 10 12 23 e5 0e 10 12 2f 99 10 11 02 f2 [ ....#..../.....]
|
1768
|
+
# XGODY 12" 4K Dashcam
|
1769
|
+
# 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
|
1770
|
+
# 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
|
1771
|
+
# 0020: cd 61 00 00 23 23 23 23 6d 00 00 00 c1 ec 41 20 [.a..####m.....A ]
|
1772
|
+
# 0030: 20 f0 12 10 12 24 e5 0e 10 11 2f 92 10 12 00 f6 [ ....$..../.....]
|
1773
|
+
# Rexing dashcam V1GW-4K
|
1774
|
+
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
1775
|
+
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
1776
|
+
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
1777
|
+
# 0030: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
|
1778
|
+
# 0040: 01 00 00 00 23 23 23 23 73 00 00 00 c0 20 20 20 [....####s.... ]
|
1779
|
+
# 0050: 20 f0 12 10 12 23 e5 0e 10 12 2f 95 10 12 01 f3 [ ....#..../.....]
|
1780
|
+
# 0060: 16 18 10 26 b4 1a 10 04 f2 6e 18 12 20 f0 0e 11 [...&.....n.. ...]
|
1781
|
+
# 0070: 13 22 b3 16 10 01 fb 76 18 10 24 fa 0e 11 10 22 [.".....v..$...."]
|
1782
|
+
# Kingslim D4 dashcam
|
1783
|
+
# 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
|
1784
|
+
# 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
|
1785
|
+
# 0020: 15 70 fe c5 29 5c c3 41 ae c7 af 42 00 00 d1 be [.p..)\.A...B....]
|
1786
|
+
# 0030: 00 00 80 3b 00 00 2c 3e 00 00 00 00 00 00 00 00 [...;..,>........]
|
1787
|
+
# 0040: 00 00 00 00 00 00 00 00 00 00 00 00 26 26 26 26 [............&&&&]
|
1788
|
+
# 0050: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
|
1789
|
+
# 0060: 01 00 00 00 23 23 23 23 75 00 00 00 c0 22 20 20 [....####u...." ]
|
1790
|
+
# 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
|
1791
|
+
my %dirInfo = ( DataPt => $dataPt, DirStart => $pos, DirName => "LigoGPS_$pos" );
|
1792
|
+
# (this is weak, but the only difference I could find between these 2 headers)
|
1793
|
+
# (NOTE: ../testpics/gps_video/forum16229.mp4 uses this word for a counter!)
|
1794
|
+
$$et{LigoGPSScale} = 3 if $pos == 16 and $$dataPt =~ /^.{12}\xf0\x03\0\0.{16}\0{4}/s;
|
1795
|
+
ProcessLigoGPS($et, \%dirInfo, $tagTbl);
|
1796
|
+
$done = 1;
|
1797
|
+
|
1798
|
+
# also... when offset is 0x50 (Kingslim), the GPS also exists in this format:
|
1799
|
+
# ($latRef, $lonRef) = ($1, $2);
|
1800
|
+
# ($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
|
1801
|
+
# # lat/lon aren't decoded properly, but spd,trk,acc are
|
1802
|
+
# $lat = GetFloat($dataPt, 0x1c);
|
1803
|
+
# $lon = GetFloat($dataPt, 0x20);
|
1804
|
+
# $et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
|
1805
|
+
# $et->Warn('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
|
1806
|
+
# $lat = abs $lat;
|
1807
|
+
# $lon = abs $lon;
|
1808
|
+
# $spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
|
1809
|
+
# $trk = GetFloat($dataPt, 0x28);
|
1810
|
+
# $acc[0] = GetFloat($dataPt, 0x2c);
|
1811
|
+
# $acc[1] = GetFloat($dataPt, 0x30);
|
1812
|
+
# $acc[2] = GetFloat($dataPt, 0x34);
|
1777
1813
|
|
1778
1814
|
} elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
|
1779
1815
|
|
@@ -1821,14 +1857,14 @@ sub ProcessFreeGPS($$$)
|
|
1821
1857
|
# 0060: 42 3e 49 49 40 42 45 3c 55 3c 45 47 3e 45 43 41 [B>II@BE<U<EG>ECA]
|
1822
1858
|
# decipher $GPRMC by subtracting 16 from each character value
|
1823
1859
|
$_ = pack 'C*', map { $_>=16 and $_-=16 } unpack('x60C80', $$dataPt);
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1860
|
+
if (/[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+)/) {
|
1861
|
+
($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
|
1862
|
+
$yr += ($yr >= 70 ? 1900 : 2000);
|
1863
|
+
$spd = $9 * $knotsToKph if length $9;
|
1864
|
+
$trk = $10 if length $10;
|
1865
|
+
} else {
|
1866
|
+
$done = 1;
|
1827
1867
|
}
|
1828
|
-
($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
|
1829
|
-
$yr += ($yr >= 70 ? 1900 : 2000);
|
1830
|
-
$spd = $9 * $knotsToKph if length $9;
|
1831
|
-
$trk = $10 if length $10;
|
1832
1868
|
|
1833
1869
|
} elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0{5}/s) {
|
1834
1870
|
|
@@ -1857,7 +1893,7 @@ sub ProcessFreeGPS($$$)
|
|
1857
1893
|
($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
|
1858
1894
|
unpack('x48V6a1a1a1x1', $$dataPt);
|
1859
1895
|
|
1860
|
-
$et->
|
1896
|
+
$et->Warn('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
|
1861
1897
|
# (see https://exiftool.org/forum/index.php?topic=11320.0)
|
1862
1898
|
|
1863
1899
|
$spd = GetFloat($dataPt, 0x60);
|
@@ -1865,7 +1901,7 @@ sub ProcessFreeGPS($$$)
|
|
1865
1901
|
$lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
|
1866
1902
|
$lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
|
1867
1903
|
$ddd = 1; # don't convert until we know what the format is
|
1868
|
-
#my $serialNum = substr($$dataPt, 0x68, 20);
|
1904
|
+
#my $serialNum = substr($$dataPt, 0x68, 20); # (confirmed)
|
1869
1905
|
|
1870
1906
|
} elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
|
1871
1907
|
|
@@ -1881,15 +1917,14 @@ sub ProcessFreeGPS($$$)
|
|
1881
1917
|
# 0070: 43 41 3c 40 42 40 46 42 40 3c 3c 3c 51 3a 47 46 [CA<@B@FB@<<<Q:GF]
|
1882
1918
|
# 0080: 00 2a 36 35 00 00 00 00 00 00 00 00 00 00 00 00 [.*65............]
|
1883
1919
|
|
1884
|
-
$et->
|
1920
|
+
$et->Warn("Can't yet decrypt EACHPAI timed GPS", 1);
|
1885
1921
|
# (see https://exiftool.org/forum/index.php?topic=5095.msg61266#msg61266)
|
1886
|
-
|
1887
|
-
return 1;
|
1922
|
+
$done = 1;
|
1888
1923
|
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1924
|
+
# my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
|
1925
|
+
# # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
|
1926
|
+
# substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
|
1927
|
+
# # (other values are currently unknown)
|
1893
1928
|
|
1894
1929
|
} elsif ($$dataPt =~ /^.{64}A([NS])([EW])\0/s) {
|
1895
1930
|
|
@@ -1905,17 +1940,17 @@ sub ProcessFreeGPS($$$)
|
|
1905
1940
|
# 0070: 05 00 00 00 7f 00 00 00 07 01 00 00 00 00 00 00 [................]
|
1906
1941
|
($latRef, $lonRef) = ($1, $2);
|
1907
1942
|
($yr,$mon,$day,$hr,$min,$sec,@acc) = unpack('x68V6x20V3', $$dataPt);
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1943
|
+
if ($mon>=1 and $mon<=12 and $day>=1 and $day<=31) {
|
1944
|
+
# (not sure about acc scaling)
|
1945
|
+
@acc = map { SignedInt32 / 1000 } @acc;
|
1946
|
+
$lon = GetFloat($dataPt, 0x5c);
|
1947
|
+
$lat = GetFloat($dataPt, 0x60);
|
1948
|
+
$spd = GetFloat($dataPt, 0x64) * $knotsToKph;
|
1949
|
+
$trk = GetFloat($dataPt, 0x68);
|
1950
|
+
$alt = GetFloat($dataPt, 0x6c);
|
1951
|
+
} else {
|
1952
|
+
$done = 1;
|
1911
1953
|
}
|
1912
|
-
# (not sure about acc scaling)
|
1913
|
-
@acc = map { SignedInt32 / 1000 } @acc;
|
1914
|
-
$lon = GetFloat($dataPt, 0x5c);
|
1915
|
-
$lat = GetFloat($dataPt, 0x60);
|
1916
|
-
$spd = GetFloat($dataPt, 0x64) * $knotsToKph;
|
1917
|
-
$trk = GetFloat($dataPt, 0x68);
|
1918
|
-
$alt = GetFloat($dataPt, 0x6c);
|
1919
1954
|
|
1920
1955
|
} elsif (substr($$dataPt,0x45,3) eq 'ATC') {
|
1921
1956
|
|
@@ -1960,7 +1995,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
1960
1995
|
my $i;
|
1961
1996
|
for ($i=0; $i<@dateMax; ++$i) {
|
1962
1997
|
next if $now[$i] <= $dateMax[$i];
|
1963
|
-
$et->
|
1998
|
+
$et->Warn('Invalid GPS date/time');
|
1964
1999
|
next ATCRec; # ignore this record
|
1965
2000
|
}
|
1966
2001
|
# look for next ATC record in temporal sequence
|
@@ -2027,8 +2062,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2027
2062
|
}
|
2028
2063
|
# save position of most recent record (needed when parsing the next freeGPS block)
|
2029
2064
|
$$et{FreeGPS2}{RecentRecPos} = $lastRecPos;
|
2030
|
-
|
2031
|
-
return 1;
|
2065
|
+
$done = 1;
|
2032
2066
|
|
2033
2067
|
} elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s and $dirLen >= 0x88) {
|
2034
2068
|
|
@@ -2065,6 +2099,11 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2065
2099
|
|
2066
2100
|
$debug and $et->FoundTag(GPSType => 13);
|
2067
2101
|
# INNOVV MP4 video (same format as INNOVV TS)
|
2102
|
+
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
2103
|
+
# 0010: 41 4e 45 00 e4 56 96 45 86 b1 ca 44 5c 8f e2 40 [ANE..V.E...D\..@]
|
2104
|
+
# 0020: 33 33 58 43 c3 00 00 00 30 00 00 00 a0 fe ff ff [33XC....0.......]
|
2105
|
+
# 0030: 41 4e 45 00 e3 56 96 45 82 b1 ca 44 5c 8f fa 40 [ANE..V.E...D\..@]
|
2106
|
+
# 0040: c3 75 56 43 8c ff ff ff 8c 00 00 00 c3 fd ff ff [.uVC............]
|
2068
2107
|
while ($$dataPt =~ /(A[NS][EW]\0.{28})/sg) {
|
2069
2108
|
my $dat = $1;
|
2070
2109
|
$lat = abs(GetFloat(\$dat, 4)); # (abs just to be safe)
|
@@ -2080,10 +2119,9 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2080
2119
|
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
2081
2120
|
$et->HandleTag($tagTbl, Accelerometer => "@acc");
|
2082
2121
|
}
|
2083
|
-
|
2084
|
-
return 1;
|
2122
|
+
$done = 1;
|
2085
2123
|
|
2086
|
-
} elsif ($$dataPt =~ /^.{
|
2124
|
+
} elsif ($$dataPt =~ /^.{20}[\0-\x18][\0-\x3b]{2}[\0-\x09]A([NS])([EW])/s) {
|
2087
2125
|
|
2088
2126
|
$debug and $et->FoundTag(GPSType => 14);
|
2089
2127
|
# XBHT motorcycle dashcam Model XB702
|
@@ -2092,12 +2130,11 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2092
2130
|
# 0020: 44 3d c5 02 48 6d ff 07 df 03 00 00 6b 00 00 00 [D=..Hm......k...]
|
2093
2131
|
# 0030: 00 00 00 00 00 17 05 11 0d 25 18 01 41 4e 45 64 [.........%..ANEd]
|
2094
2132
|
# 0040: 8b 3f 00 00 30 3d c5 02 50 6d ff 07 df 03 00 00 [.?..0=..Pm......]
|
2095
|
-
while ($$dataPt =~ /(.{
|
2133
|
+
while ($$dataPt =~ /(.{7}[\0-\x09]A[NS][EW].{25})/sg) {
|
2096
2134
|
my $dat = $1;
|
2097
|
-
|
2135
|
+
($yr,$mon,$day,$hr,$min,$sec,$ss,$latRef,$lonRef,$lat,$lon,$spd) =
|
2098
2136
|
unpack('xC7xCCx5VVx4v', $dat);
|
2099
2137
|
$yr += 2000; $lat /= 1e4; $lon /= 1e4;
|
2100
|
-
$ss = 0 if $ss > 9; # (just in case)
|
2101
2138
|
ConvertLatLon($lat, $lon);
|
2102
2139
|
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
2103
2140
|
my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d.%d',$yr,$mon,$day,$hr,$min,$sec,$ss);
|
@@ -2106,8 +2143,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2106
2143
|
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
|
2107
2144
|
$et->HandleTag($tagTbl, GPSSpeed => $spd);
|
2108
2145
|
}
|
2109
|
-
|
2110
|
-
return 1;
|
2146
|
+
$done = 1;
|
2111
2147
|
|
2112
2148
|
} elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
|
2113
2149
|
|
@@ -2231,7 +2267,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2231
2267
|
}
|
2232
2268
|
|
2233
2269
|
} elsif ($$dataPt =~ m/^.{30}A.{20}VV/) {
|
2234
|
-
|
2270
|
+
|
2235
2271
|
$debug and $et->FoundTag(GPSType => 19);
|
2236
2272
|
# 70mai A810 dashcam (note: no timestamps in the samples I have)
|
2237
2273
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 ed 01 00 00 [..@.freeGPS ....]
|
@@ -2295,14 +2331,14 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2295
2331
|
$et->HandleTag($tagTbl, GPSTrack => $trk);
|
2296
2332
|
last if $pos += 0x20 > length($$dataPt) - 0x1e;
|
2297
2333
|
}
|
2298
|
-
|
2299
|
-
return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
|
2334
|
+
$done = 1;
|
2300
2335
|
}
|
2336
|
+
SetByteOrder($oldOrder);
|
2337
|
+
return $$et{DOC_NUM} ? 1 : 0 if $done;
|
2338
|
+
return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
|
2301
2339
|
#
|
2302
2340
|
# save tag values extracted by above code
|
2303
2341
|
#
|
2304
|
-
SetByteOrder($oldOrder);
|
2305
|
-
return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
|
2306
2342
|
FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
|
2307
2343
|
$sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
|
2308
2344
|
if (defined $yr) {
|
@@ -2442,8 +2478,8 @@ sub Process_tx3g($$$)
|
|
2442
2478
|
if ($text =~ /^HOME\(/) {
|
2443
2479
|
# --- sample text from Autel Evo II drone ---
|
2444
2480
|
# HOME(W: 109.318642, N: 40.769371) 2023-09-12 10:28:07
|
2445
|
-
# GPS(W: 109.339287, N: 40.768574, 2371.76m)
|
2446
|
-
# HDR ISO:100 SHUTTER:1000 EV:-0.7 F-NUM:1.8
|
2481
|
+
# GPS(W: 109.339287, N: 40.768574, 2371.76m)
|
2482
|
+
# HDR ISO:100 SHUTTER:1000 EV:-0.7 F-NUM:1.8
|
2447
2483
|
# F.PRY (1.0\xc2\xb0, -3.7\xc2\xb0, -59.0\xc2\xb0), G.PRY (-51.1\xc2\xb0, 0.0\xc2\xb0, -58.9\xc2\xb0)
|
2448
2484
|
my $line;
|
2449
2485
|
foreach $line (split /\x0a/, $text) {
|
@@ -2520,7 +2556,7 @@ sub Process_mebx($$$)
|
|
2520
2556
|
Size => $len - 8,
|
2521
2557
|
);
|
2522
2558
|
} else {
|
2523
|
-
$et->
|
2559
|
+
$et->Warn('No key information for mebx ID ' . PrintableTagID($id,1));
|
2524
2560
|
}
|
2525
2561
|
}
|
2526
2562
|
return 1;
|
@@ -2643,7 +2679,7 @@ sub Process_gdat($$$)
|
|
2643
2679
|
{
|
2644
2680
|
my ($et, $dirInfo, $tagTbl) = @_;
|
2645
2681
|
unless ($$et{OPTIONS}{ExtractEmbedded}) {
|
2646
|
-
$et->
|
2682
|
+
$et->Warn('Use the ExtractEmbedded option to extract timed GPSData',3);
|
2647
2683
|
return 0;
|
2648
2684
|
}
|
2649
2685
|
my $dataPt = $$dirInfo{DataPt};
|
@@ -2694,13 +2730,220 @@ sub Process_nbmt($$$)
|
|
2694
2730
|
delete $$et{NoMoreTextDecoding};
|
2695
2731
|
delete $$et{DOC_NUM};
|
2696
2732
|
} else {
|
2697
|
-
$et->
|
2733
|
+
$et->Warn('Use the ExtractEmbedded option to extract timed GPSData',3);
|
2734
|
+
}
|
2735
|
+
return 1;
|
2736
|
+
}
|
2737
|
+
|
2738
|
+
#------------------------------------------------------------------------------
|
2739
|
+
# Un-do LIGOGPS fuzzing
|
2740
|
+
# Inputs: 0) fuzzed latitude, 1) fuzzed longitude, 2) scale factor
|
2741
|
+
# Returns: 0) latitude, 1) longitude
|
2742
|
+
sub UnfuzzLigoGPS($$$)
|
2743
|
+
{
|
2744
|
+
my ($lat, $lon, $scl) = @_;
|
2745
|
+
my $lat2 = int($lat / 10) * 10;
|
2746
|
+
my $lon2 = int($lon / 10) * 10;
|
2747
|
+
return($lat2 + ($lon - $lon2) * $scl, $lon2 + ($lat - $lat2) * $scl);
|
2748
|
+
}
|
2749
|
+
|
2750
|
+
#------------------------------------------------------------------------------
|
2751
|
+
# Decrypt LIGOGPSINFO record (starting with "####")
|
2752
|
+
# Inputs: 0) encrypted GPS record incuding 8-byte header
|
2753
|
+
# Returns: decrypted record including 4-byte uint32 header, or undef on error
|
2754
|
+
sub DecryptLigoGPS($)
|
2755
|
+
{
|
2756
|
+
my $str = shift;
|
2757
|
+
my $num = unpack('x4V',$str);
|
2758
|
+
return undef if $num < 4;
|
2759
|
+
$num = 0x84 if $num > 0x84; # (be safe)
|
2760
|
+
my @in = unpack("x8C$num",$str);
|
2761
|
+
my @out;
|
2762
|
+
while (@in) {
|
2763
|
+
my $b = shift @in;
|
2764
|
+
my $steeringBits = $b & 0xe0;
|
2765
|
+
if ($steeringBits >= 0xc0) {
|
2766
|
+
return undef if @in < 4;
|
2767
|
+
push @out, (shift(@in) | $b & 0x01) ^ 0x20,
|
2768
|
+
(shift(@in) | $b & 0x02) ^ 0x20,
|
2769
|
+
(shift(@in) | $b & 0x0c) ^ 0x20,
|
2770
|
+
shift(@in) ^ 0x20 | $b & 0x30;
|
2771
|
+
} elsif ($steeringBits >= 0x40) {
|
2772
|
+
return undef if @in < 3;
|
2773
|
+
if ($steeringBits == 0x40) {
|
2774
|
+
push @out, 0x20,
|
2775
|
+
(shift(@in) | $b & 0x01) ^ 0x20,
|
2776
|
+
(shift(@in) | $b & 0x06) ^ 0x20,
|
2777
|
+
(shift(@in) | $b & 0x18) ^ 0x20;
|
2778
|
+
} elsif ($steeringBits == 0x60) {
|
2779
|
+
push @out, (shift(@in) | $b & 0x03) ^ 0x20,
|
2780
|
+
0x20,
|
2781
|
+
(shift(@in) | $b & 0x04) ^ 0x20,
|
2782
|
+
(shift(@in) | $b & 0x18) ^ 0x20;
|
2783
|
+
} elsif ($steeringBits == 0x80) {
|
2784
|
+
push @out, (shift(@in) | $b & 0x03) ^ 0x20,
|
2785
|
+
(shift(@in) | $b & 0x0c) ^ 0x20,
|
2786
|
+
0x20,
|
2787
|
+
(shift(@in) | $b & 0x10) ^ 0x20;
|
2788
|
+
} else {
|
2789
|
+
push @out, (shift(@in) | $b & 0x01) ^ 0x20,
|
2790
|
+
(shift(@in) | $b & 0x06) ^ 0x20,
|
2791
|
+
(shift(@in) | $b & 0x18) ^ 0x20,
|
2792
|
+
0x20;
|
2793
|
+
}
|
2794
|
+
} elsif ($steeringBits == 0x00) {
|
2795
|
+
return undef if @in < 1;
|
2796
|
+
push @out, shift(@in) | $b & 0x13;
|
2797
|
+
} else {
|
2798
|
+
return undef; # (shouldn't happen)
|
2799
|
+
}
|
2800
|
+
}
|
2801
|
+
return pack 'C*', @out;
|
2802
|
+
}
|
2803
|
+
|
2804
|
+
#------------------------------------------------------------------------------
|
2805
|
+
# Decipher and parse LIGOGPSINFO record (starting with "####")
|
2806
|
+
# Inputs: 0) ExifTool ref, 1) enciphered string, 2) tag table ref
|
2807
|
+
# Returns: true if this looked like an enciphered string
|
2808
|
+
# Notes: handles contained tags, but may defer handling until full cipher is known
|
2809
|
+
sub DecipherLigoGPS($$$)
|
2810
|
+
{
|
2811
|
+
my ($et, $str, $tagTbl) = @_;
|
2812
|
+
|
2813
|
+
# (enciphered characters must be in the range 0x30-0x5f ('0' - '_'))
|
2814
|
+
$str =~ m[^####.{4}([0-_])[0-_]{3}/[0-_]{2}/[0-_]{2} ..([0-_])..([0-_]).([0-_]) ]s or return undef;
|
2815
|
+
return undef unless $2 eq $3; # (colons in time string must be the same)
|
2816
|
+
|
2817
|
+
my $cipherInfo = $$et{LigoCipher};
|
2818
|
+
$cipherInfo or $cipherInfo = $$et{LigoCipher} = { cache => [ ], secs => [ ], two => -1 };
|
2819
|
+
my $decipher = $$cipherInfo{decipher};
|
2820
|
+
my $cache = $$cipherInfo{cache};
|
2821
|
+
|
2822
|
+
# determine the cipher code table based on the advancing 1's digit of seconds
|
2823
|
+
unless ($decipher) {
|
2824
|
+
push @$cache, $str; # cache records until we can decipher them
|
2825
|
+
my ($millennium, $colon) = ($1, $2);
|
2826
|
+
# determine the Caesar cipher lookup table
|
2827
|
+
# (only characters in range 0x30-0x5f are encrypted)
|
2828
|
+
my $secs = $$cipherInfo{secs};
|
2829
|
+
push @$secs, $4 unless @$secs and $${secs}[-1] eq $4;
|
2830
|
+
$$cipherInfo{two} = $#$secs if $4 eq $millennium; # save index of enciphered '2'
|
2831
|
+
return 1 if @$secs < 10; # must cache the data until we know all 10 digits
|
2832
|
+
my $two = $$cipherInfo{two}; # (index of '2' in the array)
|
2833
|
+
my %decipher = ( $colon => ':' ); # (':' is the time separator)
|
2834
|
+
foreach (0..9) {
|
2835
|
+
my $ch = $$secs[($_ + $two - 2 + 10) % 10];
|
2836
|
+
if ($two < 0 or defined $decipher{$ch}) { # (must be a unique code for each digit)
|
2837
|
+
@$cipherInfo{'secs','two'} = ([ ], -1); # reset and try again
|
2838
|
+
$et->Warn('Hiccup while deciphering LIGOGPSINFO');
|
2839
|
+
return 1;
|
2840
|
+
}
|
2841
|
+
$decipher{$ch} = chr($_ + 0x30);
|
2842
|
+
}
|
2843
|
+
# also know the lat/lon quadrant from the signs of the coordinates
|
2844
|
+
if ($str =~ / ([0-_])$colon(-?).*? ([0-_])$colon(-?)/) {
|
2845
|
+
@decipher{$1,$3} = ($2 ? 'S' : 'N', $4 ? 'W' : 'E');
|
2846
|
+
}
|
2847
|
+
# fill in unknown entries with '?' (only chars 0x30-0x5f are enciphered)
|
2848
|
+
defined $decipher{$_} or $decipher{$_} = '?' foreach map(chr, 0x30..0x5f);
|
2849
|
+
$decipher = $$cipherInfo{decipher} = \%decipher;
|
2850
|
+
$str = shift @$cache; # start deciphering at oldest cache entry
|
2851
|
+
}
|
2852
|
+
|
2853
|
+
# apply reverse Caesar cipher and extract GPS information
|
2854
|
+
do {
|
2855
|
+
my $pre = substr($str, 4, 4); # save second 4 bytes of header
|
2856
|
+
($str = substr($str,8)) =~ s/\0+$//; # remove 8-byte header and null padding
|
2857
|
+
$str =~ s/([0-_])/$$decipher{$1}/g; # decipher
|
2858
|
+
if ($$et{OPTIONS}{Verbose} > 1) {
|
2859
|
+
$et->VPrint(1, "$$et{INDENT}\(Deciphered: ".unpack('H8',$pre)." $str)\n");
|
2860
|
+
}
|
2861
|
+
# add back leading 4 bytes (int16u counter plus 2 unknown bytes), and parse
|
2862
|
+
# (not fuzzed in my only sample when found in standard 'skip' atom)
|
2863
|
+
ParseLigoGPS($et, "$pre$str", $tagTbl, $$et{LigoType} eq 'LigoGPSInfo');
|
2864
|
+
} while $str = shift @$cache;
|
2865
|
+
|
2866
|
+
return 1;
|
2867
|
+
}
|
2868
|
+
|
2869
|
+
#------------------------------------------------------------------------------
|
2870
|
+
# Parse decrypted/deciphered (but not defuzzed) LIGOGPSINFO record
|
2871
|
+
# (record starts with 4-byte int32u counter followed by date/time, etc)
|
2872
|
+
# Inputs: 0) ExifTool ref, 1) GPS string, 2) tag table ref, 3) not fuzzed
|
2873
|
+
# Returns: nothing
|
2874
|
+
sub ParseLigoGPS($$$;$)
|
2875
|
+
{
|
2876
|
+
my ($et, $str, $tagTbl, $noFuzz) = @_;
|
2877
|
+
|
2878
|
+
# example string input
|
2879
|
+
# "....2022/09/19 12:45:24 N:31.285065 W:124.759483 46.93 km/h x:-0.000 y:-0.000 z:-0.000"
|
2880
|
+
unless ($str=~ /^.{4}(\S+ \S+)\s+([NS?]):(-?)([.\d]+)\s+([EW?]):(-?)([\.\d]+)\s+([.\d]+)/s) {
|
2881
|
+
$et->Warn('LIGOGPSINFO format error');
|
2882
|
+
return;
|
2883
|
+
}
|
2884
|
+
my ($time,$latRef,$latNeg,$lat,$lonRef,$lonNeg,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8);
|
2885
|
+
my %gpsScl = ( 1 => 1.524855137, 2 => 1.456027985, 3 => 1.15368 );
|
2886
|
+
my $spdScl = $noFuzz ? $knotsToKph : 1.85407333;
|
2887
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
2888
|
+
$time =~ tr(/)(:);
|
2889
|
+
# convert from DDMM.MMMMMM to DD.DDDDDD if necessary
|
2890
|
+
# (speed wasn't scaled in my 1 sample with this format)
|
2891
|
+
$lat =~ /^\d{3}/ and ConvertLatLon($lat,$lon), $spdScl = 1;
|
2892
|
+
unless ($noFuzz) { # unfuzz the coordinates if necessary
|
2893
|
+
my $scl = $$et{OPTIONS}{LigoGPSScale} || $$et{LigoGPSScale} || 1;
|
2894
|
+
$scl = $gpsScl{$scl} if $gpsScl{$scl};
|
2895
|
+
($lat, $lon) = UnfuzzLigoGPS($lat, $lon, $scl);
|
2896
|
+
}
|
2897
|
+
# a final sanity check
|
2898
|
+
($lat > 90 or $lon > 180) and $et->Warn('LIGOGPSINFO coordinates out of range'), return;
|
2899
|
+
$$et{SET_GROUP1} = 'LIGO';
|
2900
|
+
$et->HandleTag($tagTbl, 'GPSDateTime', $time);
|
2901
|
+
# (ignore N/S/E/W if coordinate is signed)
|
2902
|
+
$et->HandleTag($tagTbl, 'GPSLatitude', $lat * (($latNeg or $latRef eq 'S') ? -1 : 1));
|
2903
|
+
$et->HandleTag($tagTbl, 'GPSLongitude', $lon * (($lonNeg or $lonRef eq 'W') ? -1 : 1));
|
2904
|
+
$et->HandleTag($tagTbl, 'GPSSpeed', $spd * $spdScl);
|
2905
|
+
$et->HandleTag($tagTbl, 'GPSTrack', $1) if $str =~ /\bA:(\S+)/;
|
2906
|
+
# (have a sample where tab is used to separate acc components)
|
2907
|
+
$et->HandleTag($tagTbl, 'Accelerometer',"$1 $2 $3") if $str =~ /x:(\S+)\sy:(\S+)\sz:(\S+)/;
|
2908
|
+
$et->HandleTag($tagTbl, 'M', $1) if $str =~ /\bM:(\S+)/;
|
2909
|
+
$et->HandleTag($tagTbl, 'H', $1) if $str =~ /\bH:(\S+)/;
|
2910
|
+
delete $$et{SET_GROUP1};
|
2911
|
+
}
|
2912
|
+
|
2913
|
+
#------------------------------------------------------------------------------
|
2914
|
+
# Process LIGOGPSINFO data (non-JSON format)
|
2915
|
+
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
2916
|
+
# 3) 1=LIGOGPS lat/lon/spd weren't fuzzed
|
2917
|
+
# Returns: 1 on success
|
2918
|
+
sub ProcessLigoGPS($$$;$)
|
2919
|
+
{
|
2920
|
+
my ($et, $dirInfo, $tagTbl, $noFuzz) = @_;
|
2921
|
+
my $dataPt = $$dirInfo{DataPt};
|
2922
|
+
my $pos = ($$dirInfo{DirStart} || 0) + 0x14;
|
2923
|
+
my $cipherInfo = $$et{LigoCipher};
|
2924
|
+
return undef if $pos > length $$dataPt;
|
2925
|
+
$$et{LigoType} = $$dirInfo{DirName} || 'LigoGPS';
|
2926
|
+
push @{$$et{PATH}}, $$et{LigoType} unless $$dirInfo{DirID};
|
2927
|
+
# not fuzzed if header is "LIGOGPSINFO\0\0\0\0\x01" (BlueSkySeaDV688)
|
2928
|
+
$noFuzz = 1 if substr($$dataPt, $pos-8, 4) eq "\0\0\0\x01";
|
2929
|
+
$et->VerboseDir($$et{LigoType});
|
2930
|
+
for (; $pos + 0x84 <= length($$dataPt); $pos+=0x84) {
|
2931
|
+
my $dat = substr($$dataPt, $pos, 0x84);
|
2932
|
+
$dat =~ /^####/ or next; # (have seen blank records filled with zeros, so keep trying)
|
2933
|
+
# decipher if we already know the encryption
|
2934
|
+
$cipherInfo and $$cipherInfo{decipher} and DecipherLigoGPS($et, $dat, $tagTbl) and next;
|
2935
|
+
my $str = DecryptLigoGPS($dat);
|
2936
|
+
defined $str or DecipherLigoGPS($et, $dat, $tagTbl), next; # try to decipher
|
2937
|
+
$et->VPrint(1, "$$et{INDENT}\(Decrypted: ",unpack('V',$str),' ',substr($str,4),")\n") if $$et{OPTIONS}{Verbose} > 1;
|
2938
|
+
ParseLigoGPS($et, $str, $tagTbl, $noFuzz);
|
2698
2939
|
}
|
2940
|
+
pop @{$$et{PATH}} unless $$dirInfo{DirID};
|
2941
|
+
delete $$et{DOC_NUM};
|
2699
2942
|
return 1;
|
2700
2943
|
}
|
2701
2944
|
|
2702
2945
|
#------------------------------------------------------------------------------
|
2703
|
-
# Process
|
2946
|
+
# Process LIGOGPSINFO JSON-format GPS (Yada RoadCam Pro 4K BT58189)
|
2704
2947
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
2705
2948
|
# Returns: 1 on success
|
2706
2949
|
# Sample data (chained 512-byte records starting like this):
|
@@ -2711,13 +2954,14 @@ sub Process_nbmt($$$)
|
|
2711
2954
|
# 0040: 22 3a 20 22 32 30 32 33 22 2c 20 22 4d 6f 6e 74 [": "2023", "Mont]
|
2712
2955
|
# 0050: 68 22 3a 20 22 31 32 22 2c 20 22 44 61 79 22 3a [h": "12", "Day":]
|
2713
2956
|
# 0060: 20 22 32 38 22 2c 20 22 73 74 61 74 75 73 22 3a [ "28", "status":]
|
2714
|
-
sub
|
2957
|
+
sub ProcessLigoJSON($$$)
|
2715
2958
|
{
|
2716
2959
|
my ($et, $dirInfo, $tagTbl) = @_;
|
2717
2960
|
my $dataPt = $$dirInfo{DataPt};
|
2718
2961
|
my $dirLen = $$dirInfo{DirLen};
|
2719
2962
|
require Image::ExifTool::Import;
|
2720
2963
|
$et->VerboseDir('LIGO_JSON', undef, length($$dataPt));
|
2964
|
+
$$et{SET_GROUP1} = 'LIGO';
|
2721
2965
|
while ($$dataPt =~ /LIGOGPSINFO (\{.*?\})/g) {
|
2722
2966
|
my $json = $1;
|
2723
2967
|
my %dbase;
|
@@ -2767,11 +3011,12 @@ sub ProcessLIGO_JSON($$$)
|
|
2767
3011
|
$et->HandleTag($tagTbl, GPSLongitude2 => $lon);
|
2768
3012
|
}
|
2769
3013
|
unless ($et->Options('ExtractEmbedded')) {
|
2770
|
-
$et->
|
3014
|
+
$et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
|
2771
3015
|
last;
|
2772
3016
|
}
|
2773
3017
|
}
|
2774
3018
|
delete $$et{DOC_NUM};
|
3019
|
+
delete $$et{SET_GROUP1};
|
2775
3020
|
return 1;
|
2776
3021
|
}
|
2777
3022
|
|
@@ -2814,7 +3059,7 @@ sub ProcessKenwood($$$)
|
|
2814
3059
|
}
|
2815
3060
|
$et->HandleTag($tagTbl, Accelerometer => "@acc") if @acc;
|
2816
3061
|
unless ($et->Options('ExtractEmbedded')) {
|
2817
|
-
$et->
|
3062
|
+
$et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
|
2818
3063
|
last;
|
2819
3064
|
}
|
2820
3065
|
}
|
@@ -2937,7 +3182,7 @@ sub ProcessKenwoodTrailer($$$)
|
|
2937
3182
|
$raf->Read($buff, 14) and $buff eq 'CCCCCCCCCCCCCC' or return 0;
|
2938
3183
|
$et->VerboseDir('Kenwood trailer', undef, undef);
|
2939
3184
|
unless ($$et{OPTIONS}{ExtractEmbedded}) {
|
2940
|
-
$et->
|
3185
|
+
$et->Warn('Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer',3);
|
2941
3186
|
return 1;
|
2942
3187
|
}
|
2943
3188
|
while ($raf->Read($buff, 121) and $buff =~ /^GPSDATA--(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/) {
|
@@ -3160,7 +3405,7 @@ sub ProcessTTAD($$$)
|
|
3160
3405
|
$et->HandleTag($tagTbl, "Unknown0$type" => "@a");
|
3161
3406
|
}
|
3162
3407
|
} else {
|
3163
|
-
$et->
|
3408
|
+
$et->Warn("Unknown TTAD record type $type",1);
|
3164
3409
|
}
|
3165
3410
|
# without -ee, stop after we find types 0,3,5 (ie. bitmask 0x29)
|
3166
3411
|
$eeOpt or ($found & 0x29) != 0x29 or EEWarn($et), last;
|
@@ -3212,7 +3457,7 @@ sub ProcessInsta360($;$)
|
|
3212
3457
|
}
|
3213
3458
|
unless ($et->Options('ExtractEmbedded')) {
|
3214
3459
|
# can arrive here when reading Insta360 trailer on JPEG image (INSP file)
|
3215
|
-
$et->
|
3460
|
+
$et->Warn('Use ExtractEmbedded option to extract timed metadata from Insta360 trailer',3);
|
3216
3461
|
return 1;
|
3217
3462
|
}
|
3218
3463
|
|
@@ -3409,8 +3654,8 @@ sub ProcessCAMM($$$)
|
|
3409
3654
|
my $rtnVal = 0;
|
3410
3655
|
while ($pos + 4 < $end) {
|
3411
3656
|
my $type = Get16u($dataPt, $pos + 2);
|
3412
|
-
my $size = $size{$type} or $et->
|
3413
|
-
$pos + $size > $end and $et->
|
3657
|
+
my $size = $size{$type} or $et->Warn("Unknown camm record type $type"), last;
|
3658
|
+
$pos + $size > $end and $et->Warn("Truncated camm record $type"), last;
|
3414
3659
|
my $tagTbl = GetTagTable("Image::ExifTool::QuickTime::camm$type");
|
3415
3660
|
$$dirInfo{DirStart} = $pos;
|
3416
3661
|
$$dirInfo{DirLen} = $size;
|