Syntax Syntax
Parameters Parameters
- $avdataoffset
-
(Required)
- $BitrateHistogram
-
(Optional)
Default value: false
Return Return
(bool)
Source Source
File: wp-includes/ID3/module.audio.mp3.php
$info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); $info['fileformat'] = $info['audio']['dataformat']; return true; } /** * @param int $avdataoffset * @param bool $BitrateHistogram * * @return bool */ public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) { // looks for synch, decodes MPEG audio header $info = &$this->getid3->info; static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; if (empty($MPEGaudioVersionLookup)) { $MPEGaudioVersionLookup = self::MPEGaudioVersionArray(); $MPEGaudioLayerLookup = self::MPEGaudioLayerArray(); $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray(); } $this->fseek($avdataoffset); $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); if ($sync_seek_buffer_size <= 0) { $this->error('Invalid $sync_seek_buffer_size at offset '.$avdataoffset); return false; } $header = $this->fread($sync_seek_buffer_size); $sync_seek_buffer_size = strlen($header); $SynchSeekOffset = 0; while ($SynchSeekOffset < $sync_seek_buffer_size) { if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) { if ($SynchSeekOffset > $sync_seek_buffer_size) { // if a synch's not found within the first 128k bytes, then give up $this->error('Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'); if (isset($info['audio']['bitrate'])) { unset($info['audio']['bitrate']); } if (isset($info['mpeg']['audio'])) { unset($info['mpeg']['audio']); } if (empty($info['mpeg'])) { unset($info['mpeg']); } return false; } elseif (feof($this->getid3->fp)) { $this->error('Could not find valid MPEG audio synch before end of file'); if (isset($info['audio']['bitrate'])) { unset($info['audio']['bitrate']); } if (isset($info['mpeg']['audio'])) { unset($info['mpeg']['audio']); } if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) { unset($info['mpeg']); } return false; } } if (($SynchSeekOffset + 1) >= strlen($header)) { $this->error('Could not find valid MPEG synch before end of file'); return false; } if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // synch detected $FirstFrameAVDataOffset = null; if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) { $FirstFrameThisfileInfo = $info; $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $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 = $info; // only overwrite real data if valid header found if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) { $info = $dummy; $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset; switch (isset($info['fileformat']) ? $info['fileformat'] : '') { case '': case 'id3': case 'ape': case 'mp3': $info['fileformat'] = 'mp3'; $info['audio']['dataformat'] = 'mp3'; break; } if (isset($FirstFrameThisfileInfo) && isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { if (!(abs($info['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. $info = $FirstFrameThisfileInfo; $info['avdataoffset'] = $FirstFrameAVDataOffset; $info['fileformat'] = 'mp3'; $info['audio']['dataformat'] = 'mp3'; $dummy = $info; unset($dummy['mpeg']['audio']); $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) { $info = $dummy; $info['avdataoffset'] = $GarbageOffsetEnd; $this->warning('apparently-valid VBR header not used because could not find '.GETID3_MP3_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 { $this->warning('using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'); } } } if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) { // VBR file with no VBR header $BitrateHistogram = true; } if ($BitrateHistogram) { $info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); if ($info['mpeg']['audio']['version'] == '1') { if ($info['mpeg']['audio']['layer'] == 3) { $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); } elseif ($info['mpeg']['audio']['layer'] == 2) { $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); } elseif ($info['mpeg']['audio']['layer'] == 1) { $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); } } elseif ($info['mpeg']['audio']['layer'] == 1) { $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); } else { $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); } $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); $synchstartoffset = $info['avdataoffset']; $this->fseek($info['avdataoffset']); // you can play with these numbers: $max_frames_scan = 50000; $max_scan_segments = 10; // don't play with these numbers: $FastMode = false; $SynchErrorsFound = 0; $frames_scanned = 0; $this_scan_segment = 0; $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments); $pct_data_scanned = 0; for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) { $frames_scanned_this_segment = 0; if ($this->ftell() >= $info['avdataend']) { break; } $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments))); if ($current_segment > 0) { $this->fseek($scan_start_offset[$current_segment]); $buffer_4k = $this->fread(4096); for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) { if (($buffer_4k[$j] == "\xFF") && ($buffer_4k[($j + 1)] > "\xE0")) { // synch detected if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) { $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength']; if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) { $scan_start_offset[$current_segment] += $j; break; } } } } } $synchstartoffset = $scan_start_offset[$current_segment]; while (($synchstartoffset < $info['avdataend']) && $this->decodeMPEGaudioHeader($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']]; if (empty($dummy['mpeg']['audio']['framelength'])) { $SynchErrorsFound++; $synchstartoffset++; } else { getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]); getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]); getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]); $synchstartoffset += $dummy['mpeg']['audio']['framelength']; } $frames_scanned++; if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) { $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']); if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) { // file likely contains < $max_frames_scan, just scan as one segment $max_scan_segments = 1; $frames_scan_per_segment = $max_frames_scan; } else { $pct_data_scanned += $this_pct_scanned; break; } } } } if ($pct_data_scanned > 0) { $this->warning('too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'); foreach ($info['mpeg']['audio'] as $key1 => $value1) { if (!preg_match('#_distribution$#i', $key1)) { continue; } foreach ($value1 as $key2 => $value2) { $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned); } } } if ($SynchErrorsFound > 0) { $this->warning('Found '.$SynchErrorsFound.' synch errors in histogram analysis'); //return false; } $bittotal = 0; $framecounter = 0; foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { $framecounter += $bitratecount; if ($bitratevalue != 'free') { $bittotal += ($bitratevalue * $bitratecount); } } if ($framecounter == 0) { $this->error('Corrupt MP3 file: framecounter == zero'); return false; } $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter); $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently $distinct_bitrates = 0; foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { if ($bitrate_count > 0) { $distinct_bitrates++; } } if ($distinct_bitrates > 1) { $info['mpeg']['audio']['bitrate_mode'] = 'vbr'; } else { $info['mpeg']['audio']['bitrate_mode'] = 'cbr'; } $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode']; } break; // exit while() } } $SynchSeekOffset++; if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) { // end of file/data if (empty($info['mpeg']['audio'])) { $this->error('could not find valid MPEG synch before end of file'); if (isset($info['audio']['bitrate'])) { unset($info['audio']['bitrate']); } if (isset($info['mpeg']['audio'])) { unset($info['mpeg']['audio']); } if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) { unset($info['mpeg']); }