exiftool_vendored 12.16.0 → 12.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +137 -1
  3. data/bin/MANIFEST +12 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +44 -43
  7. data/bin/config_files/acdsee.config +193 -6
  8. data/bin/config_files/cuepointlist.config +70 -0
  9. data/bin/config_files/example.config +1 -8
  10. data/bin/exiftool +139 -98
  11. data/bin/fmt_files/gpx.fmt +1 -1
  12. data/bin/fmt_files/gpx_wpt.fmt +1 -1
  13. data/bin/fmt_files/kml.fmt +1 -1
  14. data/bin/fmt_files/kml_track.fmt +1 -1
  15. data/bin/lib/Image/ExifTool.pm +158 -49
  16. data/bin/lib/Image/ExifTool.pod +94 -75
  17. data/bin/lib/Image/ExifTool/Apple.pm +3 -2
  18. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +25 -14
  19. data/bin/lib/Image/ExifTool/Canon.pm +28 -3
  20. data/bin/lib/Image/ExifTool/CanonCustom.pm +19 -1
  21. data/bin/lib/Image/ExifTool/DJI.pm +6 -6
  22. data/bin/lib/Image/ExifTool/DjVu.pm +6 -5
  23. data/bin/lib/Image/ExifTool/Exif.pm +50 -22
  24. data/bin/lib/Image/ExifTool/FITS.pm +13 -2
  25. data/bin/lib/Image/ExifTool/FujiFilm.pm +19 -8
  26. data/bin/lib/Image/ExifTool/GPS.pm +24 -13
  27. data/bin/lib/Image/ExifTool/H264.pm +20 -5
  28. data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
  29. data/bin/lib/Image/ExifTool/JPEG.pm +6 -2
  30. data/bin/lib/Image/ExifTool/JSON.pm +24 -3
  31. data/bin/lib/Image/ExifTool/Jpeg2000.pm +361 -16
  32. data/bin/lib/Image/ExifTool/M2TS.pm +40 -4
  33. data/bin/lib/Image/ExifTool/MIE.pm +2 -2
  34. data/bin/lib/Image/ExifTool/MRC.pm +341 -0
  35. data/bin/lib/Image/ExifTool/MWG.pm +3 -3
  36. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  37. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  38. data/bin/lib/Image/ExifTool/Microsoft.pm +298 -82
  39. data/bin/lib/Image/ExifTool/Nikon.pm +5 -5
  40. data/bin/lib/Image/ExifTool/NikonSettings.pm +25 -16
  41. data/bin/lib/Image/ExifTool/Olympus.pm +2 -2
  42. data/bin/lib/Image/ExifTool/PNG.pm +2 -2
  43. data/bin/lib/Image/ExifTool/Panasonic.pm +14 -1
  44. data/bin/lib/Image/ExifTool/PhaseOne.pm +4 -3
  45. data/bin/lib/Image/ExifTool/QuickTime.pm +148 -68
  46. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +94 -34
  47. data/bin/lib/Image/ExifTool/README +5 -4
  48. data/bin/lib/Image/ExifTool/RIFF.pm +84 -12
  49. data/bin/lib/Image/ExifTool/Samsung.pm +2 -1
  50. data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
  51. data/bin/lib/Image/ExifTool/Sony.pm +157 -49
  52. data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
  53. data/bin/lib/Image/ExifTool/TagLookup.pm +4079 -3987
  54. data/bin/lib/Image/ExifTool/TagNames.pod +642 -273
  55. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
  56. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -0
  57. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +44 -17
  58. data/bin/lib/Image/ExifTool/WriteXMP.pl +15 -8
  59. data/bin/lib/Image/ExifTool/Writer.pl +50 -14
  60. data/bin/lib/Image/ExifTool/XMP.pm +50 -11
  61. data/bin/perl-Image-ExifTool.spec +42 -42
  62. data/lib/exiftool_vendored/version.rb +1 -1
  63. metadata +52 -12
