[ 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                          $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  }


Generated: Sun May 22 01:00:03 2022 Cross-referenced by PHPXref 0.7.1