source: extensions/charlies_content/getid3/getid3/module.audio-video.flv.php @ 3544

Revision 3544, 14.5 KB checked in by vdigital, 10 years ago (diff)

Change: getid3 upgraded to -> 1.7.9

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org>               //
4//  available at http://getid3.sourceforge.net                 //
5//            or http://www.getid3.org                         //
6//                                                             //
7//  FLV module by Seth Kaufman <seth@whirl-i-gig.com>          //
8//                                                             //
9//  * version 0.1 (26 June 2005)                               //
10//                                                             //
11//  minor modifications by James Heinrich <info@getid3.org>    //
12//  * version 0.1.1 (15 July 2005)                             //
13//                                                             //
14//  Support for On2 VP6 codec and meta information             //
15//    by Steve Webster <steve.webster@featurecreep.com>        //
16//  * version 0.2 (22 February 2006)                           //
17//                                                             //
18//  Modified to not read entire file into memory               //
19//    by James Heinrich <info@getid3.org>                      //
20//  * version 0.3 (15 June 2006)                               //
21//                                                             //
22//  Bugfixes for incorrectly parsed FLV dimensions             //
23//    and incorrect parsing of onMetaTag                       //
24//    by Evgeny Moysevich <moysevich@gmail.com>                //
25//  * version 0.4 (07 December 2007)                           //
26//                                                             //
27/////////////////////////////////////////////////////////////////
28//                                                             //
29// module.audio-video.flv.php                                  //
30// module for analyzing Shockwave Flash Video files            //
31// dependencies: NONE                                          //
32//                                                            ///
33/////////////////////////////////////////////////////////////////
34
35define('GETID3_FLV_TAG_AUDIO', 8);
36define('GETID3_FLV_TAG_VIDEO', 9);
37define('GETID3_FLV_TAG_META', 18);
38
39define('GETID3_FLV_VIDEO_H263',   2);
40define('GETID3_FLV_VIDEO_SCREEN', 3);
41define('GETID3_FLV_VIDEO_VP6',    4);
42
43class getid3_flv
44{
45
46        function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
47//$start_time = microtime(true);
48                fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
49
50                $FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
51                $FLVheader = fread($fd, 5);
52
53                $ThisFileInfo['fileformat'] = 'flv';
54                $ThisFileInfo['flv']['header']['signature'] =                           substr($FLVheader, 0, 3);
55                $ThisFileInfo['flv']['header']['version']   = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
56                $TypeFlags                                  = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
57
58                if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') {
59                        $ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"';
60                        unset($ThisFileInfo['flv']);
61                        unset($ThisFileInfo['fileformat']);
62                        return false;
63                }
64
65                $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
66                $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
67
68                $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4));
69                $FLVheaderFrameLength = 9;
70                if ($FrameSizeDataLength > $FLVheaderFrameLength) {
71                        fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
72                }
73//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
74
75                $Duration = 0;
76                $found_video = false;
77                $found_audio = false;
78                $found_meta  = false;
79                while ((ftell($fd) + 16) < $ThisFileInfo['avdataend']) {
80                        $ThisTagHeader = fread($fd, 16);
81
82                        $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  0, 4));
83                        $TagType           = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  4, 1));
84                        $DataLength        = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  5, 3));
85                        $Timestamp         = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  8, 3));
86                        $LastHeaderByte    = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
87                        $NextOffset = ftell($fd) - 1 + $DataLength;
88                        if ($Timestamp > $Duration) {
89                                $Duration = $Timestamp;
90                        }
91
92//echo __LINE__.'['.ftell($fd).']=('.$TagType.')='.number_format(microtime(true) - $start_time, 3).'<br>';
93
94                        switch ($TagType) {
95                                case GETID3_FLV_TAG_AUDIO:
96                                        if (!$found_audio) {
97                                                $found_audio = true;
98                                                $ThisFileInfo['flv']['audio']['audioFormat']     =  $LastHeaderByte & 0x07;
99                                                $ThisFileInfo['flv']['audio']['audioRate']       = ($LastHeaderByte & 0x30) / 0x10;
100                                                $ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40;
101                                                $ThisFileInfo['flv']['audio']['audioType']       = ($LastHeaderByte & 0x80) / 0x80;
102                                        }
103                                        break;
104
105                                case GETID3_FLV_TAG_VIDEO:
106                                        if (!$found_video) {
107                                                $found_video = true;
108                                                $ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
109
110                                                $FLVvideoHeader = fread($fd, 11);
111
112                                                if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) {
113
114                                                        $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
115                                                        $PictureSizeType = $PictureSizeType & 0x0007;
116                                                        $ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
117                                                        switch ($PictureSizeType) {
118                                                                case 0:
119                                                                        //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
120                                                                        //$PictureSizeEnc <<= 1;
121                                                                        //$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
122                                                                        //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
123                                                                        //$PictureSizeEnc <<= 1;
124                                                                        //$ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
125
126                                                                        $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
127                                                                        $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
128                                                                        $PictureSizeEnc['x'] >>= 7;
129                                                                        $PictureSizeEnc['y'] >>= 7;
130                                                                        $ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
131                                                                        $ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
132                                                                        break;
133
134                                                                case 1:
135                                                                        $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
136                                                                        $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
137                                                                        $PictureSizeEnc['x'] >>= 7;
138                                                                        $PictureSizeEnc['y'] >>= 7;
139                                                                        $ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
140                                                                        $ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
141                                                                        break;
142
143                                                                case 2:
144                                                                        $ThisFileInfo['video']['resolution_x'] = 352;
145                                                                        $ThisFileInfo['video']['resolution_y'] = 288;
146                                                                        break;
147
148                                                                case 3:
149                                                                        $ThisFileInfo['video']['resolution_x'] = 176;
150                                                                        $ThisFileInfo['video']['resolution_y'] = 144;
151                                                                        break;
152
153                                                                case 4:
154                                                                        $ThisFileInfo['video']['resolution_x'] = 128;
155                                                                        $ThisFileInfo['video']['resolution_y'] = 96;
156                                                                        break;
157
158                                                                case 5:
159                                                                        $ThisFileInfo['video']['resolution_x'] = 320;
160                                                                        $ThisFileInfo['video']['resolution_y'] = 240;
161                                                                        break;
162
163                                                                case 6:
164                                                                        $ThisFileInfo['video']['resolution_x'] = 160;
165                                                                        $ThisFileInfo['video']['resolution_y'] = 120;
166                                                                        break;
167
168                                                                default:
169                                                                        $ThisFileInfo['video']['resolution_x'] = 0;
170                                                                        $ThisFileInfo['video']['resolution_y'] = 0;
171                                                                        break;
172
173                                                        }
174                                                }
175                                        }
176                                        break;
177
178                                // Meta tag
179                                case GETID3_FLV_TAG_META:
180                                        if (!$found_meta) {
181                                                $found_meta = true;
182                                                fseek($fd, -1, SEEK_CUR);
183                                                $reader = new AMFReader(new AMFStream(fread($fd, $DataLength)));
184                                                $eventName = $reader->readData();
185                                                $ThisFileInfo['meta'][$eventName] = $reader->readData();
186                                                unset($reader);
187
188                                                $ThisFileInfo['video']['frame_rate']   = @$ThisFileInfo['meta']['onMetaData']['framerate'];
189                                                $ThisFileInfo['video']['resolution_x'] = @$ThisFileInfo['meta']['onMetaData']['width'];
190                                                $ThisFileInfo['video']['resolution_y'] = @$ThisFileInfo['meta']['onMetaData']['height'];
191                                        }
192                                        break;
193
194                                default:
195                                        // noop
196                                        break;
197                        }
198
199                        fseek($fd, $NextOffset, SEEK_SET);
200                }
201
202                if ($ThisFileInfo['playtime_seconds'] = $Duration / 1000) {
203                    $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
204                }
205
206                if ($ThisFileInfo['flv']['header']['hasAudio']) {
207                        $ThisFileInfo['audio']['codec']           =   $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
208                        $ThisFileInfo['audio']['sample_rate']     =     $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
209                        $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
210
211                        $ThisFileInfo['audio']['channels']   = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
212                        $ThisFileInfo['audio']['lossless']   = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
213                        $ThisFileInfo['audio']['dataformat'] = 'flv';
214                }
215                if (@$ThisFileInfo['flv']['header']['hasVideo']) {
216                        $ThisFileInfo['video']['codec']      = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
217                        $ThisFileInfo['video']['dataformat'] = 'flv';
218                        $ThisFileInfo['video']['lossless']   = false;
219                }
220
221                return true;
222        }
223
224
225        function FLVaudioFormat($id) {
226                $FLVaudioFormat = array(
227                        0 => 'uncompressed',
228                        1 => 'ADPCM',
229                        2 => 'mp3',
230                        5 => 'Nellymoser 8kHz mono',
231                        6 => 'Nellymoser',
232                );
233                return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
234        }
235
236        function FLVaudioRate($id) {
237                $FLVaudioRate = array(
238                        0 =>  5500,
239                        1 => 11025,
240                        2 => 22050,
241                        3 => 44100,
242                );
243                return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
244        }
245
246        function FLVaudioBitDepth($id) {
247                $FLVaudioBitDepth = array(
248                        0 =>  8,
249                        1 => 16,
250                );
251                return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
252        }
253
254        function FLVvideoCodec($id) {
255                $FLVvideoCodec = array(
256                        GETID3_FLV_VIDEO_H263   => 'Sorenson H.263',
257                        GETID3_FLV_VIDEO_SCREEN => 'Screen video',
258                        GETID3_FLV_VIDEO_VP6    => 'On2 VP6',
259                );
260                return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false);
261        }
262}
263
264class AMFStream {
265        var $bytes;
266        var $pos;
267
268        function AMFStream(&$bytes) {
269                $this->bytes =& $bytes;
270                $this->pos = 0;
271        }
272
273        function readByte() {
274                return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
275        }
276
277        function readInt() {
278                return ($this->readByte() << 8) + $this->readByte();
279        }
280
281        function readLong() {
282                return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
283        }
284
285        function readDouble() {
286                return getid3_lib::BigEndian2Float($this->read(8));
287        }
288
289        function readUTF() {
290                $length = $this->readInt();
291                return $this->read($length);
292        }
293
294        function readLongUTF() {
295                $length = $this->readLong();
296                return $this->read($length);
297        }
298
299        function read($length) {
300                $val = substr($this->bytes, $this->pos, $length);
301                $this->pos += $length;
302                return $val;
303        }
304
305        function peekByte() {
306                $pos = $this->pos;
307                $val = $this->readByte();
308                $this->pos = $pos;
309                return $val;
310        }
311
312        function peekInt() {
313                $pos = $this->pos;
314                $val = $this->readInt();
315                $this->pos = $pos;
316                return $val;
317        }
318
319        function peekLong() {
320                $pos = $this->pos;
321                $val = $this->readLong();
322                $this->pos = $pos;
323                return $val;
324        }
325
326        function peekDouble() {
327                $pos = $this->pos;
328                $val = $this->readDouble();
329                $this->pos = $pos;
330                return $val;
331        }
332
333        function peekUTF() {
334                $pos = $this->pos;
335                $val = $this->readUTF();
336                $this->pos = $pos;
337                return $val;
338        }
339
340        function peekLongUTF() {
341                $pos = $this->pos;
342                $val = $this->readLongUTF();
343                $this->pos = $pos;
344                return $val;
345        }
346}
347
348class AMFReader {
349        var $stream;
350
351        function AMFReader(&$stream) {
352                $this->stream =& $stream;
353        }
354
355        function readData() {
356                $value = null;
357
358                $type = $this->stream->readByte();
359                switch ($type) {
360
361                        // Double
362                        case 0:
363                                $value = $this->readDouble();
364                        break;
365
366                        // Boolean
367                        case 1:
368                                $value = $this->readBoolean();
369                                break;
370
371                        // String
372                        case 2:
373                                $value = $this->readString();
374                                break;
375
376                        // Object
377                        case 3:
378                                $value = $this->readObject();
379                                break;
380
381                        // null
382                        case 6:
383                                return null;
384                                break;
385
386                        // Mixed array
387                        case 8:
388                                $value = $this->readMixedArray();
389                                break;
390
391                        // Array
392                        case 10:
393                                $value = $this->readArray();
394                                break;
395
396                        // Date
397                        case 11:
398                                $value = $this->readDate();
399                                break;
400
401                        // Long string
402                        case 13:
403                                $value = $this->readLongString();
404                                break;
405
406                        // XML (handled as string)
407                        case 15:
408                                $value = $this->readXML();
409                                break;
410
411                        // Typed object (handled as object)
412                        case 16:
413                                $value = $this->readTypedObject();
414                                break;
415
416                        // Long string
417                        default:
418                                $value = '(unknown or unsupported data type)';
419                        break;
420                }
421
422                return $value;
423        }
424
425        function readDouble() {
426                return $this->stream->readDouble();
427        }
428
429        function readBoolean() {
430                return $this->stream->readByte() == 1;
431        }
432
433        function readString() {
434                return $this->stream->readUTF();
435        }
436
437        function readObject() {
438                // Get highest numerical index - ignored
439//              $highestIndex = $this->stream->readLong();
440
441                $data = array();
442
443                while ($key = $this->stream->readUTF()) {
444                        $data[$key] = $this->readData();
445                }
446                // Mixed array record ends with empty string (0x00 0x00) and 0x09
447                if (($key == '') && ($this->stream->peekByte() == 0x09)) {
448                        // Consume byte
449                        $this->stream->readByte();
450                }
451                return $data;
452        }
453
454        function readMixedArray() {
455                // Get highest numerical index - ignored
456                $highestIndex = $this->stream->readLong();
457
458                $data = array();
459
460                while ($key = $this->stream->readUTF()) {
461                        if (is_numeric($key)) {
462                                $key = (float) $key;
463                        }
464                        $data[$key] = $this->readData();
465                }
466                // Mixed array record ends with empty string (0x00 0x00) and 0x09
467                if (($key == '') && ($this->stream->peekByte() == 0x09)) {
468                        // Consume byte
469                        $this->stream->readByte();
470                }
471
472                return $data;
473        }
474
475        function readArray() {
476                $length = $this->stream->readLong();
477                $data = array();
478
479                for ($i = 0; $i < $length; $i++) {
480                        $data[] = $this->readData();
481                }
482                return $data;
483        }
484
485        function readDate() {
486                $timestamp = $this->stream->readDouble();
487                $timezone = $this->stream->readInt();
488                return $timestamp;
489        }
490
491        function readLongString() {
492                return $this->stream->readLongUTF();
493        }
494
495        function readXML() {
496                return $this->stream->readLongUTF();
497        }
498
499        function readTypedObject() {
500                $className = $this->stream->readUTF();
501                return $this->readObject();
502        }
503}
504
505?>
Note: See TracBrowser for help on using the repository browser.