exiftool_vendored 12.18.0 → 12.22.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of exiftool_vendored might be problematic. Click here for more details.

Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +54 -0
  3. data/bin/MANIFEST +1 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +2 -2
  7. data/bin/config_files/example.config +1 -8
  8. data/bin/exiftool +70 -32
  9. data/bin/fmt_files/gpx.fmt +1 -1
  10. data/bin/fmt_files/gpx_wpt.fmt +1 -1
  11. data/bin/fmt_files/kml.fmt +1 -1
  12. data/bin/fmt_files/kml_track.fmt +1 -1
  13. data/bin/lib/Image/ExifTool.pm +74 -24
  14. data/bin/lib/Image/ExifTool.pod +33 -25
  15. data/bin/lib/Image/ExifTool/Apple.pm +3 -2
  16. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +20 -10
  17. data/bin/lib/Image/ExifTool/Canon.pm +6 -1
  18. data/bin/lib/Image/ExifTool/DJI.pm +6 -6
  19. data/bin/lib/Image/ExifTool/Exif.pm +5 -2
  20. data/bin/lib/Image/ExifTool/FITS.pm +13 -2
  21. data/bin/lib/Image/ExifTool/GPS.pm +22 -11
  22. data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
  23. data/bin/lib/Image/ExifTool/M2TS.pm +40 -4
  24. data/bin/lib/Image/ExifTool/MIE.pm +2 -2
  25. data/bin/lib/Image/ExifTool/Microsoft.pm +296 -82
  26. data/bin/lib/Image/ExifTool/Nikon.pm +2 -1
  27. data/bin/lib/Image/ExifTool/Olympus.pm +2 -2
  28. data/bin/lib/Image/ExifTool/QuickTime.pm +28 -12
  29. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +6 -5
  30. data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
  31. data/bin/lib/Image/ExifTool/Sony.pm +51 -17
  32. data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
  33. data/bin/lib/Image/ExifTool/TagLookup.pm +4031 -4022
  34. data/bin/lib/Image/ExifTool/TagNames.pod +130 -95
  35. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
  36. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +7 -5
  37. data/bin/lib/Image/ExifTool/Writer.pl +43 -10
  38. data/bin/lib/Image/ExifTool/XMP.pm +4 -4
  39. data/bin/perl-Image-ExifTool.spec +1 -1
  40. data/lib/exiftool_vendored/version.rb +1 -1
  41. metadata +20 -6
@@ -24,7 +24,7 @@ use strict;
24
24
  use vars qw($VERSION);
25
25
  use Image::ExifTool qw(:DataAccess :Utils);
26
26
 
27
- $VERSION = '1.35';
27
+ $VERSION = '1.36';
28
28
 
29
29
  sub ProcessICC($$);
30
30
  sub ProcessICC_Profile($$$);
@@ -1225,7 +1225,7 @@ sub ProcessICC_Profile($$$)
1225
1225
  my $type = substr($$dataPt, $valuePtr, 4);
1226
1226
  #### eval Validate ($type)
1227
1227
  if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
1228
- $et->Warn("Invalid $name data");
1228
+ $et->Warn("Invalid ICC $name data");
1229
1229
  } else {
1230
1230
  $et->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
1231
1231
  }
@@ -31,7 +31,7 @@ use strict;
31
31
  use vars qw($VERSION);
32
32
  use Image::ExifTool qw(:DataAccess :Utils);
33
33
 
34
- $VERSION = '1.18';
34
+ $VERSION = '1.19';
35
35
 
36
36
  # program map table "stream_type" lookup (ref 6/1)
