exiftool_vendored 11.94.0 → 12.06.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.
Potentially problematic release.
This version of exiftool_vendored might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/bin/Changes +163 -3
- data/bin/MANIFEST +5 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +32 -32
- data/bin/exiftool +152 -52
- data/bin/lib/Image/ExifTool.pm +166 -115
- data/bin/lib/Image/ExifTool.pod +108 -81
- data/bin/lib/Image/ExifTool/AIFF.pm +2 -2
- data/bin/lib/Image/ExifTool/APE.pm +2 -2
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +13 -7
- data/bin/lib/Image/ExifTool/Canon.pm +6 -3
- data/bin/lib/Image/ExifTool/CanonCustom.pm +82 -16
- data/bin/lib/Image/ExifTool/DPX.pm +56 -2
- data/bin/lib/Image/ExifTool/DarwinCore.pm +16 -3
- data/bin/lib/Image/ExifTool/Exif.pm +15 -6
- data/bin/lib/Image/ExifTool/Font.pm +9 -2
- data/bin/lib/Image/ExifTool/GIF.pm +5 -0
- data/bin/lib/Image/ExifTool/GeoTiff.pm +2 -0
- data/bin/lib/Image/ExifTool/Geotag.pm +69 -21
- data/bin/lib/Image/ExifTool/GoPro.pm +10 -1
- data/bin/lib/Image/ExifTool/H264.pm +1 -1
- data/bin/lib/Image/ExifTool/HtmlDump.pm +2 -2
- data/bin/lib/Image/ExifTool/ID3.pm +91 -12
- data/bin/lib/Image/ExifTool/Lang/de.pm +3 -1
- data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
- data/bin/lib/Image/ExifTool/M2TS.pm +44 -24
- data/bin/lib/Image/ExifTool/MWG.pm +9 -1
- data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
- data/bin/lib/Image/ExifTool/Minolta.pm +3 -2
- data/bin/lib/Image/ExifTool/MinoltaRaw.pm +11 -10
- data/bin/lib/Image/ExifTool/Nikon.pm +156 -18
- data/bin/lib/Image/ExifTool/Olympus.pm +34 -17
- data/bin/lib/Image/ExifTool/PNG.pm +14 -3
- data/bin/lib/Image/ExifTool/PPM.pm +5 -5
- data/bin/lib/Image/ExifTool/Panasonic.pm +147 -13
- data/bin/lib/Image/ExifTool/PanasonicRaw.pm +33 -0
- data/bin/lib/Image/ExifTool/Parrot.pm +2 -1
- data/bin/lib/Image/ExifTool/Pentax.pm +3 -1
- data/bin/lib/Image/ExifTool/Photoshop.pm +2 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +277 -33
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +460 -67
- data/bin/lib/Image/ExifTool/README +21 -20
- data/bin/lib/Image/ExifTool/RIFF.pm +123 -3
- data/bin/lib/Image/ExifTool/RTF.pm +12 -7
- data/bin/lib/Image/ExifTool/Ricoh.pm +19 -1
- data/bin/lib/Image/ExifTool/Shift.pl +1 -0
- data/bin/lib/Image/ExifTool/SigmaRaw.pm +40 -33
- data/bin/lib/Image/ExifTool/Sony.pm +379 -12
- data/bin/lib/Image/ExifTool/TagLookup.pm +1959 -1874
- data/bin/lib/Image/ExifTool/TagNames.pod +346 -55
- data/bin/lib/Image/ExifTool/Validate.pm +4 -4
- data/bin/lib/Image/ExifTool/WriteExif.pl +3 -2
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +26 -15
- data/bin/lib/Image/ExifTool/Writer.pl +52 -23
- data/bin/lib/Image/ExifTool/XMP.pm +41 -4
- data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
- data/bin/lib/Image/ExifTool/ZISRAW.pm +123 -0
- data/bin/perl-Image-ExifTool.spec +31 -31
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +4 -4
@@ -19,9 +19,11 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
19
19
|
use Image::ExifTool::QuickTime;
|
20
20
|
|
21
21
|
sub Process_tx3g($$$);
|
22
|
+
sub Process_marl($$$);
|
22
23
|
sub Process_mebx($$$);
|
23
24
|
sub ProcessFreeGPS($$$);
|
24
25
|
sub ProcessFreeGPS2($$$);
|
26
|
+
sub Process360Fly($$$);
|
25
27
|
|
26
28
|
# QuickTime data types that have ExifTool equivalents
|
27
29
|
# (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
|
@@ -70,6 +72,7 @@ my %processByMetaFormat = (
|
|
70
72
|
meta => 1, # ('CTMD' in CR3 images, 'priv' unknown in DJI video)
|
71
73
|
data => 1, # ('RVMI')
|
72
74
|
sbtl => 1, # (subtitle; 'tx3g' in Yuneec drone videos)
|
75
|
+
ctbx => 1, # ('marl' in GM videos)
|
73
76
|
);
|
74
77
|
|
75
78
|
# data lengths for each INSV record type
|
@@ -91,7 +94,7 @@ my %insvLimit = (
|
|
91
94
|
NOTES => q{
|
92
95
|
Timed metadata extracted from QuickTime media data and some AVI videos when
|
93
96
|
the ExtractEmbedded option is used. Although most of these tags are
|
94
|
-
combined into the single table below, ExifTool currently reads
|
97
|
+
combined into the single table below, ExifTool currently reads 46 different
|
95
98
|
formats of timed GPS metadata from video files.
|
96
99
|
},
|
97
100
|
VARS => { NO_ID => 1 },
|
@@ -133,6 +136,21 @@ my %insvLimit = (
|
|
133
136
|
SampleTime => { Groups => { 2 => 'Video' }, PrintConv => 'ConvertDuration($val)', Notes => 'sample decoding time' },
|
134
137
|
SampleDuration=>{ Groups => { 2 => 'Video' }, PrintConv => 'ConvertDuration($val)' },
|
135
138
|
UserLabel => { Groups => { 2 => 'Other' } },
|
139
|
+
SampleDateTime => {
|
140
|
+
Groups => { 2 => 'Time' },
|
141
|
+
ValueConv => q{
|
142
|
+
my $str = ConvertUnixTime($val);
|
143
|
+
my $frac = $val - int($val);
|
144
|
+
if ($frac != 0) {
|
145
|
+
$frac = sprintf('%.6f', $frac);
|
146
|
+
$frac =~ s/^0//;
|
147
|
+
$frac =~ s/0+$//;
|
148
|
+
$str .= $frac;
|
149
|
+
}
|
150
|
+
return $str;
|
151
|
+
},
|
152
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
153
|
+
},
|
136
154
|
#
|
137
155
|
# timed metadata decoded based on MetaFormat (format of 'meta' or 'data' sample description)
|
138
156
|
# [or HandlerType, or specific 'vide' type if specified]
|
@@ -158,6 +176,10 @@ my %insvLimit = (
|
|
158
176
|
Name => 'rtmd',
|
159
177
|
SubDirectory => { TagTable => 'Image::ExifTool::Sony::rtmd' },
|
160
178
|
},
|
179
|
+
marl => {
|
180
|
+
Name => 'marl',
|
181
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::marl' },
|
182
|
+
},
|
161
183
|
CTMD => { # (Canon Timed MetaData)
|
162
184
|
Name => 'CTMD',
|
163
185
|
SubDirectory => { TagTable => 'Image::ExifTool::Canon::CTMD' },
|
@@ -533,6 +555,137 @@ my %insvLimit = (
|
|
533
555
|
},
|
534
556
|
);
|
535
557
|
|
558
|
+
%Image::ExifTool::QuickTime::Tags360Fly = (
|
559
|
+
PROCESS_PROC => \&Process360Fly,
|
560
|
+
NOTES => 'Timed metadata found in MP4 videos from the 360Fly.',
|
561
|
+
1 => {
|
562
|
+
Name => 'Accel360Fly',
|
563
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Accel360Fly' },
|
564
|
+
},
|
565
|
+
2 => {
|
566
|
+
Name => 'Gyro360Fly',
|
567
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Gyro360Fly' },
|
568
|
+
},
|
569
|
+
3 => {
|
570
|
+
Name => 'Mag360Fly',
|
571
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Mag360Fly' },
|
572
|
+
},
|
573
|
+
5 => {
|
574
|
+
Name => 'GPS360Fly',
|
575
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GPS360Fly' },
|
576
|
+
},
|
577
|
+
6 => {
|
578
|
+
Name => 'Rot360Fly',
|
579
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rot360Fly' },
|
580
|
+
},
|
581
|
+
250 => {
|
582
|
+
Name => 'Fusion360Fly',
|
583
|
+
SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Fusion360Fly' },
|
584
|
+
},
|
585
|
+
);
|
586
|
+
|
587
|
+
%Image::ExifTool::QuickTime::Accel360Fly = (
|
588
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
589
|
+
GROUPS => { 2 => 'Location' },
|
590
|
+
1 => { Name => 'AccelMode', Unknown => 1 }, # (always 2 in my sample)
|
591
|
+
2 => {
|
592
|
+
Name => 'SampleTime',
|
593
|
+
Groups => { 2 => 'Video' },
|
594
|
+
Format => 'int64u',
|
595
|
+
ValueConv => '$val / 1e6',
|
596
|
+
PrintConv => 'ConvertDuration($val)',
|
597
|
+
},
|
598
|
+
10 => { Name => 'AccelYPR', Format => 'float[3]' },
|
599
|
+
);
|
600
|
+
|
601
|
+
%Image::ExifTool::QuickTime::Gyro360Fly = (
|
602
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
603
|
+
GROUPS => { 2 => 'Location' },
|
604
|
+
1 => { Name => 'GyroMode', Unknown => 1 }, # (always 1 in my sample)
|
605
|
+
2 => {
|
606
|
+
Name => 'SampleTime',
|
607
|
+
Groups => { 2 => 'Video' },
|
608
|
+
Format => 'int64u',
|
609
|
+
ValueConv => '$val / 1e6',
|
610
|
+
PrintConv => 'ConvertDuration($val)',
|
611
|
+
},
|
612
|
+
10 => { Name => 'GyroYPR', Format => 'float[3]' },
|
613
|
+
);
|
614
|
+
|
615
|
+
%Image::ExifTool::QuickTime::Mag360Fly = (
|
616
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
617
|
+
GROUPS => { 2 => 'Location' },
|
618
|
+
1 => { Name => 'MagMode', Unknown => 1 }, # (always 1 in my sample)
|
619
|
+
2 => {
|
620
|
+
Name => 'SampleTime',
|
621
|
+
Groups => { 2 => 'Video' },
|
622
|
+
Format => 'int64u',
|
623
|
+
ValueConv => '$val / 1e6',
|
624
|
+
PrintConv => 'ConvertDuration($val)',
|
625
|
+
},
|
626
|
+
10 => { Name => 'MagnetometerXYZ', Format => 'float[3]' },
|
627
|
+
);
|
628
|
+
|
629
|
+
%Image::ExifTool::QuickTime::GPS360Fly = (
|
630
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
631
|
+
GROUPS => { 2 => 'Location' },
|
632
|
+
1 => { Name => 'GPSMode', Unknown => 1 }, # (always 16 in my sample)
|
633
|
+
2 => {
|
634
|
+
Name => 'SampleTime',
|
635
|
+
Groups => { 2 => 'Video' },
|
636
|
+
Format => 'int64u',
|
637
|
+
ValueConv => '$val / 1e6',
|
638
|
+
PrintConv => 'ConvertDuration($val)',
|
639
|
+
},
|
640
|
+
10 => { Name => 'GPSLatitude', Format => 'float', PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")' },
|
641
|
+
14 => { Name => 'GPSLongitude', Format => 'float', PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")' },
|
642
|
+
18 => { Name => 'GPSAltitude', Format => 'float', PrintConv => '"$val m"' }, # (questionable accuracy)
|
643
|
+
22 => {
|
644
|
+
Name => 'GPSSpeed',
|
645
|
+
Notes => 'converted to km/hr',
|
646
|
+
Format => 'int16u',
|
647
|
+
ValueConv => '$val * 0.036',
|
648
|
+
PrintConv => 'sprintf("%.1f",$val)',
|
649
|
+
},
|
650
|
+
24 => { Name => 'GPSTrack', Format => 'int16u', ValueConv => '$val / 100' },
|
651
|
+
26 => { Name => 'Acceleration', Format => 'int16u', ValueConv => '$val / 1000' },
|
652
|
+
);
|
653
|
+
|
654
|
+
%Image::ExifTool::QuickTime::Rot360Fly = (
|
655
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
656
|
+
GROUPS => { 2 => 'Location' },
|
657
|
+
1 => { Name => 'RotMode', Unknown => 1 }, # (always 1 in my sample)
|
658
|
+
2 => {
|
659
|
+
Name => 'SampleTime',
|
660
|
+
Groups => { 2 => 'Video' },
|
661
|
+
Format => 'int64u',
|
662
|
+
ValueConv => '$val / 1e6',
|
663
|
+
PrintConv => 'ConvertDuration($val)',
|
664
|
+
},
|
665
|
+
10 => { Name => 'RotationXYZ', Format => 'float[3]' },
|
666
|
+
);
|
667
|
+
|
668
|
+
%Image::ExifTool::QuickTime::Fusion360Fly = (
|
669
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
670
|
+
GROUPS => { 2 => 'Location' },
|
671
|
+
1 => { Name => 'FusionMode', Unknown => 1 }, # (always 0 in my sample)
|
672
|
+
2 => {
|
673
|
+
Name => 'SampleTime',
|
674
|
+
Groups => { 2 => 'Video' },
|
675
|
+
Format => 'int64u',
|
676
|
+
ValueConv => '$val / 1e6',
|
677
|
+
PrintConv => 'ConvertDuration($val)',
|
678
|
+
},
|
679
|
+
10 => { Name => 'FusionYPR', Format => 'float[3]' },
|
680
|
+
);
|
681
|
+
|
682
|
+
# tags found in 'marl' ctbx timed metadata (ref PH)
|
683
|
+
%Image::ExifTool::QuickTime::marl = (
|
684
|
+
PROCESS_PROC => \&Process_marl,
|
685
|
+
GROUPS => { 2 => 'Other' },
|
686
|
+
NOTES => 'Tags extracted from the marl ctbx timed metadata of GM cars.',
|
687
|
+
);
|
688
|
+
|
536
689
|
#------------------------------------------------------------------------------
|
537
690
|
# Save information from keys in OtherSampleDesc directory for processing timed metadata
|
538
691
|
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
@@ -633,7 +786,7 @@ sub FoundSomething($$;$$)
|
|
633
786
|
|
634
787
|
#------------------------------------------------------------------------------
|
635
788
|
# Approximate GPSDateTime value from sample time and CreateDate
|
636
|
-
# Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (
|
789
|
+
# Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (s)
|
637
790
|
# 3) true if CreateDate is at end of video
|
638
791
|
# Notes: Uses ExifTool CreateDateAtEnd as flag to subtract video duration
|
639
792
|
sub SetGPSDateTime($$$)
|
@@ -663,48 +816,89 @@ sub SetGPSDateTime($$$)
|
|
663
816
|
}
|
664
817
|
}
|
665
818
|
|
819
|
+
#------------------------------------------------------------------------------
|
820
|
+
# Handle tags that we found in the subtitle 'text'
|
821
|
+
# Inputs: 0) ExifTool ref, 1) tag table ref, 2) hash of tag names/values
|
822
|
+
sub HandleTextTags($$$)
|
823
|
+
{
|
824
|
+
my ($et, $tagTbl, $tags) = @_;
|
825
|
+
my $tag;
|
826
|
+
delete $$tags{done};
|
827
|
+
delete $$tags{GPSTimeStamp} if $$tags{GPSDateTime};
|
828
|
+
foreach $tag (sort keys %$tags) {
|
829
|
+
$et->HandleTag($tagTbl, $tag => $$tags{$tag});
|
830
|
+
}
|
831
|
+
$$et{UnknownTextCount} = 0;
|
832
|
+
undef %$tags; # clear the hash
|
833
|
+
}
|
834
|
+
|
666
835
|
#------------------------------------------------------------------------------
|
667
836
|
# Process subtitle 'text'
|
668
|
-
# Inputs: 0) ExifTool ref, 1) tag table ref, 2) data ref
|
837
|
+
# Inputs: 0) ExifTool ref, 1) tag table ref, 2) data ref
|
669
838
|
sub Process_text($$$)
|
670
839
|
{
|
671
840
|
my ($et, $tagTbl, $buffPt) = @_;
|
672
|
-
my
|
841
|
+
my %tags;
|
673
842
|
|
674
843
|
return if $$et{NoMoreTextDecoding};
|
675
844
|
|
676
845
|
while ($$buffPt =~ /\$(\w+)([^\$]*)/g) {
|
677
846
|
my ($tag, $dat) = ($1, $2);
|
678
|
-
if ($tag =~ /^[A-Z]{2}RMC$/ and $dat =~ /^,(\d{2})(\d{2})(\d+
|
679
|
-
my $
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
847
|
+
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+)/) {
|
848
|
+
my $time = "$1:$2:$3";
|
849
|
+
if ($$et{LastTime}) {
|
850
|
+
if ($$et{LastTime} eq $time) {
|
851
|
+
$$et{DOC_NUM} = $$et{LastDoc};
|
852
|
+
} elsif (%tags) {
|
853
|
+
HandleTextTags($et, $tagTbl, \%tags);
|
854
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
855
|
+
}
|
856
|
+
}
|
857
|
+
$$et{LastTime} = $time;
|
858
|
+
$$et{LastDoc} = $$et{DOC_NUM};
|
859
|
+
my $year = $14 + ($14 >= 70 ? 1900 : 2000);
|
860
|
+
my $dateTime = sprintf('%.4d:%.2d:%.2d %sZ', $year, $13, $12, $time);
|
861
|
+
$tags{GPSDateTime} = $dateTime;
|
862
|
+
$tags{GPSLatitude} = (($4 || 0) + $5/60) * ($6 eq 'N' ? 1 : -1);
|
863
|
+
$tags{GPSLongitude} = (($7 || 0) + $8/60) * ($9 eq 'E' ? 1 : -1);
|
864
|
+
if (length $10) {
|
865
|
+
$tags{GPSSpeed} = $10 * $knotsToKph;
|
866
|
+
$tags{GPSSpeedRef} = 'K';
|
867
|
+
}
|
684
868
|
if (length $11) {
|
685
|
-
$
|
686
|
-
$
|
869
|
+
$tags{GPSTrack} = $11;
|
870
|
+
$tags{GPSTrackRef} = 'T';
|
687
871
|
}
|
688
|
-
|
689
|
-
|
690
|
-
|
872
|
+
} elsif ($tag =~ /^[A-Z]{2}GGA$/ and $dat =~ /^,(\d{2})(\d{2})(\d+(?:\.\d*)?),(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),[1-6]?,(\d+)?,(\.\d+|\d+\.?\d*)?,(-?\d+\.?\d*)?,M?/s) {
|
873
|
+
my $time = "$1:$2:$3";
|
874
|
+
if ($$et{LastTime}) {
|
875
|
+
if ($$et{LastTime} eq $time) {
|
876
|
+
$$et{DOC_NUM} = $$et{LastDoc};
|
877
|
+
} elsif (%tags) {
|
878
|
+
HandleTextTags($et, $tagTbl, \%tags);
|
879
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
880
|
+
}
|
691
881
|
}
|
692
|
-
|
882
|
+
$$et{LastTime} = $time;
|
883
|
+
$$et{LastDoc} = $$et{DOC_NUM};
|
884
|
+
$tags{GPSTimeStamp} = $time;
|
885
|
+
$tags{GPSLatitude} = (($4 || 0) + $5/60) * ($6 eq 'N' ? 1 : -1);
|
886
|
+
$tags{GPSLongitude} = (($7 || 0) + $8/60) * ($9 eq 'E' ? 1 : -1);
|
887
|
+
$tags{GPSSatellites} = $10 if defined $10;
|
888
|
+
$tags{GPSDOP} = $11 if defined $11;
|
889
|
+
$tags{GPSAltitude} = $12 if defined $12;
|
693
890
|
} elsif ($tag eq 'BEGINGSENSOR' and $dat =~ /^:([-+]\d+\.\d+):([-+]\d+\.\d+):([-+]\d+\.\d+)/) {
|
694
|
-
$
|
695
|
-
$found = 1;
|
891
|
+
$tags{Accelerometer} = "$1 $2 $3";
|
696
892
|
} elsif ($tag eq 'TIME' and $dat =~ /^:(\d+)/) {
|
697
|
-
$
|
698
|
-
$found = 1;
|
893
|
+
$tags{TimeCode} = $1 / ($$et{MediaTS} || 1);
|
699
894
|
} elsif ($tag eq 'BEGIN') {
|
700
|
-
$
|
701
|
-
$
|
895
|
+
$tags{Text} = $dat if length $dat;
|
896
|
+
$tags{done} = 1;
|
702
897
|
} elsif ($tag ne 'END') {
|
703
|
-
$
|
704
|
-
$found = 1;
|
898
|
+
$tags{Text} = "\$$tag$dat";
|
705
899
|
}
|
706
900
|
}
|
707
|
-
|
901
|
+
%tags and HandleTextTags($et, $tagTbl, \%tags), return;
|
708
902
|
|
709
903
|
# check for enciphered binary GPS data
|
710
904
|
# BlueSkySea:
|
@@ -740,7 +934,7 @@ sub Process_text($$$)
|
|
740
934
|
# 0120: 58 00 58 00 58 00 58 00 00 00 00 00 00 00 00 00 [X.X.X.X.........]
|
741
935
|
# 0130: 00 00 00 00 00 00 00 [.......]
|
742
936
|
if ($$buffPt =~ /^\0\0(..\xaa\xaa|\xf2\xe1\xf0\xee)/s and length $$buffPt >= 282) {
|
743
|
-
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 8, 14)));
|
937
|
+
my $val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 8, 14)));
|
744
938
|
if ($val =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/) {
|
745
939
|
$tags{GPSDateTime} = "$1:$2:$3 $4:$5:$6";
|
746
940
|
$val = pack('C*', map { $_ ^ 0xaa } unpack('C*', substr($$buffPt, 38, 9)));
|
@@ -777,12 +971,13 @@ sub Process_text($$$)
|
|
777
971
|
$tags{Accelerometer} = "@acc" if @acc;
|
778
972
|
}
|
779
973
|
}
|
974
|
+
%tags and HandleTextTags($et, $tagTbl, \%tags), return;
|
780
975
|
}
|
781
976
|
|
782
977
|
# check for DJI telemetry data, eg:
|
783
978
|
# "F/3.5, SS 1000, ISO 100, EV 0, GPS (8.6499, 53.1665, 18), D 24.26m,
|
784
979
|
# H 6.00m, H.S 2.10m/s, V.S 0.00m/s \n"
|
785
|
-
if (
|
980
|
+
if ($$buffPt =~ /GPS \(([-+]?\d*\.\d+),\s*([-+]?\d*\.\d+)/) {
|
786
981
|
$$et{CreateDateAtEnd} = 1; # set flag indicating the file creation date is at the end
|
787
982
|
$tags{GPSLatitude} = $2;
|
788
983
|
$tags{GPSLongitude} = $1;
|
@@ -797,11 +992,13 @@ sub Process_text($$$)
|
|
797
992
|
$tags{ExposureTime} = 1 / $1 if $$buffPt =~ /\bSS\s+(\d+\.?\d*)/;
|
798
993
|
$tags{ExposureCompensation} = ($1 / ($2 || 1)) if $$buffPt =~ /\bEV\s+([-+]?\d+\.?\d*)(\/\d+)?/;
|
799
994
|
$tags{ISO} = $1 if $$buffPt =~ /\bISO\s+(\d+\.?\d*)/;
|
995
|
+
HandleTextTags($et, $tagTbl, \%tags);
|
996
|
+
return;
|
800
997
|
}
|
801
998
|
|
802
999
|
# check for Mini 0806 dashcam GPS, eg:
|
803
1000
|
# "A,270519,201555.000,3356.8925,N,08420.2071,W,000.0,331.0M,+01.84,-09.80,-00.61;\n"
|
804
|
-
if (
|
1001
|
+
if ($$buffPt =~ /^A,(\d{2})(\d{2})(\d{2}),(\d{2})(\d{2})(\d{2}(\.\d+)?)/) {
|
805
1002
|
$tags{GPSDateTime} = "20$3:$2:$1 $4:$5:$6Z";
|
806
1003
|
if ($$buffPt =~ /^A,.*?,.*?,(\d{2})(\d+\.\d+),([NS])/) {
|
807
1004
|
$tags{GPSLatitude} = ($1 + $2/60) * ($3 eq 'S' ? -1 : 1);
|
@@ -813,37 +1010,56 @@ sub Process_text($$$)
|
|
813
1010
|
$tags{GPSAltitude} = $a[8] if $a[8] and $a[8] =~ s/M$//;
|
814
1011
|
$tags{GPSSpeed} = $a[7] if $a[7] and $a[7] =~ /^\d+\.\d+$/; # (NC)
|
815
1012
|
$tags{Accelerometer} = "$a[9] $a[10] $a[11]" if $a[11] and $a[11] =~ s/;\s*$//;
|
1013
|
+
HandleTextTags($et, $tagTbl, \%tags);
|
1014
|
+
return;
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
# check for Roadhawk dashcam text
|
1018
|
+
# ".;;;;D?JL;6+;;;D;R?;4;;;;DBB;;O;;;=D;L;;HO71G>F;-?=J-F:FNJJ;DPP-JF3F;;PL=DBRLBF0F;=?DNF-RD-PF;N;?=JF;;?D=F:*6F~"
|
1019
|
+
# decoded:
|
1020
|
+
# "X0000.2340Y-000.0720Z0000.9900G0001.0400$GPRMC,082138,A,5330.6683,N,00641.9749,W,012.5,87.86,050213,002.1,A"
|
1021
|
+
# (note: "002.1" is magnetic variation and is not decoded; it should have ",E" or ",W" afterward for direction)
|
1022
|
+
if ($$buffPt =~ /\*[0-9A-F]{2}~$/) {
|
1023
|
+
# (ref https://reverseengineering.stackexchange.com/questions/11582/how-to-reverse-engineer-dash-cam-metadata)
|
1024
|
+
my @decode = unpack 'C*', '-I8XQWRVNZOYPUTA0B1C2SJ9K.L,M$D3E4F5G6H7';
|
1025
|
+
my @chars = unpack 'C*', substr($$buffPt, 0, -4);
|
1026
|
+
foreach (@chars) {
|
1027
|
+
my $n = $_ - 43;
|
1028
|
+
$_ = $decode[$n] if $n >= 0 and defined $decode[$n];
|
1029
|
+
}
|
1030
|
+
my $buff = pack 'C*', @chars;
|
1031
|
+
if ($buff =~ /X(.*?)Y(.*?)Z(.*?)G(.*?)\$/) {
|
1032
|
+
# yup. the decoding worked out
|
1033
|
+
$tags{Accelerometer} = "$1 $2 $3 $4";
|
1034
|
+
$$buffPt = $buff; # (process GPRMC below)
|
1035
|
+
}
|
816
1036
|
}
|
817
1037
|
|
818
|
-
# check for Thinkware format, eg:
|
1038
|
+
# check for Thinkware format (and other NMEA RMC), eg:
|
819
1039
|
# "gsensori,4,512,-67,-12,100;GNRMC,161313.00,A,4529.87489,N,07337.01215,W,6.225,35.34,310819,,,A*52..;
|
820
1040
|
# CAR,0,0,0,0.0,0,0,0,0,0,0,0,0"
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
$tags{GPSTrackRef} = 'T';
|
837
|
-
}
|
1041
|
+
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
|
1042
|
+
# do some basic sanity checks on the date
|
1043
|
+
$13 <= 31 and $14 <= 12 and $15 <= 99)
|
1044
|
+
{
|
1045
|
+
my $year = $15 + ($15 >= 70 ? 1900 : 2000);
|
1046
|
+
$tags{GPSDateTime} = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $year, $14, $13, $1, $2, $3);
|
1047
|
+
$tags{GPSLatitude} = (($5 || 0) + $6/60) * ($7 eq 'N' ? 1 : -1);
|
1048
|
+
$tags{GPSLongitude} = (($8 || 0) + $9/60) * ($10 eq 'E' ? 1 : -1);
|
1049
|
+
if (length $11) {
|
1050
|
+
$tags{GPSSpeed} = $11 * $knotsToKph;
|
1051
|
+
$tags{GPSSpeedRef} = 'K';
|
1052
|
+
}
|
1053
|
+
if (length $12) {
|
1054
|
+
$tags{GPSTrack} = $12;
|
1055
|
+
$tags{GPSTrackRef} = 'T';
|
838
1056
|
}
|
839
|
-
$tags{GSensor} = $1 if $$buffPt =~ /\bgsensori,(.*?)(;|$)/;
|
840
|
-
$tags{Car} = $1 if $$buffPt =~ /\bCAR,(.*?)(;|$)/;
|
841
1057
|
}
|
1058
|
+
$tags{GSensor} = $1 if $$buffPt =~ /\bgsensori,(.*?)(;|$)/;
|
1059
|
+
$tags{Car} = $1 if $$buffPt =~ /\bCAR,(.*?)(;|$)/;
|
1060
|
+
|
842
1061
|
if (%tags) {
|
843
|
-
|
844
|
-
$et->HandleTag($tagTbl, $tag => $tags{$tag});
|
845
|
-
}
|
846
|
-
$$et{UnknownTextCount} = 0;
|
1062
|
+
HandleTextTags($et, $tagTbl, \%tags);
|
847
1063
|
} else {
|
848
1064
|
$$et{UnknownTextCount} = ($$et{UnknownTextCount} || 0) + 1;
|
849
1065
|
# give up trying to decode useful information if we haven't found anything for a while
|
@@ -1023,8 +1239,8 @@ sub ProcessSamples($)
|
|
1023
1239
|
$$et{ee} = $ee; # need ee information for 'keys'
|
1024
1240
|
$et->HandleTag($tagTbl, $metaFormat, undef,
|
1025
1241
|
DataPt => \$buff,
|
1026
|
-
DataPos =>
|
1027
|
-
Base => $$start[$i],
|
1242
|
+
DataPos => 0,
|
1243
|
+
Base => $$start[$i], # (Base must be set for CR3 files)
|
1028
1244
|
TagInfo => $tagInfo,
|
1029
1245
|
);
|
1030
1246
|
delete $$et{ee};
|
@@ -1036,7 +1252,7 @@ sub ProcessSamples($)
|
|
1036
1252
|
Process_text($et, $tagTbl, \$buff);
|
1037
1253
|
}
|
1038
1254
|
} elsif ($verbose) {
|
1039
|
-
$et->VPrint(0, "Unknown
|
1255
|
+
$et->VPrint(0, "Unknown $type format ($metaFormat)");
|
1040
1256
|
}
|
1041
1257
|
|
1042
1258
|
} elsif ($type eq 'gps ') { # (ie. GPSDataList tag)
|
@@ -1058,8 +1274,8 @@ sub ProcessSamples($)
|
|
1058
1274
|
FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
|
1059
1275
|
$et->HandleTag($tagTbl, $type, undef,
|
1060
1276
|
DataPt => \$buff,
|
1061
|
-
DataPos =>
|
1062
|
-
Base => $$start[$i],
|
1277
|
+
DataPos => 0,
|
1278
|
+
Base => $$start[$i], # (Base must be set for CR3 files)
|
1063
1279
|
TagInfo => $tagInfo,
|
1064
1280
|
);
|
1065
1281
|
}
|
@@ -1093,13 +1309,35 @@ sub ProcessFreeGPS($$$)
|
|
1093
1309
|
|
1094
1310
|
return 0 if $dirLen < 92;
|
1095
1311
|
|
1096
|
-
if (substr($$dataPt,
|
1312
|
+
if (substr($$dataPt,18,8) eq "\xaa\xaa\xf2\xe1\xf0\xee\x54\x54") {
|
1097
1313
|
|
1314
|
+
# (this is very similar to the encrypted text format)
|
1098
1315
|
# decode encrypted ASCII-based GPS (DashCam Azdome GS63H, ref 5)
|
1099
1316
|
# header looks like this in my sample:
|
1100
1317
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 05 01 00 00 [....freeGPS ....]
|
1101
1318
|
# 0010: 01 03 aa aa f2 e1 f0 ee 54 54 98 9a 9b 92 9a 93 [........TT......]
|
1102
1319
|
# 0020: 98 9e 98 98 9e 93 98 92 a6 9f 9f 9c 9d ed fa 8a [................]
|
1320
|
+
# decrypted (from byte 18):
|
1321
|
+
# 0000: 00 00 58 4b 5a 44 fe fe 32 30 31 38 30 39 32 34 [..XKZD..20180924]
|
1322
|
+
# 0010: 32 32 34 39 32 38 0c 35 35 36 37 47 50 20 20 20 [224928.5567GP ]
|
1323
|
+
# 0020: 00 00 00 00 00 03 4e 34 30 34 36 34 33 35 30 57 [......N40464350W]
|
1324
|
+
# 0030: 30 30 37 30 34 30 33 30 38 30 30 30 30 30 30 30 [0070403080000000]
|
1325
|
+
# 0040: 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [7...............]
|
1326
|
+
# [...]
|
1327
|
+
# 00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 30 39 [.............+09]
|
1328
|
+
# 00b0: 33 2d 30 30 33 2d 30 30 35 00 00 00 00 00 00 00 [3-003-005.......]
|
1329
|
+
# header looks like this for EEEkit gps:
|
1330
|
+
# 0000: 00 00 04 00 66 72 65 65 47 50 53 20 f0 03 00 00 [....freeGPS ....]
|
1331
|
+
# 0010: 01 03 aa aa f2 e1 f0 ee 54 54 98 9a 98 9a 9a 9f [........TT......]
|
1332
|
+
# 0020: 9b 93 9b 9c 98 99 99 9f a6 9a 9a 98 9a 9a 9f 9b [................]
|
1333
|
+
# 0030: 93 9b 9c 98 99 99 9c a9 e4 99 9d 9e 9f 98 9e 9b [................]
|
1334
|
+
# 0040: 9c fd 9b 98 98 98 9f 9f 9a 9a 93 81 9a 9b 9d 9f [................]
|
1335
|
+
# decrypted (from byte 18):
|
1336
|
+
# 0000: 00 00 58 4b 5a 44 fe fe 32 30 32 30 30 35 31 39 [..XKZD..20200519]
|
1337
|
+
# 0010: 31 36 32 33 33 35 0c 30 30 32 30 30 35 31 39 31 [162335.002005191]
|
1338
|
+
# 0020: 36 32 33 33 36 03 4e 33 37 34 35 32 34 31 36 57 [62336.N37452416W]
|
1339
|
+
# 0030: 31 32 32 32 35 35 30 30 39 2b 30 31 37 35 30 31 [122255009+017501]
|
1340
|
+
# 0040: 31 2b 30 31 34 2b 30 30 32 2b 30 32 36 2b 30 31 [1+014+002+026+01]
|
1103
1341
|
my $n = $dirLen - 18;
|
1104
1342
|
$n = 0x101 if $n > 0x101;
|
1105
1343
|
my $buf2 = pack 'C*', map { $_ ^ 0xaa } unpack 'C*', substr($$dataPt,18,$n);
|
@@ -1108,13 +1346,25 @@ sub ProcessFreeGPS($$$)
|
|
1108
1346
|
$et->VerboseDump(\$buf2);
|
1109
1347
|
}
|
1110
1348
|
# (extract longitude as 9 digits, not 8, ref PH)
|
1111
|
-
return 0 unless $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})
|
1349
|
+
return 0 unless $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})?/s;
|
1112
1350
|
($yr,$mon,$day,$hr,$min,$sec,$lbl,$latRef,$lat,$lonRef,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8,$9/1e4,$10,$11/1e4,$12);
|
1113
|
-
$spd
|
1351
|
+
if (defined $spd) { # (Azdome)
|
1352
|
+
$spd += 0; # remove leading 0's
|
1353
|
+
} elsif ($buf2 =~ /^.{57}([-+]\d{4})(\d{3})/s) { # (EEEkit)
|
1354
|
+
# $alt = $1 + 0; (doesn't look right for my sample, but the Ambarella A12 text has this)
|
1355
|
+
$spd = $2 + 0;
|
1356
|
+
}
|
1114
1357
|
$lbl =~ s/\0.*//s; $lbl =~ s/\s+$//; # truncate at null and remove trailing spaces
|
1115
1358
|
push @xtra, UserLabel => $lbl if length $lbl;
|
1116
1359
|
# extract accelerometer data (ref PH)
|
1117
|
-
|
1360
|
+
if ($buf2 =~ /^.{65}(([-+]\d{3})([-+]\d{3})([-+]\d{3})([-+]\d{3})*)/s) {
|
1361
|
+
$_ = $1;
|
1362
|
+
@acc = ($2/100, $3/100, $4/100);
|
1363
|
+
s/([-+])/ $1/g; s/^ //;
|
1364
|
+
push @xtra, AccelerometerData => $_;
|
1365
|
+
} elsif ($buf2 =~ /^.{173}([-+]\d{3})([-+]\d{3})([-+]\d{3})/s) { # (Azdome)
|
1366
|
+
@acc = ($1/100, $2/100, $3/100);
|
1367
|
+
}
|
1118
1368
|
|
1119
1369
|
} elsif ($$dataPt =~ /^.{52}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/s) {
|
1120
1370
|
|
@@ -1165,12 +1415,12 @@ sub ProcessFreeGPS($$$)
|
|
1165
1415
|
|
1166
1416
|
# decode freeGPS from Akaso dashcam
|
1167
1417
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 60 00 00 00 [....freeGPS `...]
|
1168
|
-
#
|
1169
|
-
#
|
1170
|
-
#
|
1171
|
-
#
|
1172
|
-
#
|
1173
|
-
#
|
1418
|
+
# 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
|
1419
|
+
# 0020: 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 [00000...........]
|
1420
|
+
# 0030: 12 00 00 00 2f 00 00 00 19 00 00 00 41 00 00 00 [..../.......A...]
|
1421
|
+
# 0040: 13 b3 ca 44 4e 00 00 00 29 92 fb 45 45 00 00 00 [...DN...)..EE...]
|
1422
|
+
# 0050: d9 ee b4 41 ec d1 d3 42 e4 07 00 00 01 00 00 00 [...A...B........]
|
1423
|
+
# 0060: 0c 00 00 00 01 00 00 00 05 00 00 00 00 00 00 00 [................]
|
1174
1424
|
($latRef, $lonRef) = ($1, $2);
|
1175
1425
|
($hr, $min, $sec, $yr, $mon, $day) = unpack('x48V3x28V3', $$dataPt);
|
1176
1426
|
SetByteOrder('II');
|
@@ -1181,6 +1431,56 @@ sub ProcessFreeGPS($$$)
|
|
1181
1431
|
$trk -= 360 if $trk >= 360;
|
1182
1432
|
SetByteOrder('MM');
|
1183
1433
|
|
1434
|
+
} elsif ($$dataPt =~ /^.{16}YndAkasoCar/s) {
|
1435
|
+
|
1436
|
+
# Akaso V1 dascham
|
1437
|
+
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
|
1438
|
+
# 0010: 59 6e 64 41 6b 61 73 6f 43 61 72 00 00 00 00 00 [YndAkasoCar.....]
|
1439
|
+
# 0020: 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 [00000...........]
|
1440
|
+
# 0030: 0e 00 00 00 27 00 00 00 2c 00 00 00 e3 07 00 00 [....'...,.......]
|
1441
|
+
# 0040: 05 00 00 00 1d 00 00 00 41 4e 45 00 00 00 00 00 [........ANE.....]
|
1442
|
+
# 0050: f1 4e 3e 3d 90 df ca 40 e3 50 bf 0b 0b 31 a0 40 [.N>=...@.P...1.@]
|
1443
|
+
# 0060: 4b dc c8 41 9a 79 a7 43 34 58 43 31 4f 37 31 35 [K..A.y.C4XC1O715]
|
1444
|
+
# 0070: 35 31 32 36 36 35 37 35 59 4e 44 53 0d e7 cc f9 [51266575YNDS....]
|
1445
|
+
# 0080: 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 [................]
|
1446
|
+
($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
|
1447
|
+
unpack('x48V6a1a1a1x1', $$dataPt);
|
1448
|
+
# ignore invalid fixes
|
1449
|
+
return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
|
1450
|
+
($lonRef eq 'E' or $lonRef eq 'W');
|
1451
|
+
|
1452
|
+
$et->WarnOnce("Can't yet decrypt Akaso V1 timed GPS", 1);
|
1453
|
+
# (see https://exiftool.org/forum/index.php?topic=11320.0)
|
1454
|
+
return 1;
|
1455
|
+
|
1456
|
+
SetByteOrder('II');
|
1457
|
+
$lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
|
1458
|
+
$lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
|
1459
|
+
SetByteOrder('MM');
|
1460
|
+
#my $serialNum = substr($$dataPt, 0x68, 20);
|
1461
|
+
|
1462
|
+
} elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
|
1463
|
+
|
1464
|
+
# EACHPAI dash cam
|
1465
|
+
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 ac 00 00 00 [....freeGPS ....]
|
1466
|
+
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
1467
|
+
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
1468
|
+
# 0030: 00 00 00 00 00 00 00 00 00 00 00 00 34 57 60 62 [............4W`b]
|
1469
|
+
# 0040: 5d 53 3c 41 47 45 45 42 42 3e 40 40 40 3c 51 3c []S<AGEEBB>@@@<Q<]
|
1470
|
+
# 0050: 44 42 44 40 3e 48 46 43 45 3c 5e 3c 40 48 43 41 [DBD@>HFCE<^<@HCA]
|
1471
|
+
# 0060: 42 3e 46 42 47 48 3c 67 3c 40 3e 40 42 3c 43 3e [B>FBGH<g<@>@B<C>]
|
1472
|
+
# 0070: 43 41 3c 40 42 40 46 42 40 3c 3c 3c 51 3a 47 46 [CA<@B@FB@<<<Q:GF]
|
1473
|
+
# 0080: 00 2a 36 35 00 00 00 00 00 00 00 00 00 00 00 00 [.*65............]
|
1474
|
+
|
1475
|
+
$et->WarnOnce("Can't yet decrypt EACHPAI timed GPS", 1);
|
1476
|
+
# (see https://exiftool.org/forum/index.php?topic=5095.msg61266#msg61266)
|
1477
|
+
return 1;
|
1478
|
+
|
1479
|
+
my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
|
1480
|
+
# bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
|
1481
|
+
substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
|
1482
|
+
# (other values are currently unknown)
|
1483
|
+
|
1184
1484
|
} else {
|
1185
1485
|
|
1186
1486
|
# decode binary GPS format (Viofo A119S, ref 2)
|
@@ -1195,6 +1495,21 @@ sub ProcessFreeGPS($$$)
|
|
1195
1495
|
return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
|
1196
1496
|
($lonRef eq 'E' or $lonRef eq 'W');
|
1197
1497
|
($lat,$lon,$spd,$trk) = unpack 'f*', pack 'L*', $lat, $lon, $spd, $trk;
|
1498
|
+
# lat/lon also stored as doubles by Transcend Driver Pro 230 (ref PH)
|
1499
|
+
SetByteOrder('II');
|
1500
|
+
my ($lat2, $lon2, $alt2) = (
|
1501
|
+
GetDouble($dataPt, 0x70),
|
1502
|
+
GetDouble($dataPt, 0x80),
|
1503
|
+
# GetDouble($dataPt, 0x98), # (don't know what this is)
|
1504
|
+
GetDouble($dataPt,0xa0),
|
1505
|
+
# GetDouble($dataPt,0xa8)) # (don't know what this is)
|
1506
|
+
);
|
1507
|
+
if (abs($lat2-$lat) < 0.001 and abs($lon2-$lon) < 0.001) {
|
1508
|
+
$lat = $lat2;
|
1509
|
+
$lon = $lon2;
|
1510
|
+
$alt = $alt2;
|
1511
|
+
}
|
1512
|
+
SetByteOrder('MM');
|
1198
1513
|
$yr += 2000 if $yr < 2000;
|
1199
1514
|
$spd *= $knotsToKph; # convert speed to km/h
|
1200
1515
|
# ($trk is not confirmed; may be GPSImageDirection, ref PH)
|
@@ -1587,6 +1902,33 @@ sub ParseTag($$$)
|
|
1587
1902
|
}
|
1588
1903
|
$$et{HandlerType} = $tag; # fake handler type
|
1589
1904
|
ProcessSamples($et); # we have all we need to process sample data now
|
1905
|
+
} elsif ($tag eq 'GPS ') {
|
1906
|
+
my $pos = 0;
|
1907
|
+
my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
1908
|
+
SetByteOrder('II');
|
1909
|
+
while ($pos + 36 < $dataLen) {
|
1910
|
+
my $dat = substr($$dataPt, $pos, 36);
|
1911
|
+
last if $dat eq "\x0" x 36;
|
1912
|
+
my @a = unpack 'VVVVCVCV', $dat;
|
1913
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
1914
|
+
# 0=1, 1=1, 2=secs, 3=?
|
1915
|
+
SetGPSDateTime($et, $tagTbl, $a[2]);
|
1916
|
+
my $lat = $a[5] / 1e3;
|
1917
|
+
my $lon = $a[7] / 1e3;
|
1918
|
+
my $deg = int($lat / 100);
|
1919
|
+
$lat = $deg + ($lat - $deg * 100) / 60;
|
1920
|
+
$deg = int($lon / 100);
|
1921
|
+
$lon = $deg + ($lon - $deg * 100) / 60;
|
1922
|
+
$lat = -$lat if $a[4] eq 'S';
|
1923
|
+
$lon = -$lon if $a[6] eq 'W';
|
1924
|
+
$et->HandleTag($tagTbl, GPSLatitude => $lat);
|
1925
|
+
$et->HandleTag($tagTbl, GPSLongitude => $lon);
|
1926
|
+
$et->HandleTag($tagTbl, GPSSpeed => $a[3] / 1e3);
|
1927
|
+
$et->HandleTag($tagTbl, GPSSpeedRef => 'K');
|
1928
|
+
$pos += 36;
|
1929
|
+
}
|
1930
|
+
SetByteOrder('MM');
|
1931
|
+
delete $$et{DOC_NUM};
|
1590
1932
|
}
|
1591
1933
|
}
|
1592
1934
|
|
@@ -1604,6 +1946,26 @@ sub Process_tx3g($$$)
|
|
1604
1946
|
return 1;
|
1605
1947
|
}
|
1606
1948
|
|
1949
|
+
#------------------------------------------------------------------------------
|
1950
|
+
# Process GM 'marl' ctbx metadata (ref PH)
|
1951
|
+
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
1952
|
+
# Returns: 1 on success
|
1953
|
+
sub Process_marl($$$)
|
1954
|
+
{
|
1955
|
+
my ($et, $dirInfo, $tagTablePtr) = @_;
|
1956
|
+
my $dataPt = $$dirInfo{DataPt};
|
1957
|
+
return 0 if length $$dataPt < 2;
|
1958
|
+
|
1959
|
+
# 8-byte records:
|
1960
|
+
# byte 0 seems to be tag ID (0=timestamp in sec * 1e7)
|
1961
|
+
# bytes 1-3 seem to be 24-bit signed integer (unknown meaning)
|
1962
|
+
# bytes 4-7 are an int32u value, usually a multiple of 10000
|
1963
|
+
|
1964
|
+
$et->WarnOnce("Can't yet decode timed GM data", 1);
|
1965
|
+
# (see https://exiftool.org/forum/index.php?topic=11335.msg61393#msg61393)
|
1966
|
+
return 1;
|
1967
|
+
}
|
1968
|
+
|
1607
1969
|
#------------------------------------------------------------------------------
|
1608
1970
|
# Process QuickTime 'mebx' timed metadata
|
1609
1971
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
@@ -2132,6 +2494,37 @@ sub ProcessInsta360($;$)
|
|
2132
2494
|
return 1;
|
2133
2495
|
}
|
2134
2496
|
|
2497
|
+
#------------------------------------------------------------------------------
|
2498
|
+
# Process 360Fly 'uuid' atom containing sensor data
|
2499
|
+
# (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
|
2500
|
+
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
|
2501
|
+
# Returns: 1 on success
|
2502
|
+
sub Process360Fly($$$)
|
2503
|
+
{
|
2504
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
2505
|
+
my $dataPt = $$dirInfo{DataPt};
|
2506
|
+
my $dataLen = length $$dataPt;
|
2507
|
+
my $pos = 16;
|
2508
|
+
my $lastTime = -1;
|
2509
|
+
my $streamTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
|
2510
|
+
while ($pos + 32 <= $dataLen) {
|
2511
|
+
my $type = ord substr $$dataPt, $pos, 1;
|
2512
|
+
my $time = Get64u($dataPt, $pos + 2); # (only valid for some types)
|
2513
|
+
if ($$tagTbl{$type}) {
|
2514
|
+
if ($time != $lastTime) {
|
2515
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
2516
|
+
$lastTime = $time;
|
2517
|
+
}
|
2518
|
+
}
|
2519
|
+
$et->HandleTag($tagTbl, $type, undef, DataPt => $dataPt, Start => $pos, Size => 32);
|
2520
|
+
# synthesize GPSDateTime from the timestamp for GPS records
|
2521
|
+
SetGPSDateTime($et, $streamTbl, $time / 1e6) if $type == 5;
|
2522
|
+
$pos += 32;
|
2523
|
+
}
|
2524
|
+
delete $$et{DOC_NUM};
|
2525
|
+
return 1;
|
2526
|
+
}
|
2527
|
+
|
2135
2528
|
#------------------------------------------------------------------------------
|
2136
2529
|
# Scan media data for "freeGPS" metadata if not found already (ref PH)
|
2137
2530
|
# Inputs: 0) ExifTool ref
|