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