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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +44 -1
  3. data/bin/MANIFEST +2 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +48 -48
  7. data/bin/config_files/example.config +5 -2
  8. data/bin/exiftool +96 -84
  9. data/bin/lib/Image/ExifTool/Apple.pm +0 -1
  10. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +4 -4
  11. data/bin/lib/Image/ExifTool/Canon.pm +24 -1
  12. data/bin/lib/Image/ExifTool/CanonCustom.pm +1 -1
  13. data/bin/lib/Image/ExifTool/DarwinCore.pm +2 -2
  14. data/bin/lib/Image/ExifTool/EXE.pm +1 -1
  15. data/bin/lib/Image/ExifTool/Exif.pm +4 -2
  16. data/bin/lib/Image/ExifTool/FLIR.pm +1 -1
  17. data/bin/lib/Image/ExifTool/FlashPix.pm +1 -1
  18. data/bin/lib/Image/ExifTool/FujiFilm.pm +2 -2
  19. data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  20. data/bin/lib/Image/ExifTool/Geolocation.pm +1 -1
  21. data/bin/lib/Image/ExifTool/Geotag.pm +3 -3
  22. data/bin/lib/Image/ExifTool/Google.pm +2 -2
  23. data/bin/lib/Image/ExifTool/ICC_Profile.pm +0 -1
  24. data/bin/lib/Image/ExifTool/Import.pm +1 -1
  25. data/bin/lib/Image/ExifTool/JPEG.pm +5 -1
  26. data/bin/lib/Image/ExifTool/Jpeg2000.pm +1 -1
  27. data/bin/lib/Image/ExifTool/Kandao.pm +399 -0
  28. data/bin/lib/Image/ExifTool/LNK.pm +1 -1
  29. data/bin/lib/Image/ExifTool/MRC.pm +4 -4
  30. data/bin/lib/Image/ExifTool/MWG.pm +1 -1
  31. data/bin/lib/Image/ExifTool/MacOS.pm +1 -2
  32. data/bin/lib/Image/ExifTool/Matroska.pm +56 -15
  33. data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
  34. data/bin/lib/Image/ExifTool/Nikon.pm +13 -15
  35. data/bin/lib/Image/ExifTool/NikonCustom.pm +10 -8
  36. data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
  37. data/bin/lib/Image/ExifTool/PPM.pm +1 -1
  38. data/bin/lib/Image/ExifTool/Panasonic.pm +30 -6
  39. data/bin/lib/Image/ExifTool/Pentax.pm +9 -1
  40. data/bin/lib/Image/ExifTool/PhaseOne.pm +17 -1
  41. data/bin/lib/Image/ExifTool/Plot.pm +2 -2
  42. data/bin/lib/Image/ExifTool/Protobuf.pm +42 -16
  43. data/bin/lib/Image/ExifTool/QuickTime.pm +48 -8
  44. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +2 -2
  45. data/bin/lib/Image/ExifTool/README +5 -5
  46. data/bin/lib/Image/ExifTool/RIFF.pm +5 -4
  47. data/bin/lib/Image/ExifTool/Reconyx.pm +2 -2
  48. data/bin/lib/Image/ExifTool/Samsung.pm +11 -1
  49. data/bin/lib/Image/ExifTool/Sony.pm +29 -3
  50. data/bin/lib/Image/ExifTool/TNEF.pm +2 -2
  51. data/bin/lib/Image/ExifTool/TagLookup.pm +7129 -7108
  52. data/bin/lib/Image/ExifTool/TagNames.pod +259 -111
  53. data/bin/lib/Image/ExifTool/Text.pm +1 -1
  54. data/bin/lib/Image/ExifTool/Trailer.pm +1 -1
  55. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +10 -2
  56. data/bin/lib/Image/ExifTool/Writer.pl +9 -5
  57. data/bin/lib/Image/ExifTool/XMP.pm +4 -2
  58. data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
  59. data/bin/lib/Image/ExifTool.pm +33 -26
  60. data/bin/lib/Image/ExifTool.pod +88 -79
  61. data/bin/perl-Image-ExifTool.spec +47 -47
  62. data/lib/exiftool_vendored/version.rb +1 -1
  63. 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.83';
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 sprintf('%s.%.3d UTC', Image::ExifTool::ConvertUnixTime($time), $fsec);
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) = @_;
@@ -370,7 +370,6 @@ my %manuSig = ( #6
370
370
  Groups => { 2 => 'Time' },
371
371
  PrintConv => '$self->ConvertDateTime($val)',
372
372
  },
373
-
374
373
  targ => {
375
374
  Name => 'CharTarget',
376
375
  ValueConv => '$val=~s/\0.*//; length $val > 128 ? \$val : $val',
@@ -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.39';
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