[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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

   1  <?php
   2  
   3  /////////////////////////////////////////////////////////////////
   4  /// getID3() by James Heinrich <info@getid3.org>               //
   5  //  available at https://github.com/JamesHeinrich/getID3       //
   6  //            or https://www.getid3.org                        //
   7  //            or http://getid3.sourceforge.net                 //
   8  //  see readme.txt for more details                            //
   9  /////////////////////////////////////////////////////////////////
  10  //                                                             //
  11  // module.audio.ogg.php                                        //
  12  // module for analyzing Ogg Vorbis, OggFLAC and Speex files    //
  13  // dependencies: module.audio.flac.php                         //
  14  //                                                            ///
  15  /////////////////////////////////////////////////////////////////
  16  
  17  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
  18  
  19  class getid3_ogg extends getid3_handler
  20  {
  21      /**
  22       * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html
  23       *
  24       * @return bool
  25       */
  26  	public function Analyze() {
  27          $info = &$this->getid3->info;
  28  
  29          $info['fileformat'] = 'ogg';
  30  
  31          // Warn about illegal tags - only vorbiscomments are allowed
  32          if (isset($info['id3v2'])) {
  33              $this->warning('Illegal ID3v2 tag present.');
  34          }
  35          if (isset($info['id3v1'])) {
  36              $this->warning('Illegal ID3v1 tag present.');
  37          }
  38          if (isset($info['ape'])) {
  39              $this->warning('Illegal APE tag present.');
  40          }
  41  
  42  
  43          // Page 1 - Stream Header
  44  
  45          $this->fseek($info['avdataoffset']);
  46  
  47          $oggpageinfo = $this->ParseOggPageHeader();
  48          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
  49  
  50          if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
  51              $this->error('Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)');
  52              unset($info['fileformat']);
  53              unset($info['ogg']);
  54              return false;
  55          }
  56  
  57          $filedata = $this->fread($oggpageinfo['page_length']);
  58          $filedataoffset = 0;
  59  
  60          if (substr($filedata, 0, 4) == 'fLaC') {
  61  
  62              $info['audio']['dataformat']   = 'flac';
  63              $info['audio']['bitrate_mode'] = 'vbr';
  64              $info['audio']['lossless']     = true;
  65  
  66          } elseif (substr($filedata, 1, 6) == 'vorbis') {
  67  
  68              $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
  69  
  70          } elseif (substr($filedata, 0, 8) == 'OpusHead') {
  71  
  72              if ($this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) === false) {
  73                  return false;
  74              }
  75  
  76          } elseif (substr($filedata, 0, 8) == 'Speex   ') {
  77  
  78              // http://www.speex.org/manual/node10.html
  79  
  80              $info['audio']['dataformat']   = 'speex';
  81              $info['mime_type']             = 'audio/speex';
  82              $info['audio']['bitrate_mode'] = 'abr';
  83              $info['audio']['lossless']     = false;
  84  
  85              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string']           =                              substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex   '
  86              $filedataoffset += 8;
  87              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']          =                              substr($filedata, $filedataoffset, 20);
  88              $filedataoffset += 20;
  89              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  90              $filedataoffset += 4;
  91              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  92              $filedataoffset += 4;
  93              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  94              $filedataoffset += 4;
  95              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  96              $filedataoffset += 4;
  97              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  98              $filedataoffset += 4;
  99              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 100              $filedataoffset += 4;
 101              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 102              $filedataoffset += 4;
 103              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 104              $filedataoffset += 4;
 105              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']                    = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 106              $filedataoffset += 4;
 107              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 108              $filedataoffset += 4;
 109              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers']          = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 110              $filedataoffset += 4;
 111              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 112              $filedataoffset += 4;
 113              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 114              $filedataoffset += 4;
 115  
 116              $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
 117              $info['speex']['sample_rate']   = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
 118              $info['speex']['channels']      = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
 119              $info['speex']['vbr']           = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
 120              $info['speex']['band_type']     = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
 121  
 122              $info['audio']['sample_rate']   = $info['speex']['sample_rate'];
 123              $info['audio']['channels']      = $info['speex']['channels'];
 124              if ($info['speex']['vbr']) {
 125                  $info['audio']['bitrate_mode'] = 'vbr';
 126              }
 127  
 128          } elseif (substr($filedata, 0, 7) == "\x80".'theora') {
 129  
 130              // http://www.theora.org/doc/Theora.pdf (section 6.2)
 131  
 132              $info['ogg']['pageheader']['theora']['theora_magic']             =                           substr($filedata, $filedataoffset,  7); // hard-coded to "\x80.'theora'
 133              $filedataoffset += 7;
 134              $info['ogg']['pageheader']['theora']['version_major']            = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  1));
 135              $filedataoffset += 1;
 136              $info['ogg']['pageheader']['theora']['version_minor']            = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  1));
 137              $filedataoffset += 1;
 138              $info['ogg']['pageheader']['theora']['version_revision']         = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  1));
 139              $filedataoffset += 1;
 140              $info['ogg']['pageheader']['theora']['frame_width_macroblocks']  = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  2));
 141              $filedataoffset += 2;
 142              $info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  2));
 143              $filedataoffset += 2;
 144              $info['ogg']['pageheader']['theora']['resolution_x']             = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  3));
 145              $filedataoffset += 3;
 146              $info['ogg']['pageheader']['theora']['resolution_y']             = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  3));
 147              $filedataoffset += 3;
 148              $info['ogg']['pageheader']['theora']['picture_offset_x']         = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  1));
 149              $filedataoffset += 1;
 150              $info['ogg']['pageheader']['theora']['picture_offset_y']         = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  1));
 151              $filedataoffset += 1;
 152              $info['ogg']['pageheader']['theora']['frame_rate_numerator']     = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  4));
 153              $filedataoffset += 4;
 154              $info['ogg']['pageheader']['theora']['frame_rate_denominator']   = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  4));
 155              $filedataoffset += 4;
 156              $info['ogg']['pageheader']['theora']['pixel_aspect_numerator']   = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  3));
 157              $filedataoffset += 3;
 158              $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  3));
 159              $filedataoffset += 3;
 160              $info['ogg']['pageheader']['theora']['color_space_id']           = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  1));
 161              $filedataoffset += 1;
 162              $info['ogg']['pageheader']['theora']['nominal_bitrate']          = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  3));
 163              $filedataoffset += 3;
 164              $info['ogg']['pageheader']['theora']['flags']                    = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset,  2));
 165              $filedataoffset += 2;
 166  
 167              $info['ogg']['pageheader']['theora']['quality']         = ($info['ogg']['pageheader']['theora']['flags'] & 0xFC00) >> 10;
 168              $info['ogg']['pageheader']['theora']['kfg_shift']       = ($info['ogg']['pageheader']['theora']['flags'] & 0x03E0) >>  5;
 169              $info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0018) >>  3;
 170              $info['ogg']['pageheader']['theora']['reserved']        = ($info['ogg']['pageheader']['theora']['flags'] & 0x0007) >>  0; // should be 0
 171              $info['ogg']['pageheader']['theora']['color_space']     = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']);
 172              $info['ogg']['pageheader']['theora']['pixel_format']    = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']);
 173  
 174              $info['video']['dataformat']   = 'theora';
 175              $info['mime_type']             = 'video/ogg';
 176              //$info['audio']['bitrate_mode'] = 'abr';
 177              //$info['audio']['lossless']     = false;
 178              $info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x'];
 179              $info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y'];
 180              if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) {
 181                  $info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
 182              }
 183              if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
 184                  $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
 185              }
 186              $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
 187  
 188  
 189          } elseif (substr($filedata, 0, 8) == "fishead\x00") {
 190  
 191              // Ogg Skeleton version 3.0 Format Specification
 192              // http://xiph.org/ogg/doc/skeleton.html
 193              $filedataoffset += 8;
 194              $info['ogg']['skeleton']['fishead']['raw']['version_major']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
 195              $filedataoffset += 2;
 196              $info['ogg']['skeleton']['fishead']['raw']['version_minor']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
 197              $filedataoffset += 2;
 198              $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 199              $filedataoffset += 8;
 200              $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 201              $filedataoffset += 8;
 202              $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator']           = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 203              $filedataoffset += 8;
 204              $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 205              $filedataoffset += 8;
 206              $info['ogg']['skeleton']['fishead']['raw']['utc']                          = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
 207              $filedataoffset += 20;
 208  
 209              $info['ogg']['skeleton']['fishead']['version']          = $info['ogg']['skeleton']['fishead']['raw']['version_major'].'.'.$info['ogg']['skeleton']['fishead']['raw']['version_minor'];
 210              $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'];
 211              $info['ogg']['skeleton']['fishead']['basetime']         = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator']         / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'];
 212              $info['ogg']['skeleton']['fishead']['utc']              = $info['ogg']['skeleton']['fishead']['raw']['utc'];
 213  
 214  
 215              $counter = 0;
 216              do {
 217                  $oggpageinfo = $this->ParseOggPageHeader();
 218                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo;
 219                  $filedata = $this->fread($oggpageinfo['page_length']);
 220                  $this->fseek($oggpageinfo['page_end_offset']);
 221  
 222                  if (substr($filedata, 0, 8) == "fisbone\x00") {
 223  
 224                      $filedataoffset = 8;
 225                      $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 226                      $filedataoffset += 4;
 227                      $info['ogg']['skeleton']['fisbone']['raw']['serial_number']           = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 228                      $filedataoffset += 4;
 229                      $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 230                      $filedataoffset += 4;
 231                      $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 232                      $filedataoffset += 8;
 233                      $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 234                      $filedataoffset += 8;
 235                      $info['ogg']['skeleton']['fisbone']['raw']['basegranule']             = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 236                      $filedataoffset += 8;
 237                      $info['ogg']['skeleton']['fisbone']['raw']['preroll']                 = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 238                      $filedataoffset += 4;
 239                      $info['ogg']['skeleton']['fisbone']['raw']['granuleshift']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
 240                      $filedataoffset += 1;
 241                      $info['ogg']['skeleton']['fisbone']['raw']['padding']                 =                              substr($filedata, $filedataoffset,  3);
 242                      $filedataoffset += 3;
 243  
 244                  } elseif (substr($filedata, 1, 6) == 'theora') {
 245  
 246                      $info['video']['dataformat'] = 'theora1';
 247                      $this->error('Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']');
 248                      //break;
 249  
 250                  } elseif (substr($filedata, 1, 6) == 'vorbis') {
 251  
 252                      $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
 253  
 254                  } else {
 255                      $this->error('unexpected');
 256                      //break;
 257                  }
 258              //} while ($oggpageinfo['page_seqno'] == 0);
 259              } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
 260  
 261              $this->fseek($oggpageinfo['page_start_offset']);
 262  
 263              $this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']');
 264              //return false;
 265  
 266          } elseif (substr($filedata, 0, 5) == "\x7F".'FLAC') {
 267              // https://xiph.org/flac/ogg_mapping.html
 268  
 269              $info['audio']['dataformat']   = 'flac';
 270              $info['audio']['bitrate_mode'] = 'vbr';
 271              $info['audio']['lossless']     = true;
 272  
 273              $info['ogg']['flac']['header']['version_major']  =                         ord(substr($filedata,  5, 1));
 274              $info['ogg']['flac']['header']['version_minor']  =                         ord(substr($filedata,  6, 1));
 275              $info['ogg']['flac']['header']['header_packets'] =   getid3_lib::BigEndian2Int(substr($filedata,  7, 2)) + 1; // "A two-byte, big-endian binary number signifying the number of header (non-audio) packets, not including this one. This number may be zero (0x0000) to signify 'unknown' but be aware that some decoders may not be able to handle such streams."
 276              $info['ogg']['flac']['header']['magic']          =                             substr($filedata,  9, 4);
 277              if ($info['ogg']['flac']['header']['magic'] != 'fLaC') {
 278                  $this->error('Ogg-FLAC expecting "fLaC", found "'.$info['ogg']['flac']['header']['magic'].'" ('.trim(getid3_lib::PrintHexBytes($info['ogg']['flac']['header']['magic'])).')');
 279                  return false;
 280              }
 281              $info['ogg']['flac']['header']['STREAMINFO_bytes'] = getid3_lib::BigEndian2Int(substr($filedata, 13, 4));
 282              $info['flac']['STREAMINFO'] = getid3_flac::parseSTREAMINFOdata(substr($filedata, 17, 34));
 283              if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
 284                  $info['audio']['bitrate_mode']    = 'vbr';
 285                  $info['audio']['sample_rate']     = $info['flac']['STREAMINFO']['sample_rate'];
 286                  $info['audio']['channels']        = $info['flac']['STREAMINFO']['channels'];
 287                  $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
 288                  $info['playtime_seconds']         = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
 289              }
 290  
 291          } else {
 292  
 293              $this->error('Expecting one of "vorbis", "Speex", "OpusHead", "vorbis", "fishhead", "theora", "fLaC" identifier strings, found "'.substr($filedata, 0, 8).'"');
 294              unset($info['ogg']);
 295              unset($info['mime_type']);
 296              return false;
 297  
 298          }
 299  
 300          // Page 2 - Comment Header
 301          $oggpageinfo = $this->ParseOggPageHeader();
 302          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 303  
 304          switch ($info['audio']['dataformat']) {
 305              case 'vorbis':
 306                  $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 307                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
 308                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] =                              substr($filedata, 1, 6); // hard-coded to 'vorbis'
 309  
 310                  $this->ParseVorbisComments();
 311                  break;
 312  
 313              case 'flac':
 314                  $flac = new getid3_flac($this->getid3);
 315                  if (!$flac->parseMETAdata()) {
 316                      $this->error('Failed to parse FLAC headers');
 317                      return false;
 318                  }
 319                  unset($flac);
 320                  break;
 321  
 322              case 'speex':
 323                  $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
 324                  $this->ParseVorbisComments();
 325                  break;
 326  
 327              case 'opus':
 328                  $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 329                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 0, 8); // hard-coded to 'OpusTags'
 330                  if(substr($filedata, 0, 8)  != 'OpusTags') {
 331                      $this->error('Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"');
 332                      return false;
 333                  }
 334  
 335                  $this->ParseVorbisComments();
 336                  break;
 337  
 338          }
 339  
 340          // Last Page - Number of Samples
 341          if (!getid3_lib::intValueSupported($info['avdataend'])) {
 342  
 343              $this->warning('Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)');
 344  
 345          } else {
 346  
 347              $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
 348              $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
 349              if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
 350                  $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
 351                  $info['avdataend'] = $this->ftell();
 352                  $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
 353                  $info['ogg']['samples']   = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
 354                  if ($info['ogg']['samples'] == 0) {
 355                      $this->error('Corrupt Ogg file: eos.number of samples == zero');
 356                      return false;
 357                  }
 358                  if (!empty($info['audio']['sample_rate'])) {
 359                      $info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['ogg']['samples'] / $info['audio']['sample_rate']);
 360                  }
 361              }
 362  
 363          }
 364  
 365          if (!empty($info['ogg']['bitrate_average'])) {
 366              $info['audio']['bitrate'] = $info['ogg']['bitrate_average'];
 367          } elseif (!empty($info['ogg']['bitrate_nominal'])) {
 368              $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal'];
 369          } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) {
 370              $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2;
 371          }
 372          if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
 373              if ($info['audio']['bitrate'] == 0) {
 374                  $this->error('Corrupt Ogg file: bitrate_audio == zero');
 375                  return false;
 376              }
 377              $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']);
 378          }
 379  
 380          if (isset($info['ogg']['vendor'])) {
 381              $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
 382  
 383              // Vorbis only
 384              if ($info['audio']['dataformat'] == 'vorbis') {
 385  
 386                  // Vorbis 1.0 starts with Xiph.Org
 387                  if  (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
 388  
 389                      if ($info['audio']['bitrate_mode'] == 'abr') {
 390  
 391                          // Set -b 128 on abr files
 392                          $info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000);
 393  
 394                      } elseif (($info['audio']['bitrate_mode'] == 'vbr') && ($info['audio']['channels'] == 2) && ($info['audio']['sample_rate'] >= 44100) && ($info['audio']['sample_rate'] <= 48000)) {
 395                          // Set -q N on vbr files
 396                          $info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
 397  
 398                      }
 399                  }
 400  
 401                  if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) {
 402                      $info['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($info['ogg']['bitrate_nominal'] / 1000)).'kbps';
 403                  }
 404              }
 405          }
 406  
 407          return true;
 408      }
 409  
 410      /**
 411       * @param string $filedata
 412       * @param int    $filedataoffset
 413       * @param array  $oggpageinfo
 414       *
 415       * @return bool
 416       */
 417  	public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
 418          $info = &$this->getid3->info;
 419          $info['audio']['dataformat'] = 'vorbis';
 420          $info['audio']['lossless']   = false;
 421  
 422          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 423          $filedataoffset += 1;
 424          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
 425          $filedataoffset += 6;
 426          $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 427          $filedataoffset += 4;
 428          $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 429          $filedataoffset += 1;
 430          $info['audio']['channels']       = $info['ogg']['numberofchannels'];
 431          $info['ogg']['samplerate']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 432          $filedataoffset += 4;
 433          if ($info['ogg']['samplerate'] == 0) {
 434              $this->error('Corrupt Ogg file: sample rate == zero');
 435              return false;
 436          }
 437          $info['audio']['sample_rate']    = $info['ogg']['samplerate'];
 438          $info['ogg']['samples']          = 0; // filled in later
 439          $info['ogg']['bitrate_average']  = 0; // filled in later
 440          $info['ogg']['bitrate_max']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 441          $filedataoffset += 4;
 442          $info['ogg']['bitrate_nominal']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 443          $filedataoffset += 4;
 444          $info['ogg']['bitrate_min']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 445          $filedataoffset += 4;
 446          $info['ogg']['blocksize_small']  = pow(2,  getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
 447          $info['ogg']['blocksize_large']  = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
 448          $info['ogg']['stop_bit']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
 449  
 450          $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
 451          if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) {
 452              unset($info['ogg']['bitrate_max']);
 453              $info['audio']['bitrate_mode'] = 'abr';
 454          }
 455          if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
 456              unset($info['ogg']['bitrate_nominal']);
 457          }
 458          if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) {
 459              unset($info['ogg']['bitrate_min']);
 460              $info['audio']['bitrate_mode'] = 'abr';
 461          }
 462          return true;
 463      }
 464  
 465      /**
 466       * @link http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
 467       *
 468       * @param string $filedata
 469       * @param int    $filedataoffset
 470       * @param array  $oggpageinfo
 471       *
 472       * @return bool
 473       */
 474  	public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
 475          $info = &$this->getid3->info;
 476          $info['audio']['dataformat']   = 'opus';
 477          $info['mime_type']             = 'audio/ogg; codecs=opus';
 478  
 479          /** @todo find a usable way to detect abr (vbr that is padded to be abr) */
 480          $info['audio']['bitrate_mode'] = 'vbr';
 481  
 482          $info['audio']['lossless']     = false;
 483  
 484          $info['ogg']['pageheader']['opus']['opus_magic'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'OpusHead'
 485          $filedataoffset += 8;
 486          $info['ogg']['pageheader']['opus']['version']    = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
 487          $filedataoffset += 1;
 488  
 489          if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
 490              $this->error('Unknown opus version number (only accepting 1-15)');
 491              return false;
 492          }
 493  
 494          $info['ogg']['pageheader']['opus']['out_channel_count'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
 495          $filedataoffset += 1;
 496  
 497          if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
 498              $this->error('Invalid channel count in opus header (must not be zero)');
 499              return false;
 500          }
 501  
 502          $info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
 503          $filedataoffset += 2;
 504  
 505          $info['ogg']['pageheader']['opus']['input_sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 506          $filedataoffset += 4;
 507  
 508          //$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
 509          //$filedataoffset += 2;
 510  
 511          //$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
 512          //$filedataoffset += 1;
 513  
 514          $info['opus']['opus_version']       = $info['ogg']['pageheader']['opus']['version'];
 515          $info['opus']['sample_rate_input']  = $info['ogg']['pageheader']['opus']['input_sample_rate'];
 516          $info['opus']['out_channel_count']  = $info['ogg']['pageheader']['opus']['out_channel_count'];
 517  
 518          $info['audio']['channels']          = $info['opus']['out_channel_count'];
 519          $info['audio']['sample_rate_input'] = $info['opus']['sample_rate_input'];
 520          $info['audio']['sample_rate']       = 48000; // "All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz for playback (unless the target hardware does not support this sampling rate). However, this field may be used to resample the audio back to the original sampling rate, for example, when saving the output to a file." -- https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/structOpusHead.html
 521          return true;
 522      }
 523  
 524      /**
 525       * @return array|false
 526       */
 527  	public function ParseOggPageHeader() {
 528          // http://xiph.org/ogg/vorbis/doc/framing.html
 529          $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
 530  
 531          $filedata = $this->fread($this->getid3->fread_buffer_size());
 532          $filedataoffset = 0;
 533          while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
 534              if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
 535                  // should be found before here
 536                  return false;
 537              }
 538              if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
 539                  if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) {
 540                      // get some more data, unless eof, in which case fail
 541                      return false;
 542                  }
 543              }
 544          }
 545          $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
 546  
 547          $oggheader['stream_structver']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 548          $filedataoffset += 1;
 549          $oggheader['flags_raw']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 550          $filedataoffset += 1;
 551          $oggheader['flags']['fresh']    = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
 552          $oggheader['flags']['bos']      = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
 553          $oggheader['flags']['eos']      = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
 554  
 555          $oggheader['pcm_abs_position']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
 556          $filedataoffset += 8;
 557          $oggheader['stream_serialno']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 558          $filedataoffset += 4;
 559          $oggheader['page_seqno']        = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 560          $filedataoffset += 4;
 561          $oggheader['page_checksum']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 562          $filedataoffset += 4;
 563          $oggheader['page_segments']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 564          $filedataoffset += 1;
 565          $oggheader['page_length'] = 0;
 566          for ($i = 0; $i < $oggheader['page_segments']; $i++) {
 567              $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 568              $filedataoffset += 1;
 569              $oggheader['page_length'] += $oggheader['segment_table'][$i];
 570          }
 571          $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
 572          $oggheader['page_end_offset']   = $oggheader['header_end_offset'] + $oggheader['page_length'];
 573          $this->fseek($oggheader['header_end_offset']);
 574  
 575          return $oggheader;
 576      }
 577  
 578      /**
 579       * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
 580       *
 581       * @return bool
 582       */
 583  	public function ParseVorbisComments() {
 584          $info = &$this->getid3->info;
 585  
 586          $OriginalOffset = $this->ftell();
 587          $commentdata = null;
 588          $commentdataoffset = 0;
 589          $VorbisCommentPage = 1;
 590          $CommentStartOffset = 0;
 591  
 592          switch ($info['audio']['dataformat']) {
 593              case 'vorbis':
 594              case 'speex':
 595              case 'opus':
 596                  $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
 597                  $this->fseek($CommentStartOffset);
 598                  $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 599                  $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 600  
 601                  if ($info['audio']['dataformat'] == 'vorbis') {
 602                      $commentdataoffset += (strlen('vorbis') + 1);
 603                  }
 604                  else if ($info['audio']['dataformat'] == 'opus') {
 605                      $commentdataoffset += strlen('OpusTags');
 606                  }
 607  
 608                  break;
 609  
 610              case 'flac':
 611                  $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
 612                  $this->fseek($CommentStartOffset);
 613                  $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
 614                  break;
 615  
 616              default:
 617                  return false;
 618                  break;
 619          }
 620  
 621          $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 622          $commentdataoffset += 4;
 623  
 624          $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
 625          $commentdataoffset += $VendorSize;
 626  
 627          $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 628          $commentdataoffset += 4;
 629          $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
 630  
 631          $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
 632          $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
 633          for ($i = 0; $i < $CommentsCount; $i++) {
 634  
 635              if ($i >= 10000) {
 636                  // https://github.com/owncloud/music/issues/212#issuecomment-43082336
 637                  $this->warning('Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments');
 638                  break;
 639              }
 640  
 641              $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
 642  
 643              if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
 644                  if ($oggpageinfo = $this->ParseOggPageHeader()) {
 645                      $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 646  
 647                      $VorbisCommentPage++;
 648  
 649                      // First, save what we haven't read yet
 650                      $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 651  
 652                      // Then take that data off the end
 653                      $commentdata     = substr($commentdata, 0, $commentdataoffset);
 654  
 655                      // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 656                      $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 657                      $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 658  
 659                      // Finally, stick the unused data back on the end
 660                      $commentdata .= $AsYetUnusedData;
 661  
 662                      //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 663                      $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
 664                  }
 665  
 666              }
 667              $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 668  
 669              // replace avdataoffset with position just after the last vorbiscomment
 670              $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4;
 671  
 672              $commentdataoffset += 4;
 673              while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
 674                  if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
 675                      $this->warning('Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments');
 676                      break 2;
 677                  }
 678  
 679                  $VorbisCommentPage++;
 680  
 681                  $oggpageinfo = $this->ParseOggPageHeader();
 682                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 683  
 684                  // First, save what we haven't read yet
 685                  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 686  
 687                  // Then take that data off the end
 688                  $commentdata     = substr($commentdata, 0, $commentdataoffset);
 689  
 690                  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 691                  $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 692                  $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 693  
 694                  // Finally, stick the unused data back on the end
 695                  $commentdata .= $AsYetUnusedData;
 696  
 697                  //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 698                  if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
 699                      $this->warning('undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
 700                      break;
 701                  }
 702                  $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
 703                  if ($readlength <= 0) {
 704                      $this->warning('invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
 705                      break;
 706                  }
 707                  $commentdata .= $this->fread($readlength);
 708  
 709                  //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
 710              }
 711              $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset;
 712              $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']);
 713              $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size'];
 714  
 715              if (!$commentstring) {
 716  
 717                  // no comment?
 718                  $this->warning('Blank Ogg comment ['.$i.']');
 719  
 720              } elseif (strstr($commentstring, '=')) {
 721  
 722                  $commentexploded = explode('=', $commentstring, 2);
 723                  $ThisFileInfo_ogg_comments_raw[$i]['key']   = strtoupper($commentexploded[0]);
 724                  $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
 725  
 726                  if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
 727  
 728                      // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
 729                      // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard.
 730                      // http://flac.sourceforge.net/format.html#metadata_block_picture
 731                      $flac = new getid3_flac($this->getid3);
 732                      $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
 733                      $flac->parsePICTURE();
 734                      $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
 735                      unset($flac);
 736  
 737                  } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
 738  
 739                      $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
 740                      $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
 741                      /** @todo use 'coverartmime' where available */
 742                      $imageinfo = getid3_lib::GetDataImageSize($data);
 743                      if ($imageinfo === false || !isset($imageinfo['mime'])) {
 744                          $this->warning('COVERART vorbiscomment tag contains invalid image');
 745                          continue;
 746                      }
 747  
 748                      $ogg = new self($this->getid3);
 749                      $ogg->setStringMode($data);
 750                      $info['ogg']['comments']['picture'][] = array(
 751                          'image_mime'   => $imageinfo['mime'],
 752                          'datalength'   => strlen($data),
 753                          'picturetype'  => 'cover art',
 754                          'image_height' => $imageinfo['height'],
 755                          'image_width'  => $imageinfo['width'],
 756                          'data'         => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
 757                      );
 758                      unset($ogg);
 759  
 760                  } else {
 761  
 762                      $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
 763  
 764                  }
 765  
 766              } else {
 767  
 768                  $this->warning('[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring);
 769  
 770              }
 771              unset($ThisFileInfo_ogg_comments_raw[$i]);
 772          }
 773          unset($ThisFileInfo_ogg_comments_raw);
 774  
 775  
 776          // Replay Gain Adjustment
 777          // http://privatewww.essex.ac.uk/~djmrob/replaygain/
 778          if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) {
 779              foreach ($info['ogg']['comments'] as $index => $commentvalue) {
 780                  switch ($index) {
 781                      case 'rg_audiophile':
 782                      case 'replaygain_album_gain':
 783                          $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
 784                          unset($info['ogg']['comments'][$index]);
 785                          break;
 786  
 787                      case 'rg_radio':
 788                      case 'replaygain_track_gain':
 789                          $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
 790                          unset($info['ogg']['comments'][$index]);
 791                          break;
 792  
 793                      case 'replaygain_album_peak':
 794                          $info['replay_gain']['album']['peak'] = (double) $commentvalue[0];
 795                          unset($info['ogg']['comments'][$index]);
 796                          break;
 797  
 798                      case 'rg_peak':
 799                      case 'replaygain_track_peak':
 800                          $info['replay_gain']['track']['peak'] = (double) $commentvalue[0];
 801                          unset($info['ogg']['comments'][$index]);
 802                          break;
 803  
 804                      case 'replaygain_reference_loudness':
 805                          $info['replay_gain']['reference_volume'] = (double) $commentvalue[0];
 806                          unset($info['ogg']['comments'][$index]);
 807                          break;
 808  
 809                      default:
 810                          // do nothing
 811                          break;
 812                  }
 813              }
 814          }
 815  
 816          $this->fseek($OriginalOffset);
 817  
 818          return true;
 819      }
 820  
 821      /**
 822       * @param int $mode
 823       *
 824       * @return string|null
 825       */
 826  	public static function SpeexBandModeLookup($mode) {
 827          static $SpeexBandModeLookup = array();
 828          if (empty($SpeexBandModeLookup)) {
 829              $SpeexBandModeLookup[0] = 'narrow';
 830              $SpeexBandModeLookup[1] = 'wide';
 831              $SpeexBandModeLookup[2] = 'ultra-wide';
 832          }
 833          return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
 834      }
 835  
 836      /**
 837       * @param array $OggInfoArray
 838       * @param int   $SegmentNumber
 839       *
 840       * @return int
 841       */
 842  	public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
 843          $segmentlength = 0;
 844          for ($i = 0; $i < $SegmentNumber; $i++) {
 845              $segmentlength = 0;
 846              foreach ($OggInfoArray['segment_table'] as $key => $value) {
 847                  $segmentlength += $value;
 848                  if ($value < 255) {
 849                      break;
 850                  }
 851              }
 852          }
 853          return $segmentlength;
 854      }
 855  
 856      /**
 857       * @param int $nominal_bitrate
 858       *
 859       * @return float
 860       */
 861  	public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
 862  
 863          // decrease precision
 864          $nominal_bitrate = $nominal_bitrate / 1000;
 865  
 866          if ($nominal_bitrate < 128) {
 867              // q-1 to q4
 868              $qval = ($nominal_bitrate - 64) / 16;
 869          } elseif ($nominal_bitrate < 256) {
 870              // q4 to q8
 871              $qval = $nominal_bitrate / 32;
 872          } elseif ($nominal_bitrate < 320) {
 873              // q8 to q9
 874              $qval = ($nominal_bitrate + 256) / 64;
 875          } else {
 876              // q9 to q10
 877              $qval = ($nominal_bitrate + 1300) / 180;
 878          }
 879          //return $qval; // 5.031324
 880          //return intval($qval); // 5
 881          return round($qval, 1); // 5 or 4.9
 882      }
 883  
 884      /**
 885       * @param int $colorspace_id
 886       *
 887       * @return string|null
 888       */
 889  	public static function TheoraColorSpace($colorspace_id) {
 890          // http://www.theora.org/doc/Theora.pdf (table 6.3)
 891          static $TheoraColorSpaceLookup = array();
 892          if (empty($TheoraColorSpaceLookup)) {
 893              $TheoraColorSpaceLookup[0] = 'Undefined';
 894              $TheoraColorSpaceLookup[1] = 'Rec. 470M';
 895              $TheoraColorSpaceLookup[2] = 'Rec. 470BG';
 896              $TheoraColorSpaceLookup[3] = 'Reserved';
 897          }
 898          return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
 899      }
 900  
 901      /**
 902       * @param int $pixelformat_id
 903       *
 904       * @return string|null
 905       */
 906  	public static function TheoraPixelFormat($pixelformat_id) {
 907          // http://www.theora.org/doc/Theora.pdf (table 6.4)
 908          static $TheoraPixelFormatLookup = array();
 909          if (empty($TheoraPixelFormatLookup)) {
 910              $TheoraPixelFormatLookup[0] = '4:2:0';
 911              $TheoraPixelFormatLookup[1] = 'Reserved';
 912              $TheoraPixelFormatLookup[2] = '4:2:2';
 913              $TheoraPixelFormatLookup[3] = '4:4:4';
 914          }
 915          return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
 916      }
 917  
 918  }


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7