exiftool-vendored.exe 12.99.0 → 13.16.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 (209) hide show
  1. package/bin/exiftool.exe +0 -0
  2. package/bin/exiftool_files/exiftool.pl +183 -70
  3. package/bin/exiftool_files/lib/File/RandomAccess.pm +1 -1
  4. package/bin/exiftool_files/lib/File/RandomAccess.pod +2 -2
  5. package/bin/exiftool_files/lib/Image/ExifTool/AAC.pm +1 -1
  6. package/bin/exiftool_files/lib/Image/ExifTool/AES.pm +1 -1
  7. package/bin/exiftool_files/lib/Image/ExifTool/AFCP.pm +6 -6
  8. package/bin/exiftool_files/lib/Image/ExifTool/AIFF.pm +2 -2
  9. package/bin/exiftool_files/lib/Image/ExifTool/APE.pm +2 -2
  10. package/bin/exiftool_files/lib/Image/ExifTool/APP12.pm +1 -1
  11. package/bin/exiftool_files/lib/Image/ExifTool/ASF.pm +2 -2
  12. package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +11 -9
  13. package/bin/exiftool_files/lib/Image/ExifTool/Audible.pm +1 -1
  14. package/bin/exiftool_files/lib/Image/ExifTool/BMP.pm +1 -1
  15. package/bin/exiftool_files/lib/Image/ExifTool/BPG.pm +1 -1
  16. package/bin/exiftool_files/lib/Image/ExifTool/BZZ.pm +1 -1
  17. package/bin/exiftool_files/lib/Image/ExifTool/BigTIFF.pm +1 -1
  18. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +36 -22
  19. package/bin/exiftool_files/lib/Image/ExifTool/CBOR.pm +5 -2
  20. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +66 -27
  21. package/bin/exiftool_files/lib/Image/ExifTool/CanonCustom.pm +1 -1
  22. package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +1 -1
  23. package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +1 -1
  24. package/bin/exiftool_files/lib/Image/ExifTool/CaptureOne.pm +1 -1
  25. package/bin/exiftool_files/lib/Image/ExifTool/Casio.pm +1 -1
  26. package/bin/exiftool_files/lib/Image/ExifTool/Charset.pm +1 -1
  27. package/bin/exiftool_files/lib/Image/ExifTool/DICOM.pm +1 -1
  28. package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +196 -30
  29. package/bin/exiftool_files/lib/Image/ExifTool/DNG.pm +1 -1
  30. package/bin/exiftool_files/lib/Image/ExifTool/DPX.pm +1 -1
  31. package/bin/exiftool_files/lib/Image/ExifTool/DV.pm +1 -1
  32. package/bin/exiftool_files/lib/Image/ExifTool/DarwinCore.pm +1 -1
  33. package/bin/exiftool_files/lib/Image/ExifTool/DjVu.pm +1 -1
  34. package/bin/exiftool_files/lib/Image/ExifTool/EXE.pm +138 -33
  35. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +30 -17
  36. package/bin/exiftool_files/lib/Image/ExifTool/FITS.pm +3 -3
  37. package/bin/exiftool_files/lib/Image/ExifTool/FLAC.pm +1 -1
  38. package/bin/exiftool_files/lib/Image/ExifTool/FLIF.pm +3 -3
  39. package/bin/exiftool_files/lib/Image/ExifTool/FLIR.pm +1 -1
  40. package/bin/exiftool_files/lib/Image/ExifTool/Fixup.pm +1 -1
  41. package/bin/exiftool_files/lib/Image/ExifTool/Flash.pm +1 -1
  42. package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +17 -21
  43. package/bin/exiftool_files/lib/Image/ExifTool/Font.pm +2 -2
  44. package/bin/exiftool_files/lib/Image/ExifTool/FotoStation.pm +1 -1
  45. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +1 -1
  46. package/bin/exiftool_files/lib/Image/ExifTool/GE.pm +1 -1
  47. package/bin/exiftool_files/lib/Image/ExifTool/GIF.pm +144 -93
  48. package/bin/exiftool_files/lib/Image/ExifTool/GIMP.pm +1 -1
  49. package/bin/exiftool_files/lib/Image/ExifTool/GM.pm +1 -1
  50. package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +34 -30
  51. package/bin/exiftool_files/lib/Image/ExifTool/GeoTiff.pm +1 -1
  52. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
  53. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.pm +19 -9
  54. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +49 -14
  55. package/bin/exiftool_files/lib/Image/ExifTool/GoPro.pm +120 -8
  56. package/bin/exiftool_files/lib/Image/ExifTool/H264.pm +1 -1
  57. package/bin/exiftool_files/lib/Image/ExifTool/HP.pm +2 -2
  58. package/bin/exiftool_files/lib/Image/ExifTool/HTML.pm +1 -1
  59. package/bin/exiftool_files/lib/Image/ExifTool/HtmlDump.pm +1 -1
  60. package/bin/exiftool_files/lib/Image/ExifTool/ICC_Profile.pm +81 -2
  61. package/bin/exiftool_files/lib/Image/ExifTool/ICO.pm +1 -1
  62. package/bin/exiftool_files/lib/Image/ExifTool/ID3.pm +8 -8
  63. package/bin/exiftool_files/lib/Image/ExifTool/IPTC.pm +10 -7
  64. package/bin/exiftool_files/lib/Image/ExifTool/ISO.pm +1 -1
  65. package/bin/exiftool_files/lib/Image/ExifTool/ITC.pm +1 -1
  66. package/bin/exiftool_files/lib/Image/ExifTool/Import.pm +5 -4
  67. package/bin/exiftool_files/lib/Image/ExifTool/InDesign.pm +2 -2
  68. package/bin/exiftool_files/lib/Image/ExifTool/InfiRay.pm +1 -1
  69. package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +32 -5
  70. package/bin/exiftool_files/lib/Image/ExifTool/JPEGDigest.pm +1 -1
  71. package/bin/exiftool_files/lib/Image/ExifTool/JSON.pm +1 -1
  72. package/bin/exiftool_files/lib/Image/ExifTool/JVC.pm +1 -1
  73. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +10 -9
  74. package/bin/exiftool_files/lib/Image/ExifTool/Kodak.pm +1 -1
  75. package/bin/exiftool_files/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
  76. package/bin/exiftool_files/lib/Image/ExifTool/LIF.pm +1 -1
  77. package/bin/exiftool_files/lib/Image/ExifTool/LNK.pm +2 -2
  78. package/bin/exiftool_files/lib/Image/ExifTool/Lang/cs.pm +1 -1
  79. package/bin/exiftool_files/lib/Image/ExifTool/Lang/de.pm +1 -1
  80. package/bin/exiftool_files/lib/Image/ExifTool/Lang/en_ca.pm +1 -1
  81. package/bin/exiftool_files/lib/Image/ExifTool/Lang/en_gb.pm +1 -1
  82. package/bin/exiftool_files/lib/Image/ExifTool/Lang/es.pm +1 -1
  83. package/bin/exiftool_files/lib/Image/ExifTool/Lang/fi.pm +1 -1
  84. package/bin/exiftool_files/lib/Image/ExifTool/Lang/fr.pm +1 -1
  85. package/bin/exiftool_files/lib/Image/ExifTool/Lang/it.pm +1 -1
  86. package/bin/exiftool_files/lib/Image/ExifTool/Lang/ja.pm +1 -1
  87. package/bin/exiftool_files/lib/Image/ExifTool/Lang/ko.pm +1 -1
  88. package/bin/exiftool_files/lib/Image/ExifTool/Lang/nl.pm +1 -1
  89. package/bin/exiftool_files/lib/Image/ExifTool/Lang/pl.pm +1 -1
  90. package/bin/exiftool_files/lib/Image/ExifTool/Lang/ru.pm +1 -1
  91. package/bin/exiftool_files/lib/Image/ExifTool/Lang/sk.pm +1 -1
  92. package/bin/exiftool_files/lib/Image/ExifTool/Lang/sv.pm +1 -1
  93. package/bin/exiftool_files/lib/Image/ExifTool/Lang/tr.pm +1 -1
  94. package/bin/exiftool_files/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
  95. package/bin/exiftool_files/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
  96. package/bin/exiftool_files/lib/Image/ExifTool/Leaf.pm +1 -1
  97. package/bin/exiftool_files/lib/Image/ExifTool/LigoGPS.pm +409 -0
  98. package/bin/exiftool_files/lib/Image/ExifTool/Lytro.pm +1 -1
  99. package/bin/exiftool_files/lib/Image/ExifTool/M2TS.pm +58 -19
  100. package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +15 -6
  101. package/bin/exiftool_files/lib/Image/ExifTool/MIEUnits.pod +1 -1
  102. package/bin/exiftool_files/lib/Image/ExifTool/MIFF.pm +1 -1
  103. package/bin/exiftool_files/lib/Image/ExifTool/MISB.pm +1 -1
  104. package/bin/exiftool_files/lib/Image/ExifTool/MNG.pm +1 -1
  105. package/bin/exiftool_files/lib/Image/ExifTool/MOI.pm +1 -1
  106. package/bin/exiftool_files/lib/Image/ExifTool/MPC.pm +1 -1
  107. package/bin/exiftool_files/lib/Image/ExifTool/MPEG.pm +1 -1
  108. package/bin/exiftool_files/lib/Image/ExifTool/MPF.pm +1 -1
  109. package/bin/exiftool_files/lib/Image/ExifTool/MRC.pm +1 -1
  110. package/bin/exiftool_files/lib/Image/ExifTool/MWG.pm +1 -1
  111. package/bin/exiftool_files/lib/Image/ExifTool/MXF.pm +3 -3
  112. package/bin/exiftool_files/lib/Image/ExifTool/MacOS.pm +3 -2
  113. package/bin/exiftool_files/lib/Image/ExifTool/MakerNotes.pm +1 -1
  114. package/bin/exiftool_files/lib/Image/ExifTool/Matroska.pm +22 -6
  115. package/bin/exiftool_files/lib/Image/ExifTool/Microsoft.pm +2 -2
  116. package/bin/exiftool_files/lib/Image/ExifTool/Minolta.pm +1 -1
  117. package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
  118. package/bin/exiftool_files/lib/Image/ExifTool/Motorola.pm +1 -1
  119. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +495 -39
  120. package/bin/exiftool_files/lib/Image/ExifTool/NikonCapture.pm +1 -1
  121. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +2 -2
  122. package/bin/exiftool_files/lib/Image/ExifTool/NikonSettings.pm +1 -1
  123. package/bin/exiftool_files/lib/Image/ExifTool/Nintendo.pm +1 -1
  124. package/bin/exiftool_files/lib/Image/ExifTool/OOXML.pm +8 -8
  125. package/bin/exiftool_files/lib/Image/ExifTool/Ogg.pm +1 -1
  126. package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +1 -1
  127. package/bin/exiftool_files/lib/Image/ExifTool/OpenEXR.pm +1 -1
  128. package/bin/exiftool_files/lib/Image/ExifTool/Opus.pm +1 -1
  129. package/bin/exiftool_files/lib/Image/ExifTool/Other.pm +1 -1
  130. package/bin/exiftool_files/lib/Image/ExifTool/PCX.pm +1 -1
  131. package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +49 -18
  132. package/bin/exiftool_files/lib/Image/ExifTool/PGF.pm +1 -1
  133. package/bin/exiftool_files/lib/Image/ExifTool/PICT.pm +1 -1
  134. package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +4 -4
  135. package/bin/exiftool_files/lib/Image/ExifTool/PLUS.pm +1 -1
  136. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +20 -8
  137. package/bin/exiftool_files/lib/Image/ExifTool/PPM.pm +12 -3
  138. package/bin/exiftool_files/lib/Image/ExifTool/PSP.pm +1 -1
  139. package/bin/exiftool_files/lib/Image/ExifTool/Palm.pm +1 -1
  140. package/bin/exiftool_files/lib/Image/ExifTool/Panasonic.pm +27 -3
  141. package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +1 -1
  142. package/bin/exiftool_files/lib/Image/ExifTool/Parrot.pm +1 -1
  143. package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +1 -1
  144. package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +6 -5
  145. package/bin/exiftool_files/lib/Image/ExifTool/PhotoCD.pm +1 -1
  146. package/bin/exiftool_files/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
  147. package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +65 -4
  148. package/bin/exiftool_files/lib/Image/ExifTool/PostScript.pm +1 -1
  149. package/bin/exiftool_files/lib/Image/ExifTool/PrintIM.pm +1 -1
  150. package/bin/exiftool_files/lib/Image/ExifTool/Protobuf.pm +270 -0
  151. package/bin/exiftool_files/lib/Image/ExifTool/Qualcomm.pm +1 -1
  152. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +326 -88
  153. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +266 -200
  154. package/bin/exiftool_files/lib/Image/ExifTool/README +12 -2
  155. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +21 -6
  156. package/bin/exiftool_files/lib/Image/ExifTool/RSRC.pm +1 -1
  157. package/bin/exiftool_files/lib/Image/ExifTool/RTF.pm +2 -2
  158. package/bin/exiftool_files/lib/Image/ExifTool/Radiance.pm +1 -1
  159. package/bin/exiftool_files/lib/Image/ExifTool/Rawzor.pm +1 -1
  160. package/bin/exiftool_files/lib/Image/ExifTool/Real.pm +1 -1
  161. package/bin/exiftool_files/lib/Image/ExifTool/Reconyx.pm +1 -1
  162. package/bin/exiftool_files/lib/Image/ExifTool/Red.pm +1 -1
  163. package/bin/exiftool_files/lib/Image/ExifTool/Ricoh.pm +4 -4
  164. package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +2 -2
  165. package/bin/exiftool_files/lib/Image/ExifTool/Sanyo.pm +1 -1
  166. package/bin/exiftool_files/lib/Image/ExifTool/Scalado.pm +1 -1
  167. package/bin/exiftool_files/lib/Image/ExifTool/Shift.pl +1 -1
  168. package/bin/exiftool_files/lib/Image/ExifTool/Shortcuts.pm +1 -1
  169. package/bin/exiftool_files/lib/Image/ExifTool/Sigma.pm +1 -1
  170. package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +1 -1
  171. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +5 -4
  172. package/bin/exiftool_files/lib/Image/ExifTool/SonyIDC.pm +1 -1
  173. package/bin/exiftool_files/lib/Image/ExifTool/Stim.pm +1 -1
  174. package/bin/exiftool_files/lib/Image/ExifTool/TagInfoXML.pm +6 -5
  175. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +7025 -6967
  176. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +477 -46
  177. package/bin/exiftool_files/lib/Image/ExifTool/Text.pm +4 -3
  178. package/bin/exiftool_files/lib/Image/ExifTool/Theora.pm +1 -1
  179. package/bin/exiftool_files/lib/Image/ExifTool/Torrent.pm +3 -3
  180. package/bin/exiftool_files/lib/Image/ExifTool/Unknown.pm +1 -1
  181. package/bin/exiftool_files/lib/Image/ExifTool/VCard.pm +3 -3
  182. package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +6 -6
  183. package/bin/exiftool_files/lib/Image/ExifTool/Vivo.pm +124 -0
  184. package/bin/exiftool_files/lib/Image/ExifTool/Vorbis.pm +1 -1
  185. package/bin/exiftool_files/lib/Image/ExifTool/WPG.pm +1 -1
  186. package/bin/exiftool_files/lib/Image/ExifTool/WTV.pm +1 -1
  187. package/bin/exiftool_files/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  188. package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +3 -3
  189. package/bin/exiftool_files/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  190. package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +1 -1
  191. package/bin/exiftool_files/lib/Image/ExifTool/WritePNG.pl +1 -1
  192. package/bin/exiftool_files/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
  193. package/bin/exiftool_files/lib/Image/ExifTool/WritePostScript.pl +1 -1
  194. package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +166 -79
  195. package/bin/exiftool_files/lib/Image/ExifTool/WriteRIFF.pl +17 -6
  196. package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +3 -3
  197. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +89 -96
  198. package/bin/exiftool_files/lib/Image/ExifTool/XISF.pm +1 -1
  199. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +39 -14
  200. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +103 -1
  201. package/bin/exiftool_files/lib/Image/ExifTool/XMPStruct.pl +2 -3
  202. package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +2 -2
  203. package/bin/exiftool_files/lib/Image/ExifTool/ZISRAW.pm +1 -1
  204. package/bin/exiftool_files/lib/Image/ExifTool/iWork.pm +1 -1
  205. package/bin/exiftool_files/lib/Image/ExifTool.pm +338 -166
  206. package/bin/exiftool_files/lib/Image/ExifTool.pod +119 -73
  207. package/bin/exiftool_files/readme_windows.txt +8 -13
  208. package/bin/exiftool_files/windows_exiftool.txt +102 -53
  209. package/package.json +11 -11
