wp/wp-includes/ID3/module.audio-video.asf.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
--- a/wp/wp-includes/ID3/module.audio-video.asf.php	Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/ID3/module.audio-video.asf.php	Fri Sep 05 18:40:08 2025 +0200
@@ -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
 	 */
@@ -175,7 +193,7 @@
 						$info['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000);
 
 						//$info['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate'];
-						$info['bitrate'] = ((isset($thisfile_asf_filepropertiesobject['filesize']) ? $thisfile_asf_filepropertiesobject['filesize'] : $info['filesize']) * 8) / $info['playtime_seconds'];
+						$info['bitrate'] = getid3_lib::SafeDiv($thisfile_asf_filepropertiesobject['filesize'] * 8, $info['playtime_seconds']);
 					}
 					break;
 
@@ -317,6 +335,7 @@
 
 					// shortcut
 					$thisfile_asf['codec_list_object'] = array();
+					/** @var mixed[] $thisfile_asf_codeclistobject */
 					$thisfile_asf_codeclistobject      = &$thisfile_asf['codec_list_object'];
 
 					$thisfile_asf_codeclistobject['offset']                    = $NextObjectOffset + $offset;
@@ -332,6 +351,9 @@
 						break;
 					}
 					$thisfile_asf_codeclistobject['codec_entries_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					if ($thisfile_asf_codeclistobject['codec_entries_count'] > 0) {
+						$thisfile_asf_codeclistobject['codec_entries'] = array();
+					}
 					$offset += 4;
 					for ($CodecEntryCounter = 0; $CodecEntryCounter < $thisfile_asf_codeclistobject['codec_entries_count']; $CodecEntryCounter++) {
 						// shortcut
@@ -528,7 +550,7 @@
 					$offset += 16;
 					$thisfile_asf_markerobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']);
 					if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) {
-						$this->warning('marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}');
+						$this->warning('marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}');
 						break;
 					}
 					$thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
@@ -648,7 +670,7 @@
 							break;
 
 						default:
-							$this->warning('error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or  "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}');
+							$this->warning('error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['error_correction_type']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or  "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}');
 							//return false;
 							break;
 					}
@@ -1044,7 +1066,7 @@
 								break;
 						}
 
-						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) {
+						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { // @phpstan-ignore-line
 							foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) {
 								if (isset($dataarray['flags']['stream_number']) && ($dataarray['flags']['stream_number'] == $streamnumber)) {
 									$thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate'];
@@ -1130,7 +1152,7 @@
 						$videomediaoffset += 4;
 						$thisfile_asf_videomedia_currentstream['format_data']['codec_data']       = substr($streamdata['type_specific_data'], $videomediaoffset);
 
-						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) {
+						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { // @phpstan-ignore-line
 							foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) {
 								if (isset($dataarray['flags']['stream_number']) && ($dataarray['flags']['stream_number'] == $streamnumber)) {
 									$thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate'];
@@ -1442,7 +1464,7 @@
 				}
 			}
 		}
-		$info['bitrate'] = (isset($thisfile_audio['bitrate']) ? $thisfile_audio['bitrate'] : 0) + (isset($thisfile_video['bitrate']) ? $thisfile_video['bitrate'] : 0);
+		$info['bitrate'] = 0 + (isset($thisfile_audio['bitrate']) ? $thisfile_audio['bitrate'] : 0) + (isset($thisfile_video['bitrate']) ? $thisfile_video['bitrate'] : 0);
 
 		if ((!isset($info['playtime_seconds']) || ($info['playtime_seconds'] <= 0)) && ($info['bitrate'] > 0)) {
 			$info['playtime_seconds'] = ($info['filesize'] - $info['avdataoffset']) / ($info['bitrate'] / 8);
@@ -1577,8 +1599,9 @@
 			'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;
 	}
@@ -1741,7 +1764,7 @@
 	 * @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;
@@ -1805,8 +1828,8 @@
 					$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;
@@ -1823,7 +1846,7 @@
 						$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;
@@ -1845,7 +1868,7 @@
 						$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;
@@ -1853,6 +1876,40 @@
 
 					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;
@@ -1970,6 +2027,103 @@
 					}
 					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'])) {