File Coverage

blib/lib/Video/Info/MPEG/Video.pm
Criterion Covered Total %
statement 111 118 94.0
branch 29 34 85.2
condition 9 11 81.8
subroutine 16 16 100.0
pod 0 11 0.0
total 165 190 86.8


line stmt bran cond sub pod time code
1             ##------------------------------------------------------------------------
2             ## Package: Video::Info::MPEG::Video
3             ## Author: Benjamin R. Ginter
4             ## Notice: Copyright (c) 2001 Benjamin R. Ginter
5             ## Purpose: Parse video streams
6             ## Comments: None
7             ## CVS: $Id: Video.pm,v 1.4 2003/07/08 07:35:33 allenday Exp $
8             ##------------------------------------------------------------------------
9              
10             package Video::Info::MPEG::Video;
11 5     5   29 use strict;
  5         11  
  5         205  
12 5     5   27 use Video::Info::MPEG;
  5         14  
  5         102  
13 5     5   27 use Video::Info::MPEG::Constants;
  5         10  
  5         1133  
14              
15 5     5   29 use constant DEBUG => 0;
  5         11  
  5         298  
16 5     5   28 use base qw(Video::Info::MPEG);
  5         8  
  5         34092  
17              
18             sub init {
19 12     12 0 511 my $self = shift;
20 12         45 my %param = @_;
21 12         64 $self->init_attributes(@_);
22 12         426 $self->handle($self->filename);
23 12   100     441 $self->context($param{-context} || 'video');
24             }
25              
26             ##------------------------------------------------------------------------
27             ## parse()
28             ##
29             ## Parse a video stream
30             ##------------------------------------------------------------------------
31             sub parse {
32 9     9 0 16 my $self = shift;
33 9         18 my $offset = shift;
34              
35 9 100       29 $offset = 0 if !defined $offset;
36              
37 9   100     48 $self->{offset} = $self->{last_offset} || $offset;
38              
39 9         12 print "Video::Info::MPEG::Video::parse( $offset )\n" if DEBUG;
40             # print "\n", '-' x 74, "\n", "Parse Video: $offset\n", '-' x 74, "\n";
41              
42             ## Make sure we have video
43 9 100       28 $self->is_video() or return 0;
44             #if we made it this far, assume a bona fide MPEG
45 6         202 $self->type('MPEG');
46 6         106 $self->get_size();
47 6         24 $self->get_frame_rate();
48 6         21 $self->get_aspect_ratio();
49 6         29 $self->get_bitrate();
50 6         55 $self->get_duration();
51 6         106 $self->get_extensions();
52 6         28 $self->get_gop();
53 6         42 $self->get_header_size();
54              
55 6         15 if ( DEBUG ) {
56             print " DIMENSIONS: ", $self->width, 'x', $self->height, "\n";
57             printf " FRAME RATE: %0.2f fps\n", $self->fps;
58             printf " ASPECT RATIO: %s ( %d )\n", $self->aspect, $self->aspect_raw;
59             print " BITRATE: ", $self->vrate, "\n";
60             print " DURATION: ", $self->duration, "\n";
61             print " HEADER SIZE: $self->{video_header_size}\n";
62             }
63              
64 6         52 return 1;
65             }
66              
67             ##------------------------------------------------------------------------
68             ## get_size()
69             ##
70             ## Get the width and height
71             ##------------------------------------------------------------------------
72             sub get_size {
73 6     6 0 22 my $self = shift;
74              
75 6         18 $self->{offset} += 4;
76              
77 6         26 $self->width( $self->grab( 2, $self->{offset} ) >> 4 );
78 6         56 $self->height( $self->grab( 2, $self->{offset} + 1 ) & 0x0FFF );
79 6 50 33     179 if ( !defined $self->width || !defined $self->height ) {
80 0         0 return 0;
81             }
82 6         227 return 1;
83             }
84              
85             ##------------------------------------------------------------------------
86             ## is_video()
87             ##
88             ## Verify we're really dealing with a video packet
89             ##
90             ## This method searches up to eof for the start code in case there is
91             ## junk at the beginning of the file. Should we limit this somehow?
92             ##------------------------------------------------------------------------
93             sub is_video {
94 9     9 0 14 my $self = shift;
95              
96 9         13 print "is_video: offset $self->{offset}\n" if DEBUG;
97              
98             # return 0 if !$self->next_start_code( SEQ_START_CODE, $self->{offset} );
99              
100 9         234 while ( $self->{offset} <= $self->filesize - 4 ) {
101 145032         1453808 my $a = $self->get_byte( $self->{offset} );
102 145032 100       403399 if ( $a != 0x00 ) { $self->{offset}++; next; }
  140296         245450  
  140296         4719802  
103            
104 4736         18111 my $b = $self->get_byte( $self->{offset} + 1 );
105 4736 100       16512 if ( $b != 0x00 ) { $self->{offset} += 2; next; };
  3568         13684  
  3568         114109  
106              
107 1168         4383 my $c = $self->get_byte( $self->{offset} + 2 );
108 1168 100       3949 if ( $c != 0x01 ) { $self->{offset} += 3; next; };
  409         635  
  409         10251  
109              
110 759         6695 my $d = $self->get_byte( $self->{offset} + 3 );
111              
112 759         2096 printf "Found 0x%02x @ %d\n", $d, $self->{offset} + 3 if DEBUG;
113             # sleep 1;
114              
115 759 100 100     6907 if ( $d == SEQ_START_CODE ) {
    100          
116 5         20 return 1;
117             }
118             elsif ( $self->{context} eq 'video' && $d == SYS_PKT ) {
119 3         6 print "Returning because video context\n" if DEBUG;
120 3         30 return 0;
121             }
122 751         28861 $self->{offset}++;
123             }
124              
125 1         14 $self->{offset} = $self->{last_offset};
126              
127 1         6 return 1;
128             }
129              
130             ##------------------------------------------------------------------------
131             ## get_frame_rate()
132             ##
133             ## Extract the frame_rate index and do the lookup
134             ##------------------------------------------------------------------------
135             sub get_frame_rate {
136 6     6 0 13 my $self = shift;
137              
138 6         20 $self->{offset} += 3;
139              
140 6         24 my $frame_rate_index = $self->grab( 1, $self->{offset} ) & 0x0f;
141              
142 6 50       33 if ( $frame_rate_index > 8 ) {
143 0         0 print "Invalid frame rate index: $frame_rate_index\n" if DEBUG;
144             ## $self->fps( 0.0 );
145 0         0 return 0;
146             }
147              
148 6         360 $self->fps( $FRAME_RATE->[ $frame_rate_index ] );
149            
150 6         47 return 1;
151             }
152              
153             ##------------------------------------------------------------------------
154             ## get_aspect_ratio()
155             ##
156             ## Extract the aspect ratio index and do the lookup.
157             ##
158             ## NOTE: Don't die() on invalid aspect ratios as they are fairly common
159             ## For example, 320x240 is invalid. :)
160             ##------------------------------------------------------------------------
161             sub get_aspect_ratio {
162 6     6 0 11 my $self = shift;
163              
164 6         29 my $aspect = ( $self->grab( 1, $self->{offset} ) & 0xF0 ) >> 4;
165 6 50       22 if ( !$aspect ) {
166             # print "Invalid aspect ratio: $aspect\n";
167 0         0 return 0;
168             }
169 6 100       15 if ( $aspect > $#{ $ASPECT_RATIO } ) {
  6         27  
170             # print "Reserved aspect ratio: $aspect\n";
171 3         73 $self->aspect( 'Reserved' );
172             }
173             else {
174             # print "Aspect Ratio: ", $ASPECT_RATIO->[ $aspect ], "\n";
175 3         87 $self->aspect( $ASPECT_RATIO->[ $aspect ] );
176             }
177              
178 6         197 $self->aspect_raw( $aspect );
179              
180 6         109 return 1;
181             }
182              
183             ##------------------------------------------------------------------------
184             ## get_bitrate()
185             ##
186             ## From the MPEG-2.2 spec:
187             ##
188             ## bit_rate -- This is a 30-bit integer. The lower 18 bits of the
189             ## integer are in bit_rate_value and the upper 12 bits are in
190             ## bit_rate_extension. The 30-bit integer specifies the bitrate of the
191             ## bitstream measured in units of 400 bits/second, rounded upwards.
192             ## The value zero is forbidden.
193             ##
194             ## So ignoring all the variable bitrate stuff for now, this 30 bit integer
195             ## multiplied times 400 bits/sec should give the rate in bits/sec.
196             ##
197             ## TODO: Variable bitrates? I need one that implements this.
198             ##
199             ## Continued from the MPEG-2.2 spec:
200             ##
201             ## If the bitstream is a constant bitrate stream, the bitrate specified
202             ## is the actual rate of operation of the VBV specified in annex C. If
203             ## the bitstream is a variable bitrate stream, the STD specifications in
204             ## ISO/IEC 13818-1 supersede the VBV, and the bitrate specified here is
205             ## used to dimension the transport stream STD (2.4.2 in ITU-T Rec. xxx |
206             ## ISO/IEC 13818-1), or the program stream STD (2.4.5 in ITU-T Rec. xxx |
207             ## ISO/IEC 13818-1).
208             ##
209             ## If the bitstream is not a constant rate bitstream the vbv_delay
210             ## field shall have the value FFFF in hexadecimal.
211             ##
212             ## Given the value encoded in the bitrate field, the bitstream shall be
213             ## generated so that the video encoding and the worst case multiplex
214             ## jitter do not cause STD buffer overflow or underflow.
215             ##
216             ##
217             ##------------------------------------------------------------------------
218             sub get_bitrate {
219 6     6 0 10 my $self = shift;
220              
221 6         14 $self->{offset}++;
222              
223             ## grab a short
224 6         21 my $bitrate = $self->grab( 2, $self->{offset} ) << 2;
225 6         26 my $lasttwo = $self->get_byte( $self->{offset} + 2 ) >> 6;
226              
227 6 100       151 if(!$self->vrate){
228 4         120 $self->vrate( ( $bitrate | $lasttwo ) * 400);
229             } else {
230             }
231             }
232              
233             ##------------------------------------------------------------------------
234             ## get_duration()
235             ##
236             ##
237             ##------------------------------------------------------------------------
238             sub get_duration {
239 6     6 0 11 my $self = shift;
240 6         149 $self->duration ( ( $self->filesize * 8 ) / ( $self->vrate * 400 ) );
241             }
242              
243             ##------------------------------------------------------------------------
244             ## get_extensions()
245             ##
246             ## TODO: make the $START_CODE->{$code} description the actual method name
247             ## for the extension handler.
248             ##------------------------------------------------------------------------
249             sub get_extensions {
250 6     6 0 10 my $self = shift;
251            
252 6         18 while (1) {
253 6         386 my $code = $self->next_start_code( undef, $self->{offset}, 1 );
254 6 100       25 last if $code == 0xB8;
255 3         9 $self->{offset} = $self->{last_offset};
256            
257 3         12 $code = $self->get_byte( $self->{offset} + 3 );
258 3         15 my $descr = $START_CODE->{$code};
259            
260 3 50       10 if ( defined $descr ) {
261             ## printf "EXTENSION: %s\n", $START_CODE->{$code};
262            
263 3 50       21 if ( $descr eq 'extension_start_code' ) {
    100          
264 0         0 $self->parse_extension( $self->{offset} );
265 0         0 next;
266             }
267             elsif ( $descr eq 'user_data_start_code' ) {
268 2         26 $self->parse_user_data( $self->{offset} );
269 2         10 last;
270             }
271             else {
272 1         2 print "No methods to handle $descr\n" if DEBUG;
273 1         4 last;
274             }
275             }
276            
277 0         0 $self->{offset}++;
278             }
279             }
280              
281             ##------------------------------------------------------------------------
282             ## get_gop()
283             ##
284             ## Find first GOP header after video sequence header
285             ##------------------------------------------------------------------------
286             sub get_gop {
287 6     6 0 8 my $self = shift;
288              
289 6 100       37 if ( !$self->next_start_code( 0xb8, $self->{offset} ) ) {
290             ##Ben: should we return 0 here?
291             ##Allen: yes, i suppose so.
292 1         4 return 0;
293             ##Allen: let's not do this: die "Couldn't find first GOP after Video Sequence start!\n";
294             }
295 5         13 print "Found GOP Header (0xB8) at $self->{last_offset} $self->{offset}\n" if DEBUG;
296             }
297              
298             ##------------------------------------------------------------------------
299             ## get_header_size()
300             ##
301             ## Video header size
302             ##------------------------------------------------------------------------
303             sub get_header_size {
304 6     6 0 11 my $self = shift;
305              
306 6         11 print "OFFSETS: $self->{last_offset} $self->{offset}\n" if DEBUG;
307              
308 6         224 $self->header_size( $self->{last_offset} - $self->{offset} );
309 6         1206 print "HEADER_SIZE: ", $self->header_size, "\n" if DEBUG;
310             }
311              
312             1;
313              
314             __END__