/** * @return bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $DSSheader = fread($this->getid3->fp, 1256); if (!preg_match('#^(\\x02|\\x03)dss#', $DSSheader)) { $info['error'][] = 'Expecting "[02-03] 64 73 73" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($DSSheader, 0, 4)) . '"'; return false; } // some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm // shortcut $info['dss'] = array(); $thisfile_dss =& $info['dss']; $info['fileformat'] = 'dss'; $info['audio']['dataformat'] = 'dss'; $info['audio']['bitrate_mode'] = 'cbr'; //$thisfile_dss['encoding'] = 'ISO-8859-1'; $thisfile_dss['version'] = ord(substr($DSSheader, 0, 1)); $thisfile_dss['date_create'] = self::DSSdateStringToUnixDate(substr($DSSheader, 38, 12)); $thisfile_dss['date_complete'] = self::DSSdateStringToUnixDate(substr($DSSheader, 50, 12)); //$thisfile_dss['length'] = intval(substr($DSSheader, 62, 6)); // I thought time was in seconds, it's actually HHMMSS $thisfile_dss['length'] = intval(substr($DSSheader, 62, 2) * 3600 + substr($DSSheader, 64, 2) * 60 + substr($DSSheader, 66, 2)); $thisfile_dss['priority'] = ord(substr($DSSheader, 793, 1)); $thisfile_dss['comments'] = trim(substr($DSSheader, 798, 100)); //$info['audio']['bits_per_sample'] = ?; //$info['audio']['sample_rate'] = ?; $info['audio']['channels'] = 1; $info['playtime_seconds'] = $thisfile_dss['length']; $info['audio']['bitrate'] = $info['filesize'] * 8 / $info['playtime_seconds']; return true; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'rar'; if ($this->option_use_rar_extension === true) { if (function_exists('rar_open')) { if ($rp = rar_open($info['filenamepath'])) { $info['rar']['files'] = array(); $entries = rar_list($rp); foreach ($entries as $entry) { $info['rar']['files'] = Helper::array_merge_clobber($info['rar']['files'], Helper::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize())); } rar_close($rp); return true; } else { $info['error'][] = 'failed to rar_open(' . $info['filename'] . ')'; } } else { $info['error'][] = 'RAR support does not appear to be available in this PHP installation'; } } else { $info['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)'; } return false; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $AAheader = fread($this->getid3->fp, 8); $magic = "W�u6"; if (substr($AAheader, 4, 4) != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($AAheader, 4, 4)) . '"'; return false; } // shortcut $info['aa'] = array(); $thisfile_au =& $info['aa']; $info['fileformat'] = 'aa'; $info['audio']['dataformat'] = 'aa'; $info['error'][] = 'Audible Audiobook (.aa) parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']'; return false; $info['audio']['bitrate_mode'] = 'cbr'; // is it? $thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_au['filesize'] = Helper::BigEndian2Int(substr($AUheader, 0, 4)); if ($thisfile_au['filesize'] > $info['avdataend'] - $info['avdataoffset']) { $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['filesize'] . '" bytes of data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"'; } $info['audio']['bits_per_sample'] = 16; // is it? $info['audio']['sample_rate'] = $thisfile_au['sample_rate']; $info['audio']['channels'] = $thisfile_au['channels']; //$info['playtime_seconds'] = 0; //$info['audio']['bitrate'] = 0; return true; }
/** * @return bool */ 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($EXEheader, 0, 2)) . '"'; return false; } $info['fileformat'] = 'exe'; $info['exe']['mz']['magic'] = 'MZ'; $info['exe']['mz']['raw']['last_page_size'] = Helper::LittleEndian2Int(substr($EXEheader, 2, 2)); $info['exe']['mz']['raw']['page_count'] = Helper::LittleEndian2Int(substr($EXEheader, 4, 2)); $info['exe']['mz']['raw']['relocation_count'] = Helper::LittleEndian2Int(substr($EXEheader, 6, 2)); $info['exe']['mz']['raw']['header_paragraphs'] = Helper::LittleEndian2Int(substr($EXEheader, 8, 2)); $info['exe']['mz']['raw']['min_memory_paragraphs'] = Helper::LittleEndian2Int(substr($EXEheader, 10, 2)); $info['exe']['mz']['raw']['max_memory_paragraphs'] = Helper::LittleEndian2Int(substr($EXEheader, 12, 2)); $info['exe']['mz']['raw']['initial_ss'] = Helper::LittleEndian2Int(substr($EXEheader, 14, 2)); $info['exe']['mz']['raw']['initial_sp'] = Helper::LittleEndian2Int(substr($EXEheader, 16, 2)); $info['exe']['mz']['raw']['checksum'] = Helper::LittleEndian2Int(substr($EXEheader, 18, 2)); $info['exe']['mz']['raw']['cs_ip'] = Helper::LittleEndian2Int(substr($EXEheader, 20, 4)); $info['exe']['mz']['raw']['relocation_table_offset'] = Helper::LittleEndian2Int(substr($EXEheader, 24, 2)); $info['exe']['mz']['raw']['overlay_number'] = 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 GetId3Core() [' . $this->getid3->version() . ']'; return false; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $SZIPHeader = fread($this->getid3->fp, 6); if (substr($SZIPHeader, 0, 4) != "SZ\n") { $info['error'][] = 'Expecting "53 5A 0A 04" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($SZIPHeader, 0, 4)) . '"'; return false; } $info['fileformat'] = 'szip'; $info['szip']['major_version'] = Helper::BigEndian2Int(substr($SZIPHeader, 4, 1)); $info['szip']['minor_version'] = Helper::BigEndian2Int(substr($SZIPHeader, 5, 1)); while (!feof($this->getid3->fp)) { $NextBlockID = fread($this->getid3->fp, 2); switch ($NextBlockID) { case 'SZ': // Note that szip files can be concatenated, this has the same effect as // concatenating the files. this also means that global header blocks // might be present between directory/data blocks. fseek($this->getid3->fp, 4, SEEK_CUR); break; case 'BH': $BHheaderbytes = Helper::BigEndian2Int(fread($this->getid3->fp, 3)); $BHheaderdata = fread($this->getid3->fp, $BHheaderbytes); $BHheaderoffset = 0; while (strpos($BHheaderdata, "", $BHheaderoffset) > 0) { //filename as \0 terminated string (empty string indicates end) //owner as \0 terminated string (empty is same as last file) //group as \0 terminated string (empty is same as last file) //3 byte filelength in this block //2 byte access flags //4 byte creation time (like in unix) //4 byte modification time (like in unix) //4 byte access time (like in unix) $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "")); $BHheaderoffset += strlen($BHdataArray['filename']) + 1; $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "")); $BHheaderoffset += strlen($BHdataArray['owner']) + 1; $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "")); $BHheaderoffset += strlen($BHdataArray['group']) + 1; $BHdataArray['filelength'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3)); $BHheaderoffset += 3; $BHdataArray['access_flags'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2)); $BHheaderoffset += 2; $BHdataArray['creation_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; $BHdataArray['modification_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; $BHdataArray['access_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; $info['szip']['BH'][] = $BHdataArray; } break; default: break 2; } } return true; }
/** * @return bool */ 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'] = Helper::LittleEndian2Int(substr($fileData, 4, 4)); $info['bink']['frame_count'] = 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 bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $DOCFILEheader = fread($this->getid3->fp, 8); $magic = "поЮ║╠А"; if (substr($DOCFILEheader, 0, 8) != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at ' . $info['avdataoffset'] . ', found ' . Helper::PrintHexBytes(substr($DOCFILEheader, 0, 8)) . ' instead.'; return false; } $info['fileformat'] = 'msoffice'; $info['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']'; return false; }
/** * @return bool */ 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($RKAUHeader, 0, 3)) . '"'; return false; } $info['fileformat'] = 'rkau'; $info['audio']['dataformat'] = 'rkau'; $info['audio']['bitrate_mode'] = 'vbr'; $info['rkau']['raw']['version'] = 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 GetId3Core() [' . $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'] = Helper::LittleEndian2Int(substr($RKAUHeader, 4, 4)); $info['rkau']['sample_rate'] = Helper::LittleEndian2Int(substr($RKAUHeader, 8, 4)); $info['rkau']['channels'] = Helper::LittleEndian2Int(substr($RKAUHeader, 12, 1)); $info['rkau']['bits_per_sample'] = Helper::LittleEndian2Int(substr($RKAUHeader, 13, 1)); $info['rkau']['raw']['quality'] = Helper::LittleEndian2Int(substr($RKAUHeader, 14, 1)); $this->RKAUqualityLookup($info['rkau']); $info['rkau']['raw']['flags'] = 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'] = 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 bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $AUheader = fread($this->getid3->fp, 8); $magic = '.snd'; if (substr($AUheader, 0, 4) != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" (".snd") at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($AUheader, 0, 4)) . '"'; return false; } // shortcut $info['au'] = array(); $thisfile_au =& $info['au']; $info['fileformat'] = 'au'; $info['audio']['dataformat'] = 'au'; $info['audio']['bitrate_mode'] = 'cbr'; $thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_au['header_length'] = Helper::BigEndian2Int(substr($AUheader, 4, 4)); $AUheader .= fread($this->getid3->fp, $thisfile_au['header_length'] - 8); $info['avdataoffset'] += $thisfile_au['header_length']; $thisfile_au['data_size'] = Helper::BigEndian2Int(substr($AUheader, 8, 4)); $thisfile_au['data_format_id'] = Helper::BigEndian2Int(substr($AUheader, 12, 4)); $thisfile_au['sample_rate'] = Helper::BigEndian2Int(substr($AUheader, 16, 4)); $thisfile_au['channels'] = Helper::BigEndian2Int(substr($AUheader, 20, 4)); $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24)); $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']); $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']); if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) { $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; } else { unset($thisfile_au['bits_per_sample']); } $info['audio']['sample_rate'] = $thisfile_au['sample_rate']; $info['audio']['channels'] = $thisfile_au['channels']; if ($info['avdataoffset'] + $thisfile_au['data_size'] > $info['avdataend']) { $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['data_size'] . '" bytes of audio data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"'; } $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); $info['audio']['bitrate'] = $thisfile_au['data_size'] * 8 / $info['playtime_seconds']; return true; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $efaxheader = fread($this->getid3->fp, 1024); $info['efax']['header']['magic'] = substr($efaxheader, 0, 2); if ($info['efax']['header']['magic'] != "Üþ") { $info['error'][] = 'Invalid eFax byte order identifier (expecting DC FE, found ' . Helper::PrintHexBytes($info['efax']['header']['magic']) . ') at offset ' . $info['avdataoffset']; return false; } $info['fileformat'] = 'efax'; $info['efax']['header']['filesize'] = Helper::LittleEndian2Int(substr($efaxheader, 2, 4)); if ($info['efax']['header']['filesize'] != $info['filesize']) { $info['error'][] = 'Probable ' . ($info['efax']['header']['filesize'] > $info['filesize'] ? 'truncated' : 'corrupt') . ' file, expecting ' . $info['efax']['header']['filesize'] . ' bytes, found ' . $info['filesize'] . ' bytes'; } $info['efax']['header']['software1'] = rtrim(substr($efaxheader, 26, 32), ""); $info['efax']['header']['software2'] = rtrim(substr($efaxheader, 58, 32), ""); $info['efax']['header']['software3'] = rtrim(substr($efaxheader, 90, 32), ""); $info['efax']['header']['pages'] = Helper::LittleEndian2Int(substr($efaxheader, 198, 2)); $info['efax']['header']['data_bytes'] = Helper::LittleEndian2Int(substr($efaxheader, 202, 4)); $info['error'][] = 'eFax parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']'; return false; return true; }
/** * @return type */ public function getBit() { $result = Helper::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1; $this->skipBits(1); return $result; }
/** * @return bool */ public function CalculateReplayGain() { if (isset($this->info['replay_gain'])) { if (!isset($this->info['replay_gain']['reference_volume'])) { $this->info['replay_gain']['reference_volume'] = (double) 89.0; } if (isset($this->info['replay_gain']['track']['adjustment'])) { $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; } if (isset($this->info['replay_gain']['album']['adjustment'])) { $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; } if (isset($this->info['replay_gain']['track']['peak'])) { $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - Helper::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); } if (isset($this->info['replay_gain']['album']['peak'])) { $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - Helper::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); } } return true; }
/** * @return bool */ 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 = 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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 6)); $offset += 6; $thisfile_ofr_thisblock['raw']['sample_type'] = 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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config']; $offset += 1; $thisfile_ofr_thisblock['sample_rate'] = 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'] = 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'] = 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(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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['sample_count'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['raw']['sample_type'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']); $offset += 1; $COMPdata['raw']['channel_configuration'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']); $offset += 1; $COMPdata['raw']['algorithm_id'] = 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'] = 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 GetId3Core()'; 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'] = 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 GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_riff = new Riff($getid3_temp); $getid3_riff->ParseRIFFdata($RIFFdata); $info['riff'] = $getid3_temp->info['riff']; unset($getid3_riff, $getid3_temp, $RIFFdata); return true; }
/** * @param type $Header4Bytes * * @return bool */ public static function MPEGaudioHeaderDecode($Header4Bytes) { // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM // A - Frame sync (all bits set) // B - MPEG Audio version ID // C - Layer description // D - Protection bit // E - Bitrate index // F - Sampling rate frequency index // G - Padding bit // H - Private bit // I - Channel Mode // J - Mode extension (Only if Joint stereo) // K - Copyright // L - Original // M - Emphasis if (strlen($Header4Bytes) != 4) { return false; } $MPEGrawHeader['synch'] = (Helper::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xffe0) >> 4; $MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3; // BB $MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x6) >> 1; // CC $MPEGrawHeader['protection'] = ord($Header4Bytes[1]) & 0x1; // D $MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xf0) >> 4; // EEEE $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0xc) >> 2; // FF $MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x2) >> 1; // G $MPEGrawHeader['private'] = ord($Header4Bytes[2]) & 0x1; // H $MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xc0) >> 6; // II $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; // JJ $MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x8) >> 3; // K $MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x4) >> 2; // L $MPEGrawHeader['emphasis'] = ord($Header4Bytes[3]) & 0x3; // MM return $MPEGrawHeader; }
/** * @return bool */ 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'] = Helper::BigEndian2Int(substr($FLVheader, 3, 1)); $TypeFlags = Helper::BigEndian2Int(substr($FLVheader, 4, 1)); $magic = 'FLV'; if ($info['flv']['header']['signature'] != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . 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 = 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 = Helper::BigEndian2Int(substr($ThisTagHeader, 0, 4)); $TagType = Helper::BigEndian2Int(substr($ThisTagHeader, 4, 1)); $DataLength = Helper::BigEndian2Int(substr($ThisTagHeader, 5, 3)); $Timestamp = Helper::BigEndian2Int(substr($ThisTagHeader, 8, 3)); $LastHeaderByte = 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 = Helper::BigEndian2Int(substr($FLVvideoHeader, 0, 1)); if ($AVCPacketType == AVCSequenceParameterSetReader::H264_AVC_SEQUENCE_HEADER) { // read AVCDecoderConfigurationRecord $configurationVersion = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 1)); $AVCProfileIndication = Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 1)); $profile_compatibility = Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 1)); $lengthSizeMinusOne = Helper::BigEndian2Int(substr($FLVvideoHeader, 7, 1)); $numOfSequenceParameterSets = 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 = 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 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 = 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'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 2)); $PictureSizeEnc['y'] = 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'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 3)); $PictureSizeEnc['y'] = 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 AMFStream($datachunk); $reader = new 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] = 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; }
/** * @return string */ public function GenerateCONTchunk() { foreach ($this->tag_data as $key => $value) { // limit each value to 0xFFFF bytes $this->tag_data[$key] = substr($value, 0, 65535); } $CONTchunk = ""; // object version $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0, 2); $CONTchunk .= !empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : ''; $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0, 2); $CONTchunk .= !empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : ''; $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0, 2); $CONTchunk .= !empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : ''; $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0, 2); $CONTchunk .= !empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : ''; if ($this->paddedlength > strlen($CONTchunk) + 8) { $CONTchunk .= str_repeat("", $this->paddedlength - strlen($CONTchunk) - 8); } $CONTchunk = 'CONT' . Helper::BigEndian2String(strlen($CONTchunk) + 8, 4) . $CONTchunk; // CONT chunk identifier + chunk length return $CONTchunk; }
/** * @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'] = Helper::LittleEndian2Int(substr($BonkData, 5, 1)); $thisfile_bonk_BONK['number_samples'] = Helper::LittleEndian2Int(substr($BonkData, 6, 4)); $thisfile_bonk_BONK['sample_rate'] = Helper::LittleEndian2Int(substr($BonkData, 10, 4)); $thisfile_bonk_BONK['channels'] = Helper::LittleEndian2Int(substr($BonkData, 14, 1)); $thisfile_bonk_BONK['lossless'] = (bool) Helper::LittleEndian2Int(substr($BonkData, 15, 1)); $thisfile_bonk_BONK['joint_stereo'] = (bool) Helper::LittleEndian2Int(substr($BonkData, 16, 1)); $thisfile_bonk_BONK['number_taps'] = Helper::LittleEndian2Int(substr($BonkData, 17, 2)); $thisfile_bonk_BONK['downsampling_ratio'] = Helper::LittleEndian2Int(substr($BonkData, 19, 1)); $thisfile_bonk_BONK['samples_per_packet'] = 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'] = 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'] = 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 = 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('Helpers\\GetId3\\Module\\Tag\\Id3v2')) { $getid3_temp = new GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_id3v2 = new 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; } }
/** * @param type $items * @param type $isheader * * @return type */ public function GenerateAPEtagHeaderFooter(&$items, $isheader = false) { $tagdatalength = 0; foreach ($items as $itemdata) { $tagdatalength += strlen($itemdata); } $APEheader = 'APETAGEX'; $APEheader .= Helper::LittleEndian2String(2000, 4); $APEheader .= Helper::LittleEndian2String(32 + $tagdatalength, 4); $APEheader .= Helper::LittleEndian2String(count($items), 4); $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false); $APEheader .= str_repeat("", 8); return $APEheader; }
public function RemoveID3v1() { // File MUST be writeable - CHMOD(646) at least if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { $this->setRealFileSize(); if ($this->filesize <= 0 || !Helper::intValueSupported($this->filesize)) { $this->errors[] = 'Unable to RemoveID3v1(' . $this->filename . ') because filesize (' . $this->filesize . ') is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB'; return false; } if ($fp_source = fopen($this->filename, 'r+b')) { fseek($fp_source, -128, SEEK_END); if (fread($fp_source, 3) == 'TAG') { ftruncate($fp_source, $this->filesize - 128); } else { // no ID3v1 tag to begin with - do nothing } fclose($fp_source); return true; } else { $this->errors[] = 'Could not fopen(' . $this->filename . ', "r+b")'; } } else { $this->errors[] = $this->filename . ' is not writeable'; } return false; }
/** * @return bool */ 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . 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'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 2)); $offset += 2; $info['gif']['header']['raw']['height'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 2)); $offset += 2; $info['gif']['header']['raw']['flags'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; $info['gif']['header']['raw']['bg_color_index'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 1)); $offset += 1; $info['gif']['header']['raw']['aspect_ratio'] = 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 GetId3Core() 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; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $LPACheader = fread($this->getid3->fp, 14); if (substr($LPACheader, 0, 4) != 'LPAC') { $info['error'][] = 'Expected "LPAC" at offset ' . $info['avdataoffset'] . ', found "' . $StreamMarker . '"'; return false; } $info['avdataoffset'] += 14; $info['fileformat'] = 'lpac'; $info['audio']['dataformat'] = 'lpac'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; $info['lpac']['file_version'] = Helper::BigEndian2Int(substr($LPACheader, 4, 1)); $flags['audio_type'] = Helper::BigEndian2Int(substr($LPACheader, 5, 1)); $info['lpac']['total_samples'] = Helper::BigEndian2Int(substr($LPACheader, 6, 4)); $flags['parameters'] = Helper::BigEndian2Int(substr($LPACheader, 10, 4)); $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x4); $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x2); $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x1); if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) { $info['warning'][] = '24-bit and 16-bit flags cannot both be set'; } $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x8000000); $info['lpac']['block_length'] = pow(2, ($flags['parameters'] & 0x7000000) >> 24) * 256; $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x800000); $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x400000); $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x40000); $info['lpac']['quantization'] = ($flags['parameters'] & 0x1f00) >> 8; $info['lpac']['max_prediction_order'] = $flags['parameters'] & 0x3f; if ($info['lpac']['flags']['fast_compress'] && $info['lpac']['max_prediction_order'] != 3) { $info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $info['lpac']['max_prediction_order'] . '"'; } switch ($info['lpac']['file_version']) { case 6: if ($info['lpac']['flags']['adaptive_quantization']) { $info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; } if ($info['lpac']['quantization'] != 20) { $info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually ' . $info['lpac']['flags']['Q']; } break; default: //$info['warning'][] = 'This version of GetId3Core() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org'; break; } $getid3_temp = new GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info = $info; $getid3_riff = new Riff($getid3_temp); $getid3_riff->analyze(); $info['avdataoffset'] = $getid3_temp->info['avdataoffset']; $info['riff'] = $getid3_temp->info['riff']; $info['error'] = $getid3_temp->info['error']; $info['warning'] = $getid3_temp->info['warning']; $info['lpac']['comments']['comment'] = $getid3_temp->info['comments']; $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate']; unset($getid3_temp, $getid3_riff); $info['audio']['channels'] = $info['lpac']['flags']['stereo'] ? 2 : 1; if ($info['lpac']['flags']['24_bit']) { $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample']; } elseif ($info['lpac']['flags']['16_bit']) { $info['audio']['bits_per_sample'] = 16; } else { $info['audio']['bits_per_sample'] = 8; } if ($info['lpac']['flags']['fast_compress']) { // fast $info['audio']['encoder_options'] = '-1'; } else { switch ($info['lpac']['max_prediction_order']) { case 20: // simple $info['audio']['encoder_options'] = '-2'; break; case 30: // medium $info['audio']['encoder_options'] = '-3'; break; case 40: // high $info['audio']['encoder_options'] = '-4'; break; case 60: // extrahigh $info['audio']['encoder_options'] = '-5'; break; } } $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate']; $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; return true; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; if (!Helper::intValueSupported($info['filesize'])) { $info['warning'][] = 'Unable to check for ID3v1 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB'; return false; } fseek($this->getid3->fp, -256, SEEK_END); $preid3v1 = fread($this->getid3->fp, 128); $id3v1tag = fread($this->getid3->fp, 128); if (substr($id3v1tag, 0, 3) == 'TAG') { $info['avdataend'] = $info['filesize'] - 128; $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30)); $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4)); $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1)); // If second-last byte of comment field is null and last byte of comment field is non-null // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number if ($id3v1tag[125] === "" && $id3v1tag[126] !== "") { $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1)); $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28); } $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']); $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']); if (!empty($ParsedID3v1['genre'])) { unset($ParsedID3v1['genreid']); } if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || $ParsedID3v1['genre'] == 'Unknown')) { unset($ParsedID3v1['genre']); } foreach ($ParsedID3v1 as $key => $value) { $ParsedID3v1['comments'][$key][0] = $value; } // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces $GoodFormatID3v1tag = $this->GenerateID3v1Tag($ParsedID3v1['title'], $ParsedID3v1['artist'], $ParsedID3v1['album'], $ParsedID3v1['year'], isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false, $ParsedID3v1['comment'], !empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''); $ParsedID3v1['padding_valid'] = true; if ($id3v1tag !== $GoodFormatID3v1tag) { $ParsedID3v1['padding_valid'] = false; $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; } $ParsedID3v1['tag_offset_end'] = $info['filesize']; $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; $info['id3v1'] = $ParsedID3v1; } if (substr($preid3v1, 0, 3) == 'TAG') { // The way iTunes handles tags is, well, brain-damaged. // It completely ignores v1 if ID3v2 is present. // This goes as far as adding a new v1 tag *even if there already is one* // A suspected double-ID3v1 tag has been detected, but it could be that // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag if (substr($preid3v1, 96, 8) == 'APETAGEX') { // an APE tag footer was found before the last ID3v1, assume false "TAG" synch } elseif (substr($preid3v1, 119, 6) == 'LYRICS') { // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch } else { // APE and Lyrics3 footers not found - assume double ID3v1 $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; $info['avdataend'] -= 128; } } return true; }
/** * @return bool */ 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_monkeysaudio_raw['magic']) . '"'; unset($info['fileformat']); return false; } $thisfile_monkeysaudio_raw['nVersion'] = Helper::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+ if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { $thisfile_monkeysaudio_raw['nCompressionLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, 6, 2)); $thisfile_monkeysaudio_raw['nFormatFlags'] = Helper::LittleEndian2Int(substr($MACheaderData, 8, 2)); $thisfile_monkeysaudio_raw['nChannels'] = Helper::LittleEndian2Int(substr($MACheaderData, 10, 2)); $thisfile_monkeysaudio_raw['nSampleRate'] = Helper::LittleEndian2Int(substr($MACheaderData, 12, 4)); $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, 16, 4)); $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, 20, 4)); $thisfile_monkeysaudio_raw['nTotalFrames'] = Helper::LittleEndian2Int(substr($MACheaderData, 24, 4)); $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = Helper::LittleEndian2Int(substr($MACheaderData, 28, 4)); $thisfile_monkeysaudio_raw['nPeakLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, 32, 4)); $thisfile_monkeysaudio_raw['nSeekElements'] = Helper::LittleEndian2Int(substr($MACheaderData, 38, 2)); $offset = 8; } else { $offset = 8; // APE_DESCRIPTOR $thisfile_monkeysaudio_raw['nDescriptorBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nHeaderBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nSeekTableBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16); $offset += 16; // APE_HEADER $thisfile_monkeysaudio_raw['nCompressionLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nFormatFlags'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nTotalFrames'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4)); $offset += 4; $thisfile_monkeysaudio_raw['nBitsPerSample'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nChannels'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2)); $offset += 2; $thisfile_monkeysaudio_raw['nSampleRate'] = 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; }
/** * @param type $atom_data * * @return type * * @link http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG */ public function QuicktimeParseNikonNCTG($atom_data) { // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 // Data is stored as records of: // * 4 bytes record type // * 2 bytes size of data field type: // 0x0001 = flag (size field *= 1-byte) // 0x0002 = char (size field *= 1-byte) // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? // * 2 bytes data size field // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15") // all integers are stored BigEndian $NCTGtagName = array(0x1 => 'Make', 0x2 => 'Model', 0x3 => 'Software', 0x11 => 'CreateDate', 0x12 => 'DateTimeOriginal', 0x13 => 'FrameCount', 0x16 => 'FrameRate', 0x22 => 'FrameWidth', 0x23 => 'FrameHeight', 0x32 => 'AudioChannels', 0x33 => 'AudioBitsPerSample', 0x34 => 'AudioSampleRate', 0x2000001 => 'MakerNoteVersion', 0x2000005 => 'WhiteBalance', 0x200000b => 'WhiteBalanceFineTune', 0x200001e => 'ColorSpace', 0x2000023 => 'PictureControlData', 0x2000024 => 'WorldTime', 0x2000032 => 'UnknownInfo', 0x2000083 => 'LensType', 0x2000084 => 'Lens'); $offset = 0; $datalength = strlen($atom_data); $parsed = array(); while ($offset < $datalength) { //echo GetId3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>'; $record_type = Helper::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4; $data_size_type = Helper::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; $data_size = Helper::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; switch ($data_size_type) { case 0x1: // 0x0001 = flag (size field *= 1-byte) $data = Helper::BigEndian2Int(substr($atom_data, $offset, $data_size * 1)); $offset += $data_size * 1; break; case 0x2: // 0x0002 = char (size field *= 1-byte) $data = substr($atom_data, $offset, $data_size * 1); $offset += $data_size * 1; $data = rtrim($data, ""); break; case 0x3: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB $data = ''; for ($i = $data_size - 1; $i >= 0; --$i) { $data .= substr($atom_data, $offset + $i * 2, 2); } $data = Helper::BigEndian2Int($data); $offset += $data_size * 2; break; case 0x4: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD $data = ''; for ($i = $data_size - 1; $i >= 0; --$i) { $data .= substr($atom_data, $offset + $i * 4, 4); } $data = Helper::BigEndian2Int($data); $offset += $data_size * 4; break; case 0x5: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together $data = array(); for ($i = 0; $i < $data_size; ++$i) { $numerator = Helper::BigEndian2Int(substr($atom_data, $offset + $i * 8 + 0, 4)); $denomninator = Helper::BigEndian2Int(substr($atom_data, $offset + $i * 8 + 4, 4)); if ($denomninator == 0) { $data[$i] = false; } else { $data[$i] = (double) $numerator / $denomninator; } } $offset += 8 * $data_size; if (count($data) == 1) { $data = $data[0]; } break; case 0x7: // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? $data = substr($atom_data, $offset, $data_size * 1); $offset += $data_size * 1; break; case 0x8: // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? $data = substr($atom_data, $offset, $data_size * 2); $offset += $data_size * 2; break; default: echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: ' . $data_size_type . '<br>'; break 2; } switch ($record_type) { case 0x11: // CreateDate // CreateDate case 0x12: // DateTimeOriginal $data = strtotime($data); break; case 0x200001e: // ColorSpace switch ($data) { case 1: $data = 'sRGB'; break; case 2: $data = 'Adobe RGB'; break; } break; case 0x2000023: // PictureControlData $PictureControlAdjust = array(0 => 'default', 1 => 'quick', 2 => 'full'); $FilterEffect = array(0x80 => 'off', 0x81 => 'yellow', 0x82 => 'orange', 0x83 => 'red', 0x84 => 'green', 0xff => 'n/a'); $ToningEffect = array(0x80 => 'b&w', 0x81 => 'sepia', 0x82 => 'cyanotype', 0x83 => 'red', 0x84 => 'yellow', 0x85 => 'green', 0x86 => 'blue-green', 0x87 => 'blue', 0x88 => 'purple-blue', 0x89 => 'red-purple', 0xff => 'n/a'); $data = array('PictureControlVersion' => substr($data, 0, 4), 'PictureControlName' => rtrim(substr($data, 4, 20), ""), 'PictureControlBase' => rtrim(substr($data, 24, 20), ""), 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))], 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)), 'Sharpness' => ord(substr($data, 50, 1)), 'Contrast' => ord(substr($data, 51, 1)), 'Brightness' => ord(substr($data, 52, 1)), 'Saturation' => ord(substr($data, 53, 1)), 'HueAdjustment' => ord(substr($data, 54, 1)), 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))], 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))], 'ToningSaturation' => ord(substr($data, 57, 1))); break; case 0x2000024: // WorldTime // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime // timezone is stored as offset from GMT in minutes $timezone = Helper::BigEndian2Int(substr($data, 0, 2)); if ($timezone & 0x8000) { $timezone = 0 - (0x10000 - $timezone); } $timezone /= 60; $dst = (bool) Helper::BigEndian2Int(substr($data, 2, 1)); switch (Helper::BigEndian2Int(substr($data, 3, 1))) { case 2: $datedisplayformat = 'D/M/Y'; break; case 1: $datedisplayformat = 'M/D/Y'; break; case 0: default: $datedisplayformat = 'Y/M/D'; break; } $data = array('timezone' => floatval($timezone), 'dst' => $dst, 'display' => $datedisplayformat); break; case 0x2000083: // LensType $data = array('mf' => (bool) ($data & 0x1), 'd' => (bool) ($data & 0x2), 'g' => (bool) ($data & 0x4), 'vr' => (bool) ($data & 0x8)); break; } $tag_name = isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x' . str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT); $parsed[$tag_name] = $data; } return $parsed; }
/** * @return bool */ public function analyze() { $info =& $this->getid3->info; // based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html $info['fileformat'] = 'vqf'; $info['audio']['dataformat'] = 'vqf'; $info['audio']['bitrate_mode'] = 'cbr'; $info['audio']['lossless'] = false; // shortcut $info['vqf']['raw'] = array(); $thisfile_vqf =& $info['vqf']; $thisfile_vqf_raw =& $thisfile_vqf['raw']; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $VQFheaderData = fread($this->getid3->fp, 16); $offset = 0; $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); $magic = 'TWIN'; if ($thisfile_vqf_raw['header_tag'] != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_vqf_raw['header_tag']) . '"'; unset($info['vqf']); unset($info['fileformat']); return false; } $offset += 4; $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); $offset += 8; $thisfile_vqf_raw['size'] = Helper::BigEndian2Int(substr($VQFheaderData, $offset, 4)); $offset += 4; while (ftell($this->getid3->fp) < $info['avdataend']) { $ChunkBaseOffset = ftell($this->getid3->fp); $chunkoffset = 0; $ChunkData = fread($this->getid3->fp, 8); $ChunkName = substr($ChunkData, $chunkoffset, 4); if ($ChunkName == 'DATA') { $info['avdataoffset'] = $ChunkBaseOffset; break; } $chunkoffset += 4; $ChunkSize = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; if ($ChunkSize > $info['avdataend'] - ftell($this->getid3->fp)) { $info['error'][] = 'Invalid chunk size (' . $ChunkSize . ') for chunk "' . $ChunkName . '" at offset ' . $ChunkBaseOffset; break; } if ($ChunkSize > 0) { $ChunkData .= fread($this->getid3->fp, $ChunkSize); } switch ($ChunkName) { case 'COMM': // shortcut $thisfile_vqf['COMM'] = array(); $thisfile_vqf_COMM =& $thisfile_vqf['COMM']; $thisfile_vqf_COMM['channel_mode'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $thisfile_vqf_COMM['bitrate'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $thisfile_vqf_COMM['sample_rate'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $thisfile_vqf_COMM['security_level'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; $info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); $info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; $info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate'] / 1000); if ($info['audio']['bitrate'] == 0) { $info['error'][] = 'Corrupt VQF file: bitrate_audio == zero'; return false; } break; case 'NAME': case 'AUTH': case '(c) ': case 'FILE': case 'COMT': case 'ALBM': $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8)); break; case 'DSIZ': $thisfile_vqf['DSIZ'] = Helper::BigEndian2Int(substr($ChunkData, 8, 4)); break; default: $info['warning'][] = 'Unhandled chunk type "' . $ChunkName . '" at offset ' . $ChunkBaseOffset; break; } } $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; if (isset($thisfile_vqf['DSIZ']) && $thisfile_vqf['DSIZ'] != $info['avdataend'] - $info['avdataoffset'] - strlen('DATA')) { switch ($thisfile_vqf['DSIZ']) { case 0: case 1: $info['warning'][] = 'Invalid DSIZ value "' . $thisfile_vqf['DSIZ'] . '". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v' . ($thisfile_vqf['DSIZ'] + 1) . '.0'; $info['audio']['encoder'] = 'Ahead Nero'; break; default: $info['warning'][] = 'Probable corrupted file - should be ' . $thisfile_vqf['DSIZ'] . ' bytes, actually ' . ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA')); break; } } return true; }
/** * @return bool */ 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . 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'] = Helper::LittleEndian2Int(substr($VOCheader, 20, 2)); $thisfile_voc['header']['minor_version'] = Helper::LittleEndian2Int(substr($VOCheader, 22, 1)); $thisfile_voc['header']['major_version'] = Helper::LittleEndian2Int(substr($VOCheader, 23, 1)); do { $BlockOffset = ftell($this->getid3->fp); $BlockData = fread($this->getid3->fp, 4); $BlockType = ord($BlockData[0]); $BlockSize = Helper::LittleEndian2Int(substr($BlockData, 1, 3)); $ThisBlock = array(); 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'] = Helper::LittleEndian2Int(substr($BlockData, 4, 1)); $ThisBlock['compression_type'] = 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'] = 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'] = 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'] = Helper::LittleEndian2Int(substr($BlockData, 4, 2)); $ThisBlock['pack_method'] = Helper::LittleEndian2Int(substr($BlockData, 6, 1)); $ThisBlock['stereo'] = (bool) Helper::LittleEndian2Int(substr($BlockData, 7, 1)); $thisfile_audio['channels'] = $ThisBlock['stereo'] ? 2 : 1; $thisfile_audio['sample_rate'] = 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'] = Helper::LittleEndian2Int(substr($BlockData, 4, 4)); $ThisBlock['bits_per_sample'] = Helper::LittleEndian2Int(substr($BlockData, 8, 1)); $ThisBlock['channels'] = Helper::LittleEndian2Int(substr($BlockData, 9, 1)); $ThisBlock['wFormat'] = 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 bool */ 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 "' . Helper::PrintHexBytes($info['swf']['header']['signature']) . '"'; unset($info['swf']); unset($info['fileformat']); return false; break; } $info['swf']['header']['version'] = Helper::LittleEndian2Int(substr($SWFfileData, 3, 1)); $info['swf']['header']['length'] = 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'] = Helper::Bin2Dec($X2); $info['swf']['header']['frame_height'] = 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'] = Helper::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); $info['swf']['header']['frame_count'] = 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 = Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); $TagID = ($TagIDTagLength & 0xfffc) >> 6; $TagLength = $TagIDTagLength & 0x3f; $CurrentOffset += 2; if ($TagLength == 0x3f) { $TagLength = 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(Helper::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); break; default: if ($this->ReturnAllTagData) { $info['swf']['tags'][] = $TagData; } break; } $CurrentOffset += $TagLength; } return true; }
/** * @return bool */ 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_bmp_header_raw['identifier']) . '"'; unset($info['fileformat']); unset($info['bmp']); return false; } $thisfile_bmp_header_raw['filesize'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['reserved1'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['reserved2'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['data_offset'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['header_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; // check if the hardcoded-to-1 "planes" is at offset 22 or 26 $planes22 = Helper::LittleEndian2Int(substr($BMPheader, 22, 2)); $planes26 = 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['height'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['planes'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['bits_per_pixel'] = 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['bmp_data_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_h'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_v'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['colors_used'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['colors_important'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_units'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['reserved1'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['recording'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['rendering'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['size1'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['size2'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['color_encoding'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['identifier'] = 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['height'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['planes'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['bits_per_pixel'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2)); $offset += 2; $thisfile_bmp_header_raw['compression'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['bmp_data_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['resolution_h'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['resolution_v'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true); $offset += 4; $thisfile_bmp_header_raw['colors_used'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['colors_important'] = 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['green_mask'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['blue_mask'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['alpha_mask'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['cs_type'] = 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['gamma_green'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['gamma_blue'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header['ciexyz_red'] = Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red'])); $thisfile_bmp_header['ciexyz_green'] = Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green'])); $thisfile_bmp_header['ciexyz_blue'] = 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['profile_data_offset'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['profile_data_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4)); $offset += 4; $thisfile_bmp_header_raw['reserved3'] = 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 = Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); $green = Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); $red = 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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $secondbyte = 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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $rowincrement = 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 = 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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $secondbyte = 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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); $rowincrement = 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 = 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 = 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; }
/** * @return bool */ 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 = Helper::LittleEndian2Int(substr($wavpackheader, 4, 4)); $magic = 'wvpk'; if ($blockheader_magic != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . 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'] = Helper::LittleEndian2Int(substr($wavpackheader, 12, 4)); $info['wavpack']['blockheader']['block_index'] = Helper::LittleEndian2Int(substr($wavpackheader, 16, 4)); $info['wavpack']['blockheader']['block_samples'] = Helper::LittleEndian2Int(substr($wavpackheader, 20, 4)); $info['wavpack']['blockheader']['flags_raw'] = Helper::LittleEndian2Int(substr($wavpackheader, 24, 4)); $info['wavpack']['blockheader']['crc'] = 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'] = 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 = Helper::LittleEndian2Int(substr($metablock['data'], 4, 4)); $getid3_temp = new GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_riff = new 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 GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['fileformat'] = 'riff'; $getid3_riff = new 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 GetId3Core() in metablock at offset ' . $metablock['offset']; break; case 0x24: // ID_CUESHEET $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by GetId3Core() in metablock at offset ' . $metablock['offset']; break; case 0x25: // ID_CONFIG_BLOCK $metablock['flags_raw'] = 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(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; }
/** * @param type $byteword * @param type $signed * * @return type */ public function EitherEndian2Int($byteword, $signed = false) { if ($this->getid3->info['fileformat'] == 'riff') { return Helper::LittleEndian2Int($byteword, $signed); } return Helper::BigEndian2Int($byteword, false, $signed); }