exiftool-vendored.pl 13.0.1 → 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 (216) hide show
  1. package/bin/Changes +254 -20
  2. package/bin/MANIFEST +10 -0
  3. package/bin/META.json +1 -1
  4. package/bin/META.yml +1 -1
  5. package/bin/README +3 -3
  6. package/bin/arg_files/exif2xmp.args +4 -0
  7. package/bin/arg_files/xmp2exif.args +2 -1
  8. package/bin/build_geolocation +1 -1
  9. package/bin/exiftool +356 -213
  10. package/bin/lib/File/RandomAccess.pm +1 -1
  11. package/bin/lib/File/RandomAccess.pod +2 -2
  12. package/bin/lib/Image/ExifTool/AAC.pm +1 -1
  13. package/bin/lib/Image/ExifTool/AES.pm +1 -1
  14. package/bin/lib/Image/ExifTool/AFCP.pm +6 -6
  15. package/bin/lib/Image/ExifTool/AIFF.pm +2 -2
  16. package/bin/lib/Image/ExifTool/APE.pm +2 -2
  17. package/bin/lib/Image/ExifTool/APP12.pm +1 -1
  18. package/bin/lib/Image/ExifTool/ASF.pm +2 -2
  19. package/bin/lib/Image/ExifTool/Apple.pm +11 -9
  20. package/bin/lib/Image/ExifTool/Audible.pm +1 -1
  21. package/bin/lib/Image/ExifTool/BMP.pm +1 -1
  22. package/bin/lib/Image/ExifTool/BPG.pm +1 -1
  23. package/bin/lib/Image/ExifTool/BZZ.pm +1 -1
  24. package/bin/lib/Image/ExifTool/BigTIFF.pm +1 -1
  25. package/bin/lib/Image/ExifTool/BuildTagLookup.pm +36 -22
  26. package/bin/lib/Image/ExifTool/CBOR.pm +5 -2
  27. package/bin/lib/Image/ExifTool/Canon.pm +66 -27
  28. package/bin/lib/Image/ExifTool/CanonCustom.pm +1 -1
  29. package/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
  30. package/bin/lib/Image/ExifTool/CanonVRD.pm +1 -1
  31. package/bin/lib/Image/ExifTool/CaptureOne.pm +1 -1
  32. package/bin/lib/Image/ExifTool/Casio.pm +1 -1
  33. package/bin/lib/Image/ExifTool/Charset.pm +1 -1
  34. package/bin/lib/Image/ExifTool/DICOM.pm +1 -1
  35. package/bin/lib/Image/ExifTool/DJI.pm +196 -30
  36. package/bin/lib/Image/ExifTool/DNG.pm +1 -1
  37. package/bin/lib/Image/ExifTool/DPX.pm +1 -1
  38. package/bin/lib/Image/ExifTool/DV.pm +1 -1
  39. package/bin/lib/Image/ExifTool/DarwinCore.pm +1 -1
  40. package/bin/lib/Image/ExifTool/DjVu.pm +1 -1
  41. package/bin/lib/Image/ExifTool/EXE.pm +138 -33
  42. package/bin/lib/Image/ExifTool/Exif.pm +29 -16
  43. package/bin/lib/Image/ExifTool/FITS.pm +3 -3
  44. package/bin/lib/Image/ExifTool/FLAC.pm +1 -1
  45. package/bin/lib/Image/ExifTool/FLIF.pm +3 -3
  46. package/bin/lib/Image/ExifTool/FLIR.pm +1 -1
  47. package/bin/lib/Image/ExifTool/Fixup.pm +1 -1
  48. package/bin/lib/Image/ExifTool/Flash.pm +1 -1
  49. package/bin/lib/Image/ExifTool/FlashPix.pm +17 -21
  50. package/bin/lib/Image/ExifTool/Font.pm +2 -2
  51. package/bin/lib/Image/ExifTool/FotoStation.pm +1 -1
  52. package/bin/lib/Image/ExifTool/FujiFilm.pm +1 -1
  53. package/bin/lib/Image/ExifTool/GE.pm +1 -1
  54. package/bin/lib/Image/ExifTool/GIF.pm +144 -93
  55. package/bin/lib/Image/ExifTool/GIMP.pm +1 -1
  56. package/bin/lib/Image/ExifTool/GM.pm +1 -1
  57. package/bin/lib/Image/ExifTool/GPS.pm +34 -30
  58. package/bin/lib/Image/ExifTool/GeoTiff.pm +1 -1
  59. package/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  60. package/bin/lib/Image/ExifTool/Geolocation.pm +19 -9
  61. package/bin/lib/Image/ExifTool/Geotag.pm +46 -12
  62. package/bin/lib/Image/ExifTool/GoPro.pm +120 -8
  63. package/bin/lib/Image/ExifTool/H264.pm +1 -1
  64. package/bin/lib/Image/ExifTool/HP.pm +2 -2
  65. package/bin/lib/Image/ExifTool/HTML.pm +1 -1
  66. package/bin/lib/Image/ExifTool/HtmlDump.pm +1 -1
  67. package/bin/lib/Image/ExifTool/ICC_Profile.pm +81 -2
  68. package/bin/lib/Image/ExifTool/ICO.pm +1 -1
  69. package/bin/lib/Image/ExifTool/ID3.pm +8 -8
  70. package/bin/lib/Image/ExifTool/IPTC.pm +10 -7
  71. package/bin/lib/Image/ExifTool/ISO.pm +1 -1
  72. package/bin/lib/Image/ExifTool/ITC.pm +1 -1
  73. package/bin/lib/Image/ExifTool/Import.pm +5 -4
  74. package/bin/lib/Image/ExifTool/InDesign.pm +2 -2
  75. package/bin/lib/Image/ExifTool/InfiRay.pm +1 -1
  76. package/bin/lib/Image/ExifTool/JPEG.pm +32 -5
  77. package/bin/lib/Image/ExifTool/JPEGDigest.pm +1 -1
  78. package/bin/lib/Image/ExifTool/JSON.pm +1 -1
  79. package/bin/lib/Image/ExifTool/JVC.pm +1 -1
  80. package/bin/lib/Image/ExifTool/Jpeg2000.pm +10 -9
  81. package/bin/lib/Image/ExifTool/Kodak.pm +1 -1
  82. package/bin/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
  83. package/bin/lib/Image/ExifTool/LIF.pm +1 -1
  84. package/bin/lib/Image/ExifTool/LNK.pm +2 -2
  85. package/bin/lib/Image/ExifTool/Lang/cs.pm +1 -1
  86. package/bin/lib/Image/ExifTool/Lang/de.pm +1 -1
  87. package/bin/lib/Image/ExifTool/Lang/en_ca.pm +1 -1
  88. package/bin/lib/Image/ExifTool/Lang/en_gb.pm +1 -1
  89. package/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
  90. package/bin/lib/Image/ExifTool/Lang/fi.pm +1 -1
  91. package/bin/lib/Image/ExifTool/Lang/fr.pm +1 -1
  92. package/bin/lib/Image/ExifTool/Lang/it.pm +1 -1
  93. package/bin/lib/Image/ExifTool/Lang/ja.pm +1 -1
  94. package/bin/lib/Image/ExifTool/Lang/ko.pm +1 -1
  95. package/bin/lib/Image/ExifTool/Lang/nl.pm +1 -1
  96. package/bin/lib/Image/ExifTool/Lang/pl.pm +1 -1
  97. package/bin/lib/Image/ExifTool/Lang/ru.pm +1 -1
  98. package/bin/lib/Image/ExifTool/Lang/sk.pm +1 -1
  99. package/bin/lib/Image/ExifTool/Lang/sv.pm +1 -1
  100. package/bin/lib/Image/ExifTool/Lang/tr.pm +1 -1
  101. package/bin/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
  102. package/bin/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
  103. package/bin/lib/Image/ExifTool/Leaf.pm +1 -1
  104. package/bin/lib/Image/ExifTool/LigoGPS.pm +409 -0
  105. package/bin/lib/Image/ExifTool/Lytro.pm +1 -1
  106. package/bin/lib/Image/ExifTool/M2TS.pm +57 -18
  107. package/bin/lib/Image/ExifTool/MIE.pm +15 -6
  108. package/bin/lib/Image/ExifTool/MIEUnits.pod +1 -1
  109. package/bin/lib/Image/ExifTool/MIFF.pm +1 -1
  110. package/bin/lib/Image/ExifTool/MISB.pm +1 -1
  111. package/bin/lib/Image/ExifTool/MNG.pm +1 -1
  112. package/bin/lib/Image/ExifTool/MOI.pm +1 -1
  113. package/bin/lib/Image/ExifTool/MPC.pm +1 -1
  114. package/bin/lib/Image/ExifTool/MPEG.pm +1 -1
  115. package/bin/lib/Image/ExifTool/MPF.pm +1 -1
  116. package/bin/lib/Image/ExifTool/MRC.pm +1 -1
  117. package/bin/lib/Image/ExifTool/MWG.pm +1 -1
  118. package/bin/lib/Image/ExifTool/MXF.pm +3 -3
  119. package/bin/lib/Image/ExifTool/MacOS.pm +3 -2
  120. package/bin/lib/Image/ExifTool/MakerNotes.pm +1 -1
  121. package/bin/lib/Image/ExifTool/Matroska.pm +22 -6
  122. package/bin/lib/Image/ExifTool/Microsoft.pm +2 -2
  123. package/bin/lib/Image/ExifTool/Minolta.pm +1 -1
  124. package/bin/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
  125. package/bin/lib/Image/ExifTool/Motorola.pm +1 -1
  126. package/bin/lib/Image/ExifTool/Nikon.pm +495 -39
  127. package/bin/lib/Image/ExifTool/NikonCapture.pm +1 -1
  128. package/bin/lib/Image/ExifTool/NikonCustom.pm +2 -2
  129. package/bin/lib/Image/ExifTool/NikonSettings.pm +1 -1
  130. package/bin/lib/Image/ExifTool/Nintendo.pm +1 -1
  131. package/bin/lib/Image/ExifTool/OOXML.pm +8 -8
  132. package/bin/lib/Image/ExifTool/Ogg.pm +1 -1
  133. package/bin/lib/Image/ExifTool/Olympus.pm +1 -1
  134. package/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
  135. package/bin/lib/Image/ExifTool/Opus.pm +1 -1
  136. package/bin/lib/Image/ExifTool/Other.pm +1 -1
  137. package/bin/lib/Image/ExifTool/PCX.pm +1 -1
  138. package/bin/lib/Image/ExifTool/PDF.pm +49 -18
  139. package/bin/lib/Image/ExifTool/PGF.pm +1 -1
  140. package/bin/lib/Image/ExifTool/PICT.pm +1 -1
  141. package/bin/lib/Image/ExifTool/PLIST.pm +4 -4
  142. package/bin/lib/Image/ExifTool/PLUS.pm +1 -1
  143. package/bin/lib/Image/ExifTool/PNG.pm +20 -8
  144. package/bin/lib/Image/ExifTool/PPM.pm +12 -3
  145. package/bin/lib/Image/ExifTool/PSP.pm +1 -1
  146. package/bin/lib/Image/ExifTool/Palm.pm +1 -1
  147. package/bin/lib/Image/ExifTool/Panasonic.pm +27 -3
  148. package/bin/lib/Image/ExifTool/PanasonicRaw.pm +1 -1
  149. package/bin/lib/Image/ExifTool/Parrot.pm +1 -1
  150. package/bin/lib/Image/ExifTool/Pentax.pm +1 -1
  151. package/bin/lib/Image/ExifTool/PhaseOne.pm +4 -4
  152. package/bin/lib/Image/ExifTool/PhotoCD.pm +1 -1
  153. package/bin/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
  154. package/bin/lib/Image/ExifTool/Photoshop.pm +65 -4
  155. package/bin/lib/Image/ExifTool/PostScript.pm +1 -1
  156. package/bin/lib/Image/ExifTool/PrintIM.pm +1 -1
  157. package/bin/lib/Image/ExifTool/Protobuf.pm +270 -0
  158. package/bin/lib/Image/ExifTool/Qualcomm.pm +1 -1
  159. package/bin/lib/Image/ExifTool/QuickTime.pm +326 -88
  160. package/bin/lib/Image/ExifTool/QuickTimeStream.pl +199 -195
  161. package/bin/lib/Image/ExifTool/README +12 -2
  162. package/bin/lib/Image/ExifTool/RIFF.pm +21 -6
  163. package/bin/lib/Image/ExifTool/RSRC.pm +1 -1
  164. package/bin/lib/Image/ExifTool/RTF.pm +2 -2
  165. package/bin/lib/Image/ExifTool/Radiance.pm +1 -1
  166. package/bin/lib/Image/ExifTool/Rawzor.pm +1 -1
  167. package/bin/lib/Image/ExifTool/Real.pm +1 -1
  168. package/bin/lib/Image/ExifTool/Reconyx.pm +1 -1
  169. package/bin/lib/Image/ExifTool/Red.pm +1 -1
  170. package/bin/lib/Image/ExifTool/Ricoh.pm +4 -4
  171. package/bin/lib/Image/ExifTool/Samsung.pm +2 -2
  172. package/bin/lib/Image/ExifTool/Sanyo.pm +1 -1
  173. package/bin/lib/Image/ExifTool/Scalado.pm +1 -1
  174. package/bin/lib/Image/ExifTool/Shift.pl +1 -1
  175. package/bin/lib/Image/ExifTool/Shortcuts.pm +1 -1
  176. package/bin/lib/Image/ExifTool/Sigma.pm +1 -1
  177. package/bin/lib/Image/ExifTool/SigmaRaw.pm +1 -1
  178. package/bin/lib/Image/ExifTool/Sony.pm +5 -4
  179. package/bin/lib/Image/ExifTool/SonyIDC.pm +1 -1
  180. package/bin/lib/Image/ExifTool/Stim.pm +1 -1
  181. package/bin/lib/Image/ExifTool/TagInfoXML.pm +6 -5
  182. package/bin/lib/Image/ExifTool/TagLookup.pm +7023 -6968
  183. package/bin/lib/Image/ExifTool/TagNames.pod +445 -29
  184. package/bin/lib/Image/ExifTool/Text.pm +4 -3
  185. package/bin/lib/Image/ExifTool/Theora.pm +1 -1
  186. package/bin/lib/Image/ExifTool/Torrent.pm +3 -3
  187. package/bin/lib/Image/ExifTool/Unknown.pm +1 -1
  188. package/bin/lib/Image/ExifTool/VCard.pm +3 -3
  189. package/bin/lib/Image/ExifTool/Validate.pm +6 -6
  190. package/bin/lib/Image/ExifTool/Vivo.pm +124 -0
  191. package/bin/lib/Image/ExifTool/Vorbis.pm +1 -1
  192. package/bin/lib/Image/ExifTool/WPG.pm +1 -1
  193. package/bin/lib/Image/ExifTool/WTV.pm +1 -1
  194. package/bin/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  195. package/bin/lib/Image/ExifTool/WriteExif.pl +3 -3
  196. package/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  197. package/bin/lib/Image/ExifTool/WritePDF.pl +1 -1
  198. package/bin/lib/Image/ExifTool/WritePNG.pl +1 -1
  199. package/bin/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
  200. package/bin/lib/Image/ExifTool/WritePostScript.pl +1 -1
  201. package/bin/lib/Image/ExifTool/WriteQuickTime.pl +166 -79
  202. package/bin/lib/Image/ExifTool/WriteRIFF.pl +17 -6
  203. package/bin/lib/Image/ExifTool/WriteXMP.pl +3 -3
  204. package/bin/lib/Image/ExifTool/Writer.pl +89 -96
  205. package/bin/lib/Image/ExifTool/XISF.pm +1 -1
  206. package/bin/lib/Image/ExifTool/XMP.pm +28 -13
  207. package/bin/lib/Image/ExifTool/XMP2.pl +103 -1
  208. package/bin/lib/Image/ExifTool/XMPStruct.pl +2 -3
  209. package/bin/lib/Image/ExifTool/ZIP.pm +2 -2
  210. package/bin/lib/Image/ExifTool/ZISRAW.pm +1 -1
  211. package/bin/lib/Image/ExifTool/iWork.pm +1 -1
  212. package/bin/lib/Image/ExifTool.pm +335 -163
  213. package/bin/lib/Image/ExifTool.pod +119 -73
  214. package/bin/perl-Image-ExifTool.spec +1 -1
  215. package/bin/windows_exiftool.txt +96 -51
  216. package/package.json +6 -6
