exiftool-vendored.exe 13.43.0 → 13.44.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 (52) hide show
  1. package/bin/exiftool.exe +0 -0
  2. package/bin/exiftool_files/exiftool.pl +2 -2
  3. package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +0 -1
  4. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +2 -2
  5. package/bin/exiftool_files/lib/Image/ExifTool/CanonCustom.pm +1 -1
  6. package/bin/exiftool_files/lib/Image/ExifTool/DarwinCore.pm +2 -2
  7. package/bin/exiftool_files/lib/Image/ExifTool/EXE.pm +1 -1
  8. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +1 -0
  9. package/bin/exiftool_files/lib/Image/ExifTool/FLIR.pm +1 -1
  10. package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +1 -1
  11. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +2 -2
  12. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
  13. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.pm +1 -1
  14. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +1 -1
  15. package/bin/exiftool_files/lib/Image/ExifTool/Google.pm +2 -2
  16. package/bin/exiftool_files/lib/Image/ExifTool/ICC_Profile.pm +0 -1
  17. package/bin/exiftool_files/lib/Image/ExifTool/Import.pm +1 -1
  18. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +1 -1
  19. package/bin/exiftool_files/lib/Image/ExifTool/Kandao.pm +399 -0
  20. package/bin/exiftool_files/lib/Image/ExifTool/LNK.pm +1 -1
  21. package/bin/exiftool_files/lib/Image/ExifTool/MRC.pm +4 -4
  22. package/bin/exiftool_files/lib/Image/ExifTool/MWG.pm +1 -1
  23. package/bin/exiftool_files/lib/Image/ExifTool/MacOS.pm +1 -2
  24. package/bin/exiftool_files/lib/Image/ExifTool/Matroska.pm +56 -15
  25. package/bin/exiftool_files/lib/Image/ExifTool/Microsoft.pm +1 -1
  26. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +5 -5
  27. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +5 -6
  28. package/bin/exiftool_files/lib/Image/ExifTool/OpenEXR.pm +1 -1
  29. package/bin/exiftool_files/lib/Image/ExifTool/PPM.pm +1 -1
  30. package/bin/exiftool_files/lib/Image/ExifTool/Panasonic.pm +18 -2
  31. package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +17 -1
  32. package/bin/exiftool_files/lib/Image/ExifTool/Plot.pm +2 -2
  33. package/bin/exiftool_files/lib/Image/ExifTool/Protobuf.pm +2 -2
  34. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +25 -5
  35. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +1 -1
  36. package/bin/exiftool_files/lib/Image/ExifTool/README +5 -5
  37. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +3 -3
  38. package/bin/exiftool_files/lib/Image/ExifTool/Reconyx.pm +2 -2
  39. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +12 -3
  40. package/bin/exiftool_files/lib/Image/ExifTool/TNEF.pm +2 -2
  41. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +18 -2
  42. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +244 -109
  43. package/bin/exiftool_files/lib/Image/ExifTool/Text.pm +1 -1
  44. package/bin/exiftool_files/lib/Image/ExifTool/Trailer.pm +1 -1
  45. package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +1 -1
  46. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +3 -2
  47. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +4 -2
  48. package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +1 -1
  49. package/bin/exiftool_files/lib/Image/ExifTool.pm +5 -2
  50. package/bin/exiftool_files/lib/Image/ExifTool.pod +79 -79
  51. package/bin/exiftool_files/windows_exiftool.txt +50 -50
  52. package/package.json +4 -4
package/bin/exiftool.exe CHANGED
Binary file
@@ -11,7 +11,7 @@ use strict;
11
11
  use warnings;
12
12
  require 5.004;
13
13
 
14
- my $version = '13.43';
14
+ my $version = '13.44';
15
15
 
16
16
  $^W = 1; # enable global warnings
17
17
 
@@ -3288,7 +3288,7 @@ sub SetImageInfo($$$)
3288
3288
  }
3289
3289
  }
3290
3290
  next;
3291
- }
3291
+ }
3292
3292
  my @exclTags = @csvExclude;
3293
3293
  foreach (@exclTags) {
3294
3294
  tr/-0-9a-zA-Z_:#?*//dc; # remove illegal characters
@@ -116,7 +116,6 @@ sub ConvertPLIST($$);
116
116
  # - changed in 12.19 to MediaGroupUUID, NealKrawetz private communication
117
117
  # - changed back to ContentIdentifier since Apple writes this to Keys content.identifier (forum14874)
118
118
  Writable => 'string',
119
-
120
119
  },
121
120
  # 0x0012 - (QRMOutputType, ref 2)
122
121
  # 0x0013 - (SphereExternalForceOffset, ref 2)
@@ -346,7 +346,7 @@ or "Rights-en-US"). (See L<http://www.ietf.org/rfc/rfc3066.txt> for the RFC
346
346
  3066 specification.) A C<lang-alt> tag with no language code accesses the
347
347
  "x-default" language, but causes other languages for this tag to be deleted
