source: spip-zone/_plugins_/getID3/trunk/getid3/module.audio-video.flv.php @ 92618

Last change on this file since 92618 was 92618, checked in by brunobergot@…, 4 years ago

version 1.3.8 : maj de la lib getID3 en version 1.9.10 + maj copyright

  • Property svn:executable set to *
File size: 24.8 KB
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//          also https://github.com/JamesHeinrich/getID3       //
7//                                                             //
8//  FLV module by Seth Kaufman <sethØwhirl-i-gig*com>          //
9//                                                             //
10//  * version 0.1 (26 June 2005)                               //
11//                                                             //
12//                                                             //
13//  * version 0.1.1 (15 July 2005)                             //
14//  minor modifications by James Heinrich <info@getid3.org>    //
15//                                                             //
16//  * version 0.2 (22 February 2006)                           //
17//  Support for On2 VP6 codec and meta information             //
18//    by Steve Webster <steve.websterØfeaturecreep*com>        //
19//                                                             //
20//  * version 0.3 (15 June 2006)                               //
21//  Modified to not read entire file into memory               //
22//    by James Heinrich <info@getid3.org>                      //
23//                                                             //
24//  * version 0.4 (07 December 2007)                           //
25//  Bugfixes for incorrectly parsed FLV dimensions             //
26//    and incorrect parsing of onMetaTag                       //
27//    by Evgeny Moysevich <moysevichØgmail*com>                //
28//                                                             //
29//  * version 0.5 (21 May 2009)                                //
30//  Fixed parsing of audio tags and added additional codec     //
31//    details. The duration is now read from onMetaTag (if     //
32//    exists), rather than parsing whole file                  //
33//    by Nigel Barnes <ngbarnesØhotmail*com>                   //
34//                                                             //
35//  * version 0.6 (24 May 2009)                                //
36//  Better parsing of files with h264 video                    //
37//    by Evgeny Moysevich <moysevichØgmail*com>                //
38//                                                             //
39//  * version 0.6.1 (30 May 2011)                              //
40//    prevent infinite loops in expGolombUe()                  //
41//                                                             //
42//  * version 0.7.0 (16 Jul 2013)                              //
43//  handle GETID3_FLV_VIDEO_VP6FLV_ALPHA                       //
44//  improved AVCSequenceParameterSetReader::readData()         //
45//    by Xander Schouwerwou <schouwerwouØgmail*com>            //
46//                                                             //
47/////////////////////////////////////////////////////////////////
48//                                                             //
49// module.audio-video.flv.php                                  //
50// module for analyzing Shockwave Flash Video files            //
51// dependencies: NONE                                          //
52//                                                            ///
53/////////////////////////////////////////////////////////////////
54
55define('GETID3_FLV_TAG_AUDIO',          8);
56define('GETID3_FLV_TAG_VIDEO',          9);
57define('GETID3_FLV_TAG_META',          18);
58
59define('GETID3_FLV_VIDEO_H263',         2);
60define('GETID3_FLV_VIDEO_SCREEN',       3);
61define('GETID3_FLV_VIDEO_VP6FLV',       4);
62define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
63define('GETID3_FLV_VIDEO_SCREENV2',     6);
64define('GETID3_FLV_VIDEO_H264',         7);
65
66define('H264_AVC_SEQUENCE_HEADER',          0);
67define('H264_PROFILE_BASELINE',            66);
68define('H264_PROFILE_MAIN',                77);
69define('H264_PROFILE_EXTENDED',            88);
70define('H264_PROFILE_HIGH',               100);
71define('H264_PROFILE_HIGH10',             110);
72define('H264_PROFILE_HIGH422',            122);
73define('H264_PROFILE_HIGH444',            144);
74define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
75
76class getid3_flv extends getid3_handler {
77
78        const magic = 'FLV';
79
80        public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
81
82        public function Analyze() {
83                $info = &$this->getid3->info;
84
85                $this->fseek($info['avdataoffset']);
86
87                $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
88                $FLVheader = $this->fread(5);
89
90                $info['fileformat'] = 'flv';
91                $info['flv']['header']['signature'] =                           substr($FLVheader, 0, 3);
92                $info['flv']['header']['version']   = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
93                $TypeFlags                          = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
94
95                if ($info['flv']['header']['signature'] != self::magic) {
96                        $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"';
97                        unset($info['flv'], $info['fileformat']);
98                        return false;
99                }
100
101                $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
102                $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
103
104                $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4));
105                $FLVheaderFrameLength = 9;
106                if ($FrameSizeDataLength > $FLVheaderFrameLength) {
107                        $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
108                }
109                $Duration = 0;
110                $found_video = false;
111                $found_audio = false;
112                $found_meta  = false;
113                $found_valid_meta_playtime = false;
114                $tagParseCount = 0;
115                $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
116                $flv_framecount = &$info['flv']['framecount'];
117                while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime))  {
118                        $ThisTagHeader = $this->fread(16);
119
120                        $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  0, 4));
121                        $TagType           = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  4, 1));
122                        $DataLength        = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  5, 3));
123                        $Timestamp         = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  8, 3));
124                        $LastHeaderByte    = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
125                        $NextOffset = $this->ftell() - 1 + $DataLength;
126                        if ($Timestamp > $Duration) {
127                                $Duration = $Timestamp;
128                        }
129
130                        $flv_framecount['total']++;
131                        switch ($TagType) {
132                                case GETID3_FLV_TAG_AUDIO:
133                                        $flv_framecount['audio']++;
134                                        if (!$found_audio) {
135                                                $found_audio = true;
136                                                $info['flv']['audio']['audioFormat']     = ($LastHeaderByte >> 4) & 0x0F;
137                                                $info['flv']['audio']['audioRate']       = ($LastHeaderByte >> 2) & 0x03;
138                                                $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01;
139                                                $info['flv']['audio']['audioType']       =  $LastHeaderByte       & 0x01;
140                                        }
141                                        break;
142
143                                case GETID3_FLV_TAG_VIDEO:
144                                        $flv_framecount['video']++;
145                                        if (!$found_video) {
146                                                $found_video = true;
147                                                $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
148
149                                                $FLVvideoHeader = $this->fread(11);
150
151                                                if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
152                                                        // this code block contributed by: moysevichØgmail*com
153
154                                                        $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
155                                                        if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
156                                                                //      read AVCDecoderConfigurationRecord
157                                                                $configurationVersion       = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  4, 1));
158                                                                $AVCProfileIndication       = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  5, 1));
159                                                                $profile_compatibility      = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  6, 1));
160                                                                $lengthSizeMinusOne         = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  7, 1));
161                                                                $numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  8, 1));
162
163                                                                if (($numOfSequenceParameterSets & 0x1F) != 0) {
164                                                                        //      there is at least one SequenceParameterSet
165                                                                        //      read size of the first SequenceParameterSet
166                                                                        //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
167                                                                        $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
168                                                                        //      read the first SequenceParameterSet
169                                                                        $sps = $this->fread($spsSize);
170                                                                        if (strlen($sps) == $spsSize) { //      make sure that whole SequenceParameterSet was red
171                                                                                $spsReader = new AVCSequenceParameterSetReader($sps);
172                                                                                $spsReader->readData();
173                                                                                $info['video']['resolution_x'] = $spsReader->getWidth();
174                                                                                $info['video']['resolution_y'] = $spsReader->getHeight();
175                                                                        }
176                                                                }
177                                                        }
178                                                        // end: moysevichØgmail*com
179
180                                                } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
181
182                                                        $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
183                                                        $PictureSizeType = $PictureSizeType & 0x0007;
184                                                        $info['flv']['header']['videoSizeType'] = $PictureSizeType;
185                                                        switch ($PictureSizeType) {
186                                                                case 0:
187                                                                        //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
188                                                                        //$PictureSizeEnc <<= 1;
189                                                                        //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
190                                                                        //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
191                                                                        //$PictureSizeEnc <<= 1;
192                                                                        //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
193
194                                                                        $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7;
195                                                                        $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7;
196                                                                        $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
197                                                                        $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
198                                                                        break;
199
200                                                                case 1:
201                                                                        $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7;
202                                                                        $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7;
203                                                                        $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
204                                                                        $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
205                                                                        break;
206
207                                                                case 2:
208                                                                        $info['video']['resolution_x'] = 352;
209                                                                        $info['video']['resolution_y'] = 288;
210                                                                        break;
211
212                                                                case 3:
213                                                                        $info['video']['resolution_x'] = 176;
214                                                                        $info['video']['resolution_y'] = 144;
215                                                                        break;
216
217                                                                case 4:
218                                                                        $info['video']['resolution_x'] = 128;
219                                                                        $info['video']['resolution_y'] = 96;
220                                                                        break;
221
222                                                                case 5:
223                                                                        $info['video']['resolution_x'] = 320;
224                                                                        $info['video']['resolution_y'] = 240;
225                                                                        break;
226
227                                                                case 6:
228                                                                        $info['video']['resolution_x'] = 160;
229                                                                        $info['video']['resolution_y'] = 120;
230                                                                        break;
231
232                                                                default:
233                                                                        $info['video']['resolution_x'] = 0;
234                                                                        $info['video']['resolution_y'] = 0;
235                                                                        break;
236
237                                                        }
238
239                                                } elseif ($info['flv']['video']['videoCodec'] ==  GETID3_FLV_VIDEO_VP6FLV_ALPHA) {
240
241                                                        /* contributed by schouwerwouØgmail*com */
242                                                        if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set
243                                                                $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
244                                                                $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2));
245                                                                $info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3;
246                                                                $info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3;
247                                                        }
248                                                        /* end schouwerwouØgmail*com */
249
250                                                }
251                                                if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
252                                                        $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
253                                                }
254                                        }
255                                        break;
256
257                                // Meta tag
258                                case GETID3_FLV_TAG_META:
259                                        if (!$found_meta) {
260                                                $found_meta = true;
261                                                $this->fseek(-1, SEEK_CUR);
262                                                $datachunk = $this->fread($DataLength);
263                                                $AMFstream = new AMFStream($datachunk);
264                                                $reader = new AMFReader($AMFstream);
265                                                $eventName = $reader->readData();
266                                                $info['flv']['meta'][$eventName] = $reader->readData();
267                                                unset($reader);
268
269                                                $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate');
270                                                foreach ($copykeys as $sourcekey => $destkey) {
271                                                        if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
272                                                                switch ($sourcekey) {
273                                                                        case 'width':
274                                                                        case 'height':
275                                                                                $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
276                                                                                break;
277                                                                        case 'audiodatarate':
278                                                                                $info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
279                                                                                break;
280                                                                        case 'videodatarate':
281                                                                        case 'frame_rate':
282                                                                        default:
283                                                                                $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
284                                                                                break;
285                                                                }
286                                                        }
287                                                }
288                                                if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
289                                                        $found_valid_meta_playtime = true;
290                                                }
291                                        }
292                                        break;
293
294                                default:
295                                        // noop
296                                        break;
297                        }
298                        $this->fseek($NextOffset);
299                }
300
301                $info['playtime_seconds'] = $Duration / 1000;
302                if ($info['playtime_seconds'] > 0) {
303                        $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
304                }
305
306                if ($info['flv']['header']['hasAudio']) {
307                        $info['audio']['codec']           =   self::audioFormatLookup($info['flv']['audio']['audioFormat']);
308                        $info['audio']['sample_rate']     =     self::audioRateLookup($info['flv']['audio']['audioRate']);
309                        $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']);
310
311                        $info['audio']['channels']   =  $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
312                        $info['audio']['lossless']   = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
313                        $info['audio']['dataformat'] = 'flv';
314                }
315                if (!empty($info['flv']['header']['hasVideo'])) {
316                        $info['video']['codec']      = self::videoCodecLookup($info['flv']['video']['videoCodec']);
317                        $info['video']['dataformat'] = 'flv';
318                        $info['video']['lossless']   = false;
319                }
320
321                // Set information from meta
322                if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
323                        $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
324                        $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
325                }
326                if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
327                        $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']);
328                }
329                if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
330                        $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']);
331                }
332                return true;
333        }
334
335
336        public static function audioFormatLookup($id) {
337                static $lookup = array(
338                        0  => 'Linear PCM, platform endian',
339                        1  => 'ADPCM',
340                        2  => 'mp3',
341                        3  => 'Linear PCM, little endian',
342                        4  => 'Nellymoser 16kHz mono',
343                        5  => 'Nellymoser 8kHz mono',
344                        6  => 'Nellymoser',
345                        7  => 'G.711A-law logarithmic PCM',
346                        8  => 'G.711 mu-law logarithmic PCM',
347                        9  => 'reserved',
348                        10 => 'AAC',
349                        11 => 'Speex',
350                        12 => false, // unknown?
351                        13 => false, // unknown?
352                        14 => 'mp3 8kHz',
353                        15 => 'Device-specific sound',
354                );
355                return (isset($lookup[$id]) ? $lookup[$id] : false);
356        }
357
358        public static function audioRateLookup($id) {
359                static $lookup = array(
360                        0 =>  5500,
361                        1 => 11025,
362                        2 => 22050,
363                        3 => 44100,
364                );
365                return (isset($lookup[$id]) ? $lookup[$id] : false);
366        }
367
368        public static function audioBitDepthLookup($id) {
369                static $lookup = array(
370                        0 =>  8,
371                        1 => 16,
372                );
373                return (isset($lookup[$id]) ? $lookup[$id] : false);
374        }
375
376        public static function videoCodecLookup($id) {
377                static $lookup = array(
378                        GETID3_FLV_VIDEO_H263         => 'Sorenson H.263',
379                        GETID3_FLV_VIDEO_SCREEN       => 'Screen video',
380                        GETID3_FLV_VIDEO_VP6FLV       => 'On2 VP6',
381                        GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
382                        GETID3_FLV_VIDEO_SCREENV2     => 'Screen video v2',
383                        GETID3_FLV_VIDEO_H264         => 'Sorenson H.264',
384                );
385                return (isset($lookup[$id]) ? $lookup[$id] : false);
386        }
387}
388
389class AMFStream {
390        public $bytes;
391        public $pos;
392
393        public function __construct(&$bytes) {
394                $this->bytes =& $bytes;
395                $this->pos = 0;
396        }
397
398        public function readByte() {
399                return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
400        }
401
402        public function readInt() {
403                return ($this->readByte() << 8) + $this->readByte();
404        }
405
406        public function readLong() {
407                return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
408        }
409
410        public function readDouble() {
411                return getid3_lib::BigEndian2Float($this->read(8));
412        }
413
414        public function readUTF() {
415                $length = $this->readInt();
416                return $this->read($length);
417        }
418
419        public function readLongUTF() {
420                $length = $this->readLong();
421                return $this->read($length);
422        }
423
424        public function read($length) {
425                $val = substr($this->bytes, $this->pos, $length);
426                $this->pos += $length;
427                return $val;
428        }
429
430        public function peekByte() {
431                $pos = $this->pos;
432                $val = $this->readByte();
433                $this->pos = $pos;
434                return $val;
435        }
436
437        public function peekInt() {
438                $pos = $this->pos;
439                $val = $this->readInt();
440                $this->pos = $pos;
441                return $val;
442        }
443
444        public function peekLong() {
445                $pos = $this->pos;
446                $val = $this->readLong();
447                $this->pos = $pos;
448                return $val;
449        }
450
451        public function peekDouble() {
452                $pos = $this->pos;
453                $val = $this->readDouble();
454                $this->pos = $pos;
455                return $val;
456        }
457
458        public function peekUTF() {
459                $pos = $this->pos;
460                $val = $this->readUTF();
461                $this->pos = $pos;
462                return $val;
463        }
464
465        public function peekLongUTF() {
466                $pos = $this->pos;
467                $val = $this->readLongUTF();
468                $this->pos = $pos;
469                return $val;
470        }
471}
472
473class AMFReader {
474        public $stream;
475
476        public function __construct(&$stream) {
477                $this->stream =& $stream;
478        }
479
480        public function readData() {
481                $value = null;
482
483                $type = $this->stream->readByte();
484                switch ($type) {
485
486                        // Double
487                        case 0:
488                                $value = $this->readDouble();
489                        break;
490
491                        // Boolean
492                        case 1:
493                                $value = $this->readBoolean();
494                                break;
495
496                        // String
497                        case 2:
498                                $value = $this->readString();
499                                break;
500
501                        // Object
502                        case 3:
503                                $value = $this->readObject();
504                                break;
505
506                        // null
507                        case 6:
508                                return null;
509                                break;
510
511                        // Mixed array
512                        case 8:
513                                $value = $this->readMixedArray();
514                                break;
515
516                        // Array
517                        case 10:
518                                $value = $this->readArray();
519                                break;
520
521                        // Date
522                        case 11:
523                                $value = $this->readDate();
524                                break;
525
526                        // Long string
527                        case 13:
528                                $value = $this->readLongString();
529                                break;
530
531                        // XML (handled as string)
532                        case 15:
533                                $value = $this->readXML();
534                                break;
535
536                        // Typed object (handled as object)
537                        case 16:
538                                $value = $this->readTypedObject();
539                                break;
540
541                        // Long string
542                        default:
543                                $value = '(unknown or unsupported data type)';
544                                break;
545                }
546
547                return $value;
548        }
549
550        public function readDouble() {
551                return $this->stream->readDouble();
552        }
553
554        public function readBoolean() {
555                return $this->stream->readByte() == 1;
556        }
557
558        public function readString() {
559                return $this->stream->readUTF();
560        }
561
562        public function readObject() {
563                // Get highest numerical index - ignored
564//              $highestIndex = $this->stream->readLong();
565
566                $data = array();
567
568                while ($key = $this->stream->readUTF()) {
569                        $data[$key] = $this->readData();
570                }
571                // Mixed array record ends with empty string (0x00 0x00) and 0x09
572                if (($key == '') && ($this->stream->peekByte() == 0x09)) {
573                        // Consume byte
574                        $this->stream->readByte();
575                }
576                return $data;
577        }
578
579        public function readMixedArray() {
580                // Get highest numerical index - ignored
581                $highestIndex = $this->stream->readLong();
582
583                $data = array();
584
585                while ($key = $this->stream->readUTF()) {
586                        if (is_numeric($key)) {
587                                $key = (float) $key;
588                        }
589                        $data[$key] = $this->readData();
590                }
591                // Mixed array record ends with empty string (0x00 0x00) and 0x09
592                if (($key == '') && ($this->stream->peekByte() == 0x09)) {
593                        // Consume byte
594                        $this->stream->readByte();
595                }
596
597                return $data;
598        }
599
600        public function readArray() {
601                $length = $this->stream->readLong();
602                $data = array();
603
604                for ($i = 0; $i < $length; $i++) {
605                        $data[] = $this->readData();
606                }
607                return $data;
608        }
609
610        public function readDate() {
611                $timestamp = $this->stream->readDouble();
612                $timezone = $this->stream->readInt();
613                return $timestamp;
614        }
615
616        public function readLongString() {
617                return $this->stream->readLongUTF();
618        }
619
620        public function readXML() {
621                return $this->stream->readLongUTF();
622        }
623
624        public function readTypedObject() {
625                $className = $this->stream->readUTF();
626                return $this->readObject();
627        }
628}
629
630class AVCSequenceParameterSetReader {
631        public $sps;
632        public $start = 0;
633        public $currentBytes = 0;
634        public $currentBits = 0;
635        public $width;
636        public $height;
637
638        public function __construct($sps) {
639                $this->sps = $sps;
640        }
641
642        public function readData() {
643                $this->skipBits(8);
644                $this->skipBits(8);
645                $profile = $this->getBits(8);                               // read profile
646                if ($profile > 0) {
647                        $this->skipBits(8);
648                        $level_idc = $this->getBits(8);                         // level_idc
649                        $this->expGolombUe();                                   // seq_parameter_set_id // sps
650                        $this->expGolombUe();                                   // log2_max_frame_num_minus4
651                        $picOrderType = $this->expGolombUe();                   // pic_order_cnt_type
652                        if ($picOrderType == 0) {
653                                $this->expGolombUe();                               // log2_max_pic_order_cnt_lsb_minus4
654                        } elseif ($picOrderType == 1) {
655                                $this->skipBits(1);                                 // delta_pic_order_always_zero_flag
656                                $this->expGolombSe();                               // offset_for_non_ref_pic
657                                $this->expGolombSe();                               // offset_for_top_to_bottom_field
658                                $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle
659                                for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) {
660                                        $this->expGolombSe();                           // offset_for_ref_frame[ i ]
661                                }
662                        }
663                        $this->expGolombUe();                                   // num_ref_frames
664                        $this->skipBits(1);                                     // gaps_in_frame_num_value_allowed_flag
665                        $pic_width_in_mbs_minus1 = $this->expGolombUe();        // pic_width_in_mbs_minus1
666                        $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1
667
668                        $frame_mbs_only_flag = $this->getBits(1);               // frame_mbs_only_flag
669                        if ($frame_mbs_only_flag == 0) {
670                                $this->skipBits(1);                                 // mb_adaptive_frame_field_flag
671                        }
672                        $this->skipBits(1);                                     // direct_8x8_inference_flag
673                        $frame_cropping_flag = $this->getBits(1);               // frame_cropping_flag
674
675                        $frame_crop_left_offset   = 0;
676                        $frame_crop_right_offset  = 0;
677                        $frame_crop_top_offset    = 0;
678                        $frame_crop_bottom_offset = 0;
679
680                        if ($frame_cropping_flag) {
681                                $frame_crop_left_offset   = $this->expGolombUe();   // frame_crop_left_offset
682                                $frame_crop_right_offset  = $this->expGolombUe();   // frame_crop_right_offset
683                                $frame_crop_top_offset    = $this->expGolombUe();   // frame_crop_top_offset
684                                $frame_crop_bottom_offset = $this->expGolombUe();   // frame_crop_bottom_offset
685                        }
686                        $this->skipBits(1);                                     // vui_parameters_present_flag
687                        // etc
688
689                        $this->width  = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2);
690                        $this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2);
691                }
692        }
693
694        public function skipBits($bits) {
695                $newBits = $this->currentBits + $bits;
696                $this->currentBytes += (int)floor($newBits / 8);
697                $this->currentBits = $newBits % 8;
698        }
699
700        public function getBit() {
701                $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
702                $this->skipBits(1);
703                return $result;
704        }
705
706        public function getBits($bits) {
707                $result = 0;
708                for ($i = 0; $i < $bits; $i++) {
709                        $result = ($result << 1) + $this->getBit();
710                }
711                return $result;
712        }
713
714        public function expGolombUe() {
715                $significantBits = 0;
716                $bit = $this->getBit();
717                while ($bit == 0) {
718                        $significantBits++;
719                        $bit = $this->getBit();
720
721                        if ($significantBits > 31) {
722                                // something is broken, this is an emergency escape to prevent infinite loops
723                                return 0;
724                        }
725                }
726                return (1 << $significantBits) + $this->getBits($significantBits) - 1;
727        }
728
729        public function expGolombSe() {
730                $result = $this->expGolombUe();
731                if (($result & 0x01) == 0) {
732                        return -($result >> 1);
733                } else {
734                        return ($result + 1) >> 1;
735                }
736        }
737
738        public function getWidth() {
739                return $this->width;
740        }
741
742        public function getHeight() {
743                return $this->height;
744        }
745}
Note: See TracBrowser for help on using the repository browser.