exiftool_vendored 13.04.0 → 13.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.
- checksums.yaml +4 -4
- data/bin/Changes +22 -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 +27 -21
- data/bin/lib/Image/ExifTool/DJI.pm +90 -28
- data/bin/lib/Image/ExifTool/Geolocation.pm +2 -1
- data/bin/lib/Image/ExifTool/GoPro.pm +3 -3
- data/bin/lib/Image/ExifTool/JPEG.pm +19 -4
- 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 +2336 -2324
- data/bin/lib/Image/ExifTool/TagNames.pod +61 -2
- 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 +38 -0
- data/bin/lib/Image/ExifTool.pm +54 -28
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 277daacb61b45f8ffcf01c45e8a2ae6c7814aa878af57ce5e700433e37a73a89
|
4
|
+
data.tar.gz: c27dc0342c61725b98adb52806417396827df205172ae6793f14bb6eb9d3949b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acb79bdb9f29cb00439ad4006faba198b460abed0c655e1cce6d7a3845ec14c8fe19a778dcbdb5401bb5f77b51329329e9ab8a6932c98a9d04ff63ca04be9af0
|
7
|
+
data.tar.gz: 2771ca21af4d53e1d2138b4b94dd187f1e59c9f9c6230f8d3a8d83be61979b320570084a50f5ae5a3d038114a46cca518dca35e517d8304ed98087c73ceeb163
|
data/bin/Changes
CHANGED
@@ -7,6 +7,26 @@ RSS feed: https://exiftool.org/rss.xml
|
|
7
7
|
Note: The most recent production release is Version 13.00. (Other versions are
|
8
8
|
considered development releases, and are not uploaded to MetaCPAN.)
|
9
9
|
|
10
|
+
Dec. 5, 2024 - Version 13.06
|
11
|
+
|
12
|
+
- Decode timed metadata from MP4 videos of yet another dashcam model
|
13
|
+
- Patched issue where FileSequence could increment twice for each file when a
|
14
|
+
-if condition was used
|
15
|
+
- API Changes:
|
16
|
+
- Revert default WindowsLongPath option until we can solve the pipe
|
17
|
+
problem)
|
18
|
+
|
19
|
+
Dec. 4, 2024 - Version 13.05
|
20
|
+
|
21
|
+
- Added a new SonyModelID
|
22
|
+
- Added support for XMP HDRGainMap and apdi namespaces
|
23
|
+
- Decode DJI timed djmd and dbgi protobuf-format metadata
|
24
|
+
- Decode APP10 AROT HDRGainCurve and APP2 URN UniformResourceName
|
25
|
+
- Decode a couple of new GoPro tags
|
26
|
+
- API Changes:
|
27
|
+
- Changed default WindowsLongPath option to 1 (please report if this
|
28
|
+
causes any problems)
|
29
|
+
|
10
30
|
Nov. 26, 2024 - Version 13.04
|
11
31
|
|
12
32
|
- Added the ability to write GPSDOP and GPSMeasureMode from the -geotag option
|
@@ -21,6 +41,8 @@ Nov. 26, 2024 - Version 13.04
|
|
21
41
|
- Renamed an Unknown Photoshop tag
|
22
42
|
- Convert GoPro GPSSpeed and GPSSpeed3D from m/s to km/h
|
23
43
|
- Patched to tolerate XML header in DOCX xml files
|
44
|
+
- Fixed incorrect file offsets for tags in some embedded files of -htmldump
|
45
|
+
output
|
24
46
|
- Fixed -htmldump output to show the same names for unknown EXIF tags as with
|
25
47
|
the -u option
|
26
48
|
|
data/bin/MANIFEST
CHANGED
@@ -397,6 +397,7 @@ lib/Image/ExifTool/PhotoMechanic.pm
|
|
397
397
|
lib/Image/ExifTool/Photoshop.pm
|
398
398
|
lib/Image/ExifTool/PostScript.pm
|
399
399
|
lib/Image/ExifTool/PrintIM.pm
|
400
|
+
lib/Image/ExifTool/Protobuf.pm
|
400
401
|
lib/Image/ExifTool/Qualcomm.pm
|
401
402
|
lib/Image/ExifTool/QuickTime.pm
|
402
403
|
lib/Image/ExifTool/QuickTimeStream.pl
|
data/bin/META.json
CHANGED
data/bin/META.yml
CHANGED
data/bin/README
CHANGED
@@ -109,8 +109,8 @@ your home directory, then you would type the following commands in a
|
|
109
109
|
terminal window to extract and run ExifTool:
|
110
110
|
|
111
111
|
cd ~/Desktop
|
112
|
-
gzip -dc Image-ExifTool-13.
|
113
|
-
cd Image-ExifTool-13.
|
112
|
+
gzip -dc Image-ExifTool-13.06.tar.gz | tar -xf -
|
113
|
+
cd Image-ExifTool-13.06
|
114
114
|
./exiftool t/images/ExifTool.jpg
|
115
115
|
|
116
116
|
Note: These commands extract meta information from one of the test images.
|
data/bin/exiftool
CHANGED
@@ -11,7 +11,7 @@ use strict;
|
|
11
11
|
use warnings;
|
12
12
|
require 5.004;
|
13
13
|
|
14
|
-
my $version = '13.
|
14
|
+
my $version = '13.06';
|
15
15
|
|
16
16
|
# add our 'lib' directory to the include list BEFORE 'use Image::ExifTool'
|
17
17
|
my $exePath;
|
@@ -1918,7 +1918,7 @@ if (@dbKeys) {
|
|
1918
1918
|
print $vout "Imported entry for '${_}' (full path: '${absPath}')\n";
|
1919
1919
|
}
|
1920
1920
|
} elsif ($verbose and $verbose > 1) {
|
1921
|
-
print $vout "Imported entry for '${_}' (
|
1921
|
+
print $vout "Imported entry for '${_}' (no full path)\n";
|
1922
1922
|
}
|
1923
1923
|
}
|
1924
1924
|
}
|
@@ -2172,7 +2172,10 @@ sub GetImageInfo($$)
|
|
2172
2172
|
}
|
2173
2173
|
# can't make use of $info if verbose because we must reprocess
|
2174
2174
|
# the file anyway to generate the verbose output
|
2175
|
-
|
2175
|
+
if ($verbose or defined $fastCondition or defined $diff) {
|
2176
|
+
undef $info;
|
2177
|
+
--$$et{FILE_SEQUENCE};
|
2178
|
+
}
|
2176
2179
|
} elsif ($file =~ s/^(\@JSON:)(.*)/$1/) {
|
2177
2180
|
# read JSON file from command line
|
2178
2181
|
my $dat = $2;
|
@@ -4163,11 +4166,12 @@ sub ScanDir($$;$)
|
|
4163
4166
|
}
|
4164
4167
|
$dir =~ /\/$/ or $dir .= '/'; # make sure directory name ends with '/'
|
4165
4168
|
foreach $file (@fileList) {
|
4169
|
+
next if $file eq '.' or $file eq '..';
|
4166
4170
|
my $path = "$dir$file";
|
4167
4171
|
if ($et->IsDirectory($path)) {
|
4168
4172
|
next unless $recurse;
|
4169
4173
|
# ignore directories starting with "." by default
|
4170
|
-
next if $file =~ /^\./ and
|
4174
|
+
next if $file =~ /^\./ and $recurse == 1;
|
4171
4175
|
next if $ignore{$file} or ($ignore{SYMLINKS} and -l $path);
|
4172
4176
|
ScanDir($et, $path, $list);
|
4173
4177
|
last if $end;
|
@@ -4315,14 +4319,15 @@ sub AbsPath($)
|
|
4315
4319
|
{
|
4316
4320
|
my $file = shift;
|
4317
4321
|
my $path;
|
4318
|
-
if (defined $file
|
4319
|
-
|
4320
|
-
|
4321
|
-
|
4322
|
-
|
4323
|
-
$
|
4324
|
-
$path =
|
4322
|
+
if (defined $file) {
|
4323
|
+
return undef if $file eq '*'; # (CSV SourceFile may be '*' -- no absolute path for that)
|
4324
|
+
if ($^O eq 'MSWin32' and $mt->Options('WindowsLongPath')) {
|
4325
|
+
$path = $mt->WindowsLongPath($file);
|
4326
|
+
} elsif (eval { require Cwd }) {
|
4327
|
+
local $SIG{'__WARN__'} = sub { };
|
4328
|
+
$path = eval { Cwd::abs_path($file) };
|
4325
4329
|
}
|
4330
|
+
$path =~ tr/\\/\// if $^O eq 'MSWin32' and defined $path; # use forward slashes
|
4326
4331
|
}
|
4327
4332
|
return $path;
|
4328
4333
|
}
|
@@ -5907,7 +5912,7 @@ with this command:
|
|
5907
5912
|
|
5908
5913
|
produces output like this:
|
5909
5914
|
|
5910
|
-
-- Generated by ExifTool 13.
|
5915
|
+
-- Generated by ExifTool 13.06 --
|
5911
5916
|
File: a.jpg - 2003:10:31 15:44:19
|
5912
5917
|
(f/5.6, 1/60s, ISO 100)
|
5913
5918
|
File: b.jpg - 2006:05:23 11:57:38
|
@@ -6022,7 +6027,8 @@ import). May be combined with B<-s> to print tag names instead of
|
|
6022
6027
|
descriptions, or B<-S> to print tag values only, tab-delimited on a single
|
6023
6028
|
line. The B<-t> option may be combined with B<-j>, B<-php> or B<-X> to add
|
6024
6029
|
tag table information (C<table>, tag C<id>, and C<index> for cases where
|
6025
|
-
multiple conditional tags exist with the same ID)
|
6030
|
+
multiple conditional tags exist with the same ID), which allows the
|
6031
|
+
corresponding tag to be located in the B<-listx> output.
|
6026
6032
|
|
6027
6033
|
=item B<-T> (B<-table>)
|
6028
6034
|
|
@@ -6781,14 +6787,14 @@ written (provided they can be calculated from the track log, and they are
|
|
6781
6787
|
supported by the destination metadata format): GPSLatitude, GPSLatitudeRef,
|
6782
6788
|
GPSLongitude, GPSLongitudeRef, GPSAltitude, GPSAltitudeRef, GPSDateStamp,
|
6783
6789
|
GPSTimeStamp, GPSDateTime, GPSTrack, GPSTrackRef, GPSSpeed, GPSSpeedRef,
|
6784
|
-
GPSImgDirection, GPSImgDirectionRef,
|
6785
|
-
AmbientTemperature and CameraElevationAngle. By
|
6786
|
-
tags are created in EXIF, and updated in XMP only if
|
6787
|
-
QuickTime-format files GPSCoordinates is created in
|
6788
|
-
(ItemList by default) as well as in XMP. However,
|
6789
|
-
C<XMP:Geotime> or C<QuickTime:Geotime> may be specified to
|
6790
|
-
only to one group. Also, C<ItemList:Geotime>, C<Keys:Geotime>
|
6791
|
-
C<UserData:Geotime> may be used to write to a specific location in
|
6790
|
+
GPSImgDirection, GPSImgDirectionRef, GPSMeasureMode, GPSDOP, GPSPitch,
|
6791
|
+
GPSRoll, GPSCoordinates, AmbientTemperature and CameraElevationAngle. By
|
6792
|
+
default, in image files tags are created in EXIF, and updated in XMP only if
|
6793
|
+
they already exist. In QuickTime-format files GPSCoordinates is created in
|
6794
|
+
the preferred location (ItemList by default) as well as in XMP. However,
|
6795
|
+
C<EXIF:Geotime>, C<XMP:Geotime> or C<QuickTime:Geotime> may be specified to
|
6796
|
+
write to write only to one group. Also, C<ItemList:Geotime>, C<Keys:Geotime>
|
6797
|
+
or C<UserData:Geotime> may be used to write to a specific location in
|
6792
6798
|
QuickTime-format files. Note that GPSPitch and GPSRoll are non-standard,
|
6793
6799
|
and require user-defined tags in order to be written.
|
6794
6800
|
|
@@ -15,11 +15,11 @@ use Image::ExifTool qw(:DataAccess :Utils);
|
|
15
15
|
use Image::ExifTool::Exif;
|
16
16
|
use Image::ExifTool::XMP;
|
17
17
|
use Image::ExifTool::GPS;
|
18
|
+
use Image::ExifTool::Protobuf;
|
18
19
|
|
19
|
-
$VERSION = '1.
|
20
|
+
$VERSION = '1.10';
|
20
21
|
|
21
22
|
sub ProcessDJIInfo($$$);
|
22
|
-
sub Process_djmd($$$);
|
23
23
|
|
24
24
|
my %convFloat2 = (
|
25
25
|
PrintConv => 'sprintf("%+.2f", $val)',
|
@@ -187,34 +187,96 @@ my %convFloat2 = (
|
|
187
187
|
},
|
188
188
|
);
|
189
189
|
|
190
|
-
#
|
191
|
-
%Image::ExifTool::DJI::
|
192
|
-
|
190
|
+
# metadata in protobuf format (djmd and dbgi meta types, ref PH)
|
191
|
+
%Image::ExifTool::DJI::Protobuf = (
|
192
|
+
GROUPS => { 0 => 'Protobuf', 1 => 'DJI', 2 => 'Location' },
|
193
|
+
TAG_PREFIX => '',
|
194
|
+
PROCESS_PROC => \&Image::ExifTool::Protobuf::ProcessProtobuf,
|
195
|
+
NOTES => q{
|
196
|
+
Tags found in protobuf-format DJI meta djmd and dbgi timed metadata. Only a
|
197
|
+
few tags are currently known, but unknown djmd tags may be extracted by
|
198
|
+
setting the Unknown option to 1 (or 2 to also extract unknown dbgi debug
|
199
|
+
tags). Tag ID's are composed of the corresponding .proto file name combined
|
200
|
+
with the hierarchical protobuf field numbers. The "dvtm_AVATA2.proto" file
|
201
|
+
is used by the DJI Avanta 2, and "dvtm_ac203.proto" by the OsmoAction4.
|
202
|
+
},
|
203
|
+
Protocol => { },
|
204
|
+
# dvtm_ac203_1-1-6 - some version number
|
205
|
+
'dvtm_ac203_1-1-10' => 'Model',
|
206
|
+
'dvtm_ac203_2-3-1' => { Name => 'FrameWidth', Format => 'unsigned' },
|
207
|
+
'dvtm_ac203_2-3-2' => { Name => 'FrameHeight', Format => 'unsigned' },
|
208
|
+
'dvtm_ac203_2-3-3' => { Name => 'FrameRate', Format => 'float' },
|
209
|
+
# dvtm_ac203_3-4-1-4 - model code?
|
210
|
+
'dvtm_ac203_3-4-2-1-1' => {
|
211
|
+
Name => 'CoordinateUnits',
|
212
|
+
Format => 'unsigned',
|
213
|
+
# don't extract this -- just convert to degrees
|
214
|
+
RawConv => '$$self{CoordUnits} = $val; undef',
|
215
|
+
Hidden => 1,
|
216
|
+
# PrintConv => { 0 => 'Radians', 1 => 'Degrees' },
|
217
|
+
},
|
218
|
+
'dvtm_ac203_3-4-2-1-2' => {
|
219
|
+
Name => 'GPSLatitude',
|
220
|
+
Format => 'double',
|
221
|
+
# set ExifTool GPSLatitude/GPSLongitude members so GPSDateTime will be generated if necessary
|
222
|
+
RawConv => '$$self{GPSLatitude} = $$self{CoordUnits} ? $val : $val * 180 / 3.141592653589793', # (NC)
|
223
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
224
|
+
},
|
225
|
+
'dvtm_ac203_3-4-2-1-3' => {
|
226
|
+
Name => 'GPSLongitude',
|
227
|
+
Format => 'double',
|
228
|
+
RawConv => '$$self{GPSLongitude} = $$self{CoordUnits} ? $val : $val * 180 / 3.141592653589793', # (NC)
|
229
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
|
230
|
+
},
|
231
|
+
'dvtm_ac203_3-4-2-2' => {
|
232
|
+
Name => 'GPSAltitude',
|
233
|
+
Format => 'unsigned',
|
234
|
+
ValueConv => '$val / 1000',
|
235
|
+
},
|
236
|
+
'dvtm_ac203_3-4-2-6-1' => {
|
237
|
+
Name => 'GPSDateTime',
|
238
|
+
Format => 'string',
|
239
|
+
Groups => { 2 => 'Time' },
|
240
|
+
RawConv => '$$self{GPSDateTime} = $val',
|
241
|
+
ValueConv => '$val =~ tr/-/:/; $val',
|
242
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
243
|
+
},
|
244
|
+
# dvtm_AVATA2_1-1-2 - some version number
|
245
|
+
# dvtm_AVATA2_1-1-3 - some version number
|
246
|
+
'dvtm_AVATA2_1-1-10' => 'Model',
|
247
|
+
'dvtm_AVATA2_2-2-3-1' => 'SerialNumber', # (NC)
|
248
|
+
'dvtm_AVATA2_2-3-1' => { Name => 'FrameWidth', Format => 'unsigned' },
|
249
|
+
'dvtm_AVATA2_2-3-2' => { Name => 'FrameHeight', Format => 'unsigned' },
|
250
|
+
'dvtm_AVATA2_2-3-3' => { Name => 'FrameRate', Format => 'float' },
|
251
|
+
# dvtm_AVATA2_3-1-1 - frame number (starting at 1)
|
252
|
+
'dvtm_AVATA2_3-1-2' => { # (also 3-2-1-6 and 3-4-1-6)
|
253
|
+
Name => 'TimeStamp',
|
254
|
+
Format => 'unsigned',
|
255
|
+
# milliseconds, but I don't know what the zero is
|
256
|
+
ValueConv => '$val / 1e6',
|
257
|
+
},
|
258
|
+
# dvtm_AVATA2_3-2-1-4 - model code?
|
259
|
+
# dvtm_AVATA2_3-4-1-4 - model code?
|
260
|
+
'dvtm_AVATA2_3-4-4-1-1' => { # (NC) (default seems to be radians if missing)
|
261
|
+
Name => 'CoordinateDegrees',
|
262
|
+
Format => 'unsigned',
|
263
|
+
RawConv => '$$self{CoordDegrees} = $val; undef',
|
264
|
+
Hidden => 1,
|
265
|
+
},
|
266
|
+
'dvtm_AVATA2_3-4-4-1-2' => {
|
267
|
+
Name => 'GPSLatitude',
|
268
|
+
Format => 'double',
|
269
|
+
RawConv => '$$self{GPSLatitude} = $$self{CoordDegrees} ? $val : $val * 180 / 3.141592653589793', # (NC)
|
270
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
271
|
+
},
|
272
|
+
'dvtm_AVATA2_3-4-4-1-3' => {
|
273
|
+
Name => 'GPSLongitude',
|
274
|
+
Format => 'double',
|
275
|
+
RawConv => '$$self{GPSLongitude} = $$self{CoordDegrees} ? $val : $val * 180 / 3.141592653589793', # (NC)
|
276
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
|
277
|
+
},
|
193
278
|
);
|
194
279
|
|
195
|
-
#------------------------------------------------------------------------------
|
196
|
-
# Process DJI djmd timed data from Action4 videos (ref PH)
|
197
|
-
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
198
|
-
# Returns: 1 on success
|
199
|
-
# TODO: work in progress
|
200
|
-
sub Process_djmd($$$)
|
201
|
-
{
|
202
|
-
my ($et, $dirInfo, $tagTbl) = @_;
|
203
|
-
my $dataPt = $$dirInfo{DataPt};
|
204
|
-
my ($pos, $bit, $val) = (6, 0, 0);
|
205
|
-
for (;;) {
|
206
|
-
my $v = Get8u($dataPt, $pos);
|
207
|
-
$val += ($v & 0x7f) << $bit;
|
208
|
-
last unless $v & 0x80;
|
209
|
-
++$pos;
|
210
|
-
$bit += 7;
|
211
|
-
}
|
212
|
-
$pos += 49;
|
213
|
-
my @a = unpack("x${pos}fxfxfxfx3fxfxf", $$dataPt);
|
214
|
-
print "$val @a\n";
|
215
|
-
return 1;
|
216
|
-
}
|
217
|
-
|
218
280
|
#------------------------------------------------------------------------------
|
219
281
|
# Process DJI info (ref PH)
|
220
282
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
@@ -18,7 +18,8 @@
|
|
18
18
|
# specifying $Image::ExifTool::Geolocation::altDir. This
|
19
19
|
# database and has entries in the same order as Geolocation.dat,
|
20
20
|
# and each entry is a newline-separated list of alternate names
|
21
|
-
# terminated by a null byte.
|
21
|
+
# terminated by a null byte. These alternate names are used
|
22
|
+
# only when searching for a city by name (eg. "Big Apple").
|
22
23
|
#
|
23
24
|
# Databases are based on data from geonames.org with a
|
24
25
|
# Creative Commons license, reformatted as follows in the
|
@@ -16,7 +16,7 @@ use vars qw($VERSION);
|
|
16
16
|
use Image::ExifTool qw(:DataAccess :Utils);
|
17
17
|
use Image::ExifTool::QuickTime;
|
18
18
|
|
19
|
-
$VERSION = '1.
|
19
|
+
$VERSION = '1.11';
|
20
20
|
|
21
21
|
sub ProcessGoPro($$$);
|
22
22
|
sub ProcessString($$$);
|
@@ -423,7 +423,7 @@ my %addUnits = (
|
|
423
423
|
},
|
424
424
|
},
|
425
425
|
# VLTA (GPMF) - seen: 78 ('N') (fmt B -- wrong format?)
|
426
|
-
|
426
|
+
VFPS => { Name => 'VideoFrameRate', PrintConv => '$val=~s( )(/);$val' }, #PH (GPMF, fmt L)
|
427
427
|
VFRH => { #PH (Karma)
|
428
428
|
Name => 'VisualFlightRulesHUD',
|
429
429
|
BinaryData => 1,
|
@@ -431,7 +431,7 @@ my %addUnits = (
|
|
431
431
|
# TYPE=ffffsS
|
432
432
|
},
|
433
433
|
# VLTE (GPMF) - seen: 'Y','N' (fmt c)
|
434
|
-
|
434
|
+
VRES => { Name => 'VideoFrameSize', PrintConv => '$val=~s/ /x/;$val' }, #PH (GPMF, fmt L)
|
435
435
|
WBAL => 'ColorTemperatures', #PH (gpmd)
|
436
436
|
WRGB => { #PH (gpmd)
|
437
437
|
Name => 'WhiteBalanceRGB',
|
@@ -11,7 +11,7 @@ use strict;
|
|
11
11
|
use vars qw($VERSION);
|
12
12
|
use Image::ExifTool qw(:DataAccess :Utils);
|
13
13
|
|
14
|
-
$VERSION = '1.
|
14
|
+
$VERSION = '1.38';
|
15
15
|
|
16
16
|
sub ProcessOcad($$$);
|
17
17
|
sub ProcessJPEG_HDR($$$);
|
@@ -87,6 +87,11 @@ sub ProcessJPEG_HDR($$$);
|
|
87
87
|
Name => 'InfiRayVersion',
|
88
88
|
Condition => '$$valPt =~ /^....IJPEG\0/s',
|
89
89
|
SubDirectory => { TagTable => 'Image::ExifTool::InfiRay::Version' },
|
90
|
+
}, {
|
91
|
+
Name => 'UniformResourceName',
|
92
|
+
Groups => { 1 => 'APP2' },
|
93
|
+
Condition => '$$valPt =~ /^urn:/',
|
94
|
+
Notes => 'used in Apple HDR images',
|
90
95
|
}, {
|
91
96
|
Name => 'PreviewImage',
|
92
97
|
Condition => '$$valPt =~ /^(|QVGA\0|BGTH)\xff\xd8\xff\xdb/',
|
@@ -246,16 +251,26 @@ sub ProcessJPEG_HDR($$$);
|
|
246
251
|
Condition => '$$valPt =~ /^SEAL\0/',
|
247
252
|
SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
|
248
253
|
}],
|
249
|
-
APP10 => {
|
254
|
+
APP10 => [{
|
250
255
|
Name => 'Comment',
|
251
256
|
Condition => '$$valPt =~ /^UNICODE\0/',
|
252
257
|
Notes => 'PhotoStudio Unicode comment',
|
253
|
-
|
258
|
+
}, {
|
259
|
+
Name => 'HDRGainCurve', #PH (NC)
|
260
|
+
Condition => '$$valPt =~ /^AROT\0\0.{4}/s',
|
261
|
+
Groups => { 1 => 'APP10', 2 => 'Image' },
|
262
|
+
ValueConv => q{
|
263
|
+
my $n = unpack('x6N', $val);
|
264
|
+
return '<truncated AROT data>' if length($val)-6 < $n * 4;
|
265
|
+
my $str = join ' ', unpack("x10V$n", $val);
|
266
|
+
return \$str;
|
267
|
+
},
|
268
|
+
}],
|
254
269
|
APP11 => [{
|
255
270
|
Name => 'JPEG-HDR',
|
256
271
|
Condition => '$$valPt =~ /^HDR_RI /',
|
257
272
|
SubDirectory => { TagTable => 'Image::ExifTool::JPEG::HDR' },
|
258
|
-
|
273
|
+
}, {
|
259
274
|
Name => 'JUMBF',
|
260
275
|
Condition => '$$valPt =~ /^JP/',
|
261
276
|
SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::Main' },
|
@@ -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
|