@@ -43,12 +43,12 @@
43
43
  package Image::ExifTool::QuickTime;
44
44
 
45
45
  use strict;
46
- use vars qw($VERSION $AUTOLOAD %stringEncoding);
46
+ use vars qw($VERSION $AUTOLOAD %stringEncoding %avType);
47
47
  use Image::ExifTool qw(:DataAccess :Utils);
48
48
  use Image::ExifTool::Exif;
49
49
  use Image::ExifTool::GPS;
50
50
 
51
- $VERSION = '3.04';
51
+ $VERSION = '3.11';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -70,7 +70,6 @@ sub Process_gsen($$$);
70
70
  sub Process_gdat($$$);
71
71
  sub Process_nbmt($$$);
72
72
  sub ProcessKenwood($$$);
73
- sub ProcessLIGO_JSON($$$);
74
73
  sub ProcessRIFFTrailer($$$);
75
74
  sub ProcessTTAD($$$);
76
75
  sub ProcessNMEA($$$);
@@ -252,15 +251,20 @@ my %timeInfo = (
252
251
  # Note: This value will be in UTC if generated by a system that is aware of the time zone
253
252
  # (also note: this code is duplicated for the CreateDate tag)
254
253
  RawConv => q{
255
- my $offset = (66 * 365 + 17) * 24 * 3600;
256
- return $val - $offset if $val >= $offset or $$self{OPTIONS}{QuickTimeUTC};
257
- if ($val and not $$self{IsWriting}) {
258
- $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
254
+ if ($val) {
255
+ my $offset = (66 * 365 + 17) * 24 * 3600;
256
+ if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
257
+ $val -= $offset;
258
+ } elsif (not $$self{IsWriting}) {
259
+ $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
260
+ }
261
+ } else {
262
+ undef $val if $self->Options('StrictDate');
259
263
  }
260
264
  return $val;
261
265
  },
262
266
  RawConvInv => q{
263
- if ($$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
267
+ if ($val and $$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
264
268
  # convert to UTC
265
269
  my $offset = (66 * 365 + 17) * 24 * 3600;
266
270
  $val = ConvertUnixTime($val - $offset);
@@ -273,10 +277,14 @@ my %timeInfo = (
273
277
  ValueConvInv => q{
274
278
  $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
275
279
  return undef unless defined $val;
280
+ return $val unless $val;
276
281
  return $val + (66 * 365 + 17) * 24 * 3600;
277
282
  },
278
283
  PrintConv => '$self->ConvertDateTime($val)',
279
- PrintConvInv => '$self->InverseDateTime($val)',
284
+ PrintConvInv => q{
285
+ return $val if $val eq '0000:00:00 00:00:00';
286
+ return $self->InverseDateTime($val);
287
+ }
280
288
  # (can't put Groups here because they aren't constant!)
281
289
  );
282
290
  # properties for ISO 8601 format date/time tags
@@ -350,6 +358,19 @@ my %vendorID = (
350
358
  5 => 'UTF16',
351
359
  );
352
360
 
361
+ # media types for which we have separate Keys tables (AudioKeys, VideoKeys)
362
+ %avType = (
363
+ soun => 'Audio',
364
+ vide => 'Video',
365
+ );
366
+
367
+ # path to Keys/ItemList/UserData tags stored in tracks
368
+ my %trackPath = (
369
+ 'MOV-Movie-Track-Meta-ItemList' => 'Keys',
370
+ 'MOV-Movie-Track-UserData-Meta-ItemList' => 'ItemList',
371
+ 'MOV-Movie-Track-UserData' => 'UserData',
372
+ );
373
+
353
374
  my %graphicsMode = (
354
375
  # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
355
376
  0x00 => 'srcCopy',
@@ -584,7 +605,26 @@ my %userDefined = (
584
605
  Condition => '$$valPt =~ /^\0[\0-\x04]..[a-zA-Z ]{4}/s',
585
606
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SkipInfo' },
586
607
  },
587
- { Name => 'Skip', Unknown => 1, Binary => 1 },
608
+ {
609
+ Name => 'LigoGPSInfo',
610
+ Condition => '$$valPt =~ /^LIGOGPSINFO\0/ and $$self{OPTIONS}{ExtractEmbedded}',
611
+ SubDirectory => {
612
+ TagTable => 'Image::ExifTool::QuickTime::Stream',
613
+ ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoGPS',
614
+ },
615
+ },
616
+ {
617
+ Name => 'Skip',
618
+ RawConv => q{
619
+ if ($val =~ /^LIGOGPSINFO\0/) {
620
+ $self->Warn('Use the ExtractEmbedded option to decode timed GPS',3);
621
+ return undef;
622
+ }
623
+ return $val;
624
+ },
625
+ Unknown => 1,
626
+ Binary => 1,
627
+ },
588
628
  ],
589
629
  wide => { Unknown => 1, Binary => 1 },
590
630
  ftyp => { #MP4
@@ -677,7 +717,7 @@ my %userDefined = (
677
717
  Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
678
718
  Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
679
719
  RawConv => q{
680
- $self->WarnOnce('Use the ExtractEmbedded option to decode timed SensorData',3);
720
+ $self->Warn('Use the ExtractEmbedded option to decode timed SensorData',3);
681
721
  return \$val;
682
722
  },
683
723
  },
@@ -762,11 +802,11 @@ my %userDefined = (
762
802
  ProcessProc => \&ProcessKenwood,
763
803
  },
764
804
  },{
765
- Name => 'LIGO_JSON',
805
+ Name => 'LigoJSON',
766
806
  Condition => '$$valPt =~ /^LIGOGPSINFO \{/',
767
807
  SubDirectory => {
768
808
  TagTable => 'Image::ExifTool::QuickTime::Stream',
769
- ProcessProc => \&ProcessLIGO_JSON,
809
+ ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoJSON',
770
810
  },
771
811
  },{
772
812
  Name => 'FLIRData',
@@ -902,6 +942,10 @@ my %userDefined = (
902
942
  },
903
943
  # '35AX'? - seen "AT" (Yada RoadCam Pro 4K dashcam)
904
944
  cust => 'CustomInfo', # 70mai A810
945
+ SEAL => {
946
+ Name => 'SEAL',
947
+ SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
948
+ },
905
949
  );
906
950
 
907
951
  # stuff seen in 'skip' atom (70mai Pro Plus+ MP4 videos)
@@ -1299,7 +1343,7 @@ my %userDefined = (
1299
1343
  Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/',
1300
1344
  Notes => 'Garmin GPS sensor data',
1301
1345
  RawConv => q{
1302
- $self->WarnOnce('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1346
+ $self->Warn('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1303
1347
  return \$val;
1304
1348
  },
1305
1349
  },
@@ -1399,11 +1443,15 @@ my %userDefined = (
1399
1443
  Groups => { 2 => 'Time' },
1400
1444
  %timeInfo,
1401
1445
  RawConv => q{
1402
- my $offset = (66 * 365 + 17) * 24 * 3600;
1403
- if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1404
- $val -= $offset;
1405
- } elsif ($val and not $$self{IsWriting}) {
1406
- $self->WarnOnce('Patched incorrect time zero for QuickTime date/time tag',1);
1446
+ if ($val) {
1447
+ my $offset = (66 * 365 + 17) * 24 * 3600;
1448
+ if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1449
+ $val -= $offset;
1450
+ } elsif (not $$self{IsWriting}) {
1451
+ $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
1452
+ }
1453
+ } else {
1454
+ undef $val if $$self{OPTIONS}{StrictDate};
1407
1455
  }
1408
1456
  return $$self{CreateDate} = $val;
1409
1457
  },
@@ -1501,7 +1549,7 @@ my %userDefined = (
1501
1549
  # (this tag is readable/writable as a block through the Extra SphericalVideoXML tags)
1502
1550
  Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
1503
1551
  WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
1504
- HandlerType => 'vide', # only write in video tracks
1552
+ MediaType => 'vide', # only write in video tracks
1505
1553
  SubDirectory => {
1506
1554
  TagTable => 'Image::ExifTool::XMP::Main',
1507
1555
  Start => 16,
@@ -2209,8 +2257,8 @@ my %userDefined = (
2209
2257
  _cx_ => { Name => 'CX', Format => 'rational64s', Unknown => 1 },
2210
2258
  _cy_ => { Name => 'CY', Format => 'rational64s', Unknown => 1 },
2211
2259
  rads => { Name => 'Rads', Format => 'rational64s', Unknown => 1 },
2212
- lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2213
- Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess)
2260
+ lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2261
+ Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2214
2262
  pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2215
2263
  # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2216
2264
  # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
@@ -2553,7 +2601,7 @@ my %userDefined = (
2553
2601
  TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2554
2602
  TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2555
2603
  # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2556
- TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7f]+/g); "@a"', List => 1 },
2604
+ TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7e]+/g); "@a"', List => 1 },
2557
2605
  );
2558
2606
 
2559
2607
  # User-specific media data atoms (ref 11)
@@ -2809,7 +2857,7 @@ my %userDefined = (
2809
2857
  },
2810
2858
  iinf => [{
2811
2859
  Name => 'ItemInformation',
2812
- Condition => '$$valPt =~ /^\0/', # (check for version 0)
2860
+ Condition => '$$self{LastItemID} = -1; $$valPt =~ /^\0/', # (check for version 0)
2813
2861
  SubDirectory => {
2814
2862
  TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2815
2863
  Start => 6, # (4-byte version/flags + 2-byte count)
@@ -2829,10 +2877,18 @@ my %userDefined = (
2829
2877
  IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
2830
2878
  },
2831
2879
  },
2832
- 'keys' => {
2880
+ 'keys' => [{
2881
+ Name => 'AudioKeys',
2882
+ Condition => '$$self{MediaType} eq "soun"',
2883
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioKeys' },
2884
+ },{
2885
+ Name => 'VideoKeys',
2886
+ Condition => '$$self{MediaType} eq "vide"',
2887
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoKeys' },
2888
+ },{
2833
2889
  Name => 'Keys',
2834
2890
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
2835
- },
2891
+ }],
2836
2892
  bxml => {
2837
2893
  Name => 'BinaryXML',
2838
2894
  Flags => ['Binary','Unknown'],
@@ -2881,6 +2937,17 @@ my %userDefined = (
2881
2937
  %unknownInfo,
2882
2938
  },
2883
2939
  ],
2940
+ grpl => {
2941
+ Name => 'Unknown_grpl',
2942
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::grpl' },
2943
+ },
2944
+ );
2945
+
2946
+ # unknown grpl container
2947
+ %Image::ExifTool::QuickTime::grpl = (
2948
+ PROCESS_PROC => \&ProcessMOV,
2949
+ GROUPS => { 2 => 'Video' },
2950
+ # altr - seen "00 00 00 00 00 00 00 41 00 00 00 02 00 00 00 42 00 00 00 2e"
2884
2951
  );
2885
2952
 
2886
2953
  # additional metadata container (ref ISO14496-12:2015)
@@ -3025,6 +3092,7 @@ my %userDefined = (
3025
3092
  );
3026
3093
 
3027
3094
  # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
3095
+ # (NOTE: conversions are the same as Image::ExifTool::ICC_Profile::ColorRep tags)
3028
3096
  %Image::ExifTool::QuickTime::ColorRep = (
3029
3097
  PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3030
3098
  GROUPS => { 2 => 'Video' },
@@ -3097,6 +3165,7 @@ my %userDefined = (
3097
3165
  10 => {
3098
3166
  Name => 'VideoFullRangeFlag',
3099
3167
  Mask => 0x80,
3168
+ PrintConv => { 0 => 'Limited', 1 => 'Full' },
3100
3169
  },
3101
3170
  );
3102
3171
 
@@ -3307,7 +3376,13 @@ my %userDefined = (
3307
3376
  the associations between items in the file. This information is used by
3308
3377
  ExifTool, but these entries are not extracted as tags.
3309
3378
  },
3310
- dimg => { Name => 'DerivedImageRef', RawConv => 'undef' },
3379
+ dimg => {
3380
+ Name => 'DerivedImageRef',
3381
+ # also parse these for the ID of the primary 'tmap' item
3382
+ # (tone-mapped image in HDRGainMap HEIC by iPhone 15 and 16)
3383
+ RawConv => \&ParseContentDescribes,
3384
+ WriteHook => \&ParseContentDescribes,
3385
+ },
3311
3386
  thmb => { Name => 'ThumbnailRef', RawConv => 'undef' },
3312
3387
  auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
3313
3388
  cdsc => {
@@ -3325,7 +3400,8 @@ my %userDefined = (
3325
3400
  # hvc1 - HEVC image
3326
3401
  # lhv1 - L-HEVC image
3327
3402
  # infe - ItemInformationEntry
3328
- # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,hvt1(tile image)
3403
+ # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,tmap,hvt1(tile image)
3404
+ # ('tmap' has something to do with the new gainmap written by iPhone 15 and 16)
3329
3405
  infe => {
3330
3406
  Name => 'ItemInfoEntry',
3331
3407
  RawConv => \&ParseItemInfoEntry,
@@ -3446,7 +3522,11 @@ my %userDefined = (
3446
3522
  #
3447
3523
  '----' => {
3448
3524
  Name => 'iTunesInfo',
3449
- SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::iTunesInfo' },
3525
+ Deletable => 1, # (deletable via 'iTunes' group)
3526
+ SubDirectory => {
3527
+ TagTable => 'Image::ExifTool::QuickTime::iTunesInfo',
3528
+ DirName => 'iTunes', # (necessary for group 'iTunes' delete)
3529
+ },
3450
3530
  },
3451
3531
  aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
3452
3532
  covr => { Name => 'CoverArt', Groups => { 2 => 'Preview' }, Binary => 1 },
@@ -3553,7 +3633,7 @@ my %userDefined = (
3553
3633
  my $id = Image::ExifTool::ID3::GetGenreID($val);
3554
3634
  return unless defined $id and $id =~ /^\d+$/;
3555
3635
  return $id + 1;
3556
- },
3636
+ },
3557
3637
  },
3558
3638
  egid => 'EpisodeGlobalUniqueID', #7
3559
3639
  geID => { #10
@@ -6601,21 +6681,11 @@ my %userDefined = (
6601
6681
  publisher => { },
6602
6682
  software => { },
6603
6683
  year => { Groups => { 2 => 'Time' } },
6604
- 'camera.identifier' => 'CameraIdentifier', # (iPhone 4)
6605
- 'camera.framereadouttimeinmicroseconds' => { # (iPhone 4)
6606
- Name => 'FrameReadoutTime',
6607
- ValueConv => '$val * 1e-6',
6608
- ValueConvInv => 'int($val * 1e6 + 0.5)',
6609
- PrintConv => '$val * 1e6 . " microseconds"',
6610
- PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6611
- },
6612
- # 'camera.focal_length.35mm_equivalent' - not top level (written to Keys in video track)
6613
- # 'camera.lens_model' - not top level (written to Keys in video track)
6614
6684
  'location.ISO6709' => {
6615
6685
  Name => 'GPSCoordinates',
6616
6686
  Groups => { 2 => 'Location' },
6617
6687
  Notes => q{
6618
- Google Photos may ignore this if the coorinates have more than 5 digits
6688
+ Google Photos may ignore this if the coordinates have more than 5 digits
6619
6689
  after the decimal
6620
6690
  },
6621
6691
  ValueConv => \&ConvertISO6709,
@@ -6671,7 +6741,6 @@ my %userDefined = (
6671
6741
  #
6672
6742
  # the following tags aren't in the com.apple.quicktime namespace:
6673
6743
  #
6674
- 'com.apple.photos.captureMode' => 'CaptureMode',
6675
6744
  'com.android.version' => 'AndroidVersion',
6676
6745
  'com.android.capture.fps' => { Name => 'AndroidCaptureFPS', Writable => 'float' },
6677
6746
  'com.android.manufacturer' => 'AndroidMake',
@@ -6753,6 +6822,7 @@ my %userDefined = (
6753
6822
  ValueConv => 'unpack("N", $val)',
6754
6823
  Writable => 0, # (don't make this writable because it is found in timed metadata)
6755
6824
  },
6825
+ 'full-frame-rate-playback-intent' => 'FullFrameRatePlaybackIntent', #forum16824
6756
6826
  #
6757
6827
  # seen in Apple ProRes RAW file
6758
6828
  #
@@ -6770,6 +6840,58 @@ my %userDefined = (
6770
6840
  # (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
6771
6841
  );
6772
6842
 
6843
+ # Keys tags in the audio track (ref PH)
6844
+ %Image::ExifTool::QuickTime::AudioKeys = (
6845
+ PROCESS_PROC => \&ProcessKeys,
6846
+ WRITE_PROC => \&WriteKeys,
6847
+ CHECK_PROC => \&CheckQTValue,
6848
+ WRITABLE => 1,
6849
+ GROUPS => { 1 => 'AudioKeys', 2 => 'Audio' },
6850
+ WRITE_GROUP => 'AudioKeys',
6851
+ LANG_INFO => \&GetLangInfo,
6852
+ NOTES => q{
6853
+ Keys tags written in the audio track by some Apple devices. These tags
6854
+ belong to the ExifTool AudioKeys family 1 gorup.
6855
+ },
6856
+ 'player.movie.audio.gain' => 'AudioGain',
6857
+ 'player.movie.audio.treble' => 'Treble',
6858
+ 'player.movie.audio.bass' => 'Bass',
6859
+ 'player.movie.audio.balance' => 'Balance',
6860
+ 'player.movie.audio.pitchshift' => 'PitchShift',
6861
+ 'player.movie.audio.mute' => {
6862
+ Name => 'Mute',
6863
+ Format => 'int8u',
6864
+ PrintConv => { 0 => 'Off', 1 => 'On' },
6865
+ },
6866
+ );
6867
+
6868
+ # Keys tags in the video track (ref PH)
6869
+ %Image::ExifTool::QuickTime::VideoKeys = (
6870
+ PROCESS_PROC => \&ProcessKeys,
6871
+ WRITE_PROC => \&WriteKeys,
6872
+ CHECK_PROC => \&CheckQTValue,
6873
+ VARS => { LONG_TAGS => 2 },
6874
+ WRITABLE => 1,
6875
+ GROUPS => { 1 => 'VideoKeys', 2 => 'Camera' },
6876
+ WRITE_GROUP => 'VideoKeys',
6877
+ LANG_INFO => \&GetLangInfo,
6878
+ NOTES => q{
6879
+ Keys tags written in the video track. These tags belong to the ExifTool
6880
+ VideoKeys family 1 gorup.
6881
+ },
6882
+ 'camera.identifier' => 'CameraIdentifier',
6883
+ 'camera.lens_model' => 'LensModel',
6884
+ 'camera.focal_length.35mm_equivalent' => 'FocalLengthIn35mmFormat',
6885
+ 'camera.framereadouttimeinmicroseconds' => {
6886
+ Name => 'FrameReadoutTime',
6887
+ ValueConv => '$val * 1e-6',
6888
+ ValueConvInv => 'int($val * 1e6 + 0.5)',
6889
+ PrintConv => '$val * 1e6 . " microseconds"',
6890
+ PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6891
+ },
6892
+ 'com.apple.photos.captureMode' => 'CaptureMode',
6893
+ );
6894
+
6773
6895
  # iTunes info ('----') atoms
6774
6896
  %Image::ExifTool::QuickTime::iTunesInfo = (
6775
6897
  PROCESS_PROC => \&ProcessMOV,
@@ -7235,7 +7357,7 @@ my %userDefined = (
7235
7357
  {
7236
7358
  Name => 'VideoFrameRate',
7237
7359
  Notes => 'average rate calculated from time-to-sample table for video media',
7238
- Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
7360
+ Condition => '$$self{MediaType} eq "vide"',
7239
7361
  Format => 'undef', # (necessary to prevent decoding as string!)
7240
7362
  # (must be RawConv so appropriate MediaTS is used in calculation)
7241
7363
  RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
@@ -7826,7 +7948,6 @@ my %userDefined = (
7826
7948
  mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
7827
7949
  # sing - seen 4 zeros
7828
7950
  # hi32 - seen "00 00 00 04"
7829
-
7830
7951
  );
7831
7952
 
7832
7953
  # MP4 hint sample description box (ref 5)
@@ -8074,6 +8195,7 @@ my %userDefined = (
8074
8195
  Format => 'undef[4]',
8075
8196
  RawConv => q{
8076
8197
  $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
8198
+ $$self{MediaType} = $val if @{$$self{PATH}} > 1 and $$self{PATH}[-2] eq 'Media';
8077
8199
  $$self{HasHandler}{$val} = 1; # remember all our handlers
8078
8200
  return $val;
8079
8201
  },
@@ -8658,24 +8780,28 @@ sub UnpackLang($;$)
8658
8780
  # Get language code string given QuickTime language and country codes
8659
8781
  # Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
8660
8782
  # Returns: language code string (ie. "fra-FR") or undef for default language
8783
+ # ex) 0x15c7 0x0000 is 'eng' with no country (ie. returns 'und' unless $noDef)
8784
+ # 0x15c7 0x5553 is 'eng-US'
8785
+ # 0x1a41 0x4652 is 'fra-FR'
8786
+ # 0x55c4 is 'und'
8661
8787
  sub GetLangCode($;$$)
8662
8788
  {
8663
8789
  my ($lang, $ctry, $noDef) = @_;
8664
8790
  # ignore country ('ctry') and language lists ('lang') for now
8665
8791
  undef $ctry if $ctry and $ctry <= 255;
8666
8792
  undef $lang if $lang and $lang <= 255;
8667
- $lang = UnpackLang($lang, $noDef);
8793
+ my $langCode = UnpackLang($lang, $noDef);
8668
8794
  # add country code if specified
8669
8795
  if ($ctry) {
8670
8796
  $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
8671
8797
  # treat 'ZZ' like a default country (see ref 12)
8672
8798
  undef $ctry if $ctry eq 'ZZ';
8673
8799
  if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
8674
- $lang or $lang = 'und';
8675
- $lang .= "-$ctry";
8800
+ $langCode or $langCode = UnpackLang($lang,1) || 'und';
8801
+ $langCode .= "-$ctry";
8676
8802
  }
8677
8803
  }
8678
- return $lang;
8804
+ return $langCode;
8679
8805
  }
8680
8806
 
8681
8807
  #------------------------------------------------------------------------------
@@ -8773,6 +8899,7 @@ sub PrintableTagID($;$)
8773
8899
  # ContentType - mime type of item
8774
8900
  # ContentEncoding - item encoding
8775
8901
  # URI - URI of a 'uri '-type item
8902
+ # infe - raw data for 'infe' box (when writing only) [retracted]
8776
8903
  # ipma:
8777
8904
  # Association - list of associated properties in the ipco container
8778
8905
  # Essential - list of "essential" flags for the associated properties
@@ -8923,10 +9050,18 @@ sub ParseItemInfoEntry($$)
8923
9050
  $$items{$id}{URI} = GetString(\$val, $pos);
8924
9051
  }
8925
9052
  }
9053
+ #[retracted] # save raw infe box when writing in case we need to sort items later
9054
+ #[retracted] $$items{$id}{infe} = pack('N', length($val)+8) . 'infe' . $val if $$et{IsWriting};
8926
9055
  $et->VPrint(1, "$$et{INDENT} Item $id: Type=", $$items{$id}{Type} || '',
8927
9056
  ' Name=', $$items{$id}{Name} || '',
8928
9057
  ' ContentType=', $$items{$id}{ContentType} || '',
9058
+ ($$et{PrimaryItem} and $$et{PrimaryItem} == $id) ? ' (PrimaryItem)' : '',
8929
9059
  "\n") if $verbose > 1;
9060
+ unless ($id > $$et{LastItemID}) {
9061
+ $et->Warn('Item info entries are out of order'); #[retracted] unless $$et{IsWriting};
9062
+ #[retracted] $$et{ItemsNotSorted} = 1; # set flag indicating the items weren't sorted
9063
+ }
9064
+ $$et{LastItemID} = $id;
8930
9065
  return undef;
8931
9066
  }
8932
9067
 
@@ -8947,6 +9082,7 @@ sub ParseItemPropAssoc($$)
8947
9082
  my $flg = Get32u(\$val, 0);
8948
9083
  my $num = Get32u(\$val, 4);
8949
9084
  my $pos = 8;
9085
+ my $lastID = -1;
8950
9086
  for ($i=0; $i<$num; ++$i) {
8951
9087
  if ($ver == 0) {
8952
9088
  return undef if $pos + 3 > $len;
@@ -8979,6 +9115,9 @@ sub ParseItemPropAssoc($$)
8979
9115
  $$items{$id}{Association} = \@association;
8980
9116
  $$items{$id}{Essential} = \@essential;
8981
9117
  $et->VPrint(1, "$$et{INDENT} Item $id properties: @association\n") if $verbose > 1;
9118
+ # (according to ISO/IEC 23008-12, these entries must be sorted by item ID)
9119
+ $et->Warn('Item property association entries are out of order') unless $id > $lastID;
9120
+ $lastID = $id;
8982
9121
  }
8983
9122
  return undef;
8984
9123
  }
@@ -9025,18 +9164,21 @@ sub HandleItemInfo($)
9025
9164
  }
9026
9165
  }
9027
9166
  $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
9028
- $warn = "Can't currently extract $type with construction method $$item{ConstructionMethod}" if $$item{ConstructionMethod};
9029
- $et->WarnOnce($warn) if $warn and $name;
9167
+ # Note: In HEIC's, these seem to indicate data in 'idat' instead of 'mdat'
9168
+ my $constMeth = $$item{ConstructionMethod} || 0;
9169
+ $warn = "Can't currently extract $type with construction method $constMeth" if $constMeth > 1;
9170
+ $warn = "No 'idat' for $type object with construction method 1" if $constMeth == 1 and not $$et{MediaDataInfo};
9171
+ $et->Warn($warn) if $warn and $name;
9030
9172
  $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
9031
9173
  unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
9032
9174
  $warn = "No Extents for $type item";
9033
- $et->WarnOnce($warn) if $name;
9175
+ $et->Warn($warn) if $name;
9034
9176
  }
9035
9177
  if ($warn) {
9036
9178
  $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9037
9179
  next;
9038
9180
  }
9039
- my $base = $$item{BaseOffset} || 0;
9181
+ my $base = ($$item{BaseOffset} || 0) + ($constMeth ? $$et{MediaDataInfo}[0] : 0);
9040
9182
  if ($verbose > 2) {
9041
9183
  # do verbose hex dump
9042
9184
  my $len = 0;
@@ -9094,7 +9236,7 @@ sub HandleItemInfo($)
9094
9236
  $et->VerboseDump(\$buff);
9095
9237
  } else {
9096
9238
  $warn = "Error inflating $name metadata";
9097
- $et->WarnOnce($warn);
9239
+ $et->Warn($warn);
9098
9240
  $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9099
9241
  next;
9100
9242
  }
@@ -9175,6 +9317,7 @@ sub HandleItemInfo($)
9175
9317
  delete $$et{DOC_NUM};
9176
9318
  }
9177
9319
  delete $$et{ItemInfo};
9320
+ delete $$et{MediaDataInfo};
9178
9321
  }
9179
9322
 
9180
9323
  #------------------------------------------------------------------------------
@@ -9183,7 +9326,7 @@ sub HandleItemInfo($)
9183
9326
  sub EEWarn($)
9184
9327
  {
9185
9328
  my $et = shift;
9186
- $et->WarnOnce('The ExtractEmbedded option may find more tags in the media data',3);
9329
+ $et->Warn('The ExtractEmbedded option may find more tags in the media data',3);
9187
9330
  }
9188
9331
 
9189
9332
  #------------------------------------------------------------------------------
@@ -9476,6 +9619,8 @@ sub ProcessKeys($$$)
9476
9619
  my $groups = $$tagInfo{Groups};
9477
9620
  $$newInfo{Groups} = $groups ? { %$groups } : { };
9478
9621
  $$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
9622
+ # set Keys group. This is necessary for logic when reading the associated ItemList entry,
9623
+ # but note that the group name will be overridden by TAG_EXTRA G1 for tags in a track
9479
9624
  $$newInfo{Groups}{1} = 'Keys';
9480
9625
  } elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
9481
9626
  # create info for tags with reasonable id's
@@ -9522,6 +9667,43 @@ sub ProcessMetaKeys($$$)
9522
9667
  return 1;
9523
9668
  }
9524
9669
 
9670
+ #------------------------------------------------------------------------------
9671
+ # Identify trailers at specified offset from end of file
9672
+ # Inputs: 0) RAF reference, 1) Offset from end of file
9673
+ # Returns: Array ref to first trailer in linked list: 0) name of trailer,
9674
+ # 1) absolute offset to start of this trailer, 2) trailer length,
9675
+ # 3) ref to next trailer. Or undef if no trailer found, or error string on error
9676
+ # - file position is returned to its original location
9677
+ sub IdentifyTrailers($)
9678
+ {
9679
+ my $raf = shift;
9680
+ my ($trailer, $nextTrail, $buff, $type, $len);
9681
+ my $pos = $raf->Tell();
9682
+ my $offset = 0; # positive offset back from end of file
9683
+ while ($raf->Seek(-40-$offset, 2) and $raf->Read($buff, 40) == 40) {
9684
+ if (substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf') {
9685
+ ($type, $len) = ('Insta360', unpack('V',$buff));
9686
+ } elsif ($buff =~ /\&\&\&\&(.{4})$/) {
9687
+ ($type, $len) = ('LigoGPS', Get32u(\$buff, 36));
9688
+ } elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}([\x10\x18])(\x04)$/s or
9689
+ $buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}([\x10\x18])(\x08)$/s)
9690
+ {
9691
+ my $oldOrder = GetByteOrder();
9692
+ SetByteOrder($1 eq "\x10" ? 'MM' : 'II');
9693
+ $type = 'MIE';
9694
+ $len = ($2 eq "\x04") ? Get32u(\$buff, 34) : Get64u(\$buff, 30);
9695
+ SetByteOrder($oldOrder);
9696
+ } else {
9697
+ last;
9698
+ }
9699
+ $trailer = [ $type , $raf->Tell() - $len, $len, $nextTrail ];
9700
+ $nextTrail = $trailer;
9701
+ $offset += $len;
9702
+ }
9703
+ $raf->Seek($pos,0) or return 'Seek error';
9704
+ return $trailer;
9705
+ }
9706
+
9525
9707
  #------------------------------------------------------------------------------
9526
9708
  # Process a QuickTime atom
9527
9709
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
@@ -9543,7 +9725,7 @@ sub ProcessMOV($$;$)
9543
9725
 
9544
9726
  my $topLevel = not $$et{InQuickTime};
9545
9727
  $$et{InQuickTime} = 1;
9546
- $$et{HandlerType} = $$et{MetaFormat} = '' unless defined $$et{HandlerType};
9728
+ $$et{HandlerType} = $$et{MetaFormat} = $$et{MediaType} = '' if $topLevel;
9547
9729
 
9548
9730
  unless (defined $$et{KeysCount}) {
9549
9731
  $$et{KeysCount} = 0; # initialize ItemList key directory count
@@ -9569,15 +9751,10 @@ sub ProcessMOV($$;$)
9569
9751
  }
9570
9752
  ($size, $tag) = unpack('Na4', $buff);
9571
9753
  my $fast = $$et{OPTIONS}{FastScan} || 0;
9572
- # check for Insta360 trailer
9754
+ # check for Insta360, LIGOGPSINFO or MIE trailer
9573
9755
  if ($topLevel and not $fast) {
9574
- my $pos = $raf->Tell();
9575
- if ($raf->Seek(-40, 2) and $raf->Read($buff, 40) == 40 and
9576
- substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf')
9577
- {
9578
- $trailer = [ 'Insta360', $raf->Tell() - unpack('V',$buff) ];
9579
- }
9580
- $raf->Seek($pos,0) or return 0;
9756
+ $trailer = IdentifyTrailers($raf);
9757
+ $trailer and not ref $trailer and $et->Warn($trailer), return 0;
9581
9758
  }
9582
9759
  if ($dataPt) {
9583
9760
  $verbose and $et->VerboseDir($$dirInfo{DirName});
@@ -9670,7 +9847,7 @@ sub ProcessMOV($$;$)
9670
9847
  $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
9671
9848
  last;
9672
9849
  } elsif ($et->Options('LargeFileSupport') eq '2') {
9673
- $et->WarnOnce('Processing large atom (LargeFileSupport is 2)');
9850
+ $et->Warn('Processing large atom (LargeFileSupport is 2)');
9674
9851
  }
9675
9852
  }
9676
9853
  $size = $hi * 4294967296 + $lo - 16;
@@ -9685,23 +9862,25 @@ sub ProcessMOV($$;$)
9685
9862
  if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
9686
9863
  my $i = Get32u(\$tag,0);
9687
9864
  my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
9688
- $et->WarnOnce("Duplicate $str at " . join('-', @{$$et{PATH}}));
9865
+ $et->Warn("Duplicate $str at " . join('-', @{$$et{PATH}}));
9689
9866
  $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
9690
9867
  }
9691
9868
  $$et{ValidatePath}{$path} = 1;
9692
9869
  }
