/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $EXEheader = fread($this->getid3->fp, 28); $magic = 'MZ'; if (substr($EXEheader, 0, 2) != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes(substr($EXEheader, 0, 2)) . '"'; return false; } $info['fileformat'] = 'exe'; $info['exe']['mz']['magic'] = 'MZ'; $info['exe']['mz']['raw']['last_page_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 2, 2)); $info['exe']['mz']['raw']['page_count'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 4, 2)); $info['exe']['mz']['raw']['relocation_count'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 6, 2)); $info['exe']['mz']['raw']['header_paragraphs'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 8, 2)); $info['exe']['mz']['raw']['min_memory_paragraphs'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 10, 2)); $info['exe']['mz']['raw']['max_memory_paragraphs'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 12, 2)); $info['exe']['mz']['raw']['initial_ss'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 14, 2)); $info['exe']['mz']['raw']['initial_sp'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 16, 2)); $info['exe']['mz']['raw']['checksum'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 18, 2)); $info['exe']['mz']['raw']['cs_ip'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 20, 4)); $info['exe']['mz']['raw']['relocation_table_offset'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 24, 2)); $info['exe']['mz']['raw']['overlay_number'] = GetId3_Lib_Helper::LittleEndian2Int(substr($EXEheader, 26, 2)); $info['exe']['mz']['byte_size'] = ($info['exe']['mz']['raw']['page_count'] - 1) * 512 + $info['exe']['mz']['raw']['last_page_size']; $info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16; $info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16; $info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16; $info['error'][] = 'EXE parsing not enabled in this version of GetId3() [' . $this->getid3->version() . ']'; return false; }
/** * * @return boolean */ public function ParseBink() { $info =& $this->getid3->info; $info['fileformat'] = 'bink'; $info['video']['dataformat'] = 'bink'; $fileData = 'BIK' . fread($this->getid3->fp, 13); $info['bink']['data_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($fileData, 4, 4)); $info['bink']['frame_count'] = GetId3_Lib_Helper::LittleEndian2Int(substr($fileData, 8, 2)); if ($info['avdataend'] - $info['avdataoffset'] != $info['bink']['data_size'] + 8) { $info['error'][] = 'Probably truncated file: expecting ' . $info['bink']['data_size'] . ' bytes, found ' . ($info['avdataend'] - $info['avdataoffset']); } return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $RKAUHeader = fread($this->getid3->fp, 20); $magic = 'RKA'; if (substr($RKAUHeader, 0, 3) != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes(substr($RKAUHeader, 0, 3)) . '"'; return false; } $info['fileformat'] = 'rkau'; $info['audio']['dataformat'] = 'rkau'; $info['audio']['bitrate_mode'] = 'vbr'; $info['rkau']['raw']['version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 3, 1)); $info['rkau']['version'] = '1.' . str_pad($info['rkau']['raw']['version'] & 0xf, 2, '0', STR_PAD_LEFT); if ($info['rkau']['version'] > 1.07 || $info['rkau']['version'] < 1.06) { $info['error'][] = 'This version of GetId3() [' . $this->getid3->version() . '] can only parse RKAU files v1.06 and 1.07 (this file is v' . $info['rkau']['version'] . ')'; unset($info['rkau']); return false; } $info['rkau']['source_bytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 4, 4)); $info['rkau']['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 8, 4)); $info['rkau']['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 12, 1)); $info['rkau']['bits_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 13, 1)); $info['rkau']['raw']['quality'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 14, 1)); $this->RKAUqualityLookup($info['rkau']); $info['rkau']['raw']['flags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 15, 1)); $info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x1)); $info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x2); $info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x4); if ($info['rkau']['flags']['streaming']) { $info['avdataoffset'] += 20; $info['rkau']['compressed_bytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($RKAUHeader, 16, 4)); } else { $info['avdataoffset'] += 16; $info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1; } // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes, // sometimes it's more, sometimes less. No idea why(?) $info['audio']['lossless'] = $info['rkau']['lossless']; $info['audio']['channels'] = $info['rkau']['channels']; $info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample']; $info['audio']['sample_rate'] = $info['rkau']['sample_rate']; $info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8)); $info['audio']['bitrate'] = $info['rkau']['compressed_bytes'] * 8 / $info['playtime_seconds']; return true; }
/** * * @return boolean */ public function ZIPparseEndOfCentralDirectory() { $EndOfCentralDirectory['offset'] = ftell($this->getid3->fp); $ZIPendOfCentralDirectory = fread($this->getid3->fp, 22); $EndOfCentralDirectory['signature'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4)); if ($EndOfCentralDirectory['signature'] != 0x6054b50) { // invalid End Of Central Directory Signature fseek($this->getid3->fp, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly return false; } $EndOfCentralDirectory['disk_number_current'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2)); $EndOfCentralDirectory['disk_number_start_directory'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2)); $EndOfCentralDirectory['directory_entries_this_disk'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2)); $EndOfCentralDirectory['directory_entries_total'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2)); $EndOfCentralDirectory['directory_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4)); $EndOfCentralDirectory['directory_offset'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4)); $EndOfCentralDirectory['comment_length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2)); if ($EndOfCentralDirectory['comment_length'] > 0) { $EndOfCentralDirectory['comment'] = fread($this->getid3->fp, $EndOfCentralDirectory['comment_length']); } return $EndOfCentralDirectory; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; $offset = 0; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $rawdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); switch (substr($rawdata, $offset, 4)) { case 'LA02': case 'LA03': case 'LA04': $info['fileformat'] = 'la'; $info['audio']['dataformat'] = 'la'; $info['audio']['lossless'] = true; $info['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1); $info['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1); $info['la']['version'] = (double) $info['la']['version_major'] + $info['la']['version_minor'] / 10; $offset += 4; $info['la']['uncompressed_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; if ($info['la']['uncompressed_size'] == 0) { $info['error'][] = 'Corrupt LA file: uncompressed_size == zero'; return false; } $WAVEchunk = substr($rawdata, $offset, 4); if ($WAVEchunk !== 'WAVE') { $info['error'][] = 'Expected "WAVE" (' . GetId3_Lib_Helper::PrintHexBytes('WAVE') . ') at offset ' . $offset . ', found "' . $WAVEchunk . '" (' . GetId3_Lib_Helper::PrintHexBytes($WAVEchunk) . ') instead.'; return false; } $offset += 4; $info['la']['fmt_size'] = 24; if ($info['la']['version'] >= 0.3) { $info['la']['fmt_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $info['la']['header_size'] = 49 + $info['la']['fmt_size'] - 24; $offset += 4; } else { // version 0.2 didn't support additional data blocks $info['la']['header_size'] = 41; } $fmt_chunk = substr($rawdata, $offset, 4); if ($fmt_chunk !== 'fmt ') { $info['error'][] = 'Expected "fmt " (' . GetId3_Lib_Helper::PrintHexBytes('fmt ') . ') at offset ' . $offset . ', found "' . $fmt_chunk . '" (' . GetId3_Lib_Helper::PrintHexBytes($fmt_chunk) . ') instead.'; return false; } $offset += 4; $fmt_size = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; $info['la']['raw']['format'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; $info['la']['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; if ($info['la']['channels'] == 0) { $info['error'][] = 'Corrupt LA file: channels == zero'; return false; } $info['la']['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; if ($info['la']['sample_rate'] == 0) { $info['error'][] = 'Corrupt LA file: sample_rate == zero'; return false; } $info['la']['bytes_per_second'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; $info['la']['bytes_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; $info['la']['bits_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 2)); $offset += 2; $info['la']['samples'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; $info['la']['raw']['flags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 1)); $offset += 1; $info['la']['flags']['seekable'] = (bool) ($info['la']['raw']['flags'] & 0x1); if ($info['la']['version'] >= 0.4) { $info['la']['flags']['high_compression'] = (bool) ($info['la']['raw']['flags'] & 0x2); } $info['la']['original_crc'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; // mikeØbevin*de // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 // in earlier versions. A seekpoint is added every blocksize * seekevery // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should // give the number of bytes used for the seekpoints. Of course, if seeking // is disabled, there are no seekpoints stored. if ($info['la']['version'] >= 0.4) { $info['la']['blocksize'] = 61440; $info['la']['seekevery'] = 19; } else { $info['la']['blocksize'] = 73728; $info['la']['seekevery'] = 16; } $info['la']['seekpoint_count'] = 0; if ($info['la']['flags']['seekable']) { $info['la']['seekpoint_count'] = floor($info['la']['samples'] / ($info['la']['blocksize'] * $info['la']['seekevery'])); for ($i = 0; $i < $info['la']['seekpoint_count']; $i++) { $info['la']['seekpoints'][] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; } } if ($info['la']['version'] >= 0.3) { // Following the main header information, the program outputs all of the // seekpoints. Following these is what I called the 'footer start', // i.e. the position immediately after the La audio data is finished. $info['la']['footerstart'] = GetId3_Lib_Helper::LittleEndian2Int(substr($rawdata, $offset, 4)); $offset += 4; if ($info['la']['footerstart'] > $info['filesize']) { $info['warning'][] = 'FooterStart value points to offset ' . $info['la']['footerstart'] . ' which is beyond end-of-file (' . $info['filesize'] . ')'; $info['la']['footerstart'] = $info['filesize']; } } else { // La v0.2 didn't have FooterStart value $info['la']['footerstart'] = $info['avdataend']; } if ($info['la']['footerstart'] < $info['avdataend']) { if ($RIFFtempfilename = tempnam(GetId3_GetId3::getTempDir(), 'id3')) { if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) { $RIFFdata = 'WAVE'; if ($info['la']['version'] == 0.2) { $RIFFdata .= substr($rawdata, 12, 24); } else { $RIFFdata .= substr($rawdata, 16, 24); } if ($info['la']['footerstart'] < $info['avdataend']) { fseek($this->getid3->fp, $info['la']['footerstart'], SEEK_SET); $RIFFdata .= fread($this->getid3->fp, $info['avdataend'] - $info['la']['footerstart']); } $RIFFdata = 'RIFF' . GetId3_Lib_Helper::LittleEndian2String(strlen($RIFFdata), 4, false) . $RIFFdata; fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata)); fclose($RIFF_fp); $getid3_temp = new GetId3_GetId3(); $getid3_temp->openfile($RIFFtempfilename); $getid3_riff = new GetId3_Module_AudioVideo_Riff($getid3_temp); $getid3_riff->Analyze(); if (empty($getid3_temp->info['error'])) { $info['riff'] = $getid3_temp->info['riff']; } else { $info['warning'][] = 'Error parsing RIFF portion of La file: ' . implode($getid3_temp->info['error']); } unset($getid3_temp, $getid3_riff); } unlink($RIFFtempfilename); } } // $info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway $info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart']; $info['avdataoffset'] = $info['avdataoffset'] + $offset; //$info['la']['codec'] = RIFFwFormatTagLookup($info['la']['raw']['format']); $info['la']['compression_ratio'] = (double) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']); $info['playtime_seconds'] = (double) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels']; if ($info['playtime_seconds'] == 0) { $info['error'][] = 'Corrupt LA file: playtime_seconds == zero'; return false; } $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; //$info['audio']['codec'] = $info['la']['codec']; $info['audio']['bits_per_sample'] = $info['la']['bits_per_sample']; break; default: if (substr($rawdata, $offset, 2) == 'LA') { $info['error'][] = 'This version of GetId3() [' . $this->getid3->version() . '] does not support LA version ' . substr($rawdata, $offset + 2, 1) . '.' . substr($rawdata, $offset + 3, 1) . ' which this appears to be - check http://getid3.sourceforge.net for updates.'; } else { $info['error'][] = 'Not a LA (Lossless-Audio) file'; } return false; break; } $info['audio']['channels'] = $info['la']['channels']; $info['audio']['sample_rate'] = (int) $info['la']['sample_rate']; $info['audio']['encoder'] = 'LA v' . $info['la']['version']; return true; }
/** * * @return boolean */ public function ParseOptimFROGheader45() { // for fileformat of v4.50a and higher $info =& $this->getid3->info; $RIFFdata = ''; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); while (!feof($this->getid3->fp) && ftell($this->getid3->fp) < $info['avdataend']) { $BlockOffset = ftell($this->getid3->fp); $BlockData = fread($this->getid3->fp, 8); $offset = 8; $BlockName = substr($BlockData, 0, 4); $BlockSize = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 4, 4)); if ($BlockName == 'OFRX') { $BlockName = 'OFR '; } if (!isset($info['ofr'][$BlockName])) { $info['ofr'][$BlockName] = array(); } $thisfile_ofr_thisblock =& $info['ofr'][$BlockName]; switch ($BlockName) { case 'OFR ': // shortcut $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $info['audio']['encoder'] = 'OptimFROG 4.50 alpha'; switch ($BlockSize) { case 12: case 15: // good break; default: $info['warning'][] = '"' . $BlockName . '" contains more data than expected (expected 12 or 15 bytes, found ' . $BlockSize . ' bytes)'; break; } $BlockData .= fread($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['total_samples'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 6)); $offset += 6; $thisfile_ofr_thisblock['raw']['sample_type'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); $offset += 1; $thisfile_ofr_thisblock['channel_config'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config']; $offset += 1; $thisfile_ofr_thisblock['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; if ($BlockSize > 12) { // OFR 4.504b or higher $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']); $thisfile_ofr_thisblock['raw']['encoder_id'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 2)); $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']); $offset += 2; $thisfile_ofr_thisblock['raw']['compression'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']); $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']); $offset += 1; $info['audio']['encoder'] = 'OptimFROG ' . $thisfile_ofr_thisblock['encoder']; $info['audio']['encoder_options'] = '--mode ' . $thisfile_ofr_thisblock['compression']; if (($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xf0) >> 4 == 7) { // v4.507 if (strtolower(GetId3_Lib_Helper::fileextension($info['filename'])) == 'ofs') { // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference // between lossless and lossy other than the file extension. $info['audio']['dataformat'] = 'ofs'; $info['audio']['lossless'] = true; } } } $info['audio']['channels'] = $thisfile_ofr_thisblock['channels']; $info['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; $info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); break; case 'COMP': // unlike other block types, there CAN be multiple COMP blocks $COMPdata['offset'] = $BlockOffset; $COMPdata['size'] = $BlockSize; if ($info['avdataoffset'] == 0) { $info['avdataoffset'] = $BlockOffset; } // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data $BlockData .= fread($this->getid3->fp, 14); fseek($this->getid3->fp, $BlockSize - 14, SEEK_CUR); $COMPdata['crc_32'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['sample_count'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['raw']['sample_type'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']); $offset += 1; $COMPdata['raw']['channel_configuration'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']); $offset += 1; $COMPdata['raw']['algorithm_id'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 2)); //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']); $offset += 2; if ($info['ofr']['OFR ']['size'] > 12) { // OFR 4.504b or higher $COMPdata['raw']['encoder_id'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, $offset, 2)); $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']); $offset += 2; } if ($COMPdata['crc_32'] == 0x454e4f4e) { // ASCII value of 'NONE' - placeholder value in v4.50a $COMPdata['crc_32'] = false; } $thisfile_ofr_thisblock[] = $COMPdata; break; case 'HEAD': $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $RIFFdata .= fread($this->getid3->fp, $BlockSize); break; case 'TAIL': $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; if ($BlockSize > 0) { $RIFFdata .= fread($this->getid3->fp, $BlockSize); } break; case 'RECV': // block contains no useful meta data - simply note and skip $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; case 'APET': // APEtag v2 $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version (' . $this->getid3->version() . ') of GetId3()'; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; case 'MD5 ': // APEtag v2 $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; if ($BlockSize == 16) { $thisfile_ofr_thisblock['md5_binary'] = fread($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['md5_string'] = GetId3_Lib_Helper::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); $info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; } else { $info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found ' . $BlockSize . ' instead'; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); } break; default: $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $info['warning'][] = 'Unhandled OptimFROG block type "' . $BlockName . '" at offset ' . $thisfile_ofr_thisblock['offset']; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; } } if (isset($info['ofr']['TAIL']['offset'])) { $info['avdataend'] = $info['ofr']['TAIL']['offset']; } $info['playtime_seconds'] = (double) $info['ofr']['OFR ']['total_samples'] / ($info['audio']['channels'] * $info['audio']['sample_rate']); $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; // move the data chunk after all other chunks (if any) // so that the RIFF parser doesn't see EOF when trying // to skip over the data chunk $RIFFdata = substr($RIFFdata, 0, 36) . substr($RIFFdata, 44) . substr($RIFFdata, 36, 8); $getid3_temp = new GetId3_GetId3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_riff = new GetId3_Module_AudioVideo_Riff($getid3_temp); $getid3_riff->ParseRIFFdata($RIFFdata); $info['riff'] = $getid3_temp->info['riff']; unset($getid3_riff, $getid3_temp, $RIFFdata); return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $FLVdataLength = $info['avdataend'] - $info['avdataoffset']; $FLVheader = fread($this->getid3->fp, 5); $info['fileformat'] = 'flv'; $info['flv']['header']['signature'] = substr($FLVheader, 0, 3); $info['flv']['header']['version'] = GetId3_Lib_Helper::BigEndian2Int(substr($FLVheader, 3, 1)); $TypeFlags = GetId3_Lib_Helper::BigEndian2Int(substr($FLVheader, 4, 1)); $magic = 'FLV'; if ($info['flv']['header']['signature'] != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($info['flv']['header']['signature']) . '"'; unset($info['flv']); unset($info['fileformat']); return false; } $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x4); $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x1); $FrameSizeDataLength = GetId3_Lib_Helper::BigEndian2Int(fread($this->getid3->fp, 4)); $FLVheaderFrameLength = 9; if ($FrameSizeDataLength > $FLVheaderFrameLength) { fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); } $Duration = 0; $found_video = false; $found_audio = false; $found_meta = false; $found_valid_meta_playtime = false; $tagParseCount = 0; $info['flv']['framecount'] = array('total' => 0, 'audio' => 0, 'video' => 0); $flv_framecount =& $info['flv']['framecount']; while (ftell($this->getid3->fp) + 16 < $info['avdataend'] && ($tagParseCount++ <= $this->max_frames || !$found_valid_meta_playtime)) { $ThisTagHeader = fread($this->getid3->fp, 16); $PreviousTagLength = GetId3_Lib_Helper::BigEndian2Int(substr($ThisTagHeader, 0, 4)); $TagType = GetId3_Lib_Helper::BigEndian2Int(substr($ThisTagHeader, 4, 1)); $DataLength = GetId3_Lib_Helper::BigEndian2Int(substr($ThisTagHeader, 5, 3)); $Timestamp = GetId3_Lib_Helper::BigEndian2Int(substr($ThisTagHeader, 8, 3)); $LastHeaderByte = GetId3_Lib_Helper::BigEndian2Int(substr($ThisTagHeader, 15, 1)); $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength; if ($Timestamp > $Duration) { $Duration = $Timestamp; } $flv_framecount['total']++; switch ($TagType) { case self::GETID3_FLV_TAG_AUDIO: $flv_framecount['audio']++; if (!$found_audio) { $found_audio = true; $info['flv']['audio']['audioFormat'] = $LastHeaderByte >> 4 & 0xf; $info['flv']['audio']['audioRate'] = $LastHeaderByte >> 2 & 0x3; $info['flv']['audio']['audioSampleSize'] = $LastHeaderByte >> 1 & 0x1; $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x1; } break; case self::GETID3_FLV_TAG_VIDEO: $flv_framecount['video']++; if (!$found_video) { $found_video = true; $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x7; $FLVvideoHeader = fread($this->getid3->fp, 11); if ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H264) { // this code block contributed by: moysevichØgmail*com $AVCPacketType = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 0, 1)); if ($AVCPacketType == GetId3_Module_AudioVideo_AVCSequenceParameterSetReader::H264_AVC_SEQUENCE_HEADER) { // read AVCDecoderConfigurationRecord $configurationVersion = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 1)); $AVCProfileIndication = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 1)); $profile_compatibility = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 1)); $lengthSizeMinusOne = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 7, 1)); $numOfSequenceParameterSets = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 8, 1)); if (($numOfSequenceParameterSets & 0x1f) != 0) { // there is at least one SequenceParameterSet // read size of the first SequenceParameterSet //$spsSize = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); $spsSize = GetId3_Lib_Helper::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); // read the first SequenceParameterSet $sps = fread($this->getid3->fp, $spsSize); if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red $spsReader = new GetId3_Module_AudioVideo_AVCSequenceParameterSetReader($sps); $spsReader->readData(); $info['video']['resolution_x'] = $spsReader->getWidth(); $info['video']['resolution_y'] = $spsReader->getHeight(); } } } // end: moysevichØgmail*com } elseif ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H263) { $PictureSizeType = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 3, 2)) >> 7; $PictureSizeType = $PictureSizeType & 0x7; $info['flv']['header']['videoSizeType'] = $PictureSizeType; switch ($PictureSizeType) { case 0: //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); //$PictureSizeEnc <<= 1; //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); //$PictureSizeEnc <<= 1; //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; $PictureSizeEnc['x'] = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 2)); $PictureSizeEnc['y'] = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); $PictureSizeEnc['x'] >>= 7; $PictureSizeEnc['y'] >>= 7; $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xff; $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xff; break; case 1: $PictureSizeEnc['x'] = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 3)); $PictureSizeEnc['y'] = GetId3_Lib_Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 3)); $PictureSizeEnc['x'] >>= 7; $PictureSizeEnc['y'] >>= 7; $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xffff; $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xffff; break; case 2: $info['video']['resolution_x'] = 352; $info['video']['resolution_y'] = 288; break; case 3: $info['video']['resolution_x'] = 176; $info['video']['resolution_y'] = 144; break; case 4: $info['video']['resolution_x'] = 128; $info['video']['resolution_y'] = 96; break; case 5: $info['video']['resolution_x'] = 320; $info['video']['resolution_y'] = 240; break; case 6: $info['video']['resolution_x'] = 160; $info['video']['resolution_y'] = 120; break; default: $info['video']['resolution_x'] = 0; $info['video']['resolution_y'] = 0; break; } } $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y']; } break; // Meta tag // Meta tag case self::GETID3_FLV_TAG_META: if (!$found_meta) { $found_meta = true; fseek($this->getid3->fp, -1, SEEK_CUR); $datachunk = fread($this->getid3->fp, $DataLength); $AMFstream = new GetId3_Module_AudioVideo_AMFStream($datachunk); $reader = new GetId3_Module_AudioVideo_AMFReader($AMFstream); $eventName = $reader->readData(); $info['flv']['meta'][$eventName] = $reader->readData(); unset($reader); $copykeys = array('framerate' => 'frame_rate', 'width' => 'resolution_x', 'height' => 'resolution_y', 'audiodatarate' => 'bitrate', 'videodatarate' => 'bitrate'); foreach ($copykeys as $sourcekey => $destkey) { if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) { switch ($sourcekey) { case 'width': case 'height': $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey])); break; case 'audiodatarate': $info['audio'][$destkey] = GetId3_Lib_Helper::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000)); break; case 'videodatarate': case 'frame_rate': default: $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey]; break; } } } if (!empty($info['flv']['meta']['onMetaData']['duration'])) { $found_valid_meta_playtime = true; } } break; default: // noop break; } fseek($this->getid3->fp, $NextOffset, SEEK_SET); } $info['playtime_seconds'] = $Duration / 1000; if ($info['playtime_seconds'] > 0) { $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } if ($info['flv']['header']['hasAudio']) { $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']); $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']); $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']); $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo $info['audio']['lossless'] = $info['flv']['audio']['audioFormat'] ? false : true; // 0=uncompressed $info['audio']['dataformat'] = 'flv'; } if (!empty($info['flv']['header']['hasVideo'])) { $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']); $info['video']['dataformat'] = 'flv'; $info['video']['lossless'] = false; } // Set information from meta if (!empty($info['flv']['meta']['onMetaData']['duration'])) { $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration']; $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']); } if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']); } return true; }
/** * * @staticvar type $shorten_present * @return boolean */ public function Analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $ShortenHeader = fread($this->getid3->fp, 8); $magic = 'ajkg'; if (substr($ShortenHeader, 0, 4) != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes(substr($ShortenHeader, 0, 4)) . '"'; return false; } $info['fileformat'] = 'shn'; $info['audio']['dataformat'] = 'shn'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; $info['shn']['version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ShortenHeader, 4, 1)); fseek($this->getid3->fp, $info['avdataend'] - 12, SEEK_SET); $SeekTableSignatureTest = fread($this->getid3->fp, 12); $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); if ($info['shn']['seektable']['present']) { $info['shn']['seektable']['length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length']; fseek($this->getid3->fp, $info['shn']['seektable']['offset'], SEEK_SET); $SeekTableMagic = fread($this->getid3->fp, 4); $magic = 'SEEK'; if ($SeekTableMagic != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['shn']['seektable']['offset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($SeekTableMagic) . '"'; return false; } else { // typedef struct tag_TSeekEntry // { // unsigned long SampleNumber; // unsigned long SHNFileByteOffset; // unsigned long SHNLastBufferReadPosition; // unsigned short SHNByteGet; // unsigned short SHNBufferOffset; // unsigned short SHNFileBitOffset; // unsigned long SHNGBuffer; // unsigned short SHNBitShift; // long CBuf0[3]; // long CBuf1[3]; // long Offset0[4]; // long Offset1[4]; // }TSeekEntry; $SeekTableData = fread($this->getid3->fp, $info['shn']['seektable']['length'] - 16); $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); //$info['shn']['seektable']['entries'] = array(); //$SeekTableOffset = 0; //for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) { // $SeekTableEntry['sample_number'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_file_byte_offset'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_last_buffer_read_position'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_byte_get'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // $SeekTableEntry['shn_buffer_offset'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // $SeekTableEntry['shn_file_bit_offset'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // $SeekTableEntry['shn_gbuffer'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_bit_shift'] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // for ($j = 0; $j < 3; $j++) { // $SeekTableEntry['cbuf0'][$j] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // for ($j = 0; $j < 3; $j++) { // $SeekTableEntry['cbuf1'][$j] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // for ($j = 0; $j < 4; $j++) { // $SeekTableEntry['offset0'][$j] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // for ($j = 0; $j < 4; $j++) { // $SeekTableEntry['offset1'][$j] = GetId3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // // $info['shn']['seektable']['entries'][] = $SeekTableEntry; //} } } if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { $info['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files'; return false; } if (GetId3_GetId3::environmentIsWindows()) { $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe'); foreach ($RequiredFiles as $required_file) { if (!is_readable(GetId3_GetId3::getHelperAppsDir() . $required_file)) { $info['error'][] = GetId3_GetId3::getHelperAppsDir() . $required_file . ' does not exist'; return false; } } $commandline = GetId3_GetId3::getHelperAppsDir() . 'shorten.exe -x "' . $info['filenamepath'] . '" - | ' . GetId3_GetId3::getHelperAppsDir() . 'head.exe -c 64'; $commandline = str_replace('/', '\\', $commandline); } else { static $shorten_present; if (!isset($shorten_present)) { $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; } if (!$shorten_present) { $info['error'][] = 'shorten binary was not found in path or /usr/local/bin'; return false; } $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '') . 'shorten -x ' . escapeshellarg($info['filenamepath']) . ' - | head -c 64'; } $output = `{$commandline}`; if (!empty($output) && substr($output, 12, 4) == 'fmt ') { $fmt_size = GetId3_Lib_Helper::LittleEndian2Int(substr($output, 16, 4)); $DecodedWAVFORMATEX = GetId3_Module_AudioVideo_Riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size)); $info['audio']['channels'] = $DecodedWAVFORMATEX['channels']; $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; if (substr($output, 20 + $fmt_size, 4) == 'data') { $info['playtime_seconds'] = GetId3_Lib_Helper::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; } else { $info['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; return false; } $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8; } else { $info['error'][] = 'shorten failed to decode file to WAV for parsing'; return false; } return true; }
/** * * @return boolean */ public function ParseMPCsv6() { // this is SV4 - SV6 $info =& $this->getid3->info; $thisfile_mpc_header =& $info['mpc']['header']; $offset = 0; $thisfile_mpc_header['size'] = 8; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $MPCheaderData = fread($this->getid3->fp, $thisfile_mpc_header['size']); // add size of file header to avdataoffset - calc bitrate correctly + MD5 data $info['avdataoffset'] += $thisfile_mpc_header['size']; // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :) $HeaderDWORD[0] = GetId3_Lib_Helper::LittleEndian2Int(substr($MPCheaderData, 0, 4)); $HeaderDWORD[1] = GetId3_Lib_Helper::LittleEndian2Int(substr($MPCheaderData, 4, 4)); // DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA // aaaa aaaa abcd dddd dddd deee eeff ffff // // a = bitrate = anything // b = IS = anything // c = MS = anything // d = streamversion = 0000000004 or 0000000005 or 0000000006 // e = maxband = anything // f = blocksize = 000001 for SV5+, anything(?) for SV4 $thisfile_mpc_header['target_bitrate'] = ($HeaderDWORD[0] & 0xff800000) >> 23; $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x400000) >> 22); $thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x200000) >> 21); $thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x1ff800) >> 11; $thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7 $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x7c0) >> 6; // related to lowpass frequency, not sure how it translates exactly $thisfile_mpc_header['block_size'] = $HeaderDWORD[0] & 0x3f; switch ($thisfile_mpc_header['stream_version_major']) { case 4: $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1] >> 16; break; case 5: case 6: $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1]; break; default: $info['error'] = 'Expecting 4, 5 or 6 in version field, found ' . $thisfile_mpc_header['stream_version_major'] . ' instead'; unset($info['mpc']); return false; break; } if ($thisfile_mpc_header['stream_version_major'] > 4 && $thisfile_mpc_header['block_size'] != 1) { $info['warning'][] = 'Block size expected to be 1, actual value found: ' . $thisfile_mpc_header['block_size']; } $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7 $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels']; if ($thisfile_mpc_header['target_bitrate'] == 0) { $info['audio']['bitrate_mode'] = 'vbr'; } else { $info['audio']['bitrate_mode'] = 'cbr'; } $info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152; $info['audio']['bitrate'] = $info['mpc']['bitrate']; $info['audio']['encoder'] = 'SV' . $thisfile_mpc_header['stream_version_major']; return true; }
/** * * @return boolean */ public function ParseVorbisComments() { $info =& $this->getid3->info; $OriginalOffset = $this->ftell(); $commentdataoffset = 0; $VorbisCommentPage = 1; switch ($info['audio']['dataformat']) { case 'vorbis': case 'speex': $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block $this->fseek($CommentStartOffset); $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); if ($info['audio']['dataformat'] == 'vorbis') { $commentdataoffset += strlen('vorbis') + 1; } break; case 'flac': $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4; $this->fseek($CommentStartOffset); $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']); break; default: return false; } $VendorSize = GetId3_Lib_Helper::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $commentdataoffset += 4; $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); $commentdataoffset += $VendorSize; $CommentsCount = GetId3_Lib_Helper::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $commentdataoffset += 4; $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset; $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT'); $ThisFileInfo_ogg_comments_raw =& $info['ogg']['comments_raw']; for ($i = 0; $i < $CommentsCount; $i++) { $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; if ($this->ftell() < $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4) { if ($oggpageinfo = $this->ParseOggPageHeader()) { $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; $VorbisCommentPage++; // First, save what we haven't read yet $AsYetUnusedData = substr($commentdata, $commentdataoffset); // Then take that data off the end $commentdata = substr($commentdata, 0, $commentdataoffset); // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct $commentdata .= str_repeat("", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); $commentdataoffset += 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']; // Finally, stick the unused data back on the end $commentdata .= $AsYetUnusedData; //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1)); } } $ThisFileInfo_ogg_comments_raw[$i]['size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); // replace avdataoffset with position just after the last vorbiscomment $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4; $commentdataoffset += 4; while (strlen($commentdata) - $commentdataoffset < $ThisFileInfo_ogg_comments_raw[$i]['size']) { if ($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend'] || $ThisFileInfo_ogg_comments_raw[$i]['size'] < 0) { $info['warning'][] = 'Invalid Ogg comment size (comment #' . $i . ', claims to be ' . number_format($ThisFileInfo_ogg_comments_raw[$i]['size']) . ' bytes) - aborting reading comments'; break 2; } $VorbisCommentPage++; $oggpageinfo = $this->ParseOggPageHeader(); $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; // First, save what we haven't read yet $AsYetUnusedData = substr($commentdata, $commentdataoffset); // Then take that data off the end $commentdata = substr($commentdata, 0, $commentdataoffset); // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct $commentdata .= str_repeat("", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); $commentdataoffset += 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']; // Finally, stick the unused data back on the end $commentdata .= $AsYetUnusedData; //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) { $info['warning'][] = 'undefined Vorbis Comment page "' . $VorbisCommentPage . '" at offset ' . $this->ftell(); break; } $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1); if ($readlength <= 0) { $info['warning'][] = 'invalid length Vorbis Comment page "' . $VorbisCommentPage . '" at offset ' . $this->ftell(); break; } $commentdata .= $this->fread($readlength); //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; } $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset; $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']); $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size']; if (!$commentstring) { // no comment? $info['warning'][] = 'Blank Ogg comment [' . $i . ']'; } elseif (strstr($commentstring, '=')) { $commentexploded = explode('=', $commentstring, 2); $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]); $ThisFileInfo_ogg_comments_raw[$i]['value'] = isset($commentexploded[1]) ? $commentexploded[1] : ''; $ThisFileInfo_ogg_comments_raw[$i]['data'] = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']); $ThisFileInfo_ogg_comments_raw[$i]['data_length'] = strlen($ThisFileInfo_ogg_comments_raw[$i]['data']); if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') { // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard. // http://flac.sourceforge.net/format.html#metadata_block_picture $getid3_temp = new GetId3_GetId3(); $getid3_flac = new GetId3_Module_Audio_Flac($getid3_temp); $getid3_flac->data_string = $ThisFileInfo_ogg_comments_raw[$i]['data']; $getid3_flac->data_string_flag = true; if ($getid3_flac->parsePICTURE()) { if (!empty($getid3_temp->info['flac']['PICTURE'])) { foreach ($getid3_temp->info['flac']['PICTURE'] as $key => $value) { $ThisFileInfo_ogg_comments_raw[$i]['data'] = $value['data']; $ThisFileInfo_ogg_comments_raw[$i]['data_length'] = strlen($value['data']); $ThisFileInfo_ogg_comments_raw[$i]['image_mime'] = $value['image_mime']; $ThisFileInfo_ogg_comments_raw[$i]['width'] = $value['width']; $ThisFileInfo_ogg_comments_raw[$i]['height'] = $value['height']; $ThisFileInfo_ogg_comments_raw[$i]['type'] = $value['type']; $ThisFileInfo_ogg_comments_raw[$i]['typeid'] = $value['typeid']; $ThisFileInfo_ogg_comments_raw[$i]['color_depth'] = $value['color_depth']; $ThisFileInfo_ogg_comments_raw[$i]['colors_indexed'] = $value['colors_indexed']; } } } else { $info['warning'][] = 'Failed to GetId3_flac.parsePICTURE()'; } unset($getid3_flac, $getid3_temp); } if (preg_match('#^(BM|GIF|\\xFF\\xD8\\xFF|\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A|II\\x2A\\x00|MM\\x00\\x2A)#s', $ThisFileInfo_ogg_comments_raw[$i]['data'])) { $imageinfo = array(); $imagechunkcheck = GetId3_Lib_Helper::GetDataImageSize($ThisFileInfo_ogg_comments_raw[$i]['data'], $imageinfo); unset($imageinfo); if (!empty($imagechunkcheck)) { $ThisFileInfo_ogg_comments_raw[$i]['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); if ($ThisFileInfo_ogg_comments_raw[$i]['image_mime'] && $ThisFileInfo_ogg_comments_raw[$i]['image_mime'] != 'application/octet-stream') { unset($ThisFileInfo_ogg_comments_raw[$i]['value']); } } } if (isset($ThisFileInfo_ogg_comments_raw[$i]['value'])) { unset($ThisFileInfo_ogg_comments_raw[$i]['data']); $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value']; } else { do { if ($this->inline_attachments === false) { // skip entirely unset($ThisFileInfo_ogg_comments_raw[$i]['data']); break; } if ($this->inline_attachments === true) { // great } elseif (is_int($this->inline_attachments)) { if ($this->inline_attachments < $ThisFileInfo_ogg_comments_raw[$i]['data_length']) { // too big, skip $info['warning'][] = 'attachment at ' . $ThisFileInfo_ogg_comments_raw[$i]['offset'] . ' is too large to process inline (' . number_format($ThisFileInfo_ogg_comments_raw[$i]['data_length']) . ' bytes)'; unset($ThisFileInfo_ogg_comments_raw[$i]['data']); break; } } elseif (is_string($this->inline_attachments)) { $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { // cannot write, skip $info['warning'][] = 'attachment at ' . $ThisFileInfo_ogg_comments_raw[$i]['offset'] . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)'; unset($ThisFileInfo_ogg_comments_raw[$i]['data']); break; } } // if we get this far, must be OK if (is_string($this->inline_attachments)) { $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $ThisFileInfo_ogg_comments_raw[$i]['offset']; if (!file_exists($destination_filename) || is_writable($destination_filename)) { file_put_contents($destination_filename, $ThisFileInfo_ogg_comments_raw[$i]['data']); } else { $info['warning'][] = 'attachment at ' . $ThisFileInfo_ogg_comments_raw[$i]['offset'] . ' cannot be saved to "' . $destination_filename . '" (not writable)'; } $ThisFileInfo_ogg_comments_raw[$i]['data_filename'] = $destination_filename; unset($ThisFileInfo_ogg_comments_raw[$i]['data']); } else { $info['ogg']['comments']['picture'][] = array('data' => $ThisFileInfo_ogg_comments_raw[$i]['data'], 'image_mime' => $ThisFileInfo_ogg_comments_raw[$i]['image_mime']); } } while (false); } } else { $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair [' . $i . ']: ' . $commentstring; } } // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/ if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) { foreach ($info['ogg']['comments'] as $index => $commentvalue) { switch ($index) { case 'rg_audiophile': case 'replaygain_album_gain': $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'rg_radio': case 'replaygain_track_gain': $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'replaygain_album_peak': $info['replay_gain']['album']['peak'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'rg_peak': case 'replaygain_track_peak': $info['replay_gain']['track']['peak'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'replaygain_reference_loudness': $info['replay_gain']['reference_volume'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; default: // do nothing break; } } } $this->fseek($OriginalOffset); return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'tta'; $info['audio']['dataformat'] = 'tta'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $ttaheader = fread($this->getid3->fp, 26); $info['tta']['magic'] = substr($ttaheader, 0, 3); $magic = 'TTA'; if ($info['tta']['magic'] != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($info['tta']['magic']) . '"'; unset($info['fileformat']); unset($info['audio']); unset($info['tta']); return false; } switch ($ttaheader[3]) { case "": // TTA v1.x // TTA v1.x case "": // TTA v1.x // TTA v1.x case "": // TTA v1.x // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year." $info['tta']['major_version'] = 1; $info['avdataoffset'] += 16; $info['tta']['compression_level'] = ord($ttaheader[3]); $info['tta']['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 4, 2)); $info['tta']['bits_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 6, 2)); $info['tta']['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 8, 4)); $info['tta']['samples_per_channel'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 12, 4)); $info['audio']['encoder_options'] = '-e' . $info['tta']['compression_level']; $info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate']; break; case '2': // TTA v2.x // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4." $info['tta']['major_version'] = 2; $info['avdataoffset'] += 20; $info['tta']['compression_level'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 4, 2)); $info['tta']['audio_format'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 6, 2)); $info['tta']['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 8, 2)); $info['tta']['bits_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 10, 2)); $info['tta']['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 12, 4)); $info['tta']['data_length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 16, 4)); $info['audio']['encoder_options'] = '-e' . $info['tta']['compression_level']; $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate']; break; case '1': // TTA v3.x // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher." $info['tta']['major_version'] = 3; $info['avdataoffset'] += 26; $info['tta']['audio_format'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 4, 2)); // GetId3_riff::RIFFwFormatTagLookup() $info['tta']['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 6, 2)); $info['tta']['bits_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 8, 2)); $info['tta']['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 10, 4)); $info['tta']['data_length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 14, 4)); $info['tta']['crc32_footer'] = substr($ttaheader, 18, 4); $info['tta']['seek_point'] = GetId3_Lib_Helper::LittleEndian2Int(substr($ttaheader, 22, 4)); $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate']; break; default: $info['error'][] = 'This version of GetId3() [' . $this->getid3->version() . '] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v' . $ttaheader[3]; return false; break; } $info['audio']['encoder'] = 'TTA v' . $info['tta']['major_version']; $info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample']; $info['audio']['sample_rate'] = $info['tta']['sample_rate']; $info['audio']['channels'] = $info['tta']['channels']; $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; return true; }
/** * * @param type $APEheaderFooterData * @return boolean * @link http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html */ public function parseAPEheaderFooter($APEheaderFooterData) { // shortcut $headerfooterinfo['raw'] = array(); $headerfooterinfo_raw =& $headerfooterinfo['raw']; $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8); if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') { return false; } $headerfooterinfo_raw['version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($APEheaderFooterData, 8, 4)); $headerfooterinfo_raw['tagsize'] = GetId3_Lib_Helper::LittleEndian2Int(substr($APEheaderFooterData, 12, 4)); $headerfooterinfo_raw['tag_items'] = GetId3_Lib_Helper::LittleEndian2Int(substr($APEheaderFooterData, 16, 4)); $headerfooterinfo_raw['global_flags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($APEheaderFooterData, 20, 4)); $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8); $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000; if ($headerfooterinfo['tag_version'] >= 2) { $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']); } return $headerfooterinfo; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; // based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html $info['fileformat'] = 'mac'; $info['audio']['dataformat'] = 'mac'; $info['audio']['bitrate_mode'] = 'vbr'; $info['audio']['lossless'] = true; $info['monkeys_audio']['raw'] = array(); $thisfile_monkeysaudio =& $info['monkeys_audio']; $thisfile_monkeysaudio_raw =& $thisfile_monkeysaudio['raw']; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $MACheaderData = fread($this->getid3->fp, 74); $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4); $magic = 'MAC '; if ($thisfile_monkeysaudio_raw['magic'] != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($thisfile_monkeysaudio_raw['magic']) . '"'; unset($info['fileformat']); return false; } $thisfile_monkeysaudio_raw['nVersion'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+ if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { $thisfile_monkeysaudio_raw['nCompressionLevel'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 6, 2)); $thisfile_monkeysaudio_raw['nFormatFlags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 8, 2)); $thisfile_monkeysaudio_raw['nChannels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 10, 2)); $thisfile_monkeysaudio_raw['nSampleRate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 12, 4)); $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 16, 4)); $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 20, 4)); $thisfile_monkeysaudio_raw['nTotalFrames'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 24, 4)); $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 28, 4)); $thisfile_monkeysaudio_raw['nPeakLevel'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 32, 4)); $thisfile_monkeysaudio_raw['nSeekElements'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, 38, 2)); $offset = 8; } else { $offset = 8; // APE_DESCRIPTOR $thisfile_monkeysaudio_raw['nDescriptorBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nHeaderBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nSeekTableBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16); $offset += 16; // APE_HEADER $thisfile_monkeysaudio_raw['nCompressionLevel'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nFormatFlags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nTotalFrames'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nBitsPerSample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nChannels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nSampleRate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; } $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x1); $thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x2); $thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x4); $thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x8); $thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x10); $thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x20); $thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000; $thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']); if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { $thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']); } $thisfile_monkeysaudio['bits_per_sample'] = $thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16); $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels']; $info['audio']['channels'] = $thisfile_monkeysaudio['channels']; $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate']; if ($thisfile_monkeysaudio['sample_rate'] == 0) { $info['error'][] = 'Corrupt MAC file: frequency == zero'; return false; } $info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate']; if ($thisfile_monkeysaudio['flags']['peak_level']) { $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel']; $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1); } if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { $thisfile_monkeysaudio['samples'] = ($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame'] + $thisfile_monkeysaudio_raw['nFinalFrameBlocks']; } else { $thisfile_monkeysaudio['samples'] = ($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame'] + $thisfile_monkeysaudio_raw['nFinalFrameSamples']; } $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate']; if ($thisfile_monkeysaudio['playtime'] == 0) { $info['error'][] = 'Corrupt MAC file: playtime == zero'; return false; } $info['playtime_seconds'] = $thisfile_monkeysaudio['playtime']; $thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset']; $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8); if ($thisfile_monkeysaudio['uncompressed_size'] == 0) { $info['error'][] = 'Corrupt MAC file: uncompressed_size == zero'; return false; } $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']); $thisfile_monkeysaudio['bitrate'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample'] / $thisfile_monkeysaudio['playtime'] * $thisfile_monkeysaudio['compression_ratio']; $info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate']; // add size of MAC header to avdataoffset if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes']; $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes']; $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes']; $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes']; $info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes']; } else { $info['avdataoffset'] += $offset; } if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("", 16)) { //$info['warning'][] = 'cFileMD5 is null'; } else { $info['md5_data_source'] = ''; $md5 = $thisfile_monkeysaudio_raw['cFileMD5']; for ($i = 0; $i < strlen($md5); $i++) { $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT); } if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) { unset($info['md5_data_source']); } } } $info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample']; $info['audio']['encoder'] = 'MAC v' . number_format($thisfile_monkeysaudio['version'], 2); $info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']) . ' compression'; return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; // shortcuts $info['bmp']['header']['raw'] = array(); $thisfile_bmp =& $info['bmp']; $thisfile_bmp_header =& $thisfile_bmp['header']; $thisfile_bmp_header_raw =& $thisfile_bmp_header['raw']; // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp // all versions // WORD bfType; // DWORD bfSize; // WORD bfReserved1; // WORD bfReserved2; // DWORD bfOffBits; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $offset = 0; $BMPheader = fread($this->getid3->fp, 14 + 40); $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2); $offset += 2; $magic = 'BM'; if ($thisfile_bmp_header_raw['identifier'] != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($thisfile_bmp_header_raw['identifier']) . '"'; unset($info['fileformat']); unset($info['bmp']); return false; } $thisfile_bmp_header_raw['filesize'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['reserved1'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['reserved2'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['data_offset'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['header_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; // check if the hardcoded-to-1 "planes" is at offset 22 or 26 $planes22 = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, 22, 2)); $planes26 = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, 26, 2)); if ($planes22 == 1 && $planes26 != 1) { $thisfile_bmp['type_os'] = 'OS/2'; $thisfile_bmp['type_version'] = 1; } elseif ($planes26 == 1 && $planes22 != 1) { $thisfile_bmp['type_os'] = 'Windows'; $thisfile_bmp['type_version'] = 1; } elseif ($thisfile_bmp_header_raw['header_size'] == 12) { $thisfile_bmp['type_os'] = 'OS/2'; $thisfile_bmp['type_version'] = 1; } elseif ($thisfile_bmp_header_raw['header_size'] == 40) { $thisfile_bmp['type_os'] = 'Windows'; $thisfile_bmp['type_version'] = 1; } elseif ($thisfile_bmp_header_raw['header_size'] == 84) { $thisfile_bmp['type_os'] = 'Windows'; $thisfile_bmp['type_version'] = 4; } elseif ($thisfile_bmp_header_raw['header_size'] == 100) { $thisfile_bmp['type_os'] = 'Windows'; $thisfile_bmp['type_version'] = 5; } else { $info['error'][] = 'Unknown BMP subtype (or not a BMP file)'; unset($info['fileformat']); unset($info['bmp']); return false; } $info['fileformat'] = 'bmp'; $info['video']['dataformat'] = 'bmp'; $info['video']['lossless'] = true; $info['video']['pixel_aspect_ratio'] = (double) 1; if ($thisfile_bmp['type_os'] == 'OS/2') { // OS/2-format BMP // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm // DWORD Size; /* Size of this structure in bytes */ // DWORD Width; /* Bitmap width in pixels */ // DWORD Height; /* Bitmap height in pixel */ // WORD NumPlanes; /* Number of bit planes (color depth) */ // WORD BitsPerPixel; /* Number of bits per pixel per plane */ $thisfile_bmp_header_raw['width'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['height'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['planes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['bits_per_pixel'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; $info['video']['codec'] = 'BI_RGB ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit'; $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; if ($thisfile_bmp['type_version'] >= 2) { // DWORD Compression; /* Bitmap compression scheme */ // DWORD ImageDataSize; /* Size of bitmap data in bytes */ // DWORD XResolution; /* X resolution of display device */ // DWORD YResolution; /* Y resolution of display device */ // DWORD ColorsUsed; /* Number of color table indices used */ // DWORD ColorsImportant; /* Number of important color indices */ // WORD Units; /* Type of units used to measure resolution */ // WORD Reserved; /* Pad structure to 4-byte boundary */ // WORD Recording; /* Recording algorithm */ // WORD Rendering; /* Halftoning algorithm used */ // DWORD Size1; /* Reserved for halftoning algorithm use */ // DWORD Size2; /* Reserved for halftoning algorithm use */ // DWORD ColorEncoding; /* Color model used in bitmap */ // DWORD Identifier; /* Reserved for application use */ $thisfile_bmp_header_raw['compression'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['bmp_data_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_h'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_v'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['colors_used'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['colors_important'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_units'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['reserved1'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['recording'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['rendering'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['size1'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['size2'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['color_encoding'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['identifier'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']); $info['video']['codec'] = $thisfile_bmp_header['compression'] . ' ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit'; } } elseif ($thisfile_bmp['type_os'] == 'Windows') { // Windows-format BMP // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp // all versions // DWORD biSize; // LONG biWidth; // LONG biHeight; // WORD biPlanes; // WORD biBitCount; // DWORD biCompression; // DWORD biSizeImage; // LONG biXPelsPerMeter; // LONG biYPelsPerMeter; // DWORD biClrUsed; // DWORD biClrImportant; // possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ? $thisfile_bmp_header_raw['width'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['height'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['planes'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['bits_per_pixel'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['compression'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['bmp_data_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_h'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['resolution_v'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['colors_used'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['colors_important'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']); $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; $info['video']['codec'] = $thisfile_bmp_header['compression'] . ' ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit'; $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; if ($thisfile_bmp['type_version'] >= 4 || $thisfile_bmp_header_raw['compression'] == 3) { // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen $BMPheader .= fread($this->getid3->fp, 44); // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp // Win95+, WinNT4.0+ // DWORD bV4RedMask; // DWORD bV4GreenMask; // DWORD bV4BlueMask; // DWORD bV4AlphaMask; // DWORD bV4CSType; // CIEXYZTRIPLE bV4Endpoints; // DWORD bV4GammaRed; // DWORD bV4GammaGreen; // DWORD bV4GammaBlue; $thisfile_bmp_header_raw['red_mask'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['green_mask'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['blue_mask'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['alpha_mask'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['cs_type'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4); $offset += 4; $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4); $offset += 4; $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4); $offset += 4; $thisfile_bmp_header_raw['gamma_red'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['gamma_green'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['gamma_blue'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header['ciexyz_red'] = GetId3_Lib_Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red'])); $thisfile_bmp_header['ciexyz_green'] = GetId3_Lib_Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green'])); $thisfile_bmp_header['ciexyz_blue'] = GetId3_Lib_Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue'])); } if ($thisfile_bmp['type_version'] >= 5) { $BMPheader .= fread($this->getid3->fp, 16); // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp // Win98+, Win2000+ // DWORD bV5Intent; // DWORD bV5ProfileData; // DWORD bV5ProfileSize; // DWORD bV5Reserved; $thisfile_bmp_header_raw['intent'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['profile_data_offset'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['profile_data_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['reserved3'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; } } else { $info['error'][] = 'Unknown BMP format in header.'; return false; } if ($this->ExtractPalette || $this->ExtractData) { $PaletteEntries = 0; if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) { $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']); } elseif (isset($thisfile_bmp_header_raw['colors_used']) && $thisfile_bmp_header_raw['colors_used'] > 0 && $thisfile_bmp_header_raw['colors_used'] <= 256) { $PaletteEntries = $thisfile_bmp_header_raw['colors_used']; } if ($PaletteEntries > 0) { $BMPpalette = fread($this->getid3->fp, 4 * $PaletteEntries); $paletteoffset = 0; for ($i = 0; $i < $PaletteEntries; $i++) { // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp // BYTE rgbBlue; // BYTE rgbGreen; // BYTE rgbRed; // BYTE rgbReserved; $blue = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); $green = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); $red = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); if ($thisfile_bmp['type_os'] == 'OS/2' && $thisfile_bmp['type_version'] == 1) { // no padding byte } else { $paletteoffset++; // padding byte } $thisfile_bmp['palette'][$i] = $red << 16 | $green << 8 | $blue; } } } if ($this->ExtractData) { fseek($this->getid3->fp, $thisfile_bmp_header_raw['data_offset'], SEEK_SET); $RowByteLength = ceil($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8) / 4) * 4; // round up to nearest DWORD boundry $BMPpixelData = fread($this->getid3->fp, $thisfile_bmp_header_raw['height'] * $RowByteLength); $pixeldataoffset = 0; $thisfile_bmp_header_raw['compression'] = isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : ''; switch ($thisfile_bmp_header_raw['compression']) { case 0: // BI_RGB switch ($thisfile_bmp_header_raw['bits_per_pixel']) { case 1: for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) { for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]); for ($i = 7; $i >= 0; $i--) { $paletteindex = ($paletteindexbyte & 0x1 << $i) >> $i; $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; $col++; } } while ($pixeldataoffset % 4 != 0) { // lines are padded to nearest DWORD $pixeldataoffset++; } } break; case 4: for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) { for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]); for ($i = 1; $i >= 0; $i--) { $paletteindex = ($paletteindexbyte & 0xf << 4 * $i) >> 4 * $i; $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; $col++; } } while ($pixeldataoffset % 4 != 0) { // lines are padded to nearest DWORD $pixeldataoffset++; } } break; case 8: for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) { for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { $paletteindex = ord($BMPpixelData[$pixeldataoffset++]); $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; } while ($pixeldataoffset % 4 != 0) { // lines are padded to nearest DWORD $pixeldataoffset++; } } break; case 24: for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) { for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { $thisfile_bmp['data'][$row][$col] = ord($BMPpixelData[$pixeldataoffset + 2]) << 16 | ord($BMPpixelData[$pixeldataoffset + 1]) << 8 | ord($BMPpixelData[$pixeldataoffset]); $pixeldataoffset += 3; } while ($pixeldataoffset % 4 != 0) { // lines are padded to nearest DWORD $pixeldataoffset++; } } break; case 32: for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) { for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { $thisfile_bmp['data'][$row][$col] = ord($BMPpixelData[$pixeldataoffset + 3]) << 24 | ord($BMPpixelData[$pixeldataoffset + 2]) << 16 | ord($BMPpixelData[$pixeldataoffset + 1]) << 8 | ord($BMPpixelData[$pixeldataoffset]); $pixeldataoffset += 4; } while ($pixeldataoffset % 4 != 0) { // lines are padded to nearest DWORD $pixeldataoffset++; } } break; case 16: // ? break; default: $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data'; break; } break; case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp switch ($thisfile_bmp_header_raw['bits_per_pixel']) { case 8: $pixelcounter = 0; while ($pixeldataoffset < strlen($BMPpixelData)) { $firstbyte = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $secondbyte = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); if ($firstbyte == 0) { // escaped/absolute mode - the first byte of the pair can be set to zero to // indicate an escape character that denotes the end of a line, the end of // a bitmap, or a delta, depending on the value of the second byte. switch ($secondbyte) { case 0: // end of line // no need for special processing, just ignore break; case 1: // end of bitmap $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case break; case 2: // delta - The 2 bytes following the escape contain unsigned values // indicating the horizontal and vertical offsets of the next pixel // from the current position. $colincrement = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $rowincrement = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $col = $pixelcounter % $thisfile_bmp_header_raw['width'] + $colincrement; $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'] - $rowincrement; $pixelcounter = $row * $thisfile_bmp_header_raw['width'] + $col; break; default: // In absolute mode, the first byte is zero and the second byte is a // value in the range 03H through FFH. The second byte represents the // number of bytes that follow, each of which contains the color index // of a single pixel. Each run must be aligned on a word boundary. for ($i = 0; $i < $secondbyte; $i++) { $paletteindex = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $col = $pixelcounter % $thisfile_bmp_header_raw['width']; $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width']; $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; $pixelcounter++; } while ($pixeldataoffset % 2 != 0) { // Each run must be aligned on a word boundary. $pixeldataoffset++; } break; } } else { // encoded mode - the first byte specifies the number of consecutive pixels // to be drawn using the color index contained in the second byte. for ($i = 0; $i < $firstbyte; $i++) { $col = $pixelcounter % $thisfile_bmp_header_raw['width']; $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width']; $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte]; $pixelcounter++; } } } break; default: $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data'; break; } break; case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp switch ($thisfile_bmp_header_raw['bits_per_pixel']) { case 4: $pixelcounter = 0; while ($pixeldataoffset < strlen($BMPpixelData)) { $firstbyte = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $secondbyte = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); if ($firstbyte == 0) { // escaped/absolute mode - the first byte of the pair can be set to zero to // indicate an escape character that denotes the end of a line, the end of // a bitmap, or a delta, depending on the value of the second byte. switch ($secondbyte) { case 0: // end of line // no need for special processing, just ignore break; case 1: // end of bitmap $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case break; case 2: // delta - The 2 bytes following the escape contain unsigned values // indicating the horizontal and vertical offsets of the next pixel // from the current position. $colincrement = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $rowincrement = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $col = $pixelcounter % $thisfile_bmp_header_raw['width'] + $colincrement; $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'] - $rowincrement; $pixelcounter = $row * $thisfile_bmp_header_raw['width'] + $col; break; default: // In absolute mode, the first byte is zero. The second byte contains the number // of color indexes that follow. Subsequent bytes contain color indexes in their // high- and low-order 4 bits, one color index for each pixel. In absolute mode, // each run must be aligned on a word boundary. unset($paletteindexes); for ($i = 0; $i < ceil($secondbyte / 2); $i++) { $paletteindexbyte = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $paletteindexes[] = ($paletteindexbyte & 0xf0) >> 4; $paletteindexes[] = $paletteindexbyte & 0xf; } while ($pixeldataoffset % 2 != 0) { // Each run must be aligned on a word boundary. $pixeldataoffset++; } foreach ($paletteindexes as $paletteindex) { $col = $pixelcounter % $thisfile_bmp_header_raw['width']; $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width']; $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; $pixelcounter++; } break; } } else { // encoded mode - the first byte of the pair contains the number of pixels to be // drawn using the color indexes in the second byte. The second byte contains two // color indexes, one in its high-order 4 bits and one in its low-order 4 bits. // The first of the pixels is drawn using the color specified by the high-order // 4 bits, the second is drawn using the color in the low-order 4 bits, the third // is drawn using the color in the high-order 4 bits, and so on, until all the // pixels specified by the first byte have been drawn. $paletteindexes[0] = ($secondbyte & 0xf0) >> 4; $paletteindexes[1] = $secondbyte & 0xf; for ($i = 0; $i < $firstbyte; $i++) { $col = $pixelcounter % $thisfile_bmp_header_raw['width']; $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width']; $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[$i % 2]]; $pixelcounter++; } } } break; default: $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data'; break; } break; case 3: // BI_BITFIELDS switch ($thisfile_bmp_header_raw['bits_per_pixel']) { case 16: case 32: $redshift = 0; $greenshift = 0; $blueshift = 0; while (($thisfile_bmp_header_raw['red_mask'] >> $redshift & 0x1) == 0) { $redshift++; } while (($thisfile_bmp_header_raw['green_mask'] >> $greenshift & 0x1) == 0) { $greenshift++; } while (($thisfile_bmp_header_raw['blue_mask'] >> $blueshift & 0x1) == 0) { $blueshift++; } for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) { for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { $pixelvalue = GetId3_Lib_Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8)); $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8; $red = intval(round((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift) * 255)); $green = intval(round((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift) * 255)); $blue = intval(round((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) * 255)); $thisfile_bmp['data'][$row][$col] = $red << 16 | $green << 8 | $blue; } while ($pixeldataoffset % 4 != 0) { // lines are padded to nearest DWORD $pixeldataoffset++; } } break; default: $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data'; break; } break; default: // unhandled compression type $info['error'][] = 'Unknown/unhandled compression type value (' . $thisfile_bmp_header_raw['compression'] . ') - cannot decompress pixel data'; break; } } return true; }
/** * * @param type $fileoffset * @param type $getTOCoffsets * @return boolean */ public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets = false) { $info =& $this->getid3->info; fseek($this->getid3->fp, $fileoffset, SEEK_SET); $NSVfheader = fread($this->getid3->fp, 28); $offset = 0; $info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4); $offset += 4; if ($info['nsv']['NSVf']['identifier'] != 'NSVf') { $info['error'][] = 'expected "NSVf" at offset (' . $fileoffset . '), found "' . $info['nsv']['NSVf']['identifier'] . '" instead'; unset($info['nsv']['NSVf']); return false; } $info['nsv']['NSVs']['offset'] = $fileoffset; $info['nsv']['NSVf']['header_length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; $info['nsv']['NSVf']['file_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) { $info['warning'][] = 'truncated file - NSVf header indicates ' . $info['nsv']['NSVf']['file_size'] . ' bytes, file actually ' . $info['avdataend'] . ' bytes'; } $info['nsv']['NSVf']['playtime_ms'] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; $info['nsv']['NSVf']['meta_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; $info['nsv']['NSVf']['TOC_entries_1'] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; $info['nsv']['NSVf']['TOC_entries_2'] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; if ($info['nsv']['NSVf']['playtime_ms'] == 0) { $info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero'; return false; } $NSVfheader .= fread($this->getid3->fp, $info['nsv']['NSVf']['meta_size'] + 4 * $info['nsv']['NSVf']['TOC_entries_1'] + 4 * $info['nsv']['NSVf']['TOC_entries_2']); $NSVfheaderlength = strlen($NSVfheader); $info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']); $offset += $info['nsv']['NSVf']['meta_size']; if ($getTOCoffsets) { $TOCcounter = 0; while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) { if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) { $info['nsv']['NSVf']['TOC_1'][$TOCcounter] = GetId3_Lib_Helper::LittleEndian2Int(substr($NSVfheader, $offset, 4)); $offset += 4; $TOCcounter++; } } } if (trim($info['nsv']['NSVf']['metadata']) != '') { $info['nsv']['NSVf']['metadata'] = str_replace('`', "", $info['nsv']['NSVf']['metadata']); $CommentPairArray = explode("" . ' ', $info['nsv']['NSVf']['metadata']); foreach ($CommentPairArray as $CommentPair) { if (strstr($CommentPair, '=' . "")) { list($key, $value) = explode('=' . "", $CommentPair, 2); $info['nsv']['comments'][strtolower($key)][] = trim(str_replace("", '', $value)); } } } $info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000; $info['bitrate'] = $info['nsv']['NSVf']['file_size'] * 8 / $info['playtime_seconds']; return true; }
/** * * @param type $BonkTagName */ public function HandleBonkTags($BonkTagName) { $info =& $this->getid3->info; switch ($BonkTagName) { case 'BONK': // shortcut $thisfile_bonk_BONK =& $info['bonk']['BONK']; $BonkData = "" . 'BONK' . fread($this->getid3->fp, 17); $thisfile_bonk_BONK['version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 5, 1)); $thisfile_bonk_BONK['number_samples'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 6, 4)); $thisfile_bonk_BONK['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 10, 4)); $thisfile_bonk_BONK['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 14, 1)); $thisfile_bonk_BONK['lossless'] = (bool) GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 15, 1)); $thisfile_bonk_BONK['joint_stereo'] = (bool) GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 16, 1)); $thisfile_bonk_BONK['number_taps'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 17, 2)); $thisfile_bonk_BONK['downsampling_ratio'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 19, 1)); $thisfile_bonk_BONK['samples_per_packet'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 20, 2)); $info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17; $info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size']; $info['fileformat'] = 'bonk'; $info['audio']['dataformat'] = 'bonk'; $info['audio']['bitrate_mode'] = 'vbr'; // assumed $info['audio']['channels'] = $thisfile_bonk_BONK['channels']; $info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate']; $info['audio']['channelmode'] = $thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'; $info['audio']['lossless'] = $thisfile_bonk_BONK['lossless']; $info['audio']['codec'] = 'bonk'; $info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']); if ($info['playtime_seconds'] > 0) { $info['audio']['bitrate'] = ($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8 / $info['playtime_seconds']; } break; case 'INFO': // shortcut $thisfile_bonk_INFO =& $info['bonk']['INFO']; $thisfile_bonk_INFO['version'] = GetId3_Lib_Helper::LittleEndian2Int(fread($this->getid3->fp, 1)); $thisfile_bonk_INFO['entries_count'] = 0; $NextInfoDataPair = fread($this->getid3->fp, 5); if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { while (!feof($this->getid3->fp)) { //$CurrentSeekInfo['offset'] = GetId3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4)); //$CurrentSeekInfo['nextbit'] = GetId3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1)); //$thisfile_bonk_INFO[] = $CurrentSeekInfo; $NextInfoDataPair = fread($this->getid3->fp, 5); if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { fseek($this->getid3->fp, -5, SEEK_CUR); break; } $thisfile_bonk_INFO['entries_count']++; } } break; case 'META': $BonkData = "" . 'META' . fread($this->getid3->fp, $info['bonk']['META']['size'] - 5); $info['bonk']['META']['version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, 5, 1)); $MetaTagEntries = floor((strlen($BonkData) - 8 - 6) / 8); // BonkData - xxxxmeta - ØMETA $offset = 6; for ($i = 0; $i < $MetaTagEntries; $i++) { $MetaEntryTagName = substr($BonkData, $offset, 4); $offset += 4; $MetaEntryTagOffset = GetId3_Lib_Helper::LittleEndian2Int(substr($BonkData, $offset, 4)); $offset += 4; $info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset; } break; case ' ID3': $info['audio']['encoder'] = 'Extended BONK v0.9+'; // ID3v2 checking is optional if (class_exists('GetId3_Module_Tag_Id3v2')) { $getid3_temp = new GetId3_GetId3(); $getid3_temp->openfile($this->getid3->filename); $getid3_id3v2 = new GetId3_Module_Tag_Id3v2($getid3_temp); $getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2; $info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze(); if ($info['bonk'][' ID3']['valid']) { $info['id3v2'] = $getid3_temp->info['id3v2']; } unset($getid3_temp, $getid3_id3v2); } break; default: $info['warning'][] = 'Unexpected Bonk tag "' . $BonkTagName . '" at offset ' . $info['bonk'][$BonkTagName]['offset']; break; } }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); while (true) { $wavpackheader = fread($this->getid3->fp, 32); if (ftell($this->getid3->fp) >= $info['avdataend']) { break; } elseif (feof($this->getid3->fp)) { break; } elseif (isset($info['wavpack']['blockheader']['total_samples']) && isset($info['wavpack']['blockheader']['block_samples']) && $info['wavpack']['blockheader']['total_samples'] > 0 && $info['wavpack']['blockheader']['block_samples'] > 0 && (!isset($info['wavpack']['riff_trailer_size']) || $info['wavpack']['riff_trailer_size'] <= 0) && (isset($info['wavpack']['config_flags']['md5_checksum']) && $info['wavpack']['config_flags']['md5_checksum'] === false || !empty($info['md5_data_source']))) { break; } $blockheader_offset = ftell($this->getid3->fp) - 32; $blockheader_magic = substr($wavpackheader, 0, 4); $blockheader_size = GetId3_Lib_Helper::LittleEndian2Int(substr($wavpackheader, 4, 4)); $magic = 'wvpk'; if ($blockheader_magic != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . GetId3_Lib_Helper::PrintHexBytes($blockheader_magic) . '"'; switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: unset($info['fileformat']); unset($info['audio']); unset($info['wavpack']); break; } return false; } if (empty($info['wavpack']['blockheader']['block_samples']) || empty($info['wavpack']['blockheader']['total_samples']) || $info['wavpack']['blockheader']['block_samples'] <= 0 || $info['wavpack']['blockheader']['total_samples'] <= 0) { // Also, it is possible that the first block might not have // any samples (block_samples == 0) and in this case you should skip blocks // until you find one with samples because the other information (like // total_samples) are not guaranteed to be correct until (block_samples > 0) // Finally, I have defined a format for files in which the length is not known // (for example when raw files are created using pipes). In these cases // total_samples will be -1 and you must seek to the final block to determine // the total number of samples. $info['audio']['dataformat'] = 'wavpack'; $info['fileformat'] = 'wavpack'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; $info['wavpack']['blockheader']['offset'] = $blockheader_offset; $info['wavpack']['blockheader']['magic'] = $blockheader_magic; $info['wavpack']['blockheader']['size'] = $blockheader_size; if ($info['wavpack']['blockheader']['size'] >= 0x100000) { $info['error'][] = 'Expecting WavPack block size less than "0x100000", found "' . $info['wavpack']['blockheader']['size'] . '" at offset ' . $info['wavpack']['blockheader']['offset']; switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: unset($info['fileformat']); unset($info['audio']); unset($info['wavpack']); break; } return false; } $info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader[8]); $info['wavpack']['blockheader']['major_version'] = ord($wavpackheader[9]); if ($info['wavpack']['blockheader']['major_version'] != 4 || $info['wavpack']['blockheader']['minor_version'] < 4 && $info['wavpack']['blockheader']['minor_version'] > 16) { $info['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "' . $info['wavpack']['blockheader']['major_version'] . '.' . $info['wavpack']['blockheader']['minor_version'] . '" at offset ' . $info['wavpack']['blockheader']['offset']; switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: unset($info['fileformat']); unset($info['audio']); unset($info['wavpack']); break; } return false; } $info['wavpack']['blockheader']['track_number'] = ord($wavpackheader[10]); // unused $info['wavpack']['blockheader']['index_number'] = ord($wavpackheader[11]); // unused $info['wavpack']['blockheader']['total_samples'] = GetId3_Lib_Helper::LittleEndian2Int(substr($wavpackheader, 12, 4)); $info['wavpack']['blockheader']['block_index'] = GetId3_Lib_Helper::LittleEndian2Int(substr($wavpackheader, 16, 4)); $info['wavpack']['blockheader']['block_samples'] = GetId3_Lib_Helper::LittleEndian2Int(substr($wavpackheader, 20, 4)); $info['wavpack']['blockheader']['flags_raw'] = GetId3_Lib_Helper::LittleEndian2Int(substr($wavpackheader, 24, 4)); $info['wavpack']['blockheader']['crc'] = GetId3_Lib_Helper::LittleEndian2Int(substr($wavpackheader, 28, 4)); $info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x3); $info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x4); $info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x8); $info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x10); $info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x20); $info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x40); $info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x80); $info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x100); $info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x200); $info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x400); $info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x800); $info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x1000); $info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid']; } while (!feof($this->getid3->fp) && ftell($this->getid3->fp) < $blockheader_offset + $blockheader_size + 8) { $metablock = array('offset' => ftell($this->getid3->fp)); $metablockheader = fread($this->getid3->fp, 2); if (feof($this->getid3->fp)) { break; } $metablock['id'] = ord($metablockheader[0]); $metablock['function_id'] = $metablock['id'] & 0x3f; $metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']); // The 0x20 bit in the id of the meta subblocks (which is defined as // ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that // if a decoder encounters an id that it does not know about, it uses // that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set // then the decoder simply ignores the metadata, but if it is zero // then the decoder should quit because it means that an understanding // of the metadata is required to correctly decode the audio. $metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20); $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); $metablock['large_block'] = (bool) ($metablock['id'] & 0x80); if ($metablock['large_block']) { $metablockheader .= fread($this->getid3->fp, 2); } $metablock['size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words $metablock['data'] = null; if ($metablock['size'] > 0) { switch ($metablock['function_id']) { case 0x21: // ID_RIFF_HEADER // ID_RIFF_HEADER case 0x22: // ID_RIFF_TRAILER // ID_RIFF_TRAILER case 0x23: // ID_REPLAY_GAIN // ID_REPLAY_GAIN case 0x24: // ID_CUESHEET // ID_CUESHEET case 0x25: // ID_CONFIG_BLOCK // ID_CONFIG_BLOCK case 0x26: // ID_MD5_CHECKSUM $metablock['data'] = fread($this->getid3->fp, $metablock['size']); if ($metablock['padded_data']) { // padded to the nearest even byte $metablock['size']--; $metablock['data'] = substr($metablock['data'], 0, -1); } break; case 0x0: // ID_DUMMY // ID_DUMMY case 0x1: // ID_ENCODER_INFO // ID_ENCODER_INFO case 0x2: // ID_DECORR_TERMS // ID_DECORR_TERMS case 0x3: // ID_DECORR_WEIGHTS // ID_DECORR_WEIGHTS case 0x4: // ID_DECORR_SAMPLES // ID_DECORR_SAMPLES case 0x5: // ID_ENTROPY_VARS // ID_ENTROPY_VARS case 0x6: // ID_HYBRID_PROFILE // ID_HYBRID_PROFILE case 0x7: // ID_SHAPING_WEIGHTS // ID_SHAPING_WEIGHTS case 0x8: // ID_FLOAT_INFO // ID_FLOAT_INFO case 0x9: // ID_INT32_INFO // ID_INT32_INFO case 0xa: // ID_WV_BITSTREAM // ID_WV_BITSTREAM case 0xb: // ID_WVC_BITSTREAM // ID_WVC_BITSTREAM case 0xc: // ID_WVX_BITSTREAM // ID_WVX_BITSTREAM case 0xd: // ID_CHANNEL_INFO fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); break; default: $info['warning'][] = 'Unexpected metablock type "0x' . str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT) . '" at offset ' . $metablock['offset']; fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); break; } switch ($metablock['function_id']) { case 0x21: // ID_RIFF_HEADER $original_wav_filesize = GetId3_Lib_Helper::LittleEndian2Int(substr($metablock['data'], 4, 4)); $getid3_temp = new GetId3_GetId3(); $getid3_temp->openfile($this->getid3->filename); $getid3_riff = new GetId3_Module_AudioVideo_Riff($getid3_temp); $getid3_riff->ParseRIFFdata($metablock['data']); $metablock['riff'] = $getid3_temp->info['riff']; $info['audio']['sample_rate'] = $getid3_temp->info['riff']['raw']['fmt ']['nSamplesPerSec']; unset($getid3_riff, $getid3_temp); $metablock['riff']['original_filesize'] = $original_wav_filesize; $info['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; $info['playtime_seconds'] = $info['wavpack']['blockheader']['total_samples'] / $info['audio']['sample_rate']; // Safe RIFF header in case there's a RIFF footer later $metablockRIFFheader = $metablock['data']; break; case 0x22: // ID_RIFF_TRAILER $metablockRIFFfooter = $metablockRIFFheader . $metablock['data']; $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); $getid3_temp = new GetId3_GetId3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['fileformat'] = 'riff'; $getid3_riff = new GetId3_Module_AudioVideo_Riff($getid3_temp); $metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']); if (!empty($metablock['riff']['INFO'])) { $getid3_riff->RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); $info['tags']['riff'] = $metablock['comments']; } unset($getid3_temp, $getid3_riff); break; case 0x23: // ID_REPLAY_GAIN $info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by GetId3() in metablock at offset ' . $metablock['offset']; break; case 0x24: // ID_CUESHEET $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by GetId3() in metablock at offset ' . $metablock['offset']; break; case 0x25: // ID_CONFIG_BLOCK $metablock['flags_raw'] = GetId3_Lib_Helper::LittleEndian2Int(substr($metablock['data'], 0, 3)); $metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x1); // "adobe" mode for 32-bit floats $metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x2); // fast mode $metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x4); // double fast $metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x8); // high quality mode $metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x10); // double high (not used yet) $metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x20); // bitrate is kbps, not bits / sample $metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x40); // automatic noise shaping $metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x80); // shaping mode specified $metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x100); // joint-stereo mode specified $metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x200); // copy file-time from source $metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x400); // create executable $metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x800); // create correction file $metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x1000); // maximize bybrid compression $metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x2000); // psychoacoustic quality mode $metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x4000); // raw mode (not implemented yet) $metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x8000); // calc noise in hybrid mode $metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x10000); // obsolete (for information) $metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x20000); // extra processing mode $metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x40000); // no wvx stream w/ floats & big ints $metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x80000); // compute & store MD5 signature $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress % $info['wavpack']['config_flags'] = $metablock['flags']; $info['audio']['encoder_options'] = ''; if ($info['wavpack']['blockheader']['flags']['hybrid']) { $info['audio']['encoder_options'] .= ' -b???'; } $info['audio']['encoder_options'] .= $metablock['flags']['adobe_mode'] ? ' -a' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['optimize_wvc'] ? ' -cc' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['create_exe'] ? ' -e' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['fast_flag'] ? ' -f' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['joint_override'] ? ' -j?' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['high_flag'] ? ' -h' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['md5_checksum'] ? ' -m' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['calc_noise'] ? ' -n' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['shape_override'] ? ' -s?' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['extra_mode'] ? ' -x?' : ''; if (!empty($info['audio']['encoder_options'])) { $info['audio']['encoder_options'] = trim($info['audio']['encoder_options']); } elseif (isset($info['audio']['encoder_options'])) { unset($info['audio']['encoder_options']); } break; case 0x26: // ID_MD5_CHECKSUM if (strlen($metablock['data']) == 16) { $info['md5_data_source'] = strtolower(GetId3_Lib_Helper::PrintHexBytes($metablock['data'], true, false, false)); } else { $info['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset ' . $metablock['offset'] . ', but found ' . strlen($metablock['data']) . ' bytes'; } break; case 0x0: // ID_DUMMY // ID_DUMMY case 0x1: // ID_ENCODER_INFO // ID_ENCODER_INFO case 0x2: // ID_DECORR_TERMS // ID_DECORR_TERMS case 0x3: // ID_DECORR_WEIGHTS // ID_DECORR_WEIGHTS case 0x4: // ID_DECORR_SAMPLES // ID_DECORR_SAMPLES case 0x5: // ID_ENTROPY_VARS // ID_ENTROPY_VARS case 0x6: // ID_HYBRID_PROFILE // ID_HYBRID_PROFILE case 0x7: // ID_SHAPING_WEIGHTS // ID_SHAPING_WEIGHTS case 0x8: // ID_FLOAT_INFO // ID_FLOAT_INFO case 0x9: // ID_INT32_INFO // ID_INT32_INFO case 0xa: // ID_WV_BITSTREAM // ID_WV_BITSTREAM case 0xb: // ID_WVC_BITSTREAM // ID_WVC_BITSTREAM case 0xc: // ID_WVX_BITSTREAM // ID_WVX_BITSTREAM case 0xd: // ID_CHANNEL_INFO unset($metablock); break; } } if (!empty($metablock)) { $info['wavpack']['metablocks'][] = $metablock; } } } $info['audio']['encoder'] = 'WavPack v' . $info['wavpack']['blockheader']['major_version'] . '.' . str_pad($info['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT); $info['audio']['bits_per_sample'] = $info['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8; $info['audio']['channels'] = $info['wavpack']['blockheader']['flags']['mono'] ? 1 : 2; if (!empty($info['playtime_seconds'])) { $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } else { $info['audio']['dataformat'] = 'wvc'; } return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'gif'; $info['video']['dataformat'] = 'gif'; $info['video']['lossless'] = true; $info['video']['pixel_aspect_ratio'] = (double) 1; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $GIFheader = fread($this->getid3->fp, 13); $offset = 0; $info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3); $offset += 3; $magic = 'GIF'; if ($info['gif']['header']['raw']['identifier'] != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($info['gif']['header']['raw']['identifier']) . '"'; unset($info['fileformat']); unset($info['gif']); return false; } $info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3); $offset += 3; $info['gif']['header']['raw']['width'] = GetId3_Lib_Helper::LittleEndian2Int(substr($GIFheader, $offset, 2)); $offset += 2; $info['gif']['header']['raw']['height'] = GetId3_Lib_Helper::LittleEndian2Int(substr($GIFheader, $offset, 2)); $offset += 2; $info['gif']['header']['raw']['flags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; $info['gif']['header']['raw']['bg_color_index'] = GetId3_Lib_Helper::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; $info['gif']['header']['raw']['aspect_ratio'] = GetId3_Lib_Helper::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; $info['video']['resolution_x'] = $info['gif']['header']['raw']['width']; $info['video']['resolution_y'] = $info['gif']['header']['raw']['height']; $info['gif']['version'] = $info['gif']['header']['raw']['version']; $info['gif']['header']['flags']['global_color_table'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x80); if ($info['gif']['header']['raw']['flags'] & 0x80) { // Number of bits per primary color available to the original image, minus 1 $info['gif']['header']['bits_per_pixel'] = 3 * ((($info['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1); } else { $info['gif']['header']['bits_per_pixel'] = 0; } $info['gif']['header']['flags']['global_color_sorted'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x40); if ($info['gif']['header']['flags']['global_color_table']) { // the number of bytes contained in the Global Color Table. To determine that // actual size of the color table, raise 2 to [the value of the field + 1] $info['gif']['header']['global_color_size'] = pow(2, ($info['gif']['header']['raw']['flags'] & 0x7) + 1); $info['video']['bits_per_sample'] = ($info['gif']['header']['raw']['flags'] & 0x7) + 1; } else { $info['gif']['header']['global_color_size'] = 0; } if ($info['gif']['header']['raw']['aspect_ratio'] != 0) { // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 $info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64; } // if ($info['gif']['header']['flags']['global_color_table']) { // $GIFcolorTable = fread($this->getid3->fp, 3 * $info['gif']['header']['global_color_size']); // $offset = 0; // for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) { // $red = GetId3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); // $green = GetId3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); // $blue = GetId3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); // $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); // } // } // // // Image Descriptor // while (!feof($this->getid3->fp)) { // $NextBlockTest = fread($this->getid3->fp, 1); // switch ($NextBlockTest) { // // case ',': // ',' - Image separator character // // $ImageDescriptorData = $NextBlockTest.fread($this->getid3->fp, 9); // $ImageDescriptor = array(); // $ImageDescriptor['image_left'] = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); // $ImageDescriptor['image_top'] = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); // $ImageDescriptor['image_width'] = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2)); // $ImageDescriptor['image_height'] = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2)); // $ImageDescriptor['flags_raw'] = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1)); // $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80); // $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40); // $info['gif']['image_descriptor'][] = $ImageDescriptor; // // if ($ImageDescriptor['flags']['use_local_color_map']) { // // $info['warning'][] = 'This version of GetId3() cannot parse local color maps for GIFs'; // return true; // // } //echo 'Start of raster data: '.ftell($this->getid3->fp).'<BR>'; // $RasterData = array(); // $RasterData['code_size'] = GetId3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); // $RasterData['block_byte_count'] = GetId3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); // $info['gif']['raster_data'][count($info['gif']['image_descriptor']) - 1] = $RasterData; // // $CurrentCodeSize = $RasterData['code_size'] + 1; // for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) { // $DefaultDataLookupTable[$i] = chr($i); // } // $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code // $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code // // // $NextValue = $this->GetLSBits($CurrentCodeSize); // echo 'Clear Code: '.$NextValue.'<BR>'; // // $NextValue = $this->GetLSBits($CurrentCodeSize); // echo 'First Color: '.$NextValue.'<BR>'; // // $Prefix = $NextValue; //$i = 0; // while ($i++ < 20) { // $NextValue = $this->GetLSBits($CurrentCodeSize); // echo $NextValue.'<BR>'; // } //return true; // break; // // case '!': // // GIF Extension Block // $ExtensionBlockData = $NextBlockTest.fread($this->getid3->fp, 2); // $ExtensionBlock = array(); // $ExtensionBlock['function_code'] = GetId3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1)); // $ExtensionBlock['byte_length'] = GetId3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); // $ExtensionBlock['data'] = fread($this->getid3->fp, $ExtensionBlock['byte_length']); // $info['gif']['extension_blocks'][] = $ExtensionBlock; // break; // // case ';': // $info['gif']['terminator_offset'] = ftell($this->getid3->fp) - 1; // // GIF Terminator // break; // // default: // break; // // // } // } return true; }
/** * * @param type $directorydata * @return string */ public function ParseDirectoryRecord($directorydata) { $info =& $this->getid3->info; if (isset($info['iso']['supplementary_volume_descriptor'])) { $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode } else { $TextEncoding = 'ISO-8859-1'; // Latin-1 } fseek($this->getid3->fp, $directorydata['location_bytes'], SEEK_SET); $DirectoryRecordData = fread($this->getid3->fp, 1); while (ord($DirectoryRecordData[0]) > 33) { $DirectoryRecordData .= fread($this->getid3->fp, ord($DirectoryRecordData[0]) - 1); $ThisDirectoryRecord['raw']['length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 0, 1)); $ThisDirectoryRecord['raw']['extended_attribute_length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 1, 1)); $ThisDirectoryRecord['raw']['offset_logical'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 2, 4)); $ThisDirectoryRecord['raw']['filesize'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 10, 4)); $ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7); $ThisDirectoryRecord['raw']['file_flags'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 25, 1)); $ThisDirectoryRecord['raw']['file_unit_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 26, 1)); $ThisDirectoryRecord['raw']['interleave_gap_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 27, 1)); $ThisDirectoryRecord['raw']['volume_sequence_number'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 28, 2)); $ThisDirectoryRecord['raw']['file_identifier_length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($DirectoryRecordData, 32, 1)); $ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']); $ThisDirectoryRecord['file_identifier_ascii'] = GetId3_Lib_Helper::iconv_fallback($TextEncoding, $info['encoding'], $ThisDirectoryRecord['raw']['file_identifier']); $ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize']; $ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048; $ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x1); $ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x2); $ThisDirectoryRecord['file_flags']['associated'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x4); $ThisDirectoryRecord['file_flags']['extended'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x8); $ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10); $ThisDirectoryRecord['file_flags']['multiple'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80); $ThisDirectoryRecord['recording_timestamp'] = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']); if ($ThisDirectoryRecord['file_flags']['directory']) { $ThisDirectoryRecord['filename'] = $directorydata['full_path']; } else { $ThisDirectoryRecord['filename'] = $directorydata['full_path'] . $this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']); $info['iso']['files'] = GetId3_Lib_Helper::array_merge_clobber($info['iso']['files'], GetId3_Lib_Helper::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize'])); } $DirectoryRecord[] = $ThisDirectoryRecord; $DirectoryRecordData = fread($this->getid3->fp, 1); } return $DirectoryRecord; }
/** * * @param type $byteword * @param type $signed * @return type */ public function EitherEndian2Int($byteword, $signed = false) { if ($this->getid3->info['fileformat'] == 'riff') { return GetId3_Lib_Helper::LittleEndian2Int($byteword, $signed); } return GetId3_Lib_Helper::BigEndian2Int($byteword, false, $signed); }
/** * * @param type $bytestring * @param type $byteorder * @return boolean */ public function TIFFendian2Int($bytestring, $byteorder) { if ($byteorder == 'Intel') { return GetId3_Lib_Helper::LittleEndian2Int($bytestring); } elseif ($byteorder == 'Motorola') { return GetId3_Lib_Helper::BigEndian2Int($bytestring); } return false; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'swf'; $info['video']['dataformat'] = 'swf'; // http://www.openswf.org/spec/SWFfileformat.html fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $SWFfileData = fread($this->getid3->fp, $info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3); switch ($info['swf']['header']['signature']) { case 'FWS': $info['swf']['header']['compressed'] = false; break; case 'CWS': $info['swf']['header']['compressed'] = true; break; default: $info['error'][] = 'Expecting "FWS" or "CWS" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes($info['swf']['header']['signature']) . '"'; unset($info['swf']); unset($info['fileformat']); return false; break; } $info['swf']['header']['version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($SWFfileData, 3, 1)); $info['swf']['header']['length'] = GetId3_Lib_Helper::LittleEndian2Int(substr($SWFfileData, 4, 4)); if ($info['swf']['header']['compressed']) { $SWFHead = substr($SWFfileData, 0, 8); $SWFfileData = substr($SWFfileData, 8); if ($decompressed = @gzuncompress($SWFfileData)) { $SWFfileData = $SWFHead . $decompressed; } else { $info['error'][] = 'Error decompressing compressed SWF data (' . strlen($SWFfileData) . ' bytes compressed, should be ' . ($info['swf']['header']['length'] - 8) . ' bytes uncompressed)'; return false; } } $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xf8) >> 3; $FrameSizeDataLength = ceil((5 + 4 * $FrameSizeBitsPerValue) / 8); $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x7), 3, '0', STR_PAD_LEFT); for ($i = 1; $i < $FrameSizeDataLength; $i++) { $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); } list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); $info['swf']['header']['frame_width'] = GetId3_Lib_Helper::Bin2Dec($X2); $info['swf']['header']['frame_height'] = GetId3_Lib_Helper::Bin2Dec($Y2); // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm // Next in the header is the frame rate, which is kind of weird. // It is supposed to be stored as a 16bit integer, but the first byte // (or last depending on how you look at it) is completely ignored. // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. // Byte at (8 + $FrameSizeDataLength) is always zero and ignored $info['swf']['header']['frame_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); $info['swf']['header']['frame_count'] = GetId3_Lib_Helper::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); $info['video']['frame_rate'] = $info['swf']['header']['frame_rate']; $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20)); $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20)); $info['video']['pixel_aspect_ratio'] = (double) 1; if ($info['swf']['header']['frame_count'] > 0 && $info['swf']['header']['frame_rate'] > 0) { $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate']; } //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; // SWF tags $CurrentOffset = 12 + $FrameSizeDataLength; $SWFdataLength = strlen($SWFfileData); while ($CurrentOffset < $SWFdataLength) { //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; $TagIDTagLength = GetId3_Lib_Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); $TagID = ($TagIDTagLength & 0xfffc) >> 6; $TagLength = $TagIDTagLength & 0x3f; $CurrentOffset += 2; if ($TagLength == 0x3f) { $TagLength = GetId3_Lib_Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4)); $CurrentOffset += 4; } unset($TagData); $TagData['offset'] = $CurrentOffset; $TagData['size'] = $TagLength; $TagData['id'] = $TagID; $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength); switch ($TagID) { case 0: // end of movie break 2; case 9: // Set background color //$info['swf']['tags'][] = $TagData; $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(GetId3_Lib_Helper::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); break; default: if ($this->ReturnAllTagData) { $info['swf']['tags'][] = $TagData; } break; } $CurrentOffset += $TagLength; } return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; $OriginalAVdataOffset = $info['avdataoffset']; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $VOCheader = fread($this->getid3->fp, 26); $magic = 'Creative Voice File'; if (substr($VOCheader, 0, 19) != $magic) { $info['error'][] = 'Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes(substr($VOCheader, 0, 19)) . '"'; return false; } // shortcuts $thisfile_audio =& $info['audio']; $info['voc'] = array(); $thisfile_voc =& $info['voc']; $info['fileformat'] = 'voc'; $thisfile_audio['dataformat'] = 'voc'; $thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio['lossless'] = true; $thisfile_audio['channels'] = 1; // might be overriden below $thisfile_audio['bits_per_sample'] = 8; // might be overriden below // byte # Description // ------ ------------------------------------------ // 00-12 'Creative Voice File' // 13 1A (eof to abort printing of file) // 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation) // 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) // 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) $thisfile_voc['header']['datablock_offset'] = GetId3_Lib_Helper::LittleEndian2Int(substr($VOCheader, 20, 2)); $thisfile_voc['header']['minor_version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($VOCheader, 22, 1)); $thisfile_voc['header']['major_version'] = GetId3_Lib_Helper::LittleEndian2Int(substr($VOCheader, 23, 1)); do { $BlockOffset = ftell($this->getid3->fp); $BlockData = fread($this->getid3->fp, 4); $BlockType = ord($BlockData[0]); $BlockSize = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 1, 3)); $ThisBlock = array(); GetId3_Lib_Helper::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1); switch ($BlockType) { case 0: // Terminator // do nothing, we'll break out of the loop down below break; case 1: // Sound data $BlockData .= fread($this->getid3->fp, 2); if ($info['avdataoffset'] <= $OriginalAVdataOffset) { $info['avdataoffset'] = ftell($this->getid3->fp); } fseek($this->getid3->fp, $BlockSize - 2, SEEK_CUR); $ThisBlock['sample_rate_id'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 4, 1)); $ThisBlock['compression_type'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 5, 1)); $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']); if ($ThisBlock['compression_type'] <= 3) { $thisfile_voc['compressed_bits_per_sample'] = GetId3_Lib_Helper::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name'])); } // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available) if (empty($thisfile_audio['sample_rate'])) { // SR byte = 256 - (1000000 / sample_rate) $thisfile_audio['sample_rate'] = GetId3_Lib_Helper::trunc(1000000 / (256 - $ThisBlock['sample_rate_id']) / $thisfile_audio['channels']); } break; case 2: // Sound continue // Sound continue case 3: // Silence // Silence case 4: // Marker // Marker case 6: // Repeat // Repeat case 7: // End repeat // nothing useful, just skip fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; case 8: // Extended $BlockData .= fread($this->getid3->fp, 4); //00-01 Time Constant: // Mono: 65536 - (256000000 / sample_rate) // Stereo: 65536 - (256000000 / (sample_rate * 2)) $ThisBlock['time_constant'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 4, 2)); $ThisBlock['pack_method'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 6, 1)); $ThisBlock['stereo'] = (bool) GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 7, 1)); $thisfile_audio['channels'] = $ThisBlock['stereo'] ? 2 : 1; $thisfile_audio['sample_rate'] = GetId3_Lib_Helper::trunc(256000000 / (65536 - $ThisBlock['time_constant']) / $thisfile_audio['channels']); break; case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit $BlockData .= fread($this->getid3->fp, 12); if ($info['avdataoffset'] <= $OriginalAVdataOffset) { $info['avdataoffset'] = ftell($this->getid3->fp); } fseek($this->getid3->fp, $BlockSize - 12, SEEK_CUR); $ThisBlock['sample_rate'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 4, 4)); $ThisBlock['bits_per_sample'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 8, 1)); $ThisBlock['channels'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 9, 1)); $ThisBlock['wFormat'] = GetId3_Lib_Helper::LittleEndian2Int(substr($BlockData, 10, 2)); $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']); if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) { $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']); } $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate']; $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample']; $thisfile_audio['channels'] = $ThisBlock['channels']; break; default: $info['warning'][] = 'Unhandled block type "' . $BlockType . '" at offset ' . $BlockOffset; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; } if (!empty($ThisBlock)) { $ThisBlock['block_offset'] = $BlockOffset; $ThisBlock['block_size'] = $BlockSize; $ThisBlock['block_type_id'] = $BlockType; $thisfile_voc['blocks'][] = $ThisBlock; } } while (!feof($this->getid3->fp) && $BlockType != 0); // Terminator block doesn't have size field, so seek back 3 spaces fseek($this->getid3->fp, -3, SEEK_CUR); ksort($thisfile_voc['blocktypes']); if (!empty($thisfile_voc['compressed_bits_per_sample'])) { $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; ///AH $info['ac3']['raw']['bsi'] = array(); $thisfile_ac3 =& $info['ac3']; $thisfile_ac3_raw =& $thisfile_ac3['raw']; $thisfile_ac3_raw_bsi =& $thisfile_ac3_raw['bsi']; // http://www.atsc.org/standards/a_52a.pdf $info['fileformat'] = 'ac3'; // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 // new audio samples per channel. A synchronization information (SI) header at the beginning // of each frame contains information needed to acquire and maintain synchronization. A // bit stream information (BSI) header follows SI, and contains parameters describing the coded // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the // end of each frame is an error check field that includes a CRC word for error detection. An // additional CRC word is located in the SI header, the use of which, by a decoder, is optional. // // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC // syncinfo() { // syncword 16 // crc1 16 // fscod 2 // frmsizecod 6 // } /* end of syncinfo */ $this->fseek($info['avdataoffset']); $this->AC3header['syncinfo'] = $this->fread(5); $magic = "\vw"; if (strpos($this->AC3header['syncinfo'], $magic) === 0) { $thisfile_ac3_raw['synchinfo']['synchword'] = $magic; $offset = 2; } else { if (!$this->isDependencyFor('matroska')) { unset($info['fileformat'], $info['ac3']); return $this->error('Expecting "' . GetId3_Lib_Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . GetId3_Lib_Helper::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)) . '"'); } $offset = 0; $this->fseek(-2, SEEK_CUR); } $info['audio']['dataformat'] = 'ac3'; $info['audio']['bitrate_mode'] = 'cbr'; $info['audio']['lossless'] = false; $thisfile_ac3_raw['synchinfo']['crc1'] = GetId3_Lib_Helper::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2)); $ac3_synchinfo_fscod_frmsizecod = GetId3_Lib_Helper::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset + 2, 1)); $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xc0) >> 6; $thisfile_ac3_raw['synchinfo']['frmsizecod'] = $ac3_synchinfo_fscod_frmsizecod & 0x3f; $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']); if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) { $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; } $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']); $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']); $info['audio']['bitrate'] = $thisfile_ac3['bitrate']; $this->AC3header['bsi'] = GetId3_Lib_Helper::BigEndian2Bin($this->fread(15)); $ac3_bsi_offset = 0; $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5); if ($thisfile_ac3_raw_bsi['bsid'] > 8) { // Decoders which can decode version 8 will thus be able to decode version numbers less than 8. // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. $this->error('Bit stream identification is version ' . $thisfile_ac3_raw_bsi['bsid'] . ', but GetId3() only understands up to version 8'); unset($info['ac3']); return false; } $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3); $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3); $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']); foreach ($ac3_coding_mode as $key => $value) { $thisfile_ac3[$key] = $value; } switch ($thisfile_ac3_raw_bsi['acmod']) { case 0: case 1: $info['audio']['channelmode'] = 'mono'; break; case 3: case 4: $info['audio']['channelmode'] = 'stereo'; break; default: $info['audio']['channelmode'] = 'surround'; break; } $info['audio']['channels'] = $thisfile_ac3['num_channels']; if ($thisfile_ac3_raw_bsi['acmod'] & 0x1) { // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2); $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']); } if ($thisfile_ac3_raw_bsi['acmod'] & 0x4) { // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2); $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']); } if ($thisfile_ac3_raw_bsi['acmod'] == 0x2) { // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround. $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2); $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']); } $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon']; if ($thisfile_ac3_raw_bsi['lfeon']) { //$info['audio']['channels']++; $info['audio']['channels'] .= '.1'; } $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']); // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5); $thisfile_ac3['dialogue_normalization'] = '-' . $thisfile_ac3_raw_bsi['dialnorm'] . 'dB'; $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['compre_flag']) { $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8); $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']); } $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['langcode_flag']) { $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8); } $thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['audprodie']) { $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5); $thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2); $thisfile_ac3['mixing_level'] = 80 + $thisfile_ac3_raw_bsi['mixlevel'] . 'dB'; $thisfile_ac3['room_type'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']); } if ($thisfile_ac3_raw_bsi['acmod'] == 0x0) { // If acmod is 0, then two completely independent program channels (dual mono) // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, // a number of additional items are present in BSI or audblk to fully describe Ch2. // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5); $thisfile_ac3['dialogue_normalization2'] = '-' . $thisfile_ac3_raw_bsi['dialnorm2'] . 'dB'; $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['compre_flag2']) { $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8); $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']); } $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['langcode_flag2']) { $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8); } $thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['audprodie2']) { $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5); $thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2); $thisfile_ac3['mixing_level2'] = 80 + $thisfile_ac3_raw_bsi['mixlevel2'] . 'dB'; $thisfile_ac3['room_type2'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']); } } $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['timecode1_flag']) { $thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14); } $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['timecode2_flag']) { $thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14); } $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1); if ($thisfile_ac3_raw_bsi['addbsi_flag']) { $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6); $this->AC3header['bsi'] .= GetId3_Lib_Helper::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length'])); $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8); $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8; } return true; }
public function ASF_WMpicture(&$data) { //typedef struct _WMPicture{ // LPWSTR pwszMIMEType; // BYTE bPictureType; // LPWSTR pwszDescription; // DWORD dwDataLen; // BYTE* pbData; //} WM_PICTURE; $WMpicture = array(); $offset = 0; $WMpicture['image_type_id'] = GetId3_Lib_Helper::LittleEndian2Int(substr($data, $offset, 1)); $offset += 1; $WMpicture['image_type'] = $this->WMpictureTypeLookup($WMpicture['image_type_id']); $WMpicture['image_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($data, $offset, 4)); $offset += 4; $WMpicture['image_mime'] = ''; do { $next_byte_pair = substr($data, $offset, 2); $offset += 2; $WMpicture['image_mime'] .= $next_byte_pair; } while ($next_byte_pair !== ""); $WMpicture['image_description'] = ''; do { $next_byte_pair = substr($data, $offset, 2); $offset += 2; $WMpicture['image_description'] .= $next_byte_pair; } while ($next_byte_pair !== ""); $WMpicture['dataoffset'] = $offset; $WMpicture['data'] = substr($data, $offset); $imageinfo = array(); $WMpicture['image_mime'] = ''; $imagechunkcheck = GetId3_Lib_Helper::GetDataImageSize($WMpicture['data'], $imageinfo); unset($imageinfo); if (!empty($imagechunkcheck)) { $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); } if (!isset($this->getid3->info['asf']['comments']['picture'])) { $this->getid3->info['asf']['comments']['picture'] = array(); } $this->getid3->info['asf']['comments']['picture'][] = array('data' => $WMpicture['data'], 'image_mime' => $WMpicture['image_mime']); return $WMpicture; }