~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-includes/ID3/module.audio-video.flv.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

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