wp/wp-includes/ID3/module.audio.mp3.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
--- a/wp/wp-includes/ID3/module.audio.mp3.php	Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/ID3/module.audio.mp3.php	Fri Sep 05 18:40:08 2025 +0200
@@ -315,6 +315,10 @@
 			$encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
 		}
 
+		if (isset($thisfile_mpeg_audio['bitrate']) && $thisfile_mpeg_audio['bitrate'] === 'free') {
+			$encoder_options .= ' --freeformat';
+		}
+
 		if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
 			$encoder_options .= ' --nogap';
 		}
@@ -750,7 +754,8 @@
 							unset($thisfile_mpeg_audio_lame['long_version']);
 
 							// It the LAME tag was only introduced in LAME v3.90
-							// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
+							// https://wiki.hydrogenaud.io/index.php/LAME#VBR_header_and_LAME_tag
+							// https://hydrogenaud.io/index.php?topic=9933
 
 							// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
 							// are assuming a 'Xing' identifier offset of 0x24, which is the case for
@@ -786,7 +791,7 @@
 							$thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
 
 							// bytes $A7-$AE  Replay Gain
-							// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
+							// https://web.archive.org/web/20021015212753/http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
 							// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
 							if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
 								// LAME 3.94a16 and later - 9.23 fixed point
@@ -914,7 +919,7 @@
 
 
 							// LAME CBR
-							if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
+							if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1 && $thisfile_mpeg_audio['bitrate'] !== 'free') {
 
 								$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 								$thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
@@ -1169,6 +1174,7 @@
 
 			$nextframetestarray = array('error' => array(), 'warning' => array(), 'avdataend' => $info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
 			if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
+				/** @phpstan-ignore-next-line */
 				getid3_lib::safe_inc($info['mp3_validity_check_bitrates'][$nextframetestarray['mpeg']['audio']['bitrate']]);
 				if ($ScanAsCBR) {
 					// force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
@@ -1374,11 +1380,11 @@
 							$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] = isset($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]) ? ++$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] : 1;
 							$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] = isset($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]) ? ++$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] : 1;
 							if (++$frames_scanned >= $max_frames_scan) {
-								$pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
+								$pct_data_scanned = getid3_lib::SafeDiv($this->ftell() - $info['avdataoffset'], $info['avdataend'] - $info['avdataoffset']);
 								$this->warning('too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.');
 								foreach ($Distribution as $key1 => $value1) {
 									foreach ($value1 as $key2 => $value2) {
-										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
+										$Distribution[$key1][$key2] = $pct_data_scanned ? round($value2 / $pct_data_scanned) : 1;
 									}
 								}
 								break;
@@ -1469,7 +1475,7 @@
 		$SyncSeekAttemptsMax = 1000;
 		$FirstFrameThisfileInfo = null;
 		while ($SynchSeekOffset < $sync_seek_buffer_size) {
-			if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {
+			if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !$this->feof()) {
 
 				if ($SynchSeekOffset > $sync_seek_buffer_size) {
 					// if a synch's not found within the first 128k bytes, then give up
@@ -1484,20 +1490,6 @@
 						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;
 				}
 			}
 
@@ -1646,7 +1638,7 @@
 								}
 								$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']);
+									$this_pct_scanned = getid3_lib::SafeDiv($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;
@@ -1737,6 +1729,10 @@
 
 		}
 		$info['audio']['channels']        = $info['mpeg']['audio']['channels'];
+		if ($info['audio']['channels'] < 1) {
+			$this->error('Corrupt MP3 file: no channels');
+			return false;
+		}
 		$info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
 		$info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
 		return true;