diff -r be944660c56a -r 3d72ae0968f4 wp/wp-includes/ID3/module.audio-video.quicktime.php --- a/wp/wp-includes/ID3/module.audio-video.quicktime.php Wed Sep 21 18:19:35 2022 +0200 +++ b/wp/wp-includes/ID3/module.audio-video.quicktime.php Tue Sep 27 16:37:53 2022 +0200 @@ -24,7 +24,18 @@ class getid3_quicktime extends getid3_handler { - public $ReturnAtomData = true; + /** audio-video.quicktime + * return all parsed data from all atoms if true, otherwise just returned parsed metadata + * + * @var bool + */ + public $ReturnAtomData = false; + + /** audio-video.quicktime + * return all parsed data from all atoms if true, otherwise just returned parsed metadata + * + * @var bool + */ public $ParseAllPossibleAtoms = false; /** @@ -170,7 +181,7 @@ } } - if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) { + if (!isset($info['bitrate']) && !empty($info['playtime_seconds'])) { $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; } if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) { @@ -560,15 +571,28 @@ default: $atom_structure['data'] = substr($boxdata, 8); if ($atomname == 'covr') { - // not a foolproof check, but better than nothing - if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { - $atom_structure['image_mime'] = 'image/jpeg'; - } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) { - $atom_structure['image_mime'] = 'image/png'; - } elseif (preg_match('#^GIF#', $atom_structure['data'])) { - $atom_structure['image_mime'] = 'image/gif'; + if (!empty($atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/unknown'; // provide default MIME type to ensure array keys exist + if (function_exists('getimagesizefromstring') && ($getimagesize = getimagesizefromstring($atom_structure['data'])) && !empty($getimagesize['mime'])) { + $atom_structure['image_mime'] = $getimagesize['mime']; + } else { + // if getimagesizefromstring is not available, or fails for some reason, fall back to simple detection of common image formats + $ImageFormatSignatures = array( + 'image/jpeg' => "\xFF\xD8\xFF", + 'image/png' => "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", + 'image/gif' => 'GIF', + ); + foreach ($ImageFormatSignatures as $mime => $image_format_signature) { + if (substr($atom_structure['data'], 0, strlen($image_format_signature)) == $image_format_signature) { + $atom_structure['image_mime'] = $mime; + break; + } + } + } + $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover'); + } else { + $this->warning('Unknown empty "covr" image at offset '.$baseoffset); } - $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover'); } break; @@ -728,11 +752,13 @@ $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag']; $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag']; - $ptv_lookup[0] = 'normal'; - $ptv_lookup[1] = 'double'; - $ptv_lookup[2] = 'half'; - $ptv_lookup[3] = 'full'; - $ptv_lookup[4] = 'current'; + $ptv_lookup = array( + 0 => 'normal', + 1 => 'double', + 2 => 'half', + 3 => 'full', + 4 => 'current' + ); if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; } else { @@ -908,13 +934,13 @@ $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); - $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); + $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (((int) $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); - $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']); + $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']); $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; @@ -1598,33 +1624,61 @@ break; case 'NCDT': - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + // https://exiftool.org/TagNames/Nikon.html // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); break; case 'NCTH': // Nikon Camera THumbnail image case 'NCVW': // Nikon Camera preVieW image - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + case 'NCM1': // Nikon Camera preview iMage 1 + case 'NCM2': // Nikon Camera preview iMage 2 + // https://exiftool.org/TagNames/Nikon.html if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) { + $descriptions = array( + 'NCTH' => 'Nikon Camera Thumbnail Image', + 'NCVW' => 'Nikon Camera Preview Image', + 'NCM1' => 'Nikon Camera Preview Image 1', + 'NCM2' => 'Nikon Camera Preview Image 2', + ); $atom_structure['data'] = $atom_data; $atom_structure['image_mime'] = 'image/jpeg'; - $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image')); - $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); + $atom_structure['description'] = isset($descriptions[$atomname]) ? $descriptions[$atomname] : 'Nikon preview image'; + $info['quicktime']['comments']['picture'][] = array( + 'image_mime' => $atom_structure['image_mime'], + 'data' => $atom_data, + 'description' => $atom_structure['description'] + ); } break; - case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG - $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); + case 'NCTG': // Nikon - https://exiftool.org/TagNames/Nikon.html#NCTG + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.nikon-nctg.php', __FILE__, true); + $nikonNCTG = new getid3_tag_nikon_nctg($this->getid3); + + $atom_structure['data'] = $nikonNCTG->parse($atom_data); break; - case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html - case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html - case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html + 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) { + $makerNoteVersion .= ' '.ord($atom_data[$i]); + } else { + $makerNoteVersion .= $atom_data[$i]; + } + } + $makerNoteVersion = rtrim($makerNoteVersion, "\x00"); + $atom_structure['data'] = array( + 'MakerNoteVersion' => $makerNoteVersion + ); + break; + case 'NCDB': // Nikon - https://exiftool.org/TagNames/Nikon.html + case 'CNCV': // Canon:CompressorVersion - https://exiftool.org/TagNames/Canon.html $atom_structure['data'] = $atom_data; break; case "\x00\x00\x00\x00": // some kind of metacontainer, may contain a big data dump such as: // 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 - // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt + // https://xhelmboyx.tripod.com/formats/qti-layout.txt $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); @@ -1721,6 +1775,7 @@ 'unknown_data' => array(), 'debug_list' => '', // Used to debug variables stored as comma delimited strings ); + $debug_structure = array(); $debug_structure['debug_items'] = array(); // Can start loop here to decode all sensor data in 32 Byte chunks: foreach (str_split($atom_SENSOR_data, 32) as $sensor_key => $sensor_data) { @@ -2039,7 +2094,7 @@ * @return array|false */ public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { - $atom_structure = false; + $atom_structure = array(); $subatomoffset = 0; $subatomcounter = 0; if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) { @@ -2057,17 +2112,22 @@ $subatomoffset += 4; continue; } - return $atom_structure; + break; } if (strlen($subatomdata) < ($subatomsize - 8)) { // we don't have enough data to decode the subatom. // this may be because we are refusing to parse large subatoms, or it may be because this atom had its size set too large // so we passed in the start of a following atom incorrectly? - return $atom_structure; + break; } $atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); $subatomoffset += $subatomsize; } + + if (empty($atom_structure)) { + return false; + } + return $atom_structure; } @@ -2552,8 +2612,9 @@ static $QuicktimeContentRatingLookup = array(); if (empty($QuicktimeContentRatingLookup)) { $QuicktimeContentRatingLookup[0] = 'None'; + $QuicktimeContentRatingLookup[1] = 'Explicit'; $QuicktimeContentRatingLookup[2] = 'Clean'; - $QuicktimeContentRatingLookup[4] = 'Explicit'; + $QuicktimeContentRatingLookup[4] = 'Explicit (old)'; } return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid'); } @@ -2607,189 +2668,6 @@ } /** - * @param string $atom_data - * - * @return array - */ - public function QuicktimeParseNikonNCTG($atom_data) { - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG - // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 - // Data is stored as records of: - // * 4 bytes record type - // * 2 bytes size of data field type: - // 0x0001 = flag (size field *= 1-byte) - // 0x0002 = char (size field *= 1-byte) - // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB - // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD - // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together - // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? - // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? - // * 2 bytes data size field - // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15") - // all integers are stored BigEndian - - $NCTGtagName = array( - 0x00000001 => 'Make', - 0x00000002 => 'Model', - 0x00000003 => 'Software', - 0x00000011 => 'CreateDate', - 0x00000012 => 'DateTimeOriginal', - 0x00000013 => 'FrameCount', - 0x00000016 => 'FrameRate', - 0x00000022 => 'FrameWidth', - 0x00000023 => 'FrameHeight', - 0x00000032 => 'AudioChannels', - 0x00000033 => 'AudioBitsPerSample', - 0x00000034 => 'AudioSampleRate', - 0x02000001 => 'MakerNoteVersion', - 0x02000005 => 'WhiteBalance', - 0x0200000b => 'WhiteBalanceFineTune', - 0x0200001e => 'ColorSpace', - 0x02000023 => 'PictureControlData', - 0x02000024 => 'WorldTime', - 0x02000032 => 'UnknownInfo', - 0x02000083 => 'LensType', - 0x02000084 => 'Lens', - ); - - $offset = 0; - $data = null; - $datalength = strlen($atom_data); - $parsed = array(); - while ($offset < $datalength) { - $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4; - $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; - $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; - switch ($data_size_type) { - case 0x0001: // 0x0001 = flag (size field *= 1-byte) - $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1)); - $offset += ($data_size * 1); - break; - case 0x0002: // 0x0002 = char (size field *= 1-byte) - $data = substr($atom_data, $offset, $data_size * 1); - $offset += ($data_size * 1); - $data = rtrim($data, "\x00"); - break; - case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB - $data = ''; - for ($i = $data_size - 1; $i >= 0; $i--) { - $data .= substr($atom_data, $offset + ($i * 2), 2); - } - $data = getid3_lib::BigEndian2Int($data); - $offset += ($data_size * 2); - break; - case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD - $data = ''; - for ($i = $data_size - 1; $i >= 0; $i--) { - $data .= substr($atom_data, $offset + ($i * 4), 4); - } - $data = getid3_lib::BigEndian2Int($data); - $offset += ($data_size * 4); - break; - case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together - $data = array(); - for ($i = 0; $i < $data_size; $i++) { - $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4)); - $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4)); - if ($denomninator == 0) { - $data[$i] = false; - } else { - $data[$i] = (double) $numerator / $denomninator; - } - } - $offset += (8 * $data_size); - if (count($data) == 1) { - $data = $data[0]; - } - break; - case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? - $data = substr($atom_data, $offset, $data_size * 1); - $offset += ($data_size * 1); - break; - case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? - $data = substr($atom_data, $offset, $data_size * 2); - $offset += ($data_size * 2); - break; - default: - echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'
'; - break 2; - } - - switch ($record_type) { - case 0x00000011: // CreateDate - case 0x00000012: // DateTimeOriginal - $data = strtotime($data); - break; - case 0x0200001e: // ColorSpace - switch ($data) { - case 1: - $data = 'sRGB'; - break; - case 2: - $data = 'Adobe RGB'; - break; - } - break; - case 0x02000023: // PictureControlData - $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full'); - $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a'); - $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'); - $data = array( - 'PictureControlVersion' => substr($data, 0, 4), - 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"), - 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"), - //'?' => substr($data, 44, 4), - 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))], - 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)), - 'Sharpness' => ord(substr($data, 50, 1)), - 'Contrast' => ord(substr($data, 51, 1)), - 'Brightness' => ord(substr($data, 52, 1)), - 'Saturation' => ord(substr($data, 53, 1)), - 'HueAdjustment' => ord(substr($data, 54, 1)), - 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))], - 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))], - 'ToningSaturation' => ord(substr($data, 57, 1)), - ); - break; - case 0x02000024: // WorldTime - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime - // timezone is stored as offset from GMT in minutes - $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2)); - if ($timezone & 0x8000) { - $timezone = 0 - (0x10000 - $timezone); - } - $timezone /= 60; - - $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1)); - switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) { - case 2: - $datedisplayformat = 'D/M/Y'; break; - case 1: - $datedisplayformat = 'M/D/Y'; break; - case 0: - default: - $datedisplayformat = 'Y/M/D'; break; - } - - $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat); - break; - case 0x02000083: // LensType - $data = array( - //'_' => $data, - 'mf' => (bool) ($data & 0x01), - 'd' => (bool) ($data & 0x02), - 'g' => (bool) ($data & 0x04), - 'vr' => (bool) ($data & 0x08), - ); - break; - } - $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT)); - $parsed[$tag_name] = $data; - } - return $parsed; - } - - /** * @param string $keyname * @param string|array $data * @param string $boxname