exiftool_vendored 12.18.0 → 12.22.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.
Potentially problematic release.
This version of exiftool_vendored might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/Changes +54 -0
- data/bin/MANIFEST +1 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +2 -2
- data/bin/config_files/example.config +1 -8
- data/bin/exiftool +70 -32
- data/bin/fmt_files/gpx.fmt +1 -1
- data/bin/fmt_files/gpx_wpt.fmt +1 -1
- data/bin/fmt_files/kml.fmt +1 -1
- data/bin/fmt_files/kml_track.fmt +1 -1
- data/bin/lib/Image/ExifTool.pm +74 -24
- data/bin/lib/Image/ExifTool.pod +33 -25
- data/bin/lib/Image/ExifTool/Apple.pm +3 -2
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +20 -10
- data/bin/lib/Image/ExifTool/Canon.pm +6 -1
- data/bin/lib/Image/ExifTool/DJI.pm +6 -6
- data/bin/lib/Image/ExifTool/Exif.pm +5 -2
- data/bin/lib/Image/ExifTool/FITS.pm +13 -2
- data/bin/lib/Image/ExifTool/GPS.pm +22 -11
- data/bin/lib/Image/ExifTool/ICC_Profile.pm +2 -2
- data/bin/lib/Image/ExifTool/M2TS.pm +40 -4
- data/bin/lib/Image/ExifTool/MIE.pm +2 -2
- data/bin/lib/Image/ExifTool/Microsoft.pm +296 -82
- data/bin/lib/Image/ExifTool/Nikon.pm +2 -1
- data/bin/lib/Image/ExifTool/Olympus.pm +2 -2
- data/bin/lib/Image/ExifTool/QuickTime.pm +28 -12
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +6 -5
- data/bin/lib/Image/ExifTool/Shortcuts.pm +9 -0
- data/bin/lib/Image/ExifTool/Sony.pm +51 -17
- data/bin/lib/Image/ExifTool/TagInfoXML.pm +1 -0
- data/bin/lib/Image/ExifTool/TagLookup.pm +4031 -4022
- data/bin/lib/Image/ExifTool/TagNames.pod +130 -95
- data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +7 -5
- data/bin/lib/Image/ExifTool/Writer.pl +43 -10
- data/bin/lib/Image/ExifTool/XMP.pm +4 -4
- data/bin/perl-Image-ExifTool.spec +1 -1
- data/lib/exiftool_vendored/version.rb +1 -1
- 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.
|
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.
|
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
|
+
$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
|
203
|
-
|
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',
|
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 $
|
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
|
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
|
|