[ Index ]

WordPress Cross Reference

title

Body

[close]

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

   1  <?php
   2  /////////////////////////////////////////////////////////////////
   3  /// getID3() by James Heinrich <info@getid3.org>               //
   4  //  available at http://getid3.sourceforge.net                 //
   5  //            or http://www.getid3.org                         //
   6  /////////////////////////////////////////////////////////////////
   7  // See readme.txt for more details                             //
   8  /////////////////////////////////////////////////////////////////
   9  //                                                             //
  10  // module.audio.ogg.php                                        //
  11  // module for analyzing Ogg Vorbis, OggFLAC and Speex files    //
  12  // dependencies: module.audio.flac.php                         //
  13  //                                                            ///
  14  /////////////////////////////////////////////////////////////////
  15  
  16  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
  17  
  18  class getid3_ogg extends getid3_handler
  19  {
  20      // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
  21  	public function Analyze() {
  22          $info = &$this->getid3->info;
  23  
  24          $info['fileformat'] = 'ogg';
  25  
  26          // Warn about illegal tags - only vorbiscomments are allowed
  27          if (isset($info['id3v2'])) {
  28              $info['warning'][] = 'Illegal ID3v2 tag present.';
  29          }
  30          if (isset($info['id3v1'])) {
  31              $info['warning'][] = 'Illegal ID3v1 tag present.';
  32          }
  33          if (isset($info['ape'])) {
  34              $info['warning'][] = 'Illegal APE tag present.';
  35          }
  36  
  37  
  38          // Page 1 - Stream Header
  39  
  40          $this->fseek($info['avdataoffset']);
  41  
  42          $oggpageinfo = $this->ParseOggPageHeader();
  43          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
  44  
  45          if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
  46              $info['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?)';
  47              unset($info['fileformat']);
  48              unset($info['ogg']);
  49              return false;
  50          }
  51  
  52          $filedata = $this->fread($oggpageinfo['page_length']);
  53          $filedataoffset = 0;
  54  
  55          if (substr($filedata, 0, 4) == 'fLaC') {
  56  
  57              $info['audio']['dataformat']   = 'flac';
  58              $info['audio']['bitrate_mode'] = 'vbr';
  59              $info['audio']['lossless']     = true;
  60  
  61          } elseif (substr($filedata, 1, 6) == 'vorbis') {
  62  
  63              $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
  64  
  65          } elseif (substr($filedata, 0, 8) == 'Speex   ') {
  66  
  67              // http://www.speex.org/manual/node10.html
  68  
  69              $info['audio']['dataformat']   = 'speex';
  70              $info['mime_type']             = 'audio/speex';
  71              $info['audio']['bitrate_mode'] = 'abr';
  72              $info['audio']['lossless']     = false;
  73  
  74              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string']           =                              substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex   '
  75              $filedataoffset += 8;
  76              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']          =                              substr($filedata, $filedataoffset, 20);
  77              $filedataoffset += 20;
  78              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  79              $filedataoffset += 4;
  80              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  81              $filedataoffset += 4;
  82              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  83              $filedataoffset += 4;
  84              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  85              $filedataoffset += 4;
  86              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  87              $filedataoffset += 4;
  88              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  89              $filedataoffset += 4;
  90              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  91              $filedataoffset += 4;
  92              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  93              $filedataoffset += 4;
  94              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']                    = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  95              $filedataoffset += 4;
  96              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  97              $filedataoffset += 4;
  98              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers']          = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  99              $filedataoffset += 4;
 100              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 101              $filedataoffset += 4;
 102              $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 103              $filedataoffset += 4;
 104  
 105              $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
 106              $info['speex']['sample_rate']   = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
 107              $info['speex']['channels']      = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
 108              $info['speex']['vbr']           = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
 109              $info['speex']['band_type']     = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
 110  
 111              $info['audio']['sample_rate']   = $info['speex']['sample_rate'];
 112              $info['audio']['channels']      = $info['speex']['channels'];
 113              if ($info['speex']['vbr']) {
 114                  $info['audio']['bitrate_mode'] = 'vbr';
 115              }
 116  
 117  
 118          } elseif (substr($filedata, 0, 8) == "fishead\x00") {
 119  
 120              // Ogg Skeleton version 3.0 Format Specification
 121              // http://xiph.org/ogg/doc/skeleton.html
 122              $filedataoffset += 8;
 123              $info['ogg']['skeleton']['fishead']['raw']['version_major']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
 124              $filedataoffset += 2;
 125              $info['ogg']['skeleton']['fishead']['raw']['version_minor']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  2));
 126              $filedataoffset += 2;
 127              $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 128              $filedataoffset += 8;
 129              $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 130              $filedataoffset += 8;
 131              $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator']           = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 132              $filedataoffset += 8;
 133              $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 134              $filedataoffset += 8;
 135              $info['ogg']['skeleton']['fishead']['raw']['utc']                          = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
 136              $filedataoffset += 20;
 137  
 138              $info['ogg']['skeleton']['fishead']['version']          = $info['ogg']['skeleton']['fishead']['raw']['version_major'].'.'.$info['ogg']['skeleton']['fishead']['raw']['version_minor'];
 139              $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'];
 140              $info['ogg']['skeleton']['fishead']['basetime']         = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator']         / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'];
 141              $info['ogg']['skeleton']['fishead']['utc']              = $info['ogg']['skeleton']['fishead']['raw']['utc'];
 142  
 143  
 144              $counter = 0;
 145              do {
 146                  $oggpageinfo = $this->ParseOggPageHeader();
 147                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo;
 148                  $filedata = $this->fread($oggpageinfo['page_length']);
 149                  $this->fseek($oggpageinfo['page_end_offset']);
 150  
 151                  if (substr($filedata, 0, 8) == "fisbone\x00") {
 152  
 153                      $filedataoffset = 8;
 154                      $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 155                      $filedataoffset += 4;
 156                      $info['ogg']['skeleton']['fisbone']['raw']['serial_number']           = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 157                      $filedataoffset += 4;
 158                      $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 159                      $filedataoffset += 4;
 160                      $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 161                      $filedataoffset += 8;
 162                      $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 163                      $filedataoffset += 8;
 164                      $info['ogg']['skeleton']['fisbone']['raw']['basegranule']             = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  8));
 165                      $filedataoffset += 8;
 166                      $info['ogg']['skeleton']['fisbone']['raw']['preroll']                 = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
 167                      $filedataoffset += 4;
 168                      $info['ogg']['skeleton']['fisbone']['raw']['granuleshift']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  1));
 169                      $filedataoffset += 1;
 170                      $info['ogg']['skeleton']['fisbone']['raw']['padding']                 =                              substr($filedata, $filedataoffset,  3);
 171                      $filedataoffset += 3;
 172  
 173                  } elseif (substr($filedata, 1, 6) == 'theora') {
 174  
 175                      $info['video']['dataformat'] = 'theora';
 176                      $info['error'][] = 'Ogg Theora not correctly handled in this version of getID3 ['.$this->getid3->version().']';
 177                      //break;
 178  
 179                  } elseif (substr($filedata, 1, 6) == 'vorbis') {
 180  
 181                      $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
 182  
 183                  } else {
 184                      $info['error'][] = 'unexpected';
 185                      //break;
 186                  }
 187              //} while ($oggpageinfo['page_seqno'] == 0);
 188              } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
 189  
 190              $this->fseek($oggpageinfo['page_start_offset']);
 191  
 192              $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']';
 193              //return false;
 194  
 195          } else {
 196  
 197              $info['error'][] = 'Expecting either "Speex   " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
 198              unset($info['ogg']);
 199              unset($info['mime_type']);
 200              return false;
 201  
 202          }
 203  
 204          // Page 2 - Comment Header
 205          $oggpageinfo = $this->ParseOggPageHeader();
 206          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 207  
 208          switch ($info['audio']['dataformat']) {
 209              case 'vorbis':
 210                  $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 211                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
 212                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] =                              substr($filedata, 1, 6); // hard-coded to 'vorbis'
 213  
 214                  $this->ParseVorbisComments();
 215                  break;
 216  
 217              case 'flac':
 218                  $flac = new getid3_flac($this->getid3);
 219                  if (!$flac->parseMETAdata()) {
 220                      $info['error'][] = 'Failed to parse FLAC headers';
 221                      return false;
 222                  }
 223                  unset($flac);
 224                  break;
 225  
 226              case 'speex':
 227                  $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
 228                  $this->ParseVorbisComments();
 229                  break;
 230          }
 231  
 232  
 233          // Last Page - Number of Samples
 234          if (!getid3_lib::intValueSupported($info['avdataend'])) {
 235  
 236              $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
 237  
 238          } else {
 239  
 240              $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
 241              $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
 242              if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
 243                  $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
 244                  $info['avdataend'] = $this->ftell();
 245                  $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
 246                  $info['ogg']['samples']   = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
 247                  if ($info['ogg']['samples'] == 0) {
 248                      $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
 249                      return false;
 250                  }
 251                  if (!empty($info['audio']['sample_rate'])) {
 252                      $info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['ogg']['samples'] / $info['audio']['sample_rate']);
 253                  }
 254              }
 255  
 256          }
 257  
 258          if (!empty($info['ogg']['bitrate_average'])) {
 259              $info['audio']['bitrate'] = $info['ogg']['bitrate_average'];
 260          } elseif (!empty($info['ogg']['bitrate_nominal'])) {
 261              $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal'];
 262          } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) {
 263              $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2;
 264          }
 265          if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
 266              if ($info['audio']['bitrate'] == 0) {
 267                  $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
 268                  return false;
 269              }
 270              $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']);
 271          }
 272  
 273          if (isset($info['ogg']['vendor'])) {
 274              $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
 275  
 276              // Vorbis only
 277              if ($info['audio']['dataformat'] == 'vorbis') {
 278  
 279                  // Vorbis 1.0 starts with Xiph.Org
 280                  if  (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
 281  
 282                      if ($info['audio']['bitrate_mode'] == 'abr') {
 283  
 284                          // Set -b 128 on abr files
 285                          $info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000);
 286  
 287                      } elseif (($info['audio']['bitrate_mode'] == 'vbr') && ($info['audio']['channels'] == 2) && ($info['audio']['sample_rate'] >= 44100) && ($info['audio']['sample_rate'] <= 48000)) {
 288                          // Set -q N on vbr files
 289                          $info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
 290  
 291                      }
 292                  }
 293  
 294                  if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) {
 295                      $info['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($info['ogg']['bitrate_nominal'] / 1000)).'kbps';
 296                  }
 297              }
 298          }
 299  
 300          return true;
 301      }
 302  
 303  	public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
 304          $info = &$this->getid3->info;
 305          $info['audio']['dataformat'] = 'vorbis';
 306          $info['audio']['lossless']   = false;
 307  
 308          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 309          $filedataoffset += 1;
 310          $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
 311          $filedataoffset += 6;
 312          $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 313          $filedataoffset += 4;
 314          $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 315          $filedataoffset += 1;
 316          $info['audio']['channels']       = $info['ogg']['numberofchannels'];
 317          $info['ogg']['samplerate']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 318          $filedataoffset += 4;
 319          if ($info['ogg']['samplerate'] == 0) {
 320              $info['error'][] = 'Corrupt Ogg file: sample rate == zero';
 321              return false;
 322          }
 323          $info['audio']['sample_rate']    = $info['ogg']['samplerate'];
 324          $info['ogg']['samples']          = 0; // filled in later
 325          $info['ogg']['bitrate_average']  = 0; // filled in later
 326          $info['ogg']['bitrate_max']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 327          $filedataoffset += 4;
 328          $info['ogg']['bitrate_nominal']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 329          $filedataoffset += 4;
 330          $info['ogg']['bitrate_min']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 331          $filedataoffset += 4;
 332          $info['ogg']['blocksize_small']  = pow(2,  getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
 333          $info['ogg']['blocksize_large']  = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
 334          $info['ogg']['stop_bit']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
 335  
 336          $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
 337          if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) {
 338              unset($info['ogg']['bitrate_max']);
 339              $info['audio']['bitrate_mode'] = 'abr';
 340          }
 341          if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
 342              unset($info['ogg']['bitrate_nominal']);
 343          }
 344          if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) {
 345              unset($info['ogg']['bitrate_min']);
 346              $info['audio']['bitrate_mode'] = 'abr';
 347          }
 348          return true;
 349      }
 350  
 351  	public function ParseOggPageHeader() {
 352          // http://xiph.org/ogg/vorbis/doc/framing.html
 353          $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
 354  
 355          $filedata = $this->fread($this->getid3->fread_buffer_size());
 356          $filedataoffset = 0;
 357          while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
 358              if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
 359                  // should be found before here
 360                  return false;
 361              }
 362              if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
 363                  if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
 364                      // get some more data, unless eof, in which case fail
 365                      return false;
 366                  }
 367              }
 368          }
 369          $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
 370  
 371          $oggheader['stream_structver']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 372          $filedataoffset += 1;
 373          $oggheader['flags_raw']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 374          $filedataoffset += 1;
 375          $oggheader['flags']['fresh']    = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
 376          $oggheader['flags']['bos']      = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
 377          $oggheader['flags']['eos']      = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
 378  
 379          $oggheader['pcm_abs_position']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
 380          $filedataoffset += 8;
 381          $oggheader['stream_serialno']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 382          $filedataoffset += 4;
 383          $oggheader['page_seqno']        = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 384          $filedataoffset += 4;
 385          $oggheader['page_checksum']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 386          $filedataoffset += 4;
 387          $oggheader['page_segments']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 388          $filedataoffset += 1;
 389          $oggheader['page_length'] = 0;
 390          for ($i = 0; $i < $oggheader['page_segments']; $i++) {
 391              $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 392              $filedataoffset += 1;
 393              $oggheader['page_length'] += $oggheader['segment_table'][$i];
 394          }
 395          $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
 396          $oggheader['page_end_offset']   = $oggheader['header_end_offset'] + $oggheader['page_length'];
 397          $this->fseek($oggheader['header_end_offset']);
 398  
 399          return $oggheader;
 400      }
 401  
 402      // http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
 403  	public function ParseVorbisComments() {
 404          $info = &$this->getid3->info;
 405  
 406          $OriginalOffset = $this->ftell();
 407          $commentdataoffset = 0;
 408          $VorbisCommentPage = 1;
 409  
 410          switch ($info['audio']['dataformat']) {
 411              case 'vorbis':
 412              case 'speex':
 413                  $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
 414                  $this->fseek($CommentStartOffset);
 415                  $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 416                  $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 417  
 418                  if ($info['audio']['dataformat'] == 'vorbis') {
 419                      $commentdataoffset += (strlen('vorbis') + 1);
 420                  }
 421                  break;
 422  
 423              case 'flac':
 424                  $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
 425                  $this->fseek($CommentStartOffset);
 426                  $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
 427                  break;
 428  
 429              default:
 430                  return false;
 431          }
 432  
 433          $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 434          $commentdataoffset += 4;
 435  
 436          $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
 437          $commentdataoffset += $VendorSize;
 438  
 439          $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 440          $commentdataoffset += 4;
 441          $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
 442  
 443          $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
 444          $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
 445          for ($i = 0; $i < $CommentsCount; $i++) {
 446  
 447              $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
 448  
 449              if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
 450                  if ($oggpageinfo = $this->ParseOggPageHeader()) {
 451                      $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 452  
 453                      $VorbisCommentPage++;
 454  
 455                      // First, save what we haven't read yet
 456                      $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 457  
 458                      // Then take that data off the end
 459                      $commentdata     = substr($commentdata, 0, $commentdataoffset);
 460  
 461                      // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 462                      $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 463                      $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 464  
 465                      // Finally, stick the unused data back on the end
 466                      $commentdata .= $AsYetUnusedData;
 467  
 468                      //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 469                      $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
 470                  }
 471  
 472              }
 473              $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 474  
 475              // replace avdataoffset with position just after the last vorbiscomment
 476              $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4;
 477  
 478              $commentdataoffset += 4;
 479              while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
 480                  if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
 481                      $info['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments';
 482                      break 2;
 483                  }
 484  
 485                  $VorbisCommentPage++;
 486  
 487                  $oggpageinfo = $this->ParseOggPageHeader();
 488                  $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 489  
 490                  // First, save what we haven't read yet
 491                  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 492  
 493                  // Then take that data off the end
 494                  $commentdata     = substr($commentdata, 0, $commentdataoffset);
 495  
 496                  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 497                  $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 498                  $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 499  
 500                  // Finally, stick the unused data back on the end
 501                  $commentdata .= $AsYetUnusedData;
 502  
 503                  //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 504                  if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
 505                      $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
 506                      break;
 507                  }
 508                  $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
 509                  if ($readlength <= 0) {
 510                      $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
 511                      break;
 512                  }
 513                  $commentdata .= $this->fread($readlength);
 514  
 515                  //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
 516              }
 517              $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset;
 518              $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']);
 519              $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size'];
 520  
 521              if (!$commentstring) {
 522  
 523                  // no comment?
 524                  $info['warning'][] = 'Blank Ogg comment ['.$i.']';
 525  
 526              } elseif (strstr($commentstring, '=')) {
 527  
 528                  $commentexploded = explode('=', $commentstring, 2);
 529                  $ThisFileInfo_ogg_comments_raw[$i]['key']   = strtoupper($commentexploded[0]);
 530                  $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
 531  
 532                  if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
 533  
 534                      // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
 535                      // 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.
 536                      // http://flac.sourceforge.net/format.html#metadata_block_picture
 537                      $flac = new getid3_flac($this->getid3);
 538                      $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
 539                      $flac->parsePICTURE();
 540                      $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
 541                      unset($flac);
 542  
 543                  } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
 544  
 545                      $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
 546                      $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
 547                      /** @todo use 'coverartmime' where available */
 548                      $imageinfo = getid3_lib::GetDataImageSize($data);
 549                      if ($imageinfo === false || !isset($imageinfo['mime'])) {
 550                          $this->warning('COVERART vorbiscomment tag contains invalid image');
 551                          continue;
 552                      }
 553  
 554                      $ogg = new self($this->getid3);
 555                      $ogg->setStringMode($data);
 556                      $info['ogg']['comments']['picture'][] = array(
 557                          'image_mime' => $imageinfo['mime'],
 558                          'data'       => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
 559                      );
 560                      unset($ogg);
 561  
 562                  } else {
 563  
 564                      $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
 565  
 566                  }
 567  
 568              } else {
 569  
 570                  $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
 571  
 572              }
 573              unset($ThisFileInfo_ogg_comments_raw[$i]);
 574          }
 575          unset($ThisFileInfo_ogg_comments_raw);
 576  
 577  
 578          // Replay Gain Adjustment
 579          // http://privatewww.essex.ac.uk/~djmrob/replaygain/
 580          if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) {
 581              foreach ($info['ogg']['comments'] as $index => $commentvalue) {
 582                  switch ($index) {
 583                      case 'rg_audiophile':
 584                      case 'replaygain_album_gain':
 585                          $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
 586                          unset($info['ogg']['comments'][$index]);
 587                          break;
 588  
 589                      case 'rg_radio':
 590                      case 'replaygain_track_gain':
 591                          $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
 592                          unset($info['ogg']['comments'][$index]);
 593                          break;
 594  
 595                      case 'replaygain_album_peak':
 596                          $info['replay_gain']['album']['peak'] = (double) $commentvalue[0];
 597                          unset($info['ogg']['comments'][$index]);
 598                          break;
 599  
 600                      case 'rg_peak':
 601                      case 'replaygain_track_peak':
 602                          $info['replay_gain']['track']['peak'] = (double) $commentvalue[0];
 603                          unset($info['ogg']['comments'][$index]);
 604                          break;
 605  
 606                      case 'replaygain_reference_loudness':
 607                          $info['replay_gain']['reference_volume'] = (double) $commentvalue[0];
 608                          unset($info['ogg']['comments'][$index]);
 609                          break;
 610  
 611                      default:
 612                          // do nothing
 613                          break;
 614                  }
 615              }
 616          }
 617  
 618          $this->fseek($OriginalOffset);
 619  
 620          return true;
 621      }
 622  
 623  	public static function SpeexBandModeLookup($mode) {
 624          static $SpeexBandModeLookup = array();
 625          if (empty($SpeexBandModeLookup)) {
 626              $SpeexBandModeLookup[0] = 'narrow';
 627              $SpeexBandModeLookup[1] = 'wide';
 628              $SpeexBandModeLookup[2] = 'ultra-wide';
 629          }
 630          return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
 631      }
 632  
 633  
 634  	public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
 635          for ($i = 0; $i < $SegmentNumber; $i++) {
 636              $segmentlength = 0;
 637              foreach ($OggInfoArray['segment_table'] as $key => $value) {
 638                  $segmentlength += $value;
 639                  if ($value < 255) {
 640                      break;
 641                  }
 642              }
 643          }
 644          return $segmentlength;
 645      }
 646  
 647  
 648  	public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
 649  
 650          // decrease precision
 651          $nominal_bitrate = $nominal_bitrate / 1000;
 652  
 653          if ($nominal_bitrate < 128) {
 654              // q-1 to q4
 655              $qval = ($nominal_bitrate - 64) / 16;
 656          } elseif ($nominal_bitrate < 256) {
 657              // q4 to q8
 658              $qval = $nominal_bitrate / 32;
 659          } elseif ($nominal_bitrate < 320) {
 660              // q8 to q9
 661              $qval = ($nominal_bitrate + 256) / 64;
 662          } else {
 663              // q9 to q10
 664              $qval = ($nominal_bitrate + 1300) / 180;
 665          }
 666          //return $qval; // 5.031324
 667          //return intval($qval); // 5
 668          return round($qval, 1); // 5 or 4.9
 669      }
 670  
 671  }


Generated: Tue Mar 25 01:41:18 2014 WordPress honlapkészítés: online1.hu