--- a/wp/wp-includes/ID3/module.audio-video.quicktime.php Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/ID3/module.audio-video.quicktime.php Fri Sep 05 18:40:08 2025 +0200
@@ -61,12 +61,16 @@
$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 @@
$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 {
@@ -108,7 +114,7 @@
unset($info['avdataend_tmp']);
}
- if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
+ if (isset($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
$durations = $this->quicktime_time_to_sample_table($info);
for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
$bookmark = array();
@@ -146,7 +152,7 @@
} elseif (strlen($lat_deg) == 4) { // [+-]DDMM.M
$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
} elseif (strlen($lat_deg) == 6) { // [+-]DDMMSS.S
- $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
+ $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval((int) ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
}
if (strlen($lon_deg) == 3) { // [+-]DDD.D
@@ -154,7 +160,7 @@
} elseif (strlen($lon_deg) == 5) { // [+-]DDDMM.M
$ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
} elseif (strlen($lon_deg) == 7) { // [+-]DDDMMSS.S
- $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
+ $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval((int) ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
}
if (strlen($alt_deg) == 3) { // [+-]DDD.D
@@ -162,7 +168,7 @@
} elseif (strlen($alt_deg) == 5) { // [+-]DDDMM.M
$ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
} elseif (strlen($alt_deg) == 7) { // [+-]DDDMMSS.S
- $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
+ $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval((int) ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
}
foreach (array('latitude', 'longitude', 'altitude') as $key) {
@@ -259,7 +265,9 @@
} 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
@@ -324,7 +332,7 @@
}
} elseif (isset($value_array['time_to_sample_table'])) {
foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
- if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
+ if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0) && !empty($info['quicktime']['time_scale'])) {
$framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
$framecount = $value_array2['sample_count'];
}
@@ -768,8 +776,8 @@
case 'stsd': // Sample Table Sample Description atom
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
+ $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
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
// see: https://github.com/JamesHeinrich/getID3/issues/111
@@ -797,7 +805,6 @@
$stsdEntriesDataOffset += 2;
$atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
-
if (substr($atom_structure['sample_description_table'][$i]['data'], 1, 54) == 'application/octet-stream;type=com.parrot.videometadata') {
// special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones
$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type'] = substr($atom_structure['sample_description_table'][$i]['data'], 1, 55);
@@ -843,6 +850,7 @@
case 'dvcp':
case 'gif ':
case 'h263':
+ case 'hvc1':
case 'jpeg':
case 'kpcd':
case 'mjpa':
@@ -884,7 +892,8 @@
break;
case 'mp4a':
- default:
+ $atom_structure['sample_description_table'][$i]['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_structure['sample_description_table'][$i]['data'], 20), $baseoffset + $stsdEntriesDataOffset - 20 - 16, $atomHierarchy, $ParseAllPossibleAtoms);
+
$info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
$info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
$info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
@@ -910,6 +919,9 @@
break;
}
break;
+
+ default:
+ break;
}
break;
@@ -1541,6 +1553,21 @@
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
@@ -1642,7 +1669,7 @@
);
$atom_structure['data'] = $atom_data;
$atom_structure['image_mime'] = 'image/jpeg';
- $atom_structure['description'] = isset($descriptions[$atomname]) ? $descriptions[$atomname] : 'Nikon preview image';
+ $atom_structure['description'] = $descriptions[$atomname];
$info['quicktime']['comments']['picture'][] = array(
'image_mime' => $atom_structure['image_mime'],
'data' => $atom_data,
@@ -1659,7 +1686,7 @@
case 'NCHD': // Nikon:MakerNoteVersion - https://exiftool.org/TagNames/Nikon.html
$makerNoteVersion = '';
for ($i = 0, $iMax = strlen($atom_data); $i < $iMax; ++$i) {
- if (ord($atom_data[$i]) >= 0x00 && ord($atom_data[$i]) <= 0x1F) {
+ if (ord($atom_data[$i]) <= 0x1F) {
$makerNoteVersion .= ' '.ord($atom_data[$i]);
} else {
$makerNoteVersion .= $atom_data[$i];
@@ -1700,7 +1727,8 @@
$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'];
@@ -2075,6 +2103,119 @@
$atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data);
break;
+
+ case 'esds': // Elementary Stream DeScriptor
+ // https://github.com/JamesHeinrich/getID3/issues/414
+ // https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/formats/mp4/es_descriptor.cc
+ // https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/formats/mp4/es_descriptor.h
+ $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
+ $esds_offset = 4;
+
+ $atom_structure['ES_DescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ if ($atom_structure['ES_DescrTag'] != 0x03) {
+ $this->warning('expecting esds.ES_DescrTag = 0x03, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DescrTag']).'), at offset '.$atom_structure['offset']);
+ break;
+ }
+ $atom_structure['ES_DescrSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
+
+ $atom_structure['ES_ID'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 2));
+ $esds_offset += 2;
+ $atom_structure['ES_flagsraw'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ $atom_structure['ES_flags']['stream_dependency'] = (bool) ($atom_structure['ES_flagsraw'] & 0x80);
+ $atom_structure['ES_flags']['url_flag'] = (bool) ($atom_structure['ES_flagsraw'] & 0x40);
+ $atom_structure['ES_flags']['ocr_stream'] = (bool) ($atom_structure['ES_flagsraw'] & 0x20);
+ $atom_structure['ES_stream_priority'] = ($atom_structure['ES_flagsraw'] & 0x1F);
+ if ($atom_structure['ES_flags']['url_flag']) {
+ $this->warning('Unsupported esds.url_flag enabled at offset '.$atom_structure['offset']);
+ break;
+ }
+ if ($atom_structure['ES_flags']['stream_dependency']) {
+ $atom_structure['ES_dependsOn_ES_ID'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 2));
+ $esds_offset += 2;
+ }
+ if ($atom_structure['ES_flags']['ocr_stream']) {
+ $atom_structure['ES_OCR_ES_Id'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 2));
+ $esds_offset += 2;
+ }
+
+ $atom_structure['ES_DecoderConfigDescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ if ($atom_structure['ES_DecoderConfigDescrTag'] != 0x04) {
+ $this->warning('expecting esds.ES_DecoderConfigDescrTag = 0x04, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DecoderConfigDescrTag']).'), at offset '.$atom_structure['offset']);
+ break;
+ }
+ $atom_structure['ES_DecoderConfigDescrTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
+
+ $atom_structure['ES_objectTypeIndication'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ // https://stackoverflow.com/questions/3987850
+ // 0x40 = "Audio ISO/IEC 14496-3" = MPEG-4 Audio
+ // 0x67 = "Audio ISO/IEC 13818-7 LowComplexity Profile" = MPEG-2 AAC LC
+ // 0x69 = "Audio ISO/IEC 13818-3" = MPEG-2 Backward Compatible Audio (MPEG-2 Layers 1, 2, and 3)
+ // 0x6B = "Audio ISO/IEC 11172-3" = MPEG-1 Audio (MPEG-1 Layers 1, 2, and 3)
+
+ $streamTypePlusFlags = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ $atom_structure['ES_streamType'] = ($streamTypePlusFlags & 0xFC) >> 2;
+ $atom_structure['ES_upStream'] = (bool) ($streamTypePlusFlags & 0x02) >> 1;
+ $atom_structure['ES_bufferSizeDB'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 3));
+ $esds_offset += 3;
+ $atom_structure['ES_maxBitrate'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 4));
+ $esds_offset += 4;
+ $atom_structure['ES_avgBitrate'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 4));
+ $esds_offset += 4;
+ if ($atom_structure['ES_avgBitrate']) {
+ $info['quicktime']['audio']['bitrate'] = $atom_structure['ES_avgBitrate'];
+ $info['audio']['bitrate'] = $atom_structure['ES_avgBitrate'];
+ }
+
+ $atom_structure['ES_DecSpecificInfoTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ if ($atom_structure['ES_DecSpecificInfoTag'] != 0x05) {
+ $this->warning('expecting esds.ES_DecSpecificInfoTag = 0x05, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DecSpecificInfoTag']).'), at offset '.$atom_structure['offset']);
+ break;
+ }
+ $atom_structure['ES_DecSpecificInfoTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
+
+ $atom_structure['ES_DecSpecificInfo'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, $atom_structure['ES_DecSpecificInfoTagSize']));
+ $esds_offset += $atom_structure['ES_DecSpecificInfoTagSize'];
+
+ $atom_structure['ES_SLConfigDescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
+ $esds_offset += 1;
+ if ($atom_structure['ES_SLConfigDescrTag'] != 0x06) {
+ $this->warning('expecting esds.ES_SLConfigDescrTag = 0x05, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_SLConfigDescrTag']).'), at offset '.$atom_structure['offset']);
+ break;
+ }
+ $atom_structure['ES_SLConfigDescrTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
+
+ $atom_structure['ES_SLConfigDescr'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, $atom_structure['ES_SLConfigDescrTagSize']));
+ $esds_offset += $atom_structure['ES_SLConfigDescrTagSize'];
+ 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
+$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;
@@ -2323,6 +2464,7 @@
$QuicktimeVideoCodecLookup['gif '] = 'GIF';
$QuicktimeVideoCodecLookup['h261'] = 'H261';
$QuicktimeVideoCodecLookup['h263'] = 'H263';
+ $QuicktimeVideoCodecLookup['hvc1'] = 'H.265/HEVC';
$QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
$QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
$QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
@@ -2943,6 +3085,7 @@
return array();
}
+
/**
* @param array $info
*