[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ID3/ -> module.audio-video.flv.php (source)

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


Generated: Sun Jul 5 01:00:04 2020 Cross-referenced by PHPXref 0.7.1