File Coverage

blib/lib/Image/ExifTool/Matroska.pm
Criterion Covered Total %
statement 113 204 55.3
branch 50 138 36.2
condition 19 51 37.2
subroutine 5 6 83.3
pod 0 3 0.0
total 187 402 46.5


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Matroska.pm
3             #
4             # Description: Read meta information from Matroska multimedia files
5             #
6             # Revisions: 05/26/2010 - P. Harvey Created
7             #
8             # References: 1) http://www.matroska.org/technical/specs/index.html
9             # 2) https://www.matroska.org/technical/tagging.html
10             #------------------------------------------------------------------------------
11              
12             package Image::ExifTool::Matroska;
13              
14 1     1   4451 use strict;
  1         2  
  1         32  
15 1     1   5 use vars qw($VERSION);
  1         10  
  1         39  
16 1     1   5 use Image::ExifTool qw(:DataAccess :Utils);
  1         4  
  1         3942  
17              
18             $VERSION = '1.14';
19              
20             sub HandleStruct($$;$$$$);
21              
22             my %noYes = ( 0 => 'No', 1 => 'Yes' );
23              
24             my %dateInfo = (
25             Groups => { 2 => 'Time' },
26             # the spec says to use "-" as a date separator, but my only sample uses ":", so
27             # convert to ":" if necessary, and avoid translating all "-" in case someone wants
28             # to include a negative time zone (although the spec doesn't mention time zones)
29             ValueConv => '$val =~ s/^(\d{4})-(\d{2})-/$1:$2:/; $val',
30             PrintConv => '$self->ConvertDateTime($val)',
31             );
32              
33             my %uidInfo = (
34             Format => 'string',
35             ValueConv => 'unpack("H*",$val)'
36             );
37              
38             # Matroska tags
39             # Note: The tag ID's in the Matroska documentation include the length designation
40             # (the upper bits), which is not included in the tag ID's below
41             %Image::ExifTool::Matroska::Main = (
42             GROUPS => { 2 => 'Video' },
43             VARS => { NO_LOOKUP => 1 }, # omit tags from lookup
44             NOTES => q{
45             The following tags are extracted from Matroska multimedia container files.
46             This container format is used by file types such as MKA, MKV, MKS and WEBM.
47             For speed, by default ExifTool extracts tags only up to the first Cluster.
48             However, the L (-v) and L = 2 (-U) options force processing of
49             Cluster data, and the L (-ee) option skips over Clusters to
50             read subsequent tags. See
51             L for the official
52             Matroska specification.
53             },
54             # supported Format's: signed, unsigned, float, date, string, utf8
55             # (or undef by default)
56             #
57             # EBML Header
58             #
59             0xa45dfa3 => {
60             Name => 'EBMLHeader',
61             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
62             },
63             0x286 => { Name => 'EBMLVersion', Format => 'unsigned' },
64             0x2f7 => { Name => 'EBMLReadVersion', Format => 'unsigned' },
65             0x2f2 => { Name => 'EBMLMaxIDLength', Format => 'unsigned', Unknown => 1 },
66             0x2f3 => { Name => 'EBMLMaxSizeLength', Format => 'unsigned', Unknown => 1 },
67             0x282 => {
68             Name => 'DocType',
69             Format => 'string',
70             # override FileType for "webm" files
71             RawConv => '$self->OverrideFileType("WEBM") if $val eq "webm"; $val',
72             },
73             0x287 => { Name => 'DocTypeVersion', Format => 'unsigned' },
74             0x285 => { Name => 'DocTypeReadVersion',Format => 'unsigned' },
75             #
76             # General
77             #
78             0x3f => { Name => 'CRC-32', Format => 'unsigned', Unknown => 1 },
79             0x6c => { Name => 'Void', NoSave => 1, Unknown => 1 },
80             #
81             # Signature
82             #
83             0xb538667 => {
84             Name => 'SignatureSlot',
85             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
86             },
87             0x3e8a => { Name => 'SignatureAlgo', Format => 'unsigned' },
88             0x3e9a => { Name => 'SignatureHash', Format => 'unsigned' },
89             0x3ea5 => { Name =>'SignaturePublicKey',Binary => 1, Unknown => 1 },
90             0x3eb5 => { Name => 'Signature', Binary => 1, Unknown => 1 },
91             0x3e5b => {
92             Name => 'SignatureElements',
93             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
94             },
95             0x3e7b => {
96             Name => 'SignatureElementList',
97             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
98             },
99             0x2532 => { Name => 'SignedElement', Binary => 1, Unknown => 1 },
100             #
101             # Segment
102             #
103             0x8538067 => {
104             Name => 'SegmentHeader',
105             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
106             },
107             0x14d9b74 => {
108             Name => 'SeekHead',
109             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
110             },
111             0xdbb => {
112             Name => 'Seek',
113             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
114             },
115             0x13ab => { Name => 'SeekID', Binary => 1, Unknown => 1 },
116             0x13ac => { Name => 'SeekPosition', Format => 'unsigned', Unknown => 1 },
117             #
118             # Segment Info
119             #
120             0x549a966 => {
121             Name => 'Info',
122             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
123             },
124             0x33a4 => { Name => 'SegmentUID', %uidInfo, Unknown => 1 },
125             0x3384 => { Name => 'SegmentFileName', Format => 'utf8' },
126             0x1cb923 => { Name => 'PrevUID', %uidInfo, Unknown => 1 },
127             0x1c83ab => { Name => 'PrevFileName', Format => 'utf8' },
128             0x1eb923 => { Name => 'NextUID', %uidInfo, Unknown => 1 },
129             0x1e83bb => { Name => 'NextFileName', Format => 'utf8' },
130             0x0444 => { Name => 'SegmentFamily', Binary => 1, Unknown => 1 },
131             0x2924 => {
132             Name => 'ChapterTranslate',
133             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
134             },
135             0x29fc => { Name => 'ChapterTranslateEditionUID', %uidInfo, Unknown => 1 },
136             0x29bf => {
137             Name => 'ChapterTranslateCodec',
138             Format => 'unsigned',
139             PrintConv => { 0 => 'Matroska Script', 1 => 'DVD Menu' },
140             },
141             0x29a5 => { Name => 'ChapterTranslateID',Binary => 1, Unknown => 1 },
142             0xad7b1 => {
143             Name => 'TimecodeScale',
144             Format => 'unsigned',
145             RawConv => '$$self{TimecodeScale} = $val',
146             ValueConv => '$val / 1e9',
147             PrintConv => '($val * 1000) . " ms"',
148             },
149             0x489 => {
150             Name => 'Duration',
151             Format => 'float',
152             ValueConv => '$$self{TimecodeScale} ? $val * $$self{TimecodeScale} / 1e9 : $val',
153             PrintConv => '$$self{TimecodeScale} ? ConvertDuration($val) : $val',
154             },
155             0x461 => {
156             Name => 'DateTimeOriginal', # called "DateUTC" by the spec
157             Description => 'Date/Time Original',
158             Groups => { 2 => 'Time' },
159             Format => 'date',
160             PrintConv => '$self->ConvertDateTime($val)',
161             },
162             0x3ba9 => { Name => 'Title', Format => 'utf8' },
163             0xd80 => { Name => 'MuxingApp', Format => 'utf8' },
164             0x1741 => { Name => 'WritingApp', Format => 'utf8' },
165             #
166             # Cluster
167             #
168             0xf43b675 => {
169             Name => 'Cluster',
170             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
171             },
172             0x67 => {
173             Name => 'TimeCode',
174             Format => 'unsigned',
175             Unknown => 1,
176             ValueConv => '$$self{TimecodeScale} ? $val * $$self{TimecodeScale} / 1e9 : $val',
177             PrintConv => '$$self{TimecodeScale} ? ConvertDuration($val) : $val',
178             },
179             0x1854 => {
180             Name => 'SilentTracks',
181             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
182             },
183             0x18d7 => { Name => 'SilentTrackNumber',Format => 'unsigned' },
184             0x27 => { Name => 'Position', Format => 'unsigned' },
185             0x2b => { Name => 'PrevSize', Format => 'unsigned' },
186             0x23 => { Name => 'SimpleBlock', NoSave => 1, Unknown => 1 },
187             0x20 => {
188             Name => 'BlockGroup',
189             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
190             },
191             0x21 => { Name => 'Block', NoSave => 1, Unknown => 1 },
192             0x22 => { Name => 'BlockVirtual', NoSave => 1, Unknown => 1 },
193             0x35a1 => {
194             Name => 'BlockAdditions',
195             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
196             },
197             0x26 => {
198             Name => 'BlockMore',
199             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
200             },
201             0x6e => { Name => 'BlockAddID', Format => 'unsigned', Unknown => 1 },
202             0x25 => { Name => 'BlockAdditional', NoSave => 1, Unknown => 1 },
203             0x1b => {
204             Name => 'BlockDuration',
205             Format => 'unsigned',
206             Unknown => 1,
207             ValueConv => '$$self{TimecodeScale} ? $val * $$self{TimecodeScale} / 1e9 : $val',
208             PrintConv => '$$self{TimecodeScale} ? "$val s" : $val',
209             },
210             0x7a => { Name => 'ReferencePriority',Format => 'unsigned', Unknown => 1 },
211             0x7b => {
212             Name => 'ReferenceBlock',
213             Format => 'signed',
214             Unknown => 1,
215             ValueConv => '$$self{TimecodeScale} ? $val * $$self{TimecodeScale} / 1e9 : $val',
216             PrintConv => '$$self{TimecodeScale} ? "$val s" : $val',
217             },
218             0x7d => { Name => 'ReferenceVirtual', Format => 'signed', Unknown => 1 },
219             0x24 => { Name => 'CodecState', Binary => 1, Unknown => 1 },
220             0x0e => {
221             Name => 'Slices',
222             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
223             },
224             0x68 => {
225             Name => 'TimeSlice',
226             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
227             },
228             0x4c => { Name => 'LaceNumber', Format => 'unsigned', Unknown => 1 },
229             0x4d => { Name => 'FrameNumber', Format => 'unsigned', Unknown => 1 },
230             0x4b => { Name => 'BlockAdditionalID',Format => 'unsigned', Unknown => 1 },
231             0x4e => { Name => 'Delay', Format => 'unsigned', Unknown => 1 },
232             0x4f => { Name => 'ClusterDuration', Format => 'unsigned', Unknown => 1 },
233             0x2f => { Name => 'EncryptedBlock', NoSave => 1, Unknown => 1 },
234             #
235             # Tracks
236             #
237             0x654ae6b => {
238             Name => 'Tracks',
239             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
240             },
241             0x2e => {
242             Name => 'TrackEntry',
243             # reset TrackType member at the start of each track
244             Condition => 'delete $$self{TrackType}; 1',
245             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
246             },
247             0x57 => { Name => 'TrackNumber', Format => 'unsigned' },
248             0x33c5 => { Name => 'TrackUID', %uidInfo },
249             0x03 => {
250             Name => 'TrackType',
251             Format => 'unsigned',
252             PrintHex => 1,
253             # remember types of all tracks encountered, as well as the current track type
254             RawConv => '$$self{TrackTypes}{$val} = 1; $$self{TrackType} = $val',
255             PrintConv => {
256             0x01 => 'Video',
257             0x02 => 'Audio',
258             0x03 => 'Complex', # (audio+video)
259             0x10 => 'Logo',
260             0x11 => 'Subtitle',
261             0x12 => 'Buttons',
262             0x20 => 'Control',
263             },
264             },
265             0x39 => { Name => 'TrackUsed', Format => 'unsigned', PrintConv => \%noYes },
266             0x08 => { Name => 'TrackDefault', Format => 'unsigned', PrintConv => \%noYes },
267             0x15aa => { Name => 'TrackForced', Format => 'unsigned', PrintConv => \%noYes },
268             0x1c => {
269             Name => 'TrackLacing',
270             Format => 'unsigned',
271             Unknown => 1,
272             PrintConv => \%noYes,
273             },
274             0x2de7 => { Name => 'MinCache', Format => 'unsigned', Unknown => 1 },
275             0x2df8 => { Name => 'MaxCache', Format => 'unsigned', Unknown => 1 },
276             0x3e383 => [
277             {
278             Name => 'VideoFrameRate',
279             Condition => '$$self{TrackType} and $$self{TrackType} == 0x01',
280             Format => 'unsigned',
281             ValueConv => '$val ? 1e9 / $val : 0',
282             PrintConv => 'int($val * 1000 + 0.5) / 1000',
283             },{
284             Name => 'DefaultDuration',
285             Format => 'unsigned',
286             ValueConv => '$val / 1e9',
287             PrintConv => '($val * 1000) . " ms"',
288             }
289             ],
290             0x3314f => { Name => 'TrackTimecodeScale',Format => 'float' },
291             0x137f => { Name => 'TrackOffset', Format => 'signed', Unknown => 1 },
292             0x15ee => { Name => 'MaxBlockAdditionID',Format => 'unsigned', Unknown => 1 },
293             0x136e => { Name => 'TrackName', Format => 'utf8' },
294             0x2b59c => { Name => 'TrackLanguage', Format => 'string' },
295             0x2b59d => { Name => 'TrackLanguageIETF', Format => 'string' },
296             0x06 => [
297             {
298             Name => 'VideoCodecID',
299             Condition => '$$self{TrackType} and $$self{TrackType} == 0x01',
300             Format => 'string',
301             },{
302             Name => 'AudioCodecID',
303             Condition => '$$self{TrackType} and $$self{TrackType} == 0x02',
304             Format => 'string',
305             },{
306             Name => 'CodecID',
307             Format => 'string',
308             }
309             ],
310             0x23a2 => { Name => 'CodecPrivate', Binary => 1, Unknown => 1 },
311             0x58688 => [
312             {
313             Name => 'VideoCodecName',
314             Condition => '$$self{TrackType} and $$self{TrackType} == 0x01',
315             Format => 'utf8',
316             },{
317             Name => 'AudioCodecName',
318             Condition => '$$self{TrackType} and $$self{TrackType} == 0x02',
319             Format => 'utf8',
320             },{
321             Name => 'CodecName',
322             Format => 'utf8',
323             }
324             ],
325             0x3446 => { Name => 'TrackAttachmentUID',%uidInfo },
326             0x1a9697=>{ Name => 'CodecSettings', Format => 'utf8' },
327             0x1b4040=>{ Name => 'CodecInfoURL', Format => 'string' },
328             0x6b240 =>{ Name => 'CodecDownloadURL', Format => 'string' },
329             0x2a => { Name => 'CodecDecodeAll', Format => 'unsigned', PrintConv => \%noYes },
330             0x2fab => { Name => 'TrackOverlay', Format => 'unsigned', Unknown => 1 },
331             0x2624 => {
332             Name => 'TrackTranslate',
333             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
334             },
335             0x26fc => { Name => 'TrackTranslateEditionUID', %uidInfo, Unknown => 1 },
336             0x26bf => {
337             Name => 'TrackTranslateCodec',
338             Format => 'unsigned',
339             PrintConv => { 0 => 'Matroska Script', 1 => 'DVD Menu' },
340             },
341             0x26a5 => { Name => 'TrackTranslateTrackID', Binary => 1, Unknown => 1 },
342             #
343             # Video
344             #
345             0x60 => {
346             Name => 'Video',
347             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
348             },
349             0x1a => {
350             Name => 'VideoScanType',
351             Format => 'unsigned',
352             PrintConv => {
353             0 => 'Progressive',
354             1 => 'Interlaced',
355             },
356             },
357             0x13b8 => {
358             Name => 'Stereo3DMode',
359             Format => 'unsigned',
360             Printconv => {
361             0 => 'Mono',
362             1 => 'Right Eye',
363             2 => 'Left Eye',
364             3 => 'Both Eyes',
365             },
366             },
367             0x30 => { Name => 'ImageWidth', Format => 'unsigned' },
368             0x3a => { Name => 'ImageHeight', Format => 'unsigned' },
369             0x14aa => { Name => 'CropBottom', Format => 'unsigned' },
370             0x14bb => { Name => 'CropTop', Format => 'unsigned' },
371             0x14cc => { Name => 'CropLeft', Format => 'unsigned' },
372             0x14dd => { Name => 'CropRight', Format => 'unsigned' },
373             0x14b0 => { Name => 'DisplayWidth', Format => 'unsigned' },
374             0x14ba => { Name => 'DisplayHeight', Format => 'unsigned' },
375             0x14b2 => {
376             Name => 'DisplayUnit',
377             Format => 'unsigned',
378             PrintConv => {
379             0 => 'Pixels',
380             1 => 'cm',
381             2 => 'inches',
382             3 => 'Display Aspect Ratio',
383             4 => 'Unknown',
384             },
385             },
386             0x14b3 => {
387             Name => 'AspectRatioType',
388             Format => 'unsigned',
389             PrintConv => {
390             0 => 'Free Resizing',
391             1 => 'Keep Aspect Ratio',
392             2 => 'Fixed',
393             },
394             },
395             0xeb524 => { Name => 'ColorSpace', Binary => 1, Unknown => 1 },
396             0xfb523 => { Name => 'Gamma', Format => 'float' },
397             0x383e3 => { Name => 'FrameRate', Format => 'float' },
398             #
399             # Audio
400             #
401             0x61 => {
402             Name => 'Audio',
403             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
404             },
405             0x35 => { Name => 'AudioSampleRate', Format => 'float', Groups => { 2 => 'Audio' } },
406             0x38b5 => { Name => 'OutputAudioSampleRate',Format => 'float', Groups => { 2 => 'Audio' } },
407             0x1f => { Name => 'AudioChannels', Format => 'unsigned', Groups => { 2 => 'Audio' } },
408             0x3d7b => {
409             Name => 'ChannelPositions',
410             Binary => 1,
411             Unknown => 1,
412             Groups => { 2 => 'Audio' },
413             },
414             0x2264 => { Name => 'AudioBitsPerSample', Format => 'unsigned', Groups => { 2 => 'Audio' } },
415             #
416             # Content Encoding
417             #
418             0x2d80 => {
419             Name => 'ContentEncodings',
420             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
421             },
422             0x2240 => {
423             Name => 'ContentEncoding',
424             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
425             },
426             0x1031 => { Name => 'ContentEncodingOrder', Format => 'unsigned', Unknown => 1 },
427             0x1032 => { Name => 'ContentEncodingScope', Format => 'unsigned', Unknown => 1 },
428             0x1033 => {
429             Name => 'ContentEncodingType',
430             Format => 'unsigned',
431             PrintConv => { 0 => 'Compression', 1 => 'Encryption' },
432             },
433             0x1034 => {
434             Name => 'ContentCompression',
435             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
436             },
437             0x254 => {
438             Name => 'ContentCompressionAlgorithm',
439             Format => 'unsigned',
440             PrintConv => {
441             0 => 'zlib',
442             1 => 'bzlib',
443             2 => 'lzo1x',
444             3 => 'Header Stripping',
445             },
446             },
447             0x255 => { Name => 'ContentCompressionSettings',Binary => 1, Unknown => 1 },
448             0x1035 => {
449             Name => 'ContentEncryption',
450             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
451             },
452             0x7e1 => {
453             Name => 'ContentEncryptionAlgorithm',
454             Format => 'unsigned',
455             PrintConv => {
456             0 => 'Not Encrypted',
457             1 => 'DES',
458             2 => '3DES',
459             3 => 'Twofish',
460             4 => 'Blowfish',
461             5 => 'AES',
462             },
463             },
464             0x7e2 => { Name => 'ContentEncryptionKeyID',Binary => 1, Unknown => 1 },
465             0x7e3 => { Name => 'ContentSignature', Binary => 1, Unknown => 1 },
466             0x7e4 => { Name => 'ContentSignatureKeyID', Binary => 1, Unknown => 1 },
467             0x7e5 => {
468             Name => 'ContentSignatureAlgorithm',
469             Format => 'unsigned',
470             PrintConv => {
471             0 => 'Not Signed',
472             1 => 'RSA',
473             },
474             },
475             0x7e6 => {
476             Name => 'ContentSignatureHashAlgorithm',
477             Format => 'unsigned',
478             PrintConv => {
479             0 => 'Not Signed',
480             1 => 'SHA1-160',
481             2 => 'MD5',
482             },
483             },
484             #
485             # Cues
486             #
487             0xc53bb6b => {
488             Name => 'Cues',
489             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
490             },
491             0x3b => {
492             Name => 'CuePoint',
493             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
494             },
495             0x33 => {
496             Name => 'CueTime',
497             Format => 'unsigned',
498             Unknown => 1,
499             ValueConv => '$$self{TimecodeScale} ? $val * $$self{TimecodeScale} / 1e9 : $val',
500             PrintConv => '$$self{TimecodeScale} ? ConvertDuration($val) : $val',
501             },
502             0x37 => {
503             Name => 'CueTrackPositions',
504             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
505             },
506             0x77 => { Name => 'CueTrack', Format => 'unsigned', Unknown => 1 },
507             0x71 => { Name => 'CueClusterPosition',Format => 'unsigned', Unknown => 1 },
508             0x1378 => { Name => 'CueBlockNumber', Format => 'unsigned', Unknown => 1 },
509             0x6a => { Name => 'CueCodecState', Format => 'unsigned', Unknown => 1 },
510             0x5b => {
511             Name => 'CueReference',
512             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
513             },
514             0x16 => {
515             Name => 'CueRefTime',
516             Format => 'unsigned',
517             Unknown => 1,
518             ValueConv => '$$self{TimecodeScale} ? $val * $$self{TimecodeScale} / 1e9 : $val',
519             PrintConv => '$$self{TimecodeScale} ? ConvertDuration($val) : $val',
520             },
521             0x17 => { Name => 'CueRefCluster', Format => 'unsigned', Unknown => 1 },
522             0x135f=> { Name => 'CueRefNumber', Format => 'unsigned', Unknown => 1 },
523             0x6b => { Name => 'CueRefCodecState', Format => 'unsigned', Unknown => 1 },
524             #
525             # Attachments
526             #
527             0x941a469 => {
528             Name => 'Attachments',
529             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
530             },
531             0x21a7 => {
532             Name => 'AttachedFile',
533             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
534             },
535             0x67e => { Name => 'AttachedFileDescription',Format => 'utf8' },
536             0x66e => { Name => 'AttachedFileName', Format => 'utf8' },
537             0x660 => { Name => 'AttachedFileMIMEType', Format => 'string' },
538             0x65c => { Name => 'AttachedFileData', Binary => 1 },
539             0x6ae => { Name => 'AttachedFileUID', %uidInfo },
540             0x675 => { Name => 'AttachedFileReferral', Binary => 1, Unknown => 1 },
541             #
542             # Chapters
543             #
544             0x43a770 => {
545             Name => 'Chapters',
546             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
547             },
548             0x5b9 => {
549             Name => 'EditionEntry',
550             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
551             },
552             0x5bc => { Name => 'EditionUID', %uidInfo, Unknown => 1 },
553             0x5bd => { Name => 'EditionFlagHidden', Format => 'unsigned', Unknown => 1 },
554             0x5db => { Name => 'EditionFlagDefault',Format => 'unsigned', Unknown => 1 },
555             0x5dd => { Name => 'EditionFlagOrdered',Format => 'unsigned', Unknown => 1 },
556             0x36 => {
557             Name => 'ChapterAtom',
558             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
559             },
560             0x33c4 => { Name => 'ChapterUID', %uidInfo, Unknown => 1 },
561             0x11 => {
562             Name => 'ChapterTimeStart',
563             Groups => { 1 => 'Chapter#' },
564             Format => 'unsigned',
565             ValueConv => '$val / 1e9',
566             PrintConv => 'ConvertDuration($val)',
567             },
568             0x12 => {
569             Name => 'ChapterTimeEnd',
570             Format => 'unsigned',
571             ValueConv => '$val / 1e9',
572             PrintConv => 'ConvertDuration($val)',
573             },
574             0x18 => { Name => 'ChapterFlagHidden', Format => 'unsigned', Unknown => 1 },
575             0x598 => { Name => 'ChapterFlagEnabled',Format => 'unsigned', Unknown => 1 },
576             0x2e67=> { Name => 'ChapterSegmentUID', %uidInfo, Unknown => 1 },
577             0x2ebc=> { Name => 'ChapterSegmentEditionUID', %uidInfo, Unknown => 1 },
578             0x23c3 => {
579             Name => 'ChapterPhysicalEquivalent',
580             Format => 'unsigned',
581             PrintConv => {
582             10 => 'Index',
583             20 => 'Track',
584             30 => 'Session',
585             40 => 'Layer',
586             50 => 'Side',
587             60 => 'CD / DVD',
588             70 => 'Set / Package',
589             },
590             },
591             0x0f => {
592             Name => 'ChapterTrack',
593             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
594             },
595             0x09 => { Name => 'ChapterTrackNumber', Format => 'unsigned', Unknown => 1 },
596             0x00 => {
597             Name => 'ChapterDisplay',
598             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
599             },
600             0x05 => { Name => 'ChapterString', Format => 'utf8' },
601             0x37c => { Name => 'ChapterLanguage', Format => 'string' },
602             0x37e => { Name => 'ChapterCountry', Format => 'string' },
603             0x2944 => {
604             Name => 'ChapterProcess',
605             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
606             },
607             0x2955 => {
608             Name => 'ChapterProcessCodecID',
609             Format => 'unsigned',
610             Unknown => 1,
611             PrintConv => { 0 => 'Matroska', 1 => 'DVD' },
612             },
613             0x50d => { Name => 'ChapterProcessPrivate', Binary => 1, Unknown => 1 },
614             0x2911 => {
615             Name => 'ChapterProcessCommand',
616             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
617             },
618             0x2922 => {
619             Name => 'ChapterProcessTime',
620             Format => 'unsigned',
621             Unknown => 1,
622             PrintConv => {
623             0 => 'For Duration of Chapter',
624             1 => 'Before Chapter',
625             2 => 'After Chapter',
626             },
627             },
628             0x2933 => { Name => 'ChapterProcessData', Binary => 1, Unknown => 1 },
629             #
630             # Tags
631             #
632             0x254c367 => {
633             Name => 'Tags',
634             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
635             },
636             0x3373 => {
637             Name => 'Tag',
638             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
639             },
640             0x23c0 => {
641             Name => 'Targets',
642             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
643             },
644             # Targets elements
645             0x28ca => { Name => 'TargetTypeValue', Format => 'unsigned' },
646             0x23ca => { Name => 'TargetType', Format => 'string' },
647             0x23c5 => { Name => 'TagTrackUID', %uidInfo },
648             0x23c9 => { Name => 'TagEditionUID', %uidInfo },
649             0x23c4 => { Name => 'TagChapterUID', %uidInfo },
650             0x23c6 => { Name => 'TagAttachmentUID', %uidInfo },
651             0x27c8 => {
652             Name => 'SimpleTag',
653             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Main' },
654             },
655             # SimpleTag elements
656             0x5a3 => { Name => 'TagName', Format => 'utf8' },
657             0x47a => { Name => 'TagLanguage', Format => 'string' },
658             0x47a => { Name => 'TagLanguageBCP47', Format => 'string' },
659             0x484 => { Name => 'TagDefault', Format => 'unsigned', PrintConv => \%noYes },
660             0x487 => { Name => 'TagString', Format => 'utf8' },
661             0x485 => { Name => 'TagBinary', Binary => 1 },
662             #
663             # Spherical Video V2 (untested)
664             #
665             0x7670 => {
666             Name => 'Projection',
667             SubDirectory => { TagTable => 'Image::ExifTool::Matroska::Projection' },
668             },
669             );
670              
671             # Spherical video v2 projection tags (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
672             %Image::ExifTool::Matroska::Projection = (
673             GROUPS => { 2 => 'Video' },
674             VARS => { NO_LOOKUP => 1 }, # omit tags from lookup
675             NOTES => q{
676             Projection tags defined by the Spherical Video V2 specification. See
677             L
678             for the specification.
679             },
680             0x7671 => {
681             Name => 'ProjectionType',
682             Format => 'unsigned',
683             DataMember => 'ProjectionType',
684             RawConv => '$$self{ProjectionType} = $val',
685             PrintConv => {
686             0 => 'Rectangular',
687             1 => 'Equirectangular',
688             2 => 'Cubemap',
689             3 => 'Mesh',
690             },
691             },
692             # ProjectionPrivate in the spec
693             0x7672 => [{
694             Name => 'EquirectangularProj',
695             Condition => '$$self{ProjectionType} == 1',
696             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::equi' },
697             },{
698             Name => 'CubemapProj',
699             Condition => '$$self{ProjectionType} == 2',
700             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::cbmp' },
701             },{ # (don't decode 3 because it is a PITA)
702             Name => 'ProjectionPrivate',
703             Binary => 1,
704             }],
705             0x7673 => { Name => 'ProjectionPoseYaw', Format => 'float' },
706             0x7674 => { Name => 'ProjectionPosePitch', Format => 'float' },
707             0x7675 => { Name => 'ProjectionPoseRoll', Format => 'float' },
708             );
709              
710             # standardized tag names (ref 2)
711             %Image::ExifTool::Matroska::StdTag = (
712             GROUPS => { 2 => 'Video' },
713             VARS => { LONG_TAGS => 1 },
714             NOTES => q{
715             Standardized Matroska tags, stored in a SimpleTag structure (see
716             L).
717             },
718             ORIGINAL => 'Original', # struct
719             SAMPLE => 'Sample', # struct
720             COUNTRY => 'Country', # struct (should deal with this properly!)
721             TOTAL_PARTS => 'TotalParts',
722             PART_NUMBER => 'PartNumber',
723             PART_OFFSET => 'PartOffset',
724             TITLE => 'Title',
725             SUBTITLE => 'Subtitle',
726             URL => 'URL', # nested
727             SORT_WITH => 'SortWith', # nested
728             INSTRUMENTS => 'Instruments', # nested
729             EMAIL => 'Email', # nested
730             ADDRESS => 'Address', # nested
731             FAX => 'FAX', # nested
732             PHONE => 'Phone', # nested
733             ARTIST => 'Artist',
734             LEAD_PERFORMER => 'LeadPerformer',
735             ACCOMPANIMENT => 'Accompaniment',
736             COMPOSER => 'Composer',
737             ARRANGER => 'Arranger',
738             LYRICS => 'Lyrics',
739             LYRICIST => 'Lyricist',
740             CONDUCTOR => 'Conductor',
741             DIRECTOR => 'Director',
742             ASSISTANT_DIRECTOR => 'AssistantDirector',
743             DIRECTOR_OF_PHOTOGRAPHY => 'DirectorOfPhotography',
744             SOUND_ENGINEER => 'SoundEngineer',
745             ART_DIRECTOR => 'ArtDirector',
746             PRODUCTION_DESIGNER => 'ProductionDesigner',
747             CHOREGRAPHER => 'Choregrapher',
748             COSTUME_DESIGNER => 'CostumeDesigner',
749             ACTOR => 'Actor',
750             CHARACTER => 'Character',
751             WRITTEN_BY => 'WrittenBy',
752             SCREENPLAY_BY => 'ScreenplayBy',
753             EDITED_BY => 'EditedBy',
754             PRODUCER => 'Producer',
755             COPRODUCER => 'Coproducer',
756             EXECUTIVE_PRODUCER => 'ExecutiveProducer',
757             DISTRIBUTED_BY => 'DistributedBy',
758             MASTERED_BY => 'MasteredBy',
759             ENCODED_BY => 'EncodedBy',
760             MIXED_BY => 'MixedBy',
761             REMIXED_BY => 'RemixedBy',
762             PRODUCTION_STUDIO => 'ProductionStudio',
763             THANKS_TO => 'ThanksTo',
764             PUBLISHER => 'Publisher',
765             LABEL => 'Label',
766             GENRE => 'Genre',
767             MOOD => 'Mood',
768             ORIGINAL_MEDIA_TYPE => 'OriginalMediaType',
769             CONTENT_TYPE => 'ContentType',
770             SUBJECT => 'Subject',
771             DESCRIPTION => 'Description',
772             KEYWORDS => 'Keywords',
773             SUMMARY => 'Summary',
774             SYNOPSIS => 'Synopsis',
775             INITIAL_KEY => 'InitialKey',
776             PERIOD => 'Period',
777             LAW_RATING => 'LawRating',
778             DATE_RELEASED => { Name => 'DateReleased', %dateInfo },
779             DATE_RECORDED => { Name => 'DateTimeOriginal', %dateInfo, Description => 'Date/Time Original' },
780             DATE_ENCODED => { Name => 'DateEncoded', %dateInfo },
781             DATE_TAGGED => { Name => 'DateTagged', %dateInfo },
782             DATE_DIGITIZED => { Name => 'CreateDate', %dateInfo },
783             DATE_WRITTEN => { Name => 'DateWritten', %dateInfo },
784             DATE_PURCHASED => { Name => 'DatePurchased', %dateInfo },
785             RECORDING_LOCATION => 'RecordingLocation',
786             COMPOSITION_LOCATION => 'CompositionLocation',
787             COMPOSER_NATIONALITY => 'ComposerNationality',
788             COMMENT => 'Comment',
789             PLAY_COUNTER => 'PlayCounter',
790             RATING => 'Rating',
791             ENCODER => 'Encoder',
792             ENCODER_SETTINGS => 'EncoderSettings',
793             BPS => 'BPS',
794             FPS => 'FPS',
795             BPM => 'BPM',
796             MEASURE => 'Measure',
797             TUNING => 'Tuning',
798             REPLAYGAIN_GAIN => 'ReplaygainGain',
799             REPLAYGAIN_PEAK => 'ReplaygainPeak',
800             ISRC => 'ISRC',
801             MCDI => 'MCDI',
802             ISBN => 'ISBN',
803             BARCODE => 'Barcode',
804             CATALOG_NUMBER => 'CatalogNumber',
805             LABEL_CODE => 'LabelCode',
806             LCCN => 'Lccn',
807             IMDB => 'IMDB',
808             TMDB => 'TMDB',
809             TVDB => 'TVDB',
810             PURCHASE_ITEM => 'PurchaseItem',
811             PURCHASE_INFO => 'PurchaseInfo',
812             PURCHASE_OWNER => 'PurchaseOwner',
813             PURCHASE_PRICE => 'PurchasePrice',
814             PURCHASE_CURRENCY => 'PurchaseCurrency',
815             COPYRIGHT => 'Copyright',
816             PRODUCTION_COPYRIGHT => 'ProductionCopyright',
817             LICENSE => 'License',
818             TERMS_OF_USE => 'TermsOfUse',
819             );
820              
821             #------------------------------------------------------------------------------
822             # Handle MKV SimpleTag structure
823             # Inputs: 0) ExifTool ref, 1) structure ref, 2) parent tag ID, 3) parent tag Name,
824             # 4) language code, 5) country code
825             sub HandleStruct($$;$$$$)
826             {
827 0     0 0 0 local $_;
828 0         0 my ($et, $struct, $pid, $pname, $lang, $ctry) = @_;
829 0         0 my $tagTbl = GetTagTable('Image::ExifTool::Matroska::StdTag');
830 0         0 my $tag = $$struct{TagName};
831 0         0 my $tagInfo = $$tagTbl{$tag};
832             # create tag if necessary
833 0 0       0 unless (ref $tagInfo eq 'HASH') {
834 0         0 my $name = ucfirst lc $tag;
835 0         0 $name =~ tr/0-9a-zA-Z_//dc;
836 0         0 $name =~ s/_([a-z])/\U$1/g;
837 0 0       0 $name = "Tag_$name" if length $name < 2;
838 0         0 $tagInfo = AddTagToTable($tagTbl, $tag, { Name => $name });
839             }
840 0         0 my ($id, $nm);
841 0 0       0 if ($pid) {
842 0         0 $id = "$pid/$tag";
843 0         0 $nm = "$pname/$$tagInfo{Name}";
844 0 0       0 unless ($$tagTbl{$id}) {
845 0         0 my %copy = %$tagInfo;
846 0         0 $copy{Name} = $nm;
847 0         0 $tagInfo = AddTagToTable($tagTbl, $id, \%copy);
848             }
849             } else {
850 0         0 ($id, $nm) = ($tag, $$tagInfo{Name});
851             }
852 0 0 0     0 if (defined $$struct{TagString} or defined $$struct{TagBinary}) {
853 0 0       0 my $val = defined $$struct{TagString} ? $$struct{TagString} : \$$struct{TagBinary};
854 0   0     0 $lang = $$struct{TagLanguageBCP47} || $$struct{TagLanguage} || $lang;
855             # (Note: not currently handling TagDefault attribute)
856 0         0 my $code = $lang;
857 0 0       0 $code = $lang ? "${lang}-${ctry}" : "eng-${ctry}" if $ctry; # ('eng' is default lang)
    0          
858 0 0       0 if ($code) {
859 0         0 $tagInfo = Image::ExifTool::GetLangInfo($tagInfo, $code);
860 0         0 $et->HandleTag($tagTbl, $$tagInfo{TagID}, $val);
861             } else {
862 0         0 $et->HandleTag($tagTbl, $id, $val);
863             }
864             # COUNTRY is handled as an attribute for contained tags
865 0 0       0 if ($tag eq 'COUNTRY') {
866 0         0 $ctry = $val;
867 0         0 ($id, $nm) = ($pid, $pname);
868             }
869             }
870 0 0       0 if ($$struct{struct}) {
871             # step into each contained structure
872 0         0 HandleStruct($et, $_, $id, $nm, $lang, $ctry) foreach @{$$struct{struct}};
  0         0  
873             }
874             }
875              
876             #------------------------------------------------------------------------------
877             # Get variable-length Matroska integer
878             # Inputs: 0) data buffer, 1) position in data
879             # Returns: integer value and updates position, -1 for unknown/reserved value,
880             # or undef if no data left
881             sub GetVInt($$)
882             {
883 132 100   132 0 253 return undef if $_[1] >= length $_[0];
884 131         246 my $val = ord(substr($_[0], $_[1]++));
885 131         179 my $num = 0;
886 131 50       208 unless ($val) {
887 0 0       0 return undef if $_[1] >= length $_[0];
888 0         0 $val = ord(substr($_[0], $_[1]++));
889 0 0       0 return undef unless $val; # can't be this large!
890 0         0 $num += 7; # 7 more bytes to read (we just read one)
891             }
892 131         178 my $mask = 0x7f;
893 131         263 while ($val == ($val & $mask)) {
894 67         87 $mask >>= 1;
895 67         124 ++$num;
896             }
897 131         203 $val = ($val & $mask);
898 131         173 my $unknown = ($val == $mask);
899 131 50       254 return undef if $_[1] + $num > length $_[0];
900 131         256 while ($num) {
901 67         110 my $b = ord(substr($_[0], $_[1]++));
902 67 50       124 $unknown = 0 if $b != 0xff;
903 67         91 $val = $val * 256 + $b;
904 67         120 --$num;
905             }
906 131 50       241 return $unknown ? -1 : $val;
907             }
908              
909             #------------------------------------------------------------------------------
910             # Read information from a Matroska multimedia file (MKV, MKA, MKS)
911             # Inputs: 0) ExifTool object reference, 1) Directory information reference
912             # Returns: 1 on success, 0 if this wasn't a valid Matroska file
913             sub ProcessMKV($$)
914             {
915 1     1 0 6 my ($et, $dirInfo) = @_;
916 1         2 my $raf = $$dirInfo{RAF};
917 1         3 my ($buff, $buf2, @dirEnd, $trackIndent, %trackTypes, $struct);
918              
919 1 50       4 $raf->Read($buff, 4) == 4 or return 0;
920 1 50       25 return 0 unless $buff =~ /^\x1a\x45\xdf\xa3/;
921              
922             # read in 64kB blocks (already read 4 bytes)
923 1 50       4 $raf->Read($buff, 65532) or return 0;
924 1         3 my $dataLen = length $buff;
925 1         3 my ($pos, $dataPos) = (0, 4);
926              
927             # verify header length
928 1         3 my $hlen = GetVInt($buff, $pos);
929 1 50 33     7 return 0 unless $hlen and $hlen > 0;
930 1 50       4 $pos + $hlen > $dataLen and $et->Warn('Truncated Matroska header'), return 1;
931 1         4 $et->SetFileType();
932 1         5 SetByteOrder('MM');
933 1         4 my $tagTablePtr = GetTagTable('Image::ExifTool::Matroska::Main');
934              
935             # set flag to process entire file (otherwise we stop at the first Cluster)
936 1         6 my $verbose = $et->Options('Verbose');
937 1 50 33     6 my $processAll = ($verbose or $et->Options('Unknown') > 1) ? 2 : 0;
938 1 50       8 ++$processAll if $et->Options('ExtractEmbedded');
939 1         3 $$et{TrackTypes} = \%trackTypes; # store Track types reference
940 1         3 my $oldIndent = $$et{INDENT};
941 1         3 my $chapterNum = 0;
942 1         2 my $dirName = 'MKV';
943              
944             # loop over all Matroska elements
945 1         2 for (;;) {
946 66         142 while (@dirEnd) {
947 73 100       163 if ($pos + $dataPos >= $dirEnd[-1][0]) {
948 11         16 pop @dirEnd;
949 11 50       25 if ($struct) {
950 0 0 0     0 if (@dirEnd and $dirEnd[-1][2]) {
951             # save this nested structure
952 0 0       0 $dirEnd[-1][2]{struct} or $dirEnd[-1][2]{struct} = [ ];
953 0         0 push @{$dirEnd[-1][2]{struct}}, $struct;
  0         0  
954 0         0 $struct = $dirEnd[-1][2];
955             } else {
956             # handle completed structures now
957 0         0 HandleStruct($et, $struct);
958 0         0 undef $struct;
959             }
960             }
961 11 50       27 $dirName = @dirEnd ? $dirEnd[-1][1] : 'MKV';
962             # use INDENT to decide whether or not we are done this Track element
963 11 100 100     50 delete $$et{SET_GROUP1} if $trackIndent and $trackIndent eq $$et{INDENT};
964 11         33 $$et{INDENT} = substr($$et{INDENT}, 0, -2);
965             } else {
966 62         100 $dirName = $dirEnd[-1][1];
967 62         88 last;
968             }
969             }
970             # read more if we are getting close to the end of our buffer
971             # (24 more bytes should be enough to read this element header)
972 66 50 66     166 if ($pos + 24 > $dataLen and $raf->Read($buf2, 65536)) {
973 0         0 $buff = substr($buff, $pos) . $buf2;
974 0         0 undef $buf2;
975 0         0 $dataPos += $pos;
976 0         0 $dataLen = length $buff;
977 0         0 $pos = 0;
978             }
979 66         131 my $tag = GetVInt($buff, $pos);
980 66 100 66     271 last unless defined $tag and $tag >= 0;
981 65         132 my $size = GetVInt($buff, $pos);
982 65 50       119 last unless defined $size;
983 65         82 my $unknownSize;
984 65 50       119 $size < 0 and $unknownSize = 1, $size = 1e20;
985 65 50 66     223 if (@dirEnd and $pos + $dataPos + $size > $dirEnd[-1][0]) {
986 0         0 $et->Warn("Invalid or corrupted $dirEnd[-1][1] master element");
987 0         0 $pos = $dirEnd[-1][0] - $dataPos;
988 0 0 0     0 if ($pos < 0 or $pos > $dataLen) {
989 0         0 $buff = '';
990 0         0 $dataPos += $pos;
991 0         0 $dataLen = 0;
992 0         0 $pos = 0;
993 0 0       0 $raf->Seek($dataPos, 0) or last;
994             }
995 0         0 next;
996             }
997 65         163 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
998             # just fall through into the contained EBML elements
999 65 50       125 if ($tagInfo) {
    0          
1000 65 100       138 if ($$tagInfo{SubDirectory}) {
1001             # stop processing at first cluster unless we are using -v -U or -ee
1002 12 50 33     32 if ($$tagInfo{Name} eq 'Cluster' and $processAll < 2) {
1003 0 0       0 last unless $processAll;
1004 0         0 undef $tagInfo; # just skip the Cluster when -ee is used
1005             } else {
1006 12         25 $$et{INDENT} .= '| ';
1007 12         46 $et->VerboseDir($$tagTablePtr{$tag}{Name}, undef, $size);
1008 12         17 $dirName = $$tagInfo{Name};
1009 12         48 push @dirEnd, [ $pos + $dataPos + $size, $dirName, $struct ];
1010 12 50       27 $struct = { } if $dirName eq 'SimpleTag'; # keep track of SimpleTag elements
1011 12 50       26 if ($$tagInfo{Name} eq 'ChapterAtom') {
1012 0         0 $$et{SET_GROUP1} = 'Chapter' . (++$chapterNum);
1013 0         0 $trackIndent = $$et{INDENT};
1014             }
1015 12         21 next;
1016             }
1017             }
1018             } elsif ($verbose) {
1019 0         0 $et->VPrint(0,sprintf("$$et{INDENT}- Tag 0x%x (Unknown, %d bytes)\n", $tag, $size));
1020             }
1021 53 50       92 last if $unknownSize;
1022 53 50       115 if ($pos + $size > $dataLen) {
1023             # how much more do we need to read?
1024 0         0 my $more = $pos + $size - $dataLen;
1025             # just skip unknown and large data blocks
1026 0 0 0     0 if (not $tagInfo or $more > 10000000) {
1027             # don't try to skip very large blocks unless LargeFileSupport is enabled
1028 0 0 0     0 last if $more >= 0x80000000 and not $et->Options('LargeFileSupport');
1029 0 0       0 $raf->Seek($more, 1) or last;
1030 0         0 $buff = '';
1031 0         0 $dataPos += $dataLen + $more;
1032 0         0 $dataLen = 0;
1033 0         0 $pos = 0;
1034 0         0 next;
1035             } else {
1036             # read data in multiples of 64kB
1037 0         0 $more = (int($more / 65536) + 1) * 65536;
1038 0 0       0 if ($raf->Read($buf2, $more)) {
1039 0         0 $buff = substr($buff, $pos) . $buf2;
1040 0         0 undef $buf2;
1041 0         0 $dataPos += $pos;
1042 0         0 $dataLen = length $buff;
1043 0         0 $pos = 0;
1044             }
1045 0 0       0 last if $pos + $size > $dataLen;
1046             }
1047             }
1048 53 50       100 unless ($tagInfo) {
1049             # ignore the element
1050 0         0 $pos += $size;
1051 0         0 next;
1052             }
1053 53         81 my $val;
1054 53 100       109 if ($$tagInfo{Format}) {
1055 48         124 my $fmt = $$tagInfo{Format};
1056 48 100 100     169 if ($fmt eq 'string' or $fmt eq 'utf8') {
    100          
1057 10         33 ($val = substr($buff, $pos, $size)) =~ s/\0.*//s;
1058 10 100       31 $val = $et->Decode($val, 'UTF8') if $fmt eq 'utf8';
1059             } elsif ($fmt eq 'float') {
1060 4 50       8 if ($size == 4) {
    0          
1061 4         17 $val = GetFloat(\$buff, $pos);
1062             } elsif ($size == 8) {
1063 0         0 $val = GetDouble(\$buff, $pos);
1064             } else {
1065 0         0 $et->Warn("Illegal float size ($size)");
1066             }
1067             } else {
1068 34         123 my @vals = unpack("x${pos}C$size", $buff);
1069 34         57 $val = 0;
1070 34 100 66     107 if ($fmt eq 'signed' or $fmt eq 'date') {
1071 1         3 my $over = 1;
1072 1         9 foreach (@vals) {
1073 8         14 $val = $val * 256 + $_;
1074 8         15 $over *= 256;
1075             }
1076             # interpret negative numbers
1077 1 50       10 $val -= $over if $vals[0] & 0x80;
1078             # convert dates (nanoseconds since 2001:01:01)
1079 1 50       4 if ($fmt eq 'date') {
1080 1         2 my $t = $val / 1e9;
1081 1         4 my $f = $t - int($t); # fractional seconds
1082 1         5 $f =~ s/^\d+//; # remove leading zero
1083             # (8 leap days between 1970 and 2001)
1084 1         2 $t += (((2001-1970)*365+8)*24*3600);
1085 1         8 $val = Image::ExifTool::ConvertUnixTime($t) . $f . 'Z';
1086             }
1087             } else { # must be unsigned
1088 33         97 $val = $val * 256 + $_ foreach @vals;
1089             }
1090             }
1091             # set group1 to Track/Chapter number
1092 48 100       146 if ($$tagInfo{Name} eq 'TrackNumber') {
1093 2         7 $$et{SET_GROUP1} = 'Track' . $val;
1094 2         5 $trackIndent = $$et{INDENT};
1095             }
1096             }
1097 53         176 my %parms = (
1098             DataPt => \$buff,
1099             DataPos => $dataPos,
1100             Start => $pos,
1101             Size => $size,
1102             );
1103 53 50 33     168 if ($$tagInfo{NoSave} or $struct) {
1104 0 0       0 $et->VerboseInfo($tag, $tagInfo, Value => $val, %parms) if $verbose;
1105 0 0       0 $$struct{$$tagInfo{Name}} = $val if $struct;
1106             } else {
1107 53         191 $et->HandleTag($tagTablePtr, $tag, $val, %parms);
1108             }
1109 53         146 $pos += $size; # step to next element
1110             }
1111 1         4 $$et{INDENT} = $oldIndent;
1112 1         7 delete $$et{SET_GROUP1};
1113             # override file type if necessary based on existing track types
1114 1 0 33     6 unless ($trackTypes{0x01} or $trackTypes{0x03}) { # video or complex?
1115 0 0       0 if ($trackTypes{0x02}) { # audio?
    0          
1116 0         0 $et->OverrideFileType('MKA');
1117             } elsif ($trackTypes{0x11}) { # subtitle?
1118 0         0 $et->OverrideFileType('MKS');
1119             }
1120             }
1121 1         4 return 1;
1122             }
1123              
1124             1; # end
1125              
1126             __END__