[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Dec 24 01:00:02 2024 | Cross-referenced by PHPXref 0.7.1 |