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; }