exiftool_vendored 12.16.0 → 12.25.0

Sign up to get free protection for your applications and to get access to all the features.
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