@@ -1066,7 +1066,7 @@ my %componentDataDef = (
1066
1066
  '060e2b34.0101.0102.04010503.08000000' => { Name => 'Palette', Type => 'DataValue', Unknown => 1 },
1067
1067
  '060e2b34.0101.0102.04010503.09000000' => { Name => 'PaletteLayout', Type => 'RGBALayout', Unknown => 1 },
1068
1068
  '060e2b34.0101.0102.04010503.0a000000' => { Name => 'ComponentDepth', Format => 'int32u' },
1069
- '060e2b34.0101.0102.04010601.00000000' => { Name => 'VideoCodingSchemeID', Type => 'AUID', Unknown => 1 },
1069
+ '060e2b34.0101.0102.04010601.00000000' => { Name => 'VideoCodingSchemeID', Type => 'AUID' },
1070
1070
  '060e2b34.0101.0102.04010802.03000000' => { Name => 'RoundedCaptureFilmFrameRate', Format => 'int32u' },
1071
1071
  '060e2b34.0101.0102.04020301.02000000' => { Name => 'AudioAverageBitrate', Format => 'float', PrintConv => 'ConvertBitrate($val)', Groups => { 2 => 'Audio' } },
1072
1072
  '060e2b34.0101.0102.04020301.03000000' => { Name => 'AudioFixedBitrateFlag', Type => 'Boolean', Groups => { 2 => 'Audio' } },
@@ -245,7 +245,7 @@ my %mdDateInfo = (
245
245
  XAttr tags are extracted using the "xattr" utility. They are extracted if
246
246
  any "XAttr*" tag or the MacOS group is specifically requested, or by setting
247
247
  the L<XAttrTags|../ExifTool.html#XAttrTags> API option to 1 or the L<RequestAll|../ExifTool.html#RequestAll> API option to 2 or higher.
248
- And they extracted by default from MacOS "._" files when reading
248
+ And they are extracted by default from MacOS "._" files when reading
249
249
  these files directly.
250
250
  },
251
251
  'com.apple.FinderInfo' => {
@@ -5,6 +5,7 @@
5
5
  #
6
6
  # Revisions: 2010/10/01 - P. Harvey Created
7
7
  # 2011/10/05 - PH Added ProcessXtra()
8
+ # 2021/02/23 - PH Added abiltity to write Xtra tags
8
9
  #
9
10
  # References: 1) http://research.microsoft.com/en-us/um/redmond/groups/ivm/hdview/hdmetadataspec.htm
10
11
  #------------------------------------------------------------------------------
@@ -16,9 +17,11 @@ use vars qw($VERSION);
16
17
  use Image::ExifTool qw(:DataAccess :Utils);
17
18
  use Image::ExifTool::XMP;
18
19
 
19
- $VERSION = '1.20';
20
+ $VERSION = '1.23';
20
21
 
21
22
  sub ProcessXtra($$$);
23
+ sub WriteXtra($$$);
24
+ sub CheckXtra($$$);
22
25
 
23
26
  # tags written by Microsoft HDView (ref 1)
24
27
  %Image::ExifTool::Microsoft::Stitch = (
@@ -196,11 +199,20 @@ my %sRegions = (
196
199
  # and Image::ExifTool::WTV::Metadata
197
200
  %Image::ExifTool::Microsoft::Xtra = (
198
201
  PROCESS_PROC => \&ProcessXtra,
202
+ WRITE_PROC => \&WriteXtra,
203
+ CHECK_PROC => \&CheckXtra,
204
+ WRITE_GROUP => 'Microsoft',
205
+ AVOID => 1,
199
206
  GROUPS => { 0 => 'QuickTime', 2 => 'Video' },
200
207
  VARS => { NO_ID => 1 },
201
208
  NOTES => q{
202
- Tags extracted from the Microsoft "Xtra" atom of QuickTime videos. Tag ID's
203
- are not shown because some are unruly GUID's.
209
+ Tags found in the Microsoft "Xtra" atom of QuickTime videos. Tag ID's are
210
+ not shown because some are unruly GUID's. Currently most of these tags are
211
+ not writable because the Microsoft documentation is poor and samples were
212
+ not available, but more tags may be made writable in the future if samples
213
+ are provided. Note that writable tags in this table are are flagged to
214
+ "Avoid", which means that other more common tags will be written instead if
215
+ possible unless the Microsoft group is specified explicitly.
204
216
  },
205
217
  Abstract => { },
206
218
  AcquisitionTime => { Groups => { 2 => 'Time' } },
@@ -327,30 +339,34 @@ my %sRegions = (
327
339
  UserServiceRating => { },
328
340
  VideoBitrate => { },
329
341
  VideoFormat => { },
330
- 'WM/AlbumArtist' => 'AlbumArtist',
331
- 'WM/AlbumCoverURL' => 'AlbumCoverURL',
332
- 'WM/AlbumTitle' => 'AlbumTitle',
342
+ 'WM/AlbumArtist' => { Name => 'AlbumArtist', Writable => 'Unicode' }, # (NC)
343
+ 'WM/AlbumCoverURL' => { Name => 'AlbumCoverURL', Writable => 'Unicode' }, # (NC)
344
+ 'WM/AlbumTitle' => { Name => 'AlbumTitle', Writable => 'Unicode' }, # (NC)
333
345
  'WM/BeatsPerMinute' => 'BeatsPerMinute',
334
- 'WM/Category' => 'Category',
335
- 'WM/Composer' => 'Composer',
336
- 'WM/Conductor' => 'Conductor',
337
- 'WM/ContentDistributor' => 'ContentDistributor',
346
+ 'WM/Category' => { Name => 'Category', Writable => 'Unicode', List => 1 },
347
+ 'WM/Composer' => { Name => 'Composer', Writable => 'Unicode' }, # (NC)
348
+ 'WM/Conductor' => { Name => 'Conductor', Writable => 'Unicode', List => 1 },
349
+ 'WM/ContentDistributor' => { Name => 'ContentDistributor', Writable => 'Unicode' },
338
350
  'WM/ContentDistributorType' => 'ContentDistributorType',
339
351
  'WM/ContentGroupDescription'=> 'ContentGroupDescription',
340
- 'WM/Director' => 'Director',
352
+ 'WM/Director' => { Name => 'Director', Writable => 'Unicode', List => 1 },
341
353
  'WM/EncodingTime' => {
342
354
  Name => 'EncodingTime',
343
355
  Groups => { 2 => 'Time' },
356
+ Shift => 'Time',
357
+ Writable => 'date',
344
358
  PrintConv => '$self->ConvertDateTime($val)',
359
+ PrintConvInv => '$self->InverseDateTime($val)',
345
360
  },
346
361
  'WM/Genre' => 'Genre',
347
362
  'WM/GenreID' => 'GenreID',
348
- 'WM/InitialKey' => 'InitialKey',
363
+ 'WM/InitialKey' => { Name => 'InitialKey', Writable => 'Unicode' },
349
364
  'WM/Language' => 'Language',
350
365
  'WM/Lyrics' => 'Lyrics',
351
366
  'WM/MCDI' => 'MCDI',
352
367
  'WM/MediaClassPrimaryID' => {
353
368
  Name => 'MediaClassPrimaryID',
369
+ Writable => 'GUID',
354
370
  PrintConv => { #http://msdn.microsoft.com/en-us/library/windows/desktop/dd757960(v=vs.85).aspx
355
371
  'D1607DBC-E323-4BE2-86A1-48A42A28441E' => 'Music',
356
372
  'DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B' => 'Video',
@@ -360,6 +376,7 @@ my %sRegions = (
360
376
  },
361
377
  'WM/MediaClassSecondaryID' => {
362
378
  Name => 'MediaClassSecondaryID',
379
+ Writable => 'GUID',
363
380
  PrintConv => { #http://msdn.microsoft.com/en-us/library/windows/desktop/dd757960(v=vs.85).aspx
364
381
  'E0236BEB-C281-4EDE-A36D-7AF76A3D45B5' => 'Audio Book',
365
382
  '3A172A13-2BD9-4831-835B-114F6A95943F' => 'Spoken Word',
@@ -385,22 +402,22 @@ my %sRegions = (
385
402
  },
386
403
  'WM/MediaOriginalChannel' => 'MediaOriginalChannel',
387
404
  'WM/MediaStationName' => 'MediaStationName',
388
- 'WM/Mood' => 'Mood',
389
- 'WM/OriginalAlbumTitle' => 'OriginalAlbumTitle',
390
- 'WM/OriginalArtist' => 'OriginalArtist',
391
- 'WM/OriginalLyricist' => 'OriginalLyricist',
392
- 'WM/ParentalRating' => 'ParentalRating',
405
+ 'WM/Mood' => { Name => 'Mood', Writable => 'Unicode' },
406
+ 'WM/OriginalAlbumTitle' => { Name => 'OriginalAlbumTitle', Writable => 'Unicode' }, # (NC)
407
+ 'WM/OriginalArtist' => { Name => 'OriginalArtist', Writable => 'Unicode' }, # (NC)
408
+ 'WM/OriginalLyricist' => { Name => 'OriginalLyricist', Writable => 'Unicode' }, # (NC)
409
+ 'WM/ParentalRating' => { Name => 'ParentalRating', Writable => 'Unicode' },
393
410
  'WM/PartOfSet' => 'PartOfSet',
394
- 'WM/Period' => 'Period',
395
- 'WM/Producer' => 'Producer',
411
+ 'WM/Period' => { Name => 'Period', Writable => 'Unicode' },
412
+ 'WM/Producer' => { Name => 'Producer', Writable => 'Unicode', List => 1 },
396
413
  'WM/ProtectionType' => 'ProtectionType',
397
- 'WM/Provider' => 'Provider',
414
+ 'WM/Provider' => { Name => 'Provider', Writable => 'Unicode' }, # (NC)
398
415
  'WM/ProviderRating' => 'ProviderRating',
399
416
  'WM/ProviderStyle' => 'ProviderStyle',
400
- 'WM/Publisher' => 'Publisher',
401
- 'WM/SharedUserRating' => 'SharedUserRating',
417
+ 'WM/Publisher' => { Name => 'Publisher', Writable => 'Unicode' }, # (multiple entries separated by semicolon)
418
+ 'WM/SharedUserRating' => { Name => 'SharedUserRating', Writable => 'int64u' },
402
419
  'WM/SubscriptionContentID' => 'SubscriptionContentID',
403
- 'WM/SubTitle' => 'Subtitle',
420
+ 'WM/SubTitle' => { Name => 'Subtitle', Writable => 'Unicode' },
404
421
  'WM/SubTitleDescription' => 'SubtitleDescription',
405
422
  'WM/TrackNumber' => 'TrackNumber',
406
423
  'WM/UniqueFileIdentifier' => 'UniqueFileIdentifier',
@@ -412,8 +429,11 @@ my %sRegions = (
412
429
  'WM/WMContentID' => 'WMContentID',
413
430
  'WM/WMShadowFileSourceDRMType' => 'WMShadowFileSourceDRMType',
414
431
  'WM/WMShadowFileSourceFileType' => 'WMShadowFileSourceFileType',
415
- 'WM/Writer' => 'Writer',
416
- 'WM/Year' => { Name => 'Year', Groups => { 2 => 'Time' } },
432
+ 'WM/Writer' => { Name => 'Writer', Groups => { 2 => 'Author' }, Writable => 'Unicode' }, # (NC)
433
+ 'WM/Year' => { Name => 'Year', Groups => { 2 => 'Time' } },
434
+ 'WM/PromotionURL' => { Name => 'PromotionURL',Writable => 'Unicode' },
435
+ 'WM/AuthorURL' => { Name => 'AuthorURL', Groups => { 2 => 'Author' }, Writable => 'Unicode' },
436
+ 'WM/EncodedBy', => { Name => 'EncodedBy', Writable => 'Unicode' },
417
437
 
418
438
  # I can't find documentation for the following tags in videos,
419
439
  # but the tag ID's correspond to Microsoft property GUID+ID's
@@ -422,9 +442,12 @@ my %sRegions = (
422
442
  # http://multi-rename-script.googlecode.com/svn-history/r4/trunk/plugins/ShellDetails/ShellDetails.ini
423
443
  # I have observed only 1 so far:
424
444
  '{2CBAA8F5-D81F-47CA-B17A-F8D822300131} 100' => {
425
- Name => 'DateAcquired',
445
+ Name => 'DateAcquired', # (seems to be when videos are downloaded from the camera)
426
446
  Groups => { 2 => 'Time' },
447
+ Shift => 'Time',
448
+ Writable => 'vt_filetime',
427
449
  PrintConv => '$self->ConvertDateTime($val)',
450
+ PrintConvInv => '$self->InverseDateTime($val,undef)',
428
451
  },
429
452
  # the following have not yet been observed...
430
453
  '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 10' => 'Name',
@@ -764,6 +787,245 @@ my %sRegions = (
764
787
  '{64440491-4C8B-11D1-8B70-080036B11A03} 43' => 'TotalBitrate',
765
788
  );
766
789
 
790
+ #------------------------------------------------------------------------------
791
+ # check new value for Xtra tag
792
+ # Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
793
+ # Returns: error string, or undef on success
794
+ sub CheckXtra($$$)
795
+ {
796
+ my ($et, $tagInfo, $valPt) = @_;
797
+ my $format = $$tagInfo{Writable};
798
+ return 'Unknown format' unless $format;
799
+ if ($format =~ /^int/) {
800
+ return 'Not an integer' unless Image::ExifTool::IsInt($$valPt);
801
+ } elsif ($format ne 'Unicode') {
802
+ my @vals = ($$valPt);
803
+ return 'Invalid format' unless WriteXtraValue($et, $tagInfo, \@vals);
804
+ }
805
+ return undef;
806
+ }
807
+
808
+ #------------------------------------------------------------------------------
809
+ # Decode value(s) in Microsoft Xtra tag
810
+ # Inputs: 0) ExifTool object ref, 1) value data
811
+ # Returns: Scalar context: decoded value, List context: 0) decoded value, 1) format string
812
+ sub ReadXtraValue($$)
813
+ {
814
+ my ($et, $data) = @_;
815
+ my ($format, $i, @vals);
816
+
817
+ return undef if length($data) < 10;
818
+
819
+ # (version flags according to the reference, but looks more like a count - PH)
820
+ my $count = Get32u(\$data, 0);
821
+ # point to start of first value (after 4-byte count, 4-byte length and 2-byte type)
822
+ my $valPos = 10;
823
+ for ($i=0; ;) {
824
+ # (stored value includes size of $valLen and $valType, so subtract 6)
825
+ my $valLen = Get32u(\$data, $valPos - 6) - 6;
826
+ last if $valPos + $valLen > length($data);
827
+ my $valType = Get16u(\$data, $valPos - 2);
828
+ my $val = substr($data, $valPos, $valLen);
829
+ # Note: all dumb Microsoft values are little-endian inside a big-endian-format file
830
+ SetByteOrder('II');
831
+ if ($valType == 8) {
832
+ $format = 'Unicode';
833
+ $val = $et->Decode($val, 'UCS2');
834
+ } elsif ($valType == 19 and $valLen == 8) {
835
+ $format = 'int64u';
836
+ $val = Get64u(\$val, 0);
837
+ } elsif ($valType == 21 and $valLen == 8) {
838
+ $format = 'date';
839
+ $val = Get64u(\$val, 0);
840
+ # convert time from 100 ns intervals since Jan 1, 1601
841
+ $val = $val * 1e-7 - 11644473600 if $val;
842
+ # (the Nikon S100 uses UTC timezone, same as ASF - PH)
843
+ $val = Image::ExifTool::ConvertUnixTime($val, 1);
844
+ } elsif ($valType == 72 and $valLen == 16) {
845
+ $format = 'GUID';
846
+ $val = uc unpack('H*',pack('NnnNN',unpack('VvvNN',$val)));
847
+ $val =~ s/(.{8})(.{4})(.{4})(.{4})/$1-$2-$3-$4-/;
848
+ } elsif ($valType == 65 and $valLen > 4) { #PH (empirical)
849
+ $format = 'variant';
850
+ require Image::ExifTool::FlashPix;
851
+ my $vPos = 0; # (necessary because ReadFPXValue updates this)
852
+ # read entry as a VT_VARIANT (use FlashPix module for this)
853
+ $val = Image::ExifTool::FlashPix::ReadFPXValue($et, \$val, $vPos,
854
+ Image::ExifTool::FlashPix::VT_VARIANT(), $valLen, 1);
855
+ } else {
856
+ $format = "Unknown($valType)";
857
+ }
858
+ SetByteOrder('MM'); # back to native QuickTime byte ordering
859
+ push @vals, $val;
860
+ last if ++$i >= $count;
861
+ $valPos += $valLen + 6; # step to next value
862
+ last if $valPos > length($data);
863
+ }
864
+ return wantarray ? (\@vals, $format) : \@vals;
865
+ }
866
+
867
+ #------------------------------------------------------------------------------
868
+ # Write a Microsoft Xtra value
869
+ # Inputs: 0) ExifTool object ref, 1) tagInfo ref, 2) reference to list of values
870
+ # Returns: new value binary data (or empty string)
871
+ sub WriteXtraValue($$$)
872
+ {
873
+ my ($et, $tagInfo, $vals) = @_;
874
+ my $format = $$tagInfo{Writable};
875
+ my $buff = '';
876
+ my $count = 0;
877
+ my $val;
878
+ foreach $val (@$vals) {
879
+ SetByteOrder('II');
880
+ my ($type, $dat);
881
+ if ($format eq 'Unicode') {
882
+ $dat = $et->Encode($val,'UCS2','II') . "\0\0"; # (must be null terminated)
883
+ $type = 8;
884
+ } elsif ($format eq 'int64u') {
885
+ if (Image::ExifTool::IsInt($val)) {
886
+ $dat = Set64u($val);
887
+ $type = 19;
888
+ }
889
+ } elsif ($format eq 'date') {
890
+ $dat = Image::ExifTool::GetUnixTime($val, 1); # (convert to UTC, NC)
891
+ if ($dat) {
892
+ # 100ns intervals since Jan 1, 1601
893
+ $dat = Set64u(($dat + 11644473600) * 1e7);
894
+ $type = 21;
895
+ }
896
+ } elsif ($format eq 'vt_filetime') { # 'date' value inside a VT_VARIANT
897
+ $dat = Image::ExifTool::GetUnixTime($val); # (leave as local time, NC)
898
+ if ($dat) {
899
+ # 100ns intervals since Jan 1, 1601
900
+ $dat = Set32u(64) . Set64u(($dat + 11644473600) * 1e7);
901
+ $type = 65;
902
+ }
903
+ } elsif ($format eq 'GUID') {
904
+ ($dat = $val) =~ tr/-//d;
905
+ if (length($dat) == 32) {
906
+ $dat = pack('VvvNN',unpack('NnnNN',pack('H*', $dat)));
907
+ $type = 72;
908
+ }
909
+ } else {
910
+ $et->WarnOnce("Error converting value for Microsoft:$$tagInfo{Name}");
911
+ }
912
+ SetByteOrder('MM');
913
+ if (defined $type) {
914
+ ++$count;
915
+ $buff .= Set32u(length($dat)+6) . Set16u($type) . $dat;
916
+ }
917
+ }
918
+ return $count ? Set32u($count) . $buff : '';
919
+ }
920
+
921
+ #------------------------------------------------------------------------------
922
+ # Add new values to list
923
+ # Inputs: 0) ExifTool ref, 1) new value list ref, 2) nvHash ref
924
+ # Returns: true if something was added
925
+ sub AddNewValues($$$)
926
+ {
927
+ my ($et, $vals, $nvHash) = @_;
928
+ my @newVals = $et->GetNewValue($nvHash) or return undef;
929
+ if ($$et{OPTIONS}{Verbose} > 1) {
930
+ $et->VPrint(1, " + Microsoft:$$nvHash{TagInfo}{Name} = $_\n") foreach @newVals;
931
+ }
932
+ push @$vals, @newVals;
933
+ return 1;
934
+ }
935
+
936
+ #------------------------------------------------------------------------------
937
+ # Write tags to a Microsoft Xtra MP4 atom
938
+ # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
939
+ # Returns: Microsoft Xtra data block (may be empty if no Xtra data) or undef on error
940
+ sub WriteXtra($$$)
941
+ {
942
+ my ($et, $dirInfo, $tagTablePtr) = @_;
943
+ $et or return 1; # allow dummy access
944
+
945
+ my $delGroup = ($$et{DEL_GROUP} and $$et{DEL_GROUP}{Microsoft});
946
+ my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
947
+
948
+ return undef unless $delGroup or %$newTags; # don't rewrite if nothing to do
949
+
950
+ my $dataPt = $$dirInfo{DataPt};
951
+ my $dataLen = length $$dataPt;
952
+ my $newData = '';
953
+ my $pos = 0;
954
+ my ($err, %done, $changed, $tag);
955
+
956
+ if ($delGroup) {
957
+ $changed = 1 if $dataLen;
958
+ my $empty = '';
959
+ $dataPt = $empty;
960
+ $dataLen = 0;
961
+ }
962
+ for (;;) {
963
+ last if $pos + 4 > $dataLen;
964
+ my $size = Get32u($dataPt, $pos); # (includes $size word)
965
+ ($size < 8 or $pos + $size > $dataLen) and $err=1, last;
966
+ my $tagLen = Get32u($dataPt, $pos + 4);
967
+ $tagLen + 18 > $size and $err=1, last;
968
+ $tag = substr($$dataPt, $pos + 8, $tagLen);
969
+ my @newVals;
970
+ while ($$newTags{$tag}) {
971
+ my $nvHash = $et->GetNewValueHash($$newTags{$tag});
972
+ $$nvHash{CreateOnly} and delete($$newTags{$tag}), last; # don't edit this tag
973
+ my $valPos = $pos + 8 + $tagLen;
974
+ my $valLen = $size - 8 - $tagLen;
975
+ my $val = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
976
+ foreach $val (@$val) {
977
+ my $overwrite = $et->IsOverwriting($nvHash, $val);
978
+ $overwrite or push(@newVals, $val), next;
979
+ $et->VPrint(1, " - Microsoft:$$newTags{$tag}{Name} = $val\n");
980
+ next if $done{$tag};
981
+ $done{$tag} = 1;
982
+ AddNewValues($et, \@newVals, $nvHash);
983
+ }
984
+ # add to the end of the list if this was a List-type tag and we didn't delete anything
985
+ if (not $done{$tag} and $$newTags{$tag}{List}) {
986
+ AddNewValues($et, \@newVals, $nvHash) or last;
987
+ $done{$tag} = 1;
988
+ }
989
+ last; # (it was a cheap goto)
990
+ }
991
+ if ($done{$tag}) {
992
+ $changed = 1;
993
+ # write changed values
994
+ my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
995
+ if (length $buff) {
996
+ $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
997
+ }
998
+ } else {
999
+ # nothing changed; just copy over
1000
+ $newData .= substr($$dataPt, $pos, $size);
1001
+ }
1002
+ $pos += $size; # step to next entry
1003
+ }
1004
+ if ($err) {
1005
+ $et->Warn('Microsoft Xtra format error');
1006
+ return undef;
1007
+ }
1008
+ # add any new tags
1009
+ foreach $tag (sort keys %$newTags) {
1010
+ next if $done{$tag};
1011
+ my $nvHash = $et->GetNewValueHash($$newTags{$tag});
1012
+ next unless $$nvHash{IsCreating} and not $$nvHash{EditOnly};
1013
+ my @newVals;
1014
+ AddNewValues($et, \@newVals, $nvHash) or next;
1015
+ my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
1016
+ if (length $buff) {
1017
+ $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
1018
+ $changed = 1;
1019
+ }
1020
+ }
1021
+ if ($changed) {
1022
+ ++$$et{CHANGED};
1023
+ } else {
1024
+ undef $newData;
1025
+ }
1026
+ return $newData;
1027
+ }
1028
+
767
1029
  #------------------------------------------------------------------------------
768
1030
  # Extract information from Xtra MP4 atom
769
1031
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -783,61 +1045,13 @@ sub ProcessXtra($$$)
783
1045
  last if $size < 8 or $pos + $size > $dataLen;
784
1046
  my $tagLen = Get32u($dataPt, $pos + 4);
785
1047
  last if $tagLen + 18 > $size;
786
- my $tag = substr($$dataPt, $pos + 8, $tagLen);
787
- # (version flags according to the reference, but looks more like a count - PH)
788
- my $count = Get32u($dataPt, $pos + $tagLen + 8);
789
- my ($i, $valPos, $valLen, $valType, $val, $format, @vals);
790
- # point to start of first value (after 4-byte length and 2-byte type)
791
- $valPos = $pos + $tagLen + 18;
792
- for ($i=0; ;) {
793
- # (stored value includes size of $valLen and $valType, so subtract 6)
794
- $valLen = Get32u($dataPt, $valPos - 6) - 6;
795
- my $more = $pos + $size - $valPos - $valLen;
796
- last if $more < 0;
797
- $valType = Get16u($dataPt, $valPos - 2);
798
- $val = substr($$dataPt, $valPos, $valLen);
799
- # Note: all dumb Microsoft values are little-endian inside a big-endian-format file
800
- SetByteOrder('II');
801
- if ($valType == 8) {
802
- $format = 'Unicode';
803
- $val = $et->Decode($val, 'UCS2');
804
- } elsif ($valType == 19 and $valLen == 8) {
805
- $format = 'int64u';
806
- $val = Get64u(\$val, 0);
807
- } elsif ($valType == 21 and $valLen == 8) {
808
- $format = 'date';
809
- $val = Get64u(\$val, 0);
810
- # convert time from 100 ns intervals since Jan 1, 1601
811
- $val = $val * 1e-7 - 11644473600 if $val;
812
- # (the Nikon S100 uses UTC timezone, same as ASF - PH)
813
- $val = Image::ExifTool::ConvertUnixTime($val) . 'Z';
814
- } elsif ($valType == 72 and $valLen == 16) {
815
- $format = 'GUID';
816
- $val = uc unpack('H*',pack('NnnNN',unpack('VvvNN',$val)));
817
- $val =~ s/(.{8})(.{4})(.{4})(.{4})/$1-$2-$3-$4-/;
818
- } elsif ($valType == 65 && $valLen > 4) { #PH (empirical)
819
- $format = 'variant';
820
- require Image::ExifTool::FlashPix;
821
- my $vPos = $valPos; # (necessary because ReadFPXValue updates this)
822
- # read entry as a VT_VARIANT (use FlashPix module for this)
823
- $val = Image::ExifTool::FlashPix::ReadFPXValue($et, $dataPt, $vPos,
824
- Image::ExifTool::FlashPix::VT_VARIANT(), $valPos+$valLen, 1);
825
- } else {
826
- $format = "Unknown($valType)";
827
- }
828
- SetByteOrder('MM'); # back to native QuickTime byte ordering
829
- last if ++$i >= $count or $more < 6;
830
- push @vals, $val;
831
- undef $val;
832
- $valPos += $valLen + 6; # step to next value
833
- }
834
- if (@vals) {
835
- push @vals, $val if defined $val;
836
- $val = \@vals;
837
- $valPos = $pos + $tagLen + 18;
838
- $valLen = $size - 18 - $tagLen;
839
- }
1048
+ my $valLen = $size - 8 - $tagLen;
840
1049
  if ($tagLen > 0 and $valLen > 0) {
1050
+ my $tag = substr($$dataPt, $pos + 8, $tagLen);
1051
+ my $valPos = $pos + 8 + $tagLen;
1052
+ my ($val, $format) = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
1053
+ last unless defined $val;
1054
+ $val = $$val[0] if @$val == 1;
841
1055
  my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
842
1056
  unless ($tagInfo) {
843
1057
  # generate tag information for unrecognized tags
@@ -850,6 +1064,7 @@ sub ProcessXtra($$$)
850
1064
  $et->VPrint(0, $$et{INDENT}, "[adding Microsoft:$tag]\n");
851
1065
  }
852
1066
  }
1067
+ my $count = ref $val ? scalar @$val : 1;
853
1068
  $et->HandleTag($tagTablePtr, $tag, $val,
854
1069
  TagInfo => $tagInfo,
855
1070
  DataPt => $dataPt,
@@ -857,7 +1072,7 @@ sub ProcessXtra($$$)
857
1072
  Start => $valPos,
858
1073
  Size => $valLen,
859
1074
  Format => $format,
860
- Extra => " count=$count type=$valType",
1075
+ Extra => " count=$count",
861
1076
  );
862
1077
  }
863
1078
  $pos += $size; # step to next entry
@@ -880,7 +1095,8 @@ This module is used by Image::ExifTool
880
1095
  =head1 DESCRIPTION
881
1096
 
882
1097
  This module contains definitions required by Image::ExifTool to interpret
883
- Microsoft-specific EXIF and XMP tags.
1098
+ Microsoft-specific EXIF and XMP tags, and routines to read/write Microsoft
1099
+ Xtra tags in videos.
884
1100
 
885
1101
  =head1 AUTHOR
886
1102