37
37
  my %streamType = (
@@ -57,7 +57,8 @@ my %streamType = (
57
57
  0x13 => 'ISO 14496-1 SL-packetized',
58
58
  0x14 => 'ISO 13818-6 Synchronized Download Protocol',
59
59
  # 0x15-0x7F => 'ISO 13818-1 Reserved',
60
- 0x1b => 'H.264 Video',
60
+ 0x1b => 'H.264 (AVC) Video',
61
+ 0x24 => 'H.265 (HEVC) Video', #PH
61
62
  0x80 => 'DigiCipher II Video',
62
63
  0x81 => 'A52/AC-3 Audio',
63
64
  0x82 => 'HDMV DTS Audio',
@@ -67,6 +68,7 @@ my %streamType = (
67
68
  0x86 => 'DTS-HD Audio',
68
69
  0x87 => 'E-AC-3 Audio',
69
70
  0x8a => 'DTS Audio',
71
+ 0x90 => 'PGS Audio', #https://www.avsforum.com/threads/bass-eq-for-filtered-movies.2995212/page-399
70
72
  0x91 => 'A52b/AC-3 Audio',
71
73
  0x92 => 'DVD_SPU vls Subtitle',
72
74
  0x94 => 'SDDS Audio',
@@ -299,6 +301,33 @@ sub ParsePID($$$$$)
299
301
  } elsif ($type == 0x81 or $type == 0x87 or $type == 0x91) {
300
302
  # AC-3 audio
301
303
  ParseAC3Audio($et, $dataPt);
304
+ } elsif ($type < 0) {
305
+ if ($$dataPt =~ /^(.{164})?(.{24})A[NS][EW]/s) {
306
+ # (Blueskysea B4K, Novatek NT96670)
307
+ # 0000: 01 00 ff 00 30 31 32 33 34 35 37 38 61 62 63 64 [....01234578abcd]
308
+ # 0010: 65 66 67 0a 00 00 00 00 00 00 00 00 00 00 00 00 [efg.............]
309
+ # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
310
+ # 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
311
+ # 0040: 00 00 00 00 30 31 32 33 34 35 37 38 71 77 65 72 [....01234578qwer]
312
+ # 0050: 74 79 75 69 6f 70 0a 00 00 00 00 00 00 00 00 00 [tyuiop..........]
313
+ # 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
314
+ # 0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
315
+ # 0080: 00 00 00 00 63 38 61 61 32 35 63 66 34 35 65 65 [....c8aa25cf45ee]
316
+ # 0090: 61 39 65 32 34 34 32 66 61 65 62 35 65 30 39 39 [a9e2442faeb5e099]
317
+ # 00a0: 30 37 64 34 15 00 00 00 10 00 00 00 1b 00 00 00 [07d4............]
318
+ # 00b0: 15 00 00 00 01 00 00 00 09 00 00 00 41 4e 57 00 [............ANW.]
319
+ # 00c0: 82 9a 57 45 98 b2 00 46 66 66 e4 41 d7 e3 14 43 [..WE...Fff.A...C]
320
+ # 00d0: 01 00 02 00 03 00 04 00 05 00 06 00 [............]
321
+ # (Viofo A119V3)
322
+ # 0000: 08 00 00 00 07 00 00 00 18 00 00 00 15 00 00 00 [................]
323
+ # 0010: 03 00 00 00 0b 00 00 00 41 4e 45 00 01 f2 ac 45 [........ANE....E]
324
+ # 0020: 2d 7f 6e 45 b8 1e 97 41 d7 23 46 43 00 00 00 00 [-.nE...A.#FC....]
325
+ # pad with dummy header and parse with existing FreeGPS code (minimum 92 bytes)
326
+ my $dat = ("\0" x 16) . substr($$dataPt, length($1 || '')) . ("\0" x 20);
327
+ my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
328
+ Image::ExifTool::QuickTime::ProcessFreeGPS($et, { DataPt => \$dat }, $tbl);
329
+ $more = 1;
330
+ }
302
331
  }
303
332
  return $more;
304
333
  }
@@ -352,6 +381,14 @@ sub ProcessM2TS($$)
352
381
  my %needPID = ( 0 => 1 ); # lookup for stream PID's that we still need to parse
353
382
  my $pEnd = 0;
354
383
 
384
+ # scan entire file for GPS program 0x0300 if ExtractEmbedded option is 3 or higher
385
+ # (some dashcams write this program but don't include it in the PMT)
386
+ if (($et->Options('ExtractEmbedded') || 0) > 2) {
387
+ $needPID{0x0300} = 1;
388
+ $pidType{0x0300} = -1;
389
+ $pidName{0x0300} = 'unregistered dashcam GPS';
390
+ }
391
+
355
392
  # parse packets from MPEG-2 Transport Stream
356
393
  for (;;) {
357
394
 
@@ -421,12 +458,11 @@ sub ProcessM2TS($$)
421
458
  my $adaptation_field_exists = $prefix & 0x00000020;
422
459
  my $payload_data_exists = $prefix & 0x00000010;
423
460
  # my $continuity_counter = $prefix & 0x0000000f;
424
-
425
461
  if ($verbose > 1) {
426
462
  my $i = ($raf->Tell() - length($buff) + $pEnd) / $pLen - 1;
427
463
  print $out "Transport packet $i:\n";
428
464
  $et->VerboseDump(\$buff, Len => $pLen, Addr => $i * $pLen, Start => $pos - $prePos);
429
- my $str = $pidName{$pid} ? " ($pidName{$pid})" : '';
465
+ my $str = $pidName{$pid} ? " ($pidName{$pid})" : ' <not in Program Map Table!>';
430
466
  printf $out " Timecode: 0x%.4x\n", Get32u(\$buff, $pos - $prePos) if $pLen == 192;
431
467
  printf $out " Packet ID: 0x%.4x$str\n", $pid;
432
468
  printf $out " Start Flag: %s\n", $payload_unit_start_indicator ? 'Yes' : 'No';
@@ -393,7 +393,7 @@ my %offOn = ( 0 => 'Off', 1 => 'On' );
393
393
  ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
394
394
  ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val, 0)',
395
395
  PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
396
- PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
396
+ PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lat")',
397
397
  },
398
398
  Longitude => {
399
399
  Name => 'GPSLongitude',
@@ -406,7 +406,7 @@ my %offOn = ( 0 => 'Off', 1 => 'On' );
406
406
  ValueConv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
407
407
  ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val, 0)',
408
408
  PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
409
- PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
409
+ PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lon")',
410
410
  },