9693
9870
  if ($isUserData and $$et{SET_GROUP1}) {
9694
9871
  my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9695
- # add track name to UserData tags inside tracks
9696
- $tag = $$et{SET_GROUP1} . $tag;
9697
- if (not $$tagTablePtr{$tag} and $tagInfo) {
9698
- my %newInfo = %$tagInfo;
9699
- foreach ('Name', 'Description') {
9700
- next unless $$tagInfo{$_};
9701
- $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
9702
- $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
9872
+ unless ($$tagInfo{SubDirectory}) {
9873
+ # add track name to UserData tags inside tracks
9874
+ $tag = $$et{SET_GROUP1} . $tag;
9875
+ if (not $$tagTablePtr{$tag} and $tagInfo) {
9876
+ my %newInfo = %$tagInfo;
9877
+ foreach ('Name', 'Description') {
9878
+ next unless $$tagInfo{$_};
9879
+ $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
9880
+ $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
9881
+ }
9882
+ AddTagToTable($tagTablePtr, $tag, \%newInfo);
9703
9883
  }
9704
- AddTagToTable($tagTablePtr, $tag, \%newInfo);
9705
9884
  }
9706
9885
  }
9707
9886
  # set flag to store additional information for ExtractEmbedded option
@@ -9757,8 +9936,10 @@ sub ProcessMOV($$;$)
9757
9936
  # save required tag sizes
9758
9937
  if ($$tagTablePtr{"$tag-size"}) {
9759
9938
  $et->HandleTag($tagTablePtr, "$tag-size", $size);
9760
- $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()) if $$tagTablePtr{"$tag-offset"};
9939
+ $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()+$dirBase) if $$tagTablePtr{"$tag-offset"};
9761
9940
  }
