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.
- 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
|
|