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 getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram = false) { // looks for synch, decodes MPEG audio header fseek($fd, $avdataoffset, SEEK_SET); $header = ''; $SynchSeekOffset = 0; if (!defined('CONST_FF')) { define('CONST_FF', chr(0xff)); define('CONST_E0', chr(0xe0)); } static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; if (empty($MPEGaudioVersionLookup)) { $MPEGaudioVersionLookup = MPEGaudioVersionArray(); $MPEGaudioLayerLookup = MPEGaudioLayerArray(); $MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); } $header_len = strlen($header) - round(FREAD_BUFFER_SIZE / 2); while (true) { if ($SynchSeekOffset > $header_len && $avdataoffset + $SynchSeekOffset < $ThisFileInfo['avdataend'] && !feof($fd)) { if ($SynchSeekOffset > 131072) { // if a synch's not found within the first 128k bytes, then give up $ThisFileInfo['error'] .= "\n" . 'could not find valid MPEG synch within the first 131072 bytes'; if (isset($ThisFileInfo['audio']['bitrate'])) { unset($ThisFileInfo['audio']['bitrate']); } if (isset($ThisFileInfo['mpeg']['audio'])) { unset($ThisFileInfo['mpeg']['audio']); } if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || count($ThisFileInfo['mpeg']) == 0)) { unset($ThisFileInfo['mpeg']); } return false; } elseif ($header .= fread($fd, FREAD_BUFFER_SIZE)) { // great $header_len = strlen($header) - round(FREAD_BUFFER_SIZE / 2); } else { $ThisFileInfo['error'] .= "\n" . 'could not find valid MPEG synch before end of file'; if (isset($ThisFileInfo['audio']['bitrate'])) { unset($ThisFileInfo['audio']['bitrate']); } if (isset($ThisFileInfo['mpeg']['audio'])) { unset($ThisFileInfo['mpeg']['audio']); } if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || count($ThisFileInfo['mpeg']) == 0)) { unset($ThisFileInfo['mpeg']); } return false; } } if ($SynchSeekOffset + 1 >= strlen($header)) { $ThisFileInfo['error'] .= "\n" . 'could not find valid MPEG synch before end of file'; return false; } if ($header[$SynchSeekOffset] == CONST_FF && $header[$SynchSeekOffset + 1] > CONST_E0) { // synch detected if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) { $FirstFrameThisfileInfo = $ThisFileInfo; $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; if (!decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) { // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below unset($FirstFrameThisfileInfo); } } $dummy = $ThisFileInfo; // only overwrite real data if valid header found if (decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) { $ThisFileInfo = $dummy; $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset; switch ($ThisFileInfo['fileformat']) { case '': case 'id3': case 'ape': case 'mp3': $ThisFileInfo['fileformat'] = 'mp3'; $ThisFileInfo['audio']['dataformat'] = 'mp3'; } if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && $FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') { if (!CloseMatch($ThisFileInfo['audio']['bitrate'], $FirstFrameThisfileInfo['audio']['bitrate'], 1)) { // If there is garbage data between a valid VBR header frame and a sequence // of valid MPEG-audio frames the VBR data is no longer discarded. $ThisFileInfo = $FirstFrameThisfileInfo; $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset; $ThisFileInfo['fileformat'] = 'mp3'; $ThisFileInfo['audio']['dataformat'] = 'mp3'; $dummy = $ThisFileInfo; unset($dummy['mpeg']['audio']); $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; if (decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { $ThisFileInfo = $dummy; $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd; $ThisFileInfo['warning'] .= "\n" . 'apparently-valid VBR header not used because could not find ' . MPEG_VALID_CHECK_FRAMES . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for ' . ($GarbageOffsetEnd - $GarbageOffsetStart) . ' bytes between ' . $GarbageOffsetStart . ' and ' . $GarbageOffsetEnd . '), but did find valid CBR stream starting at ' . $GarbageOffsetEnd; } else { $ThisFileInfo['warning'] .= "\n" . 'using data from VBR header even though could not find ' . MPEG_VALID_CHECK_FRAMES . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for ' . ($GarbageOffsetEnd - $GarbageOffsetStart) . ' bytes between ' . $GarbageOffsetStart . ' and ' . $GarbageOffsetEnd . ')'; } } } if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && $ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr' && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) { // VBR file with no VBR header $BitrateHistogram = true; } if ($BitrateHistogram) { $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo' => 0, 'joint stereo' => 0, 'dual channel' => 0, 'mono' => 0); $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1' => 0, '2' => 0, '2.5' => 0); if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['layer'] == 'III') { $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32 => 0, 40 => 0, 48 => 0, 56 => 0, 64 => 0, 80 => 0, 96 => 0, 112 => 0, 128 => 0, 160 => 0, 192 => 0, 224 => 0, 256 => 0, 320 => 0); } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'II') { $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32 => 0, 48 => 0, 56 => 0, 64 => 0, 80 => 0, 96 => 0, 112 => 0, 128 => 0, 160 => 0, 192 => 0, 224 => 0, 256 => 0, 320 => 0, 384 => 0); } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32 => 0, 64 => 0, 96 => 0, 128 => 0, 160 => 0, 192 => 0, 224 => 0, 256 => 0, 288 => 0, 320 => 0, 352 => 0, 384 => 0, 416 => 0, 448 => 0); } } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32 => 0, 48 => 0, 56 => 0, 64 => 0, 80 => 0, 96 => 0, 112 => 0, 128 => 0, 144 => 0, 160 => 0, 176 => 0, 192 => 0, 224 => 0, 256 => 0); } else { $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 8 => 0, 16 => 0, 24 => 0, 32 => 0, 40 => 0, 48 => 0, 56 => 0, 64 => 0, 80 => 0, 96 => 0, 112 => 0, 128 => 0, 144 => 0, 160 => 0); } $dummy = array('error' => $ThisFileInfo['error'], 'warning' => $ThisFileInfo['warning'], 'avdataend' => $ThisFileInfo['avdataend'], 'avdataoffset' => $ThisFileInfo['avdataoffset']); $synchstartoffset = $ThisFileInfo['avdataoffset']; $FastMode = false; while (decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) { $FastMode = true; $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; $ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++; $ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++; $ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++; if (empty($dummy['mpeg']['audio']['framelength'])) { $ThisFileInfo['warning'] .= "\n" . 'Invalid/missing framelength in histogram analysis - aborting'; $synchstartoffset += 4; // return false; } $synchstartoffset += $dummy['mpeg']['audio']['framelength']; } $bittotal = 0; $framecounter = 0; foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { $framecounter += $bitratecount; if ($bitratevalue != 'free') { $bittotal += $bitratevalue * $bitratecount; } } if ($framecounter == 0) { $ThisFileInfo['error'] .= "\n" . 'Corrupt MP3 file: framecounter == zero'; return false; } $ThisFileInfo['mpeg']['audio']['frame_count'] = $framecounter; $ThisFileInfo['mpeg']['audio']['bitrate'] = 1000 * ($bittotal / $framecounter); $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently $distinct_bitrates = 0; foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { if ($bitrate_count > 0) { $distinct_bitrates++; } } if ($distinct_bitrates > 1) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; } else { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; } $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; } break; // exit while() } } $SynchSeekOffset++; if ($avdataoffset + $SynchSeekOffset >= $ThisFileInfo['avdataend']) { // end of file/data if (empty($ThisFileInfo['mpeg']['audio'])) { $ThisFileInfo['error'] .= "\n" . 'could not find valid MPEG synch before end of file'; if (isset($ThisFileInfo['audio']['bitrate'])) { unset($ThisFileInfo['audio']['bitrate']); } if (isset($ThisFileInfo['mpeg']['audio'])) { unset($ThisFileInfo['mpeg']['audio']); } if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) { unset($ThisFileInfo['mpeg']); } return false; } break; } } $ThisFileInfo['audio']['bits_per_sample'] = 16; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; return true; }
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; }