9941
+ # save position/size of 'idat'
9942
+ $$et{MediaDataInfo} = [ $raf->Tell() + $dirBase, $size ] if $tag eq 'idat';
9762
9943
  # stop processing at mdat/idat if -fast2 is used
9763
9944
  last if $fast > 1 and ($tag eq 'mdat' or ($tag eq 'idat' and $$et{FileType} ne 'HEIC'));
9764
9945
  # load values only if associated with a tag (or verbose) and not too big
@@ -9766,7 +9947,7 @@ sub ProcessMOV($$;$)
9766
9947
  # check for RIFF trailer (written by Auto-Vox dashcam)
9767
9948
  if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
9768
9949
  $et->VPrint(0, sprintf("Found RIFF trailer at offset 0x%x",$lastPos));
9769
- if ($et->Options('ExtractEmbedded')) {
9950
+ if ($ee) {
9770
9951
  $raf->Seek(-8, 1) or last; # seek back to start of trailer
9771
9952
  my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
9772
9953
  ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
@@ -9916,6 +10097,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
9916
10097
  }
9917
10098
  }
9918
10099
  if ($tagInfo) {
10100
+ my @found;
9919
10101
  my $subdir = $$tagInfo{SubDirectory};
9920
10102
  if ($subdir) {
9921
10103
  my $start = $$subdir{Start} || 0;
@@ -10034,21 +10216,23 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10034
10216
  Index => $index,
10035
10217
  Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
10036
10218
  ) if $verbose;
10037
- # use "Keys" in path instead of ItemList if this was defined by a Keys tag
10038
- my $isKey = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
10039
- if ($isKey) {
10040
- $oldDir = $$et{PATH}[-1];
10041
- $$et{PATH}[-1] = 'Keys';
10219
+ if (defined $value) {
10220
+ # use "Keys" in path instead of ItemList if this was defined by a Keys tag
10221
+ # (the only reason for this is to have "Keys" in the family 5 group name)
10222
+ # Note that the Keys group is specifically set by the ProcessKeys routine,
10223
+ # even though this tag would be in the ItemList table
10224
+ my $isKeys = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
10225
+ $isKeys and $oldDir = $$et{PATH}[-1], $$et{PATH}[-1] = 'Keys';
10226
+ push @found, $et->FoundTag($langInfo, $value);
10227
+ $$et{PATH}[-1] = $oldDir if $isKeys;
10042
10228
  }
10043
- $et->FoundTag($langInfo, $value) if defined $value;
10044
- $$et{PATH}[-1] = $oldDir if $isKey;
10045
10229
  $pos += $len;
10046
10230
  }
10047
10231
  } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
