exiftool_vendored 11.71.0 → 11.75.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of exiftool_vendored might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/Changes +39 -0
- data/bin/MANIFEST +17 -4
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +23 -26
- data/bin/config_files/convert_regions.config +139 -5
- data/bin/exiftool +21 -21
- data/bin/fmt_files/gpx.fmt +2 -1
- data/bin/fmt_files/gpx_wpt.fmt +2 -1
- data/bin/fmt_files/kml.fmt +2 -2
- data/bin/fmt_files/kml_track.fmt +46 -0
- data/bin/lib/Image/ExifTool.pm +42 -33
- data/bin/lib/Image/ExifTool.pod +21 -21
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +2 -2
- data/bin/lib/Image/ExifTool/Canon.pm +2 -1
- data/bin/lib/Image/ExifTool/CanonCustom.pm +30 -4
- data/bin/lib/Image/ExifTool/Exif.pm +36 -63
- data/bin/lib/Image/ExifTool/FujiFilm.pm +1 -0
- data/bin/lib/Image/ExifTool/Lang/ru.pm +4233 -503
- data/bin/lib/Image/ExifTool/MakerNotes.pm +6 -12
- data/bin/lib/Image/ExifTool/Nikon.pm +2 -1
- data/bin/lib/Image/ExifTool/Olympus.pm +2 -1
- data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
- data/bin/lib/Image/ExifTool/Parrot.pm +751 -0
- data/bin/lib/Image/ExifTool/Photoshop.pm +12 -11
- data/bin/lib/Image/ExifTool/QuickTime.pm +18 -5
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +316 -44
- data/bin/lib/Image/ExifTool/Shortcuts.pm +1 -2
- data/bin/lib/Image/ExifTool/Sigma.pm +72 -55
- data/bin/lib/Image/ExifTool/TagLookup.pm +73 -7
- data/bin/lib/Image/ExifTool/TagNames.pod +249 -18
- data/bin/lib/Image/ExifTool/Text.pm +160 -0
- data/bin/lib/Image/ExifTool/WriteXMP.pl +1 -2
- data/bin/lib/Image/ExifTool/Writer.pl +1 -1
- data/bin/lib/Image/ExifTool/XMP.pm +19 -26
- data/bin/lib/Image/ExifTool/XMP2.pl +46 -9
- data/bin/lib/Image/ExifTool/XMPStruct.pl +14 -4
- data/bin/perl-Image-ExifTool.spec +19 -19
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +6 -7
- data/bin/config_files/blueskysea.config +0 -101
- data/bin/config_files/dji.config +0 -131
- data/bin/config_files/mini0806.config +0 -99
- data/bin/config_files/thinkware.config +0 -144
@@ -828,7 +828,7 @@ sub ProcessDocumentData($$$)
|
|
828
828
|
my $raf = $$dirInfo{RAF};
|
829
829
|
my $dirLen = $$dirInfo{DirLen};
|
830
830
|
my $pos = 36; # length of header
|
831
|
-
my ($buff, $n);
|
831
|
+
my ($buff, $n, $err);
|
832
832
|
|
833
833
|
$et->VerboseDir('Photoshop Document Data', undef, $dirLen);
|
834
834
|
unless ($raf) {
|
@@ -849,24 +849,24 @@ sub ProcessDocumentData($$$)
|
|
849
849
|
my %dinfo = ( DataPt => \$buff );
|
850
850
|
$$et{IsPSB} = $psb; # set PSB flag (needed when handling Layers directory)
|
851
851
|
while ($pos + 12 <= $dirLen) {
|
852
|
-
$raf->Read($buff, 8) == 8 or last;
|
852
|
+
$raf->Read($buff, 8) == 8 or $err = 'Error reading document data', last;
|
853
853
|
# set byte order according to byte order of first signature
|
854
854
|
SetByteOrder($buff =~ /^(8BIM|8B64)/ ? 'MM' : 'II') if $pos == 36;
|
855
855
|
$buff = pack 'N*', unpack 'V*', $buff if GetByteOrder() eq 'II';
|
856
856
|
my $sig = substr($buff, 0, 4);
|
857
|
-
|
857
|
+
$sig eq '8BIM' or $sig eq '8B64' or $err = 'Bad photoshop resource', last; # verify signature
|
858
858
|
my $tag = substr($buff, 4, 4);
|
859
859
|
if ($psb and $tag =~ /^(LMsk|Lr16|Lr32|Layr|Mt16|Mt32|Mtrn|Alph|FMsk|lnk2|FEid|FXid|PxSD)$/) {
|
860
|
-
|
861
|
-
$raf->Read($buff, 8) == 8 or last;
|
860
|
+
$pos + 16 > $dirLen and $err = 'Short PSB resource', last;
|
861
|
+
$raf->Read($buff, 8) == 8 or $err = 'Error reading PSB resource', last;
|
862
862
|
$n = Get64u(\$buff, 0);
|
863
863
|
$pos += 4;
|
864
864
|
} else {
|
865
|
-
$raf->Read($buff, 4) == 4 or last;
|
865
|
+
$raf->Read($buff, 4) == 4 or $err = 'Error reading PSD resource', last;
|
866
866
|
$n = Get32u(\$buff, 0);
|
867
867
|
}
|
868
868
|
$pos += 12;
|
869
|
-
|
869
|
+
$pos + $n > $dirLen and $err = 'Truncated photoshop resource', last;
|
870
870
|
my $pad = (4 - ($n & 3)) & 3; # number of padding bytes
|
871
871
|
my $tagInfo = $$tagTablePtr{$tag};
|
872
872
|
if ($tagInfo or $verbose) {
|
@@ -874,20 +874,21 @@ sub ProcessDocumentData($$$)
|
|
874
874
|
my $fpos = $raf->Tell() + $n + $pad;
|
875
875
|
my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable});
|
876
876
|
$et->ProcessDirectory({ RAF => $raf, DirLen => $n }, $subTable);
|
877
|
-
$raf->Seek($fpos, 0) or last;
|
877
|
+
$raf->Seek($fpos, 0) or $err = 'Seek error', last;
|
878
878
|
} else {
|
879
879
|
$dinfo{DataPos} = $raf->Tell();
|
880
880
|
$dinfo{Start} = 0;
|
881
881
|
$dinfo{Size} = $n;
|
882
|
-
$raf->Read($buff, $n) == $n or last;
|
882
|
+
$raf->Read($buff, $n) == $n or $err = 'Error reading photoshop resource', last;
|
883
883
|
$et->HandleTag($tagTablePtr, $tag, undef, %dinfo);
|
884
|
-
$raf->Seek($pad, 1) or last;
|
884
|
+
$raf->Seek($pad, 1) or $err = 'Seek error', last;
|
885
885
|
}
|
886
886
|
} else {
|
887
|
-
$raf->Seek($n + $pad, 1) or last;
|
887
|
+
$raf->Seek($n + $pad, 1) or $err = 'Seek error', last;
|
888
888
|
}
|
889
889
|
$pos += $n + $pad; # step to start of next structure
|
890
890
|
}
|
891
|
+
$err and $et->Warn($err);
|
891
892
|
return 1;
|
892
893
|
}
|
893
894
|
|
@@ -44,7 +44,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
44
44
|
use Image::ExifTool::Exif;
|
45
45
|
use Image::ExifTool::GPS;
|
46
46
|
|
47
|
-
$VERSION = '2.
|
47
|
+
$VERSION = '2.38';
|
48
48
|
|
49
49
|
sub ProcessMOV($$;$);
|
50
50
|
sub ProcessKeys($$$);
|
@@ -59,6 +59,7 @@ sub Process_mebx($$$);
|
|
59
59
|
sub Process_3gf($$$);
|
60
60
|
sub Process_gps0($$$);
|
61
61
|
sub Process_gsen($$$);
|
62
|
+
sub ProcessRIFFTrailer($$$);
|
62
63
|
sub ProcessTTAD($$$);
|
63
64
|
sub ProcessNMEA($$$);
|
64
65
|
sub SaveMetaKeys($$$);
|
@@ -8293,7 +8294,7 @@ sub ProcessMOV($$;$)
|
|
8293
8294
|
my $str = $$dirInfo{DirName} . ' with ' . ($raf->Tell() - $pos) . ' bytes';
|
8294
8295
|
$et->VPrint(0,"$$et{INDENT}\[Terminator found in $str remaining]");
|
8295
8296
|
} else {
|
8296
|
-
my $t = PrintableTagID($tag);
|
8297
|
+
my $t = PrintableTagID($tag,2);
|
8297
8298
|
$et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
|
8298
8299
|
}
|
8299
8300
|
last;
|
@@ -8394,9 +8395,21 @@ sub ProcessMOV($$;$)
|
|
8394
8395
|
}
|
8395
8396
|
# load values only if associated with a tag (or verbose) and not too big
|
8396
8397
|
if ($size > 0x2000000) { # start to get worried above 32 MB
|
8398
|
+
# check for RIFF trailer (written by Auto-Vox dashcam)
|
8399
|
+
if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
|
8400
|
+
$et->VPrint(0, "Found RIFF trailer");
|
8401
|
+
if ($et->Options('ExtractEmbedded')) {
|
8402
|
+
$raf->Seek(-8, 1) or last; # seek back to start of trailer
|
8403
|
+
my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
8404
|
+
ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
|
8405
|
+
} else {
|
8406
|
+
EEWarn($et);
|
8407
|
+
}
|
8408
|
+
last;
|
8409
|
+
}
|
8397
8410
|
$ignore = 1;
|
8398
8411
|
if ($tagInfo and not $$tagInfo{Unknown} and not $eeTag) {
|
8399
|
-
my $t = PrintableTagID($tag);
|
8412
|
+
my $t = PrintableTagID($tag,2);
|
8400
8413
|
if ($size > 0x8000000) {
|
8401
8414
|
$et->Warn("Skipping '${t}' atom > 128 MB", 1);
|
8402
8415
|
} else {
|
@@ -8444,7 +8457,7 @@ ItemID: foreach $id (keys %$items) {
|
|
8444
8457
|
my $val;
|
8445
8458
|
my $missing = $size - $raf->Read($val, $size);
|
8446
8459
|
if ($missing) {
|
8447
|
-
my $t = PrintableTagID($tag);
|
8460
|
+
my $t = PrintableTagID($tag,2);
|
8448
8461
|
$et->Warn("Truncated '${t}' data (missing $missing bytes)");
|
8449
8462
|
last;
|
8450
8463
|
}
|
@@ -8721,7 +8734,7 @@ ItemID: foreach $id (keys %$items) {
|
|
8721
8734
|
Extra => sprintf(' at offset 0x%.4x', $raf->Tell()),
|
8722
8735
|
) if $verbose;
|
8723
8736
|
if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
|
8724
|
-
my $t = PrintableTagID($tag);
|
8737
|
+
my $t = PrintableTagID($tag,2);
|
8725
8738
|
$et->Warn("Truncated '${t}' data");
|
8726
8739
|
last;
|
8727
8740
|
}
|
@@ -89,20 +89,34 @@ my %insvLimit = (
|
|
89
89
|
the ExtractEmbedded option is used.
|
90
90
|
},
|
91
91
|
VARS => { NO_ID => 1 },
|
92
|
-
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")' },
|
92
|
+
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
93
93
|
GPSLongitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")' },
|
94
94
|
GPSAltitude => { PrintConv => '(sprintf("%.4f", $val) + 0) . " m"' }, # round to 4 decimals
|
95
95
|
GPSSpeed => { PrintConv => 'sprintf("%.4f", $val) + 0' }, # round to 4 decimals
|
96
96
|
GPSSpeedRef => { PrintConv => { K => 'km/h', M => 'mph', N => 'knots' } },
|
97
97
|
GPSTrack => { PrintConv => 'sprintf("%.4f", $val) + 0' }, # round to 4 decimals
|
98
98
|
GPSTrackRef => { PrintConv => { M => 'Magnetic North', T => 'True North' } },
|
99
|
-
GPSDateTime => {
|
99
|
+
GPSDateTime => {
|
100
|
+
Groups => { 2 => 'Time' },
|
101
|
+
Description => 'GPS Date/Time',
|
102
|
+
RawConv => '$$self{FoundGPSDateTime} = 1; $val',
|
103
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
104
|
+
},
|
100
105
|
GPSTimeStamp => { PrintConv => 'Image::ExifTool::GPS::PrintTimeStamp($val)', Groups => { 2 => 'Time' } },
|
101
106
|
GPSSatellites=> { },
|
102
107
|
GPSDOP => { Description => 'GPS Dilution Of Precision' },
|
108
|
+
Distance => { PrintConv => '"$val m"' },
|
109
|
+
VerticalSpeed=> { PrintConv => '"$val m/s"' },
|
110
|
+
FNumber => { PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)', Groups => { 2 => 'Camera' } },
|
111
|
+
ExposureTime => { PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)', Groups => { 2 => 'Camera' } },
|
112
|
+
ExposureCompensation => { PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)', Groups => { 2 => 'Camera' } },
|
113
|
+
ISO => { Groups => { 2 => 'Camera' } },
|
103
114
|
CameraDateTime=>{ PrintConv => '$self->ConvertDateTime($val)', Groups => { 2 => 'Time' } },
|
104
|
-
Accelerometer=> { Notes => '
|
115
|
+
Accelerometer=> { Notes => '3-axis acceleration in units of g' },
|
116
|
+
AccelerometerData => { },
|
105
117
|
AngularVelocity => { },
|
118
|
+
GSensor => { },
|
119
|
+
Car => { },
|
106
120
|
RawGSensor => {
|
107
121
|
# (same as GSensor, but offset by some unknown value)
|
108
122
|
ValueConv => 'my @a=split " ",$val; $_/=1000 foreach @a; "@a"',
|
@@ -221,6 +235,10 @@ my %insvLimit = (
|
|
221
235
|
ByteOrder => 'Little-Endian',
|
222
236
|
},
|
223
237
|
}],
|
238
|
+
mett => { # Parrot drones
|
239
|
+
Name => 'mett',
|
240
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Parrot::mett' },
|
241
|
+
},
|
224
242
|
JPEG => { # (in CR3 images) - [vide HandlerType with JPEG in SampleDescription, not MetaFormat]
|
225
243
|
Name => 'JpgFromRaw',
|
226
244
|
Groups => { 2 => 'Preview' },
|
@@ -319,6 +337,7 @@ my %insvLimit = (
|
|
319
337
|
4 => {
|
320
338
|
Name => 'GPSLatitude',
|
321
339
|
Format => 'double',
|
340
|
+
RawConv => '$$self{FoundGPSLatitude} = 1; $val',
|
322
341
|
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
323
342
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
324
343
|
},
|
@@ -342,8 +361,10 @@ my %insvLimit = (
|
|
342
361
|
FIRST_ENTRY => 0,
|
343
362
|
0x04 => {
|
344
363
|
Name => 'GPSDateTime',
|
364
|
+
Description => 'GPS Date/Time',
|
345
365
|
Groups => { 2 => 'Time' },
|
346
366
|
Format => 'double',
|
367
|
+
RawConv => '$$self{FoundGPSDateTime} = 1; $val',
|
347
368
|
ValueConv => q{
|
348
369
|
my $str = ConvertUnixTime($val);
|
349
370
|
my $frac = $val - int($val);
|
@@ -369,6 +390,7 @@ my %insvLimit = (
|
|
369
390
|
0x10 => {
|
370
391
|
Name => 'GPSLatitude',
|
371
392
|
Format => 'double',
|
393
|
+
RawConv => '$$self{FoundGPSLatitude} = 1; $val',
|
372
394
|
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
373
395
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
374
396
|
},
|
@@ -425,6 +447,7 @@ my %insvLimit = (
|
|
425
447
|
4 => {
|
426
448
|
Name => 'GPSLatitude',
|
427
449
|
Format => 'int32s',
|
450
|
+
RawConv => '$$self{FoundGPSLatitude} = 1; $val',
|
428
451
|
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val/1e6, 1)',
|
429
452
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
430
453
|
},
|
@@ -470,6 +493,7 @@ my %insvLimit = (
|
|
470
493
|
NOTES => 'Tags extracted from the tx3g sbtl timed metadata of Yuneec drones.',
|
471
494
|
Lat => {
|
472
495
|
Name => 'GPSLatitude',
|
496
|
+
RawConv => '$$self{FoundGPSLatitude} = 1; $val',
|
473
497
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
474
498
|
},
|
475
499
|
Lon => {
|
@@ -571,7 +595,7 @@ sub SaveMetaKeys($$$)
|
|
571
595
|
} else {
|
572
596
|
$str = '';
|
573
597
|
}
|
574
|
-
$et->VPrint(1, $$et{INDENT}."- Tag '".PrintableTagID($tag)."' ($len bytes)$str\n");
|
598
|
+
$et->VPrint(1, $$et{INDENT}."- Tag '".PrintableTagID($tag,2)."' ($len bytes)$str\n");
|
575
599
|
$et->VerboseDump(\$val);
|
576
600
|
}
|
577
601
|
}
|
@@ -599,14 +623,53 @@ sub FoundSomething($$;$$)
|
|
599
623
|
}
|
600
624
|
|
601
625
|
#------------------------------------------------------------------------------
|
602
|
-
#
|
603
|
-
# Inputs: 0) ExifTool ref, 1) tag table
|
604
|
-
|
626
|
+
# Approximate GPSDateTime value from sample time and CreateDate
|
627
|
+
# Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (ms)
|
628
|
+
# 3) true if CreateDate is at end of video
|
629
|
+
# Notes: Uses ExifTool CreateDateAtEnd as flag to subtract video duration
|
630
|
+
sub SetGPSDateTime($$$)
|
631
|
+
{
|
632
|
+
my ($et, $tagTbl, $sampleTime) = @_;
|
633
|
+
my $value = $$et{VALUE};
|
634
|
+
if (defined $sampleTime and $$value{CreateDate}) {
|
635
|
+
$sampleTime += $$value{CreateDate}; # adjust sample time to seconds since the epoch
|
636
|
+
if ($$et{CreateDateAtEnd}) { # adjust if CreateDate is at end of video
|
637
|
+
return unless $$value{TimeScale} and $$value{Duration};
|
638
|
+
$sampleTime -= $$value{Duration} / $$value{TimeScale};
|
639
|
+
$et->WarnOnce('Approximating GPSDateTime as CreateDate - Duration + SampleTime', 1);
|
640
|
+
} else {
|
641
|
+
$et->WarnOnce('Approximating GPSDateTime as CreateDate + SampleTime', 1);
|
642
|
+
}
|
643
|
+
unless ($et->Options('QuickTimeUTC')) {
|
644
|
+
my $tzOff = $$et{tzOff}; # use previously calculated offset
|
645
|
+
unless (defined $tzOff) {
|
646
|
+
# adjust to UTC, assuming time is local
|
647
|
+
my @tm = localtime $$value{CreateDate};
|
648
|
+
my @gm = gmtime $$value{CreateDate};
|
649
|
+
$tzOff = $$et{tzOff} = Image::ExifTool::GetTimeZone(\@tm, \@gm) * 60;
|
650
|
+
}
|
651
|
+
$sampleTime -= $tzOff; # shift from local time to UTC
|
652
|
+
}
|
653
|
+
$et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($sampleTime,0,3) . 'Z');
|
654
|
+
}
|
655
|
+
}
|
656
|
+
|
657
|
+
#------------------------------------------------------------------------------
|
658
|
+
# Process subtitle 'text'
|
659
|
+
# Inputs: 0) ExifTool ref, 1) tag table ref, 2) data ref, 3) optional sample time
|
660
|
+
sub Process_text($$$)
|
605
661
|
{
|
606
662
|
my ($et, $tagTbl, $buffPt) = @_;
|
663
|
+
my ($found, $val, %tags, $tag);
|
664
|
+
|
665
|
+
return if $$et{NoMoreTextDecoding};
|
666
|
+
|
607
667
|
while ($$buffPt =~ /\$(\w+)([^\$]*)/g) {
|
608
668
|
my ($tag, $dat) = ($1, $2);
|
609
669
|
if ($tag =~ /^[A-Z]{2}RMC$/ and $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+)/) {
|
670
|
+
my $year = $15 + ($15 >= 70 ? 1900 : 2000);
|
671
|
+
my $str = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $year, $14, $13, $1, $2, $3);
|
672
|
+
$et->HandleTag($tagTbl, GPSDateTime => $str);
|
610
673
|
$et->HandleTag($tagTbl, GPSLatitude => (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1));
|
611
674
|
$et->HandleTag($tagTbl, GPSLongitude => (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1));
|
612
675
|
if (length $11) {
|
@@ -617,23 +680,126 @@ sub ParseText($$$)
|
|
617
680
|
$et->HandleTag($tagTbl, GPSTrack => $11);
|
618
681
|
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
619
682
|
}
|
620
|
-
|
621
|
-
my $str = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $year, $14, $13, $1, $2, $3);
|
622
|
-
$et->HandleTag($tagTbl, GPSDateTime => $str);
|
683
|
+
$found = 1;
|
623
684
|
} elsif ($tag eq 'BEGINGSENSOR' and $dat =~ /^:([-+]\d+\.\d+):([-+]\d+\.\d+):([-+]\d+\.\d+)/) {
|
624
685
|
$et->HandleTag($tagTbl, Accelerometer => "$1 $2 $3");
|
686
|
+
$found = 1;
|
625
687
|
} elsif ($tag eq 'TIME' and $dat =~ /^:(\d+)/) {
|
626
688
|
$et->HandleTag($tagTbl, TimeCode => $1 / ($$et{MediaTS} || 1));
|
689
|
+
$found = 1;
|
627
690
|
} elsif ($tag eq 'BEGIN') {
|
628
691
|
$et->HandleTag($tagTbl, Text => $dat) if length $dat;
|
692
|
+
$found = 1;
|
629
693
|
} elsif ($tag ne 'END') {
|
630
694
|
$et->HandleTag($tagTbl, Text => "\$$tag$dat");
|
695
|
+
$found = 1;
|
696
|
+
}
|
697
|
+
}
|
698
|
+
return if $found;
|
699
|
+
|
700
|
+
# check for BlueSkySea enciphered binary GPS data
|
701
|
+
if ($$buffPt =~ /^\0\0..\xaa\xaa/s and length $$buffPt >= 282) {
|
702
|
+
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 8, 14)));
|
703
|
+
if ($val =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/) {
|
704
|
+
$tags{GPSDateTime} = "$1:$2:$3 $4:$5:$6";
|
705
|
+
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 38, 9)));
|
706
|
+
if ($val =~ /^([NS])(\d{2})(\d+$)$/) {
|
707
|
+
$tags{GPSLatitude} = ($2 + $3 / 600000) * ($1 eq 'S' ? -1 : 1);
|
708
|
+
}
|
709
|
+
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 47, 10)));
|
710
|
+
if ($val =~ /^([EW])(\d{3})(\d+$)$/) {
|
711
|
+
$tags{GPSLongitude} = ($2 + $3 / 600000) * ($1 eq 'W' ? -1 : 1);
|
712
|
+
}
|
713
|
+
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 62, 3)));
|
714
|
+
$tags{GPSSpeed} = $val + 0 if $val =~ /^\d+$/;
|
715
|
+
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 0xad, 12)));
|
716
|
+
# the first X,Y,Z accelerometer readings from the AccelerometerData
|
717
|
+
if ($val =~ /^([-+]\d{3})([-+]\d{3})([-+]\d{3})$/) {
|
718
|
+
$tags{Accelerometer} = "$1 $2 $3";
|
719
|
+
}
|
720
|
+
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 0xba, 96)));
|
721
|
+
my $order = GetByteOrder();
|
722
|
+
SetByteOrder('II');
|
723
|
+
$val = ReadValue(\$val, 0, 'float');
|
724
|
+
SetByteOrder($order);
|
725
|
+
$tags{AccelerometerData} = $val;
|
726
|
+
}
|
727
|
+
}
|
728
|
+
|
729
|
+
# check for DJI telemetry data, eg:
|
730
|
+
# "F/3.5, SS 1000, ISO 100, EV 0, GPS (8.6499, 53.1665, 18), D 24.26m,
|
731
|
+
# H 6.00m, H.S 2.10m/s, V.S 0.00m/s \n"
|
732
|
+
if (not %tags and $$buffPt =~ /GPS \(([-+]?\d*\.\d+),\s*([-+]?\d*\.\d+)/) {
|
733
|
+
$$et{CreateDateAtEnd} = 1; # set flag indicating the file creation date is at the end
|
734
|
+
$tags{GPSLatitude} = $2;
|
735
|
+
$tags{GPSLongitude} = $1;
|
736
|
+
$tags{GPSAltitude} = $1 if $$buffPt =~ /,\s*H\s+([-+]?\d+\.?\d*)m/;
|
737
|
+
if ($$buffPt =~ /,\s*H.S\s+([-+]?\d+\.?\d*)/) {
|
738
|
+
$tags{GPSSpeed} = $1 * $mpsToKph;
|
739
|
+
$tags{GPSSpeedRef} = 'K';
|
740
|
+
}
|
741
|
+
$tags{Distance} = $1 * $mpsToKph if $$buffPt =~ /,\s*D\s+(\d+\.?\d*)m/;
|
742
|
+
$tags{VerticalSpeed} = $1 if $$buffPt =~ /,\s*V.S\s+([-+]?\d+\.?\d*)/;
|
743
|
+
$tags{FNumber} = $1 if $$buffPt =~ /\bF\/(\d+\.?\d*)/;
|
744
|
+
$tags{ExposureTime} = 1 / $1 if $$buffPt =~ /\bSS\s+(\d+\.?\d*)/;
|
745
|
+
$tags{ExposureCompensation} = ($1 / ($2 || 1)) if $$buffPt =~ /\bEV\s+([-+]?\d+\.?\d*)(\/\d+)?/;
|
746
|
+
$tags{ISO} = $1 if $$buffPt =~ /\bISO\s+(\d+\.?\d*)/;
|
747
|
+
}
|
748
|
+
|
749
|
+
# check for Mini 0806 dashcam GPS, eg:
|
750
|
+
# "A,270519,201555.000,3356.8925,N,08420.2071,W,000.0,331.0M,+01.84,-09.80,-00.61;\n"
|
751
|
+
if (not %tags and $$buffPt =~ /^A,(\d{2})(\d{2})(\d{2}),(\d{2})(\d{2})(\d{2}(\.\d+)?)/) {
|
752
|
+
$tags{GPSDateTime} = "20$3:$2:$1 $4:$5:$6Z";
|
753
|
+
if ($$buffPt =~ /^A,.*?,.*?,(\d{2})(\d+\.\d+),([NS])/) {
|
754
|
+
$tags{GPSLatitude} = ($1 + $2/60) * ($3 eq 'S' ? -1 : 1);
|
755
|
+
}
|
756
|
+
if ($$buffPt =~ /^A,.*?,.*?,.*?,.*?,(\d{3})(\d+\.\d+),([EW])/) {
|
757
|
+
$tags{GPSLongitude} = ($1 + $2/60) * ($3 eq 'W' ? -1 : 1);
|
631
758
|
}
|
759
|
+
my @a = split ',', $$buffPt;
|
760
|
+
$tags{GPSAltitude} = $a[8] if $a[8] and $a[8] =~ s/M$//;
|
761
|
+
$tags{GPSSpeed} = $a[7] if $a[7] and $a[7] =~ /^\d+\.\d+$/; # (NC)
|
762
|
+
$tags{Accelerometer} = "$a[9] $a[10] $a[11]" if $a[11] and $a[11] =~ s/;\s*$//;
|
763
|
+
}
|
764
|
+
|
765
|
+
# check for Thinkware format, eg:
|
766
|
+
# "gsensori,4,512,-67,-12,100;GNRMC,161313.00,A,4529.87489,N,07337.01215,W,6.225,35.34,310819,,,A*52..;
|
767
|
+
# CAR,0,0,0,0.0,0,0,0,0,0,0,0,0"
|
768
|
+
unless (%tags) {
|
769
|
+
if ($$buffPt =~ /[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+)/ and
|
770
|
+
# do some basic sanity checks on the date
|
771
|
+
$13 <= 31 and $14 <= 12 and $15 <= 99)
|
772
|
+
{
|
773
|
+
my $year = $15 + ($15 >= 70 ? 1900 : 2000);
|
774
|
+
$tags{GPSDateTime} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $year, $14, $13, $1, $2, $3);
|
775
|
+
$tags{GPSLatitude} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
|
776
|
+
$tags{GPSLongitude} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
|
777
|
+
if (length $11) {
|
778
|
+
$tags{GPSSpeed} = $11 * $knotsToKph;
|
779
|
+
$tags{GPSSpeedRef} = 'K';
|
780
|
+
}
|
781
|
+
if (length $12) {
|
782
|
+
$tags{GPSTrack} = $12;
|
783
|
+
$tags{GPSTrackRef} = 'T';
|
784
|
+
}
|
785
|
+
}
|
786
|
+
$tags{GSensor} = $1 if $$buffPt =~ /\bgsensori,(.*?)(;|$)/;
|
787
|
+
$tags{Car} = $1 if $$buffPt =~ /\bCAR,(.*?)(;|$)/;
|
788
|
+
}
|
789
|
+
if (%tags) {
|
790
|
+
foreach $tag (sort keys %tags) {
|
791
|
+
$et->HandleTag($tagTbl, $tag => $tags{$tag});
|
792
|
+
}
|
793
|
+
$$et{UnknownTextCount} = 0;
|
794
|
+
} else {
|
795
|
+
$$et{UnknownTextCount} = ($$et{UnknownTextCount} || 0) + 1;
|
796
|
+
# give up trying to decode useful information if we haven't found anything for a while
|
797
|
+
$$et{NoMoreTextDecoding} = 1 if $$et{UnknownTextCount} > 100;
|
632
798
|
}
|
633
799
|
}
|
634
800
|
|
635
801
|
#------------------------------------------------------------------------------
|
636
|
-
#
|
802
|
+
# Extract embedded metadata from media samples
|
637
803
|
# Inputs: 0) ExifTool ref
|
638
804
|
# Notes: Also accesses ExifTool RAF*, SET_GROUP1, HandlerType, MetaFormat,
|
639
805
|
# ee*, and avcC elements (* = must exist)
|
@@ -725,6 +891,10 @@ sub ProcessSamples($)
|
|
725
891
|
# loop through all samples
|
726
892
|
for ($i=0; $i<@$start and $i<@$size; ++$i) {
|
727
893
|
|
894
|
+
# initialize our flags for setting GPSDateTime
|
895
|
+
delete $$et{FoundGPSLatitude};
|
896
|
+
delete $$et{FoundGPSDateTime};
|
897
|
+
|
728
898
|
# read the sample data
|
729
899
|
my $size = $$size[$i];
|
730
900
|
next unless $raf->Seek($$start[$i], 0) and $raf->Read($buff, $size) == $size;
|
@@ -782,14 +952,14 @@ sub ProcessSamples($)
|
|
782
952
|
$et->HandleTag($tagTbl, GPSLongitude => Get32s(\$buff, 16) * 180/0x80000000);
|
783
953
|
$et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, 8));
|
784
954
|
$et->HandleTag($tagTbl, GPSSpeedRef => 'M');
|
785
|
-
|
955
|
+
SetGPSDateTime($et, $tagTbl, $time[$i]);
|
956
|
+
next; # all done (don't store/process as text)
|
786
957
|
}
|
787
958
|
unless (defined $val) {
|
788
959
|
$et->HandleTag($tagTbl, Text => $buff); # just store any other text
|
789
|
-
next;
|
790
960
|
}
|
791
961
|
}
|
792
|
-
|
962
|
+
Process_text($et, $tagTbl, \$buff);
|
793
963
|
|
794
964
|
} elsif ($processByMetaFormat{$type}) {
|
795
965
|
|
@@ -800,6 +970,7 @@ sub ProcessSamples($)
|
|
800
970
|
$$et{ee} = $ee; # need ee information for 'keys'
|
801
971
|
$et->HandleTag($tagTbl, $metaFormat, undef,
|
802
972
|
DataPt => \$buff,
|
973
|
+
DataPos => $$start[$i],
|
803
974
|
Base => $$start[$i],
|
804
975
|
TagInfo => $tagInfo,
|
805
976
|
);
|
@@ -809,7 +980,7 @@ sub ProcessSamples($)
|
|
809
980
|
# "X0000.0000Y0000.0000Z0000.0000G0000.0000$GPRMC,000125,V,,,,,000.0,,280908,002.1,N*71~, 794021 \x0a"
|
810
981
|
FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
|
811
982
|
$et->HandleTag($tagTbl, Accelerometer => "$1 $2 $3 $4") if $buff =~ /X(.*?)Y(.*?)Z(.*?)G(.*?)\$/;
|
812
|
-
|
983
|
+
Process_text($et, $tagTbl, \$buff);
|
813
984
|
}
|
814
985
|
} elsif ($verbose) {
|
815
986
|
$et->VPrint(0, "Unknown meta format ($metaFormat)");
|
@@ -821,6 +992,7 @@ sub ProcessSamples($)
|
|
821
992
|
# decode "freeGPS " data (Novatek)
|
822
993
|
ProcessFreeGPS($et, {
|
823
994
|
DataPt => \$buff,
|
995
|
+
DataPos => $$start[$i],
|
824
996
|
SampleTime => $time[$i],
|
825
997
|
SampleDuration => $dur[$i],
|
826
998
|
}, $tagTbl) ;
|
@@ -833,11 +1005,14 @@ sub ProcessSamples($)
|
|
833
1005
|
FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
|
834
1006
|
$et->HandleTag($tagTbl, $type, undef,
|
835
1007
|
DataPt => \$buff,
|
1008
|
+
DataPos => $$start[$i],
|
836
1009
|
Base => $$start[$i],
|
837
1010
|
TagInfo => $tagInfo,
|
838
1011
|
);
|
839
1012
|
}
|
840
1013
|
}
|
1014
|
+
# generate approximate GPSDateTime if necessary
|
1015
|
+
SetGPSDateTime($et, $tagTbl, $time[$i]) if $$et{FoundGPSLatitude} and not $$et{FoundGPSDateTime};
|
841
1016
|
}
|
842
1017
|
if ($verbose) {
|
843
1018
|
$$et{INDENT} = $oldIndent;
|
@@ -929,7 +1104,7 @@ sub ProcessFreeGPS($$$)
|
|
929
1104
|
SetByteOrder('II');
|
930
1105
|
$lat = GetFloat($dataPt, 0x2c);
|
931
1106
|
$lon = GetFloat($dataPt, 0x30);
|
932
|
-
$spd = GetFloat($dataPt, 0x34) *
|
1107
|
+
$spd = GetFloat($dataPt, 0x34) * $knotsToKph; # (convert knots to km/h)
|
933
1108
|
$trk = GetFloat($dataPt, 0x38);
|
934
1109
|
SetByteOrder('MM');
|
935
1110
|
|
@@ -1425,32 +1600,6 @@ sub Process_3gf($$$)
|
|
1425
1600
|
return 1;
|
1426
1601
|
}
|
1427
1602
|
|
1428
|
-
#------------------------------------------------------------------------------
|
1429
|
-
# Process DuDuBell M1 dashcam / VSYS M6L 'gsen' atom (ref PH)
|
1430
|
-
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
1431
|
-
# Returns: 1 on success
|
1432
|
-
sub Process_gsen($$$)
|
1433
|
-
{
|
1434
|
-
my ($et, $dirInfo, $tagTbl) = @_;
|
1435
|
-
my $dataPt = $$dirInfo{DataPt};
|
1436
|
-
my $dirLen = $$dirInfo{DirLen};
|
1437
|
-
my $recLen = 3; # 3-byte record length
|
1438
|
-
$et->VerboseDir('gsen', undef, $dirLen);
|
1439
|
-
if ($dirLen > $recLen and not $et->Options('ExtractEmbedded')) {
|
1440
|
-
$dirLen = $recLen;
|
1441
|
-
EEWarn($et);
|
1442
|
-
}
|
1443
|
-
my $pos;
|
1444
|
-
for ($pos=0; $pos+$recLen<=$dirLen; $pos+=$recLen) {
|
1445
|
-
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
1446
|
-
my @acc = map { $_ /= 16 } unpack "x${pos}c3", $$dataPt;
|
1447
|
-
$et->HandleTag($tagTbl, Accelerometer => "@acc");
|
1448
|
-
# (there are no associated timestamps, but these are sampled at 5 Hz in my test video)
|
1449
|
-
}
|
1450
|
-
delete $$et{DOC_NUM};
|
1451
|
-
return 1;
|
1452
|
-
}
|
1453
|
-
|
1454
1603
|
#------------------------------------------------------------------------------
|
1455
1604
|
# Process DuDuBell M1 dashcam / VSYS M6L 'gps0' atom (ref PH)
|
1456
1605
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
@@ -1479,7 +1628,7 @@ sub Process_gps0($$$)
|
|
1479
1628
|
$lat = $deg + ($lat - $deg * 100) / 60;
|
1480
1629
|
$deg = int($lon / 100);
|
1481
1630
|
$lon = $deg + ($lon - $deg * 100) / 60;
|
1482
|
-
my @a = unpack('C*', substr($$dataPt
|
1631
|
+
my @a = unpack('C*', substr($$dataPt, $pos+22, 6)); # unpack date/time
|
1483
1632
|
$a[0] += 2000;
|
1484
1633
|
$et->HandleTag($tagTbl, GPSDateTime => sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ", @a));
|
1485
1634
|
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
@@ -1497,6 +1646,129 @@ sub Process_gps0($$$)
|
|
1497
1646
|
return 1;
|
1498
1647
|
}
|
1499
1648
|
|
1649
|
+
#------------------------------------------------------------------------------
|
1650
|
+
# Process DuDuBell M1 dashcam / VSYS M6L 'gsen' atom (ref PH)
|
1651
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
1652
|
+
# Returns: 1 on success
|
1653
|
+
sub Process_gsen($$$)
|
1654
|
+
{
|
1655
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
1656
|
+
my $dataPt = $$dirInfo{DataPt};
|
1657
|
+
my $dirLen = $$dirInfo{DirLen};
|
1658
|
+
my $recLen = 3; # 3-byte record length
|
1659
|
+
$et->VerboseDir('gsen', undef, $dirLen);
|
1660
|
+
if ($dirLen > $recLen and not $et->Options('ExtractEmbedded')) {
|
1661
|
+
$dirLen = $recLen;
|
1662
|
+
EEWarn($et);
|
1663
|
+
}
|
1664
|
+
my $pos;
|
1665
|
+
for ($pos=0; $pos+$recLen<=$dirLen; $pos+=$recLen) {
|
1666
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
1667
|
+
my @acc = map { $_ /= 16 } unpack "x${pos}c3", $$dataPt;
|
1668
|
+
$et->HandleTag($tagTbl, Accelerometer => "@acc");
|
1669
|
+
# (there are no associated timestamps, but these are sampled at 5 Hz in my test video)
|
1670
|
+
}
|
1671
|
+
delete $$et{DOC_NUM};
|
1672
|
+
return 1;
|
1673
|
+
}
|
1674
|
+
|
1675
|
+
#------------------------------------------------------------------------------
|
1676
|
+
# Process RIFF-format trailer written by Auto-Vox dashcam (ref PH)
|
1677
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
1678
|
+
# Returns: 1 on success
|
1679
|
+
# Note: This trailer is basically RIFF chunks added to a QuickTime-format file (augh!),
|
1680
|
+
# but there are differences in the record formats so we can't just call
|
1681
|
+
# ProcessRIFF to process the gps0 and gsen atoms using the routines above
|
1682
|
+
sub ProcessRIFFTrailer($$$)
|
1683
|
+
{
|
1684
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
1685
|
+
my $raf = $$dirInfo{RAF};
|
1686
|
+
my $verbose = $et->Options('Verbose');
|
1687
|
+
my ($buff, $pos);
|
1688
|
+
SetByteOrder('II');
|
1689
|
+
for (;;) {
|
1690
|
+
last unless $raf->Read($buff, 8) == 8;
|
1691
|
+
my ($tag, $len) = unpack('a4V', $buff);
|
1692
|
+
last if $tag eq "\0\0\0\0";
|
1693
|
+
unless ($tag =~ /^[\w ]{4}/ and $len < 0x2000000) {
|
1694
|
+
$et->Warn('Bad RIFF trailer');
|
1695
|
+
last;
|
1696
|
+
}
|
1697
|
+
$raf->Read($buff, $len) == $len or $et->Warn("Truncated $tag record in RIFF trailer"), last;
|
1698
|
+
if ($verbose) {
|
1699
|
+
$et->VPrint(0, " - RIFF trailer '${tag}' ($len bytes)\n");
|
1700
|
+
$et->VerboseDump(\$buff, Addr => $raf->Tell() - $len) if $verbose > 2;
|
1701
|
+
$$et{INDENT} .= '| ';
|
1702
|
+
$et->VerboseDir($tag, undef, $len) if $tag =~ /^(gps0|gsen)$/;
|
1703
|
+
}
|
1704
|
+
if ($tag eq 'gps0') {
|
1705
|
+
# (similar to record decoded in Process_gps0, but with some differences)
|
1706
|
+
# 0000: 41 49 54 47 74 46 94 f6 c6 c5 b4 40 34 a2 b4 37 [AITGtF.....@4..7]
|
1707
|
+
# 0010: f8 7b 8a 40 ff ff 00 00 38 00 77 0a 1a 0c 12 28 [.{.@....8.w....(]
|
1708
|
+
# 0020: 8d 01 02 40 29 07 00 00 [...@)...]
|
1709
|
+
# 0x00 - undef[4] 'AITG'
|
1710
|
+
# 0x04 - double latitude (always positive)
|
1711
|
+
# 0x0c - double longitude (always positive)
|
1712
|
+
# 0x14 - ? seen hex "ff ff 00 00" (altitude in Process_gps0 record below)
|
1713
|
+
# 0x18 - int16u speed in knots (different than km/hr in Process_gps0)
|
1714
|
+
# 0x1a - int8u[6] yr-1900,mon,day,hr,min,sec (different than -2000 in Process_gps0)
|
1715
|
+
# 0x20 - int8u direction in degrees / 2
|
1716
|
+
# 0x21 - int8u guessing that this is 1=N, 2=S - PH
|
1717
|
+
# 0x22 - int8u guessing that this is 1=E, 2=W - PH
|
1718
|
+
# 0x23 - ? seen hex "40"
|
1719
|
+
# 0x24 - in32u time since start of video (ms)
|
1720
|
+
my $recLen = 0x28;
|
1721
|
+
for ($pos=0; $pos+$recLen<$len; $pos+=$recLen) {
|
1722
|
+
substr($buff, $pos, 4) eq 'AITG' or $et->Warn('Unrecognized gps0 record'), last;
|
1723
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
1724
|
+
# lat/long are in DDDMM.MMMM format
|
1725
|
+
my $lat = GetDouble(\$buff, $pos+4);
|
1726
|
+
my $lon = GetDouble(\$buff, $pos+12);
|
1727
|
+
$et->Warn('Bad gps0 record') and last if abs($lat) > 9000 or abs($lon) > 18000;
|
1728
|
+
my $deg = int($lat / 100);
|
1729
|
+
$lat = $deg + ($lat - $deg * 100) / 60;
|
1730
|
+
$deg = int($lon / 100);
|
1731
|
+
$lon = $deg + ($lon - $deg * 100) / 60;
|
1732
|
+
$lat = -$lat if Get8u(\$buff, $pos+0x21) == 2; # wild guess
|
1733
|
+
$lon = -$lon if Get8u(\$buff, $pos+0x22) == 2; # wild guess
|
1734
|
+
my @a = unpack('C*', substr($buff, $pos+26, 6)); # unpack date/time
|
1735
|
+
$a[0] += 1900; # (different than Proces_gps0)
|
1736
|
+
$et->HandleTag($tagTbl, SampleTime => Get32u(\$buff, $pos + 0x24) / 1000);
|
1737
|
+
$et->HandleTag($tagTbl, GPSDateTime => sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ", @a));
|
1738
|
+
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
1739
|
+
$et->HandleTag($tagTbl, GPSLongitude => $lon);
|
1740
|
+
$et->HandleTag($tagTbl, GPSSpeed => Get16u(\$buff, $pos+0x18) * $knotsToKph);
|
1741
|
+
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
1742
|
+
$et->HandleTag($tagTbl, GPSTrack => Get8u(\$buff, $pos+0x20) * 2);
|
1743
|
+
$et->HandleTag($tagTbl, GPSTrackRef => 'T');
|
1744
|
+
}
|
1745
|
+
} elsif ($tag eq 'gsen') {
|
1746
|
+
# (similar to record decoded in Process_gsen)
|
1747
|
+
# 0000: 41 49 54 53 1a 0d 05 ff c8 00 00 00 [AITS........]
|
1748
|
+
# 0x00 - undef[4] 'AITS'
|
1749
|
+
# 0x04 - int8s[3] accelerometer readings
|
1750
|
+
# 0x07 - ? seen hex "ff"
|
1751
|
+
# 0x08 - in32u time since start of video (ms)
|
1752
|
+
my $recLen = 0x0c;
|
1753
|
+
for ($pos=0; $pos+$recLen<$len; $pos+=$recLen) {
|
1754
|
+
substr($buff, $pos, 4) eq 'AITS' or $et->Warn('Unrecognized gsen record'), last;
|
1755
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
1756
|
+
my @acc = map { $_ /= 24 } unpack('x'.($pos+4).'c3', $buff);
|
1757
|
+
$et->HandleTag($tagTbl, SampleTime => Get32u(\$buff, $pos + 8) / 1000);
|
1758
|
+
# 0=+Up, 1=+Right, 3=+Forward (calibration of 24 counts/g is a wild guess - PH)
|
1759
|
+
$et->HandleTag($tagTbl, Accelerometer => "@acc");
|
1760
|
+
}
|
1761
|
+
}
|
1762
|
+
# also seen, but not decoded:
|
1763
|
+
# gpsa (8 bytes): hex "01 20 00 00 08 03 02 08 "
|
1764
|
+
# gsea (20 bytes): all zeros
|
1765
|
+
$$et{INDENT} = substr($$et{INDENT}, 0, -2) if $verbose;
|
1766
|
+
}
|
1767
|
+
delete $$et{DOC_NUM};
|
1768
|
+
SetByteOrder('MM');
|
1769
|
+
return 1;
|
1770
|
+
}
|
1771
|
+
|
1500
1772
|
#------------------------------------------------------------------------------
|
1501
1773
|
# Process 'gps ' atom containing NMEA from Pittasoft Blackvue dashcam (ref PH)
|
1502
1774
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
@@ -1624,9 +1896,9 @@ sub ProcessTTAD($$$)
|
|
1624
1896
|
FoundSomething($et, $tagTbl, $sampleTime / 1000);
|
1625
1897
|
my $t = GetDouble($dataPt, $pos);
|
1626
1898
|
$et->HandleTag($tagTbl, GPSDateTime => Image::ExifTool::ConvertUnixTime($t,undef,3).'Z');
|
1627
|
-
$et->HandleTag($tagTbl, GPSAltitude => GetDouble($dataPt, $pos+0x14));
|
1628
1899
|
$et->HandleTag($tagTbl, GPSLatitude => GetDouble($dataPt, $pos+0x1c));
|
1629
1900
|
$et->HandleTag($tagTbl, GPSLongitude => GetDouble($dataPt, $pos+0x24));
|
1901
|
+
$et->HandleTag($tagTbl, GPSAltitude => GetDouble($dataPt, $pos+0x14));
|
1630
1902
|
$et->HandleTag($tagTbl, GPSSpeed => GetDouble($dataPt, $pos+0x0c) * $mpsToKph);
|
1631
1903
|
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
1632
1904
|
$et->HandleTag($tagTbl, GPSTrack => GetDouble($dataPt, $pos+0x30));
|