@@ -15,6 +15,8 @@ my %movMap = (
15
15
  QuickTime => 'ItemList', # (default location for QuickTime tags)
16
16
  ItemList => 'Meta', # MOV-Movie-UserData-Meta-ItemList
17
17
  Keys => 'Movie', # MOV-Movie-Meta-Keys !! (hack due to different Meta location)
18
+ AudioKeys => 'Track', # MOV-Movie-Track-Meta-Keys !!
19
+ VideoKeys => 'Track', # MOV-Movie-Track-Meta-Keys !!
18
20
  Meta => 'UserData',
19
21
  XMP => 'UserData', # MOV-Movie-UserData-XMP
20
22
  Microsoft => 'UserData', # MOV-Movie-UserData-Microsoft
@@ -29,6 +31,8 @@ my %mp4Map = (
29
31
  QuickTime => 'ItemList', # (default location for QuickTime tags)
30
32
  ItemList => 'Meta', # MOV-Movie-UserData-Meta-ItemList
31
33
  Keys => 'Movie', # MOV-Movie-Meta-Keys !! (hack due to different Meta location)
34
+ AudioKeys => 'Track', # MOV-Movie-Track-Meta-Keys !!
35
+ VideoKeys => 'Track', # MOV-Movie-Track-Meta-Keys !!
32
36
  Meta => 'UserData',
33
37
  UserData => 'Movie', # MOV-Movie-UserData
34
38
  Microsoft => 'UserData', # MOV-Movie-UserData-Microsoft
@@ -339,7 +343,7 @@ sub FormatQTValue($$;$$)
339
343
  $flags = 0x01; # UTF8
340
344
  $$valPt = $et->Encode($$valPt, 'UTF8');
341
345
  }
342
- defined $$valPt or $et->WarnOnce("Error converting value for $$tagInfo{Name}");
346
+ defined $$valPt or $et->Warn("Error converting value for $$tagInfo{Name}");
343
347
  return $flags;
344
348
  }