10048
10232
  # parse international text to extract all languages
10049
10233
  my $pos = 0;
10050
10234
  if ($$tagInfo{Format}) {
10051
- $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
10235
+ push @found, $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
10052
10236
  $pos = $size;
10053
10237
  }
10054
10238
  for (;;) {
@@ -10113,7 +10297,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10113
10297
  $str = substr($val, $pos-$n-2, $n) . $str;
10114
10298
  }
10115
10299
  $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
10116
- $et->FoundTag($langInfo || $tagInfo, $str);
10300
+ push @found, $et->FoundTag($langInfo || $tagInfo, $str);
10117
10301
  $pos += $len;
10118
10302
  }
10119
10303
  } else {
@@ -10127,6 +10311,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10127
10311
  $$et{BASE} = $dataPos;
10128
10312
  }
10129
10313
  my $key = $et->FoundTag($tagInfo, $val);
10314
+ push @found, $key;
10130
10315
  $$et{BASE} = $oldBase if defined $oldBase;
10131
10316
  # decode if necessary (NOTE: must be done after RawConv)
10132
10317
  if (defined $key and (not $format or $format =~ /^string/) and
@@ -10142,6 +10327,14 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10142
10327
  }
10143
10328
  }
10144
10329
  }
10330
+ # tweak family 1 group names for Keys/ItemList/UserData tags in a track
10331
+ if ($$et{SET_GROUP1} and ($dirID eq 'ilst' or $dirID eq 'udta') and @found) {
10332
+ my $type = $trackPath{join '-', @{$$et{PATH}}};
10333
+ if ($type) {
10334
+ my $grp = ($avType{$$et{MediaType}} || $$et{SET_GROUP1}) . $type;
10335
+ defined and $et->SetGroup($_, $grp) foreach @found;
10336
+ }
10337
+ }
10145
10338
  }
