function FixedPoint2_30($rawdata) { $binarystring = BigEndian2Bin($rawdata); return Bin2Dec(substr($binarystring, 0, 2)) + (double) (Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30)); }
function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch = true, $ScanAsCBR = false, $FastMPEGheaderScan = false) { static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; static $MPEGaudioFrequencyLookup; static $MPEGaudioChannelModeLookup; static $MPEGaudioModeExtensionLookup; static $MPEGaudioEmphasisLookup; if (empty($MPEGaudioVersionLookup)) { $MPEGaudioVersionLookup = MPEGaudioVersionArray(); $MPEGaudioLayerLookup = MPEGaudioLayerArray(); $MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); $MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray(); $MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray(); $MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray(); $MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray(); } if ($offset >= $ThisFileInfo['avdataend']) { $ThisFileInfo['error'] .= "\n" . 'end of file encounter looking for MPEG synch'; return false; } fseek($fd, $offset, SEEK_SET); $headerstring = fread($fd, 192); // MP3 audio frame structure: // $aa $aa $aa $aa [$bb $bb] $cc... // where $aa..$aa is the four-byte mpeg-audio header (below) // $bb $bb is the optional 2-byte CRC // and $cc... is the audio data $head4 = substr($headerstring, 0, 4); static $MPEGaudioHeaderDecodeCache = array(); if (isset($MPEGaudioHeaderDecodeCache[$head4])) { $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; } else { $MPEGheaderRawArray = MPEGaudioHeaderDecode($head4); $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; } static $MPEGaudioHeaderValidCache = array(); // Not in cache if (!isset($MPEGaudioHeaderValidCache[$head4])) { $MPEGaudioHeaderValidCache[$head4] = MPEGaudioHeaderValid($MPEGheaderRawArray); } if ($MPEGaudioHeaderValidCache[$head4]) { $ThisFileInfo['mpeg']['audio']['raw'] = $MPEGheaderRawArray; } else { $ThisFileInfo['error'] .= "\n" . 'Invalid MPEG audio header at offset ' . $offset; return false; } if (!$FastMPEGheaderScan) { $ThisFileInfo['mpeg']['audio']['version'] = $MPEGaudioVersionLookup[$ThisFileInfo['mpeg']['audio']['raw']['version']]; $ThisFileInfo['mpeg']['audio']['layer'] = $MPEGaudioLayerLookup[$ThisFileInfo['mpeg']['audio']['raw']['layer']]; $ThisFileInfo['mpeg']['audio']['channelmode'] = $MPEGaudioChannelModeLookup[$ThisFileInfo['mpeg']['audio']['raw']['channelmode']]; $ThisFileInfo['mpeg']['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono' ? 1 : 2; $ThisFileInfo['mpeg']['audio']['sample_rate'] = $MPEGaudioFrequencyLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['raw']['sample_rate']]; $ThisFileInfo['mpeg']['audio']['protection'] = !$ThisFileInfo['mpeg']['audio']['raw']['protection']; $ThisFileInfo['mpeg']['audio']['private'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['private']; $ThisFileInfo['mpeg']['audio']['modeextension'] = $MPEGaudioModeExtensionLookup[$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['modeextension']]; $ThisFileInfo['mpeg']['audio']['copyright'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['copyright']; $ThisFileInfo['mpeg']['audio']['original'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['original']; $ThisFileInfo['mpeg']['audio']['emphasis'] = $MPEGaudioEmphasisLookup[$ThisFileInfo['mpeg']['audio']['raw']['emphasis']]; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; if ($ThisFileInfo['mpeg']['audio']['protection']) { $ThisFileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2)); } } $ThisFileInfo['mpeg']['audio']['padding'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['padding']; $ThisFileInfo['mpeg']['audio']['bitrate'] = $MPEGaudioBitrateLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['bitrate']]; // For Layer II there are some combinations of bitrate and mode which are not allowed. if (!$FastMPEGheaderScan && $ThisFileInfo['mpeg']['audio']['layer'] == 'II') { //$ThisFileInfo['fileformat'] = 'mp2'; $ThisFileInfo['audio']['dataformat'] = 'mp2'; switch ($ThisFileInfo['mpeg']['audio']['channelmode']) { case 'mono': if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' || $ThisFileInfo['mpeg']['audio']['bitrate'] <= 192) { // these are ok } else { $ThisFileInfo['error'] .= "\n" . $ThisFileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $ThisFileInfo['mpeg']['audio']['channelmode'] . '.'; return false; } break; case 'stereo': case 'joint stereo': case 'dual channel': if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' || $ThisFileInfo['mpeg']['audio']['bitrate'] == 64 || $ThisFileInfo['mpeg']['audio']['bitrate'] >= 96) { // these are ok } else { $ThisFileInfo['error'] .= "\n" . $ThisFileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $ThisFileInfo['mpeg']['audio']['channelmode'] . '.'; return false; } break; } } if ($ThisFileInfo['mpeg']['audio']['bitrate'] != 'free') { if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $FrameLengthCoefficient = 48; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 4 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } else { // Layer II / III $FrameLengthCoefficient = 144; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 1 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } } else { // MPEG-2 / MPEG-2.5 if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $FrameLengthCoefficient = 24; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 4 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } else { // Layer II / III $FrameLengthCoefficient = 72; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 1 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } } // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding // http://66.96.216.160/cgi-bin/YaBB.pl?board=c&action=display&num=1018474068 // -> [Finding the next frame synch] on www.r3mix.net forums if the above link goes dead if ($ThisFileInfo['audio']['sample_rate'] > 0) { $ThisFileInfo['mpeg']['audio']['framelength'] = (int) floor($FrameLengthCoefficient * 1000 * $ThisFileInfo['mpeg']['audio']['bitrate'] / $ThisFileInfo['audio']['sample_rate']) + $FrameLengthPadding; } } $ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['bitrate']; if (isset($ThisFileInfo['mpeg']['audio']['framelength'])) { $nextframetestoffset = $offset + $ThisFileInfo['mpeg']['audio']['framelength']; } else { $ThisFileInfo['error'] .= "\n" . 'Frame at offset(' . $offset . ') is has an invalid frame length.'; return false; } //////////////////////////////////////////////////////////////////////////////////// // Variable-bitrate headers if (substr($headerstring, 4 + 32, 4) == 'VBRI') { // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Fraunhofer'; $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; $SideInfoData = substr($headerstring, 4 + 2, 32); $FraunhoferVBROffset = 4 + 32 + strlen('VBRI'); $Fraunhofer_EncoderVersion = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_encoder_version'] = BigEndian2Int($Fraunhofer_EncoderVersion); $Fraunhofer_EncoderDelay = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_encoder_delay'] = BigEndian2Int($Fraunhofer_EncoderDelay); $Fraunhofer_quality = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_quality'] = BigEndian2Int($Fraunhofer_quality); $Fraunhofer_Bytes = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int($Fraunhofer_Bytes); $Fraunhofer_Frames = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int($Fraunhofer_Frames); $Fraunhofer_SeekOffsets = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets'] = BigEndian2Int($Fraunhofer_SeekOffsets); $FraunhoferVBROffset += 4; // hardcoded $00 $01 $00 $02 - purpose unknown $Fraunhofer_OffsetStride = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets_stride'] = BigEndian2Int($Fraunhofer_OffsetStride); $previousbyteoffset = $offset; for ($i = 0; $i < $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets']; $i++) { $Fraunhofer_OffsetN = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, 2)); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_offsets_relative'][$i] = $Fraunhofer_OffsetN; $ThisFileInfo['mpeg']['audio']['VBR_offsets_absolute'][$i] = $Fraunhofer_OffsetN + $previousbyteoffset; $previousbyteoffset += $Fraunhofer_OffsetN; } } else { // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) // depending on MPEG layer and number of channels if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-1 (mono) $VBRidOffset = 4 + 17; // 0x15 $SideInfoData = substr($headerstring, 4 + 2, 17); } else { // MPEG-1 (stereo, joint-stereo, dual-channel) $VBRidOffset = 4 + 32; // 0x24 $SideInfoData = substr($headerstring, 4 + 2, 32); } } else { // 2 or 2.5 if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-2, MPEG-2.5 (mono) $VBRidOffset = 4 + 9; // 0x0D $SideInfoData = substr($headerstring, 4 + 2, 9); } else { // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) $VBRidOffset = 4 + 17; // 0x15 $SideInfoData = substr($headerstring, 4 + 2, 17); } } if (substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing' || substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info') { // 'Xing' is traditional Xing VBR frame, 'Info' is LAME-encoded CBR // 'This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.' $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Xing'; $XingVBROffset = $VBRidOffset + strlen('Xing'); $ThisFileInfo['mpeg']['audio']['xing_flags_raw'] = substr($headerstring, $XingVBROffset, 4); $XingVBROffset += 4; $XingHeader_byte4 = BigEndian2Bin(substr($ThisFileInfo['mpeg']['audio']['xing_flags_raw'], 3, 1)); $ThisFileInfo['mpeg']['audio']['xing_flags']['frames'] = (bool) $XingHeader_byte4[4]; $ThisFileInfo['mpeg']['audio']['xing_flags']['bytes'] = (bool) $XingHeader_byte4[5]; $ThisFileInfo['mpeg']['audio']['xing_flags']['toc'] = (bool) $XingHeader_byte4[6]; $ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale'] = (bool) $XingHeader_byte4[7]; if ($ThisFileInfo['mpeg']['audio']['xing_flags']['frames']) { $ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['bytes']) { $ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['toc']) { $LAMEtocData = substr($headerstring, $XingVBROffset, 100); $XingVBROffset += 100; for ($i = 0; $i < 100; $i++) { $ThisFileInfo['mpeg']['audio']['toc'][$i] = ord($LAMEtocData[$i]); } } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale']) { $ThisFileInfo['mpeg']['audio']['VBR_scale'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; } if (substr($headerstring, $XingVBROffset, 4) == 'LAME') { $ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($headerstring, $XingVBROffset, 9); $XingVBROffset += 9; $LAMEtagRevisionVBRmethod = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xf0) >> 4; $ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw'] = $LAMEtagRevisionVBRmethod & 0xf; $ThisFileInfo['mpeg']['audio']['LAME']['vbr_method'] = LAMEvbrMethodLookup($ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw']); $ThisFileInfo['mpeg']['audio']['LAME']['lowpass_frequency'] = 100 * BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = BigEndian2Float(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; $RadioReplayGainRaw = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); $XingVBROffset += 4; $ReplayGainID = ($RadioReplayGainRaw & 0xe000) >> 13; $ReplayGainNameKey = ''; switch ($ReplayGainID) { case 1: $ReplayGainNameKey = 'radio'; break; case 2: $ReplayGainNameKey = 'audiophile'; break; case 0: // replay gain not set // replay gain not set default: // reserved break; } if ($ReplayGainNameKey) { require_once GETID3_INCLUDEPATH . 'getid3.rgad.php'; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['name'] = ($RadioReplayGainRaw & 0xe000) >> 13; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['originator'] = ($RadioReplayGainRaw & 0x1c00) >> 10; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['sign_bit'] = ($RadioReplayGainRaw & 0x200) >> 9; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['gain_adjust'] = $RadioReplayGainRaw & 0x1ff; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['name'] = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['name']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['originator']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['gain_db'] = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['sign_bit']); $ThisFileInfo['replay_gain']["{$ReplayGainNameKey}"]['peak'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude']; $ThisFileInfo['replay_gain']["{$ReplayGainNameKey}"]['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['originator']; $ThisFileInfo['replay_gain']["{$ReplayGainNameKey}"]['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['gain_db']; } $EncodingFlagsATHtype = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); $ThisFileInfo['mpeg']['audio']['LAME']['ath_type'] = $EncodingFlagsATHtype & 0xf; $ABRbitrateMinBitrate = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; if ($ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw'] == 2) { // Average BitRate (ABR) $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_abr'] = $ABRbitrateMinBitrate; } elseif ($ABRbitrateMinBitrate > 0) { // Variable BitRate (VBR) - minimum bitrate $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] = $ABRbitrateMinBitrate; } $EncoderDelays = BigEndian2Int(substr($headerstring, $XingVBROffset, 3)); $XingVBROffset += 3; $ThisFileInfo['mpeg']['audio']['LAME']['encoder_delay'] = ($EncoderDelays & 0xfff000) >> 12; $ThisFileInfo['mpeg']['audio']['LAME']['end_padding'] = $EncoderDelays & 0xfff; $MiscByte = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping_raw'] = $EncodingFlagsATHtype & 0x3; $ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode_raw'] = ($EncodingFlagsATHtype & 0x1c) >> 2; $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality_raw'] = ($EncodingFlagsATHtype & 0x20) >> 5; $ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq_raw'] = ($EncodingFlagsATHtype & 0xc0) >> 6; $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping'] = $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping_raw']; $ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode'] = LAMEmiscStereoModeLookup($ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode_raw']); $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality'] = (bool) $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality_raw']; $ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq'] = LAMEmiscSourceSampleFrequencyLookup($ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq_raw']); $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_raw'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 1), false, true); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] = 1.5 * $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_raw']; $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_factor'] = pow(2, $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] / 6); $PresetSurroundBytes = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); //$Reserved = ($PresetSurroundBytes & 0xC000); $ThisFileInfo['mpeg']['audio']['LAME']['surround_info_id'] = $PresetSurroundBytes & 0x3800; $ThisFileInfo['mpeg']['audio']['LAME']['surround_info'] = LAMEsurroundInfoLookup($ThisFileInfo['mpeg']['audio']['LAME']['surround_info_id']); $ThisFileInfo['mpeg']['audio']['LAME']['preset_used_id'] = $PresetSurroundBytes & 0x7ff; $XingVBROffset += 2; $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; if ($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] > $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) { $ThisFileInfo['warning'] .= "\n" . 'Probable truncated file: expecting ' . $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] . ' bytes of audio data, only found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); } $ThisFileInfo['mpeg']['audio']['LAME']['music_crc'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); $XingVBROffset += 2; $ThisFileInfo['mpeg']['audio']['LAME']['lame_tag_crc'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); $XingVBROffset += 2; // LAME CBR if ($ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw'] == 1) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; if (empty($ThisFileInfo['mpeg']['audio']['bitrate']) || $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] != 255) { $ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min']; } } } } else { // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; if ($recursivesearch) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; if (RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { $recursivesearch = false; $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; } if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') { $ThisFileInfo['warning'] .= "\n" . 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; } } } } if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr' && isset($ThisFileInfo['mpeg']['audio']['VBR_frames']) && $ThisFileInfo['mpeg']['audio']['VBR_frames'] > 1) { $ThisFileInfo['mpeg']['audio']['VBR_frames']--; // don't count the Xing / VBRI frame if ($ThisFileInfo['mpeg']['audio']['version'] == '1' && $ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 384) / 1000; } elseif (($ThisFileInfo['mpeg']['audio']['version'] == '2' || $ThisFileInfo['mpeg']['audio']['version'] == '2.5') && $ThisFileInfo['mpeg']['audio']['layer'] == 'III') { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 576) / 1000; } else { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 1152) / 1000; } if ($ThisFileInfo['mpeg']['audio']['VBR_bitrate'] > 0) { $ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; $ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; // to avoid confusion } } // End variable-bitrate headers //////////////////////////////////////////////////////////////////////////////////// if ($recursivesearch) { if (!RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { return false; } } if (false) { // experimental side info parsing section - not returning anything useful yet $SideInfoBitstream = BigEndian2Bin($SideInfoData); $SideInfoOffset = 0; if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-1 (mono) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; $SideInfoOffset += 5; } else { // MPEG-1 (stereo, joint-stereo, dual-channel) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; $SideInfoOffset += 3; } } else { // 2 or 2.5 if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-2, MPEG-2.5 (mono) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); $SideInfoOffset += 8; $SideInfoOffset += 1; } else { // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); $SideInfoOffset += 8; $SideInfoOffset += 2; } } if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { $ThisFileInfo['mpeg']['audio']['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 2; } } } for ($granule = 0; $granule < ($ThisFileInfo['mpeg']['audio']['version'] == '1' ? 2 : 1); $granule++) { for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { $ThisFileInfo['mpeg']['audio']['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); $SideInfoOffset += 12; $ThisFileInfo['mpeg']['audio']['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; $ThisFileInfo['mpeg']['audio']['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); $SideInfoOffset += 8; if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); $SideInfoOffset += 4; } else { $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; } $ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; if ($ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] == '1') { $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); $SideInfoOffset += 2; $ThisFileInfo['mpeg']['audio']['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; for ($region = 0; $region < 2; $region++) { $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); $SideInfoOffset += 5; } $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][2] = 0; for ($window = 0; $window < 3; $window++) { $ThisFileInfo['mpeg']['audio']['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); $SideInfoOffset += 3; } } else { for ($region = 0; $region < 3; $region++) { $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); $SideInfoOffset += 5; } $ThisFileInfo['mpeg']['audio']['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); $SideInfoOffset += 4; $ThisFileInfo['mpeg']['audio']['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); $SideInfoOffset += 3; $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = 0; } if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { $ThisFileInfo['mpeg']['audio']['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; } $ThisFileInfo['mpeg']['audio']['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; $ThisFileInfo['mpeg']['audio']['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; } } } return true; }
function GetAllFileInfo($filename, $assumedFormat = '', $MD5file = false, $MD5data = false, $MD5dataIfMD5SourceKnown = false) { $ThisFileInfo = array(); $localfilepointer = null; if (!InitializeFilepointerArray($filename, $localfilepointer, $ThisFileInfo)) { // remove unneeded/meaningless keys CleanUpGetAllMP3info($ThisFileInfo); // close & remove local filepointer CloseRemoveFilepointer($localfilepointer); return $ThisFileInfo; } // Handle ID3v1 & Lyrics3 tags HandleID3v1Tag($localfilepointer, $ThisFileInfo); HandleLyrics3Tag($localfilepointer, $ThisFileInfo); rewind($localfilepointer); //$formattest = fread($localfilepointer, 16); // 16 bytes is sufficient for any format except ISO CD-image $formattest = fread($localfilepointer, 32774); // (ISO needs at least 32774 bytes) // Handle ID3v2 tag if (HandleID3v2Tag($localfilepointer, $ThisFileInfo)) { fseek($localfilepointer, $ThisFileInfo['avdataoffset'], SEEK_SET); $formattest = fread($localfilepointer, 32774); // (ISO9660 needs at least 32774 bytes) } // Handle APE tags HandleAPETag($localfilepointer, $ThisFileInfo); // Nothing-but-tags check if ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] > 0) { if ($assumedFormat) { $format_info = GetFileFormatArray(); require_once GETID3_INCLUDEPATH . $format_info["{$assumedFormat}"]['include']; $VariableFunctionName = $format_info["{$assumedFormat}"]['function']; $VariableFunctionName($localfilepointer, $ThisFileInfo); } elseif ($DeterminedFormat = GetFileFormat($formattest)) { // break if ID3/APE tags found on illegal format if (!$DeterminedFormat['allowtags'] && $ThisFileInfo['avdataoffset'] > 0 && $ThisFileInfo['avdataend'] != $ThisFileInfo['filesize']) { $ThisFileInfo['error'] .= "\n" . 'Illegal ID3 and/or APE tag found on non multimedia file.'; break; } // set mime type $ThisFileInfo['mime_type'] = $DeterminedFormat['mimetype']; // supported format signature pattern detected if (!file_exists(GETID3_INCLUDEPATH . $DeterminedFormat['include'])) { die('<HR><FONT COLOR="#FF0000">Cannot find required file: ' . GETID3_INCLUDEPATH . $DeterminedFormat['include'] . '</FONT>'); } require_once GETID3_INCLUDEPATH . $DeterminedFormat['include']; $VariableFunctionName = $DeterminedFormat['function']; $VariableFunctionName($localfilepointer, $ThisFileInfo); } elseif ($assumedFormat == 'mp3' || $assumedFormat == '' && (substr($formattest, 0, 3) == 'ID3' || substr(BigEndian2Bin(substr($formattest, 0, 2)), 0, 11) == '11111111111')) { // assume AAC-ADTS format require_once GETID3_INCLUDEPATH . 'getid3.aac.php'; $dummy = $ThisFileInfo; if (getAACADTSheaderFilepointer($localfilepointer, $dummy)) { $ThisFileInfo = $dummy; } else { // it's not AAC-ADTS format, probably MP3 require_once GETID3_INCLUDEPATH . 'getid3.mp3.php'; getMP3headerFilepointer($localfilepointer, $ThisFileInfo); } } else { // unknown format, do nothing } } if (!empty($ThisFileInfo['audio']['channels'])) { switch ($ThisFileInfo['audio']['channels']) { case 1: $ThisFileInfo['audio']['channelmode'] = 'mono'; break; case 2: $ThisFileInfo['audio']['channelmode'] = 'stereo'; break; default: // unknown? break; } } // Get the MD5 hash of the entire file if ($MD5file && empty($ThisFileInfo['md5_file'])) { set_time_limit(max($ThisFileInfo['filesize'] / 10000000, 30)); $ThisFileInfo['md5_file'] = md5_file($filename); } // Get the MD5 hash of the audio/video portion of the file // (without ID3/APE/Lyrics3/etc header/footer tags if ($MD5data && empty($ThisFileInfo['md5_data'])) { if ($MD5dataIfMD5SourceKnown || empty($ThisFileInfo['md5_data_source'])) { // Only calculate ['md5_data'] if ['md5_data_source'] is unknown, // or if $MD5dataIfMD5SourceKnown is set to true getMD5data($ThisFileInfo); } } CalculateCompressionRatioVideo($ThisFileInfo); CalculateCompressionRatioAudio($ThisFileInfo); // return tags data in alphabetical order, without duplicates $ThisFileInfo['tags'] = array_unique($ThisFileInfo['tags']); sort($ThisFileInfo['tags']); // remove unneeded/meaningless keys CleanUpGetAllMP3info($ThisFileInfo); if (isset($ThisFileInfo['fileformat'])) { // Calculate combined bitrate - audio + video $CombinedBitrate = 0; $CombinedBitrate += isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0; $CombinedBitrate += isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0; if ($CombinedBitrate > 0 && !isset($ThisFileInfo['bitrate'])) { $ThisFileInfo['bitrate'] = $CombinedBitrate; } if (isset($ThisFileInfo['video']) && !isset($ThisFileInfo['video']['bitrate']) || isset($ThisFileInfo['audio']) && !isset($ThisFileInfo['audio']['bitrate'])) { // for example, VBR MPEG video files cannot determine video bitrate: // should not set overall bitrate and playtime from audio bitrate only unset($ThisFileInfo['bitrate']); } if (!isset($ThisFileInfo['playtime_seconds']) && !empty($ThisFileInfo['bitrate'])) { $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['bitrate']; } // Set playtime string if (!empty($ThisFileInfo['playtime_seconds']) && empty($ThisFileInfo['playtime_string'])) { $ThisFileInfo['playtime_string'] = PlaytimeString($ThisFileInfo['playtime_seconds']); } } // close & remove local filepointer CloseRemoveFilepointer($localfilepointer); return $ThisFileInfo; }
function getOnlyMPEGaudioInfo($fd, &$MP3fileInfo, $audiodataoffset) { // looks for synch, decodes MPEG audio header // you may call this function directly if you don't need any ID3 info fseek($fd, $audiodataoffset); $header = ''; $SynchSeekOffset = 0; while (!isset($MP3fileInfo['fileformat']) || $MP3fileInfo['fileformat'] == '' || $MP3fileInfo['fileformat'] == 'id3') { if ($SynchSeekOffset > strlen($header) - 8192 && !feof($fd)) { if ($SynchSeekOffset > FREAD_BUFFER_SIZE * 4) { // if a synch's not found within the first 64k bytes, then give up $MP3fileInfo['error'] .= "\n" . 'could not find valid MPEG synch within the first ' . FREAD_BUFFER_SIZE * 4 . ' bytes'; if (isset($MP3fileInfo['bitrate'])) { unset($MP3fileInfo['bitrate']); } if (isset($MP3fileInfo['mpeg']['audio'])) { unset($MP3fileInfo['mpeg']['audio']); } if (isset($MP3fileInfo['mpeg']) && (!is_array($MP3fileInfo['mpeg']) || count($MP3fileInfo['mpeg']) == 0)) { unset($MP3fileInfo['mpeg']); } return FALSE; } else { if ($header .= fread($fd, FREAD_BUFFER_SIZE)) { // great } else { $MP3fileInfo['error'] .= "\n" . 'could not find valid MPEG synch before end of file'; if (isset($MP3fileInfo['bitrate'])) { unset($MP3fileInfo['bitrate']); } if (isset($MP3fileInfo['mpeg']['audio'])) { unset($MP3fileInfo['mpeg']['audio']); } if (isset($MP3fileInfo['mpeg']) && (!is_array($MP3fileInfo['mpeg']) || count($MP3fileInfo['mpeg']) == 0)) { unset($MP3fileInfo['mpeg']); } return FALSE; } } } if (ord($header[$SynchSeekOffset]) == 0xff && substr(BigEndian2Bin(substr($header, $SynchSeekOffset, 2)), 0, 11) == '11111111111') { // synch detected if (decodeMPEGaudioHeader($fd, $audiodataoffset + $SynchSeekOffset, $MP3fileInfo, TRUE)) { $MP3fileInfo['audiodataoffset'] = $audiodataoffset + $SynchSeekOffset; $MP3fileInfo['fileformat'] = 'mp3'; break; // exit for() and while() } } if (!isset($MP3fileInfo['fileformat']) || $MP3fileInfo['fileformat'] == '' || $MP3fileInfo['fileformat'] == 'id3') { $SynchSeekOffset++; if ($audiodataoffset + $SynchSeekOffset >= $MP3fileInfo['filesize']) { // end of file $MP3fileInfo['error'] .= "\n" . 'could not find valid MPEG synch before end of file'; if (isset($MP3fileInfo['bitrate'])) { unset($MP3fileInfo['bitrate']); } if (isset($MP3fileInfo['mpeg']['audio'])) { unset($MP3fileInfo['mpeg']['audio']); } if (isset($MP3fileInfo['mpeg']) && (!is_array($MP3fileInfo['mpeg']) || count($MP3fileInfo['mpeg']) == 0)) { unset($MP3fileInfo['mpeg']); } return FALSE; } } } return TRUE; }
function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) { $ThisFileInfo['fileformat'] = 'aac'; $ThisFileInfo['audio']['dataformat'] = 'aac'; $ThisFileInfo['audio']['lossless'] = false; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $AACheader = fread($fd, 1024); $offset = 0; if (substr($AACheader, 0, 4) == 'ADIF') { // http://faac.sourceforge.net/wiki/index.php?page=ADIF // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf // adif_header() { // adif_id 32 // copyright_id_present 1 // if( copyright_id_present ) // copyright_id 72 // original_copy 1 // home 1 // bitstream_type 1 // bitrate 23 // num_program_config_elements 4 // for (i = 0; i < num_program_config_elements + 1; i++ ) { // if( bitstream_type == '0' ) // adif_buffer_fullness 20 // program_config_element() // } // } $AACheaderBitstream = BigEndian2Bin($AACheader); $bitoffset = 0; $ThisFileInfo['aac']['header_type'] = 'ADIF'; $bitoffset += 32; $ThisFileInfo['aac']['header']['mpeg_version'] = 4; $ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; if ($ThisFileInfo['aac']['header']['copyright']) { $ThisFileInfo['aac']['header']['copyright_id'] = Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); $bitoffset += 72; } $ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; $ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; $ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); $bitoffset += 1; if ($ThisFileInfo['aac']['header']['is_vbr']) { $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['aac']['header']['bitrate_max'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); $bitoffset += 23; } else { $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['aac']['header']['bitrate'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); $bitoffset += 23; $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate']; } if ($ThisFileInfo['audio']['bitrate'] == 0) { $ThisFileInfo['error'] .= "\n" . 'Corrupt AAC file: bitrate_audio == zero'; return false; } $ThisFileInfo['aac']['header']['num_program_configs'] = 1 + Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) { // http://www.audiocoding.com/wiki/index.php?page=program_config_element // buffer_fullness 20 // element_instance_tag 4 // object_type 2 // sampling_frequency_index 4 // num_front_channel_elements 4 // num_side_channel_elements 4 // num_back_channel_elements 4 // num_lfe_channel_elements 2 // num_assoc_data_elements 3 // num_valid_cc_elements 4 // mono_mixdown_present 1 // mono_mixdown_element_number 4 if mono_mixdown_present == 1 // stereo_mixdown_present 1 // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 // matrix_mixdown_idx_present 1 // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 // for (i = 0; i < num_front_channel_elements; i++) { // front_element_is_cpe[i] 1 // front_element_tag_select[i] 4 // } // for (i = 0; i < num_side_channel_elements; i++) { // side_element_is_cpe[i] 1 // side_element_tag_select[i] 4 // } // for (i = 0; i < num_back_channel_elements; i++) { // back_element_is_cpe[i] 1 // back_element_tag_select[i] 4 // } // for (i = 0; i < num_lfe_channel_elements; i++) { // lfe_element_tag_select[i] 4 // } // for (i = 0; i < num_assoc_data_elements; i++) { // assoc_data_element_tag_select[i] 4 // } // for (i = 0; i < num_valid_cc_elements; i++) { // cc_element_is_ind_sw[i] 1 // valid_cc_element_tag_select[i] 4 // } // byte_alignment() VAR // comment_field_bytes 8 // for (i = 0; i < comment_field_bytes; i++) { // comment_field_data[i] 8 // } if (!$ThisFileInfo['aac']['header']['is_vbr']) { $ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); $bitoffset += 20; } $ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $ThisFileInfo['aac']['program_configs'][$i]['object_type'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); $bitoffset += 3; $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) { $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) { $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); $bitoffset += 2; $ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; } for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { $ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { $ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { $ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { $ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { $ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { $ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); $bitoffset += 1; $ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); $bitoffset += 4; } $bitoffset = ceil($bitoffset / 8) * 8; $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); $bitoffset += 8; $ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'])); $bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']; $ThisFileInfo['aac']['header']['profile_text'] = AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']); $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']); $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency']; $ThisFileInfo['audio']['channels'] = AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]); if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) { $ThisFileInfo['comments']['comment'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field']; } } $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; return true; } else { unset($ThisFileInfo['fileformat']); unset($ThisFileInfo['aac']); $ThisFileInfo['error'] .= "\n" . 'AAC-ADIF synch not found (expected "ADIF", found "' . substr($AACheader, 0, 4) . '" instead)'; return false; } }
function ID3v2FrameProcessing($frame_name, $frame_flags, &$MP3fileInfo) { // define $frame_arrayindex once here (used for many frames), override or ignore as neccesary $frame_arrayindex = count($MP3fileInfo['id3']['id3v2']["{$frame_name}"]); // 'data', 'datalength' if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'])) { $frame_arrayindex--; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { // frame flags are not part of the ID3v2.2 standard if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { // Frame Header Flags // %abc00000 %ijk00000 $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['TagAlterPreservation'] = (bool) substr($frame_flags, 0, 1); // a - Tag alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['FileAlterPreservation'] = (bool) substr($frame_flags, 1, 1); // b - File alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['ReadOnly'] = (bool) substr($frame_flags, 2, 1); // c - Read only $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression'] = (bool) substr($frame_flags, 8, 1); // i - Compression $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Encryption'] = (bool) substr($frame_flags, 9, 1); // j - Encryption $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['GroupingIdentity'] = (bool) substr($frame_flags, 10, 1); // k - Grouping identity } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 4) { // Frame Header Flags // %0abc0000 %0h00kmnp $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['TagAlterPreservation'] = (bool) substr($frame_flags, 1, 1); // a - Tag alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['FileAlterPreservation'] = (bool) substr($frame_flags, 2, 1); // b - File alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['ReadOnly'] = (bool) substr($frame_flags, 3, 1); // c - Read only $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['GroupingIdentity'] = (bool) substr($frame_flags, 9, 1); // h - Grouping identity $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression'] = (bool) substr($frame_flags, 12, 1); // k - Compression $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Encryption'] = (bool) substr($frame_flags, 13, 1); // m - Encryption $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Unsynchronisation'] = (bool) substr($frame_flags, 14, 1); // n - Unsynchronisation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['DataLengthIndicator'] = (bool) substr($frame_flags, 15, 1); // p - Data length indicator } } // Frame-level de-unsynchronization - ID3v2.4 if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Unsynchronisation'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = DeUnSynchronise($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } // Frame-level de-compression if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression'])) { // it's on the wishlist :) } } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'UFID' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'UFI') { // 4.1 UFI Unique file identifier // There may be more than one 'UFID' frame in a tag, // but only one with the same 'Owner identifier'. // <Header for 'Unique file identifier', ID: 'UFID'> // Owner identifier <text string> $00 // Identifier <up to 64 bytes binary data> $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0)); $frame_idstring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, $frame_terminatorpos); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_idstring; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(chr(0))); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'TXXX' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'TXX') { // 4.2.2 TXX User defined text information frame // There may be more than one 'TXXX' frame in each tag, // but only one with the same description. // <Header for 'User defined text information frame', ID: 'TXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // Value <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'], $frame_textencoding); } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($frame_name[0] == 'T') { // 4.2. T??[?] Text information frame // There may only be one text information frame of its kind in an tag. // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', // excluding 'TXXX' described in 4.2.6.> // Text encoding $xx // Information <text string(s) according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); // $MP3fileInfo['id3']['id3v2']["$frame_name"]['data'] = substr($MP3fileInfo['id3']['id3v2']["$frame_name"]['data'], $frame_offset); // this one-line method should work, but as a safeguard against null-padded data, do it the safe way $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } if ($frame_terminatorpos) { // there are null bytes after the data - this is not according to spec // only use data up to first null byte $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); } else { // no null bytes following data, just use all data $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression']) || !$MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression']) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'WXXX' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'WXX') { // 4.3.2 WXX User defined URL link frame // There may be more than one 'WXXX' frame in each tag, // but only one with the same description // <Header for 'User defined URL link frame', ID: 'WXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // URL <text string> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding)); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } if ($frame_terminatorpos) { // there are null bytes after the data - this is not according to spec // only use data up to first null byte $frame_urldata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, $frame_terminatorpos); } else { // no null bytes following data, just use all data $frame_urldata = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['url'] = $frame_urldata; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($frame_name[0] == 'W') { // 4.3. W??? URL link frames // There may only be one URL link frame of its kind in a tag, // except when stated otherwise in the frame description // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' // described in 4.3.2.> // URL <text string> $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['url'] = trim($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3 && $frame_name == 'IPLS' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'IPL') { // 4.4 IPL Involved people list (ID3v2.2 only) // There may only be one 'IPL' frame in each tag // <Header for 'User defined URL link frame', ID: 'IPL'> // Text encoding $xx // People list strings <textstrings> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encoding'] = TextEncodingLookup('encoding', $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'MCDI' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'MCI') { // 4.5 MCI Music CD identifier // There may only be one 'MCDI' frame in each tag // <Header for 'Music CD identifier', ID: 'MCDI'> // CD TOC <binary data> $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); // no other special processing needed } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'ETCO' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'ETC') { // 4.6 ETC Event timing codes // There may only be one 'ETCO' frame in each tag // <Header for 'Event timing codes', ID: 'ETCO'> // Time stamp format $xx // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Followed by a list of key events in the following format: // Type of event $xx // Time stamp $xx (xx ...) // The 'Time stamp' is set to zero if directly at the beginning of the sound // or after the previous event. All events MUST be sorted in chronological order. $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); while ($frame_offset < strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['typeid'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['type'] = ETCOEventLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['typeid']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['timestamp'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'MLLT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'MLL') { // 4.7 MLL MPEG location lookup table // There may only be one 'MLLT' frame in each tag // <Header for 'Location lookup table', ID: 'MLLT'> // MPEG frames between reference $xx xx // Bytes between reference $xx xx xx // Milliseconds between reference $xx xx xx // Bits for bytes deviation $xx // Bits for milliseconds dev. $xx // Then for every reference the following data is included; // Deviation in bytes %xxx.... // Deviation in milliseconds %xxx.... $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framesbetweenreferences'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, 2)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bytesbetweenreferences'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 2, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['msbetweenreferences'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 5, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 8, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsformsdeviation'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 9, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 10); while ($frame_offset < strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $deviationbitstream .= BigEndian2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); } while (strlen($deviationbitstream)) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'])); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['msdeviation'] = bindec(substr($deviationbitstream, $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'], $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsformsdeviation'])); $deviationbitstream = substr($deviationbitstream, $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'] + $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsformsdeviation']); $frame_arrayindex++; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'SYTC' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'STC') { // 4.8 STC Synchronised tempo codes // There may only be one 'SYTC' frame in each tag // <Header for 'Synchronised tempo codes', ID: 'SYTC'> // Time stamp format $xx // Tempo data <binary data> // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); while ($frame_offset < strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['tempo'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['tempo'] == 255) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['tempo'] += ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['timestamp'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $frame_arrayindex++; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'USLT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'ULT') { // 4.9 ULT Unsynchronised lyric/text transcription // There may be more than one 'Unsynchronised lyrics/text transcription' frame // in each tag, but only one with the same language and content descriptor. // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> // Text encoding $xx // Language $xx xx xx // Content descriptor <text string according to encoding> $00 (00) // Lyrics/text <full text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['languagename'] = LanguageLookup($frame_language, FALSE); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'], $frame_textencoding); } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'], $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'SYLT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'SLT') { // 4.10 SLT Synchronised lyric/text // There may be more than one 'SYLT' frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> // Text encoding $xx // Language $xx xx xx // Time stamp format $xx // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Content type $xx // Content descriptor <text string according to encoding> $00 (00) // Terminated text to be synced (typically a syllable) // Sync identifier (terminator to above string) $00 (00) // Time stamp $xx (xx ...) $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contenttypeid'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contenttype'] = SYTLContentTypeLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contenttypeid']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['languagename'] = LanguageLookup($frame_language, FALSE); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $timestampindex = 0; $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_terminatorpos = strpos($frame_remainingdata, TextEncodingLookup('terminator', $frame_textencoding)); if ($frame_terminatorpos === FALSE) { $frame_remainingdata = ''; } else { if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['data'], $frame_textencoding); } $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) { // timestamp probably omitted for first data item } else { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['timestamp'] = BigEndian2Int(substr($frame_remainingdata, 0, 4)); $frame_remainingdata = substr($frame_remainingdata, 4); } $timestampindex++; } } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'COMM' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'COM') { // 4.11 COM Comments // There may be more than one comment frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Comment', ID: 'COMM'> // Text encoding $xx // Language $xx xx xx // Short content descrip. <text string according to encoding> $00 (00) // The actual text <full text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_text = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['languagename'] = LanguageLookup($frame_language, FALSE); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = $frame_text; if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($frame_text, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'RVA2') { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) // There may be more than one 'RVA2' frame in each tag, // but only one with the same identification string // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> // Identification <text string> $00 // The 'identification' string is used to identify the situation and/or // device where this adjustment should apply. The following is then // repeated for every channel: // Type of channel $xx // Volume adjustment $xx xx // Bits representing peak $xx // Peak volume $xx (xx ...) $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0)); $frame_idstring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, $frame_terminatorpos); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(chr(0))); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_idstring; while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_channeltypeid = substr($frame_remainingdata, $frame_offset++, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['channeltypeid'] = $frame_channeltypeid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['channeltype'] = RVA2ChannelTypeLookup($frame_channeltypeid); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['volumeadjust'] = BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2)) - 0x7fff; // 16-bit signed $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); $frame_bytespeakvolume = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_channeltypeid}"]['bitspeakvolume'] / 8); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['peakvolume'] = BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3 && $frame_name == 'RVAD' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'RVA') { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) // There may only be one 'RVA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'RVA'> // ID3v2.2 => Increment/decrement %000000ba // ID3v2.3 => Increment/decrement %00fedcba // Bits used for volume descr. $xx // Relative volume change, right $xx xx (xx ...) // a // Relative volume change, left $xx xx (xx ...) // b // Peak volume right $xx xx (xx ...) // Peak volume left $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, right back $xx xx (xx ...) // c // Relative volume change, left back $xx xx (xx ...) // d // Peak volume right back $xx xx (xx ...) // Peak volume left back $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, center $xx xx (xx ...) // e // Peak volume center $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, bass $xx xx (xx ...) // f // Peak volume bass $xx xx (xx ...) $frame_offset = 0; $frame_incrdecrflags = BigEndian2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsvolume'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_bytesvolume = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsvolume'] / 8); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['right'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['right'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['right'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['left'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['left'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['left'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['right'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['left'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']) > 0) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['rightrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['rightrear'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['rightrear'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['leftrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['leftrear'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['leftrear'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['rightrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['leftrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']) > 0) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['center'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['center'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['center'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['center'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']) > 0) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['bass'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['bass'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['bass'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['bass'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'EQU2') { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) // There may be more than one 'EQU2' frame in each tag, // but only one with the same identification string // <Header of 'Equalisation (2)', ID: 'EQU2'> // Interpolation method $xx // $00 Band // $01 Linear // Identification <text string> $00 // The following is then repeated for every adjustment point // Frequency $xx xx // Volume adjustment $xx xx $frame_offset = 0; $frame_interpolationmethod = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_idstring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_idstring; $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(chr(0))); while (strlen($frame_remainingdata)) { $frame_frequency = BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$frame_frequency}"] = BigEndian2Int(substr($frame_remainingdata, 2, 2)); $frame_remainingdata = substr($frame_remainingdata, 4); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['interpolationmethod'] = $frame_interpolationmethod; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3 && $frame_name == 'EQUA' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'EQU') { // 4.13 EQU Equalisation (ID3v2.2 only) // There may only be one 'EQUA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'EQU'> // Adjustment bits $xx // This is followed by 2 bytes + ('adjustment bits' rounded up to the // nearest byte) for every equalisation band in the following format, // giving a frequency range of 0 - 32767Hz: // Increment/decrement %x (MSB of the Frequency) // Frequency (lower 15 bits) // Adjustment $xx (xx ...) $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['adjustmentbits'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1); $frame_adjustmentbytes = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['adjustmentbits'] / 8); $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); while (strlen($frame_remainingdata)) { $frame_frequencystr = BigEndian2Bin(substr($frame_remainingdata, 0, 2)); $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['incdec'] = $frame_incdec; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['adjustment'] = BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['incdec'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['adjustment'] *= -1; } $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'RVRB' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'REV') { // 4.14 REV Reverb // There may only be one 'RVRB' frame in each tag. // <Header for 'Reverb', ID: 'RVRB'> // Reverb left (ms) $xx xx // Reverb right (ms) $xx xx // Reverb bounces, left $xx // Reverb bounces, right $xx // Reverb feedback, left to left $xx // Reverb feedback, left to right $xx // Reverb feedback, right to right $xx // Reverb feedback, right to left $xx // Premix left to right $xx // Premix right to left $xx $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['left'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['right'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bouncesL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bouncesR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackLL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackLR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackRR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackRL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['premixLR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['premixRL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'APIC' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'PIC') { // 4.15 PIC Attached picture // There may be several pictures attached to one file, // each in their individual 'APIC' frame, but only one // with the same content descriptor // <Header for 'Attached picture', ID: 'APIC'> // Text encoding $xx // ID3v2.3+ => MIME type <text string> $00 // ID3v2.2 => Image format $xx xx xx // Picture type $xx // Description <text string according to encoding> $00 (00) // Picture data <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { $frame_imagetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); if (strtolower($frame_imagetype) == 'ima') { // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoff@pacbell.net) $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); if ($frame_imagetype == 'JPEG') { $frame_imagetype = 'JPG'; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); } else { $frame_offset += 3; } } if ($MP3fileInfo['id3']['id3v2']['majorversion'] > 2) { $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); } $frame_picturetype = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['imagetype'] = $frame_imagetype; } else { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['mime'] = $frame_mimetype; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['picturetypeid'] = $frame_picturetype; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['picturetype'] = APICPictureTypeLookup($frame_picturetype); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); include_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php'; $imagechunkcheck = GetDataImageSize($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']); if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) { $imagetypes = array(1 => 'image/gif', 2 => 'image/jpeg', 3 => 'image/png'); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_mime'] = $imagetypes["{$imagechunkcheck[2]}"]; if ($imagechunkcheck[0]) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_width'] = $imagechunkcheck[0]; } if ($imagechunkcheck[1]) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_height'] = $imagechunkcheck[1]; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_bytes'] = strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']); //$MP3fileInfo['id3']['id3v2']["$frame_name"]["$frame_arrayindex"]['image_offset'] = $MP3fileInfo['id3']['id3v2']["$frame_name"]['dataoffset'] + $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)) + ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'GEOB' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'GEO') { // 4.16 GEO General encapsulated object // There may be more than one 'GEOB' frame in each tag, // but only one with the same content descriptor // <Header for 'General encapsulated object', ID: 'GEOB'> // Text encoding $xx // MIME type <text string> $00 // Filename <text string according to encoding> $00 (00) // Content description <text string according to encoding> $00 (00) // Encapsulated object <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_filename = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_filename) === 0) { $frame_filename = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['objectdata'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['mime'] = $frame_mimetype; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['filename'] = $frame_filename; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'PCNT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'CNT') { // 4.17 CNT Play counter // There may only be one 'PCNT' frame in each tag. // When the counter reaches all one's, one byte is inserted in // front of the counter thus making the counter eight bits bigger // <Header for 'Play counter', ID: 'PCNT'> // Counter $xx xx xx xx (xx ...) $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = BigEndian2Int($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'POPM' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'POP') { // 4.18 POP Popularimeter // There may be more than one 'POPM' frame in each tag, // but only one with the same email address // <Header for 'Popularimeter', ID: 'POPM'> // Email to user <text string> $00 // Rating $xx // Counter $xx xx xx xx (xx ...) $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_emailaddress = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_emailaddress) === 0) { $frame_emailaddress = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_rating = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['email'] = $frame_emailaddress; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['rating'] = $frame_rating; if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'RBUF' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'BUF') { // 4.19 BUF Recommended buffer size // There may only be one 'RBUF' frame in each tag // <Header for 'Recommended buffer size', ID: 'RBUF'> // Buffer size $xx xx xx // Embedded info flag %0000000x // Offset to next tag $xx xx xx xx $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['buffersize'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3)); $frame_offset += 3; $frame_embeddedinfoflags = BigEndian2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['nexttagoffset'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'CRM') { // 4.20 Encrypted meta frame (ID3v2.2 only) // There may be more than one 'CRM' frame in a tag, // but only one with the same 'owner identifier' // <Header for 'Encrypted meta frame', ID: 'CRM'> // Owner identifier <textstring> $00 (00) // Content/explanation <textstring> $00 (00) // Encrypted datablock <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = count($MP3fileInfo['id3']['id3v2']["{$frame_name}"]) - 1; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'AENC' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'CRA') { // 4.21 CRA Audio encryption // There may be more than one 'AENC' frames in a tag, // but only one with the same 'Owner identifier' // <Header for 'Audio encryption', ID: 'AENC'> // Owner identifier <text string> $00 // Preview start $xx xx // Preview length $xx xx // Encryption info <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid == ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['previewstart'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['previewlength'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encryptioninfo'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_ownerid}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'LINK' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'LNK') { // 4.22 LNK Linked information // There may be more than one 'LINK' frame in a tag, // but only one with the same contents // <Header for 'Linked information', ID: 'LINK'> // ID3v2.3+ => Frame identifier $xx xx xx xx // ID3v2.2 => Frame identifier $xx xx xx // URL <text string> $00 // ID and additional data <text string(s)> $frame_offset = 0; if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['frameid'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; } else { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['frameid'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4); $frame_offset += 4; } $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_url = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_url) === 0) { $frame_url = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['url'] = $frame_url; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['additionaldata'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'POSS') { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) // There may only be one 'POSS' frame in each tag // <Head for 'Position synchronisation', ID: 'POSS'> // Time stamp format $xx // Position $xx (xx ...) $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['position'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'USER') { // 4.22 USER Terms of use (ID3v2.3+ only) // There may be more than one 'Terms of use' frame in a tag, // but only one with the same 'Language' // <Header for 'Terms of use frame', ID: 'USER'> // Text encoding $xx // Language $xx xx xx // The actual text <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['languagename'] = LanguageLookup($frame_language, FALSE); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (!$MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['flags']['compression']) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['data'], $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'OWNE') { // 4.23 OWNE Ownership frame (ID3v2.3+ only) // There may only be one 'OWNE' frame in a tag // <Header for 'Ownership frame', ID: 'OWNE'> // Text encoding $xx // Price paid <text string> $00 // Date of purch. <text string> // Seller <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_pricepaid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['currency'] = LookupCurrency($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['currencyid'], 'units'); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['value'] = substr($frame_pricepaid, 3); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 8); if (!IsValidDateStampString($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedateunix'] = mktime(0, 0, 0, substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'], 4, 2), substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'], 6, 2), substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'], 0, 4)); } $frame_offset += 8; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['seller'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'COMR') { // 4.24 COMR Commercial frame (ID3v2.3+ only) // There may be more than one 'commercial frame' in a tag, // but no two may be identical // <Header for 'Commercial frame', ID: 'COMR'> // Text encoding $xx // Price string <text string> $00 // Valid until <text string> // Contact URL <text string> $00 // Received as $xx // Name of seller <text string according to encoding> $00 (00) // Description <text string according to encoding> $00 (00) // Picture MIME type <string> $00 // Seller logo <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_pricestring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_rawpricearray = explode('/', $frame_pricestring); foreach ($frame_rawpricearray as $key => $val) { $frame_currencyid = substr($val, 0, 3); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['price']["{$frame_currencyid}"]['currency'] = LookupCurrency($frame_currencyid, 'units'); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['price']["{$frame_currencyid}"]['value'] = substr($val, 3); } $frame_datestring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 8); $frame_offset += 8; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_contacturl = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_receivedasid = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_sellername = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_sellername) === 0) { $frame_sellername = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_sellerlogo = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['pricevaliduntil'] = $frame_datestring; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contacturl'] = $frame_contacturl; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['receivedasid'] = $frame_receivedasid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['receivedas'] = COMRReceivedAsLookup($frame_receivedasid); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['sellername'] = $frame_sellername; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciisellername'] = RoughTranslateUnicodeToASCII($frame_sellername, $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['mime'] = $frame_mimetype; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['logo'] = $frame_sellerlogo; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'ENCR') { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) // There may be several 'ENCR' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Encryption method registration', ID: 'ENCR'> // Owner identifier <text string> $00 // Method symbol $xx // Encryption data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['methodsymbol'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'GRID') { // 4.26 GRID Group identification registration (ID3v2.3+ only) // There may be several 'GRID' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Group ID registration', ID: 'GRID'> // Owner identifier <text string> $00 // Group symbol $xx // Group dependent data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['groupsymbol'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'PRIV') { // 4.27 PRIV Private frame (ID3v2.3+ only) // The tag may contain more than one 'PRIV' frame // but only with different contents // <Header for 'Private frame', ID: 'PRIV'> // Owner identifier <text string> $00 // The private data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'SIGN') { // 4.28 SIGN Signature frame (ID3v2.4+ only) // There may be more than one 'signature frame' in a tag, // but no two may be identical // <Header for 'Signature frame', ID: 'SIGN'> // Group symbol $xx // Signature <binary data> $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['groupsymbol'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'SEEK') { // 4.29 SEEK Seek frame (ID3v2.4+ only) // There may only be one 'seek frame' in a tag // <Header for 'Seek frame', ID: 'SEEK'> // Minimum offset to next tag $xx xx xx xx $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'ASPI') { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) // There may only be one 'audio seek point index' frame in a tag // <Header for 'Seek Point Index', ID: 'ASPI'> // Indexed data start (S) $xx xx xx xx // Indexed data length (L) $xx xx xx xx // Number of index points (N) $xx xx // Bits per index point (b) $xx // Then for every index point the following data is included: // Fraction at index (Fi) $xx (xx) $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datastart'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['indexeddatalength'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['indexpoints'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsperpoint'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_bytesperpoint = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsperpoint'] / 8); for ($i = 0; $i < $frame_indexpoints; $i++) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['indexes']["{$i}"] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesperpoint)); $frame_offset += $frame_bytesperpoint; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'RGAD') { // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html // There may only be one 'RGAD' frame in a tag // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> // Peak Amplitude $xx $xx $xx $xx // Radio Replay Gain Adjustment %aaabbbcd %dddddddd // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd // a - name code // b - originator code // c - sign bit // d - replay gain adjustment $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakamplitude'] = BigEndian2Float(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $radioadjustment = Dec2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $audiophileadjustment = Dec2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['name'] = Bin2Dec(substr($radioadjustment, 0, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['originator'] = Bin2Dec(substr($radioadjustment, 3, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['signbit'] = Bin2Dec(substr($radioadjustment, 6, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['adjustment'] = Bin2Dec(substr($radioadjustment, 7, 9)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['name'] = Bin2Dec(substr($audiophileadjustment, 0, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['originator'] = Bin2Dec(substr($audiophileadjustment, 3, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['signbit'] = Bin2Dec(substr($audiophileadjustment, 6, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['adjustment'] = Bin2Dec(substr($audiophileadjustment, 7, 9)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['radio']['name'] = RGADnameLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['name']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['radio']['originator'] = RGADoriginatorLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['originator']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['radio']['adjustment'] = RGADadjustmentLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['adjustment'], $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['signbit']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['audiophile']['name'] = RGADnameLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['name']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['audiophile']['originator'] = RGADoriginatorLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['originator']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['audiophile']['adjustment'] = RGADadjustmentLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['adjustment'], $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['signbit']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } return TRUE; }
} else { if ($id3info['id3']['id3v2']['majorversion'] > 2) { $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 ($id3info['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) } $frame_flags = BigEndian2Bin(substr($frame_header, 8, 2)); } } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName($frame_name, $id3info['id3']['id3v2']['majorversion'])) { $id3info['id3']['id3v2']["{$frame_name}"]['data'] = substr($framedata, 0, $frame_size); // in getid3.frames.php - this function does all the FrameID-level parsing ID3v2FrameProcessing($frame_name, $frame_flags, $id3info); if (isset($id3info['id3']['id3v2']['APIC'][0]['data']) && strlen($id3info['id3']['id3v2']['APIC'][0]['data']) > 0) { if (isset($rawdata)) { echo $id3info['id3']['id3v2']['APIC'][0]['data']; } else { include_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php'; $imagechunkcheck = GetDataImageSize($id3info['id3']['id3v2']['APIC'][0]['data']); switch ($imagechunkcheck[2]) { case 1: header('Content-type: image/gif');
function BigEndian2Float($byteword) { // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic // http://www.psc.edu/general/software/packages/ieee/ieee.html // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html $bitword = BigEndian2Bin($byteword); $signbit = $bitword[0]; if (strlen($byteword) == 4) { // 32-bit DWORD $exponentbits = 8; $fractionbits = 23; } else { if (strlen($byteword) == 8) { // 64-bit QWORD $exponentbits = 11; $fractionbits = 52; } else { return FALSE; } } $exponentstring = substr($bitword, 1, $exponentbits); $fractionstring = substr($bitword, 9, $fractionbits); $exponent = Bin2Dec($exponentstring); $fraction = Bin2Dec($fractionstring); if ($exponent == pow(2, $exponentbits) - 1 && $fraction != 0) { // Not a Number $floatvalue = FALSE; } else { if ($exponent == pow(2, $exponentbits) - 1 && $fraction == 0) { if ($signbit == '1') { $floatvalue = '-infinity'; } else { $floatvalue = '+infinity'; } } else { if ($exponent == 0 && $fraction == 0) { if ($signbit == '1') { $floatvalue = -0; } else { $floatvalue = 0; } $floatvalue = $signbit ? 0 : -0; } else { if ($exponent == 0 && $fraction != 0) { // These are 'unnormalized' values $floatvalue = pow(2, -1 * (pow(2, $exponentbits - 1) - 2)) * DecimalBinary2Float($fractionstring); if ($signbit == '1') { $floatvalue *= -1; } } else { if ($exponent != 0) { $floatvalue = pow(2, $exponent - (pow(2, $exponentbits - 1) - 1)) * (1 + DecimalBinary2Float($fractionstring)); if ($signbit == '1') { $floatvalue *= -1; } } } } } } return (double) $floatvalue; }
function getMPEGHeaderFilepointer(&$fd, &$MP3fileInfo) { // Start code 32 bits // horizontal frame size 12 bits // vertical frame size 12 bits // pixel aspect ratio 4 bits // frame rate 4 bits // bitrate 18 bits // marker bit 1 bit // VBV buffer size 10 bits // constrained parameter flag 1 bit // intra quant. matrix flag 1 bit // intra quant. matrix values 512 bits (present if matrix flag == 1) // non-intra quant. matrix flag 1 bit // non-intra quant. matrix values 512 bits (present if matrix flag == 1) if (!$fd) { $MP3fileInfo['error'] .= "\n" . 'Could not open file'; return FALSE; } else { // MPEG video information is found as $00 $00 $01 $B3 $matching_pattern = chr(0x0) . chr(0x0) . chr(0x1) . chr(0xb3); rewind($fd); $MPEGvideoHeader = fread($fd, FREAD_BUFFER_SIZE); $offset = 0; while (substr($MPEGvideoHeader, $offset++, 4) !== $matching_pattern) { if ($offset >= strlen($MPEGvideoHeader) - 12) { $MPEGvideoHeader .= fread($fd, FREAD_BUFFER_SIZE); $MPEGvideoHeader = substr($MPEGvideoHeader, $offset); $offset = 0; if (strlen($MPEGvideoHeader) < 12) { $MP3fileInfo['error'] = "\n" . 'Could not find start of video block before end of file'; return FALSE; // return array('error'=>'Could not find start of video block before end of file'); } else { if (ftell($fd) >= 100000) { $MP3fileInfo['error'] = "\n" . 'Could not find start of video block in the first 100,000 bytes (this might not be an MPEG-video file?)'; unset($MP3fileInfo['fileformat']); return FALSE; // return array('error'=>"\n".'Could not find start of video block in the first 100,000 bytes (this might not be an MPEG-video file?)', 'fileformat'=>''); } } } } $offset += strlen($matching_pattern) - 1; $framesizes = BigEndian2Bin(substr($MPEGvideoHeader, $offset, 3)); $offset += 3; $aspect_framerate = BigEndian2Bin(substr($MPEGvideoHeader, $offset, 1)); $offset += 1; $assortedinformation = BigEndian2Bin(substr($MPEGvideoHeader, $offset, 4)); $offset += 4; $MP3fileInfo['mpeg']['video']['raw']['framesize_horizontal'] = bindec(substr($framesizes, 0, 12)); // 12 bits for horizontal frame size $MP3fileInfo['mpeg']['video']['raw']['framesize_vertical'] = bindec(substr($framesizes, 12, 12)); // 12 bits for vertical frame size $MP3fileInfo['mpeg']['video']['framesize_horizontal'] = $MP3fileInfo['mpeg']['video']['raw']['framesize_horizontal']; $MP3fileInfo['mpeg']['video']['framesize_vertical'] = $MP3fileInfo['mpeg']['video']['raw']['framesize_vertical']; $MP3fileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = bindec(substr($aspect_framerate, 0, 4)); $MP3fileInfo['mpeg']['video']['raw']['frame_rate'] = bindec(substr($aspect_framerate, 4, 4)); $MP3fileInfo['mpeg']['video']['pixel_aspect_ratio'] = MPEGvideoAspectRatioLookup($MP3fileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $MP3fileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = MPEGvideoAspectRatioTextLookup($MP3fileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $MP3fileInfo['mpeg']['video']['frame_rate'] = MPEGvideoFramerateLookup($MP3fileInfo['mpeg']['video']['raw']['frame_rate']); $MP3fileInfo['mpeg']['video']['raw']['bitrate'] = bindec(substr($assortedinformation, 0, 18)); if ($MP3fileInfo['mpeg']['video']['raw']['bitrate'] == 0x3ffff) { // 18 set bits $MP3fileInfo['mpeg']['video']['bitrate_type'] = 'variable'; } else { $MP3fileInfo['mpeg']['video']['bitrate_type'] = 'constant'; $MP3fileInfo['mpeg']['video']['bitrate_bps'] = $MP3fileInfo['mpeg']['video']['raw']['bitrate'] * 400; //$MP3fileInfo['mpeg']['video']['bitrate_kbytes'] = $MP3fileInfo['mpeg']['video']['bitrate_bps'] / 8192; } $MP3fileInfo['mpeg']['video']['raw']['marker_bit'] = bindec(substr($assortedinformation, 18, 1)); $MP3fileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = bindec(substr($assortedinformation, 19, 10)); $MP3fileInfo['mpeg']['video']['raw']['constrained_param_flag'] = bindec(substr($assortedinformation, 29, 1)); $MP3fileInfo['mpeg']['video']['raw']['intra_quant_flag'] = bindec(substr($assortedinformation, 30, 1)); return TRUE; } }
function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) { $offset = 0; $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = BigEndian2Int(substr($METAdataBlockData, $offset, 2)); $offset += 2; $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = BigEndian2Int(substr($METAdataBlockData, $offset, 3)); $offset += 3; $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = BigEndian2Int(substr($METAdataBlockData, $offset, 3)); $offset += 3; $SampleRateChannelsSampleBitsStreamSamples = BigEndian2Bin(substr($METAdataBlockData, $offset, 8)); $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20)); $ThisFileInfo['flac']['STREAMINFO']['channels'] = Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1; $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1; $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36)); $offset += 8; $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16); $offset += 16; if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) { $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels']; $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; } else { $ThisFileInfo['error'] .= "\n" . 'Corrupt METAdata block: STREAMINFO'; return false; } return true; }
function ParseOggPageHeader(&$fd) { // http://xiph.org/ogg/vorbis/doc/framing.html $basefileoffset = ftell($fd); // where we started from in the file $filedata = fread($fd, FREAD_BUFFER_SIZE); $filedataoffset = 0; while (substr($filedata, $filedataoffset++, 4) != 'OggS') { if (ftell($fd) - $basefileoffset >= 10000) { // should be found before here return FALSE; } if (strlen($filedata) < 1024) { if (feof($fd) || ($filedata .= fread($fd, FREAD_BUFFER_SIZE)) === FALSE) { // get some more data, unless eof, in which case fail return FALSE; } } } $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS' $stream_structure_version = ord(substr($filedata, $filedataoffset++, 1)); $header_type_flag = BigEndian2Bin(substr($filedata, $filedataoffset++, 1)); $header_type_flag_fresh = IntString2Bool($header_type_flag[5]); // fresh packet $header_type_flag_bos = IntString2Bool($header_type_flag[6]); // first page of logical bitstream (bos) $header_type_flag_eos = IntString2Bool($header_type_flag[7]); // last page of logical bitstream (eos) $pcm_absolute_position = LittleEndian2Int(substr($filedata, $filedataoffset, 8)); $filedataoffset += 8; $stream_serial_number = LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; $page_seq_no = LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; $page_checksum = LittleEndian2Int(substr($filedata, $filedataoffset, 4)); $filedataoffset += 4; $page_segments = LittleEndian2Int(substr($filedata, $filedataoffset, 1)); $filedataoffset += 1; for ($i = 0; $i < $page_segments; $i++) { $segment_table["{$i}"] = LittleEndian2Int(substr($filedata, $filedataoffset++, 1)); } $packettype = ord(substr($filedata, $filedataoffset, 1)); $filedataoffset += 1; $streamtype = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis' $filedataoffset += 6; fseek($fd, $filedataoffset + $basefileoffset, SEEK_SET); $oggheader['packet_type'] = $packettype; $oggheader['stream_type'] = $streamtype; $oggheader['stream_structver'] = $stream_structure_version; $oggheader['flag']['fresh'] = $header_type_flag_fresh; $oggheader['flag']['bos'] = $header_type_flag_fresh; $oggheader['flag']['eos'] = $header_type_flag_fresh; $oggheader['pcm_abs_position'] = $pcm_absolute_position; $oggheader['stream_serialno'] = $stream_serial_number; $oggheader['page_seqno'] = $page_seq_no; $oggheader['page_checksum'] = $page_checksum; $oggheader['page_segments'] = $page_segments; $oggheader['segment_table'] = $segment_table; return $oggheader; }
function GetAllMP3info($filename, $assumedFormat = '', $allowedFormats = array('zip', 'ogg', 'riff', 'sdss', 'mpeg', 'midi', 'image', 'mp3')) { include_once GETID3_INCLUDEPATH . 'getid3.lookup.php'; // Lookup tables include_once GETID3_INCLUDEPATH . 'getid3.functions.php'; // Function library include_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php'; if (!file_exists($filename)) { // this code segment is needed for the file browser demonstrated in check.php // but may interfere with finding a filename that actually does contain apparently // escaped characters (like "file\'name.mp3") and/or // %xx-format characters (like "file%20name.mp3") $filename = stripslashes($filename); if (!file_exists($filename)) { $filename = rawurldecode($filename); } } $fp = @fopen($filename, 'rb'); $MP3fileInfo['getID3version'] = GETID3VERSION; $MP3fileInfo['exist'] = (bool) $fp; $MP3fileInfo['filename'] = basename($filename); $MP3fileInfo['fileformat'] = ''; // filled in later $MP3fileInfo['error'] = ''; // filled in later, unset if not used if ($MP3fileInfo['exist']) { if (strpos($filename, 'http://') !== FALSE || strpos($filename, 'ftp://') !== FALSE) { // remote file - copy locally first and work from there $localfilepointer = tmpfile(); while ($buffer = fread($fp, FREAD_BUFFER_SIZE)) { $MP3fileInfo['filesize'] += fwrite($localfilepointer, $buffer); } fclose($fp); } else { clearstatcache(); $MP3fileInfo['filesize'] = filesize($filename); $localfilepointer = $fp; } rewind($localfilepointer); $formattest = fread($localfilepointer, FREAD_BUFFER_SIZE); if (ParseAsThisFormat('zip', $assumedFormat, $allowedFormats, $formattest)) { $MP3fileInfo['fileformat'] = 'zip'; include_once GETID3_INCLUDEPATH . 'getid3.zip.php'; getZipHeaderFilepointer($filename, $MP3fileInfo); } else { if (ParseAsThisFormat('ogg', $assumedFormat, $allowedFormats, $formattest)) { $MP3fileInfo['fileformat'] = 'ogg'; include_once GETID3_INCLUDEPATH . 'getid3.ogg.php'; getOggHeaderFilepointer($localfilepointer, $MP3fileInfo); } else { if (ParseAsThisFormat('riff', $assumedFormat, $allowedFormats, $formattest) || ParseAsThisFormat('sdss', $assumedFormat, $allowedFormats, $formattest)) { $MP3fileInfo['fileformat'] = 'riff'; include_once GETID3_INCLUDEPATH . 'getid3.riff.php'; getRIFFHeaderFilepointer($localfilepointer, $MP3fileInfo); } else { if (ParseAsThisFormat('mpeg', $assumedFormat, $allowedFormats, $formattest)) { $MP3fileInfo['fileformat'] = 'mpg'; include_once GETID3_INCLUDEPATH . 'getid3.mpeg.php'; getMPEGHeaderFilepointer($localfilepointer, $MP3fileInfo); } else { if (ParseAsThisFormat('midi', $assumedFormat, $allowedFormats, $formattest)) { $MP3fileInfo['fileformat'] = 'midi'; include_once GETID3_INCLUDEPATH . 'getid3.midi.php'; if ($assumedFormat === FALSE) { // do not parse all MIDI tracks - much faster getMIDIHeaderFilepointer($localfilepointer, $MP3fileInfo, FALSE); } else { getMIDIHeaderFilepointer($localfilepointer, $MP3fileInfo); } } else { if (in_array('image', $allowedFormats) && ($assumedFormat == 'image' || ($imagechunkcheck = GetDataImageSize($formattest)) && $imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3)) { if ($assumedFormat == 'image') { $imagechunkcheck = GetDataImageSize($formattest); } $imagetypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); if (isset($imagechunkcheck[2]) && $imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) { $MP3fileInfo['fileformat'] = $imagetypes["{$imagechunkcheck[2]}"]; $MP3fileInfo["{$MP3fileInfo['fileformat']}"]['width'] = $imagechunkcheck[0]; $MP3fileInfo["{$MP3fileInfo['fileformat']}"]['height'] = $imagechunkcheck[1]; } else { unset($MP3fileInfo['fileformat']); $MP3fileInfo['error'] = "\n" . 'Not a supported image format'; } } else { if (in_array('mp3', $allowedFormats) && $allowedFormats !== FALSE && ($assumedFormat == 'mp3' || $assumedFormat == '' && (substr($formattest, 0, 3) == 'ID3' || substr(BigEndian2Bin(substr($formattest, 0, 2)), 0, 11) == '11111111111'))) { // assume MP3 format include_once GETID3_INCLUDEPATH . 'getid3.mp3.php'; getMP3headerFilepointer($localfilepointer, $MP3fileInfo); if (!isset($MP3fileInfo['audiodataoffset'])) { $MP3fileInfo['audiobytes'] = 0; } else { $MP3fileInfo['audiobytes'] = $MP3fileInfo['filesize'] - $MP3fileInfo['audiodataoffset']; } if (isset($MP3fileInfo['id3']['id3v1'])) { $MP3fileInfo['audiobytes'] -= 128; } if (isset($mp3info['lyrics3']['raw']['lyrics3tagsize'])) { $MP3fileInfo['audiobytes'] -= $mp3info['lyrics3']['raw']['lyrics3tagsize']; } if ($MP3fileInfo['audiobytes'] < 0) { unset($MP3fileInfo['audiobytes']); } if (isset($MP3fileInfo['audiobytes']) && isset($MP3fileInfo['bitrate']) && $MP3fileInfo['bitrate'] > 0) { $MP3fileInfo['playtime_seconds'] = $MP3fileInfo['audiobytes'] * 8 / $MP3fileInfo['bitrate']; } } } } } } } } } if (isset($MP3fileInfo['playtime_seconds']) && $MP3fileInfo['playtime_seconds'] > 0 && !isset($MP3fileInfo['playtime_string'])) { $contentseconds = round(($MP3fileInfo['playtime_seconds'] / 60 - floor($MP3fileInfo['playtime_seconds'] / 60)) * 60); $contentminutes = floor($MP3fileInfo['playtime_seconds'] / 60); $MP3fileInfo['playtime_string'] = number_format($contentminutes) . ':' . str_pad($contentseconds, 2, 0, STR_PAD_LEFT); } if (isset($MP3fileInfo['error']) && !$MP3fileInfo['error']) { unset($MP3fileInfo['error']); } if (isset($MP3fileInfo['fileformat']) && !$MP3fileInfo['fileformat']) { unset($MP3fileInfo['fileformat']); } unset($SourceArrayKey); if (isset($MP3fileInfo['id3']['id3v2'])) { $SourceArrayKey = $MP3fileInfo['id3']['id3v2']; } else { if (isset($MP3fileInfo['id3']['id3v1'])) { $SourceArrayKey = $MP3fileInfo['id3']['id3v1']; } else { if (isset($MP3fileInfo['ogg'])) { $SourceArrayKey = $MP3fileInfo['ogg']; } else { if (isset($MP3fileInfo['RIFF'])) { $SourceArrayKey = $MP3fileInfo['RIFF']; } } } } if (isset($SourceArrayKey)) { $handyaccesskeystocopy = array('title', 'artist', 'album', 'year', 'genre', 'comment', 'track'); foreach ($handyaccesskeystocopy as $keytocopy) { if (isset($SourceArrayKey["{$keytocopy}"])) { $MP3fileInfo["{$keytocopy}"] = $SourceArrayKey["{$keytocopy}"]; } } } if (isset($fp) && is_resource($fp) && get_resource_type($fp) == 'file') { fclose($fp); } if (isset($localfilepointer) && is_resource($localfilepointer) && get_resource_type($localfilepointer) == 'file') { fclose($localfilepointer); } if (isset($fp)) { unset($fp); } if (isset($localfilepointer)) { unset($localfilepointer); } return $MP3fileInfo; }
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 GetAllFileInfo($filename, $assumedFormat = '', $MD5file = false, $MD5data = false) { require_once GETID3_INCLUDEPATH . 'getid3.functions.php'; // Function library $ThisFileInfo['getID3version'] = GETID3VERSION; $ThisFileInfo['fileformat'] = ''; // filled in later $ThisFileInfo['audio']['dataformat'] = ''; // filled in later, unset if not used $ThisFileInfo['video']['dataformat'] = ''; // filled in later, unset if not used $ThisFileInfo['tags'] = array(); // filled in later, unset if not used $ThisFileInfo['error'] = ''; // filled in later, unset if not used $ThisFileInfo['warning'] = ''; // filled in later, unset if not used $ThisFileInfo['exist'] = false; if (eregi('^(ht|f)tp://', $filename)) { // remote file $ThisFileInfo['filename'] = $filename; $ThisFileInfo['error'] .= "\n" . 'Remote files are not supported in this version of getID3() - please copy the file locally first'; } else { // local file $ThisFileInfo['filename'] = basename($filename); $ThisFileInfo['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); $ThisFileInfo['filenamepath'] = $ThisFileInfo['filepath'] . '/' . $ThisFileInfo['filename']; ob_start(); if ($localfilepointer = fopen($filename, 'rb')) { $ThisFileInfo['exist'] = true; //$ThisFileInfo['filesize'] = filesize($ThisFileInfo['filenamepath']); // PHP doesn't support integers larger than 31-bit (~2GB) // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer fseek($localfilepointer, 0, SEEK_END); $ThisFileInfo['filesize'] = ftell($localfilepointer); ob_end_clean(); if ($ThisFileInfo['filesize'] == 0) { if (filesize($ThisFileInfo['filenamepath']) != 0) { unset($ThisFileInfo['filesize']); $ThisFileInfo['error'] .= "\n" . 'File is most likely larger than 2GB and is not supported by PHP'; } // remove unneeded/meaningless keys CleanUpGetAllMP3info($ThisFileInfo); // close & remove local filepointer CloseRemoveFilepointer($localfilepointer); return $ThisFileInfo; } } else { $ThisFileInfo['error'] .= "\n" . 'Error opening file: ' . trim(strip_tags(ob_get_contents())); ob_end_clean(); // remove unneeded/meaningless keys CleanUpGetAllMP3info($ThisFileInfo); // close & remove local filepointer CloseRemoveFilepointer($localfilepointer); return $ThisFileInfo; } } // Initialize avdataoffset and avdataend $ThisFileInfo['avdataoffset'] = 0; $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize']; // Handle APE tags HandleAPETag($localfilepointer, $ThisFileInfo); rewind($localfilepointer); //$formattest = fread($localfilepointer, 16); // 16 bytes is sufficient for any format except ISO CD-image $formattest = fread($localfilepointer, 32774); // (ISO needs at least 32774 bytes) // Handle ID3v2 tag if (substr($formattest, 0, 3) == 'ID3') { HandleID3v2Tag($localfilepointer, $ThisFileInfo); rewind($localfilepointer); fseek($localfilepointer, $ThisFileInfo['avdataoffset'], SEEK_SET); $formattest = fread($localfilepointer, 32774); // (ISO9660 needs at least 32774 bytes) } // Handle ID3v1 tags HandleID3v1Tag($localfilepointer, $ThisFileInfo); // Nothing-but-tags check if ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] > 0) { if ($DeterminedFormat = GetFileFormat($formattest)) { // break if ID3/APE tags found on illegal format if (!$DeterminedFormat['allowtags'] && $ThisFileInfo['avdataoffset'] > 0 && $ThisFileInfo['avdataend'] != $ThisFileInfo['filesize']) { $ThisFileInfo['error'] .= "\n" . 'Illegal ID3 and/or APE tag found on non multimedia file.'; break; } // set mime type $ThisFileInfo['mime_type'] = $DeterminedFormat['mimetype']; // supported format signature pattern detected require_once GETID3_INCLUDEPATH . $DeterminedFormat['include']; switch ($DeterminedFormat['format']) { //case 'midi': // if ($assumedFormat === false) { // // do not parse all MIDI tracks - much faster // getMIDIHeaderFilepointer($localfilepointer, $ThisFileInfo, false); // } else { // getMIDIHeaderFilepointer($localfilepointer, $ThisFileInfo); // } // break; //case 'aac': // if (!getAACADIFheaderFilepointer($localfilepointer, $ThisFileInfo)) { // $dummy = $ThisFileInfo; // unset($dummy['error']); // if (getAACADTSheaderFilepointer($localfilepointer, $dummy)) { // $ThisFileInfo = $dummy; // } // } // break; default: $VariableFunctionName = $DeterminedFormat['function']; $VariableFunctionName($localfilepointer, $ThisFileInfo); break; } } elseif ($assumedFormat == 'mp3' || $assumedFormat == '' && (substr($formattest, 0, 3) == 'ID3' || substr(BigEndian2Bin(substr($formattest, 0, 2)), 0, 11) == '11111111111')) { // assume AAC-ADTS format require_once GETID3_INCLUDEPATH . 'getid3.aac.php'; $dummy = $ThisFileInfo; if (getAACADTSheaderFilepointer($localfilepointer, $dummy)) { $ThisFileInfo = $dummy; } else { // it's not AAC-ADTS format, probably MP3 require_once GETID3_INCLUDEPATH . 'getid3.mp3.php'; getMP3headerFilepointer($localfilepointer, $ThisFileInfo); } } else { // unknown format, do nothing } } if (isset($ThisFileInfo['fileformat'])) { // Calculate combined bitrate - audio + video $CombinedBitrate = 0; $CombinedBitrate += isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0; $CombinedBitrate += isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0; if ($CombinedBitrate > 0 && !isset($ThisFileInfo['bitrate'])) { $ThisFileInfo['bitrate'] = $CombinedBitrate; } // Set playtime string if (!empty($ThisFileInfo['playtime_seconds']) && empty($ThisFileInfo['playtime_string'])) { $ThisFileInfo['playtime_string'] = PlaytimeString($ThisFileInfo['playtime_seconds']); } if (!empty($ThisFileInfo['audio']['channels'])) { switch ($ThisFileInfo['audio']['channels']) { case 1: $ThisFileInfo['audio']['channelmode'] = 'mono'; break; case 2: $ThisFileInfo['audio']['channelmode'] = 'stereo'; break; default: // unknown? break; } } } // Get the MD5 hash of the entire file if ($MD5file && empty($ThisFileInfo['md5_file'])) { $ThisFileInfo['md5_file'] = md5_file($filename); } // Get the MD5 hash of the audio/video portion of the file // (without ID3/APE/Lyrics3/etc header/footer tags if ($MD5data && empty($ThisFileInfo['md5_data'])) { getMD5data($ThisFileInfo); } // return tags data in alphabetical order, without duplicates $ThisFileInfo['tags'] = array_unique($ThisFileInfo['tags']); sort($ThisFileInfo['tags']); // remove unneeded/meaningless keys CleanUpGetAllMP3info($ThisFileInfo); // close & remove local filepointer CloseRemoveFilepointer($localfilepointer); return $ThisFileInfo; }
function getMPEGHeaderFilepointer(&$fd, &$ThisFileInfo) { $ThisFileInfo['fileformat'] = 'mpeg'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])); $MPEGstreamDataLength = strlen($MPEGstreamData); $foundVideo = true; $VideoChunkOffset = 0; while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== MPEG_VIDEO_SEQUENCE_HEADER) { if ($VideoChunkOffset >= $MPEGstreamDataLength) { $foundVideo = false; break 2; } } if ($foundVideo) { // Start code 32 bits // horizontal frame size 12 bits // vertical frame size 12 bits // pixel aspect ratio 4 bits // frame rate 4 bits // bitrate 18 bits // marker bit 1 bit // VBV buffer size 10 bits // constrained parameter flag 1 bit // intra quant. matrix flag 1 bit // intra quant. matrix values 512 bits (present if matrix flag == 1) // non-intra quant. matrix flag 1 bit // non-intra quant. matrix values 512 bits (present if matrix flag == 1) $ThisFileInfo['video']['dataformat'] = 'mpeg'; // I don't know how to differentiate between MPEG-1 and MPEG-2 video stream // Any information appreciated: info@getid3.org //$ThisFileInfo['video']['codec'] = 'MPEG-1'; //$ThisFileInfo['video']['codec'] = 'MPEG-2'; $ThisFileInfo['video']['codec'] = 'MPEG'; $VideoChunkOffset += strlen(MPEG_VIDEO_SEQUENCE_HEADER) - 1; $FrameSizeDWORD = BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3)); $VideoChunkOffset += 3; $AspectRatioFrameRateDWORD = BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1)); $VideoChunkOffset += 1; $assortedinformation = BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4)); $VideoChunkOffset += 4; $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xfff000) >> 12; // 12 bits for horizontal frame size $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = $FrameSizeDWORD & 0xfff; // 12 bits for vertical frame size $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xf0) >> 4; $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = $AspectRatioFrameRateDWORD & 0xf; $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal']; $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']; $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['frame_rate'] = MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']); $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = Bin2Dec(substr($assortedinformation, 0, 18)); $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = Bin2Dec(substr($assortedinformation, 18, 1)); $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = Bin2Dec(substr($assortedinformation, 19, 10)); $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = Bin2Dec(substr($assortedinformation, 29, 1)); $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = Bin2Dec(substr($assortedinformation, 30, 1)); if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3ffff) { // 18 set bits $ThisFileInfo['warning'] .= "\n" . 'This version of getID3() [' . GETID3VERSION . '] cannot determine average bitrate of VBR MPEG video files'; $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr'; } else { $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400; $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr'; $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; } $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal']; $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical']; $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate']; $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; $ThisFileInfo['video']['lossless'] = false; $ThisFileInfo['video']['bits_per_sample'] = 24; } else { $ThisFileInfo['error'] .= "\n" . 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'; } $AudioChunkOffset = 0; while (true) { while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== MPEG_AUDIO_START) { if ($AudioChunkOffset >= $MPEGstreamDataLength) { break 2; } } require_once GETID3_INCLUDEPATH . 'getid3.mp3.php'; for ($i = 0; $i <= 2; $i++) { // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it 9 bytes, some 10 bytes after // I have no idea why or what the difference is, so this is a stupid hack. // If anybody has any better idea of what's going on, please let me know - info@getid3.org $dummy = $ThisFileInfo; if (decodeMPEGaudioHeader($fd, $AudioChunkOffset + 3 + 8 + $i, $dummy, false)) { $ThisFileInfo = $dummy; $ThisFileInfo['audio']['bits_per_sample'] = 16; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['lossless'] = false; break 2; } } } 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; }