345
349
 
@@ -374,6 +378,9 @@ sub WriteNextbase($$$)
374
378
  # Write Meta Keys to add/delete entries as necessary ('mdta' handler) (ref PH)
375
379
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
376
380
  # Returns: updated keys box data
381
+ # Note: Residual entries may be left in the 'keys' directory when deleting tags
382
+ # with language codes because the language code(s) are not known until the
383
+ # corresponding ItemList entry(s) are processed
377
384
  sub WriteKeys($$$)
378
385
  {
379
386
  my ($et, $dirInfo, $tagTablePtr) = @_;
@@ -383,13 +390,14 @@ sub WriteKeys($$$)
383
390
  my $outfile = $$dirInfo{OutFile};
384
391
  my ($tag, %done, %remap, %info, %add, $i);
385
392
 
393
+ my $keysGrp = $avType{$$et{MediaType}} ? "$avType{$$et{MediaType}}Keys" : 'Keys';
386
394
  $dirLen < 8 and $et->Warn('Short Keys box'), $dirLen = 8, $$dataPt = "\0" x 8;
387
- if ($$et{DEL_GROUP}{Keys}) {
395
+ if ($$et{DEL_GROUP}{$keysGrp}) {
388
396
  $dirLen = 8; # delete all existing keys
389
397
  # deleted keys are identified by a zero entry in the Remap lookup
390
398
  my $n = Get32u($dataPt, 4);
391
399
  for ($i=1; $i<=$n; ++$i) { $remap{$i} = 0; }
392
- $et->VPrint(0, " [deleting $n Keys entr".($n==1 ? 'y' : 'ies')."]\n");
400
+ $et->VPrint(0, " [deleting $n $keysGrp entr".($n==1 ? 'y' : 'ies')."]\n");
393
401
  ++$$et{CHANGED};
394
402
  }
395
403
  my $pos = 8;
@@ -425,7 +433,7 @@ sub WriteKeys($$$)
425
433
  }
426
434
  unless ($dontDelete) {
427
435
  # delete this key
428
- $et->VPrint(1, "$$et{INDENT}\[deleting Keys entry $index '${tag}']\n");
436
+ $et->VPrint(1, "$$et{INDENT}\[deleting $keysGrp entry $index '${tag}']\n");
429
437
  $pos += $len;
430
438
  $remap{$index++} = 0;
431
439
  ++$$et{CHANGED};
@@ -455,7 +463,7 @@ sub WriteKeys($$$)
455
463
  # add new entry to 'keys' data
456
464
  my $val = $id =~ /^com\./ ? $id : "com.apple.quicktime.$id";
457
465
  $newData .= Set32u(8 + length($val)) . 'mdta' . $val;
458
- $et->VPrint(1, "$$et{INDENT}\[adding Keys entry $newIndex '${id}']\n");
466
+ $et->VPrint(1, "$$et{INDENT}\[adding $keysGrp entry $newIndex '${id}']\n");
459
467
  $add{$newIndex++} = $tagInfo;
460
468
  ++$$et{CHANGED};
461
469
  }
@@ -470,7 +478,7 @@ sub WriteKeys($$$)
470
478
  # Info - Keys tag information, based on old index value
471
479
  # Add - Keys items deleted, based on old index value
472
480
  # Num - Number of items in edited Keys box
473
- $$et{Keys} = { Remap => \%remap, Info => \%info, Add => \%add, Num => $num };
481
+ $$et{$keysGrp} = { Remap => \%remap, Info => \%info, Add => \%add, Num => $num };
474
482
 
475
483
  return $newData; # return updated Keys box
476
484
  }
