[ Index ]

PHP Cross Reference of WordPress

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


Generated: Mon Jul 6 01:00:03 2020 Cross-referenced by PHPXref 0.7.1