File indexing completed on 2025-02-23 05:28:41
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 // // 0008 // FLV module by Seth Kaufman <sethØwhirl-i-gig*com> // 0009 // // 0010 // * version 0.1 (26 June 2005) // 0011 // // 0012 // // 0013 // * version 0.1.1 (15 July 2005) // 0014 // minor modifications by James Heinrich <info@getid3.org> // 0015 // // 0016 // * version 0.2 (22 February 2006) // 0017 // Support for On2 VP6 codec and meta information // 0018 // by Steve Webster <steve.websterØfeaturecreep*com> // 0019 // // 0020 // * version 0.3 (15 June 2006) // 0021 // Modified to not read entire file into memory // 0022 // by James Heinrich <info@getid3.org> // 0023 // // 0024 // * version 0.4 (07 December 2007) // 0025 // Bugfixes for incorrectly parsed FLV dimensions // 0026 // and incorrect parsing of onMetaTag // 0027 // by Evgeny Moysevich <moysevichØgmail*com> // 0028 // // 0029 // * version 0.5 (21 May 2009) // 0030 // Fixed parsing of audio tags and added additional codec // 0031 // details. The duration is now read from onMetaTag (if // 0032 // exists), rather than parsing whole file // 0033 // by Nigel Barnes <ngbarnesØhotmail*com> // 0034 // // 0035 // * version 0.6 (24 May 2009) // 0036 // Better parsing of files with h264 video // 0037 // by Evgeny Moysevich <moysevichØgmail*com> // 0038 // // 0039 // * version 0.6.1 (30 May 2011) // 0040 // prevent infinite loops in expGolombUe() // 0041 // // 0042 // * version 0.7.0 (16 Jul 2013) // 0043 // handle GETID3_FLV_VIDEO_VP6FLV_ALPHA // 0044 // improved AVCSequenceParameterSetReader::readData() // 0045 // by Xander Schouwerwou <schouwerwouØgmail*com> // 0046 // // 0047 ///////////////////////////////////////////////////////////////// 0048 // // 0049 // module.audio-video.flv.php // 0050 // module for analyzing Shockwave Flash Video files // 0051 // dependencies: NONE // 0052 // /// 0053 ///////////////////////////////////////////////////////////////// 0054 0055 define('GETID3_FLV_TAG_AUDIO', 8); 0056 define('GETID3_FLV_TAG_VIDEO', 9); 0057 define('GETID3_FLV_TAG_META', 18); 0058 0059 define('GETID3_FLV_VIDEO_H263', 2); 0060 define('GETID3_FLV_VIDEO_SCREEN', 3); 0061 define('GETID3_FLV_VIDEO_VP6FLV', 4); 0062 define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5); 0063 define('GETID3_FLV_VIDEO_SCREENV2', 6); 0064 define('GETID3_FLV_VIDEO_H264', 7); 0065 0066 define('H264_AVC_SEQUENCE_HEADER', 0); 0067 define('H264_PROFILE_BASELINE', 66); 0068 define('H264_PROFILE_MAIN', 77); 0069 define('H264_PROFILE_EXTENDED', 88); 0070 define('H264_PROFILE_HIGH', 100); 0071 define('H264_PROFILE_HIGH10', 110); 0072 define('H264_PROFILE_HIGH422', 122); 0073 define('H264_PROFILE_HIGH444', 144); 0074 define('H264_PROFILE_HIGH444_PREDICTIVE', 244); 0075 0076 class getid3_flv extends getid3_handler { 0077 0078 const magic = 'FLV'; 0079 0080 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 0081 0082 public function Analyze() { 0083 $info = &$this->getid3->info; 0084 0085 $this->fseek($info['avdataoffset']); 0086 0087 $FLVdataLength = $info['avdataend'] - $info['avdataoffset']; 0088 $FLVheader = $this->fread(5); 0089 0090 $info['fileformat'] = 'flv'; 0091 $info['flv']['header']['signature'] = substr($FLVheader, 0, 3); 0092 $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); 0093 $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); 0094 0095 if ($info['flv']['header']['signature'] != self::magic) { 0096 $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; 0097 unset($info['flv'], $info['fileformat']); 0098 return false; 0099 } 0100 0101 $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); 0102 $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); 0103 0104 $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4)); 0105 $FLVheaderFrameLength = 9; 0106 if ($FrameSizeDataLength > $FLVheaderFrameLength) { 0107 $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); 0108 } 0109 $Duration = 0; 0110 $found_video = false; 0111 $found_audio = false; 0112 $found_meta = false; 0113 $found_valid_meta_playtime = false; 0114 $tagParseCount = 0; 0115 $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0); 0116 $flv_framecount = &$info['flv']['framecount']; 0117 while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) { 0118 $ThisTagHeader = $this->fread(16); 0119 0120 $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); 0121 $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); 0122 $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); 0123 $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); 0124 $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); 0125 $NextOffset = $this->ftell() - 1 + $DataLength; 0126 if ($Timestamp > $Duration) { 0127 $Duration = $Timestamp; 0128 } 0129 0130 $flv_framecount['total']++; 0131 switch ($TagType) { 0132 case GETID3_FLV_TAG_AUDIO: 0133 $flv_framecount['audio']++; 0134 if (!$found_audio) { 0135 $found_audio = true; 0136 $info['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F; 0137 $info['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03; 0138 $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01; 0139 $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x01; 0140 } 0141 break; 0142 0143 case GETID3_FLV_TAG_VIDEO: 0144 $flv_framecount['video']++; 0145 if (!$found_video) { 0146 $found_video = true; 0147 $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; 0148 0149 $FLVvideoHeader = $this->fread(11); 0150 0151 if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) { 0152 // this code block contributed by: moysevichØgmail*com 0153 0154 $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1)); 0155 if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) { 0156 // read AVCDecoderConfigurationRecord 0157 $configurationVersion = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 1)); 0158 $AVCProfileIndication = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 1)); 0159 $profile_compatibility = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 1)); 0160 $lengthSizeMinusOne = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 1)); 0161 $numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 8, 1)); 0162 0163 if (($numOfSequenceParameterSets & 0x1F) != 0) { 0164 // there is at least one SequenceParameterSet 0165 // read size of the first SequenceParameterSet 0166 //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); 0167 $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); 0168 // read the first SequenceParameterSet 0169 $sps = $this->fread($spsSize); 0170 if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red 0171 $spsReader = new AVCSequenceParameterSetReader($sps); 0172 $spsReader->readData(); 0173 $info['video']['resolution_x'] = $spsReader->getWidth(); 0174 $info['video']['resolution_y'] = $spsReader->getHeight(); 0175 } 0176 } 0177 } 0178 // end: moysevichØgmail*com 0179 0180 } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) { 0181 0182 $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7; 0183 $PictureSizeType = $PictureSizeType & 0x0007; 0184 $info['flv']['header']['videoSizeType'] = $PictureSizeType; 0185 switch ($PictureSizeType) { 0186 case 0: 0187 //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); 0188 //$PictureSizeEnc <<= 1; 0189 //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; 0190 //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); 0191 //$PictureSizeEnc <<= 1; 0192 //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; 0193 0194 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7; 0195 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7; 0196 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; 0197 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; 0198 break; 0199 0200 case 1: 0201 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7; 0202 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7; 0203 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; 0204 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; 0205 break; 0206 0207 case 2: 0208 $info['video']['resolution_x'] = 352; 0209 $info['video']['resolution_y'] = 288; 0210 break; 0211 0212 case 3: 0213 $info['video']['resolution_x'] = 176; 0214 $info['video']['resolution_y'] = 144; 0215 break; 0216 0217 case 4: 0218 $info['video']['resolution_x'] = 128; 0219 $info['video']['resolution_y'] = 96; 0220 break; 0221 0222 case 5: 0223 $info['video']['resolution_x'] = 320; 0224 $info['video']['resolution_y'] = 240; 0225 break; 0226 0227 case 6: 0228 $info['video']['resolution_x'] = 160; 0229 $info['video']['resolution_y'] = 120; 0230 break; 0231 0232 default: 0233 $info['video']['resolution_x'] = 0; 0234 $info['video']['resolution_y'] = 0; 0235 break; 0236 0237 } 0238 0239 } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_VP6FLV_ALPHA) { 0240 0241 /* contributed by schouwerwouØgmail*com */ 0242 if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set 0243 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); 0244 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2)); 0245 $info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3; 0246 $info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3; 0247 } 0248 /* end schouwerwouØgmail*com */ 0249 0250 } 0251 if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) { 0252 $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y']; 0253 } 0254 } 0255 break; 0256 0257 // Meta tag 0258 case GETID3_FLV_TAG_META: 0259 if (!$found_meta) { 0260 $found_meta = true; 0261 $this->fseek(-1, SEEK_CUR); 0262 $datachunk = $this->fread($DataLength); 0263 $AMFstream = new AMFStream($datachunk); 0264 $reader = new AMFReader($AMFstream); 0265 $eventName = $reader->readData(); 0266 $info['flv']['meta'][$eventName] = $reader->readData(); 0267 unset($reader); 0268 0269 $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate'); 0270 foreach ($copykeys as $sourcekey => $destkey) { 0271 if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) { 0272 switch ($sourcekey) { 0273 case 'width': 0274 case 'height': 0275 $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey])); 0276 break; 0277 case 'audiodatarate': 0278 $info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000)); 0279 break; 0280 case 'videodatarate': 0281 case 'frame_rate': 0282 default: 0283 $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey]; 0284 break; 0285 } 0286 } 0287 } 0288 if (!empty($info['flv']['meta']['onMetaData']['duration'])) { 0289 $found_valid_meta_playtime = true; 0290 } 0291 } 0292 break; 0293 0294 default: 0295 // noop 0296 break; 0297 } 0298 $this->fseek($NextOffset); 0299 } 0300 0301 $info['playtime_seconds'] = $Duration / 1000; 0302 if ($info['playtime_seconds'] > 0) { 0303 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; 0304 } 0305 0306 if ($info['flv']['header']['hasAudio']) { 0307 $info['audio']['codec'] = self::audioFormatLookup($info['flv']['audio']['audioFormat']); 0308 $info['audio']['sample_rate'] = self::audioRateLookup($info['flv']['audio']['audioRate']); 0309 $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']); 0310 0311 $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo 0312 $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed 0313 $info['audio']['dataformat'] = 'flv'; 0314 } 0315 if (!empty($info['flv']['header']['hasVideo'])) { 0316 $info['video']['codec'] = self::videoCodecLookup($info['flv']['video']['videoCodec']); 0317 $info['video']['dataformat'] = 'flv'; 0318 $info['video']['lossless'] = false; 0319 } 0320 0321 // Set information from meta 0322 if (!empty($info['flv']['meta']['onMetaData']['duration'])) { 0323 $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration']; 0324 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; 0325 } 0326 if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { 0327 $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']); 0328 } 0329 if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { 0330 $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']); 0331 } 0332 return true; 0333 } 0334 0335 0336 public static function audioFormatLookup($id) { 0337 static $lookup = array( 0338 0 => 'Linear PCM, platform endian', 0339 1 => 'ADPCM', 0340 2 => 'mp3', 0341 3 => 'Linear PCM, little endian', 0342 4 => 'Nellymoser 16kHz mono', 0343 5 => 'Nellymoser 8kHz mono', 0344 6 => 'Nellymoser', 0345 7 => 'G.711A-law logarithmic PCM', 0346 8 => 'G.711 mu-law logarithmic PCM', 0347 9 => 'reserved', 0348 10 => 'AAC', 0349 11 => 'Speex', 0350 12 => false, // unknown? 0351 13 => false, // unknown? 0352 14 => 'mp3 8kHz', 0353 15 => 'Device-specific sound', 0354 ); 0355 return (isset($lookup[$id]) ? $lookup[$id] : false); 0356 } 0357 0358 public static function audioRateLookup($id) { 0359 static $lookup = array( 0360 0 => 5500, 0361 1 => 11025, 0362 2 => 22050, 0363 3 => 44100, 0364 ); 0365 return (isset($lookup[$id]) ? $lookup[$id] : false); 0366 } 0367 0368 public static function audioBitDepthLookup($id) { 0369 static $lookup = array( 0370 0 => 8, 0371 1 => 16, 0372 ); 0373 return (isset($lookup[$id]) ? $lookup[$id] : false); 0374 } 0375 0376 public static function videoCodecLookup($id) { 0377 static $lookup = array( 0378 GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', 0379 GETID3_FLV_VIDEO_SCREEN => 'Screen video', 0380 GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', 0381 GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel', 0382 GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2', 0383 GETID3_FLV_VIDEO_H264 => 'Sorenson H.264', 0384 ); 0385 return (isset($lookup[$id]) ? $lookup[$id] : false); 0386 } 0387 } 0388 0389 class AMFStream { 0390 public $bytes; 0391 public $pos; 0392 0393 public function __construct(&$bytes) { 0394 $this->bytes =& $bytes; 0395 $this->pos = 0; 0396 } 0397 0398 public function readByte() { 0399 return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); 0400 } 0401 0402 public function readInt() { 0403 return ($this->readByte() << 8) + $this->readByte(); 0404 } 0405 0406 public function readLong() { 0407 return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); 0408 } 0409 0410 public function readDouble() { 0411 return getid3_lib::BigEndian2Float($this->read(8)); 0412 } 0413 0414 public function readUTF() { 0415 $length = $this->readInt(); 0416 return $this->read($length); 0417 } 0418 0419 public function readLongUTF() { 0420 $length = $this->readLong(); 0421 return $this->read($length); 0422 } 0423 0424 public function read($length) { 0425 $val = substr($this->bytes, $this->pos, $length); 0426 $this->pos += $length; 0427 return $val; 0428 } 0429 0430 public function peekByte() { 0431 $pos = $this->pos; 0432 $val = $this->readByte(); 0433 $this->pos = $pos; 0434 return $val; 0435 } 0436 0437 public function peekInt() { 0438 $pos = $this->pos; 0439 $val = $this->readInt(); 0440 $this->pos = $pos; 0441 return $val; 0442 } 0443 0444 public function peekLong() { 0445 $pos = $this->pos; 0446 $val = $this->readLong(); 0447 $this->pos = $pos; 0448 return $val; 0449 } 0450 0451 public function peekDouble() { 0452 $pos = $this->pos; 0453 $val = $this->readDouble(); 0454 $this->pos = $pos; 0455 return $val; 0456 } 0457 0458 public function peekUTF() { 0459 $pos = $this->pos; 0460 $val = $this->readUTF(); 0461 $this->pos = $pos; 0462 return $val; 0463 } 0464 0465 public function peekLongUTF() { 0466 $pos = $this->pos; 0467 $val = $this->readLongUTF(); 0468 $this->pos = $pos; 0469 return $val; 0470 } 0471 } 0472 0473 class AMFReader { 0474 public $stream; 0475 0476 public function __construct(&$stream) { 0477 $this->stream =& $stream; 0478 } 0479 0480 public function readData() { 0481 $value = null; 0482 0483 $type = $this->stream->readByte(); 0484 switch ($type) { 0485 0486 // Double 0487 case 0: 0488 $value = $this->readDouble(); 0489 break; 0490 0491 // Boolean 0492 case 1: 0493 $value = $this->readBoolean(); 0494 break; 0495 0496 // String 0497 case 2: 0498 $value = $this->readString(); 0499 break; 0500 0501 // Object 0502 case 3: 0503 $value = $this->readObject(); 0504 break; 0505 0506 // null 0507 case 6: 0508 return null; 0509 break; 0510 0511 // Mixed array 0512 case 8: 0513 $value = $this->readMixedArray(); 0514 break; 0515 0516 // Array 0517 case 10: 0518 $value = $this->readArray(); 0519 break; 0520 0521 // Date 0522 case 11: 0523 $value = $this->readDate(); 0524 break; 0525 0526 // Long string 0527 case 13: 0528 $value = $this->readLongString(); 0529 break; 0530 0531 // XML (handled as string) 0532 case 15: 0533 $value = $this->readXML(); 0534 break; 0535 0536 // Typed object (handled as object) 0537 case 16: 0538 $value = $this->readTypedObject(); 0539 break; 0540 0541 // Long string 0542 default: 0543 $value = '(unknown or unsupported data type)'; 0544 break; 0545 } 0546 0547 return $value; 0548 } 0549 0550 public function readDouble() { 0551 return $this->stream->readDouble(); 0552 } 0553 0554 public function readBoolean() { 0555 return $this->stream->readByte() == 1; 0556 } 0557 0558 public function readString() { 0559 return $this->stream->readUTF(); 0560 } 0561 0562 public function readObject() { 0563 // Get highest numerical index - ignored 0564 // $highestIndex = $this->stream->readLong(); 0565 0566 $data = array(); 0567 0568 while ($key = $this->stream->readUTF()) { 0569 $data[$key] = $this->readData(); 0570 } 0571 // Mixed array record ends with empty string (0x00 0x00) and 0x09 0572 if (($key == '') && ($this->stream->peekByte() == 0x09)) { 0573 // Consume byte 0574 $this->stream->readByte(); 0575 } 0576 return $data; 0577 } 0578 0579 public function readMixedArray() { 0580 // Get highest numerical index - ignored 0581 $highestIndex = $this->stream->readLong(); 0582 0583 $data = array(); 0584 0585 while ($key = $this->stream->readUTF()) { 0586 if (is_numeric($key)) { 0587 $key = (float) $key; 0588 } 0589 $data[$key] = $this->readData(); 0590 } 0591 // Mixed array record ends with empty string (0x00 0x00) and 0x09 0592 if (($key == '') && ($this->stream->peekByte() == 0x09)) { 0593 // Consume byte 0594 $this->stream->readByte(); 0595 } 0596 0597 return $data; 0598 } 0599 0600 public function readArray() { 0601 $length = $this->stream->readLong(); 0602 $data = array(); 0603 0604 for ($i = 0; $i < $length; $i++) { 0605 $data[] = $this->readData(); 0606 } 0607 return $data; 0608 } 0609 0610 public function readDate() { 0611 $timestamp = $this->stream->readDouble(); 0612 $timezone = $this->stream->readInt(); 0613 return $timestamp; 0614 } 0615 0616 public function readLongString() { 0617 return $this->stream->readLongUTF(); 0618 } 0619 0620 public function readXML() { 0621 return $this->stream->readLongUTF(); 0622 } 0623 0624 public function readTypedObject() { 0625 $className = $this->stream->readUTF(); 0626 return $this->readObject(); 0627 } 0628 } 0629 0630 class AVCSequenceParameterSetReader { 0631 public $sps; 0632 public $start = 0; 0633 public $currentBytes = 0; 0634 public $currentBits = 0; 0635 public $width; 0636 public $height; 0637 0638 public function __construct($sps) { 0639 $this->sps = $sps; 0640 } 0641 0642 public function readData() { 0643 $this->skipBits(8); 0644 $this->skipBits(8); 0645 $profile = $this->getBits(8); // read profile 0646 if ($profile > 0) { 0647 $this->skipBits(8); 0648 $level_idc = $this->getBits(8); // level_idc 0649 $this->expGolombUe(); // seq_parameter_set_id // sps 0650 $this->expGolombUe(); // log2_max_frame_num_minus4 0651 $picOrderType = $this->expGolombUe(); // pic_order_cnt_type 0652 if ($picOrderType == 0) { 0653 $this->expGolombUe(); // log2_max_pic_order_cnt_lsb_minus4 0654 } elseif ($picOrderType == 1) { 0655 $this->skipBits(1); // delta_pic_order_always_zero_flag 0656 $this->expGolombSe(); // offset_for_non_ref_pic 0657 $this->expGolombSe(); // offset_for_top_to_bottom_field 0658 $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle 0659 for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) { 0660 $this->expGolombSe(); // offset_for_ref_frame[ i ] 0661 } 0662 } 0663 $this->expGolombUe(); // num_ref_frames 0664 $this->skipBits(1); // gaps_in_frame_num_value_allowed_flag 0665 $pic_width_in_mbs_minus1 = $this->expGolombUe(); // pic_width_in_mbs_minus1 0666 $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1 0667 0668 $frame_mbs_only_flag = $this->getBits(1); // frame_mbs_only_flag 0669 if ($frame_mbs_only_flag == 0) { 0670 $this->skipBits(1); // mb_adaptive_frame_field_flag 0671 } 0672 $this->skipBits(1); // direct_8x8_inference_flag 0673 $frame_cropping_flag = $this->getBits(1); // frame_cropping_flag 0674 0675 $frame_crop_left_offset = 0; 0676 $frame_crop_right_offset = 0; 0677 $frame_crop_top_offset = 0; 0678 $frame_crop_bottom_offset = 0; 0679 0680 if ($frame_cropping_flag) { 0681 $frame_crop_left_offset = $this->expGolombUe(); // frame_crop_left_offset 0682 $frame_crop_right_offset = $this->expGolombUe(); // frame_crop_right_offset 0683 $frame_crop_top_offset = $this->expGolombUe(); // frame_crop_top_offset 0684 $frame_crop_bottom_offset = $this->expGolombUe(); // frame_crop_bottom_offset 0685 } 0686 $this->skipBits(1); // vui_parameters_present_flag 0687 // etc 0688 0689 $this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2); 0690 $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); 0691 } 0692 } 0693 0694 public function skipBits($bits) { 0695 $newBits = $this->currentBits + $bits; 0696 $this->currentBytes += (int)floor($newBits / 8); 0697 $this->currentBits = $newBits % 8; 0698 } 0699 0700 public function getBit() { 0701 $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01; 0702 $this->skipBits(1); 0703 return $result; 0704 } 0705 0706 public function getBits($bits) { 0707 $result = 0; 0708 for ($i = 0; $i < $bits; $i++) { 0709 $result = ($result << 1) + $this->getBit(); 0710 } 0711 return $result; 0712 } 0713 0714 public function expGolombUe() { 0715 $significantBits = 0; 0716 $bit = $this->getBit(); 0717 while ($bit == 0) { 0718 $significantBits++; 0719 $bit = $this->getBit(); 0720 0721 if ($significantBits > 31) { 0722 // something is broken, this is an emergency escape to prevent infinite loops 0723 return 0; 0724 } 0725 } 0726 return (1 << $significantBits) + $this->getBits($significantBits) - 1; 0727 } 0728 0729 public function expGolombSe() { 0730 $result = $this->expGolombUe(); 0731 if (($result & 0x01) == 0) { 0732 return -($result >> 1); 0733 } else { 0734 return ($result + 1) >> 1; 0735 } 0736 } 0737 0738 public function getWidth() { 0739 return $this->width; 0740 } 0741 0742 public function getHeight() { 0743 return $this->height; 0744 } 0745 }