348
348
  when writing. The "x-default" language code may be specified when writing
349
- to preserve other existing languages (eg. "XMP-dc:Description-x-default").
349
+ to preserve other existing languages (eg. "XMP-dc:Description-x-default").
350
350
  When reading, "x-default" is not specified.
351
351
 
352
352
  The XMP tags are organized according to schema B<Namespace> in the following
@@ -629,7 +629,7 @@ and RPM).
629
629
  Extra => q{
630
630
  The extra tags provide extra features or extra information extracted or
631
631
  generated by ExifTool that is not directly associated with another tag
632
- group. The B<Group> column lists the family 1 group name when reading.
632
+ group. The B<Group> column lists the family 1 group name when reading.
633
633
  Tags with a "-" in this column are write-only.
634
634
 
635
635
  Tags in the family 1 "System" group are referred to as "pseudo" tags because
@@ -1362,7 +1362,7 @@ my %convPFn = ( PrintConv => \&ConvertPfn, PrintConvInv => \&ConvertPfnInv );
1362
1362
  PrintConvInv => '$val=~/0x([\dA-F]+)/i ? hex($1) : undef',
1363
1363
  },{
1364
1364
  Name => 'UsableMeteringModes',
1365
- Count => 2,
1365
+ Count => 2,
1366
1366
  PrintConv => [
1367
1367
  \%disableEnable,
1368
1368
  'sprintf("Flags 0x%x",$val)', # (evaluative,partial,spot,center-weighted average)
@@ -296,7 +296,7 @@ my %event = (
296
296
  taxonomicStatus => { },
297
297
  verbatimTaxonRank => { },
298
298
  vernacularName => { Writable => 'lang-alt' },
299
- superFamily => { }, # dwc.tdwg.org added 2023
299
+ superFamily => { }, # dwc.tdwg.org added 2023
300
300
  subFamily => { }, # dwc.tdwg.org added 2023
301
301
  tribe => { }, # dwc.tdwg.org added 2023
302
302
  subTribe => { }, # dwc.tdwg.org added 2023
@@ -332,7 +332,7 @@ my %event = (
332
332
  georeferenceProtocol => { },
333
333
  georeferenceRemarks => { },
334
334
  georeferenceSources => { },
335
- georeferenceVerificationStatus => { }, # dwc.tdwg.org removed 2023
335
+ georeferenceVerificationStatus => { }, # dwc.tdwg.org removed 2023
336
336
  higherGeography => { },
337
337
  higherGeographyID => { },
338
338
  island => { },
@@ -1113,7 +1113,7 @@ sub ProcessPEResources($$)
1113
1113
  #------------------------------------------------------------------------------
1114
1114
  # Process Windows PE file data dictionary
1115
1115
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref
1116
- # Returns: true on success or if the PE resources didn't exist, or false on error
1116
+ # Returns: true on success or if the PE resources didn't exist, or false on error
1117
1117
  # processing the PE resources
1118
1118
  sub ProcessPEDict($$)
1119
1119
  {
@@ -1598,6 +1598,7 @@ my %opcodeInfo = (
1598
1598
  2 => 'Sony Compressed RAW', # (lossy, ref IB)
1599
1599
  3 => 'Sony Lossless Compressed RAW', #IB
1600
1600
  4 => 'Sony Lossless Compressed RAW 2', #JR (ILCE-1)
1601
+ 6 => 'Sony Compressed RAW HQ', # ILCE-7M5
1601
1602
  },
1602
1603
  },
1603
1604
  # 0x7001 - int16u[1] (in SubIFD of Sony ARW images) - values: 0,1
@@ -479,7 +479,7 @@ my %float8g = ( Format => 'float', PrintConv => 'sprintf("%.8g",$val)' );
479
479
  VARS => { ID_FMT => 'none' },
480
480
  NOTES => q{
481
481
  Tags listed below are only for the first measurement tool, however multiple
482
- measurements may be added, and information is extracted for all of them.
482
+ measurements may be added, and information is extracted for all of them.
483
483
  Tags for subsequent measurements are generated as required with the prefixes
484
484
  "Meas2", "Meas3", etc.
485
485
  },
@@ -1454,7 +1454,7 @@ sub ProcessContents($$$)
1454
1454
  my ($w, $h) = unpack('V2',$2);
1455
1455
  $et->FoundTag(ImageWidth => $w);
1456
1456
  $et->FoundTag(ImageHeight => $h);
1457
- $et->HandleTag($tagTablePtr, OriginalFileName => $name);
1457
+ $et->HandleTag($tagTablePtr, OriginalFileName => $name);
1458
1458
  if ($$dataPt =~ /\G\x01\0{4}(.{12})/sg) {
1459
1459
  # (first 4 bytes seem to be number of objects, next 4 bytes are zero, then ICC size)
1460
1460
  my $size = unpack('x8V', $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',
@@ -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
@@ -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)
@@ -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
@@ -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, and have a family 1 group name of KVAR, KFIX or
45
+ KSTB 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