411
411
  MeasureMode => {
412
412
  Name => 'GPSMeasureMode',
@@ -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.22';
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,243 @@ 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
+ $dat = Set64u(($dat + 11644473600) * 1e7);
893
+ $type = 21;
894
+ }
895
+ } elsif ($format eq 'vt_filetime') {
896
+ $dat = Image::ExifTool::GetUnixTime($val); # (leave as local time, NC)
897
+ if ($dat) {
898
+ $dat = Set32u(64) . Set64u(($dat + 11644473600) * 1e7);
899
+ $type = 65;
900
+ }
901
+ } elsif ($format eq 'GUID') {
902
+ ($dat = $val) =~ tr/-//d;
903
+ if (length($dat) == 32) {
904
+ $dat = pack('VvvNN',unpack('NnnNN',pack('H*', $dat)));
905
+ $type = 72;
906
+ }
907
+ } else {
908
+ $et->WarnOnce("Error converting value for Microsoft:$$tagInfo{Name}");
909
+ }
910
+ SetByteOrder('MM');
911
+ if (defined $type) {
912
+ ++$count;
913
+ $buff .= Set32u(length($dat)+6) . Set16u($type) . $dat;
914
+ }
915
+ }
916
+ return $count ? Set32u($count) . $buff : '';
917
+ }
918
+
919
+ #------------------------------------------------------------------------------
920
+ # Add new values to list
921
+ # Inputs: 0) ExifTool ref, 1) new value list ref, 2) nvHash ref
922
+ # Returns: true if something was added
923
+ sub AddNewValues($$$)
924
+ {
925
+ my ($et, $vals, $nvHash) = @_;
926
+ my @newVals = $et->GetNewValue($nvHash) or return undef;
927
+ if ($$et{OPTIONS}{Verbose} > 1) {
928
+ $et->VPrint(1, " + Microsoft:$$nvHash{TagInfo}{Name} = $_\n") foreach @newVals;
929
+ }
930
+ push @$vals, @newVals;
931
+ return 1;
932
+ }
933
+
934
+ #------------------------------------------------------------------------------
935
+ # Write tags to a Microsoft Xtra MP4 atom
936
+ # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
937
+ # Returns: Microsoft Xtra data block (may be empty if no Xtra data) or undef on error
938
+ sub WriteXtra($$$)
939
+ {
940
+ my ($et, $dirInfo, $tagTablePtr) = @_;
941
+ $et or return 1; # allow dummy access
942
+
943
+ my $delGroup = ($$et{DEL_GROUP} and $$et{DEL_GROUP}{Microsoft});
944
+ my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
945
+
946
+ return undef unless $delGroup or %$newTags; # don't rewrite if nothing to do
947
+
948
+ my $dataPt = $$dirInfo{DataPt};
949
+ my $dataLen = length $$dataPt;
950
+ my $newData = '';
951
+ my $pos = 0;
952
+ my ($err, %done, $changed, $tag);
953
+
954
+ if ($delGroup) {
955
+ $changed = 1 if $dataLen;
956
+ my $empty = '';
957
+ $dataPt = $empty;
958
+ $dataLen = 0;
959
+ }
960
+ for (;;) {
961
+ last if $pos + 4 > $dataLen;
962
+ my $size = Get32u($dataPt, $pos); # (includes $size word)
963
+ ($size < 8 or $pos + $size > $dataLen) and $err=1, last;
964
+ my $tagLen = Get32u($dataPt, $pos + 4);
965
+ $tagLen + 18 > $size and $err=1, last;
966
+ $tag = substr($$dataPt, $pos + 8, $tagLen);
967
+ my @newVals;
968
+ while ($$newTags{$tag}) {
969
+ my $nvHash = $et->GetNewValueHash($$newTags{$tag});
970
+ $$nvHash{CreateOnly} and delete($$newTags{$tag}), last; # don't edit this tag
971
+ my $valPos = $pos + 8 + $tagLen;
972
+ my $valLen = $size - 8 - $tagLen;
973
+ my $val = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
974
+ foreach $val (@$val) {
975
+ my $overwrite = $et->IsOverwriting($nvHash, $val);
976
+ $overwrite or push(@newVals, $val), next;
977
+ $et->VPrint(1, " - Microsoft:$$newTags{$tag}{Name} = $val\n");
978
+ next if $done{$tag};
979
+ $done{$tag} = 1;
980
+ AddNewValues($et, \@newVals, $nvHash);
981
+ }
982
+ # add to the end of the list if this was a List-type tag and we didn't delete anything
983
+ if (not $done{$tag} and $$newTags{$tag}{List}) {
984
+ AddNewValues($et, \@newVals, $nvHash) or last;
985
+ $done{$tag} = 1;
986
+ }
987
+ last; # (it was a cheap goto)
988
+ }
989
+ if ($done{$tag}) {
990
+ # write changed values
991
+ my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
992
+ if (length $buff) {
993
+ $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
994
+ $changed = 1;
995
+ }
996
+ } else {
997
+ # nothing changed; just copy over
998
+ $newData .= substr($$dataPt, $pos, $size);
999
+ }
1000
+ $pos += $size; # step to next entry
1001
+ }
1002
+ if ($err) {
1003
+ $et->Warn('Microsoft Xtra format error');
1004
+ return undef;
1005
+ }
1006
+ # add any new tags
1007
+ foreach $tag (sort keys %$newTags) {
1008
+ next if $done{$tag};
1009
+ my $nvHash = $et->GetNewValueHash($$newTags{$tag});
1010
+ next unless $$nvHash{IsCreating} and not $$nvHash{EditOnly};
1011
+ my @newVals;
1012
+ AddNewValues($et, \@newVals, $nvHash) or next;
1013
+ my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
1014
+ if (length $buff) {
1015
+ $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
1016
+ $changed = 1;
1017
+ }
1018
+ }
1019
+ if ($changed) {
1020
+ ++$$et{CHANGED};
1021
+ } else {
1022
+ undef $newData;
1023
+ }
1024
+ return $newData;
1025
+ }
1026
+
767
1027
  #------------------------------------------------------------------------------