@@ -482,23 +490,26 @@ sub WriteKeys($$$)
482
490
  sub WriteItemInfo($$$)
483
491
  {
484
492
  my ($et, $dirInfo, $outfile) = @_;
485
- my $boxPos = $$dirInfo{BoxPos}; # hash of [length,position] for each box
493
+ my $boxPos = $$dirInfo{BoxPos}; # hash of [position,length,irefVer(iref only)] for box in $outfile
486
494
  my $raf = $$et{RAF};
487
495
  my $items = $$et{ItemInfo};
488
- my (%did, @mdatEdit, $name);
496
+ my (%did, @mdatEdit, $name, $tmap);
489
497
 
490
498
  return () unless $items and $raf;
491
499
 
492
500
  # extract information from EXIF/XMP metadata items
493
501
  my $primary = $$et{PrimaryItem};
494
502
  my $curPos = $raf->Tell();
503
+ my $lastID = 0;
495
504
  my $id;
496
505
  foreach $id (sort { $a <=> $b } keys %$items) {
506
+ $lastID = $id;
497
507
  $primary = $id unless defined $primary; # assume primary is lowest-number item if pitm missing
498
508
  my $item = $$items{$id};
499
509
  # only edit primary EXIF/XMP metadata
500
510
  next unless $$item{RefersTo} and $$item{RefersTo}{$primary};
501
511
  my $type = $$item{ContentType} || $$item{Type} || next;
512
+ $tmap = $id if $type eq 'tmap'; # save ID of primary 'tmap' item (tone-mapped image)
502
513
  # get ExifTool name for this item
503
514
  $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP' }->{$type};
504
515
  next unless $name; # only care about EXIF and XMP
@@ -548,11 +559,12 @@ sub WriteItemInfo($$$)
548
559
  $buff = $v2;
549
560
  $wasDeflated = 1;
550
561
  } else {
551
- $et->WarnOnce("Error inflating $name metadata");
562
+ $et->Warn("Error inflating $name metadata");
552
563
  next;
553
564
  }
554
565
  }
555
566
  my ($hdr, $subTable, $proc);
