source: spip-zone/_core_/plugins/medias/lib/getid3/module.audio-video.flv.php @ 113161

Last change on this file since 113161 was 113161, checked in by spip.franck@…, 5 months ago

Mise à jour de la lib getid en 1.9.16, nous étions en 1.9.14
https://github.com/JamesHeinrich/getID3/blob/master/changelog.txt

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