File Coverage

blib/lib/Flv/Info/Lite.pm
Criterion Covered Total %
statement 129 130 99.2
branch 25 32 78.1
condition 3 3 100.0
subroutine 12 12 100.0
pod 0 8 0.0
total 169 185 91.3


line stmt bran cond sub pod time code
1             package Flv::Info::Lite;
2              
3 2     2   31051 use 5.008008;
  2         6  
  2         69  
4 2     2   10 use strict;
  2         3  
  2         83  
5 2     2   16 use warnings;
  2         6  
  2         1950  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10             our @EXPORT_OK = qw( flv_info );
11             our @EXPORT = qw();
12             our $VERSION = '0.02';
13              
14             sub flv_info {
15 1     1 0 10 my %tag_type = (
16             8 => 'audio',
17             9 => 'video',
18             18 => 'script',
19             );
20              
21 1         40 my %audio_format = (
22             0 => 'uncompressed',
23             1 => 'ADPCM',
24             2 => 'MP3',
25             3 => 'Linear_PCM_little_endian',
26             4 => 'Nellymoser_16kHz_mono',
27             5 => 'Nellymoser_8kHz_mono',
28             6 => 'Nellymoser',
29             7 => 'G.711_A-law',
30             8 => 'G.711_mu-law',
31             10 => 'AAC',
32             11 => 'Speex',
33             14 => 'MP3_8kHz',
34             15 => 'Device-specific_sound',
35             );
36              
37 1         6 my %audio_rate = (
38             0 => '5518Hz',
39             1 => '11025Hz',
40             2 => '22050Hz',
41             3 => '44100Hz',
42             );
43              
44 1         4 my %audio_size = (
45             0 => '8bit',
46             1 => '16bit',
47             );
48              
49 1         4 my %audio_type = (
50             0 => 'mono',
51             1 => 'stereo',
52             );
53              
54 1         6 my %video_codec = (
55             1 => 'JPEG',
56             2 => 'Sorenson_H.263',
57             3 => 'Screen_video',
58             4 => 'On2_VP6',
59             5 => 'On2_VP6_alpha',
60             6 => 'Screen_video_v2',
61             7 => 'AVC',
62             );
63              
64 1         5 my %video_type = (
65             1 => 'keyframe',
66             2 => 'interframe',
67             3 => 'disposable_interframe',
68             4 => 'generated_keyframe',
69             5 => 'video_info/command_frame',
70             );
71              
72 1         14 my %avc_packet_type = (
73             0 => 'avc_seq_header',
74             1 => 'avc_nalu',
75             2 => 'avc_seq_end',
76             );
77              
78 1         4 my $input = shift;
79 1         2 my %flv_info;
80 1         3 $flv_info{frame_count} = 0;
81              
82 1 50       4 if ( $input eq '-' ) {
83 0         0 open FH, "<-";
84             }
85             else {
86 1         40 open FH, "<", $input;
87             }
88 1         4 binmode FH;
89 1         2 my $buf;
90              
91 1         23 read( FH, $buf, 3 ); #File Header
92              
93 1         3 read( FH, $buf, 1 ); #Version
94              
95 1         2 read( FH, $buf, 1 ); # jump Type Flags
96 1         5 my $flags = unpack 'C', substr( $buf, 0, 1 );
97 1         4 my $type_flags_audio = ( ( $flags >> 2 ) & 0x01 );
98 1         2 my $type_flags_video = $flags & 0x01;
99 1         3 $flv_info{have_audio} = $type_flags_audio;
100 1         3 $flv_info{have_video} = $type_flags_video;
101              
102 1         3 read( FH, $buf, 4 ); #Header Size
103              
104 1         2 read( FH, $buf, 4 ); # jump PreviousTagSize0
105              
106 1         2 my $tag_id = 0;
107              
108 1         6 while ( read( FH, $buf, 8 ) ) {
109 171         135 $tag_id++;
110              
111 171         102 my ( $tag_type, $data_size, $ts, @datasize, @timestamp );
112             (
113 171         401 $tag_type, $datasize[0], $datasize[1], $datasize[2],
114             $timestamp[1], $timestamp[2], $timestamp[3], $timestamp[0]
115             ) = unpack 'CCCCCCCC', $buf;
116              
117 171         208 $data_size = ( $datasize[0] * 256 + $datasize[1] ) * 256 + $datasize[2];
118 171         157 $ts =
119             ( ( $timestamp[0] * 256 + $timestamp[1] ) * 256 + $timestamp[2] ) *
120             256 + $timestamp[3];
121              
122 171         157 read( FH, $buf, 3 ); # jump SteamID, Always 0.
123 171         370 read( FH, $buf, $data_size );
124              
125 171         171 $tag_type = $tag_type{$tag_type};
126              
127 171 100       257 if ( $tag_type eq 'audio' ) {
    100          
    50          
128 65         78 my $flags = unpack 'C', substr( $buf, 0, 1 );
129 65         55 my $format = ( ( $flags >> 4 ) & 0x0f );
130 65         48 my $rate = ( ( $flags >> 2 ) & 0x03 );
131 65         47 my $size = ( ( $flags >> 1 ) & 0x01 );
132 65         39 my $type = $flags & 0x01;
133 65         59 my $audio_format = $audio_format{$format};
134 65         51 my $audio_rate = $audio_rate{$rate}; # Always 44100 when AAC
135 65         56 my $audio_size = $audio_size{$size};
136 65         66 my $audio_type = $audio_type{$type};
137             }
138             elsif ( $tag_type eq 'video' ) {
139 105         117 my $flags = unpack 'C', substr( $buf, 0, 1 );
140 105         95 my $type = ( $flags >> 4 ) & 0x0f;
141 105         77 my $codec = $flags & 0x0f;
142 105         80 my $video_type = $video_type{$type};
143 105         82 my $video_codec = $video_codec{$codec};
144 105         78 my $if_real_frame = 1;
145 105 50       142 if ( $video_codec eq 'AVC' ) {
146 105         72 my @avc_time;
147 105         78 my $avc_header = substr( $buf, 1, 4 );
148 105         76 my $avc_packet_type;
149 105         172 ( $avc_packet_type, $avc_time[0], $avc_time[1], $avc_time[2] )
150             = unpack 'CCCC', $avc_header;
151 105         143 my $composition_time =
152             ( $avc_time[0] * 256 + $avc_time[1] ) * 256 + $avc_time[2];
153 105         96 $avc_packet_type = $avc_packet_type{$avc_packet_type};
154 105 100 100     337 $if_real_frame = 0
155             if $avc_packet_type eq 'avc_seq_header'
156             or $avc_packet_type eq 'avc_seq_end';
157             }
158 105 100       167 $flv_info{frame_count}++ if $if_real_frame;
159             }
160             elsif ( $tag_type eq 'script' ) {
161 1         6 my %meta_info = extract_amf0($buf);
162 1         4 foreach ( keys %meta_info ) {
163 14         15 $flv_info{$_} = $meta_info{$_};
164             }
165             }
166             else {
167             }
168              
169 171         355 read( FH, $buf, 4 ); # jump PreviousTagSize
170             }
171 1         25 return %flv_info;
172             }
173              
174             sub extract_amf0 {
175 1     1 0 1 my %amf0;
176 1         4 $amf0{data} = shift;
177 1         2 $amf0{pos} = 0;
178              
179 1         18 my %data;
180              
181 1         5 my $type = ord( amf0_read( \%amf0, 1 ) );
182 1 50       4 if ( $type == 2 ) {
183 1         3 my $string = amf0_read_string( \%amf0 );
184             }
185 1         3 amf0_read_unit( \%amf0 );
186              
187 1         5 while ( length( $amf0{data} ) > $amf0{pos} ) {
188 14         456 my $key = amf0_read_string( \%amf0 );
189 14         20 my $value = amf0_read_unit( \%amf0 );
190 14         39 $data{$key} = $value;
191             }
192 1         10 return %data;
193             }
194              
195             sub amf0_read_unit {
196 15     15 0 15 my $amf0 = shift;
197              
198 15         16 my $type = ord( amf0_read( $amf0, 1 ) );
199 15 100       59 if ( $type == 8 ) {
    100          
    100          
    100          
200 1         5 amf0_read( $amf0, 4 );
201 1         1 return 1;
202             }
203             elsif ( $type == 2 ) {
204 1         2 my $string = amf0_read_string($amf0);
205 1         3 return $string;
206             }
207             elsif ( $type == 1 ) {
208 1         2 my $boolean = amf0_read_boolean($amf0);
209 1         1 return $boolean;
210             }
211             elsif ( $type == 0 ) {
212 11         14 my $double = amf0_read_double($amf0);
213 11         16 return $double;
214             }
215              
216             }
217              
218             sub amf0_read {
219 77     77 0 53 my $amf0 = shift;
220 77         50 my $read_bytes = shift;
221              
222 77         87 my $bytes = substr( $amf0->{data}, $amf0->{pos}, $read_bytes );
223 77         62 $amf0->{pos} += $read_bytes;
224              
225 77         110 return $bytes;
226             }
227              
228             sub amf0_read_int {
229 16     16 0 13 my $amf0 = shift;
230              
231 16         18 my $first_byte = amf0_read( $amf0, 1 );
232 16         21 my $second_byte = amf0_read( $amf0, 1 );
233              
234 16 50       25 my $first_num = defined($first_byte) ? ord($first_byte) : 0;
235 16 50       18 my $second_num = defined($second_byte) ? ord($second_byte) : 0;
236              
237 16         12 my $amf0_int = ( ($first_num) << 8 ) | $second_num;
238 16         19 return $amf0_int;
239             }
240              
241             sub amf0_read_boolean {
242 1     1 0 2 my $amf0 = shift;
243 1         3 my $data = amf0_read( $amf0, 1 );
244 2     2   11 no warnings 'numeric';
  2         2  
  2         367  
245 1 50       6 my $boolean = $data == 1 ? 1 : 0;
246 1         1 return $boolean;
247             }
248              
249             sub amf0_read_string {
250 16     16 0 14 my $amf0 = shift;
251 16         18 my $string_length = amf0_read_int($amf0);
252 16         18 my $string = amf0_read( $amf0, $string_length );
253 16         17 return $string;
254             }
255              
256             sub amf0_read_double {
257 11     11 0 10 my $amf0 = shift;
258 11         13 my @data = split //, amf0_read( $amf0, 8 );
259 11         13 my $data;
260 11         14 foreach ( reverse @data ) {
261 88 100       104 $_ = "0" unless $_;
262 88         80 $data .= $_;
263             }
264 11         23 my @zz = unpack( "d", $data );
265 11         17 return $zz[0];
266             }
267              
268             1;
269             __END__