exiftool_vendored 13.42.0 → 13.45.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 +44 -1
- data/bin/MANIFEST +2 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +48 -48
- data/bin/config_files/example.config +5 -2
- data/bin/exiftool +96 -84
- data/bin/lib/Image/ExifTool/Apple.pm +0 -1
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +4 -4
- data/bin/lib/Image/ExifTool/Canon.pm +24 -1
- data/bin/lib/Image/ExifTool/CanonCustom.pm +1 -1
- data/bin/lib/Image/ExifTool/DarwinCore.pm +2 -2
- data/bin/lib/Image/ExifTool/EXE.pm +1 -1
- data/bin/lib/Image/ExifTool/Exif.pm +4 -2
- data/bin/lib/Image/ExifTool/FLIR.pm +1 -1
- data/bin/lib/Image/ExifTool/FlashPix.pm +1 -1
- data/bin/lib/Image/ExifTool/FujiFilm.pm +2 -2
- data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
- data/bin/lib/Image/ExifTool/Geolocation.pm +1 -1
- data/bin/lib/Image/ExifTool/Geotag.pm +3 -3
- data/bin/lib/Image/ExifTool/Google.pm +2 -2
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +0 -1
- data/bin/lib/Image/ExifTool/Import.pm +1 -1
- data/bin/lib/Image/ExifTool/JPEG.pm +5 -1
- data/bin/lib/Image/ExifTool/Jpeg2000.pm +1 -1
- data/bin/lib/Image/ExifTool/Kandao.pm +399 -0
- data/bin/lib/Image/ExifTool/LNK.pm +1 -1
- data/bin/lib/Image/ExifTool/MRC.pm +4 -4
- data/bin/lib/Image/ExifTool/MWG.pm +1 -1
- data/bin/lib/Image/ExifTool/MacOS.pm +1 -2
- data/bin/lib/Image/ExifTool/Matroska.pm +56 -15
- data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
- data/bin/lib/Image/ExifTool/Nikon.pm +13 -15
- data/bin/lib/Image/ExifTool/NikonCustom.pm +10 -8
- data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
- data/bin/lib/Image/ExifTool/PPM.pm +1 -1
- data/bin/lib/Image/ExifTool/Panasonic.pm +30 -6
- data/bin/lib/Image/ExifTool/Pentax.pm +9 -1
- data/bin/lib/Image/ExifTool/PhaseOne.pm +17 -1
- data/bin/lib/Image/ExifTool/Plot.pm +2 -2
- data/bin/lib/Image/ExifTool/Protobuf.pm +42 -16
- data/bin/lib/Image/ExifTool/QuickTime.pm +48 -8
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +2 -2
- data/bin/lib/Image/ExifTool/README +5 -5
- data/bin/lib/Image/ExifTool/RIFF.pm +5 -4
- data/bin/lib/Image/ExifTool/Reconyx.pm +2 -2
- data/bin/lib/Image/ExifTool/Samsung.pm +11 -1
- data/bin/lib/Image/ExifTool/Sony.pm +29 -3
- data/bin/lib/Image/ExifTool/TNEF.pm +2 -2
- data/bin/lib/Image/ExifTool/TagLookup.pm +7129 -7108
- data/bin/lib/Image/ExifTool/TagNames.pod +259 -111
- data/bin/lib/Image/ExifTool/Text.pm +1 -1
- data/bin/lib/Image/ExifTool/Trailer.pm +1 -1
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +10 -2
- data/bin/lib/Image/ExifTool/Writer.pl +9 -5
- data/bin/lib/Image/ExifTool/XMP.pm +4 -2
- data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
- data/bin/lib/Image/ExifTool.pm +33 -26
- data/bin/lib/Image/ExifTool.pod +88 -79
- data/bin/perl-Image-ExifTool.spec +47 -47
- data/lib/exiftool_vendored/version.rb +1 -1
- metadata +2 -1
|
@@ -876,7 +876,7 @@ my %faceCategories = (
|
|
|
876
876
|
0x144a => { Name => 'WBRed', Writable => 'int16u' },
|
|
877
877
|
0x144b => { Name => 'WBGreen', Writable => 'int16u' },
|
|
878
878
|
0x144c => { Name => 'WBBlue', Writable => 'int16u' },
|
|
879
|
-
|
|
879
|
+
|
|
880
880
|
0x144d => { Name => 'RollAngle', Writable => 'rational64s' }, #forum14319
|
|
881
881
|
0x3803 => { #forum10037
|
|
882
882
|
Name => 'VideoRecordingMode',
|
|
@@ -1942,7 +1942,7 @@ sub ProcessRAF($$)
|
|
|
1942
1942
|
$et->SetFileType() unless $$et{DOC_NUM};
|
|
1943
1943
|
my $tbl = GetTagTable('Image::ExifTool::FujiFilm::RAFHeader');
|
|
1944
1944
|
$et->ProcessDirectory({ DataPt => \$buff, DirName => 'RAFHeader', Base => $base }, $tbl);
|
|
1945
|
-
|
|
1945
|
+
|
|
1946
1946
|
# extract information from embedded JPEG
|
|
1947
1947
|
my %dirInfo = (
|
|
1948
1948
|
Parent => 'RAF',
|
|
Binary file
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# Notes: Set $Image::ExifTool::Geolocation::geoDir to override the
|
|
13
13
|
# default directory containing the database file Geolocation.dat
|
|
14
14
|
# and the GeoLang directory with the alternate language files.
|
|
15
|
-
# If set, this directory is
|
|
15
|
+
# If set, this directory is
|
|
16
16
|
#
|
|
17
17
|
# AltNames.dat may be loaded from a different directory by
|
|
18
18
|
# specifying $Image::ExifTool::Geolocation::altDir. This
|
|
@@ -35,7 +35,7 @@ use vars qw($VERSION);
|
|
|
35
35
|
use Image::ExifTool qw(:Public);
|
|
36
36
|
use Image::ExifTool::GPS;
|
|
37
37
|
|
|
38
|
-
$VERSION = '1.
|
|
38
|
+
$VERSION = '1.84';
|
|
39
39
|
|
|
40
40
|
sub JITTER() { return 2 } # maximum time jitter
|
|
41
41
|
|
|
@@ -1269,7 +1269,7 @@ Category: foreach $category (qw{pos track alt orient atemp err dop}) {
|
|
|
1269
1269
|
my $tag = ($$nvHash{WantGroup} ? "$$nvHash{WantGroup}:" : '') . 'Geolocate';
|
|
1270
1270
|
# pass along any regular expressions to qualify geolocation search
|
|
1271
1271
|
my $parms = join ',', grep m(/), split /\s*,\s*/, $geoloc;
|
|
1272
|
-
$parms and $parms = ",$parms,both";
|
|
1272
|
+
$parms and $parms = ",$parms,both";
|
|
1273
1273
|
$et->SetNewValue($tag => "$$fix{lat},$$fix{lon}$parms");
|
|
1274
1274
|
# (the Geolocate tag will be restored to its original value
|
|
1275
1275
|
# by RestoreNewValues before the next file in batch processing)
|
|
@@ -1530,7 +1530,7 @@ sub PrintFixTime($)
|
|
|
1530
1530
|
{
|
|
1531
1531
|
my $time = $_[0] + 0.0005; # round off to nearest ms
|
|
1532
1532
|
my $fsec = int(($time - int($time)) * 1000);
|
|
1533
|
-
return
|
|
1533
|
+
return Image::ExifTool::ConvertUnixTime($time, undef, 3) . ' UTC';
|
|
1534
1534
|
}
|
|
1535
1535
|
|
|
1536
1536
|
#------------------------------------------------------------------------------
|
|
@@ -47,7 +47,7 @@ my %sEarthPose = (
|
|
|
47
47
|
NAMESPACE => { EarthPose => 'http://ns.google.com/photos/dd/1.0/earthpose/' },
|
|
48
48
|
Latitude => {
|
|
49
49
|
Writable => 'real',
|
|
50
|
-
Groups => { 2 => 'Location' },
|
|
50
|
+
Groups => { 2 => 'Location' },
|
|
51
51
|
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
|
52
52
|
ValueConvInv => '$val',
|
|
53
53
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
|
@@ -512,7 +512,7 @@ my %sAppInfo = (
|
|
|
512
512
|
GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
|
|
513
513
|
TAG_PREFIX => 'HDRPlusMakerNote',
|
|
514
514
|
PROCESS_PROC => \&ProcessHDRP,
|
|
515
|
-
VARS => {
|
|
515
|
+
VARS => {
|
|
516
516
|
ID_FMT => 'str',
|
|
517
517
|
SORT_PROC => sub {
|
|
518
518
|
my ($a,$b) = @_;
|
|
@@ -335,7 +335,7 @@ Read CSV or JSON file into a database hash.
|
|
|
335
335
|
|
|
336
336
|
1) Hash reference for database object.
|
|
337
337
|
|
|
338
|
-
2) Optional string used to represent an undefined (missing) tag value.
|
|
338
|
+
2) Optional string used to represent an undefined (missing) tag value.
|
|
339
339
|
(Used for deleting tags.)
|
|
340
340
|
|
|
341
341
|
3) For ReadCSV this gives the delimiter for CSV entries, with a default of
|
|
@@ -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.40';
|
|
15
15
|
|
|
16
16
|
sub ProcessOcad($$$);
|
|
17
17
|
sub ProcessJPEG_HDR($$$);
|
|
@@ -212,6 +212,10 @@ sub ProcessJPEG_HDR($$$);
|
|
|
212
212
|
Name => 'Pentax',
|
|
213
213
|
Condition => '$$valPt =~ /^PENTAX \0/',
|
|
214
214
|
SubDirectory => { TagTable => 'Image::ExifTool::Pentax::Main' },
|
|
215
|
+
}, {
|
|
216
|
+
Name => 'Ricoh',
|
|
217
|
+
Condition => '$$valPt =~ /^RICOH\0/',
|
|
218
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Pentax::Main' },
|
|
215
219
|
}, {
|
|
216
220
|
Name => 'Huawei',
|
|
217
221
|
Condition => '$$valPt =~ /^HUAWEI\0\0/',
|
|
@@ -132,7 +132,7 @@ my %j2cMarker = (
|
|
|
132
132
|
NOTES => q{
|
|
133
133
|
The tags below are found in JPEG 2000 images and the C2PA CAI JUMBF metadata
|
|
134
134
|
in various file types (see below). Note that ExifTool currently writes only
|
|
135
|
-
EXIF, IPTC and XMP tags in Jpeg2000 images, and EXIF and XMP in JXL images.
|
|
135
|
+
EXIF, IPTC and XMP tags in Jpeg2000 images, and EXIF and XMP in JXL images.
|
|
136
136
|
ExifTool will read/write Brotli-compressed EXIF and XMP in JXL images, but
|
|
137
137
|
the API L<Compress|../ExifTool.html#Compress> option must be set to create new EXIF and XMP in compressed
|
|
138
138
|
format.
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
#------------------------------------------------------------------------------
|
|
2
|
+
# File: Kandao.pm
|
|
3
|
+
#
|
|
4
|
+
# Description: Read Kandao MP4 metadata
|
|
5
|
+
#
|
|
6
|
+
# Revisions: 2025-12-10 - P. Harvey Created
|
|
7
|
+
#
|
|
8
|
+
# Notes: Tested with videos from the Kandao QooCam 3 Ultra
|
|
9
|
+
#------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
package Image::ExifTool::Kandao;
|
|
12
|
+
|
|
13
|
+
use strict;
|
|
14
|
+
use vars qw($VERSION);
|
|
15
|
+
use Image::ExifTool qw(:DataAccess :Utils);
|
|
16
|
+
|
|
17
|
+
$VERSION = '1.00';
|
|
18
|
+
|
|
19
|
+
sub ProcessKandao($$$);
|
|
20
|
+
sub ProcessKVAR($$);
|
|
21
|
+
|
|
22
|
+
# Kandao format codes
|
|
23
|
+
my %format = (
|
|
24
|
+
CHAR => 'string',
|
|
25
|
+
BOOL => 'int8u',
|
|
26
|
+
U8 => 'int8u',
|
|
27
|
+
U16 => 'int16u',
|
|
28
|
+
U32 => 'int32u',
|
|
29
|
+
U64 => 'int64u',
|
|
30
|
+
S8 => 'int8s',
|
|
31
|
+
S16 => 'int16s',
|
|
32
|
+
S32 => 'int32s',
|
|
33
|
+
S64 => 'int64s',
|
|
34
|
+
FLOAT => 'float',
|
|
35
|
+
DOUBLE => 'double',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
# Kandao 'kvar' and 'kfix' information
|
|
39
|
+
%Image::ExifTool::Kandao::Main = (
|
|
40
|
+
GROUPS => { 0 => 'Kandao', 1 => 'KVAR', 2 => 'Camera' },
|
|
41
|
+
VARS => { NO_LOOKUP => 1 },
|
|
42
|
+
NOTES => q{
|
|
43
|
+
Tags extracted from Kandao KVAR files and the 'kvar', 'kfix' and 'kstb'
|
|
44
|
+
atoms in Kandao MP4 videos, with a family 1 group name of KVAR, KFIX or KSTB
|
|
45
|
+
depending on their location.
|
|
46
|
+
},
|
|
47
|
+
PROCESS_PROC => \&ProcessKandao,
|
|
48
|
+
#
|
|
49
|
+
# 'kvar' tags
|
|
50
|
+
#
|
|
51
|
+
CPU_TEMP => { Name => 'CPUTemperature', ValueConv => '$val / 10' }, # U32[1]
|
|
52
|
+
BAT_TEMP => 'BatteryTemperature', # U32[1]
|
|
53
|
+
PTS_UNIT => { # U32[1]
|
|
54
|
+
Name => 'TimeStampUnit',
|
|
55
|
+
PrintConv => { 0 => 'ms', 1 => 'Subtle', 2 => 'ns' },
|
|
56
|
+
},
|
|
57
|
+
TOTAL_FRAME => 'TotalFrames', # U32[1]
|
|
58
|
+
TOTAL_TIME_MS => { # U32[1]
|
|
59
|
+
Name => 'TotalTime',
|
|
60
|
+
ValueConv => '$val / 1000',
|
|
61
|
+
},
|
|
62
|
+
LENS => 'LensData', # CHAR[534]
|
|
63
|
+
DASHBOARD => 'Dashboard', # U8[1]
|
|
64
|
+
PROJECTION => 'Projection', # CHAR[16]
|
|
65
|
+
CENTER_SHIFT => 'CenterShift', # DOUBLE[1]
|
|
66
|
+
DISTORTION => 'Distortion', # DOUBLE[1]
|
|
67
|
+
INFO => 'Info', # CHAR[239]
|
|
68
|
+
PTS => {
|
|
69
|
+
Name => 'PresentationTimeStamp',
|
|
70
|
+
Notes => 'TimeStamp for each frame',
|
|
71
|
+
Format => 'undef', # U64[x]
|
|
72
|
+
Binary => 1,
|
|
73
|
+
},
|
|
74
|
+
LENS_SN0 => 'Lens0SerialNumber', # CHAR[15]
|
|
75
|
+
LENS_SN1 => 'Lens1SerialNumber', # CHAR[15]
|
|
76
|
+
LENS_OTP_ID0 => 'Lens0OTP_ID', # U8[1]
|
|
77
|
+
LENS_OTP_ID1 => 'Lens1OTP_ID', # U8[1]
|
|
78
|
+
IMU => {
|
|
79
|
+
RecordSize => 20,
|
|
80
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Kandao::IMU' },
|
|
81
|
+
},
|
|
82
|
+
GPS => [{
|
|
83
|
+
Condition => '$$valPt =~ /^\xff{4}/',
|
|
84
|
+
RecordSize => 28,
|
|
85
|
+
SubDirectory => {
|
|
86
|
+
TagTable => 'Image::ExifTool::Kandao::GPS',
|
|
87
|
+
Start => 4,
|
|
88
|
+
},
|
|
89
|
+
},{
|
|
90
|
+
RecordSize => 20,
|
|
91
|
+
SubDirectory => {
|
|
92
|
+
TagTable => 'Image::ExifTool::Kandao::GPS',
|
|
93
|
+
},
|
|
94
|
+
}],
|
|
95
|
+
GPSX => {
|
|
96
|
+
RecordSize => 36,
|
|
97
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Kandao::GPSX' },
|
|
98
|
+
},
|
|
99
|
+
EXP => { # U8[x]
|
|
100
|
+
# actually int64u - monotonically increasing
|
|
101
|
+
Name => 'Exp',
|
|
102
|
+
},
|
|
103
|
+
# (ISP = Image Signal Processor)
|
|
104
|
+
ISP => 'ISP', # U8 - 46-byte records (not very interesting looking)
|
|
105
|
+
FRAME_ISP => {
|
|
106
|
+
# U8 - 12-byte records: int32u-ts, float, float
|
|
107
|
+
RecordSize => 12,
|
|
108
|
+
SubDirectory => { TagTable => 'Image::ExifTool::Kandao::FrameISP' },
|
|
109
|
+
},
|
|
110
|
+
GAINMAP0 => 'GainMap0',
|
|
111
|
+
GAINMAP1 => 'GainMap1',
|
|
112
|
+
#
|
|
113
|
+
# 'kfix' tags
|
|
114
|
+
#
|
|
115
|
+
PRODUCT => 'Model',
|
|
116
|
+
PROJECT => 'Project',
|
|
117
|
+
SN => 'SerialNumber',
|
|
118
|
+
PR_VER => 'ProductVersion',
|
|
119
|
+
SW_VER => 'SoftwareVersion',
|
|
120
|
+
HW_VER => 'HardwareVersion',
|
|
121
|
+
VIDEO_CAPTIME => {
|
|
122
|
+
Name => 'VideoCaptureTime',
|
|
123
|
+
Notes => 'local camera time',
|
|
124
|
+
Groups => { 2 => 'Time' },
|
|
125
|
+
ValueConv => '$val =~ s/^(\d{4})-(\d{2})-/$1:$2:/; $val',
|
|
126
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
|
127
|
+
},
|
|
128
|
+
GPS_CAPTIME => {
|
|
129
|
+
Name => 'GPSCaptureTime',
|
|
130
|
+
Groups => { 2 => 'Time' },
|
|
131
|
+
ValueConv => 'ConvertUnixTime($val/1000, 0, 3) . "Z"',
|
|
132
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
|
133
|
+
},
|
|
134
|
+
LENS_INDEX => { Name => 'LensIndex', Format => 'int16u' },
|
|
135
|
+
NEED_LSC => 'NeedLDSC',
|
|
136
|
+
# (also in 'kvar') CPU_TEMP => { Name => 'CPUTemperature', ValueConv => '$val / 10' },
|
|
137
|
+
# (also in 'kvar') BAT_TEMP => 'BatteryTemperature',
|
|
138
|
+
INPUT_INSERT => 'InputInsert',
|
|
139
|
+
VIDEO_RESOLUTION => 'VideoResolution',
|
|
140
|
+
VIDEO_CODECTYPE => 'VideoCodec',
|
|
141
|
+
VIDEO_BITRATE => { Name => 'VideoBitrate', PrintConv => '($val / 1e6) . " Mbps"' },
|
|
142
|
+
VIDEO_FORMAT => 'VideoFormat',
|
|
143
|
+
OUTPUT_INSERT => 'OutputInsert',
|
|
144
|
+
DYNAMIC_RANGE => 'DynamicRange',
|
|
145
|
+
AWB_CCT => 'AWB_CCT',
|
|
146
|
+
EV => 'EV',
|
|
147
|
+
ISO => 'ISO',
|
|
148
|
+
SHUTTER => 'Shutter',
|
|
149
|
+
IMAGE_STYLE => 'ImageStyle',
|
|
150
|
+
AE_METERING => 'AEMetering',
|
|
151
|
+
CAPTURE_MODE => 'CaptureMode',
|
|
152
|
+
HDR => 'HDR',
|
|
153
|
+
STITCHED => 'Stitched',
|
|
154
|
+
COVER_MODE => 'CoverMode',
|
|
155
|
+
STEREO => 'Stereo',
|
|
156
|
+
FOV => 'FOV',
|
|
157
|
+
AE_MODE => 'AEMode',
|
|
158
|
+
AF_FN => 'AF_FN',
|
|
159
|
+
AWB_MODE => 'AWBMode',
|
|
160
|
+
AUDIO_GAIN => 'AudioGain',
|
|
161
|
+
INTERVAL => 'Interval',
|
|
162
|
+
ISP_VER => 'ISPVersion',
|
|
163
|
+
YAW => 'Yaw',
|
|
164
|
+
FAN_LEVEL => 'FanLevel',
|
|
165
|
+
FAN_MODE => 'FanMode',
|
|
166
|
+
CUSTOMIZED => 'Customized',
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
%Image::ExifTool::Kandao::GPS = (
|
|
170
|
+
GROUPS => { 0 => 'Kandao', 1 => 'KVAR', 2 => 'Location' },
|
|
171
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
|
172
|
+
NOTES => q{
|
|
173
|
+
These tags are in the family 1 KVAR group instead of a GPS group to allow
|
|
174
|
+
them to be distinguished from the duplicate GPS tags in the GPSX table.
|
|
175
|
+
},
|
|
176
|
+
0 => { Name => 'TimeStamp', Format => 'int32u' },
|
|
177
|
+
4 => {
|
|
178
|
+
Name => 'GPSLatitude',
|
|
179
|
+
Format => 'double',
|
|
180
|
+
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
|
181
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
|
182
|
+
},
|
|
183
|
+
12 => {
|
|
184
|
+
Name => 'GPSLongitude',
|
|
185
|
+
Format => 'double',
|
|
186
|
+
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
|
187
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
|
|
188
|
+
},
|
|
189
|
+
20 => { # (optional)
|
|
190
|
+
Name => 'GPSAltitude',
|
|
191
|
+
Format => 'double',
|
|
192
|
+
PrintConv => '$_ = sprintf("%.6f", $val); s/\.?0+$//; "$_ m"',
|
|
193
|
+
},
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
%Image::ExifTool::Kandao::GPSX = (
|
|
197
|
+
GROUPS => { 0 => 'Kandao', 1 => 'GPS', 2 => 'Location' },
|
|
198
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
|
199
|
+
NOTES => q(
|
|
200
|
+
These tags are in the family 1 GPS group. They are duplicates of the tags
|
|
201
|
+
in the Kandao GPS table with the addition of GPSDateTime.
|
|
202
|
+
),
|
|
203
|
+
0 => { Name => 'TimeStamp', Format => 'int32u' },
|
|
204
|
+
4 => {
|
|
205
|
+
Name => 'GPSDateTime',
|
|
206
|
+
Description => 'GPS Date/Time',
|
|
207
|
+
Groups => { 2 => 'Time' },
|
|
208
|
+
Format => 'int64u',
|
|
209
|
+
ValueConv => 'ConvertUnixTime($val/1000, 0, 3) . "Z"',
|
|
210
|
+
PrintConv => '$self->ConvertDateTime($val)',
|
|
211
|
+
},
|
|
212
|
+
12 => {
|
|
213
|
+
Name => 'GPSLatitude',
|
|
214
|
+
Format => 'double',
|
|
215
|
+
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
|
216
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
|
217
|
+
},
|
|
218
|
+
20 => {
|
|
219
|
+
Name => 'GPSLongitude',
|
|
220
|
+
Format => 'double',
|
|
221
|
+
ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
|
|
222
|
+
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
|
|
223
|
+
},
|
|
224
|
+
28 => {
|
|
225
|
+
Name => 'GPSAltitude',
|
|
226
|
+
Format => 'double',
|
|
227
|
+
PrintConv => '$_ = sprintf("%.6f", $val); s/\.?0+$//; "$_ m"',
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
# IMU gyroscope data
|
|
232
|
+
%Image::ExifTool::Kandao::IMU = (
|
|
233
|
+
GROUPS => { 0 => 'Kandao', 1 => 'KVAR', 2 => 'Location' },
|
|
234
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
|
235
|
+
0 => { Name => 'TimeStamp', Format => 'int64u' },
|
|
236
|
+
8 => { Name => 'Gyroscope', Format => 'int16s[3]' }, # 2G max range
|
|
237
|
+
14=> { Name => 'Accelerometer', Format => 'int16s[3]' }, # 2000 deg/sec max range
|
|
238
|
+
# (Kandao docs mention optional 6-byte Magnetometer data here, but
|
|
239
|
+
# with no way to determine if it exists, and it isn't in my samples)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
# (ISP = Image Signal Processor?)
|
|
243
|
+
%Image::ExifTool::Kandao::FrameISP = (
|
|
244
|
+
GROUPS => { 0 => 'Kandao', 1 => 'KVAR', 2 => 'Other' },
|
|
245
|
+
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
|
246
|
+
0 => { Name => 'TimeStamp', Format => 'int32u', Unknown => 1 },
|
|
247
|
+
4 => { Name => 'FrameISP_4', Format => 'float[2]', Unknown => 1 },
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
#------------------------------------------------------------------------------
|
|
251
|
+
# Extract information from a Kandao 'kfix' and 'kvar' atoms
|
|
252
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo reference, 2) tag table ref
|
|
253
|
+
# Returns: 1 on success
|
|
254
|
+
sub ProcessKandao($$$)
|
|
255
|
+
{
|
|
256
|
+
my ($et, $dirInfo, $tagTbl) = @_;
|
|
257
|
+
my $raf = $$dirInfo{RAF};
|
|
258
|
+
$raf or $raf = File::RandomAccess->new($$dirInfo{DataPt});
|
|
259
|
+
my $dirName = $$dirInfo{DirName};
|
|
260
|
+
my $dataPos = ($$dirInfo{DataPos} || 0) + ($$dirInfo{Base} || 0);
|
|
261
|
+
my $verbose = $et->Options('Verbose');
|
|
262
|
+
my $ee = $et->Options('ExtractEmbedded');
|
|
263
|
+
# extract as a block from MP4 if specified
|
|
264
|
+
if ($$dirInfo{BlockInfo}) {
|
|
265
|
+
my $blockName = $$dirInfo{BlockInfo}{Name};
|
|
266
|
+
my $blockExtract = $et->Options('BlockExtract');
|
|
267
|
+
if (($blockExtract or $$et{REQ_TAG_LOOKUP}{lc $blockName} or
|
|
268
|
+
($$et{TAGS_FROM_FILE} and not $$et{EXCL_TAG_LOOKUP}{lc $blockName})))
|
|
269
|
+
{
|
|
270
|
+
$et->FoundTag($$dirInfo{BlockInfo}, $$dirInfo{DataPt});
|
|
271
|
+
return 1 if $blockExtract and $blockExtract > 1;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
my ($buff, $err, $i, $tag, $fmt);
|
|
275
|
+
SetByteOrder('II');
|
|
276
|
+
$raf->Read($buff, 4) == 4 or return 0;
|
|
277
|
+
my $n = Get32u(\$buff, 0);
|
|
278
|
+
$et->VerboseDir($dirName, $n);
|
|
279
|
+
for ($i=0; $i<$n; ++$i) {
|
|
280
|
+
$raf->Read($buff, 0x2c) == 0x2c or $err = '', last;
|
|
281
|
+
($tag = substr($buff, 0, 0x20)) =~ s/\0+$//;
|
|
282
|
+
($fmt = substr($buff, 0x20, 8)) =~ s/\0+$//;
|
|
283
|
+
my $format = $format{$fmt};
|
|
284
|
+
$format or $err = "Unknown Kandao format '${fmt}'", last;
|
|
285
|
+
my $num = Get32u(\$buff, 0x28);
|
|
286
|
+
my $size = $num * Image::ExifTool::FormatSize($format);
|
|
287
|
+
my %parms = (
|
|
288
|
+
DataPt => \$buff,
|
|
289
|
+
DataPos => $dataPos + $raf->Tell(),
|
|
290
|
+
Index => $i,
|
|
291
|
+
Format => $format,
|
|
292
|
+
);
|
|
293
|
+
$raf->Read($buff, $size) == $size or $err = '', last;
|
|
294
|
+
my $tagInfo = $et->GetTagInfo($tagTbl, $tag, \$buff);
|
|
295
|
+
unless ($tagInfo) {
|
|
296
|
+
my $name = ucfirst lc $tag;
|
|
297
|
+
$name =~ s/_([a-z])/_\u$1/g;
|
|
298
|
+
$name = Image::ExifTool::MakeTagName($name);
|
|
299
|
+
$et->VPrint(0, "$$et{INDENT}\[adding $dirName '${tag}']\n");
|
|
300
|
+
$tagInfo = AddTagToTable($tagTbl, $tag, { Name => $name });
|
|
301
|
+
}
|
|
302
|
+
my $recLen = $$tagInfo{RecordSize};
|
|
303
|
+
if ($recLen) {
|
|
304
|
+
$verbose and $et->VerboseInfo($tag, $tagInfo, %parms);
|
|
305
|
+
my $tbl = GetTagTable($$tagInfo{SubDirectory}{TagTable});
|
|
306
|
+
my $pt = $$tagInfo{SubDirectory}{Start} || 0;
|
|
307
|
+
my %dirInfo = (
|
|
308
|
+
DataPt => \$buff,,
|
|
309
|
+
DataPos => $parms{DataPos},
|
|
310
|
+
DirLen => $recLen,
|
|
311
|
+
DirName => $tag,
|
|
312
|
+
);
|
|
313
|
+
my $nLimit = 10000;
|
|
314
|
+
my $sizeLimit = $size;
|
|
315
|
+
if ($ee and int(($size - $pt) / $recLen) > $nLimit and
|
|
316
|
+
$et->Warn("Extracting only the first $nLimit $$tagInfo{Name} records", 2))
|
|
317
|
+
{
|
|
318
|
+
$sizeLimit = $pt + $recLen * $nLimit;
|
|
319
|
+
}
|
|
320
|
+
while ($pt+$recLen <= $sizeLimit) {
|
|
321
|
+
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
322
|
+
$dirInfo{DirStart} = $pt;
|
|
323
|
+
$et->ProcessDirectory(\%dirInfo, $tbl);
|
|
324
|
+
$ee or $et->Warn('Use the ExtractEmbedded option to extract all timed metadata',7), last;
|
|
325
|
+
$pt += $recLen;
|
|
326
|
+
}
|
|
327
|
+
delete $$et{DOC_NUM};
|
|
328
|
+
} else {
|
|
329
|
+
my $val;
|
|
330
|
+
if ($fmt eq 'U8' and $num > 1 and not $$tagInfo{Format}) {
|
|
331
|
+
$val = \$buff;
|
|
332
|
+
} else {
|
|
333
|
+
# override format if necessary
|
|
334
|
+
if ($$tagInfo{Format}) {
|
|
335
|
+
$format = $$tagInfo{Format};
|
|
336
|
+
$parms{Format} .= " read as $format";
|
|
337
|
+
$num = int($size / Image::ExifTool::FormatSize($format));
|
|
338
|
+
}
|
|
339
|
+
$val = ReadValue(\$buff, 0, $format, $num, $size);
|
|
340
|
+
}
|
|
341
|
+
my $key = $et->HandleTag($tagTbl, $tag, $val, %parms);
|
|
342
|
+
$et->SetGroup($key, $dirName) if $dirName ne 'KVAR';
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
$et->Warn($err || "Truncated $dirName record") if defined $err;
|
|
346
|
+
return 1;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
#------------------------------------------------------------------------------
|
|
350
|
+
# Process Kandao KVAR file
|
|
351
|
+
# Inputs: 0) ExifTool ref, 1) dirInfo reference
|
|
352
|
+
# Returns: 1 on success, 0 if this wasn't a valid KVAR file
|
|
353
|
+
sub ProcessKVAR($$)
|
|
354
|
+
{
|
|
355
|
+
my ($et, $dirInfo) = @_;
|
|
356
|
+
my $raf = $$dirInfo{RAF};
|
|
357
|
+
my ($buff, $tagTablePtr);
|
|
358
|
+
|
|
359
|
+
# verify this is a valid KVAR file
|
|
360
|
+
return 0 unless $raf->Read($buff, 44) == 44;
|
|
361
|
+
return 0 unless $buff =~ /^.{2}\0\0[A-Z].{31}(CHAR|BOOL|[US](8|16|32|64)|FLOAT|DOUBLE)\0/s;
|
|
362
|
+
$et->SetFileType('KVAR', 'application/x-kandaostudio');
|
|
363
|
+
$raf->Seek(0,0) or $et->Warn('Seek error'), return 1;
|
|
364
|
+
$$dirInfo{DirName} = 'KVAR';
|
|
365
|
+
$tagTablePtr = GetTagTable('Image::ExifTool::Kandao::Main');
|
|
366
|
+
return $et->ProcessDirectory($dirInfo, $tagTablePtr);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
1; # end
|
|
370
|
+
|
|
371
|
+
__END__
|
|
372
|
+
|
|
373
|
+
=head1 NAME
|
|
374
|
+
|
|
375
|
+
Image::ExifTool::Kandao - Read Kandao MP4 metadata
|
|
376
|
+
|
|
377
|
+
=head1 SYNOPSIS
|
|
378
|
+
|
|
379
|
+
This module is used by Image::ExifTool
|
|
380
|
+
|
|
381
|
+
=head1 DESCRIPTION
|
|
382
|
+
|
|
383
|
+
This module contains code to extract metadata from Kandao 'kfix' and 'kvar'
|
|
384
|
+
atoms in MP4 videos as shot by the Kandao QOOCAM 3 ULTRA.
|
|
385
|
+
|
|
386
|
+
=head1 AUTHOR
|
|
387
|
+
|
|
388
|
+
Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
|
|
389
|
+
|
|
390
|
+
This library is free software; you can redistribute it and/or modify it
|
|
391
|
+
under the same terms as Perl itself.
|
|
392
|
+
|
|
393
|
+
=head1 SEE ALSO
|
|
394
|
+
|
|
395
|
+
L<Image::ExifTool::TagNames/Kandao Tags>,
|
|
396
|
+
L<Image::ExifTool(3pm)|Image::ExifTool>
|
|
397
|
+
|
|
398
|
+
=cut
|
|
399
|
+
|
|
@@ -596,7 +596,7 @@ sub ProcessLinkInfo($$$)
|
|
|
596
596
|
$off = Get32u($dataPt, 0x14);
|
|
597
597
|
if ($off and $off + 0x14 <= $dataLen) {
|
|
598
598
|
my $siz = Get32u($dataPt, $off);
|
|
599
|
-
return 0 if $off + $siz > $dataLen;
|
|
599
|
+
return 0 if $off + $siz > $dataLen;
|
|
600
600
|
$pos = Get32u($dataPt, $off + 0x08);
|
|
601
601
|
if ($pos > 0x14 and $siz >= 0x18) {
|
|
602
602
|
$pos = Get32u($dataPt, $off + 0x14);
|
|
@@ -22,7 +22,7 @@ $VERSION = '1.00';
|
|
|
22
22
|
|
|
23
23
|
my %bool = (
|
|
24
24
|
Format => 'int8u',
|
|
25
|
-
PrintConv => { 0 => 'No', 1 => 'Yes' }
|
|
25
|
+
PrintConv => { 0 => 'No', 1 => 'Yes' }
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
%Image::ExifTool::MRC::Main = (
|
|
@@ -31,7 +31,7 @@ my %bool = (
|
|
|
31
31
|
VARS => { NO_LOOKUP => 1 }, # omit tags from lookup
|
|
32
32
|
FORMAT => 'int32u',
|
|
33
33
|
NOTES => q{
|
|
34
|
-
Tags extracted from Medical Research Council (MRC) format imaging files.
|
|
34
|
+
Tags extracted from Medical Research Council (MRC) format imaging files.
|
|
35
35
|
See L<https://www.ccpem.ac.uk/mrc_format/mrc2014.php> for the specification.
|
|
36
36
|
},
|
|
37
37
|
0 => 'ImageWidth',
|
|
@@ -104,7 +104,7 @@ my %bool = (
|
|
|
104
104
|
},
|
|
105
105
|
12 => {
|
|
106
106
|
Name => 'TimeStamp',
|
|
107
|
-
Format => 'double',
|
|
107
|
+
Format => 'double',
|
|
108
108
|
Condition => '$$self{BitM} & 0x01',
|
|
109
109
|
Groups => { 2 => 'Time'},
|
|
110
110
|
# shift from days since Dec 30, 1899 to Unix epoch of Jan 1, 1970
|
|
@@ -269,7 +269,7 @@ sub ProcessMRC($$)
|
|
|
269
269
|
# (I don't have any samples with extended headers for testing, so these are not yet decoded)
|
|
270
270
|
if ($$et{ExtendedHeaderSize} and $$et{ExtendedHeaderType} =~ /^FEI[12]/) {
|
|
271
271
|
unless ($raf->Read($buff,4)==4 and $raf->Seek(-4,1)) { # read metadata size
|
|
272
|
-
$et->Warn('Error reading extended header');
|
|
272
|
+
$et->Warn('Error reading extended header');
|
|
273
273
|
return 1;
|
|
274
274
|
}
|
|
275
275
|
my $size = Get32u(\$buff, 0);
|
|
@@ -487,7 +487,7 @@ my %sKeywordStruct;
|
|
|
487
487
|
GROUPS => { 0 => 'XMP', 1 => 'XMP-mwg-kw', 2 => 'Image' },
|
|
488
488
|
NAMESPACE => 'mwg-kw',
|
|
489
489
|
NOTES => q{
|
|
490
|
-
Hierarchical keywords metadata defined by the MWG 2.0 specification.
|
|
490
|
+
Hierarchical keywords metadata defined by the MWG 2.0 specification.
|
|
491
491
|
ExifTool unrolls keyword structures to an arbitrary depth of 6 to allow
|
|
492
492
|
individual levels to be accessed with different tag names, and to avoid
|
|
493
493
|
infinite recursion. See
|
|
@@ -528,7 +528,6 @@ sub ExtractMDItemTags($$)
|
|
|
528
528
|
$$et{INDENT} =~ s/\| $//;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
|
|
532
531
|
#------------------------------------------------------------------------------
|
|
533
532
|
# Read MacOS XAttr value
|
|
534
533
|
# Inputs: 0) ExifTool object ref, 1) file name
|
|
@@ -732,7 +731,7 @@ This module is used by Image::ExifTool
|
|
|
732
731
|
This module contains definitions required by Image::ExifTool to extract
|
|
733
732
|
MDItem* and XAttr* tags on MacOS systems using the "mdls" and "xattr"
|
|
734
733
|
utilities respectively. It also reads metadata directly from the MacOS "_."
|
|
735
|
-
sidecar files that are used on some filesystems to store file attributes.
|
|
734
|
+
sidecar files that are used on some filesystems to store file attributes.
|
|
736
735
|
Writable tags use "xattr", "setfile" or "osascript" for writing.
|
|
737
736
|
|
|
738
737
|
=head1 AUTHOR
|