function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) { $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']); $RIFFchunk = false; fseek($fd, $startoffset, SEEK_SET); while (ftell($fd) < $maxoffset) { $chunkname = fread($fd, 4); if (strlen($chunkname) < 4) { $ThisFileInfo['error'][] = 'Expecting chunk name at offset ' . (ftell($fd) - 4) . ' but found nothing. Aborting RIFF parsing.'; break; } $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, fread($fd, 4)); if ($chunksize == 0) { $ThisFileInfo['error'][] = 'Chunk size at offset ' . (ftell($fd) - 4) . ' is zero. Aborting RIFF parsing.'; break; } if ($chunksize % 2 != 0) { // all structures are packed on word boundaries $chunksize++; } switch ($chunkname) { case 'LIST': $listname = fread($fd, 4); switch ($listname) { case 'movi': case 'rec ': $RIFFchunk[$listname]['offset'] = ftell($fd) - 4; $RIFFchunk[$listname]['size'] = $chunksize; static $ParsedAudioStream = false; if ($ParsedAudioStream) { // skip over } else { $WhereWeWere = ftell($fd); $AudioChunkHeader = fread($fd, 12); $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); if ($AudioChunkStreamType == 'wb') { $FirstFourBytes = substr($AudioChunkHeader, 8, 4); if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) { // MP3 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { $dummy = $ThisFileInfo; $dummy['avdataoffset'] = ftell($fd) - 4; $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false); if (isset($dummy['mpeg']['audio'])) { $ThisFileInfo = $dummy; $ThisFileInfo['audio']['dataformat'] = 'mp' . $ThisFileInfo['mpeg']['audio']['layer']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); } } } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) { // AC3 $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { $dummy = $ThisFileInfo; $dummy['avdataoffset'] = ftell($fd) - 4; $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; $dummy['error'] = array(); $ac3_tag = new getid3_ac3($fd, $dummy); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['ac3'] = $dummy['ac3']; $ThisFileInfo['warning'] = $dummy['warning']; } } } } $ParsedAudioStream = true; fseek($fd, $WhereWeWere, SEEK_SET); } fseek($fd, $chunksize - 4, SEEK_CUR); break; default: if (!isset($RIFFchunk[$listname])) { $RIFFchunk[$listname] = array(); } $LISTchunkParent = $listname; $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize; if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) { $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); } break; } break; default: $thisindex = 0; if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8; $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; switch ($chunkname) { case 'data': $ThisFileInfo['avdataoffset'] = ftell($fd); $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize; $RIFFdataChunkContentsTest = fread($fd, 36); if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { // Probably is MP3 data if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $RIFFchunk[$chunkname][$thisindex]['offset'], false); } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") { // This is probably AC-3 data $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { $dummy = $ThisFileInfo; $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size']; $dummy['error'] = array(); $ac3_tag = new getid3_ac3($fd, $dummy); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['ac3'] = $dummy['ac3']; $ThisFileInfo['warning'] = $dummy['warning']; } } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") { // Dolby Digital WAV // AC-3 content, but not encoded in same format as normal AC-3 file // For one thing, byte order is swapped $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { // ok to use tmpfile here - only 56 bytes if ($fd_temp = tmpfile()) { for ($i = 0; $i < 28; $i += 2) { // swap byte order fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); } $dummy = $ThisFileInfo; $dummy['avdataoffset'] = 0; $dummy['avdataend'] = 20; $dummy['error'] = array(); $ac3_tag = new getid3_ac3($fd_temp, $dummy); fclose($fd_temp); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['ac3'] = $dummy['ac3']; $ThisFileInfo['warning'] = $dummy['warning']; } else { $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: ' . explode(';', $dummy['error']); } } else { $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV'; } } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') { // This is WavPack data $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo); } else { // This is some other kind of data (quite possibly just PCM) // do nothing special, just skip it } fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); break; case 'bext': case 'cart': case 'fmt ': case 'MEXT': case 'DISP': // always read data in $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); break; default: if (!empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) { $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; unset($RIFFchunk[$chunkname][$thisindex]['offset']); unset($RIFFchunk[$chunkname][$thisindex]['size']); if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { unset($RIFFchunk[$chunkname][$thisindex]); } if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { unset($RIFFchunk[$chunkname]); } $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize); } elseif ($chunksize < 2048) { // only read data in if smaller than 2kB $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); } else { fseek($fd, $chunksize, SEEK_CUR); } break; } break; } } return $RIFFchunk; }
public function ParseRIFF($startoffset, $maxoffset) { $info =& $this->getid3->info; $RIFFchunk = false; $FoundAllChunksWeNeed = false; try { $this->fseek($startoffset); $maxoffset = min($maxoffset, $info['avdataend']); while ($this->ftell() < $maxoffset) { $chunknamesize = $this->fread(8); //$chunkname = substr($chunknamesize, 0, 4); $chunkname = str_replace("", '_', substr($chunknamesize, 0, 4)); // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4)); //if (strlen(trim($chunkname, "\x00")) < 4) { if (strlen($chunkname) < 4) { $this->error('Expecting chunk name at offset ' . ($this->ftell() - 8) . ' but found nothing. Aborting RIFF parsing.'); break; } if ($chunksize == 0 && $chunkname != 'JUNK') { $this->warning('Chunk (' . $chunkname . ') size at offset ' . ($this->ftell() - 4) . ' is zero. Aborting RIFF parsing.'); break; } if ($chunksize % 2 != 0) { // all structures are packed on word boundaries $chunksize++; } switch ($chunkname) { case 'LIST': $listname = $this->fread(4); if (preg_match('#^(movi|rec )$#i', $listname)) { $RIFFchunk[$listname]['offset'] = $this->ftell() - 4; $RIFFchunk[$listname]['size'] = $chunksize; if (!$FoundAllChunksWeNeed) { $WhereWeWere = $this->ftell(); $AudioChunkHeader = $this->fread(12); $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); if ($AudioChunkStreamType == 'wb') { $FirstFourBytes = substr($AudioChunkHeader, 8, 4); if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) { // MP3 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $this->ftell() - 4; $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__); $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); if (isset($getid3_temp->info['mpeg']['audio'])) { $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio']; $info['audio'] = $getid3_temp->info['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']; } unset($getid3_temp, $getid3_mp3); } } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) { // AC3 $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $this->ftell() - 4; $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; if (!empty($getid3_temp->info['warning'])) { foreach ($getid3_temp->info['warning'] as $key => $value) { $info['warning'][] = $value; } } } unset($getid3_temp, $getid3_ac3); } } $FoundAllChunksWeNeed = true; $this->fseek($WhereWeWere); } $this->fseek($chunksize - 4, SEEK_CUR); } else { if (!isset($RIFFchunk[$listname])) { $RIFFchunk[$listname] = array(); } $LISTchunkParent = $listname; $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize; if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) { $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); } } break; default: if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) { $this->fseek($chunksize, SEEK_CUR); break; } $thisindex = 0; if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8; $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; switch ($chunkname) { case 'data': $info['avdataoffset'] = $this->ftell(); $info['avdataend'] = $info['avdataoffset'] + $chunksize; $testData = $this->fread(36); if ($testData === '') { break; } if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($testData, 0, 4))) { // Probably is MP3 data if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__); $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['mpeg'] = $getid3_temp->info['mpeg']; } unset($getid3_temp, $getid3_mp3); } } elseif (($isRegularAC3 = substr($testData, 0, 2) == getid3_ac3::syncword) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) { // This is probably AC-3 data $getid3_temp = new getID3(); if ($isRegularAC3) { $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataend'] = $info['avdataend']; } $getid3_ac3 = new getid3_ac3($getid3_temp); if ($isRegularAC3) { $getid3_ac3->Analyze(); } else { // Dolby Digital WAV // AC-3 content, but not encoded in same format as normal AC-3 file // For one thing, byte order is swapped $ac3_data = ''; for ($i = 0; $i < 28; $i += 2) { $ac3_data .= substr($testData, 8 + $i + 1, 1); $ac3_data .= substr($testData, 8 + $i + 0, 1); } $getid3_ac3->AnalyzeString($ac3_data); } if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; if (!empty($getid3_temp->info['warning'])) { foreach ($getid3_temp->info['warning'] as $newerror) { $this->warning('getid3_ac3() says: [' . $newerror . ']'); } } } unset($getid3_temp, $getid3_ac3); } elseif (preg_match('/^(' . implode('|', array_map('preg_quote', getid3_dts::$syncwords)) . ')/', $testData)) { // This is probably DTS data $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_dts = new getid3_dts($getid3_temp); $getid3_dts->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['dts'] = $getid3_temp->info['dts']; $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing if (!empty($getid3_temp->info['warning'])) { foreach ($getid3_temp->info['warning'] as $newerror) { $this->warning('getid3_dts() says: [' . $newerror . ']'); } } } unset($getid3_temp, $getid3_dts); } elseif (substr($testData, 0, 4) == 'wvpk') { // This is WavPack data $info['wavpack']['offset'] = $info['avdataoffset']; $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4)); $this->parseWavPackHeader(substr($testData, 8, 28)); } else { // This is some other kind of data (quite possibly just PCM) // do nothing special, just skip it } $nextoffset = $info['avdataend']; $this->fseek($nextoffset); break; case 'iXML': case 'bext': case 'cart': case 'fmt ': case 'strh': case 'strf': case 'indx': case 'MEXT': case 'DISP': // always read data in // always read data in case 'JUNK': // should be: never read data in // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc) if ($chunksize < 1048576) { if ($chunksize > 0) { $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize); if ($chunkname == 'JUNK') { if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) { // only keep text characters [chr(32)-chr(127)] $info['riff']['comments']['junk'][] = trim($matches[1]); } // but if nothing there, ignore // remove the key in either case unset($RIFFchunk[$chunkname][$thisindex]['data']); } } } else { $this->warning('Chunk "' . $chunkname . '" at offset ' . $this->ftell() . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data'); $this->fseek($chunksize, SEEK_CUR); } break; //case 'IDVX': // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize)); // break; //case 'IDVX': // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize)); // break; default: if (!empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) { $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; unset($RIFFchunk[$chunkname][$thisindex]['offset']); unset($RIFFchunk[$chunkname][$thisindex]['size']); if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { unset($RIFFchunk[$chunkname][$thisindex]); } if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { unset($RIFFchunk[$chunkname]); } $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize); } elseif ($chunksize < 2048) { // only read data in if smaller than 2kB $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize); } else { $this->fseek($chunksize, SEEK_CUR); } break; } break; } } } catch (getid3_exception $e) { if ($e->getCode() == 10) { $this->warning('RIFF parser: ' . $e->getMessage()); } else { throw $e; } } return $RIFFchunk; }
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; }
function ParseRIFF($startoffset, $maxoffset) { $info =& $this->getid3->info; $maxoffset = min($maxoffset, $info['avdataend']); $RIFFchunk = false; $FoundAllChunksWeNeed = false; if ($startoffset < 0 || !getid3_lib::intValueSupported($startoffset)) { $info['warning'][] = 'Unable to ParseRIFF() at ' . $startoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; return false; } $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset); if ($maxoffset > $max_usable_offset) { $info['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at ' . $startoffset . ' because beyond it extends to ' . $maxoffset . ', which is beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; } fseek($this->getid3->fp, $startoffset, SEEK_SET); while (ftell($this->getid3->fp) < $max_usable_offset) { $chunknamesize = fread($this->getid3->fp, 8); $chunkname = substr($chunknamesize, 0, 4); $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4)); if (strlen($chunkname) < 4) { $info['error'][] = 'Expecting chunk name at offset ' . (ftell($this->getid3->fp) - 4) . ' but found nothing. Aborting RIFF parsing.'; break; } if ($chunksize == 0) { if ($chunkname == 'JUNK') { // we'll allow zero-size JUNK frames } else { $info['warning'][] = 'Chunk size at offset ' . (ftell($this->getid3->fp) - 4) . ' is zero. Aborting RIFF parsing.'; break; } } if ($chunksize % 2 != 0) { // all structures are packed on word boundaries $chunksize++; } switch ($chunkname) { case 'LIST': $listname = fread($this->getid3->fp, 4); if (preg_match('#^(movi|rec )$#i', $listname)) { $RIFFchunk[$listname]['offset'] = ftell($this->getid3->fp) - 4; $RIFFchunk[$listname]['size'] = $chunksize; if ($FoundAllChunksWeNeed) { // skip over } else { $WhereWeWere = ftell($this->getid3->fp); $AudioChunkHeader = fread($this->getid3->fp, 12); $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); if ($AudioChunkStreamType == 'wb') { $FirstFourBytes = substr($AudioChunkHeader, 8, 4); if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) { // MP3 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4; $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize; $getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); if (isset($getid3_temp->info['mpeg']['audio'])) { $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio']; $info['audio'] = $getid3_temp->info['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']; } unset($getid3_temp, $getid3_mp3); } } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) { // AC3 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4; $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; if (!empty($getid3_temp->info['warning'])) { foreach ($getid3_temp->info['warning'] as $key => $value) { $info['warning'][] = $value; } } } unset($getid3_temp, $getid3_ac3); } } } $FoundAllChunksWeNeed = true; fseek($this->getid3->fp, $WhereWeWere, SEEK_SET); } fseek($this->getid3->fp, $chunksize - 4, SEEK_CUR); //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) { // // // data chunk, ignore // } else { if (!isset($RIFFchunk[$listname])) { $RIFFchunk[$listname] = array(); } $LISTchunkParent = $listname; $LISTchunkMaxOffset = ftell($this->getid3->fp) - 4 + $chunksize; if ($parsedChunk = $this->ParseRIFF(ftell($this->getid3->fp), ftell($this->getid3->fp) + $chunksize - 4)) { $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); } } break; default: if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) { $nextoffset = ftell($this->getid3->fp) + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 2; } fseek($this->getid3->fp, $nextoffset, SEEK_SET); break; } $thisindex = 0; if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($this->getid3->fp) - 8; $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; switch ($chunkname) { case 'data': $info['avdataoffset'] = ftell($this->getid3->fp); $info['avdataend'] = $info['avdataoffset'] + $chunksize; $RIFFdataChunkContentsTest = fread($this->getid3->fp, 36); if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { // Probably is MP3 data if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']; $getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false); if (empty($getid3_temp->info['error'])) { $info['mpeg'] = $getid3_temp->info['mpeg']; $info['audio'] = $getid3_temp->info['audio']; } unset($getid3_temp, $getid3_mp3); } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") { // This is probably AC-3 data if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; $info['warning'] = $getid3_temp->info['warning']; } unset($getid3_temp, $getid3_ac3); } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") { // Dolby Digital WAV // AC-3 content, but not encoded in same format as normal AC-3 file // For one thing, byte order is swapped if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { // ok to use tmpfile here - only 56 bytes if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) { if ($fd_temp = fopen($RIFFtempfilename, 'wb')) { for ($i = 0; $i < 28; $i += 2) { // swap byte order fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); } fclose($fd_temp); $getid3_temp = new getID3(); $getid3_temp->openfile($RIFFtempfilename); $getid3_temp->info['avdataend'] = 20; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; $info['warning'] = $getid3_temp->info['warning']; } else { $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): ' . implode(';', $getid3_temp->info['error']); } unset($getid3_ac3, $getid3_temp); } else { $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file'; } unlink($RIFFtempfilename); } else { $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file'; } } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') { // This is WavPack data $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); $this->RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28)); } else { // This is some other kind of data (quite possibly just PCM) // do nothing special, just skip it } $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 3; } fseek($this->getid3->fp, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); break; case 'iXML': case 'bext': case 'cart': case 'fmt ': case 'strh': case 'strf': case 'indx': case 'MEXT': case 'DISP': // always read data in // always read data in case 'JUNK': // should be: never read data in // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc) if ($chunksize < 1048576) { if ($chunksize > 0) { $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize); if ($chunkname == 'JUNK') { if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) { // only keep text characters [chr(32)-chr(127)] $info['riff']['comments']['junk'][] = trim($matches[1]); } // but if nothing there, ignore // remove the key in either case unset($RIFFchunk[$chunkname][$thisindex]['data']); } } } else { $info['warning'][] = 'chunk "' . $chunkname . '" at offset ' . ftell($this->getid3->fp) . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data'; $nextoffset = ftell($this->getid3->fp) + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 3; } fseek($this->getid3->fp, $nextoffset, SEEK_SET); } break; default: if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) { $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; unset($RIFFchunk[$chunkname][$thisindex]['offset']); unset($RIFFchunk[$chunkname][$thisindex]['size']); if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { unset($RIFFchunk[$chunkname][$thisindex]); } if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { unset($RIFFchunk[$chunkname]); } $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize); //} elseif (in_array($chunkname, array('ID3 ')) || (($chunksize > 0) && ($chunksize < 2048))) { } elseif ($chunksize > 0 && $chunksize < 2048) { // only read data in if smaller than 2kB $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize); } else { $nextoffset = ftell($this->getid3->fp) + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 3; } fseek($this->getid3->fp, $nextoffset, SEEK_SET); } break; } break; } } return $RIFFchunk; }
static function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) { $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']); $RIFFchunk = false; $FoundAllChunksWeNeed = false; if ($startoffset < 0 || !getid3_lib::intValueSupported($startoffset)) { $ThisFileInfo['warning'][] = 'Unable to ParseRIFF() at ' . $startoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; return false; } $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset); if ($maxoffset > $max_usable_offset) { $ThisFileInfo['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at ' . $startoffset . ' because beyond it extends to ' . $maxoffset . ', which is beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; } fseek($fd, $startoffset, SEEK_SET); while (ftell($fd) < $max_usable_offset) { $chunknamesize = fread($fd, 8); $chunkname = substr($chunknamesize, 0, 4); $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($chunknamesize, 4, 4)); if (strlen($chunkname) < 4) { $ThisFileInfo['error'][] = 'Expecting chunk name at offset ' . (ftell($fd) - 4) . ' but found nothing. Aborting RIFF parsing.'; break; } if ($chunksize == 0) { if ($chunkname == 'JUNK') { // we'll allow zero-size JUNK frames } else { $ThisFileInfo['warning'][] = 'Chunk size at offset ' . (ftell($fd) - 4) . ' is zero. Aborting RIFF parsing.'; break; } } if ($chunksize % 2 != 0) { // all structures are packed on word boundaries $chunksize++; } switch ($chunkname) { case 'LIST': $listname = fread($fd, 4); if (preg_match('#^(movi|rec )$#i', $listname)) { $RIFFchunk[$listname]['offset'] = ftell($fd) - 4; $RIFFchunk[$listname]['size'] = $chunksize; if ($FoundAllChunksWeNeed) { // skip over } else { $WhereWeWere = ftell($fd); $AudioChunkHeader = fread($fd, 12); $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); if ($AudioChunkStreamType == 'wb') { $FirstFourBytes = substr($AudioChunkHeader, 8, 4); if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) { // MP3 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { $dummy = $ThisFileInfo; $dummy['avdataoffset'] = ftell($fd) - 4; $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false); if (isset($dummy['mpeg']['audio'])) { $ThisFileInfo = $dummy; $ThisFileInfo['audio']['dataformat'] = 'mp' . $ThisFileInfo['mpeg']['audio']['layer']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); } unset($dummy); } } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) { // AC3 $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { $dummy = $ThisFileInfo; $dummy['avdataoffset'] = ftell($fd) - 4; $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; $dummy['error'] = array(); $ac3_tag = new getid3_ac3($fd, $dummy); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['ac3'] = $dummy['ac3']; $ThisFileInfo['warning'] = $dummy['warning']; } unset($ac3_tag); } } } $FoundAllChunksWeNeed = true; fseek($fd, $WhereWeWere, SEEK_SET); } fseek($fd, $chunksize - 4, SEEK_CUR); //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) { // // // data chunk, ignore // } else { if (!isset($RIFFchunk[$listname])) { $RIFFchunk[$listname] = array(); } $LISTchunkParent = $listname; $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize; if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) { $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); } } break; default: if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) { $nextoffset = ftell($fd) + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 2; } fseek($fd, $nextoffset, SEEK_SET); break; } $thisindex = 0; if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8; $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; switch ($chunkname) { case 'data': $ThisFileInfo['avdataoffset'] = ftell($fd); $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize; $RIFFdataChunkContentsTest = fread($fd, 36); if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { // Probably is MP3 data if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { $dummy = $ThisFileInfo; // copy info array, only use if there's no error $getid3_mp3 = new getid3_mp3($fd, $dummy); $dummy = $ThisFileInfo; // copy info array, only use if there's no error $getid3_mp3->getOnlyMPEGaudioInfo($fd, $dummy, $RIFFchunk[$chunkname][$thisindex]['offset'], false); // use dummy array unless error if (empty($dummy['error'])) { $ThisFileInfo = $dummy; } } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") { // This is probably AC-3 data $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { $dummy = $ThisFileInfo; $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size']; $dummy['error'] = array(); $ac3_tag = new getid3_ac3($fd, $dummy); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['ac3'] = $dummy['ac3']; $ThisFileInfo['warning'] = $dummy['warning']; } unset($ac3_tag); } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") { // Dolby Digital WAV // AC-3 content, but not encoded in same format as normal AC-3 file // For one thing, byte order is swapped $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) { // ok to use tmpfile here - only 56 bytes if ($fd_temp = tmpfile()) { for ($i = 0; $i < 28; $i += 2) { // swap byte order fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); } $dummy = $ThisFileInfo; $dummy['avdataoffset'] = 0; $dummy['avdataend'] = 20; $dummy['error'] = array(); $ac3_tag = new getid3_ac3($fd_temp, $dummy); fclose($fd_temp); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['ac3'] = $dummy['ac3']; $ThisFileInfo['warning'] = $dummy['warning']; } else { $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: ' . explode(';', $dummy['error']); } unset($ac3_tag); } else { $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV'; } } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') { // This is WavPack data $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo); } else { // This is some other kind of data (quite possibly just PCM) // do nothing special, just skip it } $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 3; } fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); break; case 'bext': case 'cart': case 'fmt ': case 'strh': case 'strf': case 'indx': case 'MEXT': case 'DISP': // always read data in $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); break; case 'JUNK': // never read data in $nextoffset = ftell($fd) + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 3; } fseek($fd, $nextoffset, SEEK_SET); break; default: if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) { $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; unset($RIFFchunk[$chunkname][$thisindex]['offset']); unset($RIFFchunk[$chunkname][$thisindex]['size']); if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { unset($RIFFchunk[$chunkname][$thisindex]); } if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { unset($RIFFchunk[$chunkname]); } $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize); } elseif ($chunksize > 0 && $chunksize < 2048) { // only read data in if smaller than 2kB $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); } else { $nextoffset = ftell($fd) + $chunksize; if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) { $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions'; break 3; } fseek($fd, $nextoffset, SEEK_SET); } break; } break; } } return $RIFFchunk; }
public function ParseRIFF($startoffset, $maxoffset) { $info =& $this->getid3->info; $RIFFchunk = false; $FoundAllChunksWeNeed = false; try { $this->fseek($startoffset); $maxoffset = min($maxoffset, $info['avdataend']); while ($this->ftell() < $maxoffset) { $chunknamesize = $this->fread(8); $chunkname = substr($chunknamesize, 0, 4); $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4)); if (strlen(trim($chunkname, "")) < 4) { $this->error('Expecting chunk name at offset ' . ($this->ftell() - 8) . ' but found nothing. Aborting RIFF parsing.'); break; } if ($chunksize == 0 && $chunkname != 'JUNK') { $this->warning('Chunk (' . $chunkname . ') size at offset ' . ($this->ftell() - 4) . ' is zero. Aborting RIFF parsing.'); break; } if ($chunksize % 2 != 0) { // all structures are packed on word boundaries $chunksize++; } switch ($chunkname) { case 'LIST': $listname = $this->fread(4); if (preg_match('#^(movi|rec )$#i', $listname)) { $RIFFchunk[$listname]['offset'] = $this->ftell() - 4; $RIFFchunk[$listname]['size'] = $chunksize; if (!$FoundAllChunksWeNeed) { $WhereWeWere = $this->ftell(); $AudioChunkHeader = $this->fread(12); $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); if ($AudioChunkStreamType == 'wb') { $FirstFourBytes = substr($AudioChunkHeader, 8, 4); if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) { // MP3 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $this->ftell() - 4; $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; $getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); if (isset($getid3_temp->info['mpeg']['audio'])) { $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio']; $info['audio'] = $getid3_temp->info['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']; } unset($getid3_temp, $getid3_mp3); } } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) { // AC3 $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $this->ftell() - 4; $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; if (!empty($getid3_temp->info['warning'])) { foreach ($getid3_temp->info['warning'] as $key => $value) { $info['warning'][] = $value; } } } unset($getid3_temp, $getid3_ac3); } } $FoundAllChunksWeNeed = true; $this->fseek($WhereWeWere); } $this->fseek($chunksize - 4, SEEK_CUR); } else { if (!isset($RIFFchunk[$listname])) { $RIFFchunk[$listname] = array(); } $LISTchunkParent = $listname; $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize; if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) { $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); } } break; default: if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) { $this->fseek($chunksize, SEEK_CUR); break; } $thisindex = 0; if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8; $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; switch ($chunkname) { case 'data': $info['avdataoffset'] = $this->ftell(); $info['avdataend'] = $info['avdataoffset'] + $chunksize; $RIFFdataChunkContentsTest = $this->fread(36); if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { // Probably is MP3 data if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']; $getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false); if (empty($getid3_temp->info['error'])) { $info['mpeg'] = $getid3_temp->info['mpeg']; $info['audio'] = $getid3_temp->info['audio']; } unset($getid3_temp, $getid3_mp3); } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == getid3_ac3::syncword) { // This is probably AC-3 data $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; $info['warning'] = $getid3_temp->info['warning']; } unset($getid3_temp, $getid3_ac3); } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == getid3_ac3::syncword) { // Dolby Digital WAV // AC-3 content, but not encoded in same format as normal AC-3 file // For one thing, byte order is swapped if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) { // ok to use tmpfile here - only 56 bytes if ($fd_temp = fopen($RIFFtempfilename, 'wb')) { for ($i = 0; $i < 28; $i += 2) { // swap byte order fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); } fclose($fd_temp); $getid3_temp = new getID3(); $getid3_temp->openfile($RIFFtempfilename); $getid3_temp->info['avdataend'] = 20; $getid3_ac3 = new getid3_ac3($getid3_temp); $getid3_ac3->Analyze(); if (empty($getid3_temp->info['error'])) { $info['audio'] = $getid3_temp->info['audio']; $info['ac3'] = $getid3_temp->info['ac3']; $info['warning'] = $getid3_temp->info['warning']; } else { $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): ' . implode(';', $getid3_temp->info['error']); } unset($getid3_ac3, $getid3_temp); } else { $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file'; } unlink($RIFFtempfilename); } else { $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file'; } } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') { // This is WavPack data $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); self::parseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28)); } else { // This is some other kind of data (quite possibly just PCM) // do nothing special, just skip it } $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize; $this->fseek($nextoffset); break; case 'iXML': case 'bext': case 'cart': case 'fmt ': case 'strh': case 'strf': case 'indx': case 'MEXT': case 'DISP': // always read data in // always read data in case 'JUNK': // should be: never read data in // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc) if ($chunksize < 1048576) { if ($chunksize > 0) { $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize); if ($chunkname == 'JUNK') { if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) { // only keep text characters [chr(32)-chr(127)] $info['riff']['comments']['junk'][] = trim($matches[1]); } // but if nothing there, ignore // remove the key in either case unset($RIFFchunk[$chunkname][$thisindex]['data']); } } } else { $this->warning('Chunk "' . $chunkname . '" at offset ' . $this->ftell() . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data'); $this->fseek($chunksize, SEEK_CUR); } break; //case 'IDVX': // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize)); // break; //case 'IDVX': // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize)); // break; default: if (!empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) { $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; unset($RIFFchunk[$chunkname][$thisindex]['offset']); unset($RIFFchunk[$chunkname][$thisindex]['size']); if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { unset($RIFFchunk[$chunkname][$thisindex]); } if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { unset($RIFFchunk[$chunkname]); } $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize); } elseif ($chunksize < 2048) { // only read data in if smaller than 2kB $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize); } else { $this->fseek($chunksize, SEEK_CUR); } break; } break; } } } catch (getid3_exception $e) { if ($e->getCode() == 10) { $this->warning('RIFF parser: ' . $e->getMessage()); } else { throw $e; } } return $RIFFchunk; }