wp/wp-includes/ID3/module.audio-video.quicktime.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    59 				break;
    59 				break;
    60 			}
    60 			}
    61 			$this->fseek($offset);
    61 			$this->fseek($offset);
    62 			$AtomHeader = $this->fread(8);
    62 			$AtomHeader = $this->fread(8);
    63 
    63 
       
    64 			// https://github.com/JamesHeinrich/getID3/issues/382
       
    65 			// Atom sizes are stored as 32-bit number in most cases, but sometimes (notably for "mdat")
       
    66 			// a 64-bit value is required, in which case the normal 32-bit size field is set to 0x00000001
       
    67 			// and the 64-bit "real" size value is the next 8 bytes.
       
    68 			$atom_size_extended_bytes = 0;
    64 			$atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
    69 			$atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
    65 			$atomname = substr($AtomHeader, 4, 4);
    70 			$atomname = substr($AtomHeader, 4, 4);
    66 
       
    67 			// 64-bit MOV patch by jlegateØktnc*com
       
    68 			if ($atomsize == 1) {
    71 			if ($atomsize == 1) {
    69 				$atomsize = getid3_lib::BigEndian2Int($this->fread(8));
    72 				$atom_size_extended_bytes = 8;
       
    73 				$atomsize = getid3_lib::BigEndian2Int($this->fread($atom_size_extended_bytes));
    70 			}
    74 			}
    71 
    75 
    72 			if (($offset + $atomsize) > $info['avdataend']) {
    76 			if (($offset + $atomsize) > $info['avdataend']) {
    73 				$info['quicktime'][$atomname]['name']   = $atomname;
    77 				$info['quicktime'][$atomname]['name']   = $atomname;
    74 				$info['quicktime'][$atomname]['size']   = $atomsize;
    78 				$info['quicktime'][$atomname]['size']   = $atomsize;
    83 				$info['quicktime'][$atomname]['name']   = $atomname;
    87 				$info['quicktime'][$atomname]['name']   = $atomname;
    84 				$info['quicktime'][$atomname]['size']   = $atomsize;
    88 				$info['quicktime'][$atomname]['size']   = $atomsize;
    85 				$info['quicktime'][$atomname]['offset'] = $offset;
    89 				$info['quicktime'][$atomname]['offset'] = $offset;
    86 				break;
    90 				break;
    87 			}
    91 			}
    88 
       
    89 			$atomHierarchy = array();
    92 			$atomHierarchy = array();
    90 			$parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
    93 			$parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize - $atom_size_extended_bytes, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
    91 			$parsedAtomData['name']   = $atomname;
    94 			$parsedAtomData['name']   = $atomname;
    92 			$parsedAtomData['size']   = $atomsize;
    95 			$parsedAtomData['size']   = $atomsize;
    93 			$parsedAtomData['offset'] = $offset;
    96 			$parsedAtomData['offset'] = $offset;
       
    97 			if ($atom_size_extended_bytes) {
       
    98 				$parsedAtomData['xsize_bytes'] = $atom_size_extended_bytes;
       
    99 			}
    94 			if (in_array($atomname, array('uuid'))) {
   100 			if (in_array($atomname, array('uuid'))) {
    95 				@$info['quicktime'][$atomname][] = $parsedAtomData;
   101 				@$info['quicktime'][$atomname][] = $parsedAtomData;
    96 			} else {
   102 			} else {
    97 				$info['quicktime'][$atomname] = $parsedAtomData;
   103 				$info['quicktime'][$atomname] = $parsedAtomData;
    98 			}
   104 			}
   106 			// otherwise any atoms beyond the 'mdat' atom would not get parsed
   112 			// otherwise any atoms beyond the 'mdat' atom would not get parsed
   107 			$info['avdataend'] = $info['avdataend_tmp'];
   113 			$info['avdataend'] = $info['avdataend_tmp'];
   108 			unset($info['avdataend_tmp']);
   114 			unset($info['avdataend_tmp']);
   109 		}
   115 		}
   110 
   116 
   111 		if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
   117 		if (isset($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
   112 			$durations = $this->quicktime_time_to_sample_table($info);
   118 			$durations = $this->quicktime_time_to_sample_table($info);
   113 			for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
   119 			for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
   114 				$bookmark = array();
   120 				$bookmark = array();
   115 				$bookmark['title'] = $info['quicktime']['comments']['chapters'][$i];
   121 				$bookmark['title'] = $info['quicktime']['comments']['chapters'][$i];
   116 				if (isset($durations[$i])) {
   122 				if (isset($durations[$i])) {
   144 					if (strlen($lat_deg) == 2) {        // [+-]DD.D
   150 					if (strlen($lat_deg) == 2) {        // [+-]DD.D
   145 						$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim($lat_deg, '0').$lat_deg_dec);
   151 						$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim($lat_deg, '0').$lat_deg_dec);
   146 					} elseif (strlen($lat_deg) == 4) {  // [+-]DDMM.M
   152 					} elseif (strlen($lat_deg) == 4) {  // [+-]DDMM.M
   147 						$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);
   153 						$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);
   148 					} elseif (strlen($lat_deg) == 6) {  // [+-]DDMMSS.S
   154 					} elseif (strlen($lat_deg) == 6) {  // [+-]DDMMSS.S
   149 						$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);
   155 						$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);
   150 					}
   156 					}
   151 
   157 
   152 					if (strlen($lon_deg) == 3) {        // [+-]DDD.D
   158 					if (strlen($lon_deg) == 3) {        // [+-]DDD.D
   153 						$ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim($lon_deg, '0').$lon_deg_dec);
   159 						$ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim($lon_deg, '0').$lon_deg_dec);
   154 					} elseif (strlen($lon_deg) == 5) {  // [+-]DDDMM.M
   160 					} elseif (strlen($lon_deg) == 5) {  // [+-]DDDMM.M
   155 						$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);
   161 						$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);
   156 					} elseif (strlen($lon_deg) == 7) {  // [+-]DDDMMSS.S
   162 					} elseif (strlen($lon_deg) == 7) {  // [+-]DDDMMSS.S
   157 						$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);
   163 						$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);
   158 					}
   164 					}
   159 
   165 
   160 					if (strlen($alt_deg) == 3) {        // [+-]DDD.D
   166 					if (strlen($alt_deg) == 3) {        // [+-]DDD.D
   161 						$ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim($alt_deg, '0').$alt_deg_dec);
   167 						$ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim($alt_deg, '0').$alt_deg_dec);
   162 					} elseif (strlen($alt_deg) == 5) {  // [+-]DDDMM.M
   168 					} elseif (strlen($alt_deg) == 5) {  // [+-]DDDMM.M
   163 						$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);
   169 						$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);
   164 					} elseif (strlen($alt_deg) == 7) {  // [+-]DDDMMSS.S
   170 					} elseif (strlen($alt_deg) == 7) {  // [+-]DDDMMSS.S
   165 						$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);
   171 						$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);
   166 					}
   172 					}
   167 
   173 
   168 					foreach (array('latitude', 'longitude', 'altitude') as $key) {
   174 					foreach (array('latitude', 'longitude', 'altitude') as $key) {
   169 						if ($ISO6709parsed[$key] !== false) {
   175 						if ($ISO6709parsed[$key] !== false) {
   170 							$value = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
   176 							$value = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
   257 			$atom_structure['name'] = $atomname;
   263 			$atom_structure['name'] = $atomname;
   258 			$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
   264 			$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
   259 		} else {
   265 		} else {
   260 			switch ($atomname) {
   266 			switch ($atomname) {
   261 				case 'moov': // MOVie container atom
   267 				case 'moov': // MOVie container atom
       
   268 				case 'moof': // MOvie Fragment box
   262 				case 'trak': // TRAcK container atom
   269 				case 'trak': // TRAcK container atom
       
   270 				case 'traf': // TRAck Fragment box
   263 				case 'clip': // CLIPping container atom
   271 				case 'clip': // CLIPping container atom
   264 				case 'matt': // track MATTe container atom
   272 				case 'matt': // track MATTe container atom
   265 				case 'edts': // EDiTS container atom
   273 				case 'edts': // EDiTS container atom
   266 				case 'tref': // Track REFerence container atom
   274 				case 'tref': // Track REFerence container atom
   267 				case 'mdia': // MeDIA container atom
   275 				case 'mdia': // MeDIA container atom
   322 									}
   330 									}
   323 								}
   331 								}
   324 							}
   332 							}
   325 						} elseif (isset($value_array['time_to_sample_table'])) {
   333 						} elseif (isset($value_array['time_to_sample_table'])) {
   326 							foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
   334 							foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
   327 								if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
   335 								if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0) && !empty($info['quicktime']['time_scale'])) {
   328 									$framerate  = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
   336 									$framerate  = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
   329 									$framecount = $value_array2['sample_count'];
   337 									$framecount = $value_array2['sample_count'];
   330 								}
   338 								}
   331 							}
   339 							}
   332 						}
   340 						}
   766 					}
   774 					}
   767 					break;
   775 					break;
   768 
   776 
   769 
   777 
   770 				case 'stsd': // Sample Table Sample Description atom
   778 				case 'stsd': // Sample Table Sample Description atom
   771 					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
   779 					$atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1)); // hardcoded: 0x00
   772 					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
   780 					$atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x000000
   773 					$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
   781 					$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
   774 
   782 
   775 					// see: https://github.com/JamesHeinrich/getID3/issues/111
   783 					// see: https://github.com/JamesHeinrich/getID3/issues/111
   776 					// Some corrupt files have been known to have high bits set in the number_entries field
   784 					// Some corrupt files have been known to have high bits set in the number_entries field
   777 					// This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000
   785 					// This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000
   795 						$stsdEntriesDataOffset += 6;
   803 						$stsdEntriesDataOffset += 6;
   796 						$atom_structure['sample_description_table'][$i]['reference_index']  = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
   804 						$atom_structure['sample_description_table'][$i]['reference_index']  = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
   797 						$stsdEntriesDataOffset += 2;
   805 						$stsdEntriesDataOffset += 2;
   798 						$atom_structure['sample_description_table'][$i]['data']             =                           substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
   806 						$atom_structure['sample_description_table'][$i]['data']             =                           substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
   799 						$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
   807 						$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
   800 
       
   801 						if (substr($atom_structure['sample_description_table'][$i]['data'],  1, 54) == 'application/octet-stream;type=com.parrot.videometadata') {
   808 						if (substr($atom_structure['sample_description_table'][$i]['data'],  1, 54) == 'application/octet-stream;type=com.parrot.videometadata') {
   802 							// special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones
   809 							// special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones
   803 							$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type']        =       substr($atom_structure['sample_description_table'][$i]['data'],  1, 55);
   810 							$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type']        =       substr($atom_structure['sample_description_table'][$i]['data'],  1, 55);
   804 							$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['metadata_version'] = (int) substr($atom_structure['sample_description_table'][$i]['data'], 55,  1);
   811 							$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['metadata_version'] = (int) substr($atom_structure['sample_description_table'][$i]['data'], 55,  1);
   805 							unset($atom_structure['sample_description_table'][$i]['data']);
   812 							unset($atom_structure['sample_description_table'][$i]['data']);
   841 									case 'cvid':
   848 									case 'cvid':
   842 									case 'dvc ':
   849 									case 'dvc ':
   843 									case 'dvcp':
   850 									case 'dvcp':
   844 									case 'gif ':
   851 									case 'gif ':
   845 									case 'h263':
   852 									case 'h263':
       
   853 									case 'hvc1':
   846 									case 'jpeg':
   854 									case 'jpeg':
   847 									case 'kpcd':
   855 									case 'kpcd':
   848 									case 'mjpa':
   856 									case 'mjpa':
   849 									case 'mjpb':
   857 									case 'mjpb':
   850 									case 'mp4v':
   858 									case 'mp4v':
   882 									case 'qtvr':
   890 									case 'qtvr':
   883 										$info['video']['dataformat'] = 'quicktimevr';
   891 										$info['video']['dataformat'] = 'quicktimevr';
   884 										break;
   892 										break;
   885 
   893 
   886 									case 'mp4a':
   894 									case 'mp4a':
   887 									default:
   895 										$atom_structure['sample_description_table'][$i]['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_structure['sample_description_table'][$i]['data'], 20), $baseoffset + $stsdEntriesDataOffset - 20 - 16, $atomHierarchy, $ParseAllPossibleAtoms);
       
   896 
   888 										$info['quicktime']['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
   897 										$info['quicktime']['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
   889 										$info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
   898 										$info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
   890 										$info['quicktime']['audio']['channels']    = $atom_structure['sample_description_table'][$i]['audio_channels'];
   899 										$info['quicktime']['audio']['channels']    = $atom_structure['sample_description_table'][$i]['audio_channels'];
   891 										$info['quicktime']['audio']['bit_depth']   = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
   900 										$info['quicktime']['audio']['bit_depth']   = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
   892 										$info['audio']['codec']                    = $info['quicktime']['audio']['codec'];
   901 										$info['audio']['codec']                    = $info['quicktime']['audio']['codec'];
   907 												break;
   916 												break;
   908 											default:
   917 											default:
   909 												$info['audio']['lossless'] = false;
   918 												$info['audio']['lossless'] = false;
   910 												break;
   919 												break;
   911 										}
   920 										}
       
   921 										break;
       
   922 
       
   923 									default:
   912 										break;
   924 										break;
   913 								}
   925 								}
   914 								break;
   926 								break;
   915 
   927 
   916 							default:
   928 							default:
  1539 					}
  1551 					}
  1540 
  1552 
  1541 					unset($mdat_offset, $chapter_string_length, $chapter_matches);
  1553 					unset($mdat_offset, $chapter_string_length, $chapter_matches);
  1542 					break;
  1554 					break;
  1543 
  1555 
       
  1556 				case 'ID32': // ID3v2
       
  1557 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
       
  1558 
       
  1559 					$getid3_temp = new getID3();
       
  1560 					$getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
       
  1561 					$getid3_id3v2 = new getid3_id3v2($getid3_temp);
       
  1562 					$getid3_id3v2->StartingOffset = $atom_structure['offset'] + 14; // framelength(4)+framename(4)+flags(4)+??(2)
       
  1563 					if ($atom_structure['valid'] = $getid3_id3v2->Analyze()) {
       
  1564 						$atom_structure['id3v2'] = $getid3_temp->info['id3v2'];
       
  1565 					} else {
       
  1566 						$this->warning('ID32 frame at offset '.$atom_structure['offset'].' did not parse');
       
  1567 					}
       
  1568 					unset($getid3_temp, $getid3_id3v2);
       
  1569 					break;
       
  1570 
  1544 				case 'free': // FREE space atom
  1571 				case 'free': // FREE space atom
  1545 				case 'skip': // SKIP atom
  1572 				case 'skip': // SKIP atom
  1546 				case 'wide': // 64-bit expansion placeholder atom
  1573 				case 'wide': // 64-bit expansion placeholder atom
  1547 					// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
  1574 					// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
  1548 
  1575 
  1640 							'NCM1' => 'Nikon Camera Preview Image 1',
  1667 							'NCM1' => 'Nikon Camera Preview Image 1',
  1641 							'NCM2' => 'Nikon Camera Preview Image 2',
  1668 							'NCM2' => 'Nikon Camera Preview Image 2',
  1642 						);
  1669 						);
  1643 						$atom_structure['data'] = $atom_data;
  1670 						$atom_structure['data'] = $atom_data;
  1644 						$atom_structure['image_mime'] = 'image/jpeg';
  1671 						$atom_structure['image_mime'] = 'image/jpeg';
  1645 						$atom_structure['description'] = isset($descriptions[$atomname]) ? $descriptions[$atomname] : 'Nikon preview image';
  1672 						$atom_structure['description'] = $descriptions[$atomname];
  1646 						$info['quicktime']['comments']['picture'][] = array(
  1673 						$info['quicktime']['comments']['picture'][] = array(
  1647 							'image_mime' => $atom_structure['image_mime'],
  1674 							'image_mime' => $atom_structure['image_mime'],
  1648 							'data' => $atom_data,
  1675 							'data' => $atom_data,
  1649 							'description' => $atom_structure['description']
  1676 							'description' => $atom_structure['description']
  1650 						);
  1677 						);
  1657 					$atom_structure['data'] = $nikonNCTG->parse($atom_data);
  1684 					$atom_structure['data'] = $nikonNCTG->parse($atom_data);
  1658 					break;
  1685 					break;
  1659 				case 'NCHD': // Nikon:MakerNoteVersion  - https://exiftool.org/TagNames/Nikon.html
  1686 				case 'NCHD': // Nikon:MakerNoteVersion  - https://exiftool.org/TagNames/Nikon.html
  1660 					$makerNoteVersion = '';
  1687 					$makerNoteVersion = '';
  1661 					for ($i = 0, $iMax = strlen($atom_data); $i < $iMax; ++$i) {
  1688 					for ($i = 0, $iMax = strlen($atom_data); $i < $iMax; ++$i) {
  1662 						if (ord($atom_data[$i]) >= 0x00 && ord($atom_data[$i]) <= 0x1F) {
  1689 						if (ord($atom_data[$i]) <= 0x1F) {
  1663 							$makerNoteVersion .= ' '.ord($atom_data[$i]);
  1690 							$makerNoteVersion .= ' '.ord($atom_data[$i]);
  1664 						} else {
  1691 						} else {
  1665 							$makerNoteVersion .= $atom_data[$i];
  1692 							$makerNoteVersion .= $atom_data[$i];
  1666 						}
  1693 						}
  1667 					}
  1694 					}
  1698 					static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other
  1725 					static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other
  1699 					// seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
  1726 					// seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
  1700 					$atom_structure['language'] =                           substr($atom_data, 4 + 0, 2);
  1727 					$atom_structure['language'] =                           substr($atom_data, 4 + 0, 2);
  1701 					$atom_structure['unknown']  = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
  1728 					$atom_structure['unknown']  = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
  1702 					$atom_structure['data']     =                           substr($atom_data, 4 + 4);
  1729 					$atom_structure['data']     =                           substr($atom_data, 4 + 4);
  1703 					$atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++];
  1730 					$atom_structure['key_name'] = (isset($info['quicktime']['temp_meta_key_names'][$metaDATAkey]) ? $info['quicktime']['temp_meta_key_names'][$metaDATAkey] : '');
       
  1731 					$metaDATAkey++;
  1704 
  1732 
  1705 					if ($atom_structure['key_name'] && $atom_structure['data']) {
  1733 					if ($atom_structure['key_name'] && $atom_structure['data']) {
  1706 						@$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
  1734 						@$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
  1707 					}
  1735 					}
  1708 					break;
  1736 					break;
  2073 					// A QuickTime movie can contain none, one, or several timed metadata tracks. Timed metadata tracks can refer to multiple tracks.
  2101 					// A QuickTime movie can contain none, one, or several timed metadata tracks. Timed metadata tracks can refer to multiple tracks.
  2074 					// Metadata tracks are linked to the tracks they describe using a track-reference of type 'cdsc'. The metadata track holds the 'cdsc' track reference.
  2102 					// Metadata tracks are linked to the tracks they describe using a track-reference of type 'cdsc'. The metadata track holds the 'cdsc' track reference.
  2075 					$atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data);
  2103 					$atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data);
  2076 					break;
  2104 					break;
  2077 
  2105 
       
  2106 
       
  2107 				case 'esds': // Elementary Stream DeScriptor
       
  2108 					// https://github.com/JamesHeinrich/getID3/issues/414
       
  2109 					// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/formats/mp4/es_descriptor.cc
       
  2110 					// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/formats/mp4/es_descriptor.h
       
  2111 					$atom_structure['version']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1)); // hardcoded: 0x00
       
  2112 					$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x000000
       
  2113 					$esds_offset = 4;
       
  2114 
       
  2115 					$atom_structure['ES_DescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2116 					$esds_offset += 1;
       
  2117 					if ($atom_structure['ES_DescrTag'] != 0x03) {
       
  2118 						$this->warning('expecting esds.ES_DescrTag = 0x03, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DescrTag']).'), at offset '.$atom_structure['offset']);
       
  2119 						break;
       
  2120 					}
       
  2121 					$atom_structure['ES_DescrSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
       
  2122 
       
  2123 					$atom_structure['ES_ID'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 2));
       
  2124 					$esds_offset += 2;
       
  2125 					$atom_structure['ES_flagsraw'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2126 					$esds_offset += 1;
       
  2127 					$atom_structure['ES_flags']['stream_dependency'] = (bool) ($atom_structure['ES_flagsraw'] & 0x80);
       
  2128 					$atom_structure['ES_flags']['url_flag']          = (bool) ($atom_structure['ES_flagsraw'] & 0x40);
       
  2129 					$atom_structure['ES_flags']['ocr_stream']        = (bool) ($atom_structure['ES_flagsraw'] & 0x20);
       
  2130 					$atom_structure['ES_stream_priority']            =        ($atom_structure['ES_flagsraw'] & 0x1F);
       
  2131 					if ($atom_structure['ES_flags']['url_flag']) {
       
  2132 						$this->warning('Unsupported esds.url_flag enabled at offset '.$atom_structure['offset']);
       
  2133 						break;
       
  2134 					}
       
  2135 					if ($atom_structure['ES_flags']['stream_dependency']) {
       
  2136 						$atom_structure['ES_dependsOn_ES_ID'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 2));
       
  2137 						$esds_offset += 2;
       
  2138 					}
       
  2139 					if ($atom_structure['ES_flags']['ocr_stream']) {
       
  2140 						$atom_structure['ES_OCR_ES_Id'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 2));
       
  2141 						$esds_offset += 2;
       
  2142 					}
       
  2143 
       
  2144 					$atom_structure['ES_DecoderConfigDescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2145 					$esds_offset += 1;
       
  2146 					if ($atom_structure['ES_DecoderConfigDescrTag'] != 0x04) {
       
  2147 						$this->warning('expecting esds.ES_DecoderConfigDescrTag = 0x04, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DecoderConfigDescrTag']).'), at offset '.$atom_structure['offset']);
       
  2148 						break;
       
  2149 					}
       
  2150 					$atom_structure['ES_DecoderConfigDescrTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
       
  2151 
       
  2152 					$atom_structure['ES_objectTypeIndication'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2153 					$esds_offset += 1;
       
  2154 					// https://stackoverflow.com/questions/3987850
       
  2155 					// 0x40 = "Audio ISO/IEC 14496-3"                       = MPEG-4 Audio
       
  2156 					// 0x67 = "Audio ISO/IEC 13818-7 LowComplexity Profile" = MPEG-2 AAC LC
       
  2157 					// 0x69 = "Audio ISO/IEC 13818-3"                       = MPEG-2 Backward Compatible Audio (MPEG-2 Layers 1, 2, and 3)
       
  2158 					// 0x6B = "Audio ISO/IEC 11172-3"                       = MPEG-1 Audio (MPEG-1 Layers 1, 2, and 3)
       
  2159 
       
  2160 					$streamTypePlusFlags = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2161 					$esds_offset += 1;
       
  2162 					$atom_structure['ES_streamType'] =        ($streamTypePlusFlags & 0xFC) >> 2;
       
  2163 					$atom_structure['ES_upStream']   = (bool) ($streamTypePlusFlags & 0x02) >> 1;
       
  2164 					$atom_structure['ES_bufferSizeDB'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 3));
       
  2165 					$esds_offset += 3;
       
  2166 					$atom_structure['ES_maxBitrate'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 4));
       
  2167 					$esds_offset += 4;
       
  2168 					$atom_structure['ES_avgBitrate'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 4));
       
  2169 					$esds_offset += 4;
       
  2170 					if ($atom_structure['ES_avgBitrate']) {
       
  2171 						$info['quicktime']['audio']['bitrate'] = $atom_structure['ES_avgBitrate'];
       
  2172 						$info['audio']['bitrate']              = $atom_structure['ES_avgBitrate'];
       
  2173 					}
       
  2174 
       
  2175 					$atom_structure['ES_DecSpecificInfoTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2176 					$esds_offset += 1;
       
  2177 					if ($atom_structure['ES_DecSpecificInfoTag'] != 0x05) {
       
  2178 						$this->warning('expecting esds.ES_DecSpecificInfoTag = 0x05, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DecSpecificInfoTag']).'), at offset '.$atom_structure['offset']);
       
  2179 						break;
       
  2180 					}
       
  2181 					$atom_structure['ES_DecSpecificInfoTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
       
  2182 
       
  2183 					$atom_structure['ES_DecSpecificInfo'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, $atom_structure['ES_DecSpecificInfoTagSize']));
       
  2184 					$esds_offset += $atom_structure['ES_DecSpecificInfoTagSize'];
       
  2185 
       
  2186 					$atom_structure['ES_SLConfigDescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1));
       
  2187 					$esds_offset += 1;
       
  2188 					if ($atom_structure['ES_SLConfigDescrTag'] != 0x06) {
       
  2189 						$this->warning('expecting esds.ES_SLConfigDescrTag = 0x05, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_SLConfigDescrTag']).'), at offset '.$atom_structure['offset']);
       
  2190 						break;
       
  2191 					}
       
  2192 					$atom_structure['ES_SLConfigDescrTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset);
       
  2193 
       
  2194 					$atom_structure['ES_SLConfigDescr'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, $atom_structure['ES_SLConfigDescrTagSize']));
       
  2195 					$esds_offset += $atom_structure['ES_SLConfigDescrTagSize'];
       
  2196 					break;
       
  2197 
       
  2198 // AVIF-related - https://docs.rs/avif-parse/0.13.2/src/avif_parse/boxes.rs.html
       
  2199 				case 'pitm': // Primary ITeM
       
  2200 				case 'iloc': // Item LOCation
       
  2201 				case 'iinf': // Item INFo
       
  2202 				case 'iref': // Image REFerence
       
  2203 				case 'iprp': // Image PRoPerties
       
  2204 $this->error('AVIF files not currently supported');
       
  2205 					$atom_structure['data'] = $atom_data;
       
  2206 					break;
       
  2207 
       
  2208 				case 'tfdt': // Track Fragment base media Decode Time box
       
  2209 				case 'tfhd': // Track Fragment HeaDer box
       
  2210 				case 'mfhd': // Movie Fragment HeaDer box
       
  2211 				case 'trun': // Track fragment RUN box
       
  2212 $this->error('fragmented mp4 files not currently supported');
       
  2213 					$atom_structure['data'] = $atom_data;
       
  2214 					break;
       
  2215 
       
  2216 				case 'mvex': // MoVie EXtends box
       
  2217 				case 'pssh': // Protection System Specific Header box
       
  2218 				case 'sidx': // Segment InDeX box
  2078 				default:
  2219 				default:
  2079 					$this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset);
  2220 					$this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset);
  2080 					$atom_structure['data'] = $atom_data;
  2221 					$atom_structure['data'] = $atom_data;
  2081 					break;
  2222 					break;
  2082 			}
  2223 			}
  2321 			$QuicktimeVideoCodecLookup['fire'] = 'Fire';
  2462 			$QuicktimeVideoCodecLookup['fire'] = 'Fire';
  2322 			$QuicktimeVideoCodecLookup['flic'] = 'FLC';
  2463 			$QuicktimeVideoCodecLookup['flic'] = 'FLC';
  2323 			$QuicktimeVideoCodecLookup['gif '] = 'GIF';
  2464 			$QuicktimeVideoCodecLookup['gif '] = 'GIF';
  2324 			$QuicktimeVideoCodecLookup['h261'] = 'H261';
  2465 			$QuicktimeVideoCodecLookup['h261'] = 'H261';
  2325 			$QuicktimeVideoCodecLookup['h263'] = 'H263';
  2466 			$QuicktimeVideoCodecLookup['h263'] = 'H263';
       
  2467 			$QuicktimeVideoCodecLookup['hvc1'] = 'H.265/HEVC';
  2326 			$QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
  2468 			$QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
  2327 			$QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
  2469 			$QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
  2328 			$QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
  2470 			$QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
  2329 			$QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
  2471 			$QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
  2330 			$QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
  2472 			$QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
  2941 			}
  3083 			}
  2942 		}
  3084 		}
  2943 		return array();
  3085 		return array();
  2944 	}
  3086 	}
  2945 
  3087 
       
  3088 
  2946 	/**
  3089 	/**
  2947 	 * @param array $info
  3090 	 * @param array $info
  2948 	 *
  3091 	 *
  2949 	 * @return int
  3092 	 * @return int
  2950 	 */
  3093 	 */