From 71ddc92e8b42670f941ad93815c62f9fb30fcd7f Mon Sep 17 00:00:00 2001 From: StudioMaX Date: Wed, 22 Dec 2021 22:22:42 +0600 Subject: [PATCH 01/24] Fix a free format mp3 files --- getid3/module.audio.mp3.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/getid3/module.audio.mp3.php b/getid3/module.audio.mp3.php index 992124f0..3d8a9442 100644 --- a/getid3/module.audio.mp3.php +++ b/getid3/module.audio.mp3.php @@ -315,6 +315,10 @@ public function GuessEncoderOptions() { $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 @@ public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $S 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 @@ public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $S $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 @@ public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $S // 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']); From 8ca97be906c0f6bbe5bdf5c1d881fa9ac3e09e7a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 22 Dec 2021 15:13:50 -0800 Subject: [PATCH 02/24] Work around divide by zero bug in MPEG-2 aspect ratio In 9ce8040f I introduced a divide-by-zero bug into the aspect ratio handling for MPEG-2 videos. In production at Wikipedia we've seen a few attempted uploads for files that trigger a divide by zero on the width, which either means corrupt files or something's wrong elsewhere. Unfortunately our logging doesn't include the files being uploaded, so I don't have a test case to analyze. But this workaround will at least avoid a fatal error that can't be caught. On our bug tracker: https://phabricator.wikimedia.org/T289189 --- getid3/module.audio-video.mpeg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getid3/module.audio-video.mpeg.php b/getid3/module.audio-video.mpeg.php index 0504da19..6d6b1cc5 100644 --- a/getid3/module.audio-video.mpeg.php +++ b/getid3/module.audio-video.mpeg.php @@ -615,7 +615,7 @@ public static function videoAspectRatioLookup($rawaspectratio, $mpeg_version=1, 2 => array(0, 1, 1.3333, 1.7778, 2.2100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), ); $ratio = (float) (isset($lookup[$mpeg_version][$rawaspectratio]) ? $lookup[$mpeg_version][$rawaspectratio] : 0); - if ($mpeg_version == 2 && $ratio != 1) { + if ($mpeg_version == 2 && $ratio != 1 && $width != 0) { // Calculate pixel aspect ratio from MPEG-2 display aspect ratio $ratio = $ratio * $height / $width; } From c7543f853874006eaca5efe0e2caa9c20ae3a006 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 25 Dec 2021 19:08:05 +0100 Subject: [PATCH 03/24] GH Actions: version update for `ramsey/composer-install` The action used to install Composer packages and handle the caching has released a new major (and some follow-up patch releases), which means, the action reference needs to be updated to benefit from it. Refs: * https://github.com/ramsey/composer-install/releases/tag/2.0.0 * https://github.com/ramsey/composer-install/releases/tag/2.0.1 * https://github.com/ramsey/composer-install/releases/tag/2.0.2 --- .github/workflows/continuous-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 42a33daf..d0149602 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -28,7 +28,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: error_reporting=-1, display_errors=On coverage: "none" - - uses: "ramsey/composer-install@v1" + - uses: "ramsey/composer-install@v2" - name: "Run the linter" run: "composer lint -- --colors" @@ -42,6 +42,6 @@ jobs: php-version: "7.4" tools: "phpstan:0.12.99" coverage: "none" - - uses: "ramsey/composer-install@v1" + - uses: "ramsey/composer-install@v2" - name: "Run PHPStan" run: "phpstan analyse -c phpstan.neon -l 4 getid3" From 6009a5976ffe6b22c1e168b085d627ae530d9827 Mon Sep 17 00:00:00 2001 From: StudioMaX Date: Tue, 28 Dec 2021 18:54:29 +0600 Subject: [PATCH 04/24] Fix typos in some Header Extension Object headers --- getid3/module.audio-video.asf.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/getid3/module.audio-video.asf.php b/getid3/module.audio-video.asf.php index a17e27fc..40b62909 100644 --- a/getid3/module.audio-video.asf.php +++ b/getid3/module.audio-video.asf.php @@ -1809,8 +1809,8 @@ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_dat $thisObject['stream_language_id_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $offset += 2; - $thisObject['average_time_per_frame'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); - $offset += 4; + $thisObject['average_time_per_frame'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; $thisObject['stream_name_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $offset += 2; @@ -1827,7 +1827,7 @@ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_dat $streamName['stream_name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $offset += 2; - $streamName['stream_name'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, $streamName['stream_name_length'])); + $streamName['stream_name'] = substr($asf_header_extension_object_data, $offset, $streamName['stream_name_length']); $offset += $streamName['stream_name_length']; $thisObject['stream_names'][$i] = $streamName; @@ -1849,7 +1849,7 @@ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_dat $payloadExtensionSystem['extension_system_info_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); $offset += 4; - $payloadExtensionSystem['extension_system_info_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, $payloadExtensionSystem['extension_system_info_length'])); + $payloadExtensionSystem['extension_system_info'] = substr($asf_header_extension_object_data, $offset, $payloadExtensionSystem['extension_system_info_length']); $offset += $payloadExtensionSystem['extension_system_info_length']; $thisObject['payload_extension_systems'][$i] = $payloadExtensionSystem; From 06382aebeb91c5eb81e5dc4e0d20f1d43c4411b1 Mon Sep 17 00:00:00 2001 From: StudioMaX Date: Tue, 28 Dec 2021 18:56:37 +0600 Subject: [PATCH 05/24] Add support for some new GUIDs inside Header Extension Object --- getid3/module.audio-video.asf.php | 156 +++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 3 deletions(-) diff --git a/getid3/module.audio-video.asf.php b/getid3/module.audio-video.asf.php index 40b62909..e83de754 100644 --- a/getid3/module.audio-video.asf.php +++ b/getid3/module.audio-video.asf.php @@ -20,6 +20,24 @@ class getid3_asf extends getid3_handler { + protected static $ASFIndexParametersObjectIndexSpecifiersIndexTypes = array( + 1 => 'Nearest Past Data Packet', + 2 => 'Nearest Past Media Object', + 3 => 'Nearest Past Cleanpoint' + ); + + protected static $ASFMediaObjectIndexParametersObjectIndexSpecifiersIndexTypes = array( + 1 => 'Nearest Past Data Packet', + 2 => 'Nearest Past Media Object', + 3 => 'Nearest Past Cleanpoint', + 0xFF => 'Frame Number Offset' + ); + + protected static $ASFTimecodeIndexParametersObjectIndexSpecifiersIndexTypes = array( + 2 => 'Nearest Past Media Object', + 3 => 'Nearest Past Cleanpoint' + ); + /** * @param getID3 $getid3 */ @@ -1581,8 +1599,9 @@ public static function KnownGUIDs() { 'GETID3_ASF_Audio_Media' => 'F8699E40-5B4D-11CF-A8FD-00805F5C442B', 'GETID3_ASF_Media_Object_Index_Object' => 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C', 'GETID3_ASF_Alt_Extended_Content_Encryption_Obj' => 'FF889EF1-ADEE-40DA-9E71-98704BB928CE', - 'GETID3_ASF_Index_Placeholder_Object' => 'D9AADE20-7C17-4F9C-BC28-8555DD98E2A2', // http://cpan.uwinnipeg.ca/htdocs/Audio-WMA/Audio/WMA.pm.html - 'GETID3_ASF_Compatibility_Object' => '26F18B5D-4584-47EC-9F5F-0E651F0452C9', // http://cpan.uwinnipeg.ca/htdocs/Audio-WMA/Audio/WMA.pm.html + 'GETID3_ASF_Index_Placeholder_Object' => 'D9AADE20-7C17-4F9C-BC28-8555DD98E2A2', // https://metacpan.org/dist/Audio-WMA/source/WMA.pm + 'GETID3_ASF_Compatibility_Object' => '26F18B5D-4584-47EC-9F5F-0E651F0452C9', // https://metacpan.org/dist/Audio-WMA/source/WMA.pm + 'GETID3_ASF_Media_Object_Index_Parameters_Object'=> '6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7', ); return $GUIDarray; } @@ -1745,7 +1764,7 @@ public static function WMpictureTypeLookup($WMpictureType) { * @return array */ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) { - // http://msdn.microsoft.com/en-us/library/bb643323.aspx + // https://web.archive.org/web/20140419205228/http://msdn.microsoft.com/en-us/library/bb643323.aspx $offset = 0; $objectOffset = 0; @@ -1857,6 +1876,40 @@ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_dat break; + case GETID3_ASF_Advanced_Mutual_Exclusion_Object: + $thisObject['exclusion_type'] = substr($asf_header_extension_object_data, $offset, 16); + $offset += 16; + $thisObject['exclusion_type_text'] = $this->BytestringToGUID($thisObject['exclusion_type']); + + $thisObject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['stream_numbers_count']; $i++) { + $thisObject['stream_numbers'][$i] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + } + + break; + + case GETID3_ASF_Stream_Prioritization_Object: + $thisObject['priority_records_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['priority_records_count']; $i++) { + $priorityRecord = array(); + + $priorityRecord['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $priorityRecord['flags_raw'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $priorityRecord['flags']['mandatory'] = (bool) $priorityRecord['flags_raw'] & 0x00000001; + + $thisObject['priority_records'][$i] = $priorityRecord; + } + + break; + case GETID3_ASF_Padding_Object: // padding, skip it break; @@ -1974,6 +2027,103 @@ public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_dat } break; + case GETID3_ASF_Index_Parameters_Object: + $thisObject['index_entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['index_specifiers_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['index_specifiers_count']; $i++) { + $indexSpecifier = array(); + + $indexSpecifier['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $indexSpecifier['index_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $indexSpecifier['index_type_text'] = isset(static::$ASFIndexParametersObjectIndexSpecifiersIndexTypes[$indexSpecifier['index_type']]) + ? static::$ASFIndexParametersObjectIndexSpecifiersIndexTypes[$indexSpecifier['index_type']] + : 'invalid' + ; + + $thisObject['index_specifiers'][$i] = $indexSpecifier; + } + + break; + + case GETID3_ASF_Media_Object_Index_Parameters_Object: + $thisObject['index_entry_count_interval'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['index_specifiers_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['index_specifiers_count']; $i++) { + $indexSpecifier = array(); + + $indexSpecifier['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $indexSpecifier['index_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $indexSpecifier['index_type_text'] = isset(static::$ASFMediaObjectIndexParametersObjectIndexSpecifiersIndexTypes[$indexSpecifier['index_type']]) + ? static::$ASFMediaObjectIndexParametersObjectIndexSpecifiersIndexTypes[$indexSpecifier['index_type']] + : 'invalid' + ; + + $thisObject['index_specifiers'][$i] = $indexSpecifier; + } + + break; + + case GETID3_ASF_Timecode_Index_Parameters_Object: + // 4.11 Timecode Index Parameters Object (mandatory only if TIMECODE index is present in file, 0 or 1) + // Field name Field type Size (bits) + // Object ID GUID 128 // GUID for the Timecode Index Parameters Object - ASF_Timecode_Index_Parameters_Object + // Object Size QWORD 64 // Specifies the size, in bytes, of the Timecode Index Parameters Object. Valid values are at least 34 bytes. + // Index Entry Count Interval DWORD 32 // This value is ignored for the Timecode Index Parameters Object. + // Index Specifiers Count WORD 16 // Specifies the number of entries in the Index Specifiers list. Valid values are 1 and greater. + // Index Specifiers array of: varies // + // * Stream Number WORD 16 // Specifies the stream number that the Index Specifiers refer to. Valid values are between 1 and 127. + // * Index Type WORD 16 // Specifies the type of index. Values are defined as follows (1 is not a valid value): + // 2 = Nearest Past Media Object - indexes point to the closest data packet containing an entire video frame or the first fragment of a video frame + // 3 = Nearest Past Cleanpoint - indexes point to the closest data packet containing an entire video frame (or first fragment of a video frame) that is a key frame. + // Nearest Past Media Object is the most common value + + $thisObject['index_entry_count_interval'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['index_specifiers_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['index_specifiers_count']; $i++) { + $indexSpecifier = array(); + + $indexSpecifier['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $indexSpecifier['index_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $indexSpecifier['index_type_text'] = isset(static::$ASFTimecodeIndexParametersObjectIndexSpecifiersIndexTypes[$indexSpecifier['index_type']]) + ? static::$ASFTimecodeIndexParametersObjectIndexSpecifiersIndexTypes[$indexSpecifier['index_type']] + : 'invalid' + ; + + $thisObject['index_specifiers'][$i] = $indexSpecifier; + } + + break; + + case GETID3_ASF_Compatibility_Object: + $thisObject['profile'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 1)); + $offset += 1; + + $thisObject['mode'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 1)); + $offset += 1; + + break; + default: $unhandled_sections++; if ($this->GUIDname($thisObject['guid_text'])) { From 15784c468189f6d975449f17c9bbfbda55457e74 Mon Sep 17 00:00:00 2001 From: StudioMaX Date: Wed, 29 Dec 2021 00:15:55 +0600 Subject: [PATCH 06/24] PHP 8.1: Deprecated: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated --- getid3/module.misc.cue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getid3/module.misc.cue.php b/getid3/module.misc.cue.php index 5082f2fb..b358f8ac 100644 --- a/getid3/module.misc.cue.php +++ b/getid3/module.misc.cue.php @@ -73,7 +73,7 @@ public function readCueSheetFilename($filename) public function readCueSheet(&$filedata) { $cue_lines = array(); - foreach (explode("\n", str_replace("\r", null, $filedata)) as $line) + foreach (explode("\n", str_replace("\r", '', $filedata)) as $line) { if ( (strlen($line) > 0) && ($line[0] != '#')) { From 58ce18e690e5d07deca0c1d70580db4f729a58c3 Mon Sep 17 00:00:00 2001 From: StudioMaX Date: Wed, 29 Dec 2021 01:16:58 +0600 Subject: [PATCH 07/24] PHP 8.1: Deprecated: substr(): Passing null to parameter #1 ($string) of type string is deprecated --- getid3/module.graphic.gif.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getid3/module.graphic.gif.php b/getid3/module.graphic.gif.php index 0e463c8c..eed4d2f6 100644 --- a/getid3/module.graphic.gif.php +++ b/getid3/module.graphic.gif.php @@ -168,7 +168,7 @@ public function Analyze() { $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); $ExtensionBlock['data'] = (($ExtensionBlock['byte_length'] > 0) ? $this->fread($ExtensionBlock['byte_length']) : null); - if (substr($ExtensionBlock['data'], 0, 11) == 'NETSCAPE2.0') { // Netscape Application Block (NAB) + if ($ExtensionBlock['byte_length'] > 0 && substr($ExtensionBlock['data'], 0, 11) == 'NETSCAPE2.0') { // Netscape Application Block (NAB) $ExtensionBlock['data'] .= $this->fread(4); if (substr($ExtensionBlock['data'], 11, 2) == "\x03\x01") { $info['gif']['animation']['animated'] = true; From 6f0cf608a878adb1c4e35bdf647d5bbf67574bbd Mon Sep 17 00:00:00 2001 From: StudioMaX Date: Wed, 29 Dec 2021 02:34:16 +0600 Subject: [PATCH 08/24] Add support for ANIMEXTS1.0 Application Extension in gif files --- getid3/module.graphic.gif.php | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/getid3/module.graphic.gif.php b/getid3/module.graphic.gif.php index eed4d2f6..f9719137 100644 --- a/getid3/module.graphic.gif.php +++ b/getid3/module.graphic.gif.php @@ -17,6 +17,7 @@ /** * @link https://www.w3.org/Graphics/GIF/spec-gif89a.txt * @link http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + * @link http://www.vurdalakov.net/misc/gif/netscape-looping-application-extension */ if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers @@ -168,14 +169,31 @@ public function Analyze() { $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); $ExtensionBlock['data'] = (($ExtensionBlock['byte_length'] > 0) ? $this->fread($ExtensionBlock['byte_length']) : null); - if ($ExtensionBlock['byte_length'] > 0 && substr($ExtensionBlock['data'], 0, 11) == 'NETSCAPE2.0') { // Netscape Application Block (NAB) - $ExtensionBlock['data'] .= $this->fread(4); - if (substr($ExtensionBlock['data'], 11, 2) == "\x03\x01") { - $info['gif']['animation']['animated'] = true; - $info['gif']['animation']['loop_count'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlock['data'], 13, 2)); - } else { - $this->warning('Expecting 03 01 at offset '.($this->ftell() - 4).', found "'.getid3_lib::PrintHexBytes(substr($ExtensionBlock['data'], 11, 2)).'"'); - } + switch ($ExtensionBlock['function_code']) { + case 0xFF: + // Application Extension + if ($ExtensionBlock['byte_length'] != 11) { + $this->warning('Expected block size of the Application Extension is 11 bytes, found '.$ExtensionBlock['byte_length'].' at offset '.$this->ftell()); + break; + } + + if (substr($ExtensionBlock['data'], 0, 11) !== 'NETSCAPE2.0' + && substr($ExtensionBlock['data'], 0, 11) !== 'ANIMEXTS1.0' + ) { + $this->warning('Ignoring unsupported Application Extension '.substr($ExtensionBlock['data'], 0, 11)); + break; + } + + // Netscape Application Block (NAB) + $ExtensionBlock['data'] .= $this->fread(4); + if (substr($ExtensionBlock['data'], 11, 2) == "\x03\x01") { + $info['gif']['animation']['animated'] = true; + $info['gif']['animation']['loop_count'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlock['data'], 13, 2)); + } else { + $this->warning('Expecting 03 01 at offset '.($this->ftell() - 4).', found "'.getid3_lib::PrintHexBytes(substr($ExtensionBlock['data'], 11, 2)).'"'); + } + + break; } if ($this->getid3->option_extra_info) { From e1e92b163b61f37b56a32006a7f6af9e83bb6b78 Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Mon, 3 Jan 2022 11:59:47 -0500 Subject: [PATCH 09/24] #365 Trying to access array offset on value of type int https://github.com/JamesHeinrich/getID3/issues/365 --- getid3/getid3.php | 2 +- getid3/module.archive.zip.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index 9bc19885..5fe5d9cc 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202112151109'; + const VERSION = '1.9.21-202201031158'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; diff --git a/getid3/module.archive.zip.php b/getid3/module.archive.zip.php index ef99c141..ffd196df 100644 --- a/getid3/module.archive.zip.php +++ b/getid3/module.archive.zip.php @@ -297,7 +297,7 @@ public function ZIPparseLocalFileHeader() { $DataDescriptor = $this->fread(16); $LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08" - $this->getid3->warning('invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature'])); + $this->getid3->warning('invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes(substr($DataDescriptor, 0, 4))); $this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly return false; } From 2279f7caca2d761dfc580dd02b401e7a1ff69dfe Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Thu, 3 Feb 2022 12:07:51 -0500 Subject: [PATCH 10/24] change @error-suppress to isset (quicktime) https://github.com/JamesHeinrich/getID3/issues/366 --- getid3/getid3.php | 2 +- getid3/module.audio-video.quicktime.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index 5fe5d9cc..8f93c622 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202201031158'; + const VERSION = '1.9.21-202202031206'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; diff --git a/getid3/module.audio-video.quicktime.php b/getid3/module.audio-video.quicktime.php index 5fca444f..31edf14d 100644 --- a/getid3/module.audio-video.quicktime.php +++ b/getid3/module.audio-video.quicktime.php @@ -1699,7 +1699,8 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset $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); - $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++]; + $atom_structure['key_name'] = (isset($info['quicktime']['temp_meta_key_names'][$metaDATAkey]) ? $info['quicktime']['temp_meta_key_names'][$metaDATAkey] : ''); + $metaDATAkey++; if ($atom_structure['key_name'] && $atom_structure['data']) { @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; From fd5edb5046ed5882e8517e844d175c205f2671e9 Mon Sep 17 00:00:00 2001 From: Rafi Ahmed Date: Fri, 18 Feb 2022 04:13:41 +0600 Subject: [PATCH 11/24] Typo fixed in getid3/getid3.php --- getid3/getid3.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index 8f93c622..56916a78 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -182,7 +182,7 @@ class getID3 public $option_md5_data = false; /** - * Use MD5 of source file if availble - only FLAC and OptimFROG + * Use MD5 of source file if available - only FLAC and OptimFROG * * @var bool */ From 57a2a83081d1e41754294413873ff297e107320e Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Tue, 22 Feb 2022 08:16:36 -0500 Subject: [PATCH 12/24] #369 fix remote urls pattern https://github.com/JamesHeinrich/getID3/pull/369 --- getid3/getid3.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index 56916a78..ca797181 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202202031206'; + const VERSION = '1.9.21-202202220815'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; @@ -568,7 +568,7 @@ public function openfile($filename, $filesize=null, $fp=null) { $this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false); // remote files not supported - if (preg_match('#^(ht|f)tp://#', $filename)) { + if (preg_match('#^(ht|f)tps?://#', $filename)) { throw new getid3_exception('Remote files are not supported - please copy the file locally first'); } From bcbba5a3d13b75584d2402f68cb358139799db89 Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Fri, 1 Apr 2022 10:49:11 -0400 Subject: [PATCH 13/24] Quicktime parse ID32 frames --- getid3/module.audio-video.quicktime.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/getid3/module.audio-video.quicktime.php b/getid3/module.audio-video.quicktime.php index 31edf14d..53a16af4 100644 --- a/getid3/module.audio-video.quicktime.php +++ b/getid3/module.audio-video.quicktime.php @@ -1540,6 +1540,21 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset unset($mdat_offset, $chapter_string_length, $chapter_matches); break; + case 'ID32': // ID3v2 + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp); + $getid3_id3v2 = new getid3_id3v2($getid3_temp); + $getid3_id3v2->StartingOffset = $atom_structure['offset'] + 14; // framelength(4)+framename(4)+flags(4)+??(2) + if ($atom_structure['valid'] = $getid3_id3v2->Analyze()) { + $atom_structure['id3v2'] = $getid3_temp->info['id3v2']; + } else { + $this->warning('ID32 frame at offset '.$atom_structure['offset'].' did not parse'); + } + unset($getid3_temp, $getid3_id3v2); + break; + case 'free': // FREE space atom case 'skip': // SKIP atom case 'wide': // 64-bit expansion placeholder atom @@ -2075,6 +2090,13 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset $atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data); 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 + case 'iinf': // Item INFo + case 'iref': // Image REFerence + case 'iprp': // Image PRoPerties default: $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset); $atom_structure['data'] = $atom_data; From c561bfdaa0e6e6713dbdd5005526dcf2205efc1a Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Fri, 1 Apr 2022 11:34:43 -0400 Subject: [PATCH 14/24] fragmented MP4 unsupported warning https://github.com/JamesHeinrich/getID3/issues/371 --- getid3/module.audio-video.quicktime.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/getid3/module.audio-video.quicktime.php b/getid3/module.audio-video.quicktime.php index 53a16af4..611ad290 100644 --- a/getid3/module.audio-video.quicktime.php +++ b/getid3/module.audio-video.quicktime.php @@ -258,7 +258,9 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset } else { switch ($atomname) { case 'moov': // MOVie container atom + case 'moof': // MOvie Fragment box case 'trak': // TRAcK container atom + case 'traf': // TRAck Fragment box case 'clip': // CLIPping container atom case 'matt': // track MATTe container atom case 'edts': // EDiTS container atom @@ -2097,6 +2099,21 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset case 'iinf': // Item INFo case 'iref': // Image REFerence case 'iprp': // Image PRoPerties +$this->error('AVIF files not currently supported'); + $atom_structure['data'] = $atom_data; + break; + + case 'tfdt': // Track Fragment base media Decode Time box + case 'tfhd': // Track Fragment HeaDer box + case 'mfhd': // Movie Fragment HeaDer box + case 'trun': // Track fragment RUN box +$this->error('fragmented mp4 files not currently supported'); + $atom_structure['data'] = $atom_data; + break; + + case 'mvex': // MoVie EXtends box + case 'pssh': // Protection System Specific Header box + case 'sidx': // Segment InDeX box default: $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset); $atom_structure['data'] = $atom_data; From 1051a471971dc984a8858b5fdee43a7164d8d3e1 Mon Sep 17 00:00:00 2001 From: "a.dmitryuk" Date: Wed, 13 Apr 2022 11:52:01 +0700 Subject: [PATCH 15/24] Add Installation docs --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 780bed6c..b967df5e 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,15 @@ Requirements * at least 4MB memory for PHP. 8MB or more is highly recommended. 12MB is required with all modules loaded. +Installation +=== +The preferred method is via [composer](https://getcomposer.org/). Follow the installation [instructions](https://getcomposer.org/doc/00-intro.md) if you do not already have composer installed. + +Once composer is installed, execute the following command in your project root to install this library: +``` +composer require james-heinrich/getid3 +``` Usage === From 079c4d7e355e911e30d3056f24418ffae9c09564 Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Thu, 14 Apr 2022 13:22:09 -0400 Subject: [PATCH 16/24] MOD improved SoundTracker support https://github.com/JamesHeinrich/getID3/issues/374 --- getid3/getid3.php | 21 +++++++++--------- getid3/module.audio.mod.php | 44 +++++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index ca797181..17dbdbb4 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202202220815'; + const VERSION = '1.9.21-202204141319'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; @@ -1054,15 +1054,16 @@ public function GetFileFormatArray() { 'mime_type' => 'audio/x-monkeys-audio', ), -// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available -// // MOD - audio - MODule (assorted sub-formats) -// 'mod' => array( -// 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)', -// 'group' => 'audio', -// 'module' => 'mod', -// 'option' => 'mod', -// 'mime_type' => 'audio/mod', -// ), + + // MOD - audio - MODule (SoundTracker) + 'mod' => array( + //'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)', // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available + 'pattern' => '^.{1080}(M\\.K\\.)', + 'group' => 'audio', + 'module' => 'mod', + 'option' => 'mod', + 'mime_type' => 'audio/mod', + ), // MOD - audio - MODule (Impulse Tracker) 'it' => array( diff --git a/getid3/module.audio.mod.php b/getid3/module.audio.mod.php index bf8be33e..654ba50e 100644 --- a/getid3/module.audio.mod.php +++ b/getid3/module.audio.mod.php @@ -31,9 +31,10 @@ public function Analyze() { return $this->getITheaderFilepointer(); } elseif (preg_match('#^Extended Module#', $fileheader)) { return $this->getXMheaderFilepointer(); - } elseif (preg_match('#^.{44}SCRM#', $fileheader)) { + } elseif (preg_match('#^.{44}SCRM#s', $fileheader)) { return $this->getS3MheaderFilepointer(); - } elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#', $fileheader)) { + //} elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#s', $fileheader)) { + } elseif (preg_match('#^.{1080}(M\\.K\\.)#s', $fileheader)) { return $this->getMODheaderFilepointer(); } $this->error('This is not a known type of MOD file'); @@ -45,17 +46,42 @@ public function Analyze() { */ public function getMODheaderFilepointer() { $info = &$this->getid3->info; - $this->fseek($info['avdataoffset'] + 1080); - $FormatID = $this->fread(4); - if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) { - $this->error('This is not a known type of MOD file'); + $this->fseek($info['avdataoffset']); + $filedata = $this->fread(1084); + //if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) { + if (substr($filedata, 1080, 4) == 'M.K.') { + + // + 0 song/module working title + // + 20 15 sample headers (see below) + // + 470 song length (number of steps in pattern table) + // + 471 song speed in beats per minute (see below) + // + 472 pattern step table + $offset = 0; + $info['mod']['title'] = rtrim(substr($filedata, $offset, 20), "\x00"); $offset += 20; + + $info['tags']['mod']['title'] = array($info['mod']['title']); + + for ($samplenumber = 0; $samplenumber < 31; $samplenumber++) { + $sampledata = array(); + $sampledata['name'] = substr($filedata, $offset, 22); $offset += 22; + $sampledata['length'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 2)); $offset += 2; + $sampledata['volume'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 2)); $offset += 2; + $sampledata['repeat_offset'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 2)); $offset += 2; + $sampledata['repeat_length'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 2)); $offset += 2; + $info['mod']['samples'][$samplenumber] = $sampledata; + } + + $info['mod']['step_count'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 1)); $offset += 1; + $info['mod']['bpm'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 1)); $offset += 1; + + } else { + $this->error('unknown MOD ID at offset 1080: '.getid3_lib::PrintHexBytes(substr($filedata, 1080, 4))); return false; } - $info['fileformat'] = 'mod'; - $this->error('MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); - return false; +$this->warning('MOD (SoundTracker) parsing incomplete in this version of getID3() ['.$this->getid3->version().']'); + return true; } /** From 9a7651013606a5195b4b0e35672c7e5108a681a7 Mon Sep 17 00:00:00 2001 From: Ben XO <75862+ben-xo@users.noreply.github.com> Date: Sun, 17 Apr 2022 12:07:57 +0100 Subject: [PATCH 17/24] Define options to suppress warnings generated by invalid iXML content, and add to the defences of XML entity injection. This PR adds LIBXML_WARNING, LIBXML_NONET and LIBXML_COMPACT (if supported), in addition to LIBXML_NOENT, to simplexml_load_string(), which is currently only used for parsing RIFF iXML (http://www.gallery.co.uk/ixml/) * LIBXML_NONET, to deny access to entites included remotely. This is the most interesting one, as there is other code nearby which is supposed to address the same issue. * LIBXML_NOWARNING, to suppress recoverable parsing warnings which we can't do anything about (and may even be deliberate) * If your version of libxml supports it, LIBXML_COMPACT, which is described at https://www.php.net/manual/en/libxml.constants.php/ as "Activate small nodes allocation optimization. This may speed up your application without needing to change the code." The gotcha is that the DOM is readonly, but we're not using this code to manipulate tags so that should be fine. I'll explain the rationale for each. Invalid XML / LIBXML_NOWARNING ============================== I encountered a .wav file with an iXML studio mastering tag in my collection. Here's an excerpt from the file: ... Optiv & CZA - Invisible Things-13 Mixdown-32 NEW 1--22 ... A=PCM,F=44100,W=16,T=SOUND FORGE Pro 12.1, As you can see, the `` tag is technically invalid because it has a nameless entity (`&`). The exact warning is `PHP Warning: simplexml_load_string(): Entity: line 3: parser error : xmlParseEntityRef: no name`. This is clearly both recoverable and harmless, and may even be intentional given that Sound Forge Pro 12 is not old (2018). Suppressing parser warnings seems to fit with the other warning suppressions nearby in the code. XML Inclusion / LIBXML_NONET ============================ This one's more interesting. In commit `afbdaa04` (2014) additional code was added by the wordpress team with the commit "improved XXE fix". The change adds option LIBXML_NOENT, and references the article http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html. The problem is that I think LIBXML_NOENT was a typo and should have been LIBXML_NONET. The article does not mention NOENT at all. Ironically, the NOENT option does the _exact opposite_ of what it sounds like it does - it *enables* entity parsing. Refs: * https://www.php.net/manual/en/libxml.constants.php - comment "The name of the constant LIBXML_NOENT is very misleading. Adding this flag actually causes the parser to load and insert the external entities. Omitting it leaves the tags untouched, which is probably what you want." * https://stackoverflow.com/q/38807506/367456 - "What does LIBXML_NOENT do (and why isn't it called LIBXML_ENT)?" I suspect this was a mistake by the original author. Nevertheless, it's fairly harmless to have entities enabled in the XML as long as it's not possible to do remote inclusions, and that's already disabled with `libxml_disable_entity_loader()`. Adding LIBXML_NONET (as referenced in the article to improve XXE defences) prevents libxml from using the network. I did not remove LIBXML_NOENT as theoretically you can define your own entites inline at the top of an XML doc, even though I very much doubt it would work properly if you did, given the first bug addressed in this PR. Performance / LIBXML_COMPACT ============================ If your version of libxml supports it this flag "Activate small nodes allocation optimization. This may speed up your application without needing to change the code." according to https://www.php.net/manual/en/libxml.constants.php . The gotcha is that the returned DOM object is readonly, but we're not using this code to manipulate a DOM so that should be fine. --- getid3/getid3.lib.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/getid3/getid3.lib.php b/getid3/getid3.lib.php index 5ba9b16d..ba7a8e68 100644 --- a/getid3/getid3.lib.php +++ b/getid3/getid3.lib.php @@ -11,6 +11,13 @@ // /// ///////////////////////////////////////////////////////////////// +if(!defined('GETID3_LIBXML_OPTIONS') && defined('LIBXML_VERSION')) { + if(LIBXML_VERSION >= 20621) { + define('GETID3_LIBXML_OPTIONS', LIBXML_NOENT | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_COMPACT); + } else { + define('GETID3_LIBXML_OPTIONS', LIBXML_NOENT | LIBXML_NONET | LIBXML_NOWARNING); + } +} class getid3_lib { @@ -731,7 +738,7 @@ public static function XML2array($XMLstring) { // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is // disabled by default, but is still needed when LIBXML_NOENT is used. $loader = @libxml_disable_entity_loader(true); - $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT); + $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', GETID3_LIBXML_OPTIONS); $return = self::SimpleXMLelement2array($XMLobject); @libxml_disable_entity_loader($loader); return $return; From 630b7a726807b430bc0c0e3485153de26f6cfaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Y=C4=B1lmaz?= Date: Mon, 18 Apr 2022 20:42:15 +0300 Subject: [PATCH 18/24] The type has been identified. ($BitrateCache[$FrameLength]]) --- getid3/module.audio.aac.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getid3/module.audio.aac.php b/getid3/module.audio.aac.php index d4f04b85..5b8448d2 100644 --- a/getid3/module.audio.aac.php +++ b/getid3/module.audio.aac.php @@ -400,7 +400,7 @@ public function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExt if (!isset($BitrateCache[$FrameLength])) { $BitrateCache[$FrameLength] = ($info['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8; } - getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1); + getid3_lib::safe_inc($info['aac']['bitrate_distribution'][(string)$BitrateCache[$FrameLength]], 1); $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength; From aaa78177aed80d4ef050035710875f2d7c370a1a Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Sun, 1 May 2022 15:51:50 -0400 Subject: [PATCH 19/24] MOD song_length https://github.com/JamesHeinrich/getID3/issues/374 --- getid3/getid3.php | 2 +- getid3/module.audio.mod.php | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index 17dbdbb4..4455726c 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202204141319'; + const VERSION = '1.9.21-202205011548'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; diff --git a/getid3/module.audio.mod.php b/getid3/module.audio.mod.php index 654ba50e..4841c640 100644 --- a/getid3/module.audio.mod.php +++ b/getid3/module.audio.mod.php @@ -35,6 +35,13 @@ public function Analyze() { return $this->getS3MheaderFilepointer(); //} elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#s', $fileheader)) { } elseif (preg_match('#^.{1080}(M\\.K\\.)#s', $fileheader)) { + /* + The four letters "M.K." - This is something Mahoney & Kaktus inserted when they + increased the number of samples from 15 to 31. If it's not there, the module/song + uses 15 samples or the text has been removed to make the module harder to rip. + Startrekker puts "FLT4" or "FLT8" there instead. + If there are more than 64 patterns, PT2.3 will insert M!K! here. + */ return $this->getMODheaderFilepointer(); } $this->error('This is not a known type of MOD file'); @@ -61,7 +68,7 @@ public function getMODheaderFilepointer() { $info['tags']['mod']['title'] = array($info['mod']['title']); - for ($samplenumber = 0; $samplenumber < 31; $samplenumber++) { + for ($samplenumber = 0; $samplenumber <= 30; $samplenumber++) { $sampledata = array(); $sampledata['name'] = substr($filedata, $offset, 22); $offset += 22; $sampledata['length'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 2)); $offset += 2; @@ -71,8 +78,14 @@ public function getMODheaderFilepointer() { $info['mod']['samples'][$samplenumber] = $sampledata; } - $info['mod']['step_count'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 1)); $offset += 1; - $info['mod']['bpm'] = getid3_lib::BigEndian2Int(substr($filedata, $offset, 1)); $offset += 1; + $info['mod']['song_length'] = getid3_lib::BigEndian2Int(substr($filedata, $offset++, 1));// Songlength. Range is 1-128. + $info['mod']['bpm'] = getid3_lib::BigEndian2Int(substr($filedata, $offset++, 1));// This byte is set to 127, so that old trackers will search through all patterns when loading. Noisetracker uses this byte for restart, ProTracker doesn't. + + for ($songposition = 0; $songposition <= 127; $songposition++) { + // Song positions 0-127. Each hold a number from 0-63 (or 0-127) + // that tells the tracker what pattern to play at that position. + $info['mod']['song_positions'][$songposition] = getid3_lib::BigEndian2Int(substr($filedata, $offset++, 1)); + } } else { $this->error('unknown MOD ID at offset 1080: '.getid3_lib::PrintHexBytes(substr($filedata, 1080, 4))); From 28c944467b4eccda204aecdb79c62a9d390e0bda Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 19 Jun 2022 18:39:18 +0200 Subject: [PATCH 20/24] GH Actions: run lint against PHP 8.2 The first alpha of PHP 8.2 was released nearly two weeks ago, so let's start linting against PHP 8.2 Includes enabling the `--show-deprecated` option. While rare, there are some deprecations which PHP can show when a file is being linted. By default these are ignored by PHP-Parallel-Lint. --- .github/workflows/continuous-integration.yml | 1 + composer.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d0149602..cdfad56d 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -21,6 +21,7 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.2" steps: - uses: "actions/checkout@v2" - uses: "shivammathur/setup-php@v2" diff --git a/composer.json b/composer.json index 91e91e4b..8b35ab47 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "ext-zlib": "Zlib extension is required for archive modules and compressed metadata." }, "scripts": { - "lint": "parallel-lint --exclude vendor --exclude .git .", + "lint": "parallel-lint --show-deprecated --exclude vendor --exclude .git .", "test": [ "composer lint" ] From 8fb5161baf903f4a25cdfc7581a82dc42c1b01bf Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Tue, 21 Jun 2022 17:33:03 -0400 Subject: [PATCH 21/24] Quicktime extended atom size https://github.com/JamesHeinrich/getID3/issues/382 --- getid3/getid3.php | 2 +- getid3/module.audio-video.quicktime.php | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index 4455726c..de53d2a7 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202205011548'; + const VERSION = '1.9.21-202206211730'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; diff --git a/getid3/module.audio-video.quicktime.php b/getid3/module.audio-video.quicktime.php index 611ad290..508e4dd0 100644 --- a/getid3/module.audio-video.quicktime.php +++ b/getid3/module.audio-video.quicktime.php @@ -61,12 +61,16 @@ public function Analyze() { $this->fseek($offset); $AtomHeader = $this->fread(8); + // https://github.com/JamesHeinrich/getID3/issues/382 + // Atom sizes are stored as 32-bit number in most cases, but sometimes (notably for "mdat") + // a 64-bit value is required, in which case the normal 32-bit size field is set to 0x00000001 + // and the 64-bit "real" size value is the next 8 bytes. + $atom_size_extended_bytes = 0; $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4)); $atomname = substr($AtomHeader, 4, 4); - - // 64-bit MOV patch by jlegateØktnc*com if ($atomsize == 1) { - $atomsize = getid3_lib::BigEndian2Int($this->fread(8)); + $atom_size_extended_bytes = 8; + $atomsize = getid3_lib::BigEndian2Int($this->fread($atom_size_extended_bytes)); } if (($offset + $atomsize) > $info['avdataend']) { @@ -85,12 +89,14 @@ public function Analyze() { $info['quicktime'][$atomname]['offset'] = $offset; break; } - $atomHierarchy = array(); - $parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms); + $parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize - $atom_size_extended_bytes, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms); $parsedAtomData['name'] = $atomname; $parsedAtomData['size'] = $atomsize; $parsedAtomData['offset'] = $offset; + if ($atom_size_extended_bytes) { + $parsedAtomData['xsize_bytes'] = $atom_size_extended_bytes; + } if (in_array($atomname, array('uuid'))) { @$info['quicktime'][$atomname][] = $parsedAtomData; } else { From 78d82a9392ee20eb150d774dd5e4acb6d278ea2c Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Wed, 6 Jul 2022 10:55:10 -0400 Subject: [PATCH 22/24] Update module.audio.ogg.php --- getid3/module.audio.ogg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getid3/module.audio.ogg.php b/getid3/module.audio.ogg.php index 5786fd0d..0cbea61e 100644 --- a/getid3/module.audio.ogg.php +++ b/getid3/module.audio.ogg.php @@ -186,7 +186,7 @@ public function Analyze() { if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) { $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator']; } - $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable'); +$this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable'); } elseif (substr($filedata, 0, 8) == "fishead\x00") { From 59439c58bfeedcfac551bef0d0ec9a2050b1a374 Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Sat, 16 Jul 2022 16:48:42 -0400 Subject: [PATCH 23/24] Quicktime add hvc1 / H.265 video codec https://github.com/JamesHeinrich/getID3/issues/387 --- getid3/getid3.php | 2 +- getid3/module.audio-video.quicktime.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/getid3/getid3.php b/getid3/getid3.php index de53d2a7..4f81776f 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202206211730'; + const VERSION = '1.9.21-202207161647'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; diff --git a/getid3/module.audio-video.quicktime.php b/getid3/module.audio-video.quicktime.php index 508e4dd0..a769f9c1 100644 --- a/getid3/module.audio-video.quicktime.php +++ b/getid3/module.audio-video.quicktime.php @@ -850,6 +850,7 @@ public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset case 'dvcp': case 'gif ': case 'h263': + case 'hvc1': case 'jpeg': case 'kpcd': case 'mjpa': @@ -2368,6 +2369,7 @@ public function QuicktimeVideoCodecLookup($codecid) { $QuicktimeVideoCodecLookup['gif '] = 'GIF'; $QuicktimeVideoCodecLookup['h261'] = 'H261'; $QuicktimeVideoCodecLookup['h263'] = 'H263'; + $QuicktimeVideoCodecLookup['hvc1'] = 'H.265/HEVC'; $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4'; $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG'; $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD'; From 45f20faa0f0a24489740392c5b512ddcc36deccd Mon Sep 17 00:00:00 2001 From: James Heinrich Date: Thu, 29 Sep 2022 12:41:13 -0400 Subject: [PATCH 24/24] v1.9.22-202207161647 changelog --- changelog.txt | 24 ++++++++++++++++++++++++ getid3/getid3.php | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 5b8ffa5a..e6a6e586 100644 --- a/changelog.txt +++ b/changelog.txt @@ -18,6 +18,30 @@ Version History =============== +1.9.22: [2022-09-29] James Heinrich :: 1.9.22-202207161647 + * bugfix #387 fails to detect h265 video codec (QuickTime) + * bugfix #385 Quicktime extended atom size + * bugfix #378 AAC bitrate cache warning + * bugfix #376 simplexml_load_string improvments + * bugfix #374 MOD improved SoundTracker support + * bugfix #371 fragmented MP4 unsupported warning + * bugfix #369 fix remote URLs pattern + * bugfix #366 change @error-suppress to isset (quicktime) + * bugfix #365 ZIP array offset on value of type int + * bugfix #364 add support for ANIMEXTS1.0 in GIF files + * bugfix #363 ASF improve support of Header Extension Object data + * bugfix #362 version update for ramsey/composer-install + * bugfix #359 MPEG-2 aspect ratio divide-by-zero + * bugfix #358 free format mp3 bitrate + * bugfix #355 undefined array key in ID3v2 chapters + * bugfix #352 avoid false detection of Musepack format + * bugfix #351 Incorrect length passed to fread on a flac file + * bugfix #348 more targeted usage of clearstatcache calls + * bugfix #347 fixed reported by PHPStan v0.12.99 + * bugfix QuickTime support 'ID32' frame (ID3v2 inside QT) + * bugfix fix various PHP 8.1 issues + * bugfix PDF prevent undefined index + 1.9.21: [2021-09-22] James Heinrich :: 1.9.21-202109171300 » add support for RIFF.guan ¤ add ID3v1 genres 148-191 diff --git a/getid3/getid3.php b/getid3/getid3.php index 4f81776f..5e955304 100644 --- a/getid3/getid3.php +++ b/getid3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.21-202207161647'; + const VERSION = '1.9.22-202207161647'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false;