wp/wp-includes/ID3/module.audio-video.quicktime.php
changeset 19 3d72ae0968f4
parent 16 a86126ab1dd4
child 21 48c4eec2b7e6
equal deleted inserted replaced
18:be944660c56a 19:3d72ae0968f4
    22 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
    22 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
    23 
    23 
    24 class getid3_quicktime extends getid3_handler
    24 class getid3_quicktime extends getid3_handler
    25 {
    25 {
    26 
    26 
    27 	public $ReturnAtomData        = true;
    27 	/** audio-video.quicktime
       
    28 	 * return all parsed data from all atoms if true, otherwise just returned parsed metadata
       
    29 	 *
       
    30 	 * @var bool
       
    31 	 */
       
    32 	public $ReturnAtomData        = false;
       
    33 
       
    34 	/** audio-video.quicktime
       
    35 	 * return all parsed data from all atoms if true, otherwise just returned parsed metadata
       
    36 	 *
       
    37 	 * @var bool
       
    38 	 */
    28 	public $ParseAllPossibleAtoms = false;
    39 	public $ParseAllPossibleAtoms = false;
    29 
    40 
    30 	/**
    41 	/**
    31 	 * @return bool
    42 	 * @return bool
    32 	 */
    43 	 */
   168 				}
   179 				}
   169 				break;
   180 				break;
   170 			}
   181 			}
   171 		}
   182 		}
   172 
   183 
   173 		if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
   184 		if (!isset($info['bitrate']) && !empty($info['playtime_seconds'])) {
   174 			$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
   185 			$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
   175 		}
   186 		}
   176 		if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
   187 		if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
   177 			$info['audio']['bitrate'] = $info['bitrate'];
   188 			$info['audio']['bitrate'] = $info['bitrate'];
   178 		}
   189 		}
   558 											case  1: // text flag
   569 											case  1: // text flag
   559 											case 13: // image flag
   570 											case 13: // image flag
   560 											default:
   571 											default:
   561 												$atom_structure['data'] = substr($boxdata, 8);
   572 												$atom_structure['data'] = substr($boxdata, 8);
   562 												if ($atomname == 'covr') {
   573 												if ($atomname == 'covr') {
   563 													// not a foolproof check, but better than nothing
   574 													if (!empty($atom_structure['data'])) {
   564 													if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
   575 														$atom_structure['image_mime'] = 'image/unknown'; // provide default MIME type to ensure array keys exist
   565 														$atom_structure['image_mime'] = 'image/jpeg';
   576 														if (function_exists('getimagesizefromstring') && ($getimagesize = getimagesizefromstring($atom_structure['data'])) && !empty($getimagesize['mime'])) {
   566 													} elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
   577 															$atom_structure['image_mime'] = $getimagesize['mime'];
   567 														$atom_structure['image_mime'] = 'image/png';
   578 														} else {
   568 													} elseif (preg_match('#^GIF#', $atom_structure['data'])) {
   579 															// if getimagesizefromstring is not available, or fails for some reason, fall back to simple detection of common image formats
   569 														$atom_structure['image_mime'] = 'image/gif';
   580 															$ImageFormatSignatures = array(
       
   581 																'image/jpeg' => "\xFF\xD8\xFF",
       
   582 																'image/png'  => "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
       
   583 																'image/gif'  => 'GIF',
       
   584 															);
       
   585 															foreach ($ImageFormatSignatures as $mime => $image_format_signature) {
       
   586 																if (substr($atom_structure['data'], 0, strlen($image_format_signature)) == $image_format_signature) {
       
   587 																	$atom_structure['image_mime'] = $mime;
       
   588 																	break;
       
   589 																}
       
   590 															}
       
   591 														}
       
   592 														$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
       
   593 													} else {
       
   594 														$this->warning('Unknown empty "covr" image at offset '.$baseoffset);
   570 													}
   595 													}
   571 													$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
       
   572 												}
   596 												}
   573 												break;
   597 												break;
   574 
   598 
   575 										}
   599 										}
   576 										break;
   600 										break;
   726 					$atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
   750 					$atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
   727 
   751 
   728 					$atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
   752 					$atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
   729 					$atom_structure['flags']['slide_show']   = (bool) $atom_structure['slide_show_flag'];
   753 					$atom_structure['flags']['slide_show']   = (bool) $atom_structure['slide_show_flag'];
   730 
   754 
   731 					$ptv_lookup[0] = 'normal';
   755 					$ptv_lookup = array(
   732 					$ptv_lookup[1] = 'double';
   756 						0 => 'normal',
   733 					$ptv_lookup[2] = 'half';
   757 						1 => 'double',
   734 					$ptv_lookup[3] = 'full';
   758 						2 => 'half',
   735 					$ptv_lookup[4] = 'current';
   759 						3 => 'full',
       
   760 						4 => 'current'
       
   761 					);
   736 					if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
   762 					if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
   737 						$atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
   763 						$atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
   738 					} else {
   764 					} else {
   739 						$this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
   765 						$this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
   740 					}
   766 					}
   906 										$atom_structure['sample_description_table'][$i]['video_encoder_name_len']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34,  1));
   932 										$atom_structure['sample_description_table'][$i]['video_encoder_name_len']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34,  1));
   907 										$atom_structure['sample_description_table'][$i]['video_encoder_name']      =                             substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
   933 										$atom_structure['sample_description_table'][$i]['video_encoder_name']      =                             substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
   908 										$atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66,  2));
   934 										$atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66,  2));
   909 										$atom_structure['sample_description_table'][$i]['video_color_table_id']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68,  2));
   935 										$atom_structure['sample_description_table'][$i]['video_color_table_id']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68,  2));
   910 
   936 
   911 										$atom_structure['sample_description_table'][$i]['video_pixel_color_type']  = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
   937 										$atom_structure['sample_description_table'][$i]['video_pixel_color_type']  = (((int) $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
   912 										$atom_structure['sample_description_table'][$i]['video_pixel_color_name']  = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
   938 										$atom_structure['sample_description_table'][$i]['video_pixel_color_name']  = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
   913 
   939 
   914 										if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
   940 										if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
   915 											$info['quicktime']['video']['codec_fourcc']        = $atom_structure['sample_description_table'][$i]['data_format'];
   941 											$info['quicktime']['video']['codec_fourcc']        = $atom_structure['sample_description_table'][$i]['data_format'];
   916 											$info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
   942 											$info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
   917 											$info['quicktime']['video']['codec']               = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']);
   943 											$info['quicktime']['video']['codec']               = (((int) $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']);
   918 											$info['quicktime']['video']['color_depth']         = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
   944 											$info['quicktime']['video']['color_depth']         = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
   919 											$info['quicktime']['video']['color_depth_name']    = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
   945 											$info['quicktime']['video']['color_depth_name']    = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
   920 
   946 
   921 											$info['video']['codec']           = $info['quicktime']['video']['codec'];
   947 											$info['video']['codec']           = $info['quicktime']['video']['codec'];
   922 											$info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
   948 											$info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
  1596 						$this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
  1622 						$this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
  1597 					}
  1623 					}
  1598 					break;
  1624 					break;
  1599 
  1625 
  1600 				case 'NCDT':
  1626 				case 'NCDT':
  1601 					// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
  1627 					// https://exiftool.org/TagNames/Nikon.html
  1602 					// Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
  1628 					// Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
  1603 					$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
  1629 					$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
  1604 					break;
  1630 					break;
  1605 				case 'NCTH': // Nikon Camera THumbnail image
  1631 				case 'NCTH': // Nikon Camera THumbnail image
  1606 				case 'NCVW': // Nikon Camera preVieW image
  1632 				case 'NCVW': // Nikon Camera preVieW image
  1607 					// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
  1633 				case 'NCM1': // Nikon Camera preview iMage 1
       
  1634 				case 'NCM2': // Nikon Camera preview iMage 2
       
  1635 					// https://exiftool.org/TagNames/Nikon.html
  1608 					if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
  1636 					if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
       
  1637 						$descriptions = array(
       
  1638 							'NCTH' => 'Nikon Camera Thumbnail Image',
       
  1639 							'NCVW' => 'Nikon Camera Preview Image',
       
  1640 							'NCM1' => 'Nikon Camera Preview Image 1',
       
  1641 							'NCM2' => 'Nikon Camera Preview Image 2',
       
  1642 						);
  1609 						$atom_structure['data'] = $atom_data;
  1643 						$atom_structure['data'] = $atom_data;
  1610 						$atom_structure['image_mime'] = 'image/jpeg';
  1644 						$atom_structure['image_mime'] = 'image/jpeg';
  1611 						$atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
  1645 						$atom_structure['description'] = isset($descriptions[$atomname]) ? $descriptions[$atomname] : 'Nikon preview image';
  1612 						$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
  1646 						$info['quicktime']['comments']['picture'][] = array(
  1613 					}
  1647 							'image_mime' => $atom_structure['image_mime'],
  1614 					break;
  1648 							'data' => $atom_data,
  1615 				case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
  1649 							'description' => $atom_structure['description']
  1616 					$atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
  1650 						);
  1617 					break;
  1651 					}
  1618 				case 'NCHD': // Nikon:MakerNoteVersion  - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
  1652 					break;
  1619 				case 'NCDB': // Nikon                   - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
  1653 				case 'NCTG': // Nikon - https://exiftool.org/TagNames/Nikon.html#NCTG
  1620 				case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
  1654 					getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.nikon-nctg.php', __FILE__, true);
       
  1655 					$nikonNCTG = new getid3_tag_nikon_nctg($this->getid3);
       
  1656 
       
  1657 					$atom_structure['data'] = $nikonNCTG->parse($atom_data);
       
  1658 					break;
       
  1659 				case 'NCHD': // Nikon:MakerNoteVersion  - https://exiftool.org/TagNames/Nikon.html
       
  1660 					$makerNoteVersion = '';
       
  1661 					for ($i = 0, $iMax = strlen($atom_data); $i < $iMax; ++$i) {
       
  1662 						if (ord($atom_data[$i]) >= 0x00 && ord($atom_data[$i]) <= 0x1F) {
       
  1663 							$makerNoteVersion .= ' '.ord($atom_data[$i]);
       
  1664 						} else {
       
  1665 							$makerNoteVersion .= $atom_data[$i];
       
  1666 						}
       
  1667 					}
       
  1668 					$makerNoteVersion = rtrim($makerNoteVersion, "\x00");
       
  1669 					$atom_structure['data'] = array(
       
  1670 						'MakerNoteVersion' => $makerNoteVersion
       
  1671 					);
       
  1672 					break;
       
  1673 				case 'NCDB': // Nikon                   - https://exiftool.org/TagNames/Nikon.html
       
  1674 				case 'CNCV': // Canon:CompressorVersion - https://exiftool.org/TagNames/Canon.html
  1621 					$atom_structure['data'] = $atom_data;
  1675 					$atom_structure['data'] = $atom_data;
  1622 					break;
  1676 					break;
  1623 
  1677 
  1624 				case "\x00\x00\x00\x00":
  1678 				case "\x00\x00\x00\x00":
  1625 					// some kind of metacontainer, may contain a big data dump such as:
  1679 					// some kind of metacontainer, may contain a big data dump such as:
  1626 					// mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4
  1680 					// mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4
  1627 					// http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
  1681 					// https://xhelmboyx.tripod.com/formats/qti-layout.txt
  1628 
  1682 
  1629 					$atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  1683 					$atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  1630 					$atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
  1684 					$atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
  1631 					$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  1685 					$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  1632 					//$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  1686 					//$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  1719 									'rotation_data'  => array(),
  1773 									'rotation_data'  => array(),
  1720 									'unknown_count'  => 0,       // ID ??
  1774 									'unknown_count'  => 0,       // ID ??
  1721 									'unknown_data'   => array(),
  1775 									'unknown_data'   => array(),
  1722 									'debug_list'     => '',      // Used to debug variables stored as comma delimited strings
  1776 									'debug_list'     => '',      // Used to debug variables stored as comma delimited strings
  1723 							);
  1777 							);
       
  1778 							$debug_structure = array();
  1724 							$debug_structure['debug_items'] = array();
  1779 							$debug_structure['debug_items'] = array();
  1725 							// Can start loop here to decode all sensor data in 32 Byte chunks:
  1780 							// Can start loop here to decode all sensor data in 32 Byte chunks:
  1726 							foreach (str_split($atom_SENSOR_data, 32) as $sensor_key => $sensor_data) {
  1781 							foreach (str_split($atom_SENSOR_data, 32) as $sensor_key => $sensor_data) {
  1727 								// This gets me a data_type code to work out what data is in the next 31 bytes.
  1782 								// This gets me a data_type code to work out what data is in the next 31 bytes.
  1728 								$sensor_data_type = substr($sensor_data, 0, 1);
  1783 								$sensor_data_type = substr($sensor_data, 0, 1);
  2037 	 * @param bool   $ParseAllPossibleAtoms
  2092 	 * @param bool   $ParseAllPossibleAtoms
  2038 	 *
  2093 	 *
  2039 	 * @return array|false
  2094 	 * @return array|false
  2040 	 */
  2095 	 */
  2041 	public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
  2096 	public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
  2042 		$atom_structure  = false;
  2097 		$atom_structure = array();
  2043 		$subatomoffset  = 0;
  2098 		$subatomoffset  = 0;
  2044 		$subatomcounter = 0;
  2099 		$subatomcounter = 0;
  2045 		if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
  2100 		if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
  2046 			return false;
  2101 			return false;
  2047 		}
  2102 		}
  2055 				// to read user data atoms, you should allow for the terminating 0.
  2110 				// to read user data atoms, you should allow for the terminating 0.
  2056 				if (strlen($atom_data) > 12) {
  2111 				if (strlen($atom_data) > 12) {
  2057 					$subatomoffset += 4;
  2112 					$subatomoffset += 4;
  2058 					continue;
  2113 					continue;
  2059 				}
  2114 				}
  2060 				return $atom_structure;
  2115 				break;
  2061 			}
  2116 			}
  2062 			if (strlen($subatomdata) < ($subatomsize - 8)) {
  2117 			if (strlen($subatomdata) < ($subatomsize - 8)) {
  2063 			    // we don't have enough data to decode the subatom.
  2118 			    // we don't have enough data to decode the subatom.
  2064 			    // this may be because we are refusing to parse large subatoms, or it may be because this atom had its size set too large
  2119 			    // this may be because we are refusing to parse large subatoms, or it may be because this atom had its size set too large
  2065 			    // so we passed in the start of a following atom incorrectly?
  2120 			    // so we passed in the start of a following atom incorrectly?
  2066 			    return $atom_structure;
  2121 			    break;
  2067 			}
  2122 			}
  2068 			$atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
  2123 			$atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
  2069 			$subatomoffset += $subatomsize;
  2124 			$subatomoffset += $subatomsize;
  2070 		}
  2125 		}
       
  2126 
       
  2127 		if (empty($atom_structure)) {
       
  2128 			return false;
       
  2129 		}
       
  2130 
  2071 		return $atom_structure;
  2131 		return $atom_structure;
  2072 	}
  2132 	}
  2073 
  2133 
  2074 	/**
  2134 	/**
  2075 	 * @param string $data
  2135 	 * @param string $data
  2550 	 */
  2610 	 */
  2551 	public function QuicktimeContentRatingLookup($rtng) {
  2611 	public function QuicktimeContentRatingLookup($rtng) {
  2552 		static $QuicktimeContentRatingLookup = array();
  2612 		static $QuicktimeContentRatingLookup = array();
  2553 		if (empty($QuicktimeContentRatingLookup)) {
  2613 		if (empty($QuicktimeContentRatingLookup)) {
  2554 			$QuicktimeContentRatingLookup[0]  = 'None';
  2614 			$QuicktimeContentRatingLookup[0]  = 'None';
       
  2615 			$QuicktimeContentRatingLookup[1]  = 'Explicit';
  2555 			$QuicktimeContentRatingLookup[2]  = 'Clean';
  2616 			$QuicktimeContentRatingLookup[2]  = 'Clean';
  2556 			$QuicktimeContentRatingLookup[4]  = 'Explicit';
  2617 			$QuicktimeContentRatingLookup[4]  = 'Explicit (old)';
  2557 		}
  2618 		}
  2558 		return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
  2619 		return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
  2559 	}
  2620 	}
  2560 
  2621 
  2561 	/**
  2622 	/**
  2602 			$QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
  2663 			$QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
  2603 			$QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
  2664 			$QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
  2604 			$QuicktimeStoreFrontCodeLookup[143441] = 'United States';
  2665 			$QuicktimeStoreFrontCodeLookup[143441] = 'United States';
  2605 		}
  2666 		}
  2606 		return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
  2667 		return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
  2607 	}
       
  2608 
       
  2609 	/**
       
  2610 	 * @param string $atom_data
       
  2611 	 *
       
  2612 	 * @return array
       
  2613 	 */
       
  2614 	public function QuicktimeParseNikonNCTG($atom_data) {
       
  2615 		// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
       
  2616 		// Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
       
  2617 		// Data is stored as records of:
       
  2618 		// * 4 bytes record type
       
  2619 		// * 2 bytes size of data field type:
       
  2620 		//     0x0001 = flag   (size field *= 1-byte)
       
  2621 		//     0x0002 = char   (size field *= 1-byte)
       
  2622 		//     0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
       
  2623 		//     0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
       
  2624 		//     0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
       
  2625 		//     0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
       
  2626 		//     0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
       
  2627 		// * 2 bytes data size field
       
  2628 		// * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
       
  2629 		// all integers are stored BigEndian
       
  2630 
       
  2631 		$NCTGtagName = array(
       
  2632 			0x00000001 => 'Make',
       
  2633 			0x00000002 => 'Model',
       
  2634 			0x00000003 => 'Software',
       
  2635 			0x00000011 => 'CreateDate',
       
  2636 			0x00000012 => 'DateTimeOriginal',
       
  2637 			0x00000013 => 'FrameCount',
       
  2638 			0x00000016 => 'FrameRate',
       
  2639 			0x00000022 => 'FrameWidth',
       
  2640 			0x00000023 => 'FrameHeight',
       
  2641 			0x00000032 => 'AudioChannels',
       
  2642 			0x00000033 => 'AudioBitsPerSample',
       
  2643 			0x00000034 => 'AudioSampleRate',
       
  2644 			0x02000001 => 'MakerNoteVersion',
       
  2645 			0x02000005 => 'WhiteBalance',
       
  2646 			0x0200000b => 'WhiteBalanceFineTune',
       
  2647 			0x0200001e => 'ColorSpace',
       
  2648 			0x02000023 => 'PictureControlData',
       
  2649 			0x02000024 => 'WorldTime',
       
  2650 			0x02000032 => 'UnknownInfo',
       
  2651 			0x02000083 => 'LensType',
       
  2652 			0x02000084 => 'Lens',
       
  2653 		);
       
  2654 
       
  2655 		$offset = 0;
       
  2656 		$data = null;
       
  2657 		$datalength = strlen($atom_data);
       
  2658 		$parsed = array();
       
  2659 		while ($offset < $datalength) {
       
  2660 			$record_type       = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));  $offset += 4;
       
  2661 			$data_size_type    = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));  $offset += 2;
       
  2662 			$data_size         = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));  $offset += 2;
       
  2663 			switch ($data_size_type) {
       
  2664 				case 0x0001: // 0x0001 = flag   (size field *= 1-byte)
       
  2665 					$data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
       
  2666 					$offset += ($data_size * 1);
       
  2667 					break;
       
  2668 				case 0x0002: // 0x0002 = char   (size field *= 1-byte)
       
  2669 					$data = substr($atom_data, $offset, $data_size * 1);
       
  2670 					$offset += ($data_size * 1);
       
  2671 					$data = rtrim($data, "\x00");
       
  2672 					break;
       
  2673 				case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
       
  2674 					$data = '';
       
  2675 					for ($i = $data_size - 1; $i >= 0; $i--) {
       
  2676 						$data .= substr($atom_data, $offset + ($i * 2), 2);
       
  2677 					}
       
  2678 					$data = getid3_lib::BigEndian2Int($data);
       
  2679 					$offset += ($data_size * 2);
       
  2680 					break;
       
  2681 				case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
       
  2682 					$data = '';
       
  2683 					for ($i = $data_size - 1; $i >= 0; $i--) {
       
  2684 						$data .= substr($atom_data, $offset + ($i * 4), 4);
       
  2685 					}
       
  2686 					$data = getid3_lib::BigEndian2Int($data);
       
  2687 					$offset += ($data_size * 4);
       
  2688 					break;
       
  2689 				case 0x0005: // 0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
       
  2690 					$data = array();
       
  2691 					for ($i = 0; $i < $data_size; $i++) {
       
  2692 						$numerator    = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
       
  2693 						$denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
       
  2694 						if ($denomninator == 0) {
       
  2695 							$data[$i] = false;
       
  2696 						} else {
       
  2697 							$data[$i] = (double) $numerator / $denomninator;
       
  2698 						}
       
  2699 					}
       
  2700 					$offset += (8 * $data_size);
       
  2701 					if (count($data) == 1) {
       
  2702 						$data = $data[0];
       
  2703 					}
       
  2704 					break;
       
  2705 				case 0x0007: // 0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
       
  2706 					$data = substr($atom_data, $offset, $data_size * 1);
       
  2707 					$offset += ($data_size * 1);
       
  2708 					break;
       
  2709 				case 0x0008: // 0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
       
  2710 					$data = substr($atom_data, $offset, $data_size * 2);
       
  2711 					$offset += ($data_size * 2);
       
  2712 					break;
       
  2713 				default:
       
  2714 					echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
       
  2715 					break 2;
       
  2716 			}
       
  2717 
       
  2718 			switch ($record_type) {
       
  2719 				case 0x00000011: // CreateDate
       
  2720 				case 0x00000012: // DateTimeOriginal
       
  2721 					$data = strtotime($data);
       
  2722 					break;
       
  2723 				case 0x0200001e: // ColorSpace
       
  2724 					switch ($data) {
       
  2725 						case 1:
       
  2726 							$data = 'sRGB';
       
  2727 							break;
       
  2728 						case 2:
       
  2729 							$data = 'Adobe RGB';
       
  2730 							break;
       
  2731 					}
       
  2732 					break;
       
  2733 				case 0x02000023: // PictureControlData
       
  2734 					$PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
       
  2735 					$FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange',    0x83=>'red', 0x84=>'green',  0xff=>'n/a');
       
  2736 					$ToningEffect = array(0x80=>'b&w', 0x81=>'sepia',  0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a');
       
  2737 					$data = array(
       
  2738 						'PictureControlVersion'     =>                           substr($data,  0,  4),
       
  2739 						'PictureControlName'        =>                     rtrim(substr($data,  4, 20), "\x00"),
       
  2740 						'PictureControlBase'        =>                     rtrim(substr($data, 24, 20), "\x00"),
       
  2741 						//'?'                       =>                           substr($data, 44,  4),
       
  2742 						'PictureControlAdjust'      => $PictureControlAdjust[ord(substr($data, 48,  1))],
       
  2743 						'PictureControlQuickAdjust' =>                       ord(substr($data, 49,  1)),
       
  2744 						'Sharpness'                 =>                       ord(substr($data, 50,  1)),
       
  2745 						'Contrast'                  =>                       ord(substr($data, 51,  1)),
       
  2746 						'Brightness'                =>                       ord(substr($data, 52,  1)),
       
  2747 						'Saturation'                =>                       ord(substr($data, 53,  1)),
       
  2748 						'HueAdjustment'             =>                       ord(substr($data, 54,  1)),
       
  2749 						'FilterEffect'              =>         $FilterEffect[ord(substr($data, 55,  1))],
       
  2750 						'ToningEffect'              =>         $ToningEffect[ord(substr($data, 56,  1))],
       
  2751 						'ToningSaturation'          =>                       ord(substr($data, 57,  1)),
       
  2752 					);
       
  2753 					break;
       
  2754 				case 0x02000024: // WorldTime
       
  2755 					// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
       
  2756 					// timezone is stored as offset from GMT in minutes
       
  2757 					$timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
       
  2758 					if ($timezone & 0x8000) {
       
  2759 						$timezone = 0 - (0x10000 - $timezone);
       
  2760 					}
       
  2761 					$timezone /= 60;
       
  2762 
       
  2763 					$dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
       
  2764 					switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
       
  2765 						case 2:
       
  2766 							$datedisplayformat = 'D/M/Y'; break;
       
  2767 						case 1:
       
  2768 							$datedisplayformat = 'M/D/Y'; break;
       
  2769 						case 0:
       
  2770 						default:
       
  2771 							$datedisplayformat = 'Y/M/D'; break;
       
  2772 					}
       
  2773 
       
  2774 					$data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
       
  2775 					break;
       
  2776 				case 0x02000083: // LensType
       
  2777 					$data = array(
       
  2778 						//'_'  => $data,
       
  2779 						'mf' => (bool) ($data & 0x01),
       
  2780 						'd'  => (bool) ($data & 0x02),
       
  2781 						'g'  => (bool) ($data & 0x04),
       
  2782 						'vr' => (bool) ($data & 0x08),
       
  2783 					);
       
  2784 					break;
       
  2785 			}
       
  2786 			$tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
       
  2787 			$parsed[$tag_name] = $data;
       
  2788 		}
       
  2789 		return $parsed;
       
  2790 	}
  2668 	}
  2791 
  2669 
  2792 	/**
  2670 	/**
  2793 	 * @param string $keyname
  2671 	 * @param string $keyname
  2794 	 * @param string|array $data
  2672 	 * @param string|array $data