Diff: STRATO-apps/wordpress_03/app/wp-includes/ID3/module.audio-video.flv.php

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