File Coverage

blib/lib/FLV/VideoTag.pm
Criterion Covered Total %
statement 74 98 75.5
branch 23 46 50.0
condition 2 5 40.0
subroutine 16 19 84.2
pod 6 6 100.0
total 121 174 69.5


line stmt bran cond sub pod time code
1             package FLV::VideoTag;
2              
3 6     6   37 use warnings;
  6         10  
  6         3151  
4 6     6   39 use strict;
  6         11  
  6         334  
5 6     6   175 use 5.008;
  6         19  
  6         228  
6 6     6   37 use Carp;
  6         11  
  6         532  
7 6     6   33 use English qw(-no_match_vars);
  6         8  
  6         4275  
8              
9 6     6   4351 use base 'FLV::Base';
  6         11  
  6         852  
10              
11 6     6   31 use FLV::Util;
  6         12  
  6         743  
12 6     6   31 use FLV::Tag;
  6         11  
  6         11436  
13              
14             our $VERSION = '0.24';
15              
16             =for stopwords codec
17              
18             =head1 NAME
19              
20             FLV::VideoTag - Flash video file data structure
21              
22             =head1 LICENSE
23              
24             See L
25              
26             =head1 METHODS
27              
28             This is a subclass of L.
29              
30             =over
31              
32             =item $self->parse($fileinst)
33              
34             Takes a FLV::File instance and extracts an FLV video tag from the file
35             stream. This method throws exceptions if the
36             stream is not a valid FLV v1.0 or v1.1 file.
37              
38             There is no return value.
39              
40             Note: this method needs more work to extract the codec specific data.
41              
42             =cut
43              
44             sub parse
45             {
46 5662     5662 1 7478 my $self = shift;
47 5662         6778 my $file = shift;
48 5662         6326 my $datasize = shift;
49              
50 5662         14227 my $flags = unpack 'C', $file->get_bytes(1);
51              
52             # The spec PDF is wrong -- type comes first, then codec
53 5662         9882 my $type = ($flags >> 4) & 0x0f;
54 5662         7299 my $codec = $flags & 0x0f;
55              
56 5662 50       27194 if (!exists $VIDEO_CODEC_IDS{$codec})
57             {
58 0         0 die 'Unknown video codec ' . $codec . ' at byte ' . $file->get_pos(-1);
59             }
60 5662 50       50891 if (!exists $VIDEO_FRAME_TYPES{$type})
61             {
62 0         0 die 'Unknown video frame type at byte ' . $file->get_pos(-1);
63             }
64              
65 5662         39058 $self->{codec} = $codec;
66 5662         10066 $self->{type} = $type;
67              
68 5662         14435 my $pos = $file->get_pos();
69              
70 5662         17213 $self->{data} = $file->get_bytes($datasize - 1);
71              
72 5662 0       21767 my $result
    0          
    0          
    50          
    50          
    100          
73             = 2 == $self->{codec} ? $self->_parse_h263($pos)
74             : 3 == $self->{codec} ? $self->_parse_screen_video($pos)
75             : 4 == $self->{codec} ? $self->_parse_on2vp6($pos)
76             : 5 == $self->{codec} ? $self->_parse_on2vp6_alpha($pos)
77             : 6 == $self->{codec} ? $self->_parse_screen_video($pos)
78             : 7 == $self->{codec} ? $self->_parse_avc($pos)
79             : die 'Unknown video type';
80              
81 5662         13843 return;
82             }
83              
84             sub _parse_h263
85             {
86 4470     4470   5821 my $self = shift;
87 4470         10514 my $pos = shift;
88              
89             # Surely there's a better way than this....
90 4470         13931 my $bits = unpack 'B67', $self->{data};
91 4470         8818 my $sizecode = substr $bits, 30, 3;
92 4470         26034 my @d = (
93             (ord pack 'B8', substr $bits, 33, 8),
94             (ord pack 'B8', substr $bits, 41, 8),
95             (ord pack 'B8', substr $bits, 49, 8),
96             (ord pack 'B8', substr $bits, 57, 8),
97             );
98 4470 0       23190 my ($width, $height, $offset)
    50          
    50          
    50          
    50          
    50          
    50          
99             = '000' eq $sizecode ? ($d[0], $d[1], 16)
100             : '001' eq $sizecode ? ($d[0] * 256 + $d[1], $d[2] * 256 + $d[3], 32)
101             : '010' eq $sizecode ? (352, 288, 0)
102             : '011' eq $sizecode ? (176, 144, 0)
103             : '100' eq $sizecode ? (128, 96, 0)
104             : '101' eq $sizecode ? (320, 240, 0)
105             : '110' eq $sizecode ? (160, 120, 0)
106             : die 'Illegal value for H.263 size code at byte ' . $pos;
107              
108 4470         11309 $self->{width} = $width;
109 4470         6674 $self->{height} = $height;
110              
111 4470         7709 my $typebits = substr $bits, 33 + $offset, 2;
112 4470         14185 my @typebits = split m//xms, $typebits;
113 4470         12444 my $type = 1 + $typebits[0] * 2 + $typebits[1];
114 4470 100       14071 if (!defined $self->{type})
    50          
115             {
116 298         490 $self->{type} = $type;
117             }
118             elsif ($type != $self->{type})
119             {
120 0         0 warn "Type mismatch: header says $VIDEO_FRAME_TYPES{$self->{type}}, "
121             . "data says $VIDEO_FRAME_TYPES{$type}";
122             }
123              
124 4470         12918 return;
125             }
126              
127             sub _parse_screen_video
128             {
129 0     0   0 my $self = shift;
130 0         0 my $pos = shift;
131              
132             # Extract 4 bytes, big-endian
133 0         0 my ($width, $height) = unpack 'nn', $self->{data};
134              
135             # Only use the lower 12 bits of each
136 0         0 $width &= 0x3fff;
137 0         0 $height &= 0x3fff;
138              
139 0         0 $self->{width} = $width;
140 0         0 $self->{height} = $height;
141              
142 0   0     0 $self->{type} ||= 1;
143              
144 0         0 return;
145             }
146              
147             sub _parse_on2vp6
148             {
149 1788     1788   2294 my $self = shift;
150 1788         2037 my $pos = shift;
151              
152 1788 100       3862 if (!$self->{type})
153             {
154              
155             # Bit 7 of the header (after 8 bits of offset) distinguishes
156             # keyframe from interframe
157             # See: http://use.perl.org/~ChrisDolan/journal/30427
158 298         801 my @bytes = unpack 'CC', $self->{data};
159 298 100       840 $self->{type} = 0 == ($bytes[1] & 0x80) ? 1 : 2;
160             }
161              
162 1788         3226 return;
163             }
164              
165             sub _parse_on2vp6_alpha
166             {
167 0     0   0 my $self = shift;
168 0         0 my $pos = shift;
169              
170 0 0       0 if (!$self->{type})
171             {
172              
173             # Bit 7 of the header (after 32 bits of offset) distinguishes
174             # keyframe from interframe
175 0         0 my @bytes = unpack 'CCCCC', $self->{data};
176 0 0       0 $self->{type} = 0 == ($bytes[4] & 0x80) ? 1 : 2;
177             }
178              
179 0         0 return;
180             }
181              
182             sub _parse_avc
183             {
184 0     0   0 my $self = shift;
185 0         0 my $pos = shift;
186              
187 0         0 my @time;
188 0         0 ($self->{avc_packet_type}, $time[0], $time[1], $time[2]) = unpack 'CCCC', $self->{data};
189 0         0 $self->{composition_time} = ($time[0] * 256 + $time[1]) * 256 + $time[2];
190              
191 0         0 return;
192             }
193              
194             =item $self->clone()
195              
196             Create an independent copy of this instance.
197              
198             =cut
199              
200             sub clone
201             {
202 1192     1192 1 1602 my $self = shift;
203              
204 1192         3086 my $copy = FLV::VideoTag->new;
205 1192         3521 FLV::Tag->copy_tag($self, $copy);
206 1192         1699 for my $key (qw( codec type width height data avc_packet_type composition_time )) {
207 8344 100       19126 if (exists $self->{$key}) {
208 5960         13991 $copy->{$key} = $self->{$key};
209             }
210             }
211 1192         3545 return $copy;
212             }
213              
214             =item $self->serialize()
215              
216             Returns a byte string representation of the tag data. Throws an
217             exception via croak() on error.
218              
219             =cut
220              
221             sub serialize
222             {
223 3237     3237 1 4191 my $self = shift;
224              
225 3237         8610 my $flags = pack 'C', ($self->{type} << 4) | $self->{codec};
226 3237         19437 return $flags . $self->{data};
227             }
228              
229             =item $self->get_info()
230              
231             Returns a hash of FLV metadata. See FLV::Info for more details.
232              
233             =cut
234              
235             sub get_info
236             {
237 4     4 1 70 my ($pkg, @args) = @_;
238              
239 4         59 return $pkg->_get_info(
240             'video',
241             {
242             type => \%VIDEO_FRAME_TYPES,
243             codec => \%VIDEO_CODEC_IDS,
244             width => undef,
245             height => undef,
246             },
247             \@args
248             );
249             }
250              
251             =item $self->is_keyframe()
252              
253             Returns a boolean.
254              
255             =cut
256              
257             sub is_keyframe
258             {
259 6176     6176 1 8939 my $self = shift;
260 6176 100 66     43814 return $self->{type} && 1 == $self->{type} ? 1 : undef;
261             }
262              
263             =item $self->get_time()
264              
265             Returns the time in milliseconds for this tag.
266              
267             =cut
268              
269             sub get_time
270             {
271 3386     3386 1 4247 my $self = shift;
272 3386         9827 return $self->{start};
273             }
274              
275             1;
276              
277             __END__