10146
10339
  } else {
10147
10340
  $et->VerboseInfo($tag, $tagInfo,
@@ -10154,11 +10347,12 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10154
10347
  last;
10155
10348
  }
10156
10349
  }
10350
+ $$et{MediaType} = '' if $tag eq 'trak'; # reset track type at end of track
10157
10351
  $dataPos += $size + 8; # point to start of next atom data
10158
10352
  last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10159
10353
  $lastPos = $raf->Tell() + $dirBase;
10160
10354
  if ($trailer and $lastPos >= $$trailer[1]) {
10161
- $et->Warn(sprintf('%s trailer at offset 0x%x', @$trailer), 1);
10355
+ $et->Warn(sprintf('%s trailer at offset 0x%x (%d bytes)', @$trailer[0..2]), 1);
10162
10356
  last;
10163
10357
  }
10164
10358
  $raf->Read($buff, 8) == 8 or last;
@@ -10209,7 +10403,33 @@ QTLang: foreach $tag (@{$$et{QTLang}}) {
10209
10403
  # process item information now that we are done processing its 'meta' container
10210
10404
  HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
10211
10405
 
10212
- ScanMediaData($et) if $ee and $topLevel; # brute force scan for metadata embedded in media data
10406
+ # process linked list of trailers
10407
+ for (; $trailer; $trailer=$$trailer[3]) {
10408
+ next if $lastPos > $$trailer[1]; # skip if we have already processed this as an atom
10409
+ last unless $raf->Seek($$trailer[1], 0);
10410
+ if ($$trailer[0] eq 'LigoGPS' and $raf->Read($buff, 8) == 8 and $buff =~ /skip$/) {
10411
+ $ee or $et->Warn('Use the ExtractEmbedded option to decode timed GPS',3), next;
10412
+ my $len = Get32u(\$buff, 0) - 16;
10413
+ if ($len > 0 and $raf->Read($buff, $len) == $len and $buff =~ /^LIGOGPSINFO\0/) {
10414
+ my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10415
+ my %dirInfo = ( DataPt => \$buff, DataPos => $$trailer[1] + 8, DirName => 'LigoGPSTrailer' );
10416
+ Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl);
10417
+ } else {
10418
+ $et->Warn('Unrecognized data in LigoGPS trailer');
10419
+ }
10420
+ } elsif ($$trailer[0] eq 'Insta360' and $ee) {
10421
+ # process Insta360 trailer if it exists
10422
+ $raf->Seek(0, 2) or $et->Warn('Seek error'), last;
10423
+ my $offset = $raf->Tell() - $$trailer[1] - $$trailer[2];
10424
+ ProcessInsta360($et, { RAF => $raf, DirName => $$trailer[0], Offset => $offset });
10425
+ } elsif ($$trailer[0] eq 'MIE') {
10426
+ require Image::ExifTool::MIE;
10427
+ Image::ExifTool::MIE::ProcessMIE($et, { RAF => $raf, DirName => 'MIE', Trailer => 1 });
10428
+ }
10429
+ }
10430
+ # brute force scan for metadata embedded in media data
10431
+ # (and process Insta360 trailer if it exists)
10432
+ ScanMediaData($et) if $ee and $topLevel;
10213
10433
 
