[ 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 = array();
 533          $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
 534  
 535          $filedata = $this->fread($this->getid3->fread_buffer_size());
 536          $filedataoffset = 0;
 537          while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
 538              if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
 539                  // should be found before here
 540                  return false;
 541              }
 542              if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
 543                  if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) {
 544                      // get some more data, unless eof, in which case fail
 545                      return false;
 546                  }
 547              }
 548          }
 549          $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
 550  
 551          $oggheader['stream_structver']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 552          $filedataoffset += 1;
 553          $oggheader['flags_raw']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 554          $filedataoffset += 1;
 555          $oggheader['flags']['fresh']    = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
 556          $oggheader['flags']['bos']      = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
 557          $oggheader['flags']['eos']      = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
 558  
 559          $oggheader['pcm_abs_position']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
 560          $filedataoffset += 8;
 561          $oggheader['stream_serialno']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 562          $filedataoffset += 4;
 563          $oggheader['page_seqno']        = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 564          $filedataoffset += 4;
 565          $oggheader['page_checksum']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 566          $filedataoffset += 4;
 567          $oggheader['page_segments']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 568          $filedataoffset += 1;
 569          $oggheader['page_length'] = 0;
 570          for ($i = 0; $i < $oggheader['page_segments']; $i++) {
 571              $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 572              $filedataoffset += 1;
 573              $oggheader['page_length'] += $oggheader['segment_table'][$i];
 574          }
 575          $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
 576          $oggheader['page_end_offset']   = $oggheader['header_end_offset'] + $oggheader['page_length'];
 577          $this->fseek($oggheader['header_end_offset']);
 578  
 579          return $oggheader;
 580      }
 581  
 582      /**
 583       * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
 584       *
 585       * @return bool
 586       */
 587  	public function ParseVorbisComments() {
 588          $info = &$this->getid3->info;
 589  
 590          $OriginalOffset = $this->ftell();
 591          $commentdata = null;
 592          $commentdataoffset = 0;
 593          $VorbisCommentPage = 1;
 594          $CommentStartOffset = 0;
 595  
 596          switch ($info['audio']['dataformat']) {
 597              case 'vorbis':
 598              case 'speex':
 599              case 'opus':
 600                  $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
 601                  $this->fseek($CommentStartOffset);
 602                  $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 603                  $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 604  
 605                  if ($info['audio']['dataformat'] == 'vorbis') {
 606                      $commentdataoffset += (strlen('vorbis') + 1);
 607                  }
 608                  else if ($info['audio']['dataformat'] == 'opus') {
 609                      $commentdataoffset += strlen('OpusTags');
 610                  }
 611  
 612                  break;
 613  
 614              case 'flac':
 615                  $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
 616                  $this->fseek($CommentStartOffset);
 617                  $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
 618                  break;
 619  
 620              default:
 621                  return false;
 622          }
 623  
 624          $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 625          $commentdataoffset += 4;
 626  
 627          $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
 628          $commentdataoffset += $VendorSize;
 629  
 630          $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 631          $commentdataoffset += 4;
 632          $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
 633  
 634          $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
 635          $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
 636          for ($i = 0; $i < $CommentsCount; $i++) {
 637  
 638              if ($i >= 10000) {
 639                  // https://github.com/owncloud/music/issues/212#issuecomment-43082336
 640                  $this->warning('Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments');
 641                  break;
 642              }
 643  
 644              $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
 645  
 646              if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
 647                  if ($oggpageinfo = $this->ParseOggPageHeader()) {
 648                      $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 649  
 650                      $VorbisCommentPage++;
 651  
 652                      // First, save what we haven't read yet
 653                      $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 654  
 655                      // Then take that data off the end
 656                      $commentdata     = substr($commentdata, 0, $commentdataoffset);
 657  
 658                      // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 659                      $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 660                      $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 661  
 662                      // Finally, stick the unused data back on the end
 663                      $commentdata .= $AsYetUnusedData;
 664  
 665                      //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 666                      $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
 667                  }
 668  
 669              }
 670              $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 671  
 672              // replace avdataoffset with position just after the last vorbiscomment
 673              $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4;
 674  
 675              $commentdataoffset += 4;
 676              while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
 677                  if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
 678                      $this->warning('Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments');
 679                      break 2;
 680                  }
 681  
 682                  $VorbisCommentPage++;
 683  
 684                  if ($oggpageinfo = $this->ParseOggPageHeader()) {
 685                      $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 686  
 687                      // First, save what we haven't read yet
 688                      $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 689  
 690                      // Then take that data off the end
 691                      $commentdata     = substr($commentdata, 0, $commentdataoffset);
 692  
 693                      // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 694                      $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 695                      $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 696  
 697                      // Finally, stick the unused data back on the end
 698                      $commentdata .= $AsYetUnusedData;
 699  
 700                      //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 701                      if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
 702                          $this->warning('undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
 703                          break;
 704                      }
 705                      $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
 706                      if ($readlength <= 0) {
 707                          $this->warning('invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
 708                          break;
 709                      }
 710                      $commentdata .= $this->fread($readlength);
 711  
 712                      //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
 713                  } else {
 714                      $this->warning('failed to ParseOggPageHeader() at offset '.$this->ftell());
 715                      break;
 716                  }
 717              }
 718              $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset;
 719              $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']);
 720              $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size'];
 721  
 722              if (!$commentstring) {
 723  
 724                  // no comment?
 725                  $this->warning('Blank Ogg comment ['.$i.']');
 726  
 727              } elseif (strstr($commentstring, '=')) {
 728  
 729                  $commentexploded = explode('=', $commentstring, 2);
 730                  $ThisFileInfo_ogg_comments_raw[$i]['key']   = strtoupper($commentexploded[0]);
 731                  $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
 732  
 733                  if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
 734  
 735                      // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
 736                      // 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.
 737                      // http://flac.sourceforge.net/format.html#metadata_block_picture
 738                      $flac = new getid3_flac($this->getid3);
 739                      $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
 740                      $flac->parsePICTURE();
 741                      $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
 742                      unset($flac);
 743  
 744                  } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
 745  
 746                      $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
 747                      $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
 748                      /** @todo use 'coverartmime' where available */
 749                      $imageinfo = getid3_lib::GetDataImageSize($data);
 750                      if ($imageinfo === false || !isset($imageinfo['mime'])) {
 751                          $this->warning('COVERART vorbiscomment tag contains invalid image');
 752                          continue;
 753                      }
 754  
 755                      $ogg = new self($this->getid3);
 756                      $ogg->setStringMode($data);
 757                      $info['ogg']['comments']['picture'][] = array(
 758                          'image_mime'   => $imageinfo['mime'],
 759                          'datalength'   => strlen($data),
 760                          'picturetype'  => 'cover art',
 761                          'image_height' => $imageinfo['height'],
 762                          'image_width'  => $imageinfo['width'],
 763                          'data'         => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
 764                      );
 765                      unset($ogg);
 766  
 767                  } else {
 768  
 769                      $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
 770  
 771                  }
 772  
 773              } else {
 774  
 775                  $this->warning('[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring);
 776  
 777              }
 778              unset($ThisFileInfo_ogg_comments_raw[$i]);
 779          }
 780          unset($ThisFileInfo_ogg_comments_raw);
 781  
 782  
 783          // Replay Gain Adjustment
 784          // http://privatewww.essex.ac.uk/~djmrob/replaygain/
 785          if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) {
 786              foreach ($info['ogg']['comments'] as $index => $commentvalue) {
 787                  switch ($index) {
 788                      case 'rg_audiophile':
 789                      case 'replaygain_album_gain':
 790                          $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
 791                          unset($info['ogg']['comments'][$index]);
 792                          break;
 793  
 794                      case 'rg_radio':
 795                      case 'replaygain_track_gain':
 796                          $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
 797                          unset($info['ogg']['comments'][$index]);
 798                          break;
 799  
 800                      case 'replaygain_album_peak':
 801                          $info['replay_gain']['album']['peak'] = (double) $commentvalue[0];
 802                          unset($info['ogg']['comments'][$index]);
 803                          break;
 804  
 805                      case 'rg_peak':
 806                      case 'replaygain_track_peak':
 807                          $info['replay_gain']['track']['peak'] = (double) $commentvalue[0];
 808                          unset($info['ogg']['comments'][$index]);
 809                          break;
 810  
 811                      case 'replaygain_reference_loudness':
 812                          $info['replay_gain']['reference_volume'] = (double) $commentvalue[0];
 813                          unset($info['ogg']['comments'][$index]);
 814                          break;
 815  
 816                      default:
 817                          // do nothing
 818                          break;
 819                  }
 820              }
 821          }
 822  
 823          $this->fseek($OriginalOffset);
 824  
 825          return true;
 826      }
 827  
 828      /**
 829       * @param int $mode
 830       *
 831       * @return string|null
 832       */
 833  	public static function SpeexBandModeLookup($mode) {
 834          static $SpeexBandModeLookup = array();
 835          if (empty($SpeexBandModeLookup)) {
 836              $SpeexBandModeLookup[0] = 'narrow';
 837              $SpeexBandModeLookup[1] = 'wide';
 838              $SpeexBandModeLookup[2] = 'ultra-wide';
 839          }
 840          return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
 841      }
 842  
 843      /**
 844       * @param array $OggInfoArray
 845       * @param int   $SegmentNumber
 846       *
 847       * @return int
 848       */
 849  	public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
 850          $segmentlength = 0;
 851          for ($i = 0; $i < $SegmentNumber; $i++) {
 852              $segmentlength = 0;
 853              foreach ($OggInfoArray['segment_table'] as $key => $value) {
 854                  $segmentlength += $value;
 855                  if ($value < 255) {
 856                      break;
 857                  }
 858              }
 859          }
 860          return $segmentlength;
 861      }
 862  
 863      /**
 864       * @param int $nominal_bitrate
 865       *
 866       * @return float
 867       */
 868  	public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
 869  
 870          // decrease precision
 871          $nominal_bitrate = $nominal_bitrate / 1000;
 872  
 873          if ($nominal_bitrate < 128) {
 874              // q-1 to q4
 875              $qval = ($nominal_bitrate - 64) / 16;
 876          } elseif ($nominal_bitrate < 256) {
 877              // q4 to q8
 878              $qval = $nominal_bitrate / 32;
 879          } elseif ($nominal_bitrate < 320) {
 880              // q8 to q9
 881              $qval = ($nominal_bitrate + 256) / 64;
 882          } else {
 883              // q9 to q10
 884              $qval = ($nominal_bitrate + 1300) / 180;
 885          }
 886          //return $qval; // 5.031324
 887          //return intval($qval); // 5
 888          return round($qval, 1); // 5 or 4.9
 889      }
 890  
 891      /**
 892       * @param int $colorspace_id
 893       *
 894       * @return string|null
 895       */
 896  	public static function TheoraColorSpace($colorspace_id) {
 897          // http://www.theora.org/doc/Theora.pdf (table 6.3)
 898          static $TheoraColorSpaceLookup = array();
 899          if (empty($TheoraColorSpaceLookup)) {
 900              $TheoraColorSpaceLookup[0] = 'Undefined';
 901              $TheoraColorSpaceLookup[1] = 'Rec. 470M';
 902              $TheoraColorSpaceLookup[2] = 'Rec. 470BG';
 903              $TheoraColorSpaceLookup[3] = 'Reserved';
 904          }
 905          return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
 906      }
 907  
 908      /**
 909       * @param int $pixelformat_id
 910       *
 911       * @return string|null
 912       */
 913  	public static function TheoraPixelFormat($pixelformat_id) {
 914          // http://www.theora.org/doc/Theora.pdf (table 6.4)
 915          static $TheoraPixelFormatLookup = array();
 916          if (empty($TheoraPixelFormatLookup)) {
 917              $TheoraPixelFormatLookup[0] = '4:2:0';
 918              $TheoraPixelFormatLookup[1] = 'Reserved';
 919              $TheoraPixelFormatLookup[2] = '4:2:2';
 920              $TheoraPixelFormatLookup[3] = '4:4:4';
 921          }
 922          return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
 923      }
 924  
 925  }


Generated: Thu Apr 18 01:00:02 2024 Cross-referenced by PHPXref 0.7.1