exiftool_vendored 13.04.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 +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
|