run:R W Run
54.21 KB
2026-03-11 16:18:51
R W Run
79.05 KB
2026-03-11 16:18:51
R W Run
1.36 KB
2026-03-11 16:18:51
R W Run
133.61 KB
2026-03-11 16:18:51
R W Run
26.5 KB
2026-03-11 16:18:51
R W Run
104.64 KB
2026-03-11 16:18:51
R W Run
164.97 KB
2026-03-11 16:18:51
R W Run
136.53 KB
2026-03-11 16:18:51
R W Run
38.46 KB
2026-03-11 16:18:51
R W Run
10.63 KB
2026-03-11 16:18:51
R W Run
19.23 KB
2026-03-11 16:18:51
R W Run
104.5 KB
2026-03-11 16:18:51
R W Run
42.74 KB
2026-03-11 16:18:51
R W Run
18.63 KB
2026-03-11 16:18:51
R W Run
14.7 KB
2026-03-11 16:18:51
R W Run
151.2 KB
2026-03-11 16:18:51
R W Run
11.75 KB
2026-03-11 16:18:51
R W Run
25.71 KB
2026-03-11 16:18:51
R W Run
error_log
📄module.audio-video.flv.php
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
56if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
57 exit;
58}
59
60define('GETID3_FLV_TAG_AUDIO', 8);
61define('GETID3_FLV_TAG_VIDEO', 9);
62define('GETID3_FLV_TAG_META', 18);
63
64define('GETID3_FLV_VIDEO_H263', 2);
65define('GETID3_FLV_VIDEO_SCREEN', 3);
66define('GETID3_FLV_VIDEO_VP6FLV', 4);
67define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
68define('GETID3_FLV_VIDEO_SCREENV2', 6);
69define('GETID3_FLV_VIDEO_H264', 7);
70
71define('H264_AVC_SEQUENCE_HEADER', 0);
72define('H264_PROFILE_BASELINE', 66);
73define('H264_PROFILE_MAIN', 77);
74define('H264_PROFILE_EXTENDED', 88);
75define('H264_PROFILE_HIGH', 100);
76define('H264_PROFILE_HIGH10', 110);
77define('H264_PROFILE_HIGH422', 122);
78define('H264_PROFILE_HIGH444', 144);
79define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
80
81class 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
423class 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
559class 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
757class 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