10214
10434
  # restore any changed options
10215
10435
  $et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
@@ -10227,6 +10447,24 @@ sub ProcessQTIF($$)
10227
10447
  return ProcessMOV($et, $dirInfo, $table);
10228
10448
  }
10229
10449
 
10450
+ #==============================================================================
10451
+ # Autoload LigoGPS module if necessary
10452
+ # NOTE: Switches to package LigoGPS!
10453
+ #
10454
+ package Image::ExifTool::LigoGPS;
10455
+ use vars qw($AUTOLOAD);
10456
+ sub AUTOLOAD {
10457
+ require Image::ExifTool::LigoGPS;
10458
+ unless (defined &$AUTOLOAD) {
10459
+ my @caller = caller(0);
10460
+ # reproduce Perl's standard 'undefined subroutine' message:
10461
+ die "Undefined subroutine $AUTOLOAD called at $caller[1] line $caller[2]\n";
10462
+ }
10463
+ no strict 'refs';
10464
+ return &$AUTOLOAD(@_); # call the function
10465
+ }
10466
+ #==============================================================================
10467
+
10230
10468
  1; # end
10231
10469
 
10232
10470
  __END__
@@ -10246,7 +10484,7 @@ information from QuickTime and MP4 video, M4A audio, and HEIC image files.
10246
10484
 
10247
10485
  =head1 AUTHOR
10248
10486
 
10249
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
10487
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
10250
10488
 
10251
10489
  This library is free software; you can redistribute it and/or modify it
10252
10490
  under the same terms as Perl itself.