768
1028
  # Extract information from Xtra MP4 atom
769
1029
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -783,61 +1043,13 @@ sub ProcessXtra($$$)
783
1043
  last if $size < 8 or $pos + $size > $dataLen;
784
1044
  my $tagLen = Get32u($dataPt, $pos + 4);
785
1045
  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
- }
1046
+ my $valLen = $size - 8 - $tagLen;
840
1047
  if ($tagLen > 0 and $valLen > 0) {
1048
+ my $tag = substr($$dataPt, $pos + 8, $tagLen);
1049
+ my $valPos = $pos + 8 + $tagLen;
1050
+ my ($val, $format) = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
1051
+ last unless defined $val;
1052
+ $val = $$val[0] if @$val == 1;
841
1053
  my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
842
1054
  unless ($tagInfo) {
843
1055
  # generate tag information for unrecognized tags
@@ -850,6 +1062,7 @@ sub ProcessXtra($$$)
850
1062
  $et->VPrint(0, $$et{INDENT}, "[adding Microsoft:$tag]\n");
851
1063
  }
852
1064
  }
1065
+ my $count = ref $val ? scalar @$val : 1;
853
1066
  $et->HandleTag($tagTablePtr, $tag, $val,
854
1067
  TagInfo => $tagInfo,
855
1068
  DataPt => $dataPt,
@@ -857,7 +1070,7 @@ sub ProcessXtra($$$)
857
1070
  Start => $valPos,
858
1071
  Size => $valLen,
859
1072
  Format => $format,
860
- Extra => " count=$count type=$valType",
1073
+ Extra => " count=$count",
861
1074
  );
862
1075
  }
863
1076
  $pos += $size; # step to next entry
@@ -880,7 +1093,8 @@ This module is used by Image::ExifTool
880
1093
  =head1 DESCRIPTION
881
1094
 
882
1095
  This module contains definitions required by Image::ExifTool to interpret
883
- Microsoft-specific EXIF and XMP tags.
1096
+ Microsoft-specific EXIF and XMP tags, and routines to read/write Microsoft
1097
+ Xtra tags in videos.
884
1098
 
885
1099
  =head1 AUTHOR
886
1100