exiftool_vendored 13.03.0 → 13.06.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 +39 -0
- data/bin/MANIFEST +1 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/exiftool +42 -32
- data/bin/lib/Image/ExifTool/Canon.pm +7 -1
- data/bin/lib/Image/ExifTool/DJI.pm +90 -28
- data/bin/lib/Image/ExifTool/Exif.pm +8 -4
- data/bin/lib/Image/ExifTool/GPS.pm +33 -29
- data/bin/lib/Image/ExifTool/Geolocation.pm +2 -1
- data/bin/lib/Image/ExifTool/Geotag.pm +43 -9
- data/bin/lib/Image/ExifTool/GoPro.pm +118 -6
- data/bin/lib/Image/ExifTool/JPEG.pm +19 -4
- data/bin/lib/Image/ExifTool/OOXML.pm +7 -7
- data/bin/lib/Image/ExifTool/Photoshop.pm +2 -2
- data/bin/lib/Image/ExifTool/Protobuf.pm +242 -0
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +66 -25
- data/bin/lib/Image/ExifTool/Sony.pm +2 -1
- data/bin/lib/Image/ExifTool/TagLookup.pm +2340 -2324
- data/bin/lib/Image/ExifTool/TagNames.pod +86 -8
- data/bin/lib/Image/ExifTool/Writer.pl +1 -1
- data/bin/lib/Image/ExifTool/XMP.pm +11 -1
- data/bin/lib/Image/ExifTool/XMP2.pl +50 -8
- data/bin/lib/Image/ExifTool.pm +61 -33
- data/bin/lib/Image/ExifTool.pod +7 -6
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +3 -2
@@ -0,0 +1,242 @@
|
|
1
|
+
#------------------------------------------------------------------------------
|
2
|
+
# File: Protobuf.pm
|
3
|
+
#
|
4
|
+
# Description: Decode protocol buffer data
|
5
|
+
#
|
6
|
+
# Revisions: 2024-12-04 - P. Harvey Created
|
7
|
+
#
|
8
|
+
# Notes: Tag definitions for Protobuf tags support additional 'signed'
|
9
|
+
# and 'unsigned' formats for varInt (type 0) values
|
10
|
+
#
|
11
|
+
# References: 1) https://protobuf.dev/programming-guides/encoding/
|
12
|
+
#------------------------------------------------------------------------------
|
13
|
+
|
14
|
+
package Image::ExifTool::Protobuf;
|
15
|
+
|
16
|
+
use strict;
|
17
|
+
use vars qw($VERSION);
|
18
|
+
use Image::ExifTool qw(:DataAccess :Utils);
|
19
|
+
|
20
|
+
$VERSION = '1.00';
|
21
|
+
|
22
|
+
sub ProcessProtobuf($$$;$);
|
23
|
+
|
24
|
+
#------------------------------------------------------------------------------
|
25
|
+
# Read bytes from dirInfo object
|
26
|
+
# Inputs: 0) dirInfo ref, 1) number of bytes
|
27
|
+
# Returns: binary data or undef on error
|
28
|
+
sub GetBytes($$)
|
29
|
+
{
|
30
|
+
my ($dirInfo, $n) = @_;
|
31
|
+
my $dataPt = $$dirInfo{DataPt};
|
32
|
+
my $pos = $$dirInfo{Pos};
|
33
|
+
return undef if $pos + $n > length $$dataPt;
|
34
|
+
$$dirInfo{Pos} += $n;
|
35
|
+
return substr($$dataPt, $pos, $n);
|
36
|
+
}
|
37
|
+
|
38
|
+
#------------------------------------------------------------------------------
|
39
|
+
# Read variable-length integer
|
40
|
+
# Inputs: 0) dirInfo ref
|
41
|
+
# Returns: integer value
|
42
|
+
sub VarInt($)
|
43
|
+
{
|
44
|
+
my $dirInfo = shift;
|
45
|
+
my $val = 0;
|
46
|
+
my $shift = 0;
|
47
|
+
for (;;) {
|
48
|
+
my $buff = GetBytes($dirInfo, 1);
|
49
|
+
defined $buff or return undef;
|
50
|
+
$val += (ord($buff) & 0x7f) << $shift;
|
51
|
+
last unless ord($buff) & 0x80;
|
52
|
+
$shift += 7;
|
53
|
+
}
|
54
|
+
return $val;
|
55
|
+
}
|
56
|
+
|
57
|
+
#------------------------------------------------------------------------------
|
58
|
+
# Read protobuf record
|
59
|
+
# Inputs: 0) dirInfo ref
|
60
|
+
# Returns: 0) record payload (plus tag id and format type in list context)
|
61
|
+
# Notes: Updates dirInfo Pos to start of next record
|
62
|
+
sub ReadRecord($)
|
63
|
+
{
|
64
|
+
my $dirInfo = shift;
|
65
|
+
my $val = VarInt($dirInfo);
|
66
|
+
return undef unless defined $val;
|
67
|
+
my $id = $val >> 3;
|
68
|
+
my $type = $val & 0x07;
|
69
|
+
my $buff;
|
70
|
+
|
71
|
+
if ($type == 0) { # varInt
|
72
|
+
$buff = VarInt($dirInfo);
|
73
|
+
} elsif ($type == 1) { # 64-bit number
|
74
|
+
$buff = GetBytes($dirInfo, 8);
|
75
|
+
} elsif ($type == 2) { # string, bytes or protobuf
|
76
|
+
my $len = VarInt($dirInfo);
|
77
|
+
if ($len) {
|
78
|
+
$buff = GetBytes($dirInfo, $len);
|
79
|
+
} else {
|
80
|
+
$buff = '';
|
81
|
+
}
|
82
|
+
} elsif ($type == 3) { # (deprecated start group)
|
83
|
+
$buff = '';
|
84
|
+
} elsif ($type == 4) { # (deprecated end group)
|
85
|
+
$buff = '';
|
86
|
+
} elsif ($type == 5) { # 32-bit number
|
87
|
+
$buff = GetBytes($dirInfo, 4);
|
88
|
+
}
|
89
|
+
return wantarray ? ($buff, $id, $type) : $buff;
|
90
|
+
}
|
91
|
+
|
92
|
+
#------------------------------------------------------------------------------
|
93
|
+
# Check to see if this could be a protobuf object
|
94
|
+
# Inputs: 0) data reference
|
95
|
+
# Retursn: true if this looks like a protobuf
|
96
|
+
sub IsProtobuf($)
|
97
|
+
{
|
98
|
+
my $pt = shift;
|
99
|
+
my $dirInfo = { DataPt => $pt, Pos => 0 };
|
100
|
+
for (;;) {
|
101
|
+
return 0 unless defined ReadRecord($dirInfo);
|
102
|
+
return 1 if $$dirInfo{Pos} == length $$pt;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
#------------------------------------------------------------------------------
|
107
|
+
# Process protobuf data (eg. DJI djmd timed data from Action4 videos) (ref 1)
|
108
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo ref with DataPt, DirName and Base,
|
109
|
+
# 2) tag table ptr, 3) prefix of parent protobuf ID's
|
110
|
+
# Returns: true on success
|
111
|
+
sub ProcessProtobuf($$$;$)
|
112
|
+
{
|
113
|
+
my ($et, $dirInfo, $tagTbl, $prefix) = @_;
|
114
|
+
my $dataPt = $$dirInfo{DataPt};
|
115
|
+
my $dirName = $$dirInfo{DirName};
|
116
|
+
my $unknown = $et->Options('Unknown') || $et->Options('Verbose');
|
117
|
+
|
118
|
+
$$dirInfo{Pos} = $$dirInfo{DirStart} || 0; # initialize buffer Pos
|
119
|
+
|
120
|
+
unless ($prefix) {
|
121
|
+
$prefix = '';
|
122
|
+
$$et{ProtocolName}{$dirName} = '*' unless defined $$et{ProtocolName}{$dirName};
|
123
|
+
SetByteOrder('II');
|
124
|
+
}
|
125
|
+
# loop through protobuf records
|
126
|
+
for (;;) {
|
127
|
+
my $pos = $$dirInfo{Pos};
|
128
|
+
last if $pos >= length $$dataPt;
|
129
|
+
my ($buff, $id, $type) = ReadRecord($dirInfo);
|
130
|
+
defined $buff or $et->WarnOnce('Protobuf format error'), last;
|
131
|
+
if ($type == 2 and $buff =~ /\.proto$/) {
|
132
|
+
# save protocol name separately for directory type
|
133
|
+
$$et{ProtocolName}{$dirName} = substr($buff, 0, -6);
|
134
|
+
$et->HandleTag($tagTbl, Protocol => $buff);
|
135
|
+
}
|
136
|
+
my $tag = "$$et{ProtocolName}{$dirName}_$prefix$id";
|
137
|
+
my $tagInfo = $$tagTbl{$tag};
|
138
|
+
if ($tagInfo) {
|
139
|
+
next if $type != 2 and $$tagInfo{Unknown} and not $unknown;
|
140
|
+
} else {
|
141
|
+
next unless $type == 2 or $unknown;
|
142
|
+
$tagInfo = AddTagToTable($tagTbl, $tag, { Unknown => 1 });
|
143
|
+
}
|
144
|
+
# set IsProtobuf flag (only for Unknown tags) if necessary
|
145
|
+
if ($type == 2 and $$tagInfo{Unknown}) {
|
146
|
+
if ($$tagInfo{IsProtobuf}) {
|
147
|
+
$$tagInfo{IsProtobuf} = 0 unless IsProtobuf(\$buff);
|
148
|
+
} elsif (not defined $$tagInfo{IsProtobuf} and $buff =~ /[^\x20-\x7f]/ and
|
149
|
+
IsProtobuf(\$buff))
|
150
|
+
{
|
151
|
+
$$tagInfo{IsProtobuf} = 1;
|
152
|
+
}
|
153
|
+
next unless $$tagInfo{IsProtobuf} or $unknown;
|
154
|
+
}
|
155
|
+
# format binary payload into a useful value
|
156
|
+
my $val;
|
157
|
+
if ($$tagInfo{Format}) {
|
158
|
+
if ($type == 0) {
|
159
|
+
$val = $buff;
|
160
|
+
$val = ($val & 1) ? -($val >> 1)-1 : ($val >> 1) if $$tagInfo{Format} eq 'signed';
|
161
|
+
} else {
|
162
|
+
$val = ReadValue(\$buff, 0, $$tagInfo{Format}, undef, length($buff));
|
163
|
+
}
|
164
|
+
} elsif ($type == 0) {
|
165
|
+
$val = $buff;
|
166
|
+
my $signed = ($val & 1) ? -($val >> 1)-1 : ($val >> 1);
|
167
|
+
$val .= sprintf(" (0x%x, signed $signed)", $val);
|
168
|
+
} elsif ($type == 1) {
|
169
|
+
$val = '0x' . unpack('H*', $buff) . ' (double ' . GetDouble(\$buff,0) . ')';
|
170
|
+
} elsif ($type == 2) {
|
171
|
+
if ($$tagInfo{IsProtobuf}) {
|
172
|
+
$et->VPrint(1, "+ Protobuf $tag (" . length($buff) . " bytes)\n");
|
173
|
+
my $addr = $$dirInfo{Base} + $$dirInfo{Pos} - length($buff);
|
174
|
+
$et->VerboseDump(\$buff, Addr => $addr);
|
175
|
+
my %subdir = ( DataPt => \$buff, Base => $addr, DirName => $dirName );
|
176
|
+
ProcessProtobuf($et, \%subdir, $tagTbl, "$prefix$id-");
|
177
|
+
next;
|
178
|
+
} elsif ($buff !~ /[^\x20-\x7f]/) {
|
179
|
+
$val = $buff; # assume this is an ASCII string
|
180
|
+
} elsif (length($buff) % 4) {
|
181
|
+
$val = '0x' . unpack('H*', $buff);
|
182
|
+
} else {
|
183
|
+
$val = '0x' . join(' ', unpack('(H8)*', $buff)); # (group in 4-byte blocks)
|
184
|
+
}
|
185
|
+
} elsif ($type == 5) {
|
186
|
+
$val = '0x' . unpack('H*', $buff) . ' (int32u ' . Get32u(\$buff, 0);
|
187
|
+
$val .= ', int32s ' . Get32s(\$buff, 0) if ord(substr($buff,3,1)) & 0x80;
|
188
|
+
$val .= ', float ' . GetFloat(\$buff, 0) . ')';
|
189
|
+
} else {
|
190
|
+
$val = $buff;
|
191
|
+
}
|
192
|
+
# get length of data in the record
|
193
|
+
my $start = $type == 0 ? $pos + 1 : $$dirInfo{Pos} - length $buff;
|
194
|
+
$et->HandleTag($tagTbl, $tag, $val,
|
195
|
+
DataPt => $dataPt,
|
196
|
+
DataPos=> $$dirInfo{Base},
|
197
|
+
Start => $start,
|
198
|
+
Size => $$dirInfo{Pos} - $start,
|
199
|
+
Extra => ", type=$type",
|
200
|
+
Format => $$tagInfo{Format},
|
201
|
+
);
|
202
|
+
}
|
203
|
+
# warn if we didn't finish exactly at the end of the buffer
|
204
|
+
$et->WarnOnce('Truncated protobuf data') unless $prefix or $$dirInfo{Pos} == length $$dataPt;
|
205
|
+
return 1;
|
206
|
+
}
|
207
|
+
|
208
|
+
__END__
|
209
|
+
|
210
|
+
=head1 NAME
|
211
|
+
|
212
|
+
Image::ExifTool::Protobuf - Decode protocol buffer information
|
213
|
+
|
214
|
+
=head1 SYNOPSIS
|
215
|
+
|
216
|
+
This module is loaded automatically by Image::ExifTool when required.
|
217
|
+
|
218
|
+
=head1 DESCRIPTION
|
219
|
+
|
220
|
+
This module contains definitions required by Image::ExifTool to decode
|
221
|
+
information in protocol buffer (protobuf) format.
|
222
|
+
|
223
|
+
=head1 AUTHOR
|
224
|
+
|
225
|
+
Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
|
226
|
+
|
227
|
+
This library is free software; you can redistribute it and/or modify it
|
228
|
+
under the same terms as Perl itself.
|
229
|
+
|
230
|
+
=head1 REFERENCES
|
231
|
+
|
232
|
+
=over 4
|
233
|
+
|
234
|
+
=item L<https://protobuf.dev/programming-guides/encoding/>
|
235
|
+
|
236
|
+
=back
|
237
|
+
|
238
|
+
=head1 SEE ALSO
|
239
|
+
|
240
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
241
|
+
|
242
|
+
=cut
|
@@ -109,7 +109,7 @@ my %insvLimit = (
|
|
109
109
|
The tags below are extracted from timed metadata in QuickTime and other
|
110
110
|
formats of video files when the ExtractEmbedded option is used. Although
|
111
111
|
most of these tags are combined into the single table below, ExifTool
|
112
|
-
currently reads
|
112
|
+
currently reads 85 different formats of timed GPS metadata from video files.
|
113
113
|
},
|
114
114
|
VARS => { NO_ID => 1 },
|
115
115
|
GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
|
@@ -339,10 +339,16 @@ my %insvLimit = (
|
|
339
339
|
Groups => { 2 => 'Preview' },
|
340
340
|
RawConv => '$self->ValidateImage(\$val,$tag)',
|
341
341
|
},
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
342
|
+
djmd => { # (DJI AC003 Osmo Action 4 cam)
|
343
|
+
Name => 'DJIMetadata',
|
344
|
+
SubDirectory => { TagTable => 'Image::ExifTool::DJI::Protobuf' },
|
345
|
+
},
|
346
|
+
dbgi => { # (DJI AC003 Osmo Action 4 cam)
|
347
|
+
Name => 'DJIDebug',
|
348
|
+
Unknown => 2,
|
349
|
+
Notes => 'extracted only if Unknown option is 2 or greater',
|
350
|
+
SubDirectory => { TagTable => 'Image::ExifTool::DJI::Protobuf' },
|
351
|
+
},
|
346
352
|
Unknown00 => { Unknown => 1 },
|
347
353
|
Unknown01 => { Unknown => 1 },
|
348
354
|
Unknown02 => { Unknown => 1 },
|
@@ -894,7 +900,7 @@ sub FoundSomething($$;$$)
|
|
894
900
|
#------------------------------------------------------------------------------
|
895
901
|
# Approximate GPSDateTime value from sample time and CreateDate
|
896
902
|
# Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (s)
|
897
|
-
# 3) true if CreateDate is
|
903
|
+
# 3) true if CreateDate is UTC
|
898
904
|
# Notes: Uses ExifTool CreateDateAtEnd as flag to subtract video duration
|
899
905
|
sub SetGPSDateTime($$$;$)
|
900
906
|
{
|
@@ -1432,7 +1438,7 @@ Sample: for ($i=0; ; ) {
|
|
1432
1438
|
|
1433
1439
|
if ($$tagTbl{$metaFormat}) {
|
1434
1440
|
my $tagInfo = $et->GetTagInfo($tagTbl, $metaFormat, \$buff);
|
1435
|
-
if ($tagInfo) {
|
1441
|
+
if ($tagInfo and (not $$tagInfo{Unknown} or $$et{OPTIONS}{Unknown} >= $$tagInfo{Unknown})) {
|
1436
1442
|
FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
|
1437
1443
|
$$et{ee} = $ee; # need ee information for 'keys'
|
1438
1444
|
$et->HandleTag($tagTbl, $metaFormat, undef,
|
@@ -1442,6 +1448,15 @@ Sample: for ($i=0; ; ) {
|
|
1442
1448
|
TagInfo => $tagInfo,
|
1443
1449
|
);
|
1444
1450
|
delete $$et{ee};
|
1451
|
+
# synthesize GPSDateTime if necessary for djmd metadata
|
1452
|
+
if ($metaFormat eq 'djmd') {
|
1453
|
+
if (defined $$et{GPSLatitude} and defined $$et{GPSLongitude} and not $$et{GPSDateTime}) {
|
1454
|
+
SetGPSDateTime($et, $tagTbl, $time[$i], 1); # (NC)
|
1455
|
+
}
|
1456
|
+
delete $$et{GPSLatitude};
|
1457
|
+
delete $$et{GPSLongitude};
|
1458
|
+
delete $$et{GPSDateTime};
|
1459
|
+
}
|
1445
1460
|
} elsif ($metaFormat eq 'camm' and $buff =~ /^X/) {
|
1446
1461
|
# seen 'camm' metadata in this format (X/Y/Z acceleration and G force? + GPRMC + ?)
|
1447
1462
|
# "X0000.0000Y0000.0000Z0000.0000G0000.0000$GPRMC,000125,V,,,,,000.0,,280908,002.1,N*71~, 794021 \x0a"
|
@@ -1670,7 +1685,7 @@ sub ProcessFreeGPS($$$)
|
|
1670
1685
|
}
|
1671
1686
|
if ($notEnc and $notStr) {
|
1672
1687
|
|
1673
|
-
$debug and $et->FoundTag(GPSType =>
|
1688
|
+
$debug and $et->FoundTag(GPSType => 3);
|
1674
1689
|
# decode freeGPS from ViofoA119v3 dashcam (similar to Novatek GPS format)
|
1675
1690
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
1676
1691
|
# 0010: 05 00 00 00 2f 00 00 00 03 00 00 00 13 00 00 00 [..../...........]
|
@@ -1698,7 +1713,7 @@ sub ProcessFreeGPS($$$)
|
|
1698
1713
|
|
1699
1714
|
} else {
|
1700
1715
|
|
1701
|
-
$debug and $et->FoundTag(GPSType =>
|
1716
|
+
$debug and $et->FoundTag(GPSType => 4);
|
1702
1717
|
# decode freeGPS from E-ACE B44 dashcam
|
1703
1718
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
1704
1719
|
# 0010: 08 00 00 00 22 00 00 00 01 00 00 00 18 00 00 00 [...."...........]
|
@@ -1735,7 +1750,7 @@ sub ProcessFreeGPS($$$)
|
|
1735
1750
|
|
1736
1751
|
} elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
|
1737
1752
|
|
1738
|
-
$debug and $et->FoundTag(GPSType =>
|
1753
|
+
$debug and $et->FoundTag(GPSType => 5);
|
1739
1754
|
# also decode 'gpmd' chunk from Kingslim D4 dashcam videos
|
1740
1755
|
# 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
|
1741
1756
|
# 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
|
@@ -1762,7 +1777,7 @@ sub ProcessFreeGPS($$$)
|
|
1762
1777
|
|
1763
1778
|
} elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
|
1764
1779
|
|
1765
|
-
$debug and $et->FoundTag(GPSType =>
|
1780
|
+
$debug and $et->FoundTag(GPSType => 6);
|
1766
1781
|
# decode freeGPS from Akaso dashcam
|
1767
1782
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 60 00 00 00 [....freeGPS `...]
|
1768
1783
|
# 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
|
@@ -1796,7 +1811,7 @@ sub ProcessFreeGPS($$$)
|
|
1796
1811
|
|
1797
1812
|
} elsif ($$dataPt =~ /^.{60}4W`b]S</s and length($$dataPt) >= 140) {
|
1798
1813
|
|
1799
|
-
$debug and $et->FoundTag(GPSType =>
|
1814
|
+
$debug and $et->FoundTag(GPSType => 7);
|
1800
1815
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 01 00 00 [..@.freeGPS ....]
|
1801
1816
|
# 0010: 5a 58 53 42 4e 58 59 53 00 00 00 00 00 00 00 00 [ZXSBNXYS........]
|
1802
1817
|
# 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
@@ -1817,7 +1832,7 @@ sub ProcessFreeGPS($$$)
|
|
1817
1832
|
|
1818
1833
|
} elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0{5}/s) {
|
1819
1834
|
|
1820
|
-
$debug and $et->FoundTag(GPSType =>
|
1835
|
+
$debug and $et->FoundTag(GPSType => 8);
|
1821
1836
|
# Akaso V1 dascham
|
1822
1837
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
|
1823
1838
|
# 0010: 59 6e 64 41 6b 61 73 6f 43 61 72 00 00 00 00 00 [YndAkasoCar.....]
|
@@ -1854,7 +1869,7 @@ sub ProcessFreeGPS($$$)
|
|
1854
1869
|
|
1855
1870
|
} elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
|
1856
1871
|
|
1857
|
-
$debug and $et->FoundTag(GPSType =>
|
1872
|
+
$debug and $et->FoundTag(GPSType => 9);
|
1858
1873
|
# EACHPAI dash cam
|
1859
1874
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 ac 00 00 00 [....freeGPS ....]
|
1860
1875
|
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
@@ -1878,7 +1893,7 @@ sub ProcessFreeGPS($$$)
|
|
1878
1893
|
|
1879
1894
|
} elsif ($$dataPt =~ /^.{64}A([NS])([EW])\0/s) {
|
1880
1895
|
|
1881
|
-
$debug and $et->FoundTag(GPSType =>
|
1896
|
+
$debug and $et->FoundTag(GPSType => 10);
|
1882
1897
|
# Vantrue S1 dashcam
|
1883
1898
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
|
1884
1899
|
# 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
|
@@ -1904,7 +1919,7 @@ sub ProcessFreeGPS($$$)
|
|
1904
1919
|
|
1905
1920
|
} elsif (substr($$dataPt,0x45,3) eq 'ATC') {
|
1906
1921
|
|
1907
|
-
$debug and $et->FoundTag(GPSType =>
|
1922
|
+
$debug and $et->FoundTag(GPSType => 11);
|
1908
1923
|
# header looks like this: (sample 1)
|
1909
1924
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 38 06 00 00 [....freeGPS 8...]
|
1910
1925
|
# 0010: 49 51 53 32 30 31 33 30 33 30 36 42 00 00 00 00 [IQS20130306B....]
|
@@ -2017,7 +2032,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2017
2032
|
|
2018
2033
|
} elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s and $dirLen >= 0x88) {
|
2019
2034
|
|
2020
|
-
$debug and $et->FoundTag(GPSType =>
|
2035
|
+
$debug and $et->FoundTag(GPSType => 12);
|
2021
2036
|
# header looks like this in my sample:
|
2022
2037
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 08 01 00 00 [....freeGPS ....]
|
2023
2038
|
# 0010: 32 30 31 33 30 38 31 35 2e 30 31 00 00 00 00 00 [20130815.01.....]
|
@@ -2048,9 +2063,9 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2048
2063
|
|
2049
2064
|
} elsif ($$dataPt =~ /^.{16}A([NS])([EW])\0/s) {
|
2050
2065
|
|
2051
|
-
$debug and $et->FoundTag(GPSType =>
|
2066
|
+
$debug and $et->FoundTag(GPSType => 13);
|
2052
2067
|
# INNOVV MP4 video (same format as INNOVV TS)
|
2053
|
-
while ($$dataPt =~ /(A[NS][EW]\0.{28})/
|
2068
|
+
while ($$dataPt =~ /(A[NS][EW]\0.{28})/sg) {
|
2054
2069
|
my $dat = $1;
|
2055
2070
|
$lat = abs(GetFloat(\$dat, 4)); # (abs just to be safe)
|
2056
2071
|
$lon = abs(GetFloat(\$dat, 8)); # (abs just to be safe)
|
@@ -2068,9 +2083,35 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2068
2083
|
SetByteOrder($oldOrder);
|
2069
2084
|
return 1;
|
2070
2085
|
|
2086
|
+
} elsif ($$dataPt =~ /^.{24}A([NS])([EW])/s) {
|
2087
|
+
|
2088
|
+
$debug and $et->FoundTag(GPSType => 14);
|
2089
|
+
# XBHT motorcycle dashcam Model XB702
|
2090
|
+
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
2091
|
+
# 0010: 00 17 05 11 0d 25 18 00 41 4e 45 64 83 3f 00 00 [.....%..ANEd.?..]
|
2092
|
+
# 0020: 44 3d c5 02 48 6d ff 07 df 03 00 00 6b 00 00 00 [D=..Hm......k...]
|
2093
|
+
# 0030: 00 00 00 00 00 17 05 11 0d 25 18 01 41 4e 45 64 [.........%..ANEd]
|
2094
|
+
# 0040: 8b 3f 00 00 30 3d c5 02 50 6d ff 07 df 03 00 00 [.?..0=..Pm......]
|
2095
|
+
while ($$dataPt =~ /(.{8}A[NS][EW].{25})/sg) {
|
2096
|
+
my $dat = $1;
|
2097
|
+
my ($yr,$mon,$day,$hr,$min,$sec,$ss,$latRef,$lonRef,$lat,$lon,$spd) =
|
2098
|
+
unpack('xC7xCCx5VVx4v', $dat);
|
2099
|
+
$yr += 2000; $lat /= 1e4; $lon /= 1e4;
|
2100
|
+
$ss = 0 if $ss > 9; # (just in case)
|
2101
|
+
ConvertLatLon($lat, $lon);
|
2102
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
2103
|
+
my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d.%d',$yr,$mon,$day,$hr,$min,$sec,$ss);
|
2104
|
+
$et->HandleTag($tagTbl, GPSDateTime => $time);
|
2105
|
+
$et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
|
2106
|
+
$et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
|
2107
|
+
$et->HandleTag($tagTbl, GPSSpeed => $spd);
|
2108
|
+
}
|
2109
|
+
SetByteOrder($oldOrder);
|
2110
|
+
return 1;
|
2111
|
+
|
2071
2112
|
} elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
|
2072
2113
|
|
2073
|
-
$debug and $et->FoundTag(GPSType =>
|
2114
|
+
$debug and $et->FoundTag(GPSType => 15);
|
2074
2115
|
# Vantrue N4 dashcam
|
2075
2116
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
|
2076
2117
|
# 0010: 0d 00 00 00 16 00 00 00 1e 00 00 00 41 00 00 00 [............A...]
|
@@ -2127,7 +2168,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2127
2168
|
($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
|
2128
2169
|
unpack('x48V6a1a1a1x1V4', $$dataPt);
|
2129
2170
|
if (substr($$dataPt, 16, 3) eq 'IQS') {
|
2130
|
-
$debug and $et->FoundTag(GPSType =>
|
2171
|
+
$debug and $et->FoundTag(GPSType => 16);
|
2131
2172
|
# Type 3b (ref PH)
|
2132
2173
|
# header looks like this in my sample:
|
2133
2174
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
|
@@ -2139,7 +2180,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2139
2180
|
$spd = Get32s($dataPt, 0x54) / 100 * $mpsToKph;
|
2140
2181
|
$alt = GetFloat($dataPt, 0x58) / 1000; # (NC)
|
2141
2182
|
} else {
|
2142
|
-
$debug and $et->FoundTag(GPSType =>
|
2183
|
+
$debug and $et->FoundTag(GPSType => 17);
|
2143
2184
|
$lat = GetFloat($dataPt, 0x4c);
|
2144
2185
|
$lon = GetFloat($dataPt, 0x50);
|
2145
2186
|
$spd = GetFloat($dataPt, 0x54) * $knotsToKph;
|
@@ -2159,7 +2200,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2159
2200
|
|
2160
2201
|
} elsif ($$dataPt =~ m<^.{23}(\d{4})/(\d{2})/(\d{2}) (\d{2}):(\d{2}):(\d{2}) [N|S]>s) {
|
2161
2202
|
|
2162
|
-
$debug and $et->FoundTag(GPSType =>
|
2203
|
+
$debug and $et->FoundTag(GPSType => 18);
|
2163
2204
|
# XGODY 12" 4K Dashcam
|
2164
2205
|
# 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
|
2165
2206
|
# 0010: 6e 6f 72 6d 61 6c 3a 32 30 32 34 2f 30 35 2f 32 [normal:2024/05/2]
|
@@ -2191,7 +2232,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2191
2232
|
|
2192
2233
|
} elsif ($$dataPt =~ m/^.{30}A.{20}VV/) {
|
2193
2234
|
|
2194
|
-
$debug and $et->FoundTag(GPSType =>
|
2235
|
+
$debug and $et->FoundTag(GPSType => 19);
|
2195
2236
|
# 70mai A810 dashcam (note: no timestamps in the samples I have)
|
2196
2237
|
# 0000: 00 00 40 00 66 72 65 65 47 50 53 20 ed 01 00 00 [..@.freeGPS ....]
|
2197
2238
|
# 0010: 03 00 ed 01 00 00 00 0f 00 00 70 08 00 00 41 66 [..........p...Af]
|
@@ -2208,7 +2249,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
|
|
2208
2249
|
|
2209
2250
|
} else {
|
2210
2251
|
|
2211
|
-
$debug and $et->FoundTag(GPSType =>
|
2252
|
+
$debug and $et->FoundTag(GPSType => 20);
|
2212
2253
|
# (look for binary GPS as stored by Nextbase 512G, ref PH)
|
2213
2254
|
# 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 01 00 00 [....freeGPS x...]
|
2214
2255
|
# 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
|
@@ -34,7 +34,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
34
34
|
use Image::ExifTool::Exif;
|
35
35
|
use Image::ExifTool::Minolta;
|
36
36
|
|
37
|
-
$VERSION = '3.
|
37
|
+
$VERSION = '3.71';
|
38
38
|
|
39
39
|
sub ProcessSRF($$$);
|
40
40
|
sub ProcessSR2($$$);
|
@@ -2166,6 +2166,7 @@ my %hidUnk = ( Hidden => 1, Unknown => 1 );
|
|
2166
2166
|
397 => 'ILCE-7CM2', #JR
|
2167
2167
|
398 => 'ILX-LR1', #JR
|
2168
2168
|
399 => 'ZV-E10M2', #JR
|
2169
|
+
400 => 'ILCE-1M2', #PH
|
2169
2170
|
},
|
2170
2171
|
},
|
2171
2172
|
0xb020 => { #2
|