567
+ my $strt = 0;
556
568
  if ($name eq 'EXIF') {
557
569
  if (not length $buff) {
558
570
  # create EXIF from scratch
@@ -562,6 +574,7 @@ sub WriteItemInfo($$$)
562
574
  $hdr = '';
563
575
  } elsif (length($buff) >= 4 and length($buff) >= 4 + unpack('N',$buff)) {
564
576
  $hdr = substr($buff, 0, 4 + unpack('N',$buff));
577
+ $strt = length $hdr;
565
578
  } else {
566
579
  $et->Warn('Invalid Exif header');
567
580
  next;
@@ -575,8 +588,8 @@ sub WriteItemInfo($$$)
575
588
  my %dirInfo = (
576
589
  DataPt => \$buff,
577
590
  DataLen => length $buff,
578
- DirStart => length $hdr,
579
- DirLen => length($buff) - length $hdr,
591
+ DirStart => $strt,
592
+ DirLen => length($buff) - $strt,
580
593
  );
581
594
  my $changed = $$et{CHANGED};
582
595
  my $newVal = $et->WriteDirectory(\%dirInfo, $subTable, $proc);
@@ -683,7 +696,7 @@ sub WriteItemInfo($$$)
683
696
  # write compressed XMP if Compress option is set
684
697
  if ($et->Options('Compress') and length $newVal) {
685
698
  if (not eval { require Compress::Zlib }) {
686
- $et->WarnOnce('Install Compress::Zlib to write compressed metadata');
699
+ $et->Warn('Install Compress::Zlib to write compressed metadata');
687
700
  } else {
688
701
  my $deflate = Compress::Zlib::deflateInit();
689
702
  if ($deflate) {
@@ -700,8 +713,10 @@ sub WriteItemInfo($$$)
700
713
  $type = "Exif\0";
701
714
  $mime = '';
702
715
  }
703
- my $id = 1;
704
- ++$id while $$items{$id} or $usedID{$id}; # find next unused item ID
716
+ my $id = ++$lastID; # use next highest available ID (so ID's in iinf are in order)
717
+ #[retracted] # create new item information hash to save infe box in case we need it for sorting
718
+ #[retracted] my $item = $$items{$id} = { };
719
+ # add new infe entry to iinf box
705
720
  my $n = length($type) + length($mime) + length($enc) + 16;
706
721
  if ($id < 0x10000) {
707
722
  $add{iinf} .= pack('Na4CCCCnn', $n, 'infe', 2, 0, 0, 1, $id, 0) . $type . $mime . $enc;
@@ -709,11 +724,14 @@ sub WriteItemInfo($$$)
709
724
  $n += 2;
710
725
  $add{iinf} .= pack('Na4CCCCNn', $n, 'infe', 3, 0, 0, 1, $id, 0) . $type . $mime . $enc;
711
726
  }
712
- # add new cdsc to iref
727
+ #[retracted] $add{iinf} .= $$item{infe};
728
+ # add new cdsc to iref (also refer to primary 'tmap' if it exists)
713
729
  if ($irefVer) {
714
- $add{iref} .= pack('Na4NnN', 18, 'cdsc', $id, 1, $primary);
730
+ my ($fmt, $siz, $num) = defined $tmap ? ('N', 22, 2) : ('', 18, 1);
731
+ $add{iref} .= pack('Na4NnN'.$fmt, $siz, 'cdsc', $id, $num, $primary, $tmap);
715
732
  } else {
716
- $add{iref} .= pack('Na4nnn', 14, 'cdsc', $id, 1, $primary);
733
+ my ($fmt, $siz, $num) = defined $tmap ? ('n', 16, 2) : ('', 14, 1);
734
+ $add{iref} .= pack('Na4nnn'.$fmt, $siz, 'cdsc', $id, $num, $primary, $tmap);
717
735
  }
718
736
  # add new entry to iloc table (see ISO14496-12:2015 pg.79)
719
737
  my $ilocVer = Get8u($outfile, $$boxPos{iloc}[0] + 8);
@@ -778,8 +796,9 @@ sub WriteItemInfo($$$)
778
796
  my $added = 0;
779
797
  my $tag;
780
798
  foreach $tag (sort { $$boxPos{$a}[0] <=> $$boxPos{$b}[0] } keys %$boxPos) {
799
+ $$boxPos{$tag}[0] += $added;
781
800
  next unless $add{$tag};
782
- my $pos = $$boxPos{$tag}[0] + $added;
801
+ my $pos = $$boxPos{$tag}[0];
783
802
  unless ($$boxPos{$tag}[1]) {
784
803
  $tag eq 'iref' or $et->Error('Internal error adding iref box'), last;
785
804
  # create new iref box
@@ -826,9 +845,34 @@ sub WriteItemInfo($$$)
826
845
  }
827
846
  # add new entries to this box (or add pitm after hdlr)
828
847
  substr($$outfile, $pos + $$boxPos{$tag}[1], 0) = $add{$tag};
848
+ $$boxPos{$tag}[1] += length $add{$tag};
829
849
  $added += length $add{$tag}; # positions are shifted by length of new entries
830
850
  }
831
851
  }
852
+ #[This sorting idea was retracted because just sorting 'iinf' wasn't sufficient to
853
+ # repair the problem where an out-of-order ID was added -- Apple Preview still
854
+ # ignores the gain-map image. It looks like either or both 'iref' and 'iloc' must
855
+ # also be sorted by ID, although the spec doesn't mention this]
856
+ #[retracted] # sort infe entries in iinf box if necessary
857
+ #[retracted] if ($$et{ItemsNotSorted}) {
858
+ #[retracted] if ($$boxPos{iinf}) {
859
+ #[retracted] my $iinfVer = Get8u($outfile, $$boxPos{iinf}[0] + 8);
860
+ #[retracted] my $off = $iinfVer == 0 ? 14 : 16; # offset to first infe item
861
+ #[retracted] my $sorted = ''; # sorted iinf payload
862
+ #[retracted] $sorted .= $$items{$_}{infe} || '' foreach sort { $a <=> $b } keys %$items;
863
+ #[retracted] if (length $sorted == $$boxPos{iinf}[1]-$off) {
864
+ #[retracted] # replace with sorted infe entries
865
+ #[retracted] substr($$outfile, $$boxPos{iinf}[0] + $off, length $sorted) = $sorted;
866
+ #[retracted] $et->Warn('Item info entries are out of order. Fixed.');
867
+ #[retracted] ++$$et{CHANGED};
868
+ #[retracted] } else {
869
+ #[retracted] $et->Warn('Error sorting item info entries');
870
+ #[retracted] }
871
+ #[retracted] } else {
872
+ #[retracted] $et->Warn('Item info entries are out of order');
873
+ #[retracted] }
874
+ #[retracted] delete $$et{ItemsNotSorted};
875
+ #[retracted] }
832
876
  delete $$et{ItemInfo};
833
877
  return @mdatEdit ? \@mdatEdit : undef;
834
878
  }
@@ -847,7 +891,7 @@ sub WriteQuickTime($$$)
847
891
  $et or return 1; # allow dummy access to autoload this package
848
892
  my ($mdat, @mdat, @mdatEdit, $edit, $track, $outBuff, $co, $term, $delCount);
849
893
  my (%langTags, $canCreate, $delGrp, %boxPos, %didDir, $writeLast, $err, $atomCount);
850
- my ($tag, $lastTag, $lastPos, $errStr, $trailer, $buf2);
894
+ my ($tag, $lastTag, $lastPos, $errStr, $trailer, $buf2, $keysGrp, $keysPath);
851
895
  my $outfile = $$dirInfo{OutFile} || return 0;
852
896
  my $raf = $$dirInfo{RAF}; # (will be null for lower-level atoms)
853
897
  my $dataPt = $$dirInfo{DataPt}; # (will be null for top-level atoms)
@@ -860,15 +904,10 @@ sub WriteQuickTime($$$)
860
904
  my $createKeys = 0;
861
905
  my ($rtnVal, $rtnErr) = $dataPt ? (undef, undef) : (1, 0);
862
906
 
863
- # check for Insta360 trailer at top level
907
+ # check for trailer at end of file
864
908
  if ($raf) {
865
- my $pos = $raf->Tell();
866
- if ($raf->Seek(-40, 2) and $raf->Read($buf2, 40) == 40 and
867
- substr($buf2, 8) eq '8db42d694ccc418790edff439fe026bf')
868
- {
869
- $trailer = [ 'Insta360', $raf->Tell() - unpack('V',$buf2) ];
870
- }
871
- $raf->Seek($pos, 0) or return 0;
909
+ $trailer = IdentifyTrailers($raf);
910
+ $trailer and not ref $trailer and $et->Error($trailer), return 1;
872
911
  }
873
912
  if ($dataPt) {
874
913
  $raf = File::RandomAccess->new($dataPt);
@@ -881,15 +920,26 @@ sub WriteQuickTime($$$)
881
920
 
882
921
  $raf->Seek($dirStart, 1) if $dirStart; # skip header if it exists
883
922
 
923
+ if ($avType{$$et{MediaType}}) {
924
+ # (note: these won't be correct now if we haven't yet processed the Media box,
925
+ # but in this case they won't be needed until after we set them properly below)
926
+ ($keysGrp, $keysPath) = ("$avType{$$et{MediaType}}Keys", 'MOV-Movie-Track');
927
+ } else {
928
+ ($keysGrp, $keysPath) = ('Keys', 'MOV-Movie');
929
+ }
884
930
  my $curPath = join '-', @{$$et{PATH}};
885
931
  my ($dir, $writePath) = ($dirName, $dirName);
886
932
  $writePath = "$dir-$writePath" while defined($dir = $$et{DirMap}{$dir});
887
933
  # hack to create Keys directories if necessary (its containing Meta is in a different location)
888
- if ($$addDirs{Keys} and $curPath =~ /^MOV-Movie(-Meta)?$/) {
934
+ if (($$addDirs{Keys} and $curPath =~ /^MOV-Movie(-Meta)?$/)) {
889
935
  $createKeys = 1; # create new Keys directories
890
- } elsif ($curPath eq 'MOV-Movie-Meta-ItemList') {
936
+ } elsif (($$addDirs{AudioKeys} or $$addDirs{VideoKeys}) and $curPath =~ /^MOV-Movie-Track(-Meta)?$/) {
937
+ $createKeys = -1; # (must wait until MediaType is known)
938
+ } elsif (($curPath eq 'MOV-Movie-Meta-ItemList') or
939
+ ($curPath eq 'MOV-Movie-Track-Meta-ItemList' and $avType{$$et{MediaType}}))
940
+ {
891
941
  $createKeys = 2; # create new Keys tags
892
- my $keys = $$et{Keys};
942
+ my $keys = $$et{$keysGrp};
893
943
  if ($keys) {
894
944
  # add new tag entries for existing Keys tags, now that we know their ID's
895
945
  # - first make lookup to convert Keys tagInfo ref to index number
@@ -897,7 +947,7 @@ sub WriteQuickTime($$$)
897
947
  foreach $index (keys %{$$keys{Info}}) {
898
948
  $keysInfo{$$keys{Info}{$index}} = $index if $$keys{Remap}{$index};
899
949
  }
900
- my $keysTable = GetTagTable('Image::ExifTool::QuickTime::Keys');
950
+ my $keysTable = GetTagTable("Image::ExifTool::QuickTime::$keysGrp");
901
951
  my $newKeysTags = $et->GetNewTagInfoHash($keysTable);
902
952
  foreach (keys %$newKeysTags) {
903
953
  my $tagInfo = $$newKeysTags{$_};
@@ -926,7 +976,8 @@ sub WriteQuickTime($$$)
926
976
  }
927
977
  if ($curPath eq $writePath or $createKeys) {
928
978
  $canCreate = 1;
929
- $delGrp = $$et{DEL_GROUP}{$dirName};
979
+ # (must check the appropriate Keys delete flag if this is a Keys ItemList)
980
+ $delGrp = $$et{DEL_GROUP}{$createKeys ? $keysGrp : $dirName};
930
981
  }
931
982
  $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT} if $$tagTablePtr{VARS};
932
983
 
@@ -973,7 +1024,7 @@ sub WriteQuickTime($$$)
973
1024
  $et->Error('End of processing at large atom (LargeFileSupport not enabled)');
974
1025
  last;
975
1026
  } elsif ($et->Options('LargeFileSupport') eq '2') {
976
- $et->WarnOnce('Processing large atom (LargeFileSupport is 2)');
1027
+ $et->Warn('Processing large atom (LargeFileSupport is 2)');
977
1028
  }
978
1029
  }
979
1030
  $size = $hi * 4294967296 + $lo - 16;
@@ -1044,12 +1095,12 @@ sub WriteQuickTime($$$)
1044
1095
  last;
1045
1096
  }
1046
1097
  }
1047
- # save the handler type for this track
1048
- if ($tag eq 'hdlr' and length $buff >= 12) {
1049
- my $hdlr = substr($buff,8,4);
1050
- $$et{HandlerType} = $hdlr if $hdlr =~ /^(vide|soun)$/;
1098
+ # save the handler type of the track media
1099
+ if ($tag eq 'hdlr' and length $buff >= 12 and
1100
+ @{$$et{PATH}} and $$et{PATH}[-1] eq 'Media')
1101
+ {
1102
+ $$et{MediaType} = substr($buff,8,4);
1051
1103
  }
1052
-
1053
1104
  # if this atom stores offsets, save its location so we can fix up offsets later
1054
1105
  # (are there any other atoms that may store absolute file offsets?)
1055
1106
  if ($tag =~ /^(stco|co64|iloc|mfra|moof|sidx|saio|gps |CTBO|uuid)$/) {
@@ -1094,11 +1145,11 @@ sub WriteQuickTime($$$)
1094
1145
  &{$$tagInfo{WriteHook}}($buff,$et) if $tagInfo and $$tagInfo{WriteHook};
1095
1146
 
1096
1147
  # allow numerical tag ID's (ItemList entries defined by Keys)
1097
- if (not $tagInfo and $dirName eq 'ItemList' and $$et{Keys}) {
1148
+ if (not $tagInfo and $dirName eq 'ItemList' and $$et{$keysGrp}) {
1098
1149
  $keysIndex = unpack('N', $tag);
1099
- my $newIndex = $$et{Keys}{Remap}{$keysIndex};
1150
+ my $newIndex = $$et{$keysGrp}{Remap}{$keysIndex};
1100
1151
  if (defined $newIndex) {
1101
- $tagInfo = $$et{Keys}{Info}{$keysIndex};
1152
+ $tagInfo = $$et{$keysGrp}{Info}{$keysIndex};
1102
1153
  unless ($newIndex) {
1103
1154
  if ($tagInfo) {
1104
1155
  $et->VPrint(1," - Keys:$$tagInfo{Name}");
@@ -1129,7 +1180,7 @@ sub WriteQuickTime($$$)
1129
1180
  next;
1130
1181
  }
1131
1182
  }
1132
- undef $tagInfo if $tagInfo and $$tagInfo{Unknown};
1183
+ undef $tagInfo if $tagInfo and $$tagInfo{AddedUnknown};
1133
1184
 
1134
1185
  if ($tagInfo and (not defined $$tagInfo{Writable} or $$tagInfo{Writable})) {
1135
1186
  my $subdir = $$tagInfo{SubDirectory};
@@ -1138,7 +1189,7 @@ sub WriteQuickTime($$$)
1138
1189
  if ($subdir) { # process atoms in this container from a buffer in memory
1139
1190
 
1140
1191
  if ($tag eq 'trak') {
1141
- undef $$et{HandlerType}; # init handler type for this track
1192
+ $$et{MediaType} = ''; # init media type for this track
1142
1193
  delete $$et{AssumedDataRef};
1143
1194
  }
1144
1195
  my $subName = $$subdir{DirName} || $$tagInfo{Name};
@@ -1207,10 +1258,13 @@ sub WriteQuickTime($$$)
1207
1258
  $$et{CHANGED} = $oldChanged;
1208
1259
  undef $newData;
1209
1260
  }
1210
- if ($tag eq 'trak' and $$et{AssumedDataRef}) {
1211
- my $grp = $$et{CUR_WRITE_GROUP} || $dirName;
1212
- $et->Error("Can't locate data reference to update offsets for $grp");
1213
- delete $$et{AssumedDataRef};
1261
+ if ($tag eq 'trak') {
1262
+ $$et{MediaType} = ''; # reset media type at end of track
1263
+ if ($$et{AssumedDataRef}) {
1264
+ my $grp = $$et{CUR_WRITE_GROUP} || $dirName;
1265
+ $et->Error("Can't locate data reference to update offsets for $grp");
1266
+ delete $$et{AssumedDataRef};
1267
+ }
1214
1268
  }
1215
1269
  $$et{CUR_WRITE_GROUP} = $oldWriteGroup;
1216
1270
  SetByteOrder('MM');
@@ -1506,7 +1560,7 @@ sub WriteQuickTime($$$)
1506
1560
  }
1507
1561
  if ($msg) {
1508
1562
  # (allow empty sample description for non-audio/video handler types, eg. 'url ', 'meta')
1509
- if ($$et{HandlerType}) {
1563
+ if ($$et{MediaType}) {
1510
1564
  my $grp = $$et{CUR_WRITE_GROUP} || $parent;
1511
1565
  $et->Error("$msg for $grp");
1512
1566
  return $rtnErr;
@@ -1542,16 +1596,26 @@ sub WriteQuickTime($$$)
1542
1596
  if (($lastTag eq 'mdat' or $lastTag eq 'moov') and not $dataPt and (not $$tagTablePtr{$tag} or
1543
1597
  ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
1544
1598
  {
1545
- # identify other known trailers
1599
+ # identify other known trailers from their first bytes
1546
1600
  $buf2 = '';
1547
1601
  $raf->Seek($lastPos,0) and $raf->Read($buf2,8);
1602
+ my ($type, $len);
1548
1603
  if ($buf2 eq 'CCCCCCCC') {
1549
- $trailer = [ 'Kenwood', $lastPos ];
1604
+ $type = 'Kenwood';
1550
1605
  } elsif ($buf2 =~ /^(gpsa|gps0|gsen|gsea)...\0/s) {
1551
- $trailer = [ 'RIFF', $lastPos ];
1606
+ $type = 'RIFF';
1552
1607
  } else {
1553
- $trailer = [ 'Unknown', $lastPos ];
1608
+ $type = 'Unknown';
1554
1609
  }
1610
+ # determine length of this trailer
1611
+ if ($trailer) {
1612
+ $len = $$trailer[1] - $lastPos; # runs to start of next trailer
1613
+ } else {
1614
+ $raf->Seek(0, 2) or $et->Error('Seek error'), return $dataPt ? undef : 1;
1615
+ $len = $raf->Tell() - $lastPos; # runs to end of file
1616
+ }
1617
+ # add to start of linked list of trailers
1618
+ $trailer = [ $type, $lastPos, $len, $trailer ];
1555
1619
  } else {
1556
1620
  $et->Error($errStr);
1557
1621
  return $dataPt ? undef : 1;
@@ -1559,7 +1623,16 @@ sub WriteQuickTime($$$)
1559
1623
  }
1560
1624
  $et->VPrint(0, " [deleting $delCount $dirName tag".($delCount==1 ? '' : 's')."]\n") if $delCount;
1561
1625
 
1562
- $createKeys &= ~0x01 unless $$addDirs{Keys}; # (Keys may have been written)
1626
+ # can finally set necessary variables for creating Video/AudioKeys tags
1627
+ if ($createKeys < 0) {
1628
+ if ($avType{$$et{MediaType}}) {
1629
+ $createKeys = 1;
1630
+ ($keysGrp, $keysPath) = ("$avType{$$et{MediaType}}Keys", 'MOV-Movie-Track');
1631
+ } else {
1632
+ $canCreate = 0;
1633
+ }
1634
+ }
1635
+ $createKeys &= ~0x01 unless $$addDirs{$keysGrp}; # (Keys may have been written)
1563
1636
 
1564
1637
  # add new directories/tags at this level if necessary
1565
1638
  if ($canCreate and (exists $$et{EDIT_DIRS}{$dirName} or $createKeys)) {
@@ -1570,13 +1643,13 @@ sub WriteQuickTime($$$)
1570
1643
  my ($tag, $index);
1571
1644
  # add Keys tags if necessary
1572
1645
  if ($createKeys) {
1573
- if ($curPath eq 'MOV-Movie') {
1646
+ if ($curPath eq $keysPath) {
1574
1647
  # add Meta for Keys if necessary
1575
1648
  unless ($didDir{meta}) {
1576
1649
  $$dirs{meta} = $Image::ExifTool::QuickTime::Movie{meta};
1577
1650
  push @addTags, 'meta';
1578
1651
  }
1579
- } elsif ($curPath eq 'MOV-Movie-Meta') {
1652
+ } elsif ($curPath eq "$keysPath-Meta") {
1580
1653
  # special case for Keys Meta -- reset directories and start again
1581
1654
  undef @addTags;
1582
1655
  $dirs = { };
@@ -1585,10 +1658,10 @@ sub WriteQuickTime($$$)
1585
1658
  $$dirs{$_} = $Image::ExifTool::QuickTime::Meta{$_};
1586
1659
  push @addTags, $_;
1587
1660
  }
1588
- } elsif ($curPath eq 'MOV-Movie-Meta-ItemList' and $$et{Keys}) {
1589
- foreach $index (sort { $a <=> $b } keys %{$$et{Keys}{Add}}) {
1661
+ } elsif ($curPath eq "$keysPath-Meta-ItemList" and $$et{$keysGrp}) {
1662
+ foreach $index (sort { $a <=> $b } keys %{$$et{$keysGrp}{Add}}) {
1590
1663
  my $id = Set32u($index);
1591
- $$newTags{$id} = $$et{Keys}{Add}{$index};
1664
+ $$newTags{$id} = $$et{$keysGrp}{Add}{$index};
1592
1665
  push @addTags, $id;
1593
1666
  }
1594
1667
  } else {
@@ -1600,8 +1673,7 @@ sub WriteQuickTime($$$)
1600
1673
  foreach $tag (@addTags) {
1601
1674
  my $tagInfo = $$dirs{$tag} || $$newTags{$tag};
1602
1675
  next if defined $$tagInfo{CanCreate} and not $$tagInfo{CanCreate};
1603
- next if defined $$tagInfo{HandlerType} and
1604
- (not $$et{HandlerType} or $$et{HandlerType} ne $$tagInfo{HandlerType});
1676
+ next if defined $$tagInfo{MediaType} and $$et{MediaType} ne $$tagInfo{MediaType};
1605
1677
  my $subdir = $$tagInfo{SubDirectory};
1606
1678
  unless ($subdir) {
1607
1679
  my $nvHash = $et->GetNewValueHash($tagInfo);
@@ -1663,13 +1735,13 @@ sub WriteQuickTime($$$)
1663
1735
  }
1664
1736
  my $subName = $$subdir{DirName} || $$tagInfo{Name};
1665
1737
  # QuickTime hierarchy is complex, so check full directory path before adding
1666
- if ($createKeys and $curPath eq 'MOV-Movie' and $subName eq 'Meta') {
1738
+ if ($createKeys and $curPath eq $keysPath and $subName eq 'Meta') {
1667
1739
  $et->VPrint(0, " Creating Meta with mdta Handler and Keys\n");
1668
1740
  # init Meta box for Keys tags with mdta Handler and empty Keys+ItemList
1669
1741
  $buf2 = "\0\0\0\x20hdlr\0\0\0\0\0\0\0\0mdta\0\0\0\0\0\0\0\0\0\0\0\0" .
1670
1742
  "\0\0\0\x10keys\0\0\0\0\0\0\0\0" .
1671
1743
  "\0\0\0\x08ilst";
1672
- } elsif ($createKeys and $curPath eq 'MOV-Movie-Meta') {
1744
+ } elsif ($createKeys and $curPath eq "$keysPath-Meta") {
1673
1745
  $buf2 = ($subName eq 'Keys' ? "\0\0\0\0\0\0\0\0" : '');
1674
1746
  } elsif ($subName eq 'Meta' and $$et{OPTIONS}{QuickTimeHandler}) {
1675
1747
  $et->VPrint(0, " Creating Meta with mdir Handler\n");
@@ -1718,8 +1790,8 @@ sub WriteQuickTime($$$)
1718
1790
  }
1719
1791
  }
1720
1792
  # add only once (must delete _after_ call to WriteDirectory())
1721
- # (Keys is a special case, and will be removed after Meta is processed)
1722
- delete $$addDirs{$subName} unless $subName eq 'Keys';
1793
+ # (Keys tags are a special case, and are handled separately)
1794
+ delete $$addDirs{$subName} unless $createKeys;
1723
1795
  }
1724
1796
  }
1725
1797
  # write HEIC metadata after top-level 'meta' box has been processed if editing this information
@@ -1745,9 +1817,9 @@ sub WriteQuickTime($$$)
1745
1817
  # (could report a file if editing nothing when it contained an empty Meta atom)
1746
1818
  # ++$$et{CHANGED};
1747
1819
  }
1748
- if ($curPath eq 'MOV-Movie-Meta') {
1749
- delete $$addDirs{Keys}; # prevent creation of another Meta for Keys tags
1750
- delete $$et{Keys};
1820
+ if ($curPath eq "$keysPath-Meta") {
1821
+ delete $$addDirs{$keysGrp}; # prevent creation of another Meta for Keys tags
1822
+ delete $$et{$keysGrp};
1751
1823
  }
1752
1824
  }
1753
1825
 
@@ -2005,21 +2077,35 @@ sub WriteQuickTime($$$)
2005
2077
  # write the stuff that must come last
2006
2078
  Write($outfile, $writeLast) or $rtnVal = 0 if $writeLast;
2007
2079
 
2008
- # copy trailer if necessary
2009
- if ($rtnVal and $trailer) {
2010
- # are we deleting the trailer?
2080
+ # copy trailers if necessary
2081
+ while ($rtnVal and $trailer) {
2082
+ # are we deleting the trailers?
2011
2083
  my $nvTrail = $et->GetNewValueHash($Image::ExifTool::Extra{Trailer});
2012
- if ($$et{DEL_GROUP}{Trailer} or ($nvTrail and not ($$nvTrail{Value} and $$nvTrail{Value}[0]))) {
2084
+ if ($$et{DEL_GROUP}{Trailer} or $$et{DEL_GROUP}{$$trailer[0]} or
2085
+ ($nvTrail and not ($$nvTrail{Value} and $$nvTrail{Value}[0])))
2086
+ {
2013
2087
  $et->Warn("Deleted $$trailer[0] trailer", 1);
2014
- } elsif ($raf->Seek($$trailer[1])) {
2015
- $et->Warn(sprintf('Copying %s trailer from offset 0x%x', @$trailer), 1);
2016
- while ($raf->Read($buf2, 65536)) {
2017
- Write($outfile, $buf2) or $rtnVal = 0, last;
2018
- }
2088
+ ++$$et{CHANGED};
2089
+ $trailer = $$trailer[3];
2090
+ next;
2091
+ }
2092
+ $raf->Seek($$trailer[1], 0) or $rtnVal = 0, last;
2093
+ if ($$trailer[0] eq 'MIE') {
2094
+ require Image::ExifTool::MIE;
2095
+ my %dirInfo = ( RAF => $raf, OutFile => $outfile );
2096
+ my $result = Image::ExifTool::MIE::ProcessMIE($et, \%dirInfo);
2097
+ $result > 0 or $et->Error('Error writing MIE trailer'), $rtnVal = 0, last;
2019
2098
  } else {
2020
- $rtnVal = 0;
2099
+ $et->Warn(sprintf('Copying %s trailer from offset 0x%x (%d bytes)', @$trailer[0..2]), 1);
2100
+ my $len = $$trailer[2];
2101
+ while ($len) {
2102
+ my $n = $len > 65536 ? 65536 : $len;
2103
+ $raf->Read($buf2, $n) == $n and Write($outfile, $buf2) or $rtnVal = 0, last;
2104
+ $len -= $n;
2105
+ }
2106
+ $rtnVal or $et->Error("Error copying $$trailer[0] trailer"), last;
2021
2107
  }
2022
- $rtnVal or $et->Error("Error copying $$trailer[0] trailer");
2108
+ $trailer = $$trailer[3]; # step to next trailer in linked list
2023
2109
  }
2024
2110
  return $rtnVal;
2025
2111
  }
@@ -2075,6 +2161,7 @@ sub WriteMOV($$)
2075
2161
  $raf->Seek(0,0);
2076
2162
 
2077
2163
  # write the file
2164
+ $$et{MediaType} = '';
2078
2165
  $$dirInfo{Parent} = '';
2079
2166
  $$dirInfo{DirName} = 'MOV';
2080
2167
  $$dirInfo{ChunkOffset} = [ ]; # (just to be safe)
@@ -2100,7 +2187,7 @@ QuickTime-based file formats like MOV and MP4.
2100
2187
 
2101
2188
  =head1 AUTHOR
2102
2189
 
2103
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
2190
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
2104
2191
 
2105
2192
  This library is free software; you can redistribute it and/or modify it
2106
2193
  under the same terms as Perl itself.
@@ -34,6 +34,11 @@ my %webpMap = (
34
34
  MakerNotes => 'ExifIFD',
35
35
  );
36
36
 
37
+ my %deletableGroup = (
38
+ "XMP\0" => 'XMP', # delete incorrectly written "XMP\0" tag with XMP group
39
+ SEAL => 'SEAL', # delete SEAL tag with SEAL group
40
+ );
41
+
37
42
  #------------------------------------------------------------------------------
38
43
  # Write RIFF file (currently WebP-type only)
39
44
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref
@@ -46,6 +51,7 @@ sub WriteRIFF($$)
46
51
  my $outfile = $$dirInfo{OutFile};
47
52
  my $outsize = 0;
48
53
  my $raf = $$dirInfo{RAF};
54
+ my $verbose = $et->Options('Verbose');
49
55
  my ($buff, $err, $pass, %has, %dirDat, $imageWidth, $imageHeight);
50
56
 
51
57
  # do this in 2 passes so we can set the size of the containing RIFF chunk
@@ -65,6 +71,7 @@ sub WriteRIFF($$)
65
71
  SetByteOrder('II');
66
72
 
67
73
  # determine which directories we must write for this file type
74
+ $et->Options(Verbose => 0) if $pass; # (avoid duplicate Verbose options here)
68
75
  $et->InitWriteDirs(\%webpMap);
69
76
  my $addDirs = $$et{ADD_DIRS};
70
77
  my $editDirs = $$et{EDIT_DIRS};
@@ -73,6 +80,7 @@ sub WriteRIFF($$)
73
80
 
74
81
  # write header
75
82
  if ($pass) {
83
+ $et->Options(Verbose => $verbose);
76
84
  my $needsVP8X = ($has{ANIM} or $has{'XMP '} or $has{EXIF} or
77
85
  $has{ALPH} or $has{ICCP});
78
86
  if ($has{VP8X} and not $needsVP8X and $$et{CHANGED}) {
@@ -146,13 +154,14 @@ sub WriteRIFF($$)
146
154
  # RIFF chunks are padded to an even number of bytes
147
155
  my $len2 = $len + ($len & 0x01);
148
156
  # handle incorrect "XMP\0" chunk ID written by Google software
149
- if ($tag eq "XMP\0") {
150
- if ($$et{DEL_GROUP}{XMP}) {
151
- # just ignore this chunk if deleting XMP
157
+ if ($deletableGroup{$tag}) {
158
+ if ($$et{DEL_GROUP}{$deletableGroup{$tag}}) {
159
+ # just ignore this chunk if deleting the associated group
152
160
  $raf->Seek($len2, 1) or $et->Error('Seek error'), last;
161
+ $et->VPrint(0, " Deleting $deletableGroup{$tag}\n") if $pass;
153
162
  ++$$et{CHANGED};
154
163
  next;
155
- } else {
164
+ } elsif ($tag eq "XMP\0") {
156
165
  $et->Warn('Incorrect XMP tag ID',1) if $pass;
157
166
  }
158
167
  }
@@ -315,8 +324,10 @@ sub WriteRIFF($$)
315
324
  $raf->Read($buff, 6) == 6 or $et->Error('Truncated VP8L chunk'), return 1;
316
325
  $outsize += 6;
317
326
  if ($buff =~ /^\x2f/s) {
327
+ my $word = Get32u(\$buff, 2);
318
328
  $imageWidth = (Get16u(\$buff, 1) & 0x3fff) + 1;
319
- $imageHeight = ((Get32u(\$buff, 2) >> 6) & 0x3fff) + 1;
329
+ $imageHeight = (($word >> 6) & 0x3fff) + 1;
330
+ $has{ALPH} = 1 if $word & 0x100000; # set alpha flag if necessary
320
331
  }
321
332
  $len2 -= 6;
322
333
  }
@@ -363,7 +374,7 @@ Currently writes only WebP files.
363
374
 
364
375
  =head1 AUTHOR
365
376
 
366
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
377
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
367
378
 
368
379
  This library is free software; you can redistribute it and/or modify it
369
380
  under the same terms as Perl itself.