function getVOCheaderFilepointer(&$fd, &$ThisFileInfo) { $OriginalAVdataOffset = $ThisFileInfo['avdataoffset']; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $VOCheader = fread($fd, 26); if (substr($VOCheader, 0, 19) != 'Creative Voice File') { $ThisFileInfo['error'] .= "\n" . 'Expecting "Creative Voice File" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "' . substr($VOCheader, 0, 19) . '"'; return false; } $ThisFileInfo['fileformat'] = 'voc'; $ThisFileInfo['audio']['dataformat'] = 'voc'; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['channels'] = 1; // might be overriden below $ThisFileInfo['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) $ThisFileInfo['voc']['header']['datablock_offset'] = LittleEndian2Int(substr($VOCheader, 20, 2)); $ThisFileInfo['voc']['header']['minor_version'] = LittleEndian2Int(substr($VOCheader, 22, 1)); $ThisFileInfo['voc']['header']['major_version'] = LittleEndian2Int(substr($VOCheader, 23, 1)); do { $BlockOffset = ftell($fd); $BlockData = fread($fd, 4); $BlockType = LittleEndian2Int(substr($BlockData, 0, 1)); $BlockSize = LittleEndian2Int(substr($BlockData, 1, 3)); $ThisBlock = array(); switch ($BlockType) { case 0: // Terminator // do nothing, we'll break out of the loop down below break; case 1: // Sound data $BlockData .= fread($fd, 2); if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { $ThisFileInfo['avdataoffset'] = ftell($fd); } fseek($fd, $BlockSize - 2, SEEK_CUR); $ThisBlock['sample_rate_id'] = LittleEndian2Int(substr($BlockData, 4, 1)); $ThisBlock['compression_type'] = LittleEndian2Int(substr($BlockData, 5, 1)); $ThisBlock['compression_name'] = VOCcompressionTypeLookup($ThisBlock['compression_type']); if ($ThisBlock['compression_type'] <= 3) { $ThisFileInfo['voc']['compressed_bits_per_sample'] = CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name'])); } if (empty($ThisFileInfo['audio']['sample_rate'])) { // Less accurate than the Extended block (#8) data // SR byte = 256-(1000000/sample_rate) $ThisFileInfo['audio']['sample_rate'] = trunc(1000000 / (256 - $ThisBlock['sample_rate_id']) / $ThisFileInfo['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($fd, $BlockSize, SEEK_CUR); break; case 8: // Extended $BlockData .= fread($fd, 4); //00-01 Time Constant: // Mono: 65536 - (256000000 / sample_rate) // Stereo: 65536 - (256000000 / (sample_rate * 2)) $ThisBlock['time_constant'] = LittleEndian2Int(substr($BlockData, 4, 2)); $ThisBlock['pack_method'] = LittleEndian2Int(substr($BlockData, 6, 1)); $ThisBlock['stereo'] = (bool) LittleEndian2Int(substr($BlockData, 7, 1)); $ThisFileInfo['audio']['channels'] = $ThisBlock['stereo'] ? 2 : 1; $ThisFileInfo['audio']['sample_rate'] = trunc(256000000 / (65536 - $ThisBlock['time_constant']) / $ThisFileInfo['audio']['channels']); break; case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit $BlockData .= fread($fd, 12); if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { $ThisFileInfo['avdataoffset'] = ftell($fd); } fseek($fd, $BlockSize - 12, SEEK_CUR); $ThisBlock['sample_rate'] = LittleEndian2Int(substr($BlockData, 4, 4)); $ThisBlock['bits_per_sample'] = LittleEndian2Int(substr($BlockData, 8, 1)); $ThisBlock['channels'] = LittleEndian2Int(substr($BlockData, 9, 1)); $ThisBlock['wFormat'] = LittleEndian2Int(substr($BlockData, 10, 2)); $ThisBlock['compression_name'] = VOCwFormatLookup($ThisBlock['wFormat']); if (VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) { $ThisFileInfo['voc']['compressed_bits_per_sample'] = VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']); } $ThisFileInfo['audio']['sample_rate'] = $ThisBlock['sample_rate']; $ThisFileInfo['audio']['bits_per_sample'] = $ThisBlock['bits_per_sample']; $ThisFileInfo['audio']['channels'] = $ThisBlock['channels']; break; default: $ThisFileInfo['warning'] .= "\n" . 'Unhandled block type "' . $BlockType . '" at offset ' . $BlockOffset; fseek($fd, $BlockSize, SEEK_CUR); break; } if (!empty($ThisBlock)) { $ThisBlock['block_offset'] = $BlockOffset; $ThisBlock['block_size'] = $BlockSize; $ThisBlock['block_type_id'] = $BlockType; $ThisFileInfo['voc']['blocks'][] = $ThisBlock; } } while (!feof($fd) && $BlockType != 0); // Terminator block doesn't have size field, so seek back 3 spaces fseek($fd, -3, SEEK_CUR); if (!empty($ThisFileInfo['voc']['compressed_bits_per_sample'])) { $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / ($ThisFileInfo['voc']['compressed_bits_per_sample'] * $ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']); $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; } return true; }
function DateMac2Unix($macdate) { // Macintosh timestamp: seconds since 00:00h January 1, 1904 // UNIX timestamp: seconds since 00:00h January 1, 1970 return CastAsInt($macdate - 2082844800); }
function Bin2Dec($binstring) { $decvalue = 0; for ($i = 0; $i < strlen($binstring); $i++) { $decvalue += (int) substr($binstring, strlen($binstring) - $i - 1, 1) * pow(2, $i); } return CastAsInt($decvalue); }
function getMPCHeaderFilepointer(&$fd, &$ThisFileInfo) { // http://www.uni-jena.de/~pfk/mpp/sv8/header.html $ThisFileInfo['fileformat'] = 'mpc'; $ThisFileInfo['audio']['dataformat'] = 'mpc'; $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['audio']['channels'] = 2; // the format appears to be hardcoded for stereo only fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $ThisFileInfo['mpc']['header']['size'] = 30; $MPCheaderData = fread($fd, $ThisFileInfo['mpc']['header']['size']); $offset = 0; $ThisFileInfo['mpc']['header']['raw']['preamble'] = substr($MPCheaderData, $offset, 3); // should be 'MP+' $offset += 3; $StreamVersionByte = LittleEndian2Int(substr($MPCheaderData, $offset, 1)); $offset += 1; $ThisFileInfo['mpc']['header']['stream_major_version'] = $StreamVersionByte & 0xf; $ThisFileInfo['mpc']['header']['stream_minor_version'] = ($StreamVersionByte & 0xf0) >> 4; $ThisFileInfo['mpc']['header']['frame_count'] = LittleEndian2Int(substr($MPCheaderData, $offset, 4)); $offset += 4; switch ($ThisFileInfo['mpc']['header']['stream_major_version']) { case 7: //$ThisFileInfo['fileformat'] = 'SV7'; break; default: $ThisFileInfo['error'] .= "\n" . 'Only MPEGplus/Musepack SV7 supported'; return false; } $FlagsByte1 = LittleEndian2Int(substr($MPCheaderData, $offset, 4)); $offset += 4; $ThisFileInfo['mpc']['header']['intensity_stereo'] = (bool) (($FlagsByte1 & 2147483648.0) >> 31); $ThisFileInfo['mpc']['header']['mid_side_stereo'] = (bool) (($FlagsByte1 & 0x40000000) >> 30); $ThisFileInfo['mpc']['header']['max_subband'] = ($FlagsByte1 & 0x3f000000) >> 24; $ThisFileInfo['mpc']['header']['raw']['profile'] = ($FlagsByte1 & 0xf00000) >> 20; $ThisFileInfo['mpc']['header']['begin_loud'] = (bool) (($FlagsByte1 & 0x80000) >> 19); $ThisFileInfo['mpc']['header']['end_loud'] = (bool) (($FlagsByte1 & 0x40000) >> 18); $ThisFileInfo['mpc']['header']['raw']['sample_rate'] = ($FlagsByte1 & 0x30000) >> 16; $ThisFileInfo['mpc']['header']['max_level'] = $FlagsByte1 & 0xffff; $ThisFileInfo['mpc']['header']['raw']['title_peak'] = LittleEndian2Int(substr($MPCheaderData, $offset, 2)); $offset += 2; $ThisFileInfo['mpc']['header']['raw']['title_gain'] = LittleEndian2Int(substr($MPCheaderData, $offset, 2), true); $offset += 2; $ThisFileInfo['mpc']['header']['raw']['album_peak'] = LittleEndian2Int(substr($MPCheaderData, $offset, 2)); $offset += 2; $ThisFileInfo['mpc']['header']['raw']['album_gain'] = LittleEndian2Int(substr($MPCheaderData, $offset, 2), true); $offset += 2; $FlagsByte2 = LittleEndian2Int(substr($MPCheaderData, $offset, 4)); $offset += 4; $ThisFileInfo['mpc']['header']['true_gapless'] = (bool) (($FlagsByte2 & 2147483648.0) >> 31); $ThisFileInfo['mpc']['header']['last_frame_length'] = ($FlagsByte2 & 0x7ff00000) >> 20; $offset += 3; // unused? $ThisFileInfo['mpc']['header']['raw']['encoder_version'] = LittleEndian2Int(substr($MPCheaderData, $offset, 1)); $offset += 1; $ThisFileInfo['mpc']['header']['profile'] = MPCprofileNameLookup($ThisFileInfo['mpc']['header']['raw']['profile']); $ThisFileInfo['mpc']['header']['sample_rate'] = MPCfrequencyLookup($ThisFileInfo['mpc']['header']['raw']['sample_rate']); if ($ThisFileInfo['mpc']['header']['sample_rate'] == 0) { $ThisFileInfo['error'] .= "\n" . 'Corrupt MPC file: frequency == zero'; return false; } $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpc']['header']['sample_rate']; $ThisFileInfo['mpc']['header']['samples'] = (($ThisFileInfo['mpc']['header']['frame_count'] - 1) * 1152 + $ThisFileInfo['mpc']['header']['last_frame_length']) * $ThisFileInfo['audio']['channels']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['mpc']['header']['samples'] / $ThisFileInfo['audio']['channels'] / $ThisFileInfo['audio']['sample_rate']; if ($ThisFileInfo['playtime_seconds'] == 0) { $ThisFileInfo['error'] .= "\n" . 'Corrupt MPC file: playtime_seconds == zero'; return false; } // add size of file header to avdataoffset - calc bitrate correctly + MD5 data $ThisFileInfo['avdataoffset'] += $ThisFileInfo['mpc']['header']['size']; $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; $ThisFileInfo['mpc']['header']['title_peak'] = $ThisFileInfo['mpc']['header']['raw']['title_peak']; $ThisFileInfo['mpc']['header']['title_peak_db'] = MPCpeakDBLookup($ThisFileInfo['mpc']['header']['title_peak']); $ThisFileInfo['mpc']['header']['title_gain_db'] = $ThisFileInfo['mpc']['header']['raw']['title_gain'] / 100; $ThisFileInfo['mpc']['header']['album_peak'] = $ThisFileInfo['mpc']['header']['raw']['album_peak']; $ThisFileInfo['mpc']['header']['album_peak_db'] = MPCpeakDBLookup($ThisFileInfo['mpc']['header']['album_peak']); $ThisFileInfo['mpc']['header']['album_gain_db'] = $ThisFileInfo['mpc']['header']['raw']['album_gain'] / 100; $ThisFileInfo['mpc']['header']['encoder_version'] = MPCencoderVersionLookup($ThisFileInfo['mpc']['header']['raw']['encoder_version']); if ($ThisFileInfo['mpc']['header']['title_peak_db']) { $ThisFileInfo['replay_gain']['radio']['peak'] = $ThisFileInfo['mpc']['header']['title_peak']; $ThisFileInfo['replay_gain']['radio']['adjustment'] = $ThisFileInfo['mpc']['header']['title_gain_db']; } else { $ThisFileInfo['replay_gain']['radio']['peak'] = CastAsInt(round($ThisFileInfo['mpc']['header']['max_level'] * 1.18)); // why? I don't know - see mppdec.c $ThisFileInfo['replay_gain']['radio']['adjustment'] = 0; } if ($ThisFileInfo['mpc']['header']['album_peak_db']) { $ThisFileInfo['replay_gain']['audiophile']['peak'] = $ThisFileInfo['mpc']['header']['album_peak']; $ThisFileInfo['replay_gain']['audiophile']['adjustment'] = $ThisFileInfo['mpc']['header']['album_gain_db']; } $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['mpc']['header']['encoder_version'] . ', SV' . $ThisFileInfo['mpc']['header']['stream_major_version'] . '.' . $ThisFileInfo['mpc']['header']['stream_minor_version']; return true; }
function getRIFFHeaderFilepointer(&$fd, &$ThisFileInfo) { $Original['avdataoffset'] = $ThisFileInfo['avdataoffset']; $Original['avdataend'] = $ThisFileInfo['avdataend']; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $RIFFheader = fread($fd, 12); switch (substr($RIFFheader, 0, 4)) { case 'FORM': $ThisFileInfo['fileformat'] = 'aiff'; $RIFFheaderSize = EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); $ThisFileInfo['RIFF'][substr($RIFFheader, 8, 4)] = ParseRIFF($fd, $ThisFileInfo['avdataoffset'] + 12, $ThisFileInfo['avdataoffset'] + $RIFFheaderSize, $ThisFileInfo); $ThisFileInfo['RIFF']['header_size'] = $RIFFheaderSize; break; case 'RIFF': case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s $RIFFtype = substr($RIFFheader, 8, 4); if ($RIFFtype == 'RMP3') { $RIFFtype == 'WAVE'; } $ThisFileInfo['fileformat'] = 'riff'; $RIFFheaderSize = EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); $ThisFileInfo['RIFF']["{$RIFFtype}"] = ParseRIFF($fd, $ThisFileInfo['avdataoffset'] + 12, $ThisFileInfo['avdataoffset'] + $RIFFheaderSize, $ThisFileInfo); $ThisFileInfo['RIFF']['header_size'] = $RIFFheaderSize; break; default: $ThisFileInfo['error'] .= "\n" . 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?)'; unset($ThisFileInfo['fileformat']); return false; break; } $streamindex = 0; $arraykeys = array_keys($ThisFileInfo['RIFF']); switch ($arraykeys[0]) { case 'WAVE': case 'RMP3': // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s if (empty($ThisFileInfo['audio']['bitrate_mode'])) { $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; } if (empty($ThisFileInfo['audio']['dataformat'])) { $ThisFileInfo['audio']['dataformat'] = 'wav'; } if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['data'][0]['offset'])) { $ThisFileInfo['avdataoffset'] = $ThisFileInfo['RIFF'][$arraykeys[0]]['data'][0]['offset'] + 8; $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['RIFF'][$arraykeys[0]]['data'][0]['size']; } if (isset($ThisFileInfo['RIFF']['WAVE']['fmt '][0]['data'])) { $ThisFileInfo['RIFF']['audio'][$streamindex] = RIFFparseWAVEFORMATex($ThisFileInfo['RIFF']['WAVE']['fmt '][0]['data']); if ($ThisFileInfo['RIFF']['audio'][$streamindex] == 0) { $ThisFileInfo['error'] .= 'Corrupt RIFF file: bitrate_audio == zero'; return false; } $ThisFileInfo['RIFF']['raw']['fmt '] = $ThisFileInfo['RIFF']['audio'][$streamindex]['raw']; unset($ThisFileInfo['RIFF']['audio'][$streamindex]['raw']); $ThisFileInfo['audio'] = array_merge_noclobber($ThisFileInfo['audio'], $ThisFileInfo['RIFF']['audio'][$streamindex]); if (substr($ThisFileInfo['audio']['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') { $ThisFileInfo['warning'] .= "\n" . 'Audio codec = ' . $ThisFileInfo['audio']['codec']; } $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['RIFF']['audio'][$streamindex]['bitrate']; if ($ThisFileInfo['audio']['bits_per_sample'] == 0) { unset($ThisFileInfo['audio']['bits_per_sample']); } $ThisFileInfo['playtime_seconds'] = (double) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']); if (isset($ThisFileInfo['RIFF']['WAVE']['data'][0]['offset']) && isset($ThisFileInfo['RIFF']['raw']['fmt ']['wFormatTag'])) { $ThisFileInfo['audio']['lossless'] = false; switch ($ThisFileInfo['RIFF']['raw']['fmt ']['wFormatTag']) { case 1: // PCM $ThisFileInfo['audio']['lossless'] = true; break; default: // do nothing break; } } } if (isset($ThisFileInfo['RIFF']['WAVE']['rgad'][0]['data'])) { require_once GETID3_INCLUDEPATH . 'getid3.rgad.php'; $rgadData = $ThisFileInfo['RIFF']['WAVE']['rgad'][0]['data']; $ThisFileInfo['RIFF']['raw']['rgad']['fPeakAmplitude'] = LittleEndian2Float(substr($rgadData, 0, 4)); $ThisFileInfo['RIFF']['raw']['rgad']['nRadioRgAdjust'] = EitherEndian2Int($ThisFileInfo, substr($rgadData, 4, 2)); $ThisFileInfo['RIFF']['raw']['rgad']['nAudiophileRgAdjust'] = EitherEndian2Int($ThisFileInfo, substr($rgadData, 6, 2)); $nRadioRgAdjustBitstring = str_pad(Dec2Bin($ThisFileInfo['RIFF']['raw']['rgad']['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); $nAudiophileRgAdjustBitstring = str_pad(Dec2Bin($ThisFileInfo['RIFF']['raw']['rgad']['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['name'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['originator'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['signbit'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['adjustment'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['name'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['originator'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['signbit'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['adjustment'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9)); $ThisFileInfo['RIFF']['rgad']['peakamplitude'] = $ThisFileInfo['RIFF']['raw']['rgad']['fPeakAmplitude']; if ($ThisFileInfo['RIFF']['raw']['rgad']['radio']['name'] != 0 && $ThisFileInfo['RIFF']['raw']['rgad']['radio']['originator'] != 0) { $ThisFileInfo['RIFF']['rgad']['radio']['name'] = RGADnameLookup($ThisFileInfo['RIFF']['raw']['rgad']['radio']['name']); $ThisFileInfo['RIFF']['rgad']['radio']['originator'] = RGADoriginatorLookup($ThisFileInfo['RIFF']['raw']['rgad']['radio']['originator']); $ThisFileInfo['RIFF']['rgad']['radio']['adjustment'] = RGADadjustmentLookup($ThisFileInfo['RIFF']['raw']['rgad']['radio']['adjustment'], $ThisFileInfo['RIFF']['raw']['rgad']['radio']['signbit']); } if ($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['name'] != 0 && $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['originator'] != 0) { $ThisFileInfo['RIFF']['rgad']['audiophile']['name'] = RGADnameLookup($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['name']); $ThisFileInfo['RIFF']['rgad']['audiophile']['originator'] = RGADoriginatorLookup($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['originator']); $ThisFileInfo['RIFF']['rgad']['audiophile']['adjustment'] = RGADadjustmentLookup($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['adjustment'], $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['signbit']); } } if (isset($ThisFileInfo['RIFF']['WAVE']['fact'][0]['data'])) { $ThisFileInfo['RIFF']['raw']['fact']['NumberOfSamples'] = EitherEndian2Int($ThisFileInfo, substr($ThisFileInfo['RIFF']['WAVE']['fact'][0]['data'], 0, 4)); if (isset($ThisFileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']) && $ThisFileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec'] > 0) { $ThisFileInfo['playtime_seconds'] = (double) $ThisFileInfo['RIFF']['raw']['fact']['NumberOfSamples'] / $ThisFileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']; } if (isset($ThisFileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec']) && $ThisFileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec']) { $ThisFileInfo['audio']['bitrate'] = CastAsInt($ThisFileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec'] * 8); } } if (isset($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'])) { $ThisFileInfo['RIFF']['WAVE']['bext'][0]['title'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 0, 256)); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['author'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 256, 32)); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['reference'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 288, 32)); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_date'] = substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 320, 10); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_time'] = substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 330, 8); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['time_reference'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 338, 8)); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['bwf_version'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 346, 1)); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['reserved'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 347, 254)); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['coding_history'] = explode("\r\n", trim(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['data'], 601))); $ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_date_unix'] = mktime(substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_time'], 0, 2), substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_time'], 3, 2), substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_time'], 6, 2), substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_date'], 5, 2), substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_date'], 8, 2), substr($ThisFileInfo['RIFF']['WAVE']['bext'][0]['origin_date'], 0, 4)); $ThisFileInfo['RIFF']['comments']['author'][] = $ThisFileInfo['RIFF']['WAVE']['bext'][0]['author']; $ThisFileInfo['RIFF']['comments']['title'][] = $ThisFileInfo['RIFF']['WAVE']['bext'][0]['title']; } if (isset($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['data'])) { $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['sound_information'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['data'], 0, 2)); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['homogenous'] = (bool) ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['sound_information'] & 0x1); if ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['homogenous']) { $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['padding'] = InverseBoolean($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['sound_information'] & 0x2); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['22_or_44'] = (bool) ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['sound_information'] & 0x4); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['free_format'] = (bool) ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['sound_information'] & 0x8); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['nominal_frame_size'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['data'], 2, 2)); } $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['anciliary_data_length'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['data'], 6, 2)); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['anciliary_data_def'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['data'], 8, 2)); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['anciliary_data_left'] = (bool) ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['anciliary_data_def'] & 0x1); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['anciliary_data_free'] = (bool) ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['anciliary_data_def'] & 0x2); $ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['flags']['anciliary_data_right'] = (bool) ($ThisFileInfo['RIFF']['WAVE']['MEXT'][0]['raw']['anciliary_data_def'] & 0x4); } if (isset($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'])) { $ThisFileInfo['RIFF']['WAVE']['cart'][0]['version'] = substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 0, 4); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['title'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 4, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['artist'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 68, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['cut_id'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 132, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['client_id'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 196, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['category'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 260, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['classification'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 324, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['out_cue'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 388, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['start_date'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 452, 10)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['start_time'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 462, 8)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['end_date'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 470, 10)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['end_time'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 480, 8)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['producer_app_id'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 488, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['producer_app_version'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 552, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['user_defined_text'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 616, 64)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['zero_db_reference'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 680, 4), true); for ($i = 0; $i < 8; $i++) { $ThisFileInfo['RIFF']['WAVE']['cart'][0]['post_time'][$i]['usage_fourcc'] = substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 684 + $i * 8, 4); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['post_time'][$i]['timer_value'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 684 + $i * 8 + 4, 4)); } $ThisFileInfo['RIFF']['WAVE']['cart'][0]['url'] = trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 748, 1024)); $ThisFileInfo['RIFF']['WAVE']['cart'][0]['tag_text'] = explode("\r\n", trim(substr($ThisFileInfo['RIFF']['WAVE']['cart'][0]['data'], 1772))); $ThisFileInfo['RIFF']['comments']['artist'][] = $ThisFileInfo['RIFF']['WAVE']['cart'][0]['artist']; $ThisFileInfo['RIFF']['comments']['title'][] = $ThisFileInfo['RIFF']['WAVE']['cart'][0]['title']; } if (!isset($ThisFileInfo['audio']['bitrate']) && isset($ThisFileInfo['RIFF']['audio'][$streamindex]['bitrate'])) { $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['RIFF']['audio'][$streamindex]['bitrate']; $ThisFileInfo['playtime_seconds'] = (double) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']); } if (!empty($ThisFileInfo['wavpack'])) { $ThisFileInfo['audio']['dataformat'] = 'wavpack'; $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['audio']['encoder'] = 'WavPack v' . $ThisFileInfo['wavpack']['version']; // Reset to the way it was - RIFF parsing will have messed this up $ThisFileInfo['avdataend'] = $Original['avdataend']; $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; fseek($fd, $ThisFileInfo['avdataoffset'] - 44, SEEK_SET); $RIFFdata = fread($fd, 44); $OrignalRIFFheaderSize = LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; $OrignalRIFFdataSize = LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { $ThisFileInfo['avdataend'] -= $OrignalRIFFheaderSize - $OrignalRIFFdataSize; fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); } // 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); ParseRIFFdata($RIFFdata, $ThisFileInfo); } break; case 'AVI ': $ThisFileInfo['video']['bitrate_mode'] = 'cbr'; $ThisFileInfo['video']['dataformat'] = 'avi'; $ThisFileInfo['mime_type'] = 'video/avi'; if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['movi']['offset'])) { $ThisFileInfo['avdataoffset'] = $ThisFileInfo['RIFF'][$arraykeys[0]]['movi']['offset'] + 8; $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['RIFF'][$arraykeys[0]]['movi']['size']; } if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['avih'][$streamindex]['data'])) { $avihData = $ThisFileInfo['RIFF']['AVI ']['hdrl']['avih'][$streamindex]['data']; $ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 0, 4)); // frame display rate (or 0L) if ($ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] == 0) { $ThisFileInfo['error'] .= 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; return false; } $ThisFileInfo['RIFF']['raw']['avih']['dwMaxBytesPerSec'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 4, 4)); // max. transfer rate $ThisFileInfo['RIFF']['raw']['avih']['dwPaddingGranularity'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. $ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 12, 4)); // the ever-present flags $ThisFileInfo['RIFF']['raw']['avih']['dwTotalFrames'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 16, 4)); // # frames in file $ThisFileInfo['RIFF']['raw']['avih']['dwInitialFrames'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 20, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwStreams'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 24, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwSuggestedBufferSize'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 28, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwWidth'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 32, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwHeight'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 36, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwScale'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 40, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwRate'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 44, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwStart'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 48, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwLength'] = EitherEndian2Int($ThisFileInfo, substr($avihData, 52, 4)); $ThisFileInfo['RIFF']['raw']['avih']['flags']['hasindex'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x10); $ThisFileInfo['RIFF']['raw']['avih']['flags']['mustuseindex'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x20); $ThisFileInfo['RIFF']['raw']['avih']['flags']['interleaved'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x100); $ThisFileInfo['RIFF']['raw']['avih']['flags']['trustcktype'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x800); $ThisFileInfo['RIFF']['raw']['avih']['flags']['capturedfile'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x10000); $ThisFileInfo['RIFF']['raw']['avih']['flags']['copyrighted'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x20010); if ($ThisFileInfo['RIFF']['raw']['avih']['dwWidth'] > 0) { $ThisFileInfo['RIFF']['video'][$streamindex]['frame_width'] = $ThisFileInfo['RIFF']['raw']['avih']['dwWidth']; $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['RIFF']['video'][$streamindex]['frame_width']; } if ($ThisFileInfo['RIFF']['raw']['avih']['dwHeight'] > 0) { $ThisFileInfo['RIFF']['video'][$streamindex]['frame_height'] = $ThisFileInfo['RIFF']['raw']['avih']['dwHeight']; $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['RIFF']['video'][$streamindex]['frame_height']; } $ThisFileInfo['RIFF']['video'][$streamindex]['frame_rate'] = round(1000000 / $ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'], 3); $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['RIFF']['video'][$streamindex]['frame_rate']; } if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][0]['data'])) { if (is_array($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'])) { for ($i = 0; $i < count($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh']); $i++) { if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][$i]['data'])) { $strhData = $ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][$i]['data']; $strhfccType = substr($strhData, 0, 4); if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strf'][$i]['data'])) { $strfData = $ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strf'][$i]['data']; switch ($strhfccType) { case 'auds': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = 'wav'; if (isset($ThisFileInfo['RIFF']['audio']) && is_array($ThisFileInfo['RIFF']['audio'])) { $streamindex = count($ThisFileInfo['RIFF']['audio']); } $ThisFileInfo['RIFF']['audio'][$streamindex] = RIFFparseWAVEFORMATex($strfData); $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex] = $ThisFileInfo['RIFF']['audio'][$streamindex]['raw']; unset($ThisFileInfo['RIFF']['audio'][$streamindex]['raw']); $ThisFileInfo['audio'] = array_merge_noclobber($ThisFileInfo['audio'], $ThisFileInfo['RIFF']['audio'][$streamindex]); $ThisFileInfo['audio']['lossless'] = false; switch ($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['wFormatTag']) { case 1: // PCM $ThisFileInfo['audio']['lossless'] = true; break; case 85: // MPEG Layer 3 $ThisFileInfo['audio']['dataformat'] = 'mp3'; break; case 8192: // AC-3 $ThisFileInfo['audio']['dataformat'] = 'ac3'; break; default: $ThisFileInfo['audio']['dataformat'] = 'wav'; break; } break; case 'iavs': case 'vids': $ThisFileInfo['RIFF']['raw']['strh'][$i]['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; $ThisFileInfo['RIFF']['raw']['strh'][$i]['fccHandler'] = substr($strhData, 4, 4); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwFlags'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 8, 4)); // Contains AVITF_* flags $ThisFileInfo['RIFF']['raw']['strh'][$i]['wPriority'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 12, 2)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['wLanguage'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 14, 2)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwInitialFrames'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 16, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwScale'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 20, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwRate'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 24, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwStart'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 28, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwLength'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 32, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwSuggestedBufferSize'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 36, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwQuality'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 40, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwSampleSize'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 44, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['rcFrame'] = EitherEndian2Int($ThisFileInfo, substr($strhData, 48, 4)); $ThisFileInfo['RIFF']['video'][$streamindex]['codec'] = RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strh'][$i]['fccHandler']); if (!$ThisFileInfo['RIFF']['video'][$streamindex]['codec'] && isset($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']) && RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc'])) { $ThisFileInfo['RIFF']['video'][$streamindex]['codec'] = RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']); } $ThisFileInfo['video']['codec'] = $ThisFileInfo['RIFF']['video'][$streamindex]['codec']; switch ($ThisFileInfo['RIFF']['raw']['strh'][$i]['fccHandler']) { case 'HFYU': // Huffman Lossless Codec // Huffman Lossless Codec case 'IRAW': // Intel YUV Uncompressed // Intel YUV Uncompressed case 'YUY2': // Uncompressed YUV 4:2:2 $ThisFileInfo['video']['lossless'] = true; break; default: $ThisFileInfo['video']['lossless'] = false; break; } switch ($strhfccType) { case 'vids': $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biSize'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biWidth'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 4, 4)); // width of the bitmap in pixels $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biHeight'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biPlanes'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biBitCount'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 14, 2)); // Specifies the number of bits per pixels $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc'] = substr($strfData, 16, 4); // $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biSizeImage'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biXPelsPerMeter'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biYPelsPerMeter'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biClrUsed'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biClrImportant'] = EitherEndian2Int($ThisFileInfo, substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important $ThisFileInfo['video']['bits_per_sample'] = $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biBitCount']; if ($ThisFileInfo['RIFF']['video'][$streamindex]['codec'] == 'DV') { $ThisFileInfo['RIFF']['video'][$streamindex]['dv_type'] = 2; } break; case 'iavs': $ThisFileInfo['RIFF']['video'][$streamindex]['dv_type'] = 1; break; } break; default: $ThisFileInfo['warning'] .= "\n" . 'Unhandled fccType for stream (' . $i . '): "' . $strhfccType . '"'; break; } } } if (isset($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']) && RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc'])) { $ThisFileInfo['RIFF']['video'][$streamindex]['codec'] = RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']); $ThisFileInfo['video']['codec'] = $ThisFileInfo['RIFF']['video'][$streamindex]['codec']; switch ($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']) { case 'HFYU': // Huffman Lossless Codec // Huffman Lossless Codec case 'IRAW': // Intel YUV Uncompressed // Intel YUV Uncompressed case 'YUY2': // Uncompressed YUV 4:2:2 $ThisFileInfo['video']['lossless'] = true; $ThisFileInfo['video']['bits_per_sample'] = 24; break; default: $ThisFileInfo['video']['lossless'] = false; $ThisFileInfo['video']['bits_per_sample'] = 24; break; } } } } } break; case 'CDDA': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = 'cda'; $ThisFileInfo['audio']['lossless'] = true; unset($ThisFileInfo['mime_type']); $ThisFileInfo['avdataoffset'] = 44; if (isset($ThisFileInfo['RIFF']['CDDA']['fmt '][0]['data'])) { $fmtData = $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['data']; $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['unknown1'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 0, 2)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['track_num'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 2, 2)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['disc_id'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 4, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['start_offset_frame'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 8, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_frames'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 12, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['unknown6'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 16, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['unknown7'] = EitherEndian2Int($ThisFileInfo, substr($fmtData, 20, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['start_offset_seconds'] = (double) $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['start_offset_frame'] / 75; $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_seconds'] = (double) $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_frames'] / 75; $ThisFileInfo['comments']['track'] = $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['track_num']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_seconds']; // hardcoded data for CD-audio $ThisFileInfo['audio']['sample_rate'] = 44100; $ThisFileInfo['audio']['channels'] = 2; $ThisFileInfo['audio']['bits_per_sample'] = 16; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['audio']['sample_rate'] * $ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['bits_per_sample']; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; } break; case 'AIFF': case 'AIFC': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = 'aiff'; $ThisFileInfo['audio']['lossless'] = true; $ThisFileInfo['mime_type'] = 'audio/x-aiff'; if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['SSND'][0]['offset'])) { $ThisFileInfo['avdataoffset'] = $ThisFileInfo['RIFF'][$arraykeys[0]]['SSND'][0]['offset'] + 8; $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['RIFF'][$arraykeys[0]]['SSND'][0]['size']; if ($ThisFileInfo['avdataend'] > $ThisFileInfo['filesize']) { if ($ThisFileInfo['avdataend'] == $ThisFileInfo['filesize'] + 1 && $ThisFileInfo['filesize'] % 2 == 1) { // structures rounded to 2-byte boundary, but dumb encoders // forget to pad end of file to make this actually work } else { $ThisFileInfo['warning'] .= "\n" . 'Probable truncated AIFF file: expecting ' . $ThisFileInfo['RIFF'][$arraykeys[0]]['SSND'][0]['size'] . ' bytes of audio data, only ' . ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']) . ' bytes found'; } $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; } } if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'])) { $ThisFileInfo['RIFF']['audio']['channels'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 0, 2), true); $ThisFileInfo['RIFF']['audio']['total_samples'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 2, 4), false); $ThisFileInfo['RIFF']['audio']['bits_per_sample'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 6, 2), true); $ThisFileInfo['RIFF']['audio']['sample_rate'] = (int) BigEndian2Float(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 8, 10)); if ($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['size'] > 18) { $ThisFileInfo['RIFF']['audio']['codec_fourcc'] = substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 18, 4); $CodecNameSize = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 22, 1), false); $ThisFileInfo['RIFF']['audio']['codec_name'] = substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMM'][0]['data'], 23, $CodecNameSize); if ($ThisFileInfo['RIFF']['audio']['codec_name'] == 'NONE') { $ThisFileInfo['audio']['codec'] = 'Pulse Code Modulation (PCM)'; $ThisFileInfo['audio']['lossless'] = true; } else { $ThisFileInfo['audio']['codec'] = $ThisFileInfo['RIFF']['audio']['codec_name']; $ThisFileInfo['audio']['lossless'] = false; } } $ThisFileInfo['audio']['channels'] = $ThisFileInfo['RIFF']['audio']['channels']; if ($ThisFileInfo['RIFF']['audio']['bits_per_sample'] > 0) { $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['RIFF']['audio']['bits_per_sample']; } $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['RIFF']['audio']['sample_rate']; if ($ThisFileInfo['audio']['sample_rate'] == 0) { $ThisFileInfo['error'] .= "\n" . 'Corrupted AIFF file: sample_rate == zero'; return false; } $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['RIFF']['audio']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; } if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['COMT'])) { $offset = 0; $CommentCount = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMT'][0]['data'], $offset, 2), false); $offset += 2; for ($i = 0; $i < $CommentCount; $i++) { $ThisFileInfo['comments_raw'][$i]['timestamp'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMT'][0]['data'], $offset, 4), false); $offset += 4; $ThisFileInfo['comments_raw'][$i]['marker_id'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMT'][0]['data'], $offset, 2), true); $offset += 2; $CommentLength = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMT'][0]['data'], $offset, 2), false); $offset += 2; $ThisFileInfo['comments_raw'][$i]['comment'] = substr($ThisFileInfo['RIFF'][$arraykeys[0]]['COMT'][0]['data'], $offset, $CommentLength); $offset += $CommentLength; $ThisFileInfo['comments_raw'][$i]['timestamp_unix'] = DateMac2Unix($ThisFileInfo['comments_raw'][$i]['timestamp']); $ThisFileInfo['RIFF']['comments']['comment'][] = $ThisFileInfo['comments_raw'][$i]['comment']; } } $CommentsChunkNames = array('NAME' => 'title', 'author' => 'artist', '(c) ' => 'copyright', 'ANNO' => 'comment'); foreach ($CommentsChunkNames as $key => $value) { if (isset($ThisFileInfo['RIFF'][$arraykeys[0]][$key][0]['data'])) { $ThisFileInfo['RIFF']['comments'][$value][] = $ThisFileInfo['RIFF'][$arraykeys[0]][$key][0]['data']; } } if (isset($ThisFileInfo['RIFF']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['RIFF']['comments'], $ThisFileInfo, true, true, true); } break; case '8SVX': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = '8svx'; $ThisFileInfo['audio']['bits_per_sample'] = 8; $ThisFileInfo['audio']['channels'] = 1; // overridden below, if need be $ThisFileInfo['mime_type'] = 'audio/x-aiff'; if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['BODY'][0]['offset'])) { $ThisFileInfo['avdataoffset'] = $ThisFileInfo['RIFF'][$arraykeys[0]]['BODY'][0]['offset'] + 8; $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['RIFF'][$arraykeys[0]]['BODY'][0]['size']; if ($ThisFileInfo['avdataend'] > $ThisFileInfo['filesize']) { $ThisFileInfo['warning'] .= "\n" . 'Probable truncated AIFF file: expecting ' . $ThisFileInfo['RIFF'][$arraykeys[0]]['BODY'][0]['size'] . ' bytes of audio data, only ' . ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']) . ' bytes found'; } } if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['offset'])) { $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['oneShotHiSamples'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 0, 4)); $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['repeatHiSamples'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 4, 4)); $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['samplesPerHiCycle'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 8, 4)); $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['samplesPerSec'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 12, 2)); $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['ctOctave'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 14, 1)); $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['sCompression'] = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 15, 1)); $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['Volume'] = FixedPoint16_16(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['data'], 16, 4)); $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['samplesPerSec']; switch ($ThisFileInfo['RIFF'][$arraykeys[0]]['VHDR'][0]['sCompression']) { case 0: $ThisFileInfo['audio']['codec'] = 'Pulse Code Modulation (PCM)'; $ThisFileInfo['audio']['lossless'] = true; $ActualBitsPerSample = 8; break; case 1: $ThisFileInfo['audio']['codec'] = 'Fibonacci-delta encoding'; $ThisFileInfo['audio']['lossless'] = false; $ActualBitsPerSample = 4; break; default: $ThisFileInfo['warning'] .= "\n" . 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "' . sCompression . '"'; break; } } if (isset($ThisFileInfo['RIFF'][$arraykeys[0]]['CHAN'][0]['data'])) { $ChannelsIndex = BigEndian2Int(substr($ThisFileInfo['RIFF'][$arraykeys[0]]['CHAN'][0]['data'], 0, 4)); switch ($ChannelsIndex) { case 6: // Stereo $ThisFileInfo['audio']['channels'] = 2; break; case 2: // Left channel only // Left channel only case 4: // Right channel only $ThisFileInfo['audio']['channels'] = 1; break; default: $ThisFileInfo['warning'] .= "\n" . 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "' . $ChannelsIndex . '"'; break; } } $CommentsChunkNames = array('NAME' => 'title', 'author' => 'artist', '(c) ' => 'copyright', 'ANNO' => 'comment'); foreach ($CommentsChunkNames as $key => $value) { if (isset($ThisFileInfo['RIFF'][$arraykeys[0]][$key][0]['data'])) { $ThisFileInfo['RIFF']['comments'][$value][] = $ThisFileInfo['RIFF'][$arraykeys[0]][$key][0]['data']; } } if (isset($ThisFileInfo['RIFF']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['RIFF']['comments'], $ThisFileInfo, true, true, true); } $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['audio']['sample_rate'] * $ActualBitsPerSample * $ThisFileInfo['audio']['channels']; if (!empty($ThisFileInfo['audio']['bitrate'])) { $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['audio']['bitrate'] / 8); } break; default: $ThisFileInfo['error'] .= "\n" . 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|), found "' . $arraykeys[0] . '" instead'; unset($ThisFileInfo['fileformat']); break; } if (isset($ThisFileInfo['RIFF']['WAVE']['DISP']) && is_array($ThisFileInfo['RIFF']['WAVE']['DISP'])) { $ThisFileInfo['tags'][] = 'riff'; $ThisFileInfo['RIFF']['comments']['title'][] = trim(substr($ThisFileInfo['RIFF']['WAVE']['DISP'][count($ThisFileInfo['RIFF']['WAVE']['DISP']) - 1]['data'], 4)); } if (isset($ThisFileInfo['RIFF']['WAVE']['INFO']) && is_array($ThisFileInfo['RIFF']['WAVE']['INFO'])) { $ThisFileInfo['tags'][] = 'riff'; $RIFFinfoKeyLookup = array('IART' => 'artist', 'IGNR' => 'genre', 'ICMT' => 'comment', 'ICOP' => 'copyright', 'IENG' => 'engineers', 'IKEY' => 'keywords', 'IMED' => 'orignalmedium', 'INAM' => 'name', 'ISRC' => 'sourcesupplier', 'ITCH' => 'digitizer', 'ISBJ' => 'subject', 'ISRF' => 'digitizationsource', 'ISFT' => 'encoded_by'); foreach ($RIFFinfoKeyLookup as $key => $value) { if (isset($ThisFileInfo['RIFF']['WAVE']['INFO']["{$key}"])) { foreach ($ThisFileInfo['RIFF']['WAVE']['INFO']["{$key}"] as $commentid => $commentdata) { if (trim($commentdata['data']) != '') { $ThisFileInfo['RIFF']['comments']["{$value}"][] = trim($commentdata['data']); } } } } } if (!empty($ThisFileInfo['RIFF']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['RIFF']['comments'], $ThisFileInfo, true, false, true); } if (empty($ThisFileInfo['audio']['encoder']) && !empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; } if (!isset($ThisFileInfo['playtime_seconds'])) { $ThisFileInfo['playtime_seconds'] = 0; } if (isset($ThisFileInfo['RIFF']['raw']['avih']['dwTotalFrames']) && isset($ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'])) { $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['RIFF']['raw']['avih']['dwTotalFrames'] * ($ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] / 1000000); } if ($ThisFileInfo['playtime_seconds'] > 0) { if (isset($ThisFileInfo['RIFF']['audio']) && isset($ThisFileInfo['RIFF']['video'])) { if (!isset($ThisFileInfo['bitrate'])) { $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; } } elseif (isset($ThisFileInfo['RIFF']['audio']) && !isset($ThisFileInfo['RIFF']['video'])) { if (!isset($ThisFileInfo['audio']['bitrate'])) { $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; } } elseif (!isset($ThisFileInfo['RIFF']['audio']) && isset($ThisFileInfo['RIFF']['video'])) { if (!isset($ThisFileInfo['video']['bitrate'])) { $ThisFileInfo['video']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; } } } if (isset($ThisFileInfo['RIFF']['video']) && isset($ThisFileInfo['audio']['bitrate']) && $ThisFileInfo['audio']['bitrate'] > 0 && $ThisFileInfo['playtime_seconds'] > 0) { $ThisFileInfo['audio']['bitrate'] = 0; $ThisFileInfo['video']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; foreach ($ThisFileInfo['RIFF']['audio'] as $channelnumber => $audioinfoarray) { $ThisFileInfo['video']['bitrate'] -= $audioinfoarray['bitrate']; $ThisFileInfo['audio']['bitrate'] += $audioinfoarray['bitrate']; } if ($ThisFileInfo['video']['bitrate'] <= 0) { unset($ThisFileInfo['video']['bitrate']); } if ($ThisFileInfo['audio']['bitrate'] <= 0) { unset($ThisFileInfo['audio']['bitrate']); } } if (!empty($ThisFileInfo['RIFF']['raw']['fmt ']['nBitsPerSample']) && $ThisFileInfo['RIFF']['raw']['fmt ']['nBitsPerSample'] > 0) { $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['RIFF']['raw']['fmt ']['nBitsPerSample']; } return true; }
function getID3v2Filepointer(&$fd, &$ThisFileInfo) { // Overall tag structure: // +-----------------------------+ // | Header (10 bytes) | // +-----------------------------+ // | Extended Header | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Frames (variable length) | // +-----------------------------+ // | Padding | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Footer (10 bytes, OPTIONAL) | // +-----------------------------+ // Header // ID3v2/file identifier "ID3" // ID3v2 version $04 00 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) // ID3v2 size 4 * %0xxxxxxx rewind($fd); $header = fread($fd, 10); if (substr($header, 0, 3) == 'ID3') { $ThisFileInfo['id3v2']['header'] = true; $ThisFileInfo['id3v2']['majorversion'] = ord($header[3]); $ThisFileInfo['id3v2']['minorversion'] = ord($header[4]); } else { return false; } if ($ThisFileInfo['id3v2']['majorversion'] > 4) { // this script probably won't correctly parse ID3v2.5.x and above. $ThisFileInfo['error'] .= "\n" . 'this script only parses up to ID3v2.4.x - this tag is ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . '.' . $ThisFileInfo['id3v2']['minorversion']; return false; } $id3_flags = BigEndian2Bin($header[5]); switch ($ThisFileInfo['id3v2']['majorversion']) { case 2: // %ab000000 in v2.2 $ThisFileInfo['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $ThisFileInfo['id3v2']['flags']['compression'] = $id3_flags[1]; // b - Compression break; case 3: // %abc00000 in v2.3 $ThisFileInfo['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $ThisFileInfo['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $ThisFileInfo['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator break; case 4: // %abcd0000 in v2.4 $ThisFileInfo['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $ThisFileInfo['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $ThisFileInfo['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator $ThisFileInfo['id3v2']['flags']['isfooter'] = $id3_flags[3]; // d - Footer present break; } $ThisFileInfo['id3v2']['headerlength'] = BigEndian2Int(substr($header, 6, 4), 1) + ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion']); // Extended Header if (isset($ThisFileInfo['id3v2']['flags']['exthead']) && $ThisFileInfo['id3v2']['flags']['exthead']) { // Extended header size 4 * %0xxxxxxx // Number of flag bytes $01 // Extended Flags $xx // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. $extheader = fread($fd, 4); $ThisFileInfo['id3v2']['extheaderlength'] = BigEndian2Int($extheader, 1); // The extended flags field, with its size described by 'number of flag bytes', is defined as: // %0bcd0000 // b - Tag is an update // Flag data length $00 // c - CRC data present // Flag data length $05 // Total frame CRC 5 * %0xxxxxxx // d - Tag restrictions // Flag data length $01 $extheaderflagbytes = fread($fd, 1); $extheaderflags = fread($fd, $extheaderflagbytes); $id3_exthead_flags = BigEndian2Bin(substr($header, 5, 1)); $ThisFileInfo['id3v2']['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1); $ThisFileInfo['id3v2']['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1); if ($ThisFileInfo['id3v2']['exthead_flags']['CRC']) { $extheaderrawCRC = fread($fd, 5); $ThisFileInfo['id3v2']['exthead_flags']['CRC'] = BigEndian2Int($extheaderrawCRC, 1); } $ThisFileInfo['id3v2']['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1); if ($ThisFileInfo['id3v2']['exthead_flags']['restrictions']) { // Restrictions %ppqrrstt $extheaderrawrestrictions = fread($fd, 1); $ThisFileInfo['id3v2']['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions } } // end extended header // Frames // All ID3v2 frames consists of one frame header followed by one or more // fields containing the actual information. The header is always 10 // bytes and laid out as follows: // // Frame ID $xx xx xx xx (four characters) // Size 4 * %0xxxxxxx // Flags $xx xx $sizeofframes = $ThisFileInfo['id3v2']['headerlength'] - ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion']); if (isset($ThisFileInfo['id3v2']['extheaderlength'])) { $sizeofframes -= $ThisFileInfo['id3v2']['extheaderlength']; } if (isset($ThisFileInfo['id3v2']['flags']['isfooter']) && $ThisFileInfo['id3v2']['flags']['isfooter']) { $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio } if ($sizeofframes > 0) { $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) if (isset($ThisFileInfo['id3v2']['flags']['unsynch']) && $ThisFileInfo['id3v2']['flags']['unsynch'] && $ThisFileInfo['id3v2']['majorversion'] <= 3) { $framedata = DeUnSynchronise($framedata); } // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead // of on tag level, making it easier to skip frames, increasing the streamability // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that // there exists an unsynchronised frame, while the new unsynchronisation flag in // the frame header [S:4.1.2] indicates unsynchronisation. $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header while (isset($framedata) && strlen($framedata) > 0) { // cycle through until no more frame data is left to parse if (strlen($framedata) < ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion'])) { // insufficient room left in ID3v2 header for actual data - must be padding $ThisFileInfo['id3v2']['padding']['start'] = $framedataoffset; $ThisFileInfo['id3v2']['padding']['length'] = strlen($framedata); $ThisFileInfo['id3v2']['padding']['valid'] = true; for ($i = 0; $i < $ThisFileInfo['id3v2']['padding']['length']; $i++) { if (substr($framedata, $i, 1) != chr(0)) { $ThisFileInfo['id3v2']['padding']['valid'] = false; $ThisFileInfo['id3v2']['padding']['errorpos'] = $ThisFileInfo['id3v2']['padding']['start'] + $i; $ThisFileInfo['warning'] .= "\n" . 'Invalid ID3v2 padding found at offset ' . $ThisFileInfo['id3v2']['padding']['errorpos']; break; } } break; // skip rest of ID3v2 header } if ($ThisFileInfo['id3v2']['majorversion'] == 2) { // Frame ID $xx xx xx (three characters) // Size $xx xx xx (24-bit integer) // Flags $xx xx $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header $framedata = substr($framedata, 6); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 3); $frame_size = BigEndian2Int(substr($frame_header, 3, 3), 0); $frame_flags = ''; // not used for anything, just to avoid E_NOTICEs } elseif ($ThisFileInfo['id3v2']['majorversion'] > 2) { // Frame ID $xx xx xx xx (four characters) // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) // Flags $xx xx $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header $framedata = substr($framedata, 10); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 4); if ($ThisFileInfo['id3v2']['majorversion'] == 3) { $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } else { // ID3v2.4+ $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) } if ($frame_size < strlen($framedata) + 4) { $nextFrameID = substr($framedata, $frame_size, 4); if (IsValidID3v2FrameName($nextFrameID, $ThisFileInfo['id3v2']['majorversion'])) { // next frame is OK } elseif ($frame_name == chr(0) . 'MP3' || $frame_name == chr(0) . chr(0) . 'MP' || $frame_name == ' MP3' || $frame_name == 'MP3e') { // MP3ext known broken frames - "ok" for the purposes of this test } elseif ($ThisFileInfo['id3v2']['majorversion'] == 4 && IsValidID3v2FrameName(substr($framedata, BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3)) { $ThisFileInfo['warning'] .= "\n" . 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of Helium2 (www.helium2.com) is a known culprit of this. Tag has been parsed as ID3v2.3'; $ThisFileInfo['id3v2']['majorversion'] = 3; $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } } $frame_flags = BigEndian2Bin(substr($frame_header, 8, 2)); } if ($ThisFileInfo['id3v2']['majorversion'] == 2 && $frame_name == chr(0) . chr(0) . chr(0) || $frame_name == chr(0) . chr(0) . chr(0) . chr(0)) { // padding encountered $ThisFileInfo['id3v2']['padding']['start'] = $framedataoffset; $ThisFileInfo['id3v2']['padding']['length'] = strlen($framedata); $ThisFileInfo['id3v2']['padding']['valid'] = true; for ($i = 0; $i < $ThisFileInfo['id3v2']['padding']['length']; $i++) { if (substr($framedata, $i, 1) != chr(0)) { $ThisFileInfo['id3v2']['padding']['valid'] = false; $ThisFileInfo['id3v2']['padding']['errorpos'] = $ThisFileInfo['id3v2']['padding']['start'] + $i; $ThisFileInfo['warning'] .= "\n" . 'Invalid ID3v2 padding found at offset ' . $ThisFileInfo['id3v2']['padding']['errorpos']; break; } } break; // skip rest of ID3v2 header } if ($frame_name == 'COM ') { $ThisFileInfo['warning'] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $ThisFileInfo['id3v2']['majorversion'] . '))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; $frame_name = 'COMM'; } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName($frame_name, $ThisFileInfo['id3v2']['majorversion'])) { $ThisFileInfo['id3v2']["{$frame_name}"]['data'] = substr($framedata, 0, $frame_size); $ThisFileInfo['id3v2']["{$frame_name}"]['datalength'] = CastAsInt($frame_size); $ThisFileInfo['id3v2']["{$frame_name}"]['dataoffset'] = $framedataoffset; $framedata = substr($framedata, $frame_size); // in getid3.frames.php - this function does all the FrameID-level parsing ID3v2FrameProcessing($frame_name, $frame_flags, $ThisFileInfo); } else { // invalid frame length or FrameID if ($frame_size <= strlen($framedata)) { if (IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $ThisFileInfo['id3v2']['majorversion'])) { // next frame is valid, just skip the current frame $framedata = substr($framedata, $frame_size); $InvalidFrameMessageType = 'warning'; $InvalidFrameMessageText = ' Next frame is valid, skipping current frame.'; } else { // next frame is invalid too, abort processing unset($framedata); $InvalidFrameMessageType = 'error'; $InvalidFrameMessageText = ' Next frame is also invalid, aborting processing.'; } } elseif ($frame_size == strlen($framedata)) { // this is the last frame, just skip $InvalidFrameMessageType = 'warning'; $InvalidFrameMessageText = ' This was the last frame.'; } else { // next frame is invalid too, abort processing unset($framedata); $InvalidFrameMessageType = 'error'; $InvalidFrameMessageText = ' Invalid frame size, aborting.'; } if (!IsValidID3v2FrameName($frame_name, $ThisFileInfo['id3v2']['majorversion'])) { switch ($frame_name) { case chr(0) . chr(0) . 'MP': case chr(0) . 'MP3': case ' MP3': case 'MP3e': case chr(0) . 'MP': case ' MP': case 'MP3': $InvalidFrameMessageType = 'warning'; $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $ThisFileInfo['id3v2']['majorversion'] . '))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; break; default: $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $ThisFileInfo['id3v2']['majorversion'] . '))).'; break; } } elseif ($frame_size > strlen($framedata)) { $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: $frame_size (' . $frame_size . ') > strlen($framedata) (' . strlen($framedata) . ')).'; } else { $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag).'; } $ThisFileInfo["{$InvalidFrameMessageType}"] .= $InvalidFrameMessageText; } $framedataoffset += $frame_size + ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion']); } } // Footer // The footer is a copy of the header, but with a different identifier. // ID3v2 identifier "3DI" // ID3v2 version $04 00 // ID3v2 flags %abcd0000 // ID3v2 size 4 * %0xxxxxxx if (isset($ThisFileInfo['id3v2']['flags']['isfooter']) && $ThisFileInfo['id3v2']['flags']['isfooter']) { $footer = fread($fd, 10); if (substr($footer, 0, 3) == '3DI') { $ThisFileInfo['id3v2']['footer'] = true; $ThisFileInfo['id3v2']['majorversion_footer'] = ord(substr($footer, 3, 1)); $ThisFileInfo['id3v2']['minorversion_footer'] = ord(substr($footer, 4, 1)); } if ($ThisFileInfo['id3v2']['majorversion_footer'] <= 4) { $id3_flags = BigEndian2Bin(substr($footer, 5, 1)); $ThisFileInfo['id3v2']['flags']['unsynch_footer'] = substr($id3_flags, 0, 1); $ThisFileInfo['id3v2']['flags']['extfoot_footer'] = substr($id3_flags, 1, 1); $ThisFileInfo['id3v2']['flags']['experim_footer'] = substr($id3_flags, 2, 1); $ThisFileInfo['id3v2']['flags']['isfooter_footer'] = substr($id3_flags, 3, 1); $ThisFileInfo['id3v2']['footerlength'] = BigEndian2Int(substr($footer, 6, 4), 1); } } // end footer if (isset($ThisFileInfo['id3v2']['comments']['genre'])) { foreach ($ThisFileInfo['id3v2']['comments']['genre'] as $key => $value) { unset($ThisFileInfo['id3v2']['comments']['genre'][$key]); $ThisFileInfo['id3v2']['comments'] = array_merge_noclobber($ThisFileInfo['id3v2']['comments'], ParseID3v2GenreString($value)); } } if (isset($ThisFileInfo['id3v2']['comments']['track'])) { foreach ($ThisFileInfo['id3v2']['comments']['track'] as $key => $value) { if (strstr($value, '/')) { list($ThisFileInfo['id3v2']['comments']['track'][$key], $ThisFileInfo['id3v2']['comments']['totaltracks'][$key]) = explode('/', $ThisFileInfo['id3v2']['comments']['track'][$key]); } // Convert track number to integer (ID3v2 track numbers could be returned as a // string ('03' for example) - this will ensure all track numbers are integers $ThisFileInfo['id3v2']['comments']['track'][$key] = intval($ThisFileInfo['id3v2']['comments']['track'][$key]); } } return true; }
function getRIFFHeaderFilepointer(&$fd, &$MP3fileInfo) { $offset = 0; rewind($fd); $MP3fileInfo['RIFF'] = ParseRIFF($fd, $offset, $MP3fileInfo['filesize']); $streamindex = 0; if (!is_array($MP3fileInfo['RIFF'])) { $MP3fileInfo['error'] .= "\n" . 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?)'; unset($MP3fileInfo['RIFF']); unset($MP3fileInfo['fileformat']); return FALSE; } $arraykeys = array_keys($MP3fileInfo['RIFF']); switch ($arraykeys[0]) { case 'WAVE': $MP3fileInfo['fileformat'] = 'wav'; if (isset($MP3fileInfo['RIFF']['WAVE']['fmt '][0]['data'])) { $fmtData = $MP3fileInfo['RIFF']['WAVE']['fmt '][0]['data']; $MP3fileInfo['RIFF']['raw']['fmt ']['wFormatTag'] = LittleEndian2Int(substr($fmtData, 0, 2)); $MP3fileInfo['RIFF']['raw']['fmt ']['nChannels'] = LittleEndian2Int(substr($fmtData, 2, 2)); $MP3fileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec'] = LittleEndian2Int(substr($fmtData, 4, 4)); $MP3fileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec'] = LittleEndian2Int(substr($fmtData, 8, 4)); $MP3fileInfo['RIFF']['raw']['fmt ']['nBlockAlign'] = LittleEndian2Int(substr($fmtData, 12, 2)); $MP3fileInfo['RIFF']['raw']['fmt ']['nBitsPerSample'] = LittleEndian2Int(substr($fmtData, 14, 2)); $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['format'] = RIFFwFormatTagLookup($MP3fileInfo['RIFF']['raw']['fmt ']['wFormatTag']); $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['channels'] = $MP3fileInfo['RIFF']['raw']['fmt ']['nChannels']; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['channelmode'] = $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['channels'] == 1 ? 'mono' : 'stereo'; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['frequency'] = $MP3fileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['bitrate'] = $MP3fileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec'] * 8; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['bitspersample'] = $MP3fileInfo['RIFF']['raw']['fmt ']['nBitsPerSample']; } if (isset($MP3fileInfo['RIFF']['WAVE']['rgad'][0]['data'])) { $rgadData = $MP3fileInfo['RIFF']['WAVE']['rgad'][0]['data']; $MP3fileInfo['RIFF']['raw']['rgad']['fPeakAmplitude'] = LittleEndian2Float(substr($rgadData, 0, 4)); $MP3fileInfo['RIFF']['raw']['rgad']['nRadioRgAdjust'] = LittleEndian2Int(substr($rgadData, 4, 2)); $MP3fileInfo['RIFF']['raw']['rgad']['nAudiophileRgAdjust'] = LittleEndian2Int(substr($rgadData, 6, 2)); $nRadioRgAdjustBitstring = str_pad(Dec2Bin($MP3fileInfo['RIFF']['raw']['rgad']['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); $nAudiophileRgAdjustBitstring = str_pad(Dec2Bin($MP3fileInfo['RIFF']['raw']['rgad']['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); $MP3fileInfo['RIFF']['raw']['rgad']['radio']['name'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); $MP3fileInfo['RIFF']['raw']['rgad']['radio']['originator'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); $MP3fileInfo['RIFF']['raw']['rgad']['radio']['signbit'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); $MP3fileInfo['RIFF']['raw']['rgad']['radio']['adjustment'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); $MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['name'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); $MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['originator'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); $MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['signbit'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); $MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['adjustment'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9)); $MP3fileInfo['RIFF']['rgad']['peakamplitude'] = $MP3fileInfo['RIFF']['raw']['rgad']['fPeakAmplitude']; if ($MP3fileInfo['RIFF']['raw']['rgad']['radio']['name'] != 0 && $MP3fileInfo['RIFF']['raw']['rgad']['radio']['originator'] != 0) { $MP3fileInfo['RIFF']['rgad']['radio']['name'] = RGADnameLookup($MP3fileInfo['RIFF']['raw']['rgad']['radio']['name']); $MP3fileInfo['RIFF']['rgad']['radio']['originator'] = RGADoriginatorLookup($MP3fileInfo['RIFF']['raw']['rgad']['radio']['originator']); $MP3fileInfo['RIFF']['rgad']['radio']['adjustment'] = RGADadjustmentLookup($MP3fileInfo['RIFF']['raw']['rgad']['radio']['adjustment'], $MP3fileInfo['RIFF']['raw']['rgad']['radio']['signbit']); } if ($MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['name'] != 0 && $MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['originator'] != 0) { $MP3fileInfo['RIFF']['rgad']['audiophile']['name'] = RGADnameLookup($MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['name']); $MP3fileInfo['RIFF']['rgad']['audiophile']['originator'] = RGADoriginatorLookup($MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['originator']); $MP3fileInfo['RIFF']['rgad']['audiophile']['adjustment'] = RGADadjustmentLookup($MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['adjustment'], $MP3fileInfo['RIFF']['raw']['rgad']['audiophile']['signbit']); } } if (isset($MP3fileInfo['RIFF']['WAVE']['fact'][0]['data'])) { $MP3fileInfo['RIFF']['raw']['fact']['NumberOfSamples'] = LittleEndian2Int(substr($MP3fileInfo['RIFF']['WAVE']['fact'][0]['data'], 0, 4)); if (isset($MP3fileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']) && $MP3fileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']) { $MP3fileInfo['playtime_seconds'] = (double) $MP3fileInfo['RIFF']['raw']['fact']['NumberOfSamples'] / $MP3fileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']; } if (isset($MP3fileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec']) && $MP3fileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec']) { $MP3fileInfo['audiobytes'] = CastAsInt(round($MP3fileInfo['playtime_seconds'] * $MP3fileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec'])); $MP3fileInfo['bitrate'] = CastAsInt($MP3fileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec'] * 8); } } if (!isset($MP3fileInfo['audiobytes']) && isset($MP3fileInfo['RIFF']['WAVE']['data'][0]['size'])) { $MP3fileInfo['audiobytes'] = $MP3fileInfo['RIFF']['WAVE']['data'][0]['size']; } if (!isset($MP3fileInfo['bitrate']) && isset($MP3fileInfo['RIFF']['audio']["{$streamindex}"]['bitrate']) && isset($MP3fileInfo['audiobytes'])) { $MP3fileInfo['bitrate'] = $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['bitrate']; $MP3fileInfo['playtime_seconds'] = (double) ($MP3fileInfo['audiobytes'] * 8 / $MP3fileInfo['bitrate']); } break; case 'AVI ': $MP3fileInfo['fileformat'] = 'avi'; if (isset($MP3fileInfo['RIFF']['AVI ']['hdrl']['avih']["{$streamindex}"]['data'])) { $avihData = $MP3fileInfo['RIFF']['AVI ']['hdrl']['avih']["{$streamindex}"]['data']; $MP3fileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] = LittleEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L) $MP3fileInfo['RIFF']['raw']['avih']['dwMaxBytesPerSec'] = LittleEndian2Int(substr($avihData, 4, 4)); // max. transfer rate $MP3fileInfo['RIFF']['raw']['avih']['dwPaddingGranularity'] = LittleEndian2Int(substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. $MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] = LittleEndian2Int(substr($avihData, 12, 4)); // the ever-present flags $MP3fileInfo['RIFF']['raw']['avih']['dwTotalFrames'] = LittleEndian2Int(substr($avihData, 16, 4)); // # frames in file $MP3fileInfo['RIFF']['raw']['avih']['dwInitialFrames'] = LittleEndian2Int(substr($avihData, 20, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwStreams'] = LittleEndian2Int(substr($avihData, 24, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwSuggestedBufferSize'] = LittleEndian2Int(substr($avihData, 28, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwWidth'] = LittleEndian2Int(substr($avihData, 32, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwHeight'] = LittleEndian2Int(substr($avihData, 36, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwScale'] = LittleEndian2Int(substr($avihData, 40, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwRate'] = LittleEndian2Int(substr($avihData, 44, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwStart'] = LittleEndian2Int(substr($avihData, 48, 4)); $MP3fileInfo['RIFF']['raw']['avih']['dwLength'] = LittleEndian2Int(substr($avihData, 52, 4)); $MP3fileInfo['RIFF']['raw']['avih']['flags']['hasindex'] = (bool) ($MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x10); $MP3fileInfo['RIFF']['raw']['avih']['flags']['mustuseindex'] = (bool) ($MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x20); $MP3fileInfo['RIFF']['raw']['avih']['flags']['interleaved'] = (bool) ($MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x100); $MP3fileInfo['RIFF']['raw']['avih']['flags']['trustcktype'] = (bool) ($MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x800); $MP3fileInfo['RIFF']['raw']['avih']['flags']['capturedfile'] = (bool) ($MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x10000); $MP3fileInfo['RIFF']['raw']['avih']['flags']['copyrighted'] = (bool) ($MP3fileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x20010); $MP3fileInfo['RIFF']['video']["{$streamindex}"]['frame_width'] = $MP3fileInfo['RIFF']['raw']['avih']['dwWidth']; $MP3fileInfo['RIFF']['video']["{$streamindex}"]['frame_height'] = $MP3fileInfo['RIFF']['raw']['avih']['dwHeight']; $MP3fileInfo['RIFF']['video']["{$streamindex}"]['frame_rate'] = round(1000000 / $MP3fileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'], 3); $MP3fileInfo['playtime_seconds'] = $MP3fileInfo['RIFF']['raw']['avih']['dwTotalFrames'] * ($MP3fileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] / 1000000); $MP3fileInfo['bitrate'] = $MP3fileInfo['filesize'] / $MP3fileInfo['playtime_seconds'] * 8; } if (isset($MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][0]['data'])) { if (is_array($MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'])) { for ($i = 0; $i < count($MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strh']); $i++) { if (isset($MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strh']["{$i}"]['data'])) { $strhData = $MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strh']["{$i}"]['data']; $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['fccType'] = substr($strhData, 0, 4); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['fccHandler'] = substr($strhData, 4, 4); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwFlags'] = LittleEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['wPriority'] = LittleEndian2Int(substr($strhData, 12, 2)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['wLanguage'] = LittleEndian2Int(substr($strhData, 14, 2)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwInitialFrames'] = LittleEndian2Int(substr($strhData, 16, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwScale'] = LittleEndian2Int(substr($strhData, 20, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwRate'] = LittleEndian2Int(substr($strhData, 24, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwStart'] = LittleEndian2Int(substr($strhData, 28, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwLength'] = LittleEndian2Int(substr($strhData, 32, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwSuggestedBufferSize'] = LittleEndian2Int(substr($strhData, 36, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwQuality'] = LittleEndian2Int(substr($strhData, 40, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['dwSampleSize'] = LittleEndian2Int(substr($strhData, 44, 4)); $MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['rcFrame'] = LittleEndian2Int(substr($strhData, 48, 4)); if (isset($MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strf']["{$i}"]['data'])) { $strfData = $MP3fileInfo['RIFF']['AVI ']['hdrl']['strl']['strf']["{$i}"]['data']; switch ($MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['fccType']) { case 'auds': if (isset($MP3fileInfo['RIFF']['audio']) && is_array($MP3fileInfo['RIFF']['audio'])) { $streamindex = count($MP3fileInfo['RIFF']['audio']); } $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['wFormatTag'] = LittleEndian2Int(substr($strfData, 0, 2)); $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nChannels'] = LittleEndian2Int(substr($strfData, 2, 2)); $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nSamplesPerSec'] = LittleEndian2Int(substr($strfData, 4, 4)); $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nAvgBytesPerSec'] = LittleEndian2Int(substr($strfData, 8, 4)); $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nBlockAlign'] = LittleEndian2Int(substr($strfData, 12, 2)); $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nBitsPerSample'] = LittleEndian2Int(substr($strfData, 14, 2)); $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['format'] = RIFFwFormatTagLookup($MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['wFormatTag']); $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['channels'] = $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nChannels']; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['channelmode'] = $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['channels'] == 1 ? 'mono' : 'stereo'; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['frequency'] = $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nSamplesPerSec']; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['bitrate'] = $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nAvgBytesPerSec'] * 8; $MP3fileInfo['RIFF']['audio']["{$streamindex}"]['bitspersample'] = $MP3fileInfo['RIFF']['raw']['strf']['auds']["{$streamindex}"]['nBitsPerSample']; break; case 'vids': $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biSize'] = LittleEndian2Int(substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biWidth'] = LittleEndian2Int(substr($strfData, 4, 4)); // width of the bitmap in pixels $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biHeight'] = LittleEndian2Int(substr($strfData, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a "bottom-up" DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a "top-down" DIB and its origin is the upper left corner $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biPlanes'] = LittleEndian2Int(substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biBitCount'] = LittleEndian2Int(substr($strfData, 14, 2)); // Specifies the number of bits per pixels $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['fourcc'] = substr($strfData, 16, 4); // $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biSizeImage'] = LittleEndian2Int(substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biXPelsPerMeter'] = LittleEndian2Int(substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biYPelsPerMeter'] = LittleEndian2Int(substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biClrUsed'] = LittleEndian2Int(substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression $MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['biClrImportant'] = LittleEndian2Int(substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important $MP3fileInfo['RIFF']['video']["{$streamindex}"]['codec'] = RIFFfourccLookup($MP3fileInfo['RIFF']['raw']['strh']["{$i}"]['fccHandler']); if (!$MP3fileInfo['RIFF']['video']["{$streamindex}"]['codec'] && RIFFfourccLookup($MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['fourcc'])) { RIFFfourccLookup($MP3fileInfo['RIFF']['raw']['strf']['vids']["{$streamindex}"]['fourcc']); } break; } } } } } } break; default: unset($MP3fileInfo['fileformat']); break; } if (isset($MP3fileInfo['RIFF']['WAVE']['INFO']) && is_array($MP3fileInfo['RIFF']['WAVE']['INFO'])) { $MP3fileInfo['RIFF']['title'] = trim(substr($MP3fileInfo['RIFF']['WAVE']['INFO']['DISP'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['DISP']) - 1]['data'], 4)); $MP3fileInfo['RIFF']['artist'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['IART'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['IART']) - 1]['data']); $MP3fileInfo['RIFF']['genre'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['IGNR'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['IGNR']) - 1]['data']); $MP3fileInfo['RIFF']['comment'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['ICMT'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['ICMT']) - 1]['data']); $MP3fileInfo['RIFF']['copyright'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['ICOP'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['ICOP']) - 1]['data']); $MP3fileInfo['RIFF']['engineers'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['IENG'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['IENG']) - 1]['data']); $MP3fileInfo['RIFF']['keywords'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['IKEY'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['IKEY']) - 1]['data']); $MP3fileInfo['RIFF']['originalmedium'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['IMED'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['IMED']) - 1]['data']); $MP3fileInfo['RIFF']['name'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['INAM'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['INAM']) - 1]['data']); $MP3fileInfo['RIFF']['sourcesupplier'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['ISRC'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['ISRC']) - 1]['data']); $MP3fileInfo['RIFF']['digitizer'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['ITCH'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['ITCH']) - 1]['data']); $MP3fileInfo['RIFF']['subject'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['ISBJ'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['ISBJ']) - 1]['data']); $MP3fileInfo['RIFF']['digitizationsource'] = trim($MP3fileInfo['RIFF']['WAVE']['INFO']['ISRF'][count($MP3fileInfo['RIFF']['WAVE']['INFO']['ISRF']) - 1]['data']); } foreach ($MP3fileInfo['RIFF'] as $key => $value) { if (!is_array($value) && !$value) { unset($MP3fileInfo['RIFF']["{$key}"]); } } return TRUE; }
function getRIFFHeaderFilepointer(&$fd, &$ThisFileInfo) { fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $RIFFheader = fread($fd, 12); switch (substr($RIFFheader, 0, 4)) { case 'RIFF': case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) $ThisFileInfo['fileformat'] = 'riff'; $ThisFileInfo['RIFF'][substr($RIFFheader, 8, 4)] = ParseRIFF($fd, $ThisFileInfo['avdataoffset'] + 12, $ThisFileInfo['avdataoffset'] + LittleEndian2Int(substr($RIFFheader, 4, 4)), $ThisFileInfo); break; default: $ThisFileInfo['error'] .= "\n" . 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?)'; unset($ThisFileInfo['fileformat']); return false; break; } $streamindex = 0; $arraykeys = array_keys($ThisFileInfo['RIFF']); switch ($arraykeys[0]) { case 'WAVE': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = 'wav'; if (isset($ThisFileInfo['RIFF']['WAVE']['fmt '][0]['data'])) { $ThisFileInfo['RIFF']['audio'][$streamindex] = RIFFparseWAVEFORMATex($ThisFileInfo['RIFF']['WAVE']['fmt '][0]['data']); if ($ThisFileInfo['RIFF']['audio'][$streamindex] == 0) { $ThisFileInfo['error'] .= 'Corrupt RIFF file: bitrate_audio == zero'; return false; } $ThisFileInfo['RIFF']['raw']['fmt '] = $ThisFileInfo['RIFF']['audio'][$streamindex]['raw']; unset($ThisFileInfo['RIFF']['audio'][$streamindex]['raw']); $ThisFileInfo['audio'] = array_merge_noclobber($ThisFileInfo['audio'], $ThisFileInfo['RIFF']['audio'][$streamindex]); $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['RIFF']['audio'][$streamindex]['bitrate']; $ThisFileInfo['playtime_seconds'] = (double) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']); if (isset($ThisFileInfo['RIFF']['WAVE']['data'][0]['offset']) && isset($ThisFileInfo['RIFF']['raw']['fmt ']['wFormatTag'])) { switch ($ThisFileInfo['RIFF']['raw']['fmt ']['wFormatTag']) { case 85: // LAME ACM require_once GETID3_INCLUDEPATH . 'getid3.mp3.php'; getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['RIFF']['WAVE']['data'][0]['offset'], false); $ThisFileInfo['audio']['dataformat'] = 'mp3'; if (isset($ThisFileInfo['mpeg']['audio'])) { $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'] * 1000; $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitratemode']); } break; default: // do nothing break; } } } if (isset($ThisFileInfo['RIFF']['WAVE']['rgad'][0]['data'])) { require_once GETID3_INCLUDEPATH . 'getid3.rgad.php'; $rgadData = $ThisFileInfo['RIFF']['WAVE']['rgad'][0]['data']; $ThisFileInfo['RIFF']['raw']['rgad']['fPeakAmplitude'] = LittleEndian2Float(substr($rgadData, 0, 4)); $ThisFileInfo['RIFF']['raw']['rgad']['nRadioRgAdjust'] = LittleEndian2Int(substr($rgadData, 4, 2)); $ThisFileInfo['RIFF']['raw']['rgad']['nAudiophileRgAdjust'] = LittleEndian2Int(substr($rgadData, 6, 2)); $nRadioRgAdjustBitstring = str_pad(Dec2Bin($ThisFileInfo['RIFF']['raw']['rgad']['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); $nAudiophileRgAdjustBitstring = str_pad(Dec2Bin($ThisFileInfo['RIFF']['raw']['rgad']['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['name'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['originator'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['signbit'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); $ThisFileInfo['RIFF']['raw']['rgad']['radio']['adjustment'] = Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['name'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['originator'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['signbit'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['adjustment'] = Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9)); $ThisFileInfo['RIFF']['rgad']['peakamplitude'] = $ThisFileInfo['RIFF']['raw']['rgad']['fPeakAmplitude']; if ($ThisFileInfo['RIFF']['raw']['rgad']['radio']['name'] != 0 && $ThisFileInfo['RIFF']['raw']['rgad']['radio']['originator'] != 0) { $ThisFileInfo['RIFF']['rgad']['radio']['name'] = RGADnameLookup($ThisFileInfo['RIFF']['raw']['rgad']['radio']['name']); $ThisFileInfo['RIFF']['rgad']['radio']['originator'] = RGADoriginatorLookup($ThisFileInfo['RIFF']['raw']['rgad']['radio']['originator']); $ThisFileInfo['RIFF']['rgad']['radio']['adjustment'] = RGADadjustmentLookup($ThisFileInfo['RIFF']['raw']['rgad']['radio']['adjustment'], $ThisFileInfo['RIFF']['raw']['rgad']['radio']['signbit']); } if ($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['name'] != 0 && $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['originator'] != 0) { $ThisFileInfo['RIFF']['rgad']['audiophile']['name'] = RGADnameLookup($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['name']); $ThisFileInfo['RIFF']['rgad']['audiophile']['originator'] = RGADoriginatorLookup($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['originator']); $ThisFileInfo['RIFF']['rgad']['audiophile']['adjustment'] = RGADadjustmentLookup($ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['adjustment'], $ThisFileInfo['RIFF']['raw']['rgad']['audiophile']['signbit']); } } if (isset($ThisFileInfo['RIFF']['WAVE']['fact'][0]['data'])) { $ThisFileInfo['RIFF']['raw']['fact']['NumberOfSamples'] = LittleEndian2Int(substr($ThisFileInfo['RIFF']['WAVE']['fact'][0]['data'], 0, 4)); if (isset($ThisFileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']) && $ThisFileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec'] > 0) { $ThisFileInfo['playtime_seconds'] = (double) $ThisFileInfo['RIFF']['raw']['fact']['NumberOfSamples'] / $ThisFileInfo['RIFF']['raw']['fmt ']['nSamplesPerSec']; } if (isset($ThisFileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec']) && $ThisFileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec']) { $ThisFileInfo['audio']['bitrate'] = CastAsInt($ThisFileInfo['RIFF']['raw']['fmt ']['nAvgBytesPerSec'] * 8); } } if (!isset($ThisFileInfo['audio']['bitrate']) && isset($ThisFileInfo['RIFF']['audio'][$streamindex]['bitrate'])) { $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['RIFF']['audio'][$streamindex]['bitrate']; $ThisFileInfo['playtime_seconds'] = (double) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']); } break; case 'AVI ': $ThisFileInfo['video']['bitrate_mode'] = 'cbr'; $ThisFileInfo['video']['dataformat'] = 'avi'; $ThisFileInfo['mime_type'] = 'video/avi'; if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['avih'][$streamindex]['data'])) { $avihData = $ThisFileInfo['RIFF']['AVI ']['hdrl']['avih'][$streamindex]['data']; $ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] = LittleEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L) if ($ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] == 0) { $ThisFileInfo['error'] .= 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; return false; } $ThisFileInfo['RIFF']['raw']['avih']['dwMaxBytesPerSec'] = LittleEndian2Int(substr($avihData, 4, 4)); // max. transfer rate $ThisFileInfo['RIFF']['raw']['avih']['dwPaddingGranularity'] = LittleEndian2Int(substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. $ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] = LittleEndian2Int(substr($avihData, 12, 4)); // the ever-present flags $ThisFileInfo['RIFF']['raw']['avih']['dwTotalFrames'] = LittleEndian2Int(substr($avihData, 16, 4)); // # frames in file $ThisFileInfo['RIFF']['raw']['avih']['dwInitialFrames'] = LittleEndian2Int(substr($avihData, 20, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwStreams'] = LittleEndian2Int(substr($avihData, 24, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwSuggestedBufferSize'] = LittleEndian2Int(substr($avihData, 28, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwWidth'] = LittleEndian2Int(substr($avihData, 32, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwHeight'] = LittleEndian2Int(substr($avihData, 36, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwScale'] = LittleEndian2Int(substr($avihData, 40, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwRate'] = LittleEndian2Int(substr($avihData, 44, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwStart'] = LittleEndian2Int(substr($avihData, 48, 4)); $ThisFileInfo['RIFF']['raw']['avih']['dwLength'] = LittleEndian2Int(substr($avihData, 52, 4)); $ThisFileInfo['RIFF']['raw']['avih']['flags']['hasindex'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x10); $ThisFileInfo['RIFF']['raw']['avih']['flags']['mustuseindex'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x20); $ThisFileInfo['RIFF']['raw']['avih']['flags']['interleaved'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x100); $ThisFileInfo['RIFF']['raw']['avih']['flags']['trustcktype'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x800); $ThisFileInfo['RIFF']['raw']['avih']['flags']['capturedfile'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x10000); $ThisFileInfo['RIFF']['raw']['avih']['flags']['copyrighted'] = (bool) ($ThisFileInfo['RIFF']['raw']['avih']['dwFlags'] & 0x20010); $ThisFileInfo['RIFF']['video'][$streamindex]['frame_width'] = $ThisFileInfo['RIFF']['raw']['avih']['dwWidth']; $ThisFileInfo['RIFF']['video'][$streamindex]['frame_height'] = $ThisFileInfo['RIFF']['raw']['avih']['dwHeight']; $ThisFileInfo['RIFF']['video'][$streamindex]['frame_rate'] = round(1000000 / $ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'], 3); if (!isset($ThisFileInfo['video']['resolution_x'])) { $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['RIFF']['video'][$streamindex]['frame_width']; } if (!isset($ThisFileInfo['video']['resolution_y'])) { $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['RIFF']['video'][$streamindex]['frame_height']; } $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['RIFF']['video'][$streamindex]['frame_rate']; } if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][0]['data'])) { if (is_array($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'])) { for ($i = 0; $i < count($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh']); $i++) { if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][$i]['data'])) { $strhData = $ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strh'][$i]['data']; $strhfccType = substr($strhData, 0, 4); if (isset($ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strf'][$i]['data'])) { $strfData = $ThisFileInfo['RIFF']['AVI ']['hdrl']['strl']['strf'][$i]['data']; switch ($strhfccType) { case 'auds': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = 'wav'; if (isset($ThisFileInfo['RIFF']['audio']) && is_array($ThisFileInfo['RIFF']['audio'])) { $streamindex = count($ThisFileInfo['RIFF']['audio']); } $ThisFileInfo['RIFF']['audio'][$streamindex] = RIFFparseWAVEFORMATex($strfData); $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex] = $ThisFileInfo['RIFF']['audio'][$streamindex]['raw']; unset($ThisFileInfo['RIFF']['audio'][$streamindex]['raw']); $ThisFileInfo['audio'] = array_merge_noclobber($ThisFileInfo['audio'], $ThisFileInfo['RIFF']['audio'][$streamindex]); switch ($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['wFormatTag']) { case 85: $ThisFileInfo['audio']['dataformat'] = 'mp3'; break; case 8192: $ThisFileInfo['audio']['dataformat'] = 'ac3'; break; default: $ThisFileInfo['audio']['dataformat'] = 'wav'; break; } break; case 'iavs': case 'vids': $ThisFileInfo['RIFF']['raw']['strh'][$i]['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; $ThisFileInfo['RIFF']['raw']['strh'][$i]['fccHandler'] = substr($strhData, 4, 4); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwFlags'] = LittleEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags $ThisFileInfo['RIFF']['raw']['strh'][$i]['wPriority'] = LittleEndian2Int(substr($strhData, 12, 2)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['wLanguage'] = LittleEndian2Int(substr($strhData, 14, 2)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwInitialFrames'] = LittleEndian2Int(substr($strhData, 16, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwScale'] = LittleEndian2Int(substr($strhData, 20, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwRate'] = LittleEndian2Int(substr($strhData, 24, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwStart'] = LittleEndian2Int(substr($strhData, 28, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwLength'] = LittleEndian2Int(substr($strhData, 32, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwSuggestedBufferSize'] = LittleEndian2Int(substr($strhData, 36, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwQuality'] = LittleEndian2Int(substr($strhData, 40, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['dwSampleSize'] = LittleEndian2Int(substr($strhData, 44, 4)); $ThisFileInfo['RIFF']['raw']['strh'][$i]['rcFrame'] = LittleEndian2Int(substr($strhData, 48, 4)); $ThisFileInfo['RIFF']['video'][$streamindex]['codec'] = RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strh'][$i]['fccHandler']); if (!$ThisFileInfo['RIFF']['video'][$streamindex]['codec'] && isset($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']) && RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc'])) { $ThisFileInfo['RIFF']['video'][$streamindex]['codec'] = RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']); } $ThisFileInfo['video']['codec'] = $ThisFileInfo['RIFF']['video'][$streamindex]['codec']; switch ($strhfccType) { case 'vids': $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biSize'] = LittleEndian2Int(substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biWidth'] = LittleEndian2Int(substr($strfData, 4, 4)); // width of the bitmap in pixels $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biHeight'] = LittleEndian2Int(substr($strfData, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biPlanes'] = LittleEndian2Int(substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biBitCount'] = LittleEndian2Int(substr($strfData, 14, 2)); // Specifies the number of bits per pixels $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc'] = substr($strfData, 16, 4); // $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biSizeImage'] = LittleEndian2Int(substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biXPelsPerMeter'] = LittleEndian2Int(substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biYPelsPerMeter'] = LittleEndian2Int(substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biClrUsed'] = LittleEndian2Int(substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression $ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['biClrImportant'] = LittleEndian2Int(substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important if ($ThisFileInfo['RIFF']['video'][$streamindex]['codec'] == 'DV') { $ThisFileInfo['RIFF']['video'][$streamindex]['dv_type'] = 2; } break; case 'iavs': $ThisFileInfo['RIFF']['video'][$streamindex]['dv_type'] = 1; break; } break; default: $ThisFileInfo['warning'] .= "\n" . 'Unhandled fccType for stream (' . $i . '): "' . $strhfccType . '"'; break; } } } if (isset($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']) && RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc'])) { $ThisFileInfo['RIFF']['video'][$streamindex]['codec'] = RIFFfourccLookup($ThisFileInfo['RIFF']['raw']['strf']["{$strhfccType}"][$streamindex]['fourcc']); $ThisFileInfo['video']['codec'] = $ThisFileInfo['RIFF']['video'][$streamindex]['codec']; } } } } break; case 'CDDA': $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['dataformat'] = 'cda'; unset($ThisFileInfo['mime_type']); if (isset($ThisFileInfo['RIFF']['CDDA']['fmt '][0]['data'])) { $fmtData = $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['data']; $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['unknown1'] = LittleEndian2Int(substr($fmtData, 0, 2)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['track_num'] = LittleEndian2Int(substr($fmtData, 2, 2)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['disc_id'] = LittleEndian2Int(substr($fmtData, 4, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['start_offset_frame'] = LittleEndian2Int(substr($fmtData, 8, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_frames'] = LittleEndian2Int(substr($fmtData, 12, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['unknown6'] = LittleEndian2Int(substr($fmtData, 16, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['unknown7'] = LittleEndian2Int(substr($fmtData, 20, 4)); $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['start_offset_seconds'] = (double) $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['start_offset_frame'] / 75; $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_seconds'] = (double) $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_frames'] / 75; $ThisFileInfo['comments']['track'] = $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['track_num']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['RIFF']['CDDA']['fmt '][0]['playtime_seconds']; // hardcoded data for CD-audio $ThisFileInfo['audio']['sample_rate'] = 44100; $ThisFileInfo['audio']['channels'] = 2; $ThisFileInfo['audio']['bits_per_sample'] = 16; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['audio']['sample_rate'] * $ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['bits_per_sample']; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; } break; default: unset($ThisFileInfo['fileformat']); break; } if (isset($ThisFileInfo['RIFF']['WAVE']['DISP']) && is_array($ThisFileInfo['RIFF']['WAVE']['DISP'])) { $ThisFileInfo['tags'][] = 'riff'; $ThisFileInfo['RIFF']['comments']['title'][] = trim(substr($ThisFileInfo['RIFF']['WAVE']['DISP'][count($ThisFileInfo['RIFF']['WAVE']['DISP']) - 1]['data'], 4)); } if (isset($ThisFileInfo['RIFF']['WAVE']['INFO']) && is_array($ThisFileInfo['RIFF']['WAVE']['INFO'])) { $ThisFileInfo['tags'][] = 'riff'; $RIFFinfoKeyLookup = array('IART' => 'artist', 'IGNR' => 'genre', 'ICMT' => 'comment', 'ICOP' => 'copyright', 'IENG' => 'engineers', 'IKEY' => 'keywords', 'IMED' => 'orignalmedium', 'INAM' => 'name', 'ISRC' => 'sourcesupplier', 'ITCH' => 'digitizer', 'ISBJ' => 'subject', 'ISRF' => 'digitizationsource'); foreach ($RIFFinfoKeyLookup as $key => $value) { foreach ($ThisFileInfo['RIFF']['WAVE']['INFO']["{$key}"] as $commentid => $commentdata) { if (trim($commentdata['data']) != '') { $ThisFileInfo['RIFF']['comments']["{$value}"][] = trim($commentdata['data']); } } } } if (!empty($ThisFileInfo['RIFF']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['RIFF']['comments'], $ThisFileInfo, true, true, true); } if (!isset($ThisFileInfo['playtime_seconds'])) { $ThisFileInfo['playtime_seconds'] = 0; } if (isset($ThisFileInfo['RIFF']['raw']['avih']['dwTotalFrames']) && isset($ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'])) { $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['RIFF']['raw']['avih']['dwTotalFrames'] * ($ThisFileInfo['RIFF']['raw']['avih']['dwMicroSecPerFrame'] / 1000000); } if ($ThisFileInfo['playtime_seconds'] > 0) { if (isset($ThisFileInfo['RIFF']['audio']) && isset($ThisFileInfo['RIFF']['video'])) { if (!isset($ThisFileInfo['bitrate'])) { $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; } } elseif (isset($ThisFileInfo['RIFF']['audio']) && !isset($ThisFileInfo['RIFF']['video'])) { if (!isset($ThisFileInfo['audio']['bitrate'])) { $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; } } elseif (!isset($ThisFileInfo['RIFF']['audio']) && isset($ThisFileInfo['RIFF']['video'])) { if (!isset($ThisFileInfo['video']['bitrate'])) { $ThisFileInfo['video']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; } } } if (isset($ThisFileInfo['RIFF']['video']) && isset($ThisFileInfo['audio']['bitrate']) && $ThisFileInfo['audio']['bitrate'] > 0 && $ThisFileInfo['playtime_seconds'] > 0) { $ThisFileInfo['audio']['bitrate'] = 0; $ThisFileInfo['video']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'] * 8; foreach ($ThisFileInfo['RIFF']['audio'] as $channelnumber => $audioinfoarray) { $ThisFileInfo['video']['bitrate'] -= $audioinfoarray['bitrate']; $ThisFileInfo['audio']['bitrate'] += $audioinfoarray['bitrate']; } if ($ThisFileInfo['video']['bitrate'] <= 0) { unset($ThisFileInfo['video']['bitrate']); } if ($ThisFileInfo['audio']['bitrate'] <= 0) { unset($ThisFileInfo['audio']['bitrate']); } } if (!empty($ThisFileInfo['RIFF']['raw']['fmt ']['nBitsPerSample'])) { $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['RIFF']['raw']['fmt ']['nBitsPerSample']; } // Skip RIFF header $ThisFileInfo['avdataoffset'] += 44; return true; }
function getID3v2Filepointer($fd, &$MP3fileInfo) { // Overall tag structure: // +-----------------------------+ // | Header (10 bytes) | // +-----------------------------+ // | Extended Header | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Frames (variable length) | // +-----------------------------+ // | Padding | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Footer (10 bytes, OPTIONAL) | // +-----------------------------+ // Header // ID3v2/file identifier "ID3" // ID3v2 version $04 00 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) // ID3v2 size 4 * %0xxxxxxx rewind($fd); $header = fread($fd, 10); if (substr($header, 0, 3) == 'ID3') { $MP3fileInfo['id3']['id3v2']['header'] = TRUE; $MP3fileInfo['id3']['id3v2']['majorversion'] = ord($header[3]); $MP3fileInfo['id3']['id3v2']['minorversion'] = ord($header[4]); } if (isset($MP3fileInfo['id3']['id3v2']['header']) && $MP3fileInfo['id3']['id3v2']['majorversion'] <= 4) { // this script probably won't correctly parse ID3v2.5.x and above. $id3_flags = BigEndian2Bin($header[5]); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { // %ab000000 in v2.2 $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $MP3fileInfo['id3']['id3v2']['flags']['compression'] = $id3_flags[1]; // b - Compression } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { // %abc00000 in v2.3 $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $MP3fileInfo['id3']['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $MP3fileInfo['id3']['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 4) { // %abcd0000 in v2.4 $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $MP3fileInfo['id3']['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $MP3fileInfo['id3']['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator $MP3fileInfo['id3']['id3v2']['flags']['isfooter'] = $id3_flags[3]; // d - Footer present } } } $MP3fileInfo['id3']['id3v2']['headerlength'] = BigEndian2Int(substr($header, 6, 4), 1) + ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); // Extended Header if (isset($MP3fileInfo['id3']['id3v2']['flags']['exthead']) && $MP3fileInfo['id3']['id3v2']['flags']['exthead']) { // Extended header size 4 * %0xxxxxxx // Number of flag bytes $01 // Extended Flags $xx // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. $extheader = fread($fd, 4); $MP3fileInfo['id3']['id3v2']['extheaderlength'] = BigEndian2Int($extheader, 1); // The extended flags field, with its size described by 'number of flag bytes', is defined as: // %0bcd0000 // b - Tag is an update // Flag data length $00 // c - CRC data present // Flag data length $05 // Total frame CRC 5 * %0xxxxxxx // d - Tag restrictions // Flag data length $01 $extheaderflagbytes = fread($fd, 1); $extheaderflags = fread($fd, $extheaderflagbytes); $id3_exthead_flags = BigEndian2Bin(substr($header, 5, 1)); $MP3fileInfo['id3']['id3v2']['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1); $MP3fileInfo['id3']['id3v2']['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1); if ($MP3fileInfo['id3']['id3v2']['exthead_flags']['CRC']) { $extheaderrawCRC = fread($fd, 5); $MP3fileInfo['id3']['id3v2']['exthead_flags']['CRC'] = BigEndian2Int($extheaderrawCRC, 1); } $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1); if ($MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions']) { // Restrictions %ppqrrstt $extheaderrawrestrictions = fread($fd, 1); $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions } } // end extended header // Frames // All ID3v2 frames consists of one frame header followed by one or more // fields containing the actual information. The header is always 10 // bytes and laid out as follows: // // Frame ID $xx xx xx xx (four characters) // Size 4 * %0xxxxxxx // Flags $xx xx $sizeofframes = $MP3fileInfo['id3']['id3v2']['headerlength'] - ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); if (isset($MP3fileInfo['id3']['id3v2']['extheaderlength'])) { $sizeofframes -= $MP3fileInfo['id3']['id3v2']['extheaderlength']; } if (isset($MP3fileInfo['id3']['id3v2']['flags']['isfooter']) && $MP3fileInfo['id3']['id3v2']['flags']['isfooter']) { $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio } if ($sizeofframes > 0) { $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) if (isset($MP3fileInfo['id3']['id3v2']['flags']['unsynch']) && $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] && $MP3fileInfo['id3']['id3v2']['majorversion'] <= 3) { $framedata = DeUnSynchronise($framedata); } // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead // of on tag level, making it easier to skip frames, increasing the streamability // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that // there exists an unsynchronised frame, while the new unsynchronisation flag in // the frame header [S:4.1.2] indicates unsynchronisation. include_once GETID3_INCLUDEPATH . 'getid3.frames.php'; // ID3v2FrameProcessing() $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header while (isset($framedata) && strlen($framedata) > 0) { // cycle through until no more frame data is left to parse if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { // Frame ID $xx xx xx (three characters) // Size $xx xx xx (24-bit integer) // Flags $xx xx $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header $framedata = substr($framedata, 6); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 3); $frame_size = BigEndian2Int(substr($frame_header, 3, 3), 0); $frame_flags = ''; // not used for anything, just to avoid E_NOTICEs } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] > 2) { // Frame ID $xx xx xx xx (four characters) // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) // Flags $xx xx $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header $framedata = substr($framedata, 10); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 4); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } else { // ID3v2.4+ $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) } if ($frame_size < strlen($framedata) + 4) { $nextFrameID = substr($framedata, $frame_size, 4); if (IsValidID3v2FrameName($nextFrameID, $MP3fileInfo['id3']['id3v2']['majorversion'])) { // next frame is OK } else { if ($frame_name == chr(0) . 'MP3' || $frame_name == ' MP3' || $frame_name == 'MP3e') { // MP3ext known broken frames - "ok" for the purposes of this test } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 4 && IsValidID3v2FrameName(substr($framedata, BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3)) { $MP3fileInfo['error'] .= "\n" . 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of Helium2 (www.helium2.com) is a known culprit of this. Tag has been parsed as ID3v2.3'; $MP3fileInfo['id3']['id3v2']['majorversion'] = 3; $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } } } } $frame_flags = BigEndian2Bin(substr($frame_header, 8, 2)); } } if ($frame_name == chr(0) . chr(0) . chr(0) . chr(0)) { // padding encountered // $MP3fileInfo['id3']['id3v2']['padding']['start'] = $MP3fileInfo['id3']['id3v2']['headerlength'] - strlen($framedata); $MP3fileInfo['id3']['id3v2']['padding']['start'] = $framedataoffset; $MP3fileInfo['id3']['id3v2']['padding']['length'] = strlen($framedata); $MP3fileInfo['id3']['id3v2']['padding']['valid'] = TRUE; for ($i = 0; $i < $MP3fileInfo['id3']['id3v2']['padding']['length']; $i++) { if (substr($framedata, $i, 1) != chr(0)) { $MP3fileInfo['id3']['id3v2']['padding']['valid'] = FALSE; $MP3fileInfo['id3']['id3v2']['padding']['errorpos'] = $MP3fileInfo['id3']['id3v2']['padding']['start'] + $i; break; } } break; // skip rest of ID3v2 header } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName($frame_name, $MP3fileInfo['id3']['id3v2']['majorversion'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($framedata, 0, $frame_size); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength'] = CastAsInt($frame_size); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset'] = $framedataoffset; $framedata = substr($framedata, $frame_size); // in getid3.frames.php - this function does all the FrameID-level parsing ID3v2FrameProcessing($frame_name, $frame_flags, $MP3fileInfo); $framedataoffset += $frame_size + ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); } else { // invalid frame length or FrameID $MP3fileInfo['error'] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $MP3fileInfo['id3']['id3v2']['majorversion'] . ' tag).'; if ($frame_size > strlen($framedata)) { $MP3fileInfo['error'] .= ' (ERROR: $frame_size (' . $frame_size . ') > strlen($framedata) (' . strlen($framedata) . ')).'; } if (!IsValidID3v2FrameName($frame_name, $MP3fileInfo['id3']['id3v2']['majorversion'])) { $MP3fileInfo['error'] .= ' (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $MP3fileInfo['id3']['id3v2']['majorversion'] . '))).'; if ($frame_name == chr(0) . 'MP3' || $frame_name == ' MP3' || $frame_name == 'MP3e') { $MP3fileInfo['error'] .= ' [Note: this particular error has been known to happen with tags edited by "MP3ext V3.3.17(unicode)"]'; } else { if ($frame_name == 'COM ') { $MP3fileInfo['error'] .= ' [Note: this particular error has been known to happen with tags edited by "iTunes X v2.0.3"]'; } } } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $MP3fileInfo['id3']['id3v2']['majorversion'])) { // next frame is valid, just skip the current frame $framedata = substr($framedata, $frame_size); } else { // next frame is invalid too, abort processing unset($framedata); } } } } // Footer // The footer is a copy of the header, but with a different identifier. // ID3v2 identifier "3DI" // ID3v2 version $04 00 // ID3v2 flags %abcd0000 // ID3v2 size 4 * %0xxxxxxx if (isset($MP3fileInfo['id3']['id3v2']['flags']['isfooter']) && $MP3fileInfo['id3']['id3v2']['flags']['isfooter']) { $footer = fread($fd, 10); if (substr($footer, 0, 3) == '3DI') { $MP3fileInfo['id3']['id3v2']['footer'] = true; $MP3fileInfo['id3']['id3v2']['majorversion_footer'] = ord(substr($footer, 3, 1)); $MP3fileInfo['id3']['id3v2']['minorversion_footer'] = ord(substr($footer, 4, 1)); } if ($MP3fileInfo['id3']['id3v2']['majorversion_footer'] <= 4) { $id3_flags = BigEndian2Bin(substr($footer, 5, 1)); $MP3fileInfo['id3']['id3v2']['flags']['unsynch_footer'] = substr($id3_flags, 0, 1); $MP3fileInfo['id3']['id3v2']['flags']['extfoot_footer'] = substr($id3_flags, 1, 1); $MP3fileInfo['id3']['id3v2']['flags']['experim_footer'] = substr($id3_flags, 2, 1); $MP3fileInfo['id3']['id3v2']['flags']['isfooter_footer'] = substr($id3_flags, 3, 1); $MP3fileInfo['id3']['id3v2']['footerlength'] = BigEndian2Int(substr($footer, 6, 4), 1); } } // end footer // Translate most common ID3v2 FrameIDs to easier-to-understand names if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { if (isset($MP3fileInfo['id3']['id3v2']['TT2'])) { $MP3fileInfo['id3']['id3v2']['title'] = $MP3fileInfo['id3']['id3v2']['TT2']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TP1'])) { $MP3fileInfo['id3']['id3v2']['artist'] = $MP3fileInfo['id3']['id3v2']['TP1']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TAL'])) { $MP3fileInfo['id3']['id3v2']['album'] = $MP3fileInfo['id3']['id3v2']['TAL']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TYE'])) { $MP3fileInfo['id3']['id3v2']['year'] = $MP3fileInfo['id3']['id3v2']['TYE']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TRK'])) { $MP3fileInfo['id3']['id3v2']['track'] = $MP3fileInfo['id3']['id3v2']['TRK']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TCO'])) { $MP3fileInfo['id3']['id3v2']['genre'] = $MP3fileInfo['id3']['id3v2']['TCO']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['COM'][0]['asciidata'])) { $MP3fileInfo['id3']['id3v2']['comment'] = $MP3fileInfo['id3']['id3v2']['COM'][0]['asciidata']; } } else { // $MP3fileInfo['id3']['id3v2']['majorversion'] > 2 if (isset($MP3fileInfo['id3']['id3v2']['TIT2'])) { $MP3fileInfo['id3']['id3v2']['title'] = $MP3fileInfo['id3']['id3v2']['TIT2']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TPE1'])) { $MP3fileInfo['id3']['id3v2']['artist'] = $MP3fileInfo['id3']['id3v2']['TPE1']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TALB'])) { $MP3fileInfo['id3']['id3v2']['album'] = $MP3fileInfo['id3']['id3v2']['TALB']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TYER'])) { $MP3fileInfo['id3']['id3v2']['year'] = $MP3fileInfo['id3']['id3v2']['TYER']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TRCK'])) { $MP3fileInfo['id3']['id3v2']['track'] = $MP3fileInfo['id3']['id3v2']['TRCK']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TCON'])) { $MP3fileInfo['id3']['id3v2']['genre'] = $MP3fileInfo['id3']['id3v2']['TCON']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['COMM'][0]['asciidata'])) { $MP3fileInfo['id3']['id3v2']['comment'] = $MP3fileInfo['id3']['id3v2']['COMM'][0]['asciidata']; } } if (isset($MP3fileInfo['id3']['id3v2']['genre'])) { $MP3fileInfo['id3']['id3v2']['genrelist'] = ParseID3v2GenreString($MP3fileInfo['id3']['id3v2']['genre']); if ($MP3fileInfo['id3']['id3v2']['genrelist']['genreid'][0] !== '') { $MP3fileInfo['id3']['id3v2']['genreid'] = $MP3fileInfo['id3']['id3v2']['genrelist']['genreid'][0]; } $MP3fileInfo['id3']['id3v2']['genre'] = $MP3fileInfo['id3']['id3v2']['genrelist']['genre'][0]; } if (isset($MP3fileInfo['id3']['id3v2']['track']) && strpos($MP3fileInfo['id3']['id3v2']['track'], '/') !== FALSE) { $tracktotaltracks = explode('/', $MP3fileInfo['id3']['id3v2']['track']); $MP3fileInfo['id3']['id3v2']['track'] = $tracktotaltracks[0]; $MP3fileInfo['id3']['id3v2']['totaltracks'] = $tracktotaltracks[1]; } } else { // MajorVersion is > 4, or no ID3v2 header present if (isset($MP3fileInfo['id3']['id3v2']['header'])) { // MajorVersion is > 4 $MP3fileInfo['error'] .= "\n" . 'this script only parses up to ID3v2.4.x - this tag is ID3v2.' . $MP3fileInfo['id3']['id3v2']['majorversion'] . '.' . $MP3fileInfo['id3']['id3v2']['minorversion']; } else { // no ID3v2 header present - this is fine, just don't process anything. } } return TRUE; }