From 2d027093ebc8aa41ae4016959752c2b1e7395062 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sun, 22 Mar 2026 02:21:35 +0300 Subject: [PATCH] Update to getID3 1.9.25 --- src/wp-includes/ID3/getid3.lib.php | 24 +-- src/wp-includes/ID3/getid3.php | 8 +- .../ID3/module.audio-video.matroska.php | 176 ++++++++++++++++-- .../ID3/module.audio-video.quicktime.php | 87 +++++++-- .../ID3/module.audio-video.riff.php | 13 +- src/wp-includes/ID3/module.audio.flac.php | 4 +- src/wp-includes/ID3/module.audio.mp3.php | 2 +- src/wp-includes/ID3/module.tag.id3v1.php | 3 + src/wp-includes/ID3/module.tag.id3v2.php | 46 ++--- 9 files changed, 287 insertions(+), 76 deletions(-) diff --git a/src/wp-includes/ID3/getid3.lib.php b/src/wp-includes/ID3/getid3.lib.php index 58865110fcff1..911876eed6ad4 100644 --- a/src/wp-includes/ID3/getid3.lib.php +++ b/src/wp-includes/ID3/getid3.lib.php @@ -19,6 +19,11 @@ } } +// Available since PHP 7.0 (2015-Dec-03 https://www.php.net/ChangeLog-7.php) +if (!defined('PHP_INT_MIN')) { + define('PHP_INT_MIN', ~PHP_INT_MAX); +} + class getid3_lib { /** @@ -74,7 +79,7 @@ public static function trunc($floatnumber) { /** * @param int|null $variable * @param-out int $variable - * @param int $increment + * @param int $increment * * @return bool */ @@ -113,21 +118,8 @@ public static function CastAsInt($floatnum) { * @return bool */ public static function intValueSupported($num) { - // check if integers are 64-bit - static $hasINT64 = null; - if ($hasINT64 === null) { // 10x faster than is_null() - /** @var int|float|object $bigInt */ - $bigInt = pow(2, 31); - $hasINT64 = is_int($bigInt); // 32-bit int are limited to (2^31)-1 - if (!$hasINT64 && !defined('PHP_INT_MIN')) { - define('PHP_INT_MIN', ~PHP_INT_MAX); - } - } - // if integers are 64-bit - no other check required - if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) { - return true; - } - return false; + // really should be <= and >= but trying "(int)9.2233720368548E+18" results in PHP warning "The float 9.2233720368548E+18 is not representable as an int, cast occurred" + return (($num < PHP_INT_MAX) && ($num > PHP_INT_MIN)); } /** diff --git a/src/wp-includes/ID3/getid3.php b/src/wp-includes/ID3/getid3.php index 2f827bc775f1a..121ff2faa24a5 100644 --- a/src/wp-includes/ID3/getid3.php +++ b/src/wp-includes/ID3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.24-202509040923'; + const VERSION = '1.9.25-202603080933'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; @@ -1951,6 +1951,12 @@ public function ChannelsBitratePlaytimeCalculations() { if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); } + + // Look up codec name if fourcc is set but codec is not + if (!empty($this->info['video']['fourcc']) && !isset($this->info['video']['codec'])) { + $this->include_module('audio-video.riff'); + $this->info['video']['codec'] = getid3_riff::fourccLookup($this->info['video']['fourcc']); + } } /** diff --git a/src/wp-includes/ID3/module.audio-video.matroska.php b/src/wp-includes/ID3/module.audio-video.matroska.php index eb5febf4336cc..ccb70904e458e 100644 --- a/src/wp-includes/ID3/module.audio-video.matroska.php +++ b/src/wp-includes/ID3/module.audio-video.matroska.php @@ -210,6 +210,19 @@ define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block. +/** + * Matroska constants + */ +define('MATROSKA_DEFAULT_TIMECODESCALE', 1000000); + +/** + * Matroska scan modes are internal state flags for how much of the file we are scanning + */ +define('MATROSKA_SCAN_HEADER', 0); +define('MATROSKA_SCAN_WHOLE_FILE', 1); +define('MATROSKA_SCAN_FIRST_CLUSTER', 2); +define('MATROSKA_SCAN_LAST_CLUSTER', 3); + /** * @tutorial http://www.matroska.org/technical/specs/index.html * @@ -241,6 +254,7 @@ class getid3_matroska extends getid3_handler private $EBMLbuffer_length = 0; private $current_offset = 0; private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID); + private $scan_mode = MATROSKA_SCAN_HEADER; /** * @return bool @@ -248,6 +262,7 @@ class getid3_matroska extends getid3_handler public function Analyze() { $info = &$this->getid3->info; + $this->scan_mode = $this->parse_whole_file ? MATROSKA_SCAN_WHOLE_FILE : MATROSKA_SCAN_HEADER; // parse container try { @@ -256,14 +271,25 @@ public function Analyze() $this->error('EBML parser: '.$e->getMessage()); } - // calculate playtime - if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { - foreach ($info['matroska']['info'] as $key => $infoarray) { - if (isset($infoarray['Duration'])) { - // TimecodeScale is how many nanoseconds each Duration unit is - $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000); - break; - } + $this->playtimeFromMetadata($info); + + // If there was no duration metadata, this might be an incomplete file or a streaming file + // We need Cluster information so we can use their timecodes to estimate playtime. + if (!isset($info['playtime_seconds']) && !$this->parse_whole_file) { + // Scan the start and end of file for Clusters to estimate duration + $this->scanStartEndForClusters($info); + } + + if (isset($info['matroska']['cluster']) && is_array($info['matroska']['cluster'])) { + if (!isset($info['playtime_seconds']) && !empty($info['matroska']['cluster'])) { + // estimate playtime using clusters if not yet known + $this->calculatePlaytimeFromClusters($info); + } + + // Remove cluster information from output if hide_clusters is true + // These could have been set during scanStartEndForClusters() + if ($this->hide_clusters) { + unset($info['matroska']['cluster']); } } @@ -332,7 +358,11 @@ public function Analyze() break;*/ } - $info['video']['streams'][$trackarray['TrackUID']] = $track_info; + if (isset($trackarray['TrackUID'])) { + $info['video']['streams'][$trackarray['TrackUID']] = $track_info; + } else { + $this->warning('Missing mandatory TrackUID for video track'); + } break; case 2: // Audio @@ -480,8 +510,11 @@ public function Analyze() $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"'); break; } - - $info['audio']['streams'][$trackarray['TrackUID']] = $track_info; + if (isset($trackarray['TrackUID'])) { + $info['audio']['streams'][$trackarray['TrackUID']] = $track_info; + } else { + $this->warning('Missing mandatory TrackUID for audio track'); + } break; } } @@ -1246,12 +1279,17 @@ private function parseEBML(&$info) { } $this->current_offset = $subelement['end']; } - if (!$this->hide_clusters) { + + if (!$this->hide_clusters || $this->playtimeFromMetadata($info) === false) { $info['matroska']['cluster'][] = $cluster_entry; } + if ($this->scan_mode === MATROSKA_SCAN_FIRST_CLUSTER) { + // Stop parsing after finding first cluster + return; + } // check to see if all the data we need exists already, if so, break out of the loop - if (!$this->parse_whole_file) { + if ($this->scan_mode === MATROSKA_SCAN_HEADER) { if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) { @@ -1919,4 +1957,116 @@ private static function getDefaultStreamInfo($streams) return $info; } + /** + * @param array $info + * + * @return float|bool Duration when present in metadata or false + */ + private function playtimeFromMetadata(&$info) { + if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { + foreach ($info['matroska']['info'] as $infoarray) { + if (isset($infoarray['Duration'])) { + // TimecodeScale is how many nanoseconds each Duration unit is + $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : MATROSKA_DEFAULT_TIMECODESCALE) / 1000000000); + return $info['playtime_seconds']; + } + } + } + return false; + } + + /** + * @param int $offset New starting offset for the buffer + * + * @return void + */ + private function resetParserBuffer($offset) { + $this->current_offset = $offset; + $this->EBMLbuffer = ''; + $this->EBMLbuffer_offset = 0; + $this->EBMLbuffer_length = 0; + } + + /** + * Scan start and end of file for cluster information when Duration is missing + * Only use this if no Duration was found in the Info element and we are not in parse_whole_file mode + * + * @param array $info + * + * @return void + */ + private function scanStartEndForClusters(&$info) { + // Scan beginning of file for first cluster + $this->resetParserBuffer($info['avdataoffset']); + $this->scan_mode = MATROSKA_SCAN_FIRST_CLUSTER; + + try { + $this->parseEBML($info); + } catch (Exception $e) { + $this->error('EBML parser (start of file): '.$e->getMessage()); + } + + // Scan end of file for last cluster + if (is_array($info['matroska']['cluster']) && !empty($info['matroska']['cluster'])) { + // Scan maximum 1MB window before EOF + $this->resetParserBuffer(max(0, $info['avdataend'] - (1024 * 1024))); + $this->scan_mode = MATROSKA_SCAN_LAST_CLUSTER; + + try { + $this->parseEBML($info); + } catch (Exception $e) { + $this->error('EBML parser (end of file): '.$e->getMessage()); + } + } + + // Reset to header parsing mode (this method is only called during header-only parsing) + $this->scan_mode = MATROSKA_SCAN_HEADER; + } + + /** + * Fetch TimecodeScale from Info element + * + * @param array $info + * + * @return int TimecodeScale value + */ + private function getTimecodeScale(&$info) { + $timecodeScale = MATROSKA_DEFAULT_TIMECODESCALE; + if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { + foreach ($info['matroska']['info'] as $infoarray) { + if (isset($infoarray['TimecodeScale'])) { + $timecodeScale = $infoarray['TimecodeScale']; + break; + } + } + } + return $timecodeScale; + } + + /** + * Calculate duration from scanned cluster timecodes + * + * @param array $info + * + * @return void + */ + private function calculatePlaytimeFromClusters(&$info) { + $minTimecode = null; + $maxTimecode = null; + if (isset($info['matroska']['cluster']) && is_array($info['matroska']['cluster'])) { + foreach ($info['matroska']['cluster'] as $cluster) { + if (isset($cluster['ClusterTimecode'])) { + if ($minTimecode === null || $cluster['ClusterTimecode'] < $minTimecode) { + $minTimecode = $cluster['ClusterTimecode']; + } + if ($maxTimecode === null || $cluster['ClusterTimecode'] > $maxTimecode) { + $maxTimecode = $cluster['ClusterTimecode']; + } + } + } + } + if ($maxTimecode !== null && $minTimecode !== null && $maxTimecode > $minTimecode) { + $info['playtime_seconds'] = ($maxTimecode - $minTimecode) * ($this->getTimecodeScale($info) / 1000000000); + } + } } diff --git a/src/wp-includes/ID3/module.audio-video.quicktime.php b/src/wp-includes/ID3/module.audio-video.quicktime.php index 301501f703043..7598e0a616dc2 100644 --- a/src/wp-includes/ID3/module.audio-video.quicktime.php +++ b/src/wp-includes/ID3/module.audio-video.quicktime.php @@ -190,6 +190,7 @@ public function Analyze() { } if ($ISO6709parsed['latitude'] === false) { $this->warning('location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug'); + unset($info['quicktime']['comments']['location.ISO6709']); } break; } @@ -472,6 +473,9 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset } else { // Apple item list box atom handler $atomoffset = 0; +// todo (2025-10-16): 0x10B5 is probably Packed ISO639-2/T language code so this code block is likely incorrect +// need to locate sample file to figure out what is going on here and why this code was written as such +// https://developer.apple.com/documentation/quicktime-file-format/language_code_values if (substr($atom_data, 2, 2) == "\x10\xB5") { // not sure what it means, but observed on iPhone4 data. // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data @@ -643,7 +647,9 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset } } } - $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); + if (!empty($atom_structure['data'])) { // https://github.com/JamesHeinrich/getID3/issues/477#issuecomment-3723356688 + $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); + } break; @@ -904,7 +910,7 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset $info['fileformat'] = 'mp4'; $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; if ($this->QuicktimeVideoCodecLookup($info['video']['fourcc'])) { - $info['video']['fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($info['video']['fourcc']); + $info['video']['codec'] = $this->QuicktimeVideoCodecLookup($info['video']['fourcc']); } // https://www.getid3.org/phpBB3/viewtopic.php?t=1550 @@ -956,6 +962,11 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset } break; + case 'keys': + // 2025-Oct-17 probably something to do with this but I haven't found clear documentation explaining what I'm seeing, ignoring for now + // https://developer.apple.com/documentation/quicktime-file-format/metadata_key_declaration_atom/ + break; + default: switch ($atom_structure['sample_description_table'][$i]['data_format']) { case 'mp4s': @@ -1753,17 +1764,28 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset break; case 'data': // metaDATA atom - // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data - $atom_structure['language'] = substr($atom_data, 4 + 0, 2); - $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); - $atom_structure['data'] = substr($atom_data, 4 + 4); + // seems to be 2 bytes language code (ASCII), 2 bytes language code (probably packed ISO639-2/T), remainder is useful data + $atom_structure['lang2'] = substr($atom_data, 4 + 0, 2); + $atom_structure['lang3'] = $this->QuicktimeLanguageLookup(getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2))); + $atom_structure['data'] = substr($atom_data, 4 + 4); $atom_structure['key_name'] = (isset($info['quicktime']['temp_meta_key_names'][$this->metaDATAkey]) ? $info['quicktime']['temp_meta_key_names'][$this->metaDATAkey] : ''); $this->metaDATAkey++; switch ($atom_structure['key_name']) { case 'com.android.capture.fps': + case 'com.apple.quicktime.live-photo.vitality-score': $atom_structure['data'] = getid3_lib::BigEndian2Float($atom_structure['data']); break; + case 'com.apple.quicktime.camera.focal_length.35mm_equivalent': + case 'com.apple.quicktime.live-photo.auto': + case 'com.apple.quicktime.live-photo.vitality-scoring-version': + case 'com.apple.quicktime.full-frame-rate-playback-intent': + $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_structure['data']); + break; + + case 'com.apple.quicktime.location.accuracy.horizontal': + $atom_structure['data'] = (float) $atom_structure['data']; // string representing float value e.g. "14.989691" + break; } if ($atom_structure['key_name'] && $atom_structure['data']) { @@ -1779,13 +1801,15 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); $keys_atom_offset = 8; + + $keys_index_base = (!empty($info['quicktime']['temp_meta_key_names']) ? count($info['quicktime']['temp_meta_key_names']) : 0); // file may contain multiple "keys" entries, starting index should be culmulative not reset to 1 on each set; https://github.com/JamesHeinrich/getID3/issues/452 for ($i = 1; $i <= $atom_structure['entry_count']; $i++) { - $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); - $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); - $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8); - $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace + $atom_structure['keys'][($keys_index_base + $i)]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); + $atom_structure['keys'][($keys_index_base + $i)]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); + $atom_structure['keys'][($keys_index_base + $i)]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][($keys_index_base + $i)]['key_size'] - 8); + $keys_atom_offset += $atom_structure['keys'][($keys_index_base + $i)]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace - $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value']; + $info['quicktime']['temp_meta_key_names'][($keys_index_base + $i)] = $atom_structure['keys'][($keys_index_base + $i)]['key_value']; } break; @@ -2231,6 +2255,34 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset $esds_offset += $atom_structure['ES_SLConfigDescrTagSize']; break; + case 'sgpd': // https://developer.apple.com/documentation/quicktime-file-format/sample_group_description_atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); // hardcoded: 0x00 + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x000000 + $sgpd_offset = 4; + + $atom_structure['grouping_type'] = getid3_lib::BigEndian2Int(substr($atom_data, $sgpd_offset, 4)); + $sgpd_offset += 4; + $atom_structure['default_length'] = getid3_lib::BigEndian2Int(substr($atom_data, $sgpd_offset, 4)); + $sgpd_offset += 4; + $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sgpd_offset, 4)); + $sgpd_offset += 4; + $atom_structure['payload_data_raw'] = substr($atom_data, $sgpd_offset); + break; + + case 'sbgp': // https://developer.apple.com/documentation/quicktime-file-format/sample-to-group_atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); // hardcoded: 0x00 + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x000000 + $sbgp_offset = 4; + + $atom_structure['grouping_type'] = getid3_lib::BigEndian2Int(substr($atom_data, $sbgp_offset, 4)); + $sbgp_offset += 4; + $atom_structure['default_length'] = getid3_lib::BigEndian2Int(substr($atom_data, $sbgp_offset, 4)); + $sbgp_offset += 4; + $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sbgp_offset, 4)); + $sbgp_offset += 4; + $atom_structure['table_data_raw'] = substr($atom_data, $sbgp_offset); + break; + // AVIF-related - https://docs.rs/avif-parse/0.13.2/src/avif_parse/boxes.rs.html case 'pitm': // Primary ITeM case 'iloc': // Item LOCation @@ -2332,6 +2384,7 @@ public function quicktime_read_mp4_descr_length($data, &$offset) { */ public function QuicktimeLanguageLookup($languageid) { // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353 + // https://developer.apple.com/documentation/quicktime-file-format/language_code_values static $QuicktimeLanguageLookup = array(); if (empty($QuicktimeLanguageLookup)) { $QuicktimeLanguageLookup[0] = 'English'; @@ -3047,11 +3100,13 @@ public function Pascal2String($pascalstring) { public function MaybePascal2String($pascalstring) { // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string // Check if string actually is in this format or written incorrectly, straight string, or null-terminated string - if (ord(substr($pascalstring, 0, 1)) == (strlen($pascalstring) - 1)) { - return substr($pascalstring, 1); - } elseif (substr($pascalstring, -1, 1) == "\x00") { - // appears to be null-terminated instead of Pascal-style - return substr($pascalstring, 0, -1); + if (strlen($pascalstring) > 0) { + if (ord(substr($pascalstring, 0, 1)) == (strlen($pascalstring) - 1)) { + return substr($pascalstring, 1); + } elseif (substr($pascalstring, -1, 1) == "\x00") { + // appears to be null-terminated instead of Pascal-style + return substr($pascalstring, 0, -1); + } } return $pascalstring; } diff --git a/src/wp-includes/ID3/module.audio-video.riff.php b/src/wp-includes/ID3/module.audio-video.riff.php index b911e20e1e890..64e4ce52f327c 100644 --- a/src/wp-includes/ID3/module.audio-video.riff.php +++ b/src/wp-includes/ID3/module.audio-video.riff.php @@ -1398,7 +1398,6 @@ public function Analyze() { if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) { - $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); $thisfile_audio['bitrate'] = 0; $thisfile_video['bitrate'] = $info['bitrate']; @@ -1737,9 +1736,11 @@ public function ParseRIFF($startoffset, $maxoffset) { $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__); $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false); - if (empty($getid3_temp->info['error'])) { - $info['audio'] = $getid3_temp->info['audio']; - $info['mpeg'] = $getid3_temp->info['mpeg']; + if (!empty($getid3_temp->info['mpeg']['audio']['bitrate']) && ($getid3_temp->info['mpeg']['audio']['bitrate'] != 'free')) { // if it detects as "free" bitrate then it's almost certainly a false-match MP3 sync, ignore + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['mpeg'] = $getid3_temp->info['mpeg']; + } } unset($getid3_temp, $getid3_mp3); } @@ -2825,6 +2826,10 @@ public static function fourccLookup($fourcc) { VLV1 VideoLogic/PURE Digital Videologic Capture VP30 On2 VP3.0 VP31 On2 VP3.1 + VP50 On2 VP5 + VP60 On2 VP6 + VP70 On2 VP7 + VP80 On2 VP8 VP6F On2 TrueMotion VP6 VX1K Lucent VX1000S Video Codec VX2K Lucent VX2000S Video Codec diff --git a/src/wp-includes/ID3/module.audio.flac.php b/src/wp-includes/ID3/module.audio.flac.php index 014061da943c2..b788b7708a166 100644 --- a/src/wp-includes/ID3/module.audio.flac.php +++ b/src/wp-includes/ID3/module.audio.flac.php @@ -168,10 +168,10 @@ public function parseMETAdata() { $info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset']; } $info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8); - if ($info['flac']['uncompressed_audio_bytes'] == 0) { + if ($info['flac']['uncompressed_audio_bytes'] == 0 && $info['flac']['STREAMINFO']['samples_stream'] > 0) { return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero'); } - if (!empty($info['flac']['compressed_audio_bytes'])) { + if (!empty($info['flac']['compressed_audio_bytes']) && $info['flac']['STREAMINFO']['samples_stream'] > 0) { $info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes']; } } diff --git a/src/wp-includes/ID3/module.audio.mp3.php b/src/wp-includes/ID3/module.audio.mp3.php index 11fbbde2cc66e..bc414c3911a18 100644 --- a/src/wp-includes/ID3/module.audio.mp3.php +++ b/src/wp-includes/ID3/module.audio.mp3.php @@ -1178,7 +1178,7 @@ public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsC $nextframetestarray = array('error' => array(), 'warning' => array(), 'avdataend' => $info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) { - getid3_lib::safe_inc($info['mp3_validity_check_bitrates'][$nextframetestarray['mpeg']['audio']['bitrate']]); + getid3_lib::safe_inc($info['mp3_validity_check_bitrates'][intval($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 if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) { diff --git a/src/wp-includes/ID3/module.tag.id3v1.php b/src/wp-includes/ID3/module.tag.id3v1.php index 442aefe35cf77..3a7ae0e2bc148 100644 --- a/src/wp-includes/ID3/module.tag.id3v1.php +++ b/src/wp-includes/ID3/module.tag.id3v1.php @@ -29,6 +29,9 @@ public function Analyze() { if (!getid3_lib::intValueSupported($info['filesize'])) { $this->warning('Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'); return false; + } elseif ($info['filesize'] < 128) { + $this->warning('Unable to check for ID3v1 because file is too small'); + return false; } if($info['filesize'] < 256) { diff --git a/src/wp-includes/ID3/module.tag.id3v2.php b/src/wp-includes/ID3/module.tag.id3v2.php index 5748476be899a..981d4d442d4c8 100644 --- a/src/wp-includes/ID3/module.tag.id3v2.php +++ b/src/wp-includes/ID3/module.tag.id3v2.php @@ -679,7 +679,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_textencoding_terminator = "\x00"; } $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -771,7 +771,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_textencoding_terminator = "\x00"; } $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['encodingid'] = $frame_textencoding; @@ -998,7 +998,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1062,7 +1062,7 @@ public function ParseID3v2Frame(&$parsedFrame) { if ($frame_terminatorpos === false) { $frame_remainingdata = ''; } else { - if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1108,7 +1108,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1148,7 +1148,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); - if (ord($frame_idstring) === 0) { + if ($frame_idstring === "\x00") { $frame_idstring = ''; } $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); @@ -1279,7 +1279,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_idstring) === 0) { + if ($frame_idstring === "\x00") { $frame_idstring = ''; } $parsedFrame['description'] = $frame_idstring; @@ -1385,7 +1385,7 @@ public function ParseID3v2Frame(&$parsedFrame) { // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_mimetype) === 0) { + if ($frame_mimetype === "\x00") { $frame_mimetype = ''; } $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); @@ -1400,7 +1400,7 @@ public function ParseID3v2Frame(&$parsedFrame) { if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_mimetype) === 0) { + if ($frame_mimetype === "\x00") { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1412,7 +1412,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $this->warning('data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset)); } else { $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1519,23 +1519,23 @@ public function ParseID3v2Frame(&$parsedFrame) { } $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_mimetype) === 0) { + if ($frame_mimetype === "\x00") { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_filename) === 0) { + if ($frame_filename === "\x00") { $frame_filename = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1574,7 +1574,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_emailaddress) === 0) { + if ($frame_emailaddress === "\x00") { $frame_emailaddress = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1639,7 +1639,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { + if ($frame_ownerid === "\x00") { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1673,7 +1673,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_url) === 0) { + if ($frame_url === "\x00") { $frame_url = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1803,17 +1803,17 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_sellername) === 0) { + if ($frame_sellername === "\x00") { $frame_sellername = ''; } $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) { + if (substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1) === "\x00") { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); @@ -1851,7 +1851,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { + if ($frame_ownerid === "\x00") { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1874,7 +1874,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { + if ($frame_ownerid === "\x00") { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00"); @@ -1894,7 +1894,7 @@ public function ParseID3v2Frame(&$parsedFrame) { $frame_offset = 0; $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { + if ($frame_ownerid === "\x00") { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen("\x00");