public function ParseRIFF($start_offset, $max_offset)
 {
     $getid3 = $this->getid3;
     $info =& $getid3->info;
     $endian_function = $this->endian_function;
     $max_offset = min($max_offset, $info['avdataend']);
     $riff_chunk = false;
     $this->fseek($start_offset, SEEK_SET);
     while ($this->ftell() < $max_offset) {
         $chunk_name = $this->fread(4);
         if (strlen($chunk_name) < 4) {
             throw new getid3_exception('Expecting chunk name at offset ' . ($this->ftell() - 4) . ' but found nothing. Aborting RIFF parsing.');
         }
         $chunk_size = getid3_lib::$endian_function($this->fread(4));
         if ($chunk_size == 0) {
             continue;
             throw new getid3_exception('Chunk size at offset ' . ($this->ftell() - 4) . ' is zero. Aborting RIFF parsing.');
         }
         if ($chunk_size % 2 != 0) {
             // all structures are packed on word boundaries
             $chunk_size++;
         }
         switch ($chunk_name) {
             case 'LIST':
                 $list_name = $this->fread(4);
                 switch ($list_name) {
                     case 'movi':
                     case 'rec ':
                         $riff_chunk[$list_name]['offset'] = $this->ftell() - 4;
                         $riff_chunk[$list_name]['size'] = $chunk_size;
                         static $parsed_audio_stream = false;
                         if (!$parsed_audio_stream) {
                             $where_we_were = $this->ftell();
                             $audio_chunk_header = $this->fread(12);
                             $audio_chunk_stream_num = substr($audio_chunk_header, 0, 2);
                             $audio_chunk_stream_type = substr($audio_chunk_header, 2, 2);
                             $audio_chunk_size = getid3_lib::LittleEndian2Int(substr($audio_chunk_header, 4, 4));
                             if ($audio_chunk_stream_type == 'wb') {
                                 $first_four_bytes = substr($audio_chunk_header, 8, 4);
                                 //// MPEG
                                 if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $first_four_bytes)) {
                                     if (!$getid3->include_module_optional('audio.mp3')) {
                                         $getid3->warning('MP3 skipped because mp3 module is missing.');
                                     } elseif (getid3_mp3::MPEGaudioHeaderBytesValid($first_four_bytes)) {
                                         // Clone getid3 - messing with offsets - better safe than sorry
                                         $clone = clone $getid3;
                                         $clone->info['avdataoffset'] = $this->ftell() - 4;
                                         $clone->info['avdataend'] = $this->ftell() + $audio_chunk_size;
                                         $mp3 = new getid3_mp3($clone);
                                         $mp3->AnalyzeMPEGaudioInfo();
                                         // Import from clone and destroy
                                         if (isset($clone->info['mpeg']['audio'])) {
                                             $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
                                             $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
                                             $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                             $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                             $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                             $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                             $info['bitrate'] = $info['audio']['bitrate'];
                                             $getid3->warning($clone->warnings());
                                             unset($clone);
                                         }
                                     }
                                 } elseif (preg_match('/^\\x0B\\x77/s', $first_four_bytes)) {
                                     if (!$getid3->include_module_optional('audio.ac3')) {
                                         $getid3->warning('AC3 skipped because ac3 module is missing.');
                                     } else {
                                         // Clone getid3 - messing with offsets - better safe than sorry
                                         $clone = clone $getid3;
                                         $clone->info['avdataoffset'] = $this->ftell() - 4;
                                         $clone->info['avdataend'] = $this->ftell() + $audio_chunk_size;
                                         // Analyze clone by fp
                                         $ac3 = new getid3_ac3($clone);
                                         $ac3->Analyze();
                                         // Import from clone and destroy
                                         $info['audio'] = $clone->info['audio'];
                                         $info['ac3'] = $clone->info['ac3'];
                                         $getid3->warning($clone->warnings());
                                         unset($clone);
                                     }
                                 }
                             }
                             $parsed_audio_stream = true;
                             $this->fseek($where_we_were, SEEK_SET);
                         }
                         $this->fseek($chunk_size - 4, SEEK_CUR);
                         break;
                     default:
                         if (!isset($riff_chunk[$list_name])) {
                             $riff_chunk[$list_name] = array();
                         }
                         $list_chunk_parent = $list_name;
                         $list_chunk_max_offset = $this->ftell() - 4 + $chunk_size;
                         if ($parsed_chunk = $this->ParseRIFF($this->ftell(), $this->ftell() + $chunk_size - 4)) {
                             $riff_chunk[$list_name] = array_merge_recursive($riff_chunk[$list_name], $parsed_chunk);
                         }
                         break;
                 }
                 break;
             default:
                 $this_index = 0;
                 if (isset($riff_chunk[$chunk_name]) && is_array($riff_chunk[$chunk_name])) {
                     $this_index = count($riff_chunk[$chunk_name]);
                 }
                 $riff_chunk[$chunk_name][$this_index]['offset'] = $this->ftell() - 8;
                 $riff_chunk[$chunk_name][$this_index]['size'] = $chunk_size;
                 switch ($chunk_name) {
                     case 'data':
                         $info['avdataoffset'] = $this->ftell();
                         $info['avdataend'] = $info['avdataoffset'] + $chunk_size;
                         $riff_data_chunk_contents_test = $this->fread(36);
                         //// This is probably MP3 data
                         if (strlen($riff_data_chunk_contents_test) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($riff_data_chunk_contents_test, 0, 4))) {
                             try {
                                 if (!$getid3->include_module_optional('audio.mp3')) {
                                     $getid3->warning('MP3 skipped because mp3 module is missing.');
                                 }
                                 // Clone getid3 - messing with offsets - better safe than sorry
                                 $clone = clone $getid3;
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($riff_data_chunk_contents_test, 0, 4))) {
                                     $mp3 = new getid3_mp3($clone);
                                     $mp3->AnalyzeMPEGaudioInfo();
                                     // Import from clone and destroy
                                     if (isset($clone->info['mpeg']['audio'])) {
                                         $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
                                         $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                         $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                         $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                         $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                         $info['bitrate'] = $info['audio']['bitrate'];
                                         $getid3->warning($clone->warnings());
                                         unset($clone);
                                     }
                                 }
                             } catch (Exception $e) {
                                 // do nothing - not MP3 data
                             }
                         } elseif (strlen($riff_data_chunk_contents_test) > 0 && substr($riff_data_chunk_contents_test, 0, 2) == "\vw") {
                             if (!$getid3->include_module_optional('audio.ac3')) {
                                 $getid3->warning('AC3 skipped because ac3 module is missing.');
                             } else {
                                 // Clone getid3 - messing with offsets - better safe than sorry
                                 $clone = clone $getid3;
                                 $clone->info['avdataoffset'] = $riff_chunk[$chunk_name][$this_index]['offset'];
                                 $clone->info['avdataend'] = $clone->info['avdataoffset'] + $riff_chunk[$chunk_name][$this_index]['size'];
                                 // Analyze clone by fp
                                 $ac3 = new getid3_ac3($clone);
                                 $ac3->Analyze();
                                 // Import from clone and destroy
                                 $info['audio'] = $clone->info['audio'];
                                 $info['ac3'] = $clone->info['ac3'];
                                 $getid3->warning($clone->warnings());
                                 unset($clone);
                             }
                         } elseif (strlen($riff_data_chunk_contents_test) > 0 && substr($riff_data_chunk_contents_test, 8, 2) == "w\v") {
                             if (!$getid3->include_module_optional('audio.ac3')) {
                                 $getid3->warning('AC3 skipped because ac3 module is missing.');
                             } else {
                                 // Extract ac3 data to string
                                 $ac3_data = '';
                                 for ($i = 0; $i < 28; $i += 2) {
                                     // swap byte order
                                     $ac3_data .= substr($riff_data_chunk_contents_test, 8 + $i + 1, 1);
                                     $ac3_data .= substr($riff_data_chunk_contents_test, 8 + $i + 0, 1);
                                 }
                                 // Clone getid3 - messing with offsets - better safe than sorry
                                 $clone = clone $getid3;
                                 $clone->info['avdataoffset'] = 0;
                                 $clone->info['avdataend'] = 20;
                                 // Analyse clone by string
                                 $ac3 = new getid3_ac3($clone);
                                 $ac3->AnalyzeString($ac3_data);
                                 // Import from clone and destroy
                                 $info['audio'] = $clone->info['audio'];
                                 $info['ac3'] = $clone->info['ac3'];
                                 $getid3->warning($clone->warnings());
                                 unset($clone);
                             }
                         }
                         if (strlen($riff_data_chunk_contents_test) > 0 && substr($riff_data_chunk_contents_test, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $info['wavpack']['offset'] = $riff_chunk[$chunk_name][$this_index]['offset'];
                             $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($riff_data_chunk_contents_test, 4, 4));
                             $this->RIFFparseWavPackHeader(substr($riff_data_chunk_contents_test, 8, 28));
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         $this->fseek($riff_chunk[$chunk_name][$this_index]['offset'] + 8 + $chunk_size, SEEK_SET);
                         break;
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                         $riff_chunk[$chunk_name][$this_index]['data'] = $this->fread($chunk_size);
                         break;
                     default:
                         if (!empty($list_chunk_parent) && $riff_chunk[$chunk_name][$this_index]['offset'] + $riff_chunk[$chunk_name][$this_index]['size'] <= $list_chunk_max_offset) {
                             $riff_chunk[$list_chunk_parent][$chunk_name][$this_index]['offset'] = $riff_chunk[$chunk_name][$this_index]['offset'];
                             $riff_chunk[$list_chunk_parent][$chunk_name][$this_index]['size'] = $riff_chunk[$chunk_name][$this_index]['size'];
                             unset($riff_chunk[$chunk_name][$this_index]['offset']);
                             unset($riff_chunk[$chunk_name][$this_index]['size']);
                             if (isset($riff_chunk[$chunk_name][$this_index]) && empty($riff_chunk[$chunk_name][$this_index])) {
                                 unset($riff_chunk[$chunk_name][$this_index]);
                             }
                             if (isset($riff_chunk[$chunk_name]) && empty($riff_chunk[$chunk_name])) {
                                 unset($riff_chunk[$chunk_name]);
                             }
                             $riff_chunk[$list_chunk_parent][$chunk_name][$this_index]['data'] = $this->fread($chunk_size);
                         } elseif ($chunk_size < 2048) {
                             // only read data in if smaller than 2kB
                             $riff_chunk[$chunk_name][$this_index]['data'] = $this->fread($chunk_size);
                         } else {
                             $this->fseek($chunk_size, SEEK_CUR);
                         }
                         break;
                 }
                 break;
         }
     }
     return $riff_chunk;
 }
 public function Analyze()
 {
     $getid3 = $this->getid3;
     $info =& $getid3->info;
     $getid3->include_module('audio.mp3');
     $info['quicktime'] = array();
     $info_quicktime =& $info['quicktime'];
     $info['fileformat'] = 'quicktime';
     $info_quicktime['hinting'] = false;
     fseek($getid3->fp, $info['avdataoffset'], SEEK_SET);
     $offset = $atom_counter = 0;
     while ($offset < $info['avdataend']) {
         fseek($getid3->fp, $offset, SEEK_SET);
         $atom_header = fread($getid3->fp, 8);
         $atom_size = getid3_lib::BigEndian2Int(substr($atom_header, 0, 4));
         $atom_name = substr($atom_header, 4, 4);
         $info_quicktime[$atom_name]['name'] = $atom_name;
         $info_quicktime[$atom_name]['size'] = $atom_size;
         $info_quicktime[$atom_name]['offset'] = $offset;
         if ($offset + $atom_size > $info['avdataend']) {
             throw new getid3_exception('Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atom_size . ' bytes)');
         }
         if ($atom_size == 0) {
             // Furthermore, for historical reasons the list of atoms is optionally
             // terminated by a 32-bit integer set to 0. If you are writing a program
             // to read user data atoms, you should allow for the terminating 0.
             break;
         }
         switch ($atom_name) {
             case 'mdat':
                 // Media DATa atom
                 // 'mdat' contains the actual data for the audio/video
                 if ($atom_size > 8 && (!isset($info['avdataend_tmp']) || $info_quicktime[$atom_name]['size'] > $info['avdataend_tmp'] - $info['avdataoffset'])) {
                     $info['avdataoffset'] = $info_quicktime[$atom_name]['offset'] + 8;
                     $old_av_data_end = $info['avdataend'];
                     $info['avdataend'] = $info_quicktime[$atom_name]['offset'] + $info_quicktime[$atom_name]['size'];
                     //// MP3
                     if (!$getid3->include_module_optional('audio.mp3')) {
                         $getid3->warning('MP3 skipped because mpeg module is missing.');
                     } else {
                         // Clone getid3 - messing with offsets - better safe than sorry
                         $clone = clone $getid3;
                         if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($clone->fp, 4)))) {
                             $mp3 = new getid3_mp3($clone);
                             $mp3->AnalyzeMPEGaudioInfo();
                             // Import from clone and destroy
                             if (isset($clone->info['mpeg']['audio'])) {
                                 $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
                                 $info['audio']['dataformat'] = 'mp3';
                                 $info['audio']['codec'] = !empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3'));
                                 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                 $info['bitrate'] = $info['audio']['bitrate'];
                                 $getid3->warning($clone->warnings());
                                 unset($clone);
                             }
                         }
                     }
                     $info['avdataend'] = $old_av_data_end;
                     unset($old_av_data_end);
                 }
                 break;
             case 'free':
                 // FREE space atom
             // FREE space atom
             case 'skip':
                 // SKIP atom
             // SKIP atom
             case 'wide':
                 // 64-bit expansion placeholder atom
                 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
                 break;
             default:
                 $atom_hierarchy = array();
                 $info_quicktime[$atom_name] = $this->QuicktimeParseAtom($atom_name, $atom_size, fread($getid3->fp, $atom_size), $offset, $atom_hierarchy);
                 break;
         }
         $offset += $atom_size;
         $atom_counter++;
     }
     if (!empty($info['avdataend_tmp'])) {
         // this value is assigned to a temp value and then erased because
         // otherwise any atoms beyond the 'mdat' atom would not get parsed
         $info['avdataend'] = $info['avdataend_tmp'];
         unset($info['avdataend_tmp']);
     }
     if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info_quicktime['video'])) {
         $info['audio']['bitrate'] = $info['bitrate'];
     }
     if (@$info['audio']['dataformat'] == 'mp4' && empty($info['video']['resolution_x'])) {
         $info['fileformat'] = 'mp4';
         $info['mime_type'] = 'audio/mp4';
         unset($info['video']['dataformat']);
     }
     if (!$getid3->option_extra_info) {
         unset($info_quicktime['moov']);
     }
     if (empty($info['audio']['dataformat']) && !empty($info_quicktime['audio'])) {
         $info['audio']['dataformat'] = 'quicktime';
     }
     if (empty($info['video']['dataformat']) && !empty($info_quicktime['video'])) {
         $info['video']['dataformat'] = 'quicktime';
     }
     return true;
 }