File Coverage

blib/lib/Image/ExifTool/MPEG.pm
Criterion Covered Total %
statement 42 136 30.8
branch 10 92 10.8
condition 12 41 29.2
subroutine 5 8 62.5
pod 0 5 0.0
total 69 282 24.4


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: MPEG.pm
3             #
4             # Description: Read MPEG-1 and MPEG-2 meta information
5             #
6             # Revisions: 05/11/2006 - P. Harvey Created
7             #
8             # References: 1) http://www.mp3-tech.org/
9             # 2) http://www.getid3.org/
10             # 3) http://dvd.sourceforge.net/dvdinfo/dvdmpeg.html
11             # 4) http://ffmpeg.org/
12             # 5) http://sourceforge.net/projects/mediainfo/
13             #------------------------------------------------------------------------------
14              
15             package Image::ExifTool::MPEG;
16              
17 4     4   530 use strict;
  4         10  
  4         156  
18 4     4   26 use vars qw($VERSION);
  4         8  
  4         169  
19 4     4   28 use Image::ExifTool qw(:DataAccess :Utils);
  4         9  
  4         9507  
20              
21             $VERSION = '1.17';
22              
23             %Image::ExifTool::MPEG::Audio = (
24             GROUPS => { 2 => 'Audio' },
25             'Bit11-12' => {
26             Name => 'MPEGAudioVersion',
27             RawConv => '$self->{MPEG_Vers} = $val',
28             PrintConv => {
29             0 => 2.5,
30             2 => 2,
31             3 => 1,
32             },
33             },
34             'Bit13-14' => {
35             Name => 'AudioLayer',
36             RawConv => '$self->{MPEG_Layer} = $val',
37             PrintConv => {
38             1 => 3,
39             2 => 2,
40             3 => 1,
41             },
42             },
43             # Bit 15 indicates CRC protection
44             'Bit16-19' => [
45             {
46             Name => 'AudioBitrate',
47             Condition => '$self->{MPEG_Vers} == 3 and $self->{MPEG_Layer} == 3',
48             Notes => 'version 1, layer 1',
49             PrintConvColumns => 3,
50             ValueConv => {
51             0 => 'free',
52             1 => 32000,
53             2 => 64000,
54             3 => 96000,
55             4 => 128000,
56             5 => 160000,
57             6 => 192000,
58             7 => 224000,
59             8 => 256000,
60             9 => 288000,
61             10 => 320000,
62             11 => 352000,
63             12 => 384000,
64             13 => 416000,
65             14 => 448000,
66             },
67             PrintConv => 'ConvertBitrate($val)',
68             },
69             {
70             Name => 'AudioBitrate',
71             Condition => '$self->{MPEG_Vers} == 3 and $self->{MPEG_Layer} == 2',
72             Notes => 'version 1, layer 2',
73             PrintConvColumns => 3,
74             ValueConv => {
75             0 => 'free',
76             1 => 32000,
77             2 => 48000,
78             3 => 56000,
79             4 => 64000,
80             5 => 80000,
81             6 => 96000,
82             7 => 112000,
83             8 => 128000,
84             9 => 160000,
85             10 => 192000,
86             11 => 224000,
87             12 => 256000,
88             13 => 320000,
89             14 => 384000,
90             },
91             PrintConv => 'ConvertBitrate($val)',
92             },
93             {
94             Name => 'AudioBitrate',
95             Condition => '$self->{MPEG_Vers} == 3 and $self->{MPEG_Layer} == 1',
96             Notes => 'version 1, layer 3',
97             PrintConvColumns => 3,
98             ValueConv => {
99             0 => 'free',
100             1 => 32000,
101             2 => 40000,
102             3 => 48000,
103             4 => 56000,
104             5 => 64000,
105             6 => 80000,
106             7 => 96000,
107             8 => 112000,
108             9 => 128000,
109             10 => 160000,
110             11 => 192000,
111             12 => 224000,
112             13 => 256000,
113             14 => 320000,
114             },
115             PrintConv => 'ConvertBitrate($val)',
116             },
117             {
118             Name => 'AudioBitrate',
119             Condition => '$self->{MPEG_Vers} != 3 and $self->{MPEG_Layer} == 3',
120             Notes => 'version 2 or 2.5, layer 1',
121             PrintConvColumns => 3,
122             ValueConv => {
123             0 => 'free',
124             1 => 32000,
125             2 => 48000,
126             3 => 56000,
127             4 => 64000,
128             5 => 80000,
129             6 => 96000,
130             7 => 112000,
131             8 => 128000,
132             9 => 144000,
133             10 => 160000,
134             11 => 176000,
135             12 => 192000,
136             13 => 224000,
137             14 => 256000,
138             },
139             PrintConv => 'ConvertBitrate($val)',
140             },
141             {
142             Name => 'AudioBitrate',
143             Condition => '$self->{MPEG_Vers} != 3 and $self->{MPEG_Layer}',
144             Notes => 'version 2 or 2.5, layer 2 or 3',
145             PrintConvColumns => 3,
146             ValueConv => {
147             0 => 'free',
148             1 => 8000,
149             2 => 16000,
150             3 => 24000,
151             4 => 32000,
152             5 => 40000,
153             6 => 48000,
154             7 => 56000,
155             8 => 64000,
156             9 => 80000,
157             10 => 96000,
158             11 => 112000,
159             12 => 128000,
160             13 => 144000,
161             14 => 160000,
162             },
163             PrintConv => 'ConvertBitrate($val)',
164             },
165             ],
166             'Bit20-21' => [
167             {
168             Name => 'SampleRate',
169             Condition => '$self->{MPEG_Vers} == 3',
170             Notes => 'version 1',
171             PrintConv => {
172             0 => 44100,
173             1 => 48000,
174             2 => 32000,
175             },
176             },
177             {
178             Name => 'SampleRate',
179             Condition => '$self->{MPEG_Vers} == 2',
180             Notes => 'version 2',
181             PrintConv => {
182             0 => 22050,
183             1 => 24000,
184             2 => 16000,
185             },
186             },
187             {
188             Name => 'SampleRate',
189             Condition => '$self->{MPEG_Vers} == 0',
190             Notes => 'version 2.5',
191             PrintConv => {
192             0 => 11025,
193             1 => 12000,
194             2 => 8000,
195             },
196             },
197             ],
198             # Bit 22 - padding flag
199             # Bit 23 - private bit
200             'Bit24-25' => {
201             Name => 'ChannelMode',
202             RawConv => '$self->{MPEG_Mode} = $val',
203             PrintConv => {
204             0 => 'Stereo',
205             1 => 'Joint Stereo',
206             2 => 'Dual Channel',
207             3 => 'Single Channel',
208             },
209             },
210             'Bit26' => {
211             Name => 'MSStereo',
212             Condition => '$self->{MPEG_Layer} == 1',
213             Notes => 'layer 3',
214             PrintConv => { 0 => 'Off', 1 => 'On' },
215             },
216             'Bit27' => {
217             Name => 'IntensityStereo',
218             Condition => '$self->{MPEG_Layer} == 1',
219             Notes => 'layer 3',
220             PrintConv => { 0 => 'Off', 1 => 'On' },
221             },
222             'Bit26-27' => {
223             Name => 'ModeExtension',
224             Condition => '$self->{MPEG_Layer} > 1',
225             Notes => 'layer 1 or 2',
226             PrintConv => {
227             0 => 'Bands 4-31',
228             1 => 'Bands 8-31',
229             2 => 'Bands 12-31',
230             3 => 'Bands 16-31',
231             },
232             },
233             'Bit28' => {
234             Name => 'CopyrightFlag',
235             PrintConv => {
236             0 => 'False',
237             1 => 'True',
238             },
239             },
240             'Bit29' => {
241             Name => 'OriginalMedia',
242             PrintConv => {
243             0 => 'False',
244             1 => 'True',
245             },
246             },
247             'Bit30-31' => {
248             Name => 'Emphasis',
249             PrintConv => {
250             0 => 'None',
251             1 => '50/15 ms',
252             2 => 'reserved',
253             3 => 'CCIT J.17',
254             },
255             },
256             );
257              
258             %Image::ExifTool::MPEG::Video = (
259             GROUPS => { 2 => 'Video' },
260             'Bit00-11' => 'ImageWidth',
261             'Bit12-23' => 'ImageHeight',
262             'Bit24-27' => {
263             Name => 'AspectRatio',
264             ValueConv => {
265             1 => 1,
266             2 => 0.6735,
267             3 => 0.7031,
268             4 => 0.7615,
269             5 => 0.8055,
270             6 => 0.8437,
271             7 => 0.8935,
272             8 => 0.9157,
273             9 => 0.9815,
274             10 => 1.0255,
275             11 => 1.0695,
276             12 => 1.0950,
277             13 => 1.1575,
278             14 => 1.2015,
279             },
280             PrintConv => {
281             1 => '1:1',
282             0.6735 => '0.6735',
283             0.7031 => '16:9, 625 line, PAL',
284             0.7615 => '0.7615',
285             0.8055 => '0.8055',
286             0.8437 => '16:9, 525 line, NTSC',
287             0.8935 => '0.8935',
288             0.9157 => '4:3, 625 line, PAL, CCIR601',
289             0.9815 => '0.9815',
290             1.0255 => '1.0255',
291             1.0695 => '1.0695',
292             1.0950 => '4:3, 525 line, NTSC, CCIR601',
293             1.1575 => '1.1575',
294             1.2015 => '1.2015',
295             },
296             },
297             'Bit28-31' => {
298             Name => 'FrameRate',
299             ValueConv => {
300             1 => 23.976,
301             2 => 24,
302             3 => 25,
303             4 => 29.97,
304             5 => 30,
305             6 => 50,
306             7 => 59.94,
307             8 => 60,
308             },
309             PrintConv => '"$val fps"',
310             },
311             'Bit32-49' => {
312             Name => 'VideoBitrate',
313             ValueConv => '$val eq 0x3ffff ? "Variable" : $val * 400',
314             PrintConv => 'ConvertBitrate($val)',
315             },
316             # these tags not very interesting
317             #'Bit50' => 'MarkerBit',
318             #'Bit51-60' => 'VBVBufferSize',
319             #'Bit61' => 'ConstrainedParamFlag',
320             #'Bit62' => 'IntraQuantMatrixFlag',
321             );
322              
323             %Image::ExifTool::MPEG::Xing = (
324             GROUPS => { 2 => 'Audio' },
325             VARS => { NO_ID => 1 },
326             NOTES => 'These tags are extracted from the Xing/Info frame.',
327             1 => { Name => 'VBRFrames' },
328             2 => { Name => 'VBRBytes' },
329             3 => { Name => 'VBRScale' },
330             4 => { Name => 'Encoder' },
331             5 => { Name => 'LameVBRQuality' },
332             6 => { Name => 'LameQuality' },
333             7 => { # (for documentation only)
334             Name => 'LameHeader',
335             SubDirectory => { TagTable => 'Image::ExifTool::MPEG::Lame' },
336             },
337             );
338              
339             # Lame header tags (ref 5)
340             %Image::ExifTool::MPEG::Lame = (
341             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
342             GROUPS => { 2 => 'Audio' },
343             NOTES => 'Tags extracted from Lame 3.90 or later header.',
344             9 => {
345             Name => 'LameMethod',
346             Mask => 0x0f,
347             PrintConv => {
348             1 => 'CBR',
349             2 => 'ABR',
350             3 => 'VBR (old/rh)',
351             4 => 'VBR (new/mtrh)',
352             5 => 'VBR (old/rh)',
353             6 => 'VBR',
354             8 => 'CBR (2-pass)',
355             9 => 'ABR (2-pass)',
356             },
357             },
358             10 => {
359             Name => 'LameLowPassFilter',
360             ValueConv => '$val * 100',
361             PrintConv => '($val / 1000) . " kHz"',
362             },
363             # 19 - EncodingFlags
364             20 => {
365             Name => 'LameBitrate',
366             ValueConv => '$val * 1000',
367             PrintConv => 'ConvertBitrate($val)',
368             },
369             24 => {
370             Name => 'LameStereoMode',
371             Mask => 0x1c,
372             PrintConv => {
373             0 => 'Mono',
374             1 => 'Stereo',
375             2 => 'Dual Channels',
376             3 => 'Joint Stereo',
377             4 => 'Forced Joint Stereo',
378             6 => 'Auto',
379             7 => 'Intensity Stereo',
380             },
381             },
382             );
383              
384             # composite tags
385             %Image::ExifTool::MPEG::Composite = (
386             Duration => {
387             Groups => { 2 => 'Video' },
388             Require => {
389             0 => 'FileSize',
390             },
391             Desire => {
392             1 => 'ID3Size',
393             2 => 'MPEG:AudioBitrate',
394             3 => 'MPEG:VideoBitrate',
395             4 => 'MPEG:VBRFrames',
396             5 => 'MPEG:SampleRate',
397             6 => 'MPEG:MPEGAudioVersion',
398             },
399             Priority => -1, # (don't want to replace any other Duration tag)
400             ValueConv => q{
401             if ($val[4] and defined $val[5] and defined $val[6]) {
402             # calculate from number of VBR audio frames
403             my $mfs = $prt[5] / ($val[6] == 3 ? 144 : 72);
404             # calculate using VBR length
405             return 8 * $val[4] / $mfs;
406             }
407             # calculate duration as file size divided by total bitrate
408             # (note: this is only approximate!)
409             return undef unless $val[2] or $val[3];
410             return undef if $val[2] and not $val[2] =~ /^\d+$/;
411             return undef if $val[3] and not $val[3] =~ /^\d+$/;
412             return (8 * ($val[0] - ($val[1]||0))) / (($val[2]||0) + ($val[3]||0));
413             },
414             PrintConv => 'ConvertDuration($val) . " (approx)"',
415             },
416             AudioBitrate => {
417             Groups => { 2 => 'Audio' },
418             Notes => 'calculated for variable-bitrate MPEG audio',
419             Require => {
420             0 => 'MPEG:MPEGAudioVersion',
421             1 => 'MPEG:SampleRate',
422             2 => 'MPEG:VBRBytes',
423             3 => 'MPEG:VBRFrames',
424             },
425             ValueConv => q{
426             return undef unless $val[3];
427             my $mfs = $prt[1] / ($val[0] == 3 ? 144 : 72);
428             return $mfs * $val[2] / $val[3];
429             },
430             PrintConv => 'ConvertBitrate($val)',
431             },
432             );
433              
434             # add our composite tags
435             Image::ExifTool::AddCompositeTags('Image::ExifTool::MPEG');
436              
437              
438             #------------------------------------------------------------------------------
439             # Process information in an MPEG audio or video frame header
440             # Inputs: 0) ExifTool object ref, 1) tag table ref, 2-N) list of 32-bit data words
441             sub ProcessFrameHeader($$@)
442             {
443 1     1 0 12 my ($et, $tagTablePtr, @data) = @_;
444 1         4 my $tag;
445 1         10 foreach $tag (sort keys %$tagTablePtr) {
446 15 100       68 next unless $tag =~ /^Bit(\d{2})-?(\d{2})?/;
447 11   66     50 my ($b1, $b2) = ($1, $2 || $1);
448 11         42 my $index = int($b1 / 32);
449 11         21 my $word = $data[$index];
450 11         15 my $mask = 0;
451 11         29 foreach (0 .. ($b2 - $b1)) {
452 20         34 $mask += (1 << $_);
453             }
454 11         23 my $val = ($word >> (31 + 32*$index - $b2)) & $mask;
455 11         32 $et->HandleTag($tagTablePtr, $tag, $val);
456             }
457             }
458              
459             #------------------------------------------------------------------------------
460             # Read MPEG audio frame header
461             # Inputs: 0) ExifTool object reference, 1) Reference to audio data
462             # 2) flag set if we are trying to recognized MP3 file only
463             # Returns: 1 on success, 0 if no audio header was found
464             sub ParseMPEGAudio($$;$)
465             {
466 9     9 0 31 my ($et, $buffPt, $mp3) = @_;
467 9         21 my ($word, $pos);
468 9   100     51 my $ext = $$et{FILE_EXT} || '';
469              
470 9         21 for (;;) {
471             # find frame sync
472 9 100       93 return 0 unless $$buffPt =~ m{(\xff.{3})}sg;
473 1         5 $word = unpack('N', $1); # get audio frame header word
474 1 50       4 unless (($word & 0xffe00000) == 0xffe00000) {
475 0         0 pos($$buffPt) = pos($$buffPt) - 2; # next possible location for frame sync
476 0         0 next;
477             }
478             # validate header as much as possible
479 1 50 33     20 if (($word & 0x180000) == 0x080000 or # 01 is a reserved version ID
      33        
      33        
      33        
      33        
      33        
      33        
480             ($word & 0x060000) == 0x000000 or # 00 is a reserved layer description
481             ($word & 0x00f000) == 0x000000 or # 0000 is the "free" bitrate index
482             ($word & 0x00f000) == 0x00f000 or # 1111 is a bad bitrate index
483             ($word & 0x000c00) == 0x000c00 or # 11 is a reserved sampling frequency
484             ($word & 0x000003) == 0x000002 or # 10 is a reserved emphasis
485             (($mp3 and ($word & 0x060000) != 0x020000))) # must be layer 3 for MP3
486             {
487             # give up easily unless this really should be an MP3 file
488 0 0       0 return 0 unless $ext eq 'MP3';
489 0         0 pos($$buffPt) = pos($$buffPt) - 1;
490 0         0 next;
491             }
492 1         3 $pos = pos($$buffPt);
493 1         2 last;
494             }
495             # set file type if not done already
496 1         6 $et->SetFileType();
497              
498 1         4 my $tagTablePtr = GetTagTable('Image::ExifTool::MPEG::Audio');
499 1         4 ProcessFrameHeader($et, $tagTablePtr, $word);
500              
501             # extract the VBR information (ref MP3::Info)
502 1         8 my ($v, $m) = ($$et{MPEG_Vers}, $$et{MPEG_Mode});
503 1   33     8 while (defined $v and defined $m) {
504 1         3 my $len = length $$buffPt;
505 1 50       4 $pos += $v == 3 ? ($m == 3 ? 17 : 32) : ($m == 3 ? 9 : 17);
    0          
    50          
506 1 50       4 last if $pos + 8 > $len;
507 1         4 my $buff = substr($$buffPt, $pos, 8);
508 1 50       6 last unless $buff =~ /^(Xing|Info)/;
509 0         0 my $xingTable = GetTagTable('Image::ExifTool::MPEG::Xing');
510 0         0 my $vbrScale;
511 0         0 my $flags = unpack('x4N', $buff);
512 0         0 my $isVBR = ($buff !~ /^Info/); # Info frame is not VBR (ref 5)
513 0         0 $pos += 8;
514 0 0       0 if ($flags & 0x01) { # VBRFrames
515 0 0       0 last if $pos + 4 > $len;
516 0 0       0 $et->HandleTag($xingTable, 1, unpack("x${pos}N", $$buffPt)) if $isVBR;
517 0         0 $pos += 4;
518             }
519 0 0       0 if ($flags & 0x02) { # VBRBytes
520 0 0       0 last if $pos + 4 > $len;
521 0 0       0 $et->HandleTag($xingTable, 2, unpack("x${pos}N", $$buffPt)) if $isVBR;
522 0         0 $pos += 4;
523             }
524 0 0       0 if ($flags & 0x04) { # VBR_TOC
525 0 0       0 last if $pos + 100 > $len;
526             # (ignore toc for now)
527 0         0 $pos += 100;
528             }
529 0 0       0 if ($flags & 0x08) { # VBRScale
530 0 0       0 last if $pos + 4 > $len;
531 0         0 $vbrScale = unpack("x${pos}N", $$buffPt);
532 0 0       0 $et->HandleTag($xingTable, 3, $vbrScale) if $isVBR;
533 0         0 $pos += 4;
534             }
535             # process Lame header (ref 5)
536 0 0       0 if ($flags & 0x10) { # Lame
    0          
537 0 0       0 last if $pos + 348 > $len;
538             } elsif ($pos + 4 <= $len) {
539 0         0 my $lib = substr($$buffPt, $pos, 4);
540 0 0 0     0 unless ($lib eq 'LAME' or $lib eq 'GOGO') {
541             # attempt to identify other encoders
542 0         0 my $n;
543 0 0       0 if (index($$buffPt, 'RCA mp3PRO Encoder') >= 0) {
    0          
    0          
544 0         0 $lib = 'RCA mp3PRO';
545             } elsif (($n = index($$buffPt, 'THOMSON mp3PRO Encoder')) >= 0) {
546 0         0 $lib = 'Thomson mp3PRO';
547 0         0 $n += 22;
548 0 0       0 $lib .= ' ' . substr($$buffPt, $n, 6) if length($$buffPt) - $n >= 6;
549             } elsif (index($$buffPt, 'MPGE') >= 0) {
550 0         0 $lib = 'Gogo (<3.0)';
551             } else {
552 0         0 last;
553             }
554 0         0 $et->HandleTag($xingTable, 4, $lib);
555 0         0 last;
556             }
557             }
558 0         0 my $lameLen = $len - $pos;
559 0 0       0 last if $lameLen < 9;
560 0         0 my $enc = substr($$buffPt, $pos, 9);
561 0 0       0 if ($enc ge 'LAME3.90') {
562 0         0 $et->HandleTag($xingTable, 4, $enc);
563 0 0       0 if ($vbrScale <= 100) {
564 0         0 $et->HandleTag($xingTable, 5, int((100 - $vbrScale) / 10));
565 0         0 $et->HandleTag($xingTable, 6, (100 - $vbrScale) % 10);
566             }
567 0         0 my %dirInfo = (
568             DataPt => $buffPt,
569             DirStart => $pos,
570             DirLen => length($$buffPt) - $pos,
571             );
572 0         0 my $subTablePtr = GetTagTable('Image::ExifTool::MPEG::Lame');
573 0         0 $et->ProcessDirectory(\%dirInfo, $subTablePtr);
574             } else {
575 0         0 $et->HandleTag($xingTable, 4, substr($$buffPt, $pos, 20));
576             }
577 0         0 last; # (didn't want to loop anyway)
578             }
579              
580 1         5 return 1;
581             }
582              
583             #------------------------------------------------------------------------------
584             # Read MPEG video frame header
585             # Inputs: 0) ExifTool object reference, 1) Reference to video data
586             # Returns: 1 on success, 0 if no video header was found
587             sub ProcessMPEGVideo($$)
588             {
589 0     0 0   my ($et, $buffPt) = @_;
590              
591 0 0         return 0 unless length $$buffPt >= 4;
592 0           my ($w1, $w2) = unpack('N2', $$buffPt);
593             # validate as much as possible
594 0 0 0       if (($w1 & 0x000000f0) == 0x00000000 or # 0000 is a forbidden aspect ratio
      0        
      0        
595             ($w1 & 0x000000f0) == 0x000000f0 or # 1111 is a reserved aspect ratio
596             ($w1 & 0x0000000f) == 0 or # frame rate must be 1-8
597             ($w1 & 0x0000000f) > 8)
598             {
599 0           return 0;
600             }
601             # set file type if not done already
602 0 0         $et->SetFileType('MPEG') unless $$et{FileType};
603              
604 0           my $tagTablePtr = GetTagTable('Image::ExifTool::MPEG::Video');
605 0           ProcessFrameHeader($et, $tagTablePtr, $w1, $w2);
606 0           return 1;
607             }
608              
609             #------------------------------------------------------------------------------
610             # Read MPEG audio and video frame headers
611             # Inputs: 0) ExifTool object reference, 1) Reference to audio/video data
612             # Returns: 1 on success, 0 if no video header was found
613             # To Do: Properly parse MPEG streams:
614             # 0xb7 - sequence end
615             # 0xb9 - end code
616             # 0xba - pack start code
617             # 0xbb - system header
618             # 0xbc - program map <-- should parse this
619             # 0xbd - private stream 1 --> for VOB, this contains sub-streams:
620             # 0x20-0x3f - pictures
621             # 0x80-0x87 - audio (AC3,DTS,SDDS)
622             # 0xa0-0xa7 - audio (LPCM)
623             # 0xbe - padding
624             # 0xbf - private stream 2
625             # 0xc0-0xdf - audio stream
626             # 0xe0-0xef - video stream
627             sub ParseMPEGAudioVideo($$)
628             {
629 0     0 0   my ($et, $buffPt) = @_;
630 0           my (%found, $didHdr);
631 0           my $rtnVal = 0;
632 0           my %proc = ( audio => \&ParseMPEGAudio, video => \&ProcessMPEGVideo );
633              
634 0           delete $$et{AudioBitrate};
635 0           delete $$et{VideoBitrate};
636              
637 0           while ($$buffPt =~ /\0\0\x01(\xb3|\xc0)/g) {
638 0 0         my $type = $1 eq "\xb3" ? 'video' : 'audio';
639 0 0         unless ($didHdr) {
640             # make sure we didn't miss an audio frame sync before this (eg. MP3 file)
641             # (the last byte of the 4-byte MP3 audio frame header word may be zero,
642             # but the 2nd last must be non-zero, so we need only check to pos-3)
643 0           my $buff = substr($$buffPt, 0, pos($$buffPt) - 3);
644 0 0         $found{audio} = 1 if ParseMPEGAudio($et, \$buff);
645 0           $didHdr = 1;
646             }
647 0 0         next if $found{$type};
648 0           my $len = length($$buffPt) - pos($$buffPt);
649 0 0         last if $len < 4;
650 0 0         $len > 256 and $len = 256;
651 0           my $dat = substr($$buffPt, pos($$buffPt), $len);
652             # process MPEG audio or video
653 0 0         if (&{$proc{$type}}($et, \$dat)) {
  0            
654 0           $rtnVal = 1;
655 0           $found{$type} = 1;
656             # done if we found audio and video
657 0 0         last if scalar(keys %found) == 2;
658             }
659             }
660 0           return $rtnVal;
661             }
662              
663             #------------------------------------------------------------------------------
664             # Read information from an MPEG file
665             # Inputs: 0) ExifTool object reference, 1) Directory information reference
666             # Returns: 1 on success, 0 if this wasn't a valid MPEG file
667             sub ProcessMPEG($$)
668             {
669 0     0 0   my ($et, $dirInfo) = @_;
670 0           my $raf = $$dirInfo{RAF};
671 0           my $buff;
672              
673 0 0         $raf->Read($buff, 4) == 4 or return 0;
674 0 0         return 0 unless $buff =~ /^\0\0\x01[\xb0-\xbf]/;
675 0           $et->SetFileType();
676              
677 0           $raf->Seek(0,0);
678 0 0         $raf->Read($buff, 65536*4) or return 0;
679              
680 0           return ParseMPEGAudioVideo($et, \$buff);
681             }
682              
683             1; # end
684              
685             __END__