WordPress.org

Make WordPress Core


Ignore:
Timestamp:
09/14/2019 07:06:09 PM (2 years ago)
Author:
jorbin
Message:

Update getID3 library to fix issues with PHP7.4

Updates to trunk version that includes fixes for PHP7.4

Changelog:
https://github.com/JamesHeinrich/getID3/compare/v1.9.14...00f3fbfd77e583099ca70a3cf0bc092e113d2b20

See: #47751,#47783.
Fixes: #48040.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/ID3/module.audio-video.quicktime.php

    r41196 r46112  
    11<?php
     2
    23/////////////////////////////////////////////////////////////////
    34/// getID3() by James Heinrich <info@getid3.org>               //
    4 //  available at http://getid3.sourceforge.net                 //
    5 //            or http://www.getid3.org                         //
    6 //          also https://github.com/JamesHeinrich/getID3       //
    7 /////////////////////////////////////////////////////////////////
    8 // See readme.txt for more details                             //
     5//  available at https://github.com/JamesHeinrich/getID3       //
     6//            or https://www.getid3.org                        //
     7//            or http://getid3.sourceforge.net                 //
     8//  see readme.txt for more details                            //
    99/////////////////////////////////////////////////////////////////
    1010//                                                             //
     
    2525    public $ParseAllPossibleAtoms = false;
    2626
     27    /**
     28     * @return bool
     29     */
    2730    public function Analyze() {
    2831        $info = &$this->getid3->info;
     
    3639        $offset      = 0;
    3740        $atomcounter = 0;
    38         $atom_data_read_buffer_size = max($this->getid3->option_fread_buffer_size * 1024, ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : 1024)); // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
     41        $atom_data_read_buffer_size = $info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : $this->getid3->option_fread_buffer_size * 1024; // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
    3942        while ($offset < $info['avdataend']) {
    4043            if (!getid3_lib::intValueSupported($offset)) {
     
    163166            $info['audio']['bitrate'] = $info['bitrate'];
    164167        }
     168        if (!empty($info['bitrate']) && !empty($info['audio']['bitrate']) && empty($info['video']['bitrate']) && !empty($info['video']['frame_rate']) && !empty($info['video']['resolution_x']) && ($info['bitrate'] > $info['audio']['bitrate'])) {
     169            $info['video']['bitrate'] = $info['bitrate'] - $info['audio']['bitrate'];
     170        }
    165171        if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
    166172            foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
     
    194200            $info['video']['dataformat'] = 'quicktime';
    195201        }
     202        if (isset($info['video']) && ($info['mime_type'] == 'audio/mp4') && empty($info['video']['resolution_x']) && empty($info['video']['resolution_y']))  {
     203            unset($info['video']);
     204        }
    196205
    197206        return true;
    198207    }
    199208
     209    /**
     210     * @param string $atomname
     211     * @param int    $atomsize
     212     * @param string $atom_data
     213     * @param int    $baseoffset
     214     * @param array  $atomHierarchy
     215     * @param bool   $ParseAllPossibleAtoms
     216     *
     217     * @return array|false
     218     */
    200219    public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
    201220        // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
     
    204223        $info = &$this->getid3->info;
    205224
    206         $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717
     225        $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see https://www.getid3.org/phpBB3/viewtopic.php?t=1717
    207226        array_push($atomHierarchy, $atomname);
    208227        $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
     
    210229        $atom_structure['size']      = $atomsize;
    211230        $atom_structure['offset']    = $baseoffset;
    212         switch ($atomname) {
    213             case 'moov': // MOVie container atom
    214             case 'trak': // TRAcK container atom
    215             case 'clip': // CLIPping container atom
    216             case 'matt': // track MATTe container atom
    217             case 'edts': // EDiTS container atom
    218             case 'tref': // Track REFerence container atom
    219             case 'mdia': // MeDIA container atom
    220             case 'minf': // Media INFormation container atom
    221             case 'dinf': // Data INFormation container atom
    222             case 'udta': // User DaTA container atom
    223             case 'cmov': // Compressed MOVie container atom
    224             case 'rmra': // Reference Movie Record Atom
    225             case 'rmda': // Reference Movie Descriptor Atom
    226             case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
    227                 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    228                 break;
    229 
    230             case 'ilst': // Item LiST container atom
    231                 if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
    232                     // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
    233                     $allnumericnames = true;
    234                     foreach ($atom_structure['subatoms'] as $subatomarray) {
    235                         if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
    236                             $allnumericnames = false;
    237                             break;
    238                         }
    239                     }
    240                     if ($allnumericnames) {
    241                         $newData = array();
     231        if (substr($atomname, 0, 3) == "\x00\x00\x00") {
     232            // https://github.com/JamesHeinrich/getID3/issues/139
     233            $atomname = getid3_lib::BigEndian2Int($atomname);
     234            $atom_structure['name'] = $atomname;
     235            $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     236        } else {
     237            switch ($atomname) {
     238                case 'moov': // MOVie container atom
     239                case 'trak': // TRAcK container atom
     240                case 'clip': // CLIPping container atom
     241                case 'matt': // track MATTe container atom
     242                case 'edts': // EDiTS container atom
     243                case 'tref': // Track REFerence container atom
     244                case 'mdia': // MeDIA container atom
     245                case 'minf': // Media INFormation container atom
     246                case 'dinf': // Data INFormation container atom
     247                case 'udta': // User DaTA container atom
     248                case 'cmov': // Compressed MOVie container atom
     249                case 'rmra': // Reference Movie Record Atom
     250                case 'rmda': // Reference Movie Descriptor Atom
     251                case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
     252                    $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     253                    break;
     254
     255                case 'ilst': // Item LiST container atom
     256                    if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
     257                        // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
     258                        $allnumericnames = true;
    242259                        foreach ($atom_structure['subatoms'] as $subatomarray) {
    243                             foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
    244                                 unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
    245                                 $newData[$subatomarray['name']] = $newData_subatomarray;
     260                            if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
     261                                $allnumericnames = false;
    246262                                break;
    247263                            }
    248264                        }
    249                         $atom_structure['data'] = $newData;
    250                         unset($atom_structure['subatoms']);
    251                     }
    252                 }
    253                 break;
    254 
    255             case "\x00\x00\x00\x01":
    256             case "\x00\x00\x00\x02":
    257             case "\x00\x00\x00\x03":
    258             case "\x00\x00\x00\x04":
    259             case "\x00\x00\x00\x05":
    260                 $atomname = getid3_lib::BigEndian2Int($atomname);
    261                 $atom_structure['name'] = $atomname;
    262                 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    263                 break;
    264 
    265             case 'stbl': // Sample TaBLe container atom
    266                 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    267                 $isVideo = false;
    268                 $framerate  = 0;
    269                 $framecount = 0;
    270                 foreach ($atom_structure['subatoms'] as $key => $value_array) {
    271                     if (isset($value_array['sample_description_table'])) {
    272                         foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
    273                             if (isset($value_array2['data_format'])) {
    274                                 switch ($value_array2['data_format']) {
    275                                     case 'avc1':
    276                                     case 'mp4v':
    277                                         // video data
    278                                         $isVideo = true;
    279                                         break;
    280                                     case 'mp4a':
    281                                         // audio data
    282                                         break;
     265                        if ($allnumericnames) {
     266                            $newData = array();
     267                            foreach ($atom_structure['subatoms'] as $subatomarray) {
     268                                foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
     269                                    unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
     270                                    $newData[$subatomarray['name']] = $newData_subatomarray;
     271                                    break;
     272                                }
     273                            }
     274                            $atom_structure['data'] = $newData;
     275                            unset($atom_structure['subatoms']);
     276                        }
     277                    }
     278                    break;
     279
     280                case 'stbl': // Sample TaBLe container atom
     281                    $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     282                    $isVideo = false;
     283                    $framerate  = 0;
     284                    $framecount = 0;
     285                    foreach ($atom_structure['subatoms'] as $key => $value_array) {
     286                        if (isset($value_array['sample_description_table'])) {
     287                            foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
     288                                if (isset($value_array2['data_format'])) {
     289                                    switch ($value_array2['data_format']) {
     290                                        case 'avc1':
     291                                        case 'mp4v':
     292                                            // video data
     293                                            $isVideo = true;
     294                                            break;
     295                                        case 'mp4a':
     296                                            // audio data
     297                                            break;
     298                                    }
     299                                }
     300                            }
     301                        } elseif (isset($value_array['time_to_sample_table'])) {
     302                            foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
     303                                if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
     304                                    $framerate  = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
     305                                    $framecount = $value_array2['sample_count'];
    283306                                }
    284307                            }
    285308                        }
    286                     } elseif (isset($value_array['time_to_sample_table'])) {
    287                         foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
    288                             if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
    289                                 $framerate  = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
    290                                 $framecount = $value_array2['sample_count'];
    291                             }
    292                         }
    293                     }
    294                 }
    295                 if ($isVideo && $framerate) {
    296                     $info['quicktime']['video']['frame_rate'] = $framerate;
    297                     $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
    298                 }
    299                 if ($isVideo && $framecount) {
    300                     $info['quicktime']['video']['frame_count'] = $framecount;
    301                 }
    302                 break;
    303 
    304 
    305             case "\xA9".'alb': // ALBum
    306             case "\xA9".'ART': //
    307             case "\xA9".'art': // ARTist
    308             case "\xA9".'aut': //
    309             case "\xA9".'cmt': // CoMmenT
    310             case "\xA9".'com': // COMposer
    311             case "\xA9".'cpy': //
    312             case "\xA9".'day': // content created year
    313             case "\xA9".'dir': //
    314             case "\xA9".'ed1': //
    315             case "\xA9".'ed2': //
    316             case "\xA9".'ed3': //
    317             case "\xA9".'ed4': //
    318             case "\xA9".'ed5': //
    319             case "\xA9".'ed6': //
    320             case "\xA9".'ed7': //
    321             case "\xA9".'ed8': //
    322             case "\xA9".'ed9': //
    323             case "\xA9".'enc': //
    324             case "\xA9".'fmt': //
    325             case "\xA9".'gen': // GENre
    326             case "\xA9".'grp': // GRouPing
    327             case "\xA9".'hst': //
    328             case "\xA9".'inf': //
    329             case "\xA9".'lyr': // LYRics
    330             case "\xA9".'mak': //
    331             case "\xA9".'mod': //
    332             case "\xA9".'nam': // full NAMe
    333             case "\xA9".'ope': //
    334             case "\xA9".'PRD': //
    335             case "\xA9".'prf': //
    336             case "\xA9".'req': //
    337             case "\xA9".'src': //
    338             case "\xA9".'swr': //
    339             case "\xA9".'too': // encoder
    340             case "\xA9".'trk': // TRacK
    341             case "\xA9".'url': //
    342             case "\xA9".'wrn': //
    343             case "\xA9".'wrt': // WRiTer
    344             case '----': // itunes specific
    345             case 'aART': // Album ARTist
    346             case 'akID': // iTunes store account type
    347             case 'apID': // Purchase Account
    348             case 'atID': //
    349             case 'catg': // CaTeGory
    350             case 'cmID': //
    351             case 'cnID': //
    352             case 'covr': // COVeR artwork
    353             case 'cpil': // ComPILation
    354             case 'cprt': // CoPyRighT
    355             case 'desc': // DESCription
    356             case 'disk': // DISK number
    357             case 'egid': // Episode Global ID
    358             case 'geID': //
    359             case 'gnre': // GeNRE
    360             case 'hdvd': // HD ViDeo
    361             case 'keyw': // KEYWord
    362             case 'ldes': // Long DEScription
    363             case 'pcst': // PodCaST
    364             case 'pgap': // GAPless Playback
    365             case 'plID': //
    366             case 'purd': // PURchase Date
    367             case 'purl': // Podcast URL
    368             case 'rati': //
    369             case 'rndu': //
    370             case 'rpdu': //
    371             case 'rtng': // RaTiNG
    372             case 'sfID': // iTunes store country
    373             case 'soaa': // SOrt Album Artist
    374             case 'soal': // SOrt ALbum
    375             case 'soar': // SOrt ARtist
    376             case 'soco': // SOrt COmposer
    377             case 'sonm': // SOrt NaMe
    378             case 'sosn': // SOrt Show Name
    379             case 'stik': //
    380             case 'tmpo': // TeMPO (BPM)
    381             case 'trkn': // TRacK Number
    382             case 'tven': // tvEpisodeID
    383             case 'tves': // TV EpiSode
    384             case 'tvnn': // TV Network Name
    385             case 'tvsh': // TV SHow Name
    386             case 'tvsn': // TV SeasoN
    387                 if ($atom_parent == 'udta') {
    388                     // User data atom handler
    389                     $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
    390                     $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
    391                     $atom_structure['data']        =                           substr($atom_data, 4);
    392 
    393                     $atom_structure['language']    = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
    394                     if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
    395                         $info['comments']['language'][] = $atom_structure['language'];
    396                     }
    397                 } else {
    398                     // Apple item list box atom handler
    399                     $atomoffset = 0;
    400                     if (substr($atom_data, 2, 2) == "\x10\xB5") {
    401                         // not sure what it means, but observed on iPhone4 data.
    402                         // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
    403                         while ($atomoffset < strlen($atom_data)) {
    404                             $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset,     2));
    405                             $boxsmalltype =                           substr($atom_data, $atomoffset + 2, 2);
    406                             $boxsmalldata =                           substr($atom_data, $atomoffset + 4, $boxsmallsize);
    407                             if ($boxsmallsize <= 1) {
    408                                 $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
    409                                 $atom_structure['data'] = null;
    410                                 $atomoffset = strlen($atom_data);
    411                                 break;
    412                             }
    413                             switch ($boxsmalltype) {
    414                                 case "\x10\xB5":
    415                                     $atom_structure['data'] = $boxsmalldata;
    416                                     break;
    417                                 default:
    418                                     $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset);
    419                                     $atom_structure['data'] = $atom_data;
    420                                     break;
    421                             }
    422                             $atomoffset += (4 + $boxsmallsize);
     309                    }
     310                    if ($isVideo && $framerate) {
     311                        $info['quicktime']['video']['frame_rate'] = $framerate;
     312                        $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
     313                    }
     314                    if ($isVideo && $framecount) {
     315                        $info['quicktime']['video']['frame_count'] = $framecount;
     316                    }
     317                    break;
     318
     319
     320                case "\xA9".'alb': // ALBum
     321                case "\xA9".'ART': //
     322                case "\xA9".'art': // ARTist
     323                case "\xA9".'aut': //
     324                case "\xA9".'cmt': // CoMmenT
     325                case "\xA9".'com': // COMposer
     326                case "\xA9".'cpy': //
     327                case "\xA9".'day': // content created year
     328                case "\xA9".'dir': //
     329                case "\xA9".'ed1': //
     330                case "\xA9".'ed2': //
     331                case "\xA9".'ed3': //
     332                case "\xA9".'ed4': //
     333                case "\xA9".'ed5': //
     334                case "\xA9".'ed6': //
     335                case "\xA9".'ed7': //
     336                case "\xA9".'ed8': //
     337                case "\xA9".'ed9': //
     338                case "\xA9".'enc': //
     339                case "\xA9".'fmt': //
     340                case "\xA9".'gen': // GENre
     341                case "\xA9".'grp': // GRouPing
     342                case "\xA9".'hst': //
     343                case "\xA9".'inf': //
     344                case "\xA9".'lyr': // LYRics
     345                case "\xA9".'mak': //
     346                case "\xA9".'mod': //
     347                case "\xA9".'nam': // full NAMe
     348                case "\xA9".'ope': //
     349                case "\xA9".'PRD': //
     350                case "\xA9".'prf': //
     351                case "\xA9".'req': //
     352                case "\xA9".'src': //
     353                case "\xA9".'swr': //
     354                case "\xA9".'too': // encoder
     355                case "\xA9".'trk': // TRacK
     356                case "\xA9".'url': //
     357                case "\xA9".'wrn': //
     358                case "\xA9".'wrt': // WRiTer
     359                case '----': // itunes specific
     360                case 'aART': // Album ARTist
     361                case 'akID': // iTunes store account type
     362                case 'apID': // Purchase Account
     363                case 'atID': //
     364                case 'catg': // CaTeGory
     365                case 'cmID': //
     366                case 'cnID': //
     367                case 'covr': // COVeR artwork
     368                case 'cpil': // ComPILation
     369                case 'cprt': // CoPyRighT
     370                case 'desc': // DESCription
     371                case 'disk': // DISK number
     372                case 'egid': // Episode Global ID
     373                case 'geID': //
     374                case 'gnre': // GeNRE
     375                case 'hdvd': // HD ViDeo
     376                case 'keyw': // KEYWord
     377                case 'ldes': // Long DEScription
     378                case 'pcst': // PodCaST
     379                case 'pgap': // GAPless Playback
     380                case 'plID': //
     381                case 'purd': // PURchase Date
     382                case 'purl': // Podcast URL
     383                case 'rati': //
     384                case 'rndu': //
     385                case 'rpdu': //
     386                case 'rtng': // RaTiNG
     387                case 'sfID': // iTunes store country
     388                case 'soaa': // SOrt Album Artist
     389                case 'soal': // SOrt ALbum
     390                case 'soar': // SOrt ARtist
     391                case 'soco': // SOrt COmposer
     392                case 'sonm': // SOrt NaMe
     393                case 'sosn': // SOrt Show Name
     394                case 'stik': //
     395                case 'tmpo': // TeMPO (BPM)
     396                case 'trkn': // TRacK Number
     397                case 'tven': // tvEpisodeID
     398                case 'tves': // TV EpiSode
     399                case 'tvnn': // TV Network Name
     400                case 'tvsh': // TV SHow Name
     401                case 'tvsn': // TV SeasoN
     402                    if ($atom_parent == 'udta') {
     403                        // User data atom handler
     404                        $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
     405                        $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
     406                        $atom_structure['data']        =                           substr($atom_data, 4);
     407
     408                        $atom_structure['language']    = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
     409                        if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
     410                            $info['comments']['language'][] = $atom_structure['language'];
    423411                        }
    424412                    } else {
    425                         while ($atomoffset < strlen($atom_data)) {
    426                             $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
    427                             $boxtype =                           substr($atom_data, $atomoffset + 4, 4);
    428                             $boxdata =                           substr($atom_data, $atomoffset + 8, $boxsize - 8);
    429                             if ($boxsize <= 1) {
    430                                 $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
    431                                 $atom_structure['data'] = null;
    432                                 $atomoffset = strlen($atom_data);
    433                                 break;
     413                        // Apple item list box atom handler
     414                        $atomoffset = 0;
     415                        if (substr($atom_data, 2, 2) == "\x10\xB5") {
     416                            // not sure what it means, but observed on iPhone4 data.
     417                            // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
     418                            while ($atomoffset < strlen($atom_data)) {
     419                                $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset,     2));
     420                                $boxsmalltype =                           substr($atom_data, $atomoffset + 2, 2);
     421                                $boxsmalldata =                           substr($atom_data, $atomoffset + 4, $boxsmallsize);
     422                                if ($boxsmallsize <= 1) {
     423                                    $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
     424                                    $atom_structure['data'] = null;
     425                                    $atomoffset = strlen($atom_data);
     426                                    break;
     427                                }
     428                                switch ($boxsmalltype) {
     429                                    case "\x10\xB5":
     430                                        $atom_structure['data'] = $boxsmalldata;
     431                                        break;
     432                                    default:
     433                                        $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset);
     434                                        $atom_structure['data'] = $atom_data;
     435                                        break;
     436                                }
     437                                $atomoffset += (4 + $boxsmallsize);
    434438                            }
    435                             $atomoffset += $boxsize;
    436 
    437                             switch ($boxtype) {
    438                                 case 'mean':
    439                                 case 'name':
    440                                     $atom_structure[$boxtype] = substr($boxdata, 4);
     439                        } else {
     440                            while ($atomoffset < strlen($atom_data)) {
     441                                $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
     442                                $boxtype =                           substr($atom_data, $atomoffset + 4, 4);
     443                                $boxdata =                           substr($atom_data, $atomoffset + 8, $boxsize - 8);
     444                                if ($boxsize <= 1) {
     445                                    $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
     446                                    $atom_structure['data'] = null;
     447                                    $atomoffset = strlen($atom_data);
    441448                                    break;
    442 
    443                                 case 'data':
    444                                     $atom_structure['version']   = getid3_lib::BigEndian2Int(substr($boxdata,  0, 1));
    445                                     $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata,  1, 3));
    446                                     switch ($atom_structure['flags_raw']) {
    447                                         case  0: // data flag
    448                                         case 21: // tmpo/cpil flag
    449                                             switch ($atomname) {
    450                                                 case 'cpil':
    451                                                 case 'hdvd':
    452                                                 case 'pcst':
    453                                                 case 'pgap':
    454                                                     // 8-bit integer (boolean)
    455                                                     $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
    456                                                     break;
    457 
    458                                                 case 'tmpo':
    459                                                     // 16-bit integer
    460                                                     $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
    461                                                     break;
    462 
    463                                                 case 'disk':
    464                                                 case 'trkn':
    465                                                     // binary
    466                                                     $num       = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
    467                                                     $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
    468                                                     $atom_structure['data']  = empty($num) ? '' : $num;
    469                                                     $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
    470                                                     break;
    471 
    472                                                 case 'gnre':
    473                                                     // enum
    474                                                     $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
    475                                                     $atom_structure['data']    = getid3_id3v1::LookupGenreName($GenreID - 1);
    476                                                     break;
    477 
    478                                                 case 'rtng':
    479                                                     // 8-bit integer
    480                                                     $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
    481                                                     $atom_structure['data']    = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
    482                                                     break;
    483 
    484                                                 case 'stik':
    485                                                     // 8-bit integer (enum)
    486                                                     $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
    487                                                     $atom_structure['data']    = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
    488                                                     break;
    489 
    490                                                 case 'sfID':
    491                                                     // 32-bit integer
    492                                                     $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
    493                                                     $atom_structure['data']    = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
    494                                                     break;
    495 
    496                                                 case 'egid':
    497                                                 case 'purl':
    498                                                     $atom_structure['data'] = substr($boxdata, 8);
    499                                                     break;
    500 
    501                                                 case 'plID':
    502                                                     // 64-bit integer
    503                                                     $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8));
    504                                                     break;
    505 
    506                                                 case 'covr':
    507                                                     $atom_structure['data'] = substr($boxdata, 8);
     449                                }
     450                                $atomoffset += $boxsize;
     451
     452                                switch ($boxtype) {
     453                                    case 'mean':
     454                                    case 'name':
     455                                        $atom_structure[$boxtype] = substr($boxdata, 4);
     456                                        break;
     457
     458                                    case 'data':
     459                                        $atom_structure['version']   = getid3_lib::BigEndian2Int(substr($boxdata,  0, 1));
     460                                        $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata,  1, 3));
     461                                        switch ($atom_structure['flags_raw']) {
     462                                            case  0: // data flag
     463                                            case 21: // tmpo/cpil flag
     464                                                switch ($atomname) {
     465                                                    case 'cpil':
     466                                                    case 'hdvd':
     467                                                    case 'pcst':
     468                                                    case 'pgap':
     469                                                        // 8-bit integer (boolean)
     470                                                        $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
     471                                                        break;
     472
     473                                                    case 'tmpo':
     474                                                        // 16-bit integer
     475                                                        $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
     476                                                        break;
     477
     478                                                    case 'disk':
     479                                                    case 'trkn':
     480                                                        // binary
     481                                                        $num       = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
     482                                                        $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
     483                                                        $atom_structure['data']  = empty($num) ? '' : $num;
     484                                                        $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
     485                                                        break;
     486
     487                                                    case 'gnre':
     488                                                        // enum
     489                                                        $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
     490                                                        $atom_structure['data']    = getid3_id3v1::LookupGenreName($GenreID - 1);
     491                                                        break;
     492
     493                                                    case 'rtng':
     494                                                        // 8-bit integer
     495                                                        $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
     496                                                        $atom_structure['data']    = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
     497                                                        break;
     498
     499                                                    case 'stik':
     500                                                        // 8-bit integer (enum)
     501                                                        $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
     502                                                        $atom_structure['data']    = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
     503                                                        break;
     504
     505                                                    case 'sfID':
     506                                                        // 32-bit integer
     507                                                        $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
     508                                                        $atom_structure['data']    = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
     509                                                        break;
     510
     511                                                    case 'egid':
     512                                                    case 'purl':
     513                                                        $atom_structure['data'] = substr($boxdata, 8);
     514                                                        break;
     515
     516                                                    case 'plID':
     517                                                        // 64-bit integer
     518                                                        $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8));
     519                                                        break;
     520
     521                                                    case 'covr':
     522                                                        $atom_structure['data'] = substr($boxdata, 8);
     523                                                        // not a foolproof check, but better than nothing
     524                                                        if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
     525                                                            $atom_structure['image_mime'] = 'image/jpeg';
     526                                                        } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
     527                                                            $atom_structure['image_mime'] = 'image/png';
     528                                                        } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
     529                                                            $atom_structure['image_mime'] = 'image/gif';
     530                                                        }
     531                                                        break;
     532
     533                                                    case 'atID':
     534                                                    case 'cnID':
     535                                                    case 'geID':
     536                                                    case 'tves':
     537                                                    case 'tvsn':
     538                                                    default:
     539                                                        // 32-bit integer
     540                                                        $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
     541                                                }
     542                                                break;
     543
     544                                            case  1: // text flag
     545                                            case 13: // image flag
     546                                            default:
     547                                                $atom_structure['data'] = substr($boxdata, 8);
     548                                                if ($atomname == 'covr') {
    508549                                                    // not a foolproof check, but better than nothing
    509550                                                    if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
     
    514555                                                        $atom_structure['image_mime'] = 'image/gif';
    515556                                                    }
    516                                                     break;
    517 
    518                                                 case 'atID':
    519                                                 case 'cnID':
    520                                                 case 'geID':
    521                                                 case 'tves':
    522                                                 case 'tvsn':
    523                                                 default:
    524                                                     // 32-bit integer
    525                                                     $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
    526                                             }
    527                                             break;
    528 
    529                                         case  1: // text flag
    530                                         case 13: // image flag
    531                                         default:
    532                                             $atom_structure['data'] = substr($boxdata, 8);
    533                                             if ($atomname == 'covr') {
    534                                                 // not a foolproof check, but better than nothing
    535                                                 if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
    536                                                     $atom_structure['image_mime'] = 'image/jpeg';
    537                                                 } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
    538                                                     $atom_structure['image_mime'] = 'image/png';
    539                                                 } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
    540                                                     $atom_structure['image_mime'] = 'image/gif';
    541557                                                }
    542                                             }
    543                                             break;
    544 
    545                                     }
    546                                     break;
    547 
    548                                 default:
    549                                     $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset);
    550                                     $atom_structure['data'] = $atom_data;
    551 
     558                                                break;
     559
     560                                        }
     561                                        break;
     562
     563                                    default:
     564                                        $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset);
     565                                        $atom_structure['data'] = $atom_data;
     566
     567                                }
    552568                            }
    553569                        }
    554570                    }
    555                 }
    556                 $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
    557                 break;
    558 
    559 
    560             case 'play': // auto-PLAY atom
    561                 $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    562 
    563                 $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
    564                 break;
    565 
    566 
    567             case 'WLOC': // Window LOCation atom
    568                 $atom_structure['location_x']  = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2));
    569                 $atom_structure['location_y']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 2));
    570                 break;
    571 
    572 
    573             case 'LOOP': // LOOPing atom
    574             case 'SelO': // play SELection Only atom
    575             case 'AllF': // play ALL Frames atom
    576                 $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
    577                 break;
    578 
    579 
    580             case 'name': //
    581             case 'MCPS': // Media Cleaner PRo
    582             case '@PRM': // adobe PReMiere version
    583             case '@PRQ': // adobe PRemiere Quicktime version
    584                 $atom_structure['data'] = $atom_data;
    585                 break;
    586 
    587 
    588             case 'cmvd': // Compressed MooV Data atom
    589                 // Code by ubergeekØubergeek*tv based on information from
    590                 // http://developer.apple.com/quicktime/icefloe/dispatch012.html
    591                 $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
    592 
    593                 $CompressedFileData = substr($atom_data, 4);
    594                 if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
    595                     $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
    596                 } else {
    597                     $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']);
    598                 }
    599                 break;
    600 
    601 
    602             case 'dcom': // Data COMpression atom
    603                 $atom_structure['compression_id']   = $atom_data;
    604                 $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
    605                 break;
    606 
    607 
    608             case 'rdrf': // Reference movie Data ReFerence atom
    609                 $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    610                 $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
    611                 $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
    612 
    613                 $atom_structure['reference_type_name']    =                           substr($atom_data,  4, 4);
    614                 $atom_structure['reference_length']       = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    615                 switch ($atom_structure['reference_type_name']) {
    616                     case 'url ':
    617                         $atom_structure['url']            =       $this->NoNullString(substr($atom_data, 12));
    618                         break;
    619 
    620                     case 'alis':
    621                         $atom_structure['file_alias']     =                           substr($atom_data, 12);
    622                         break;
    623 
    624                     case 'rsrc':
    625                         $atom_structure['resource_alias'] =                           substr($atom_data, 12);
    626                         break;
    627 
    628                     default:
    629                         $atom_structure['data']           =                           substr($atom_data, 12);
    630                         break;
    631                 }
    632                 break;
    633 
    634 
    635             case 'rmqu': // Reference Movie QUality atom
    636                 $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
    637                 break;
    638 
    639 
    640             case 'rmcs': // Reference Movie Cpu Speed atom
    641                 $atom_structure['version']          = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    642                 $atom_structure['flags_raw']        = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    643                 $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
    644                 break;
    645 
    646 
    647             case 'rmvc': // Reference Movie Version Check atom
    648                 $atom_structure['version']            = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    649                 $atom_structure['flags_raw']          = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    650                 $atom_structure['gestalt_selector']   =                           substr($atom_data,  4, 4);
    651                 $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    652                 $atom_structure['gestalt_value']      = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
    653                 $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
    654                 break;
    655 
    656 
    657             case 'rmcd': // Reference Movie Component check atom
    658                 $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    659                 $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    660                 $atom_structure['component_type']         =                           substr($atom_data,  4, 4);
    661                 $atom_structure['component_subtype']      =                           substr($atom_data,  8, 4);
    662                 $atom_structure['component_manufacturer'] =                           substr($atom_data, 12, 4);
    663                 $atom_structure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
    664                 $atom_structure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
    665                 $atom_structure['component_min_version']  = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
    666                 break;
    667 
    668 
    669             case 'rmdr': // Reference Movie Data Rate atom
    670                 $atom_structure['version']       = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    671                 $atom_structure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    672                 $atom_structure['data_rate']     = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    673 
    674                 $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
    675                 break;
    676 
    677 
    678             case 'rmla': // Reference Movie Language Atom
    679                 $atom_structure['version']     = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    680                 $atom_structure['flags_raw']   = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    681                 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
    682 
    683                 $atom_structure['language']    = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
    684                 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
    685                     $info['comments']['language'][] = $atom_structure['language'];
    686                 }
    687                 break;
    688 
    689 
    690             case 'rmla': // Reference Movie Language Atom
    691                 $atom_structure['version']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    692                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    693                 $atom_structure['track_id']  = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
    694                 break;
    695 
    696 
    697             case 'ptv ': // Print To Video - defines a movie's full screen mode
    698                 // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
    699                 $atom_structure['display_size_raw']  = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
    700                 $atom_structure['reserved_1']        = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
    701                 $atom_structure['reserved_2']        = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
    702                 $atom_structure['slide_show_flag']   = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
    703                 $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
    704 
    705                 $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
    706                 $atom_structure['flags']['slide_show']   = (bool) $atom_structure['slide_show_flag'];
    707 
    708                 $ptv_lookup[0] = 'normal';
    709                 $ptv_lookup[1] = 'double';
    710                 $ptv_lookup[2] = 'half';
    711                 $ptv_lookup[3] = 'full';
    712                 $ptv_lookup[4] = 'current';
    713                 if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
    714                     $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
    715                 } else {
    716                     $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
    717                 }
    718                 break;
    719 
    720 
    721             case 'stsd': // Sample Table Sample Description atom
    722                 $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    723                 $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    724                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    725 
    726                 // see: https://github.com/JamesHeinrich/getID3/issues/111
    727                 // Some corrupt files have been known to have high bits set in the number_entries field
    728                 // This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000
    729                 // Workaround: mask off the upper byte and throw a warning if it's nonzero
    730                 if ($atom_structure['number_entries'] > 0x000FFFFF) {
    731                     if ($atom_structure['number_entries'] > 0x00FFFFFF) {
    732                         $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Ignoring upper byte and interpreting this as 0x'.getid3_lib::PrintHexBytes(substr($atom_data, 5, 3), true, false).' = '.($atom_structure['number_entries'] & 0x00FFFFFF));
    733                         $atom_structure['number_entries'] = ($atom_structure['number_entries'] & 0x00FFFFFF);
     571                    $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
     572                    break;
     573
     574
     575                case 'play': // auto-PLAY atom
     576                    $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     577
     578                    $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
     579                    break;
     580
     581
     582                case 'WLOC': // Window LOCation atom
     583                    $atom_structure['location_x']  = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2));
     584                    $atom_structure['location_y']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 2));
     585                    break;
     586
     587
     588                case 'LOOP': // LOOPing atom
     589                case 'SelO': // play SELection Only atom
     590                case 'AllF': // play ALL Frames atom
     591                    $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
     592                    break;
     593
     594
     595                case 'name': //
     596                case 'MCPS': // Media Cleaner PRo
     597                case '@PRM': // adobe PReMiere version
     598                case '@PRQ': // adobe PRemiere Quicktime version
     599                    $atom_structure['data'] = $atom_data;
     600                    break;
     601
     602
     603                case 'cmvd': // Compressed MooV Data atom
     604                    // Code by ubergeekØubergeek*tv based on information from
     605                    // http://developer.apple.com/quicktime/icefloe/dispatch012.html
     606                    $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
     607
     608                    $CompressedFileData = substr($atom_data, 4);
     609                    if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
     610                        $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
    734611                    } else {
    735                         $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Please report this to info@getid3.org referencing bug report #111');
    736                     }
    737                 }
    738 
    739                 $stsdEntriesDataOffset = 8;
    740                 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    741                     $atom_structure['sample_description_table'][$i]['size']             = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
    742                     $stsdEntriesDataOffset += 4;
    743                     $atom_structure['sample_description_table'][$i]['data_format']      =                           substr($atom_data, $stsdEntriesDataOffset, 4);
    744                     $stsdEntriesDataOffset += 4;
    745                     $atom_structure['sample_description_table'][$i]['reserved']         = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
    746                     $stsdEntriesDataOffset += 6;
    747                     $atom_structure['sample_description_table'][$i]['reference_index']  = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
    748                     $stsdEntriesDataOffset += 2;
    749                     $atom_structure['sample_description_table'][$i]['data']             =                           substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
    750                     $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
    751 
    752                     $atom_structure['sample_description_table'][$i]['encoder_version']  = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  0, 2));
    753                     $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  2, 2));
    754                     $atom_structure['sample_description_table'][$i]['encoder_vendor']   =                           substr($atom_structure['sample_description_table'][$i]['data'],  4, 4);
    755 
    756                     switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
    757 
    758                         case "\x00\x00\x00\x00":
    759                             // audio tracks
    760                             $atom_structure['sample_description_table'][$i]['audio_channels']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  2));
    761                             $atom_structure['sample_description_table'][$i]['audio_bit_depth']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10,  2));
    762                             $atom_structure['sample_description_table'][$i]['audio_compression_id'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  2));
    763                             $atom_structure['sample_description_table'][$i]['audio_packet_size']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14,  2));
    764                             $atom_structure['sample_description_table'][$i]['audio_sample_rate']    = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16,  4));
    765 
    766                             // video tracks
    767                             // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
    768                             $atom_structure['sample_description_table'][$i]['temporal_quality'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  4));
    769                             $atom_structure['sample_description_table'][$i]['spatial_quality']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  4));
    770                             $atom_structure['sample_description_table'][$i]['width']            =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16,  2));
    771                             $atom_structure['sample_description_table'][$i]['height']           =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18,  2));
    772                             $atom_structure['sample_description_table'][$i]['resolution_x']     = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24,  4));
    773                             $atom_structure['sample_description_table'][$i]['resolution_y']     = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28,  4));
    774                             $atom_structure['sample_description_table'][$i]['data_size']        =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32,  4));
    775                             $atom_structure['sample_description_table'][$i]['frame_count']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36,  2));
    776                             $atom_structure['sample_description_table'][$i]['compressor_name']  =                             substr($atom_structure['sample_description_table'][$i]['data'], 38,  4);
    777                             $atom_structure['sample_description_table'][$i]['pixel_depth']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42,  2));
    778                             $atom_structure['sample_description_table'][$i]['color_table_id']   =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44,  2));
    779 
    780                             switch ($atom_structure['sample_description_table'][$i]['data_format']) {
    781                                 case '2vuY':
    782                                 case 'avc1':
    783                                 case 'cvid':
    784                                 case 'dvc ':
    785                                 case 'dvcp':
    786                                 case 'gif ':
    787                                 case 'h263':
    788                                 case 'jpeg':
    789                                 case 'kpcd':
    790                                 case 'mjpa':
    791                                 case 'mjpb':
    792                                 case 'mp4v':
    793                                 case 'png ':
    794                                 case 'raw ':
    795                                 case 'rle ':
    796                                 case 'rpza':
    797                                 case 'smc ':
    798                                 case 'SVQ1':
    799                                 case 'SVQ3':
    800                                 case 'tiff':
    801                                 case 'v210':
    802                                 case 'v216':
    803                                 case 'v308':
    804                                 case 'v408':
    805                                 case 'v410':
    806                                 case 'yuv2':
    807                                     $info['fileformat'] = 'mp4';
    808                                     $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
    809 // http://www.getid3.org/phpBB3/viewtopic.php?t=1550
    810 //if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers
    811 if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
    812     // assume that values stored here are more important than values stored in [tkhd] atom
    813     $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
    814     $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
    815     $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
    816     $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
    817 }
    818                                     break;
    819 
    820                                 case 'qtvr':
    821                                     $info['video']['dataformat'] = 'quicktimevr';
    822                                     break;
    823 
    824                                 case 'mp4a':
    825                                 default:
    826                                     $info['quicktime']['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
    827                                     $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
    828                                     $info['quicktime']['audio']['channels']    = $atom_structure['sample_description_table'][$i]['audio_channels'];
    829                                     $info['quicktime']['audio']['bit_depth']   = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
    830                                     $info['audio']['codec']                    = $info['quicktime']['audio']['codec'];
    831                                     $info['audio']['sample_rate']              = $info['quicktime']['audio']['sample_rate'];
    832                                     $info['audio']['channels']                 = $info['quicktime']['audio']['channels'];
    833                                     $info['audio']['bits_per_sample']          = $info['quicktime']['audio']['bit_depth'];
    834                                     switch ($atom_structure['sample_description_table'][$i]['data_format']) {
    835                                         case 'raw ': // PCM
    836                                         case 'alac': // Apple Lossless Audio Codec
    837                                             $info['audio']['lossless'] = true;
    838                                             break;
    839                                         default:
    840                                             $info['audio']['lossless'] = false;
    841                                             break;
    842                                     }
    843                                     break;
    844                             }
     612                        $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']);
     613                    }
     614                    break;
     615
     616
     617                case 'dcom': // Data COMpression atom
     618                    $atom_structure['compression_id']   = $atom_data;
     619                    $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
     620                    break;
     621
     622
     623                case 'rdrf': // Reference movie Data ReFerence atom
     624                    $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     625                    $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
     626                    $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
     627
     628                    $atom_structure['reference_type_name']    =                           substr($atom_data,  4, 4);
     629                    $atom_structure['reference_length']       = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     630                    switch ($atom_structure['reference_type_name']) {
     631                        case 'url ':
     632                            $atom_structure['url']            =       $this->NoNullString(substr($atom_data, 12));
    845633                            break;
    846634
     635                        case 'alis':
     636                            $atom_structure['file_alias']     =                           substr($atom_data, 12);
     637                            break;
     638
     639                        case 'rsrc':
     640                            $atom_structure['resource_alias'] =                           substr($atom_data, 12);
     641                            break;
     642
    847643                        default:
    848                             switch ($atom_structure['sample_description_table'][$i]['data_format']) {
    849                                 case 'mp4s':
    850                                     $info['fileformat'] = 'mp4';
    851                                     break;
    852 
    853                                 default:
    854                                     // video atom
    855                                     $atom_structure['sample_description_table'][$i]['video_temporal_quality']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  4));
    856                                     $atom_structure['sample_description_table'][$i]['video_spatial_quality']   =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  4));
    857                                     $atom_structure['sample_description_table'][$i]['video_frame_width']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16,  2));
    858                                     $atom_structure['sample_description_table'][$i]['video_frame_height']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18,  2));
    859                                     $atom_structure['sample_description_table'][$i]['video_resolution_x']      = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20,  4));
    860                                     $atom_structure['sample_description_table'][$i]['video_resolution_y']      = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24,  4));
    861                                     $atom_structure['sample_description_table'][$i]['video_data_size']         =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28,  4));
    862                                     $atom_structure['sample_description_table'][$i]['video_frame_count']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32,  2));
    863                                     $atom_structure['sample_description_table'][$i]['video_encoder_name_len']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34,  1));
    864                                     $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']);
    865                                     $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66,  2));
    866                                     $atom_structure['sample_description_table'][$i]['video_color_table_id']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68,  2));
    867 
    868                                     $atom_structure['sample_description_table'][$i]['video_pixel_color_type']  = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
    869                                     $atom_structure['sample_description_table'][$i]['video_pixel_color_name']  = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
    870 
    871                                     if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
    872                                         $info['quicktime']['video']['codec_fourcc']        = $atom_structure['sample_description_table'][$i]['data_format'];
    873                                         $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
    874                                         $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']);
    875                                         $info['quicktime']['video']['color_depth']         = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
    876                                         $info['quicktime']['video']['color_depth_name']    = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
    877 
    878                                         $info['video']['codec']           = $info['quicktime']['video']['codec'];
    879                                         $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
    880                                     }
    881                                     $info['video']['lossless']           = false;
    882                                     $info['video']['pixel_aspect_ratio'] = (float) 1;
    883                                     break;
    884                             }
     644                            $atom_structure['data']           =                           substr($atom_data, 12);
    885645                            break;
    886646                    }
    887                     switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
    888                         case 'mp4a':
    889                             $info['audio']['dataformat']         = 'mp4';
    890                             $info['quicktime']['audio']['codec'] = 'mp4';
    891                             break;
    892 
    893                         case '3ivx':
    894                         case '3iv1':
    895                         case '3iv2':
    896                             $info['video']['dataformat'] = '3ivx';
    897                             break;
    898 
    899                         case 'xvid':
    900                             $info['video']['dataformat'] = 'xvid';
    901                             break;
    902 
    903                         case 'mp4v':
    904                             $info['video']['dataformat'] = 'mpeg4';
    905                             break;
    906 
    907                         case 'divx':
    908                         case 'div1':
    909                         case 'div2':
    910                         case 'div3':
    911                         case 'div4':
    912                         case 'div5':
    913                         case 'div6':
    914                             $info['video']['dataformat'] = 'divx';
    915                             break;
    916 
    917                         default:
    918                             // do nothing
    919                             break;
    920                     }
    921                     unset($atom_structure['sample_description_table'][$i]['data']);
    922                 }
    923                 break;
    924 
    925 
    926             case 'stts': // Sample Table Time-to-Sample atom
    927                 $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    928                 $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    929                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    930                 $sttsEntriesDataOffset = 8;
    931                 //$FrameRateCalculatorArray = array();
    932                 $frames_count = 0;
    933 
    934                 $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
    935                 if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
    936                     $this->warning('QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($atom_structure['number_entries'] / 1048576).'MB).');
    937                 }
    938                 for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
    939                     $atom_structure['time_to_sample_table'][$i]['sample_count']    = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
    940                     $sttsEntriesDataOffset += 4;
    941                     $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
    942                     $sttsEntriesDataOffset += 4;
    943 
    944                     $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
    945 
    946                     // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
    947                     //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
    948                     //  $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
    949                     //  if ($stts_new_framerate <= 60) {
    950                     //      // some atoms have durations of "1" giving a very large framerate, which probably is not right
    951                     //      $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
    952                     //  }
    953                     //}
    954                     //
    955                     //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
    956                 }
    957                 $info['quicktime']['stts_framecount'][] = $frames_count;
    958                 //$sttsFramesTotal  = 0;
    959                 //$sttsSecondsTotal = 0;
    960                 //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
    961                 //  if (($frames_per_second > 60) || ($frames_per_second < 1)) {
    962                 //      // not video FPS information, probably audio information
    963                 //      $sttsFramesTotal  = 0;
    964                 //      $sttsSecondsTotal = 0;
    965                 //      break;
    966                 //  }
    967                 //  $sttsFramesTotal  += $frame_count;
    968                 //  $sttsSecondsTotal += $frame_count / $frames_per_second;
    969                 //}
    970                 //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
    971                 //  if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
    972                 //      $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
    973                 //  }
    974                 //}
    975                 break;
    976 
    977 
    978             case 'stss': // Sample Table Sync Sample (key frames) atom
    979                 if ($ParseAllPossibleAtoms) {
     647                    break;
     648
     649
     650                case 'rmqu': // Reference Movie QUality atom
     651                    $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
     652                    break;
     653
     654
     655                case 'rmcs': // Reference Movie Cpu Speed atom
     656                    $atom_structure['version']          = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     657                    $atom_structure['flags_raw']        = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     658                    $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
     659                    break;
     660
     661
     662                case 'rmvc': // Reference Movie Version Check atom
     663                    $atom_structure['version']            = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     664                    $atom_structure['flags_raw']          = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     665                    $atom_structure['gestalt_selector']   =                           substr($atom_data,  4, 4);
     666                    $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     667                    $atom_structure['gestalt_value']      = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
     668                    $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
     669                    break;
     670
     671
     672                case 'rmcd': // Reference Movie Component check atom
     673                    $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     674                    $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     675                    $atom_structure['component_type']         =                           substr($atom_data,  4, 4);
     676                    $atom_structure['component_subtype']      =                           substr($atom_data,  8, 4);
     677                    $atom_structure['component_manufacturer'] =                           substr($atom_data, 12, 4);
     678                    $atom_structure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
     679                    $atom_structure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
     680                    $atom_structure['component_min_version']  = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
     681                    break;
     682
     683
     684                case 'rmdr': // Reference Movie Data Rate atom
     685                    $atom_structure['version']       = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     686                    $atom_structure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     687                    $atom_structure['data_rate']     = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     688
     689                    $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
     690                    break;
     691
     692
     693                case 'rmla': // Reference Movie Language Atom
     694                    $atom_structure['version']     = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     695                    $atom_structure['flags_raw']   = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     696                    $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
     697
     698                    $atom_structure['language']    = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
     699                    if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
     700                        $info['comments']['language'][] = $atom_structure['language'];
     701                    }
     702                    break;
     703
     704
     705                case 'ptv ': // Print To Video - defines a movie's full screen mode
     706                    // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
     707                    $atom_structure['display_size_raw']  = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
     708                    $atom_structure['reserved_1']        = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
     709                    $atom_structure['reserved_2']        = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
     710                    $atom_structure['slide_show_flag']   = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
     711                    $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
     712
     713                    $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
     714                    $atom_structure['flags']['slide_show']   = (bool) $atom_structure['slide_show_flag'];
     715
     716                    $ptv_lookup[0] = 'normal';
     717                    $ptv_lookup[1] = 'double';
     718                    $ptv_lookup[2] = 'half';
     719                    $ptv_lookup[3] = 'full';
     720                    $ptv_lookup[4] = 'current';
     721                    if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
     722                        $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
     723                    } else {
     724                        $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
     725                    }
     726                    break;
     727
     728
     729                case 'stsd': // Sample Table Sample Description atom
    980730                    $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    981731                    $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    982732                    $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    983                     $stssEntriesDataOffset = 8;
     733
     734                    // see: https://github.com/JamesHeinrich/getID3/issues/111
     735                    // Some corrupt files have been known to have high bits set in the number_entries field
     736                    // This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000
     737                    // Workaround: mask off the upper byte and throw a warning if it's nonzero
     738                    if ($atom_structure['number_entries'] > 0x000FFFFF) {
     739                        if ($atom_structure['number_entries'] > 0x00FFFFFF) {
     740                            $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Ignoring upper byte and interpreting this as 0x'.getid3_lib::PrintHexBytes(substr($atom_data, 5, 3), true, false).' = '.($atom_structure['number_entries'] & 0x00FFFFFF));
     741                            $atom_structure['number_entries'] = ($atom_structure['number_entries'] & 0x00FFFFFF);
     742                        } else {
     743                            $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Please report this to info@getid3.org referencing bug report #111');
     744                        }
     745                    }
     746
     747                    $stsdEntriesDataOffset = 8;
    984748                    for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    985                         $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
    986                         $stssEntriesDataOffset += 4;
    987                     }
    988                 }
    989                 break;
    990 
    991 
    992             case 'stsc': // Sample Table Sample-to-Chunk atom
    993                 if ($ParseAllPossibleAtoms) {
     749                        $atom_structure['sample_description_table'][$i]['size']             = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
     750                        $stsdEntriesDataOffset += 4;
     751                        $atom_structure['sample_description_table'][$i]['data_format']      =                           substr($atom_data, $stsdEntriesDataOffset, 4);
     752                        $stsdEntriesDataOffset += 4;
     753                        $atom_structure['sample_description_table'][$i]['reserved']         = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
     754                        $stsdEntriesDataOffset += 6;
     755                        $atom_structure['sample_description_table'][$i]['reference_index']  = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
     756                        $stsdEntriesDataOffset += 2;
     757                        $atom_structure['sample_description_table'][$i]['data']             =                           substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
     758                        $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
     759
     760                        $atom_structure['sample_description_table'][$i]['encoder_version']  = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  0, 2));
     761                        $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  2, 2));
     762                        $atom_structure['sample_description_table'][$i]['encoder_vendor']   =                           substr($atom_structure['sample_description_table'][$i]['data'],  4, 4);
     763
     764                        switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
     765
     766                            case "\x00\x00\x00\x00":
     767                                // audio tracks
     768                                $atom_structure['sample_description_table'][$i]['audio_channels']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  2));
     769                                $atom_structure['sample_description_table'][$i]['audio_bit_depth']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10,  2));
     770                                $atom_structure['sample_description_table'][$i]['audio_compression_id'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  2));
     771                                $atom_structure['sample_description_table'][$i]['audio_packet_size']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14,  2));
     772                                $atom_structure['sample_description_table'][$i]['audio_sample_rate']    = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16,  4));
     773
     774                                // video tracks
     775                                // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
     776                                $atom_structure['sample_description_table'][$i]['temporal_quality'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  4));
     777                                $atom_structure['sample_description_table'][$i]['spatial_quality']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  4));
     778                                $atom_structure['sample_description_table'][$i]['width']            =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16,  2));
     779                                $atom_structure['sample_description_table'][$i]['height']           =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18,  2));
     780                                $atom_structure['sample_description_table'][$i]['resolution_x']     = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24,  4));
     781                                $atom_structure['sample_description_table'][$i]['resolution_y']     = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28,  4));
     782                                $atom_structure['sample_description_table'][$i]['data_size']        =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32,  4));
     783                                $atom_structure['sample_description_table'][$i]['frame_count']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36,  2));
     784                                $atom_structure['sample_description_table'][$i]['compressor_name']  =                             substr($atom_structure['sample_description_table'][$i]['data'], 38,  4);
     785                                $atom_structure['sample_description_table'][$i]['pixel_depth']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42,  2));
     786                                $atom_structure['sample_description_table'][$i]['color_table_id']   =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44,  2));
     787
     788                                switch ($atom_structure['sample_description_table'][$i]['data_format']) {
     789                                    case '2vuY':
     790                                    case 'avc1':
     791                                    case 'cvid':
     792                                    case 'dvc ':
     793                                    case 'dvcp':
     794                                    case 'gif ':
     795                                    case 'h263':
     796                                    case 'jpeg':
     797                                    case 'kpcd':
     798                                    case 'mjpa':
     799                                    case 'mjpb':
     800                                    case 'mp4v':
     801                                    case 'png ':
     802                                    case 'raw ':
     803                                    case 'rle ':
     804                                    case 'rpza':
     805                                    case 'smc ':
     806                                    case 'SVQ1':
     807                                    case 'SVQ3':
     808                                    case 'tiff':
     809                                    case 'v210':
     810                                    case 'v216':
     811                                    case 'v308':
     812                                    case 'v408':
     813                                    case 'v410':
     814                                    case 'yuv2':
     815                                        $info['fileformat'] = 'mp4';
     816                                        $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
     817                                        if ($this->QuicktimeVideoCodecLookup($info['video']['fourcc'])) {
     818                                            $info['video']['fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($info['video']['fourcc']);
     819                                        }
     820
     821                                        // https://www.getid3.org/phpBB3/viewtopic.php?t=1550
     822                                        //if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers
     823                                        if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
     824                                            // assume that values stored here are more important than values stored in [tkhd] atom
     825                                            $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
     826                                            $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
     827                                            $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
     828                                            $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
     829                                        }
     830                                        break;
     831
     832                                    case 'qtvr':
     833                                        $info['video']['dataformat'] = 'quicktimevr';
     834                                        break;
     835
     836                                    case 'mp4a':
     837                                    default:
     838                                        $info['quicktime']['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
     839                                        $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
     840                                        $info['quicktime']['audio']['channels']    = $atom_structure['sample_description_table'][$i]['audio_channels'];
     841                                        $info['quicktime']['audio']['bit_depth']   = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
     842                                        $info['audio']['codec']                    = $info['quicktime']['audio']['codec'];
     843                                        $info['audio']['sample_rate']              = $info['quicktime']['audio']['sample_rate'];
     844                                        $info['audio']['channels']                 = $info['quicktime']['audio']['channels'];
     845                                        $info['audio']['bits_per_sample']          = $info['quicktime']['audio']['bit_depth'];
     846                                        switch ($atom_structure['sample_description_table'][$i]['data_format']) {
     847                                            case 'raw ': // PCM
     848                                            case 'alac': // Apple Lossless Audio Codec
     849                                            case 'sowt': // signed/two's complement (Little Endian)
     850                                            case 'twos': // signed/two's complement (Big Endian)
     851                                            case 'in24': // 24-bit Integer
     852                                            case 'in32': // 32-bit Integer
     853                                            case 'fl32': // 32-bit Floating Point
     854                                            case 'fl64': // 64-bit Floating Point
     855                                                $info['audio']['lossless'] = $info['quicktime']['audio']['lossless'] = true;
     856                                                $info['audio']['bitrate']  = $info['quicktime']['audio']['bitrate']  = $info['audio']['channels'] * $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'];
     857                                                break;
     858                                            default:
     859                                                $info['audio']['lossless'] = false;
     860                                                break;
     861                                        }
     862                                        break;
     863                                }
     864                                break;
     865
     866                            default:
     867                                switch ($atom_structure['sample_description_table'][$i]['data_format']) {
     868                                    case 'mp4s':
     869                                        $info['fileformat'] = 'mp4';
     870                                        break;
     871
     872                                    default:
     873                                        // video atom
     874                                        $atom_structure['sample_description_table'][$i]['video_temporal_quality']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'],  8,  4));
     875                                        $atom_structure['sample_description_table'][$i]['video_spatial_quality']   =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12,  4));
     876                                        $atom_structure['sample_description_table'][$i]['video_frame_width']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16,  2));
     877                                        $atom_structure['sample_description_table'][$i]['video_frame_height']      =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18,  2));
     878                                        $atom_structure['sample_description_table'][$i]['video_resolution_x']      = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20,  4));
     879                                        $atom_structure['sample_description_table'][$i]['video_resolution_y']      = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24,  4));
     880                                        $atom_structure['sample_description_table'][$i]['video_data_size']         =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28,  4));
     881                                        $atom_structure['sample_description_table'][$i]['video_frame_count']       =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32,  2));
     882                                        $atom_structure['sample_description_table'][$i]['video_encoder_name_len']  =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34,  1));
     883                                        $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']);
     884                                        $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66,  2));
     885                                        $atom_structure['sample_description_table'][$i]['video_color_table_id']    =   getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68,  2));
     886
     887                                        $atom_structure['sample_description_table'][$i]['video_pixel_color_type']  = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
     888                                        $atom_structure['sample_description_table'][$i]['video_pixel_color_name']  = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
     889
     890                                        if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
     891                                            $info['quicktime']['video']['codec_fourcc']        = $atom_structure['sample_description_table'][$i]['data_format'];
     892                                            $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
     893                                            $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']);
     894                                            $info['quicktime']['video']['color_depth']         = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
     895                                            $info['quicktime']['video']['color_depth_name']    = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
     896
     897                                            $info['video']['codec']           = $info['quicktime']['video']['codec'];
     898                                            $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
     899                                        }
     900                                        $info['video']['lossless']           = false;
     901                                        $info['video']['pixel_aspect_ratio'] = (float) 1;
     902                                        break;
     903                                }
     904                                break;
     905                        }
     906                        switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
     907                            case 'mp4a':
     908                                $info['audio']['dataformat']         = 'mp4';
     909                                $info['quicktime']['audio']['codec'] = 'mp4';
     910                                break;
     911
     912                            case '3ivx':
     913                            case '3iv1':
     914                            case '3iv2':
     915                                $info['video']['dataformat'] = '3ivx';
     916                                break;
     917
     918                            case 'xvid':
     919                                $info['video']['dataformat'] = 'xvid';
     920                                break;
     921
     922                            case 'mp4v':
     923                                $info['video']['dataformat'] = 'mpeg4';
     924                                break;
     925
     926                            case 'divx':
     927                            case 'div1':
     928                            case 'div2':
     929                            case 'div3':
     930                            case 'div4':
     931                            case 'div5':
     932                            case 'div6':
     933                                $info['video']['dataformat'] = 'divx';
     934                                break;
     935
     936                            default:
     937                                // do nothing
     938                                break;
     939                        }
     940                        unset($atom_structure['sample_description_table'][$i]['data']);
     941                    }
     942                    break;
     943
     944
     945                case 'stts': // Sample Table Time-to-Sample atom
    994946                    $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    995947                    $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    996948                    $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    997                     $stscEntriesDataOffset = 8;
    998                     for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    999                         $atom_structure['sample_to_chunk_table'][$i]['first_chunk']        = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
    1000                         $stscEntriesDataOffset += 4;
    1001                         $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk']  = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
    1002                         $stscEntriesDataOffset += 4;
    1003                         $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
    1004                         $stscEntriesDataOffset += 4;
    1005                     }
    1006                 }
    1007                 break;
    1008 
    1009 
    1010             case 'stsz': // Sample Table SiZe atom
    1011                 if ($ParseAllPossibleAtoms) {
    1012                     $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1013                     $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1014                     $atom_structure['sample_size']    = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1015                     $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    1016                     $stszEntriesDataOffset = 12;
    1017                     if ($atom_structure['sample_size'] == 0) {
     949                    $sttsEntriesDataOffset = 8;
     950                    //$FrameRateCalculatorArray = array();
     951                    $frames_count = 0;
     952
     953                    $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
     954                    if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
     955                        $this->warning('QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).');
     956                    }
     957                    for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
     958                        $atom_structure['time_to_sample_table'][$i]['sample_count']    = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
     959                        $sttsEntriesDataOffset += 4;
     960                        $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
     961                        $sttsEntriesDataOffset += 4;
     962
     963                        $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
     964
     965                        // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
     966                        //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
     967                        //  $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
     968                        //  if ($stts_new_framerate <= 60) {
     969                        //      // some atoms have durations of "1" giving a very large framerate, which probably is not right
     970                        //      $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
     971                        //  }
     972                        //}
     973                        //
     974                        //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
     975                    }
     976                    $info['quicktime']['stts_framecount'][] = $frames_count;
     977                    //$sttsFramesTotal  = 0;
     978                    //$sttsSecondsTotal = 0;
     979                    //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
     980                    //  if (($frames_per_second > 60) || ($frames_per_second < 1)) {
     981                    //      // not video FPS information, probably audio information
     982                    //      $sttsFramesTotal  = 0;
     983                    //      $sttsSecondsTotal = 0;
     984                    //      break;
     985                    //  }
     986                    //  $sttsFramesTotal  += $frame_count;
     987                    //  $sttsSecondsTotal += $frame_count / $frames_per_second;
     988                    //}
     989                    //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
     990                    //  if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
     991                    //      $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
     992                    //  }
     993                    //}
     994                    break;
     995
     996
     997                case 'stss': // Sample Table Sync Sample (key frames) atom
     998                    if ($ParseAllPossibleAtoms) {
     999                        $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1000                        $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1001                        $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1002                        $stssEntriesDataOffset = 8;
    10181003                        for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    1019                             $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
    1020                             $stszEntriesDataOffset += 4;
     1004                            $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
     1005                            $stssEntriesDataOffset += 4;
    10211006                        }
    10221007                    }
    1023                 }
    1024                 break;
    1025 
    1026 
    1027             case 'stco': // Sample Table Chunk Offset atom
    1028                 if ($ParseAllPossibleAtoms) {
     1008                    break;
     1009
     1010
     1011                case 'stsc': // Sample Table Sample-to-Chunk atom
     1012                    if ($ParseAllPossibleAtoms) {
     1013                        $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1014                        $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1015                        $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1016                        $stscEntriesDataOffset = 8;
     1017                        for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
     1018                            $atom_structure['sample_to_chunk_table'][$i]['first_chunk']        = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
     1019                            $stscEntriesDataOffset += 4;
     1020                            $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk']  = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
     1021                            $stscEntriesDataOffset += 4;
     1022                            $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
     1023                            $stscEntriesDataOffset += 4;
     1024                        }
     1025                    }
     1026                    break;
     1027
     1028
     1029                case 'stsz': // Sample Table SiZe atom
     1030                    if ($ParseAllPossibleAtoms) {
     1031                        $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1032                        $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1033                        $atom_structure['sample_size']    = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1034                        $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     1035                        $stszEntriesDataOffset = 12;
     1036                        if ($atom_structure['sample_size'] == 0) {
     1037                            for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
     1038                                $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
     1039                                $stszEntriesDataOffset += 4;
     1040                            }
     1041                        }
     1042                    }
     1043                    break;
     1044
     1045
     1046                case 'stco': // Sample Table Chunk Offset atom
     1047                    if ($ParseAllPossibleAtoms) {
     1048                        $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1049                        $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1050                        $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1051                        $stcoEntriesDataOffset = 8;
     1052                        for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
     1053                            $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
     1054                            $stcoEntriesDataOffset += 4;
     1055                        }
     1056                    }
     1057                    break;
     1058
     1059
     1060                case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
     1061                    if ($ParseAllPossibleAtoms) {
     1062                        $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1063                        $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1064                        $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1065                        $stcoEntriesDataOffset = 8;
     1066                        for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
     1067                            $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
     1068                            $stcoEntriesDataOffset += 8;
     1069                        }
     1070                    }
     1071                    break;
     1072
     1073
     1074                case 'dref': // Data REFerence atom
    10291075                    $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    10301076                    $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    10311077                    $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1032                     $stcoEntriesDataOffset = 8;
     1078                    $drefDataOffset = 8;
    10331079                    for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    1034                         $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
    1035                         $stcoEntriesDataOffset += 4;
    1036                     }
    1037                 }
    1038                 break;
    1039 
    1040 
    1041             case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
    1042                 if ($ParseAllPossibleAtoms) {
     1080                        $atom_structure['data_references'][$i]['size']                    = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
     1081                        $drefDataOffset += 4;
     1082                        $atom_structure['data_references'][$i]['type']                    =                           substr($atom_data, $drefDataOffset, 4);
     1083                        $drefDataOffset += 4;
     1084                        $atom_structure['data_references'][$i]['version']                 = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 1));
     1085                        $drefDataOffset += 1;
     1086                        $atom_structure['data_references'][$i]['flags_raw']               = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 3)); // hardcoded: 0x0000
     1087                        $drefDataOffset += 3;
     1088                        $atom_structure['data_references'][$i]['data']                    =                           substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
     1089                        $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
     1090
     1091                        $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
     1092                    }
     1093                    break;
     1094
     1095
     1096                case 'gmin': // base Media INformation atom
     1097                    $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1098                    $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1099                    $atom_structure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
     1100                    $atom_structure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
     1101                    $atom_structure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atom_data,  8, 2));
     1102                    $atom_structure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
     1103                    $atom_structure['balance']                = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
     1104                    $atom_structure['reserved']               = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
     1105                    break;
     1106
     1107
     1108                case 'smhd': // Sound Media information HeaDer atom
     1109                    $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1110                    $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1111                    $atom_structure['balance']                = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
     1112                    $atom_structure['reserved']               = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
     1113                    break;
     1114
     1115
     1116                case 'vmhd': // Video Media information HeaDer atom
     1117                    $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1118                    $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
     1119                    $atom_structure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
     1120                    $atom_structure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
     1121                    $atom_structure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atom_data,  8, 2));
     1122                    $atom_structure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
     1123
     1124                    $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
     1125                    break;
     1126
     1127
     1128                case 'hdlr': // HanDLeR reference atom
     1129                    $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1130                    $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1131                    $atom_structure['component_type']         =                           substr($atom_data,  4, 4);
     1132                    $atom_structure['component_subtype']      =                           substr($atom_data,  8, 4);
     1133                    $atom_structure['component_manufacturer'] =                           substr($atom_data, 12, 4);
     1134                    $atom_structure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
     1135                    $atom_structure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
     1136                    $atom_structure['component_name']         =      $this->Pascal2String(substr($atom_data, 24));
     1137
     1138                    if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
     1139                        $info['video']['dataformat'] = 'quicktimevr';
     1140                    }
     1141                    break;
     1142
     1143
     1144                case 'mdhd': // MeDia HeaDer atom
     1145                    $atom_structure['version']               = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1146                    $atom_structure['flags_raw']             = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1147                    $atom_structure['creation_time']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1148                    $atom_structure['modify_time']           = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     1149                    $atom_structure['time_scale']            = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
     1150                    $atom_structure['duration']              = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
     1151                    $atom_structure['language_id']           = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
     1152                    $atom_structure['quality']               = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
     1153
     1154                    if ($atom_structure['time_scale'] == 0) {
     1155                        $this->error('Corrupt Quicktime file: mdhd.time_scale == zero');
     1156                        return false;
     1157                    }
     1158                    $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
     1159
     1160                    $atom_structure['creation_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
     1161                    $atom_structure['modify_time_unix']      = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
     1162                    $atom_structure['playtime_seconds']      = $atom_structure['duration'] / $atom_structure['time_scale'];
     1163                    $atom_structure['language']              = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
     1164                    if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
     1165                        $info['comments']['language'][] = $atom_structure['language'];
     1166                    }
     1167                    break;
     1168
     1169
     1170                case 'pnot': // Preview atom
     1171                    $atom_structure['modification_date']      = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // "standard Macintosh format"
     1172                    $atom_structure['version_number']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x00
     1173                    $atom_structure['atom_type']              =                           substr($atom_data,  6, 4);        // usually: 'PICT'
     1174                    $atom_structure['atom_index']             = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
     1175
     1176                    $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
     1177                    break;
     1178
     1179
     1180                case 'crgn': // Clipping ReGioN atom
     1181                    $atom_structure['region_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2)); // The Region size, Region boundary box,
     1182                    $atom_structure['boundary_box']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 8)); // and Clipping region data fields
     1183                    $atom_structure['clipping_data'] =                           substr($atom_data, 10);           // constitute a QuickDraw region.
     1184                    break;
     1185
     1186
     1187                case 'load': // track LOAD settings atom
     1188                    $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
     1189                    $atom_structure['preload_duration']   = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1190                    $atom_structure['preload_flags_raw']  = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     1191                    $atom_structure['default_hints_raw']  = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
     1192
     1193                    $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
     1194                    $atom_structure['default_hints']['high_quality']  = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
     1195                    break;
     1196
     1197
     1198                case 'tmcd': // TiMe CoDe atom
     1199                case 'chap': // CHAPter list atom
     1200                case 'sync': // SYNChronization atom
     1201                case 'scpt': // tranSCriPT atom
     1202                case 'ssrc': // non-primary SouRCe atom
     1203                    for ($i = 0; $i < strlen($atom_data); $i += 4) {
     1204                        @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
     1205                    }
     1206                    break;
     1207
     1208
     1209                case 'elst': // Edit LiST atom
    10431210                    $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    10441211                    $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    10451212                    $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1046                     $stcoEntriesDataOffset = 8;
    1047                     for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    1048                         $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
    1049                         $stcoEntriesDataOffset += 8;
    1050                     }
    1051                 }
    1052                 break;
    1053 
    1054 
    1055             case 'dref': // Data REFerence atom
    1056                 $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1057                 $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1058                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1059                 $drefDataOffset = 8;
    1060                 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
    1061                     $atom_structure['data_references'][$i]['size']                    = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
    1062                     $drefDataOffset += 4;
    1063                     $atom_structure['data_references'][$i]['type']                    =                           substr($atom_data, $drefDataOffset, 4);
    1064                     $drefDataOffset += 4;
    1065                     $atom_structure['data_references'][$i]['version']                 = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 1));
    1066                     $drefDataOffset += 1;
    1067                     $atom_structure['data_references'][$i]['flags_raw']               = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 3)); // hardcoded: 0x0000
    1068                     $drefDataOffset += 3;
    1069                     $atom_structure['data_references'][$i]['data']                    =                           substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
    1070                     $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
    1071 
    1072                     $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
    1073                 }
    1074                 break;
    1075 
    1076 
    1077             case 'gmin': // base Media INformation atom
    1078                 $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1079                 $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1080                 $atom_structure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
    1081                 $atom_structure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
    1082                 $atom_structure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atom_data,  8, 2));
    1083                 $atom_structure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
    1084                 $atom_structure['balance']                = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
    1085                 $atom_structure['reserved']               = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
    1086                 break;
    1087 
    1088 
    1089             case 'smhd': // Sound Media information HeaDer atom
    1090                 $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1091                 $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1092                 $atom_structure['balance']                = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
    1093                 $atom_structure['reserved']               = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
    1094                 break;
    1095 
    1096 
    1097             case 'vmhd': // Video Media information HeaDer atom
    1098                 $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1099                 $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
    1100                 $atom_structure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2));
    1101                 $atom_structure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2));
    1102                 $atom_structure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atom_data,  8, 2));
    1103                 $atom_structure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
    1104 
    1105                 $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
    1106                 break;
    1107 
    1108 
    1109             case 'hdlr': // HanDLeR reference atom
    1110                 $atom_structure['version']                = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1111                 $atom_structure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1112                 $atom_structure['component_type']         =                           substr($atom_data,  4, 4);
    1113                 $atom_structure['component_subtype']      =                           substr($atom_data,  8, 4);
    1114                 $atom_structure['component_manufacturer'] =                           substr($atom_data, 12, 4);
    1115                 $atom_structure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
    1116                 $atom_structure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
    1117                 $atom_structure['component_name']         =      $this->Pascal2String(substr($atom_data, 24));
    1118 
    1119                 if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
    1120                     $info['video']['dataformat'] = 'quicktimevr';
    1121                 }
    1122                 break;
    1123 
    1124 
    1125             case 'mdhd': // MeDia HeaDer atom
    1126                 $atom_structure['version']               = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1127                 $atom_structure['flags_raw']             = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1128                 $atom_structure['creation_time']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1129                 $atom_structure['modify_time']           = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    1130                 $atom_structure['time_scale']            = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
    1131                 $atom_structure['duration']              = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
    1132                 $atom_structure['language_id']           = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
    1133                 $atom_structure['quality']               = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
    1134 
    1135                 if ($atom_structure['time_scale'] == 0) {
    1136                     $this->error('Corrupt Quicktime file: mdhd.time_scale == zero');
    1137                     return false;
    1138                 }
    1139                 $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
    1140 
    1141                 $atom_structure['creation_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
    1142                 $atom_structure['modify_time_unix']      = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
    1143                 $atom_structure['playtime_seconds']      = $atom_structure['duration'] / $atom_structure['time_scale'];
    1144                 $atom_structure['language']              = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
    1145                 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
    1146                     $info['comments']['language'][] = $atom_structure['language'];
    1147                 }
    1148                 break;
    1149 
    1150 
    1151             case 'pnot': // Preview atom
    1152                 $atom_structure['modification_date']      = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // "standard Macintosh format"
    1153                 $atom_structure['version_number']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x00
    1154                 $atom_structure['atom_type']              =                           substr($atom_data,  6, 4);        // usually: 'PICT'
    1155                 $atom_structure['atom_index']             = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
    1156 
    1157                 $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
    1158                 break;
    1159 
    1160 
    1161             case 'crgn': // Clipping ReGioN atom
    1162                 $atom_structure['region_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2)); // The Region size, Region boundary box,
    1163                 $atom_structure['boundary_box']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 8)); // and Clipping region data fields
    1164                 $atom_structure['clipping_data'] =                           substr($atom_data, 10);           // constitute a QuickDraw region.
    1165                 break;
    1166 
    1167 
    1168             case 'load': // track LOAD settings atom
    1169                 $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
    1170                 $atom_structure['preload_duration']   = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1171                 $atom_structure['preload_flags_raw']  = getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    1172                 $atom_structure['default_hints_raw']  = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
    1173 
    1174                 $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
    1175                 $atom_structure['default_hints']['high_quality']  = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
    1176                 break;
    1177 
    1178 
    1179             case 'tmcd': // TiMe CoDe atom
    1180             case 'chap': // CHAPter list atom
    1181             case 'sync': // SYNChronization atom
    1182             case 'scpt': // tranSCriPT atom
    1183             case 'ssrc': // non-primary SouRCe atom
    1184                 for ($i = 0; $i < strlen($atom_data); $i += 4) {
    1185                     @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
    1186                 }
    1187                 break;
    1188 
    1189 
    1190             case 'elst': // Edit LiST atom
    1191                 $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1192                 $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1193                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1194                 for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
    1195                     $atom_structure['edit_list'][$i]['track_duration'] =   getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
    1196                     $atom_structure['edit_list'][$i]['media_time']     =   getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
    1197                     $atom_structure['edit_list'][$i]['media_rate']     = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
    1198                 }
    1199                 break;
    1200 
    1201 
    1202             case 'kmat': // compressed MATte atom
    1203                 $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1204                 $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    1205                 $atom_structure['matte_data_raw'] =               substr($atom_data,  4);
    1206                 break;
    1207 
    1208 
    1209             case 'ctab': // Color TABle atom
    1210                 $atom_structure['color_table_seed']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // hardcoded: 0x00000000
    1211                 $atom_structure['color_table_flags']  = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x8000
    1212                 $atom_structure['color_table_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2)) + 1;
    1213                 for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
    1214                     $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
    1215                     $atom_structure['color_table'][$colortableentry]['red']   = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
    1216                     $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
    1217                     $atom_structure['color_table'][$colortableentry]['blue']  = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
    1218                 }
    1219                 break;
    1220 
    1221 
    1222             case 'mvhd': // MoVie HeaDer atom
    1223                 $atom_structure['version']            =   getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1224                 $atom_structure['flags_raw']          =   getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
    1225                 $atom_structure['creation_time']      =   getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1226                 $atom_structure['modify_time']        =   getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    1227                 $atom_structure['time_scale']         =   getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
    1228                 $atom_structure['duration']           =   getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
    1229                 $atom_structure['preferred_rate']     = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
    1230                 $atom_structure['preferred_volume']   =   getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
    1231                 $atom_structure['reserved']           =                             substr($atom_data, 26, 10);
    1232                 $atom_structure['matrix_a']           = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
    1233                 $atom_structure['matrix_b']           = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
    1234                 $atom_structure['matrix_u']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
    1235                 $atom_structure['matrix_c']           = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
    1236                 $atom_structure['matrix_d']           = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
    1237                 $atom_structure['matrix_v']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
    1238                 $atom_structure['matrix_x']           = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
    1239                 $atom_structure['matrix_y']           = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
    1240                 $atom_structure['matrix_w']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
    1241                 $atom_structure['preview_time']       =   getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
    1242                 $atom_structure['preview_duration']   =   getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
    1243                 $atom_structure['poster_time']        =   getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
    1244                 $atom_structure['selection_time']     =   getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
    1245                 $atom_structure['selection_duration'] =   getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
    1246                 $atom_structure['current_time']       =   getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
    1247                 $atom_structure['next_track_id']      =   getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
    1248 
    1249                 if ($atom_structure['time_scale'] == 0) {
    1250                     $this->error('Corrupt Quicktime file: mvhd.time_scale == zero');
    1251                     return false;
    1252                 }
    1253                 $atom_structure['creation_time_unix']        = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
    1254                 $atom_structure['modify_time_unix']          = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
    1255                 $info['quicktime']['time_scale']    = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
    1256                 $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
    1257                 $info['playtime_seconds']           = $atom_structure['duration'] / $atom_structure['time_scale'];
    1258                 break;
    1259 
    1260 
    1261             case 'tkhd': // TracK HeaDer atom
    1262                 $atom_structure['version']             =   getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1263                 $atom_structure['flags_raw']           =   getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
    1264                 $atom_structure['creation_time']       =   getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1265                 $atom_structure['modify_time']         =   getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
    1266                 $atom_structure['trackid']             =   getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
    1267                 $atom_structure['reserved1']           =   getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
    1268                 $atom_structure['duration']            =   getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
    1269                 $atom_structure['reserved2']           =   getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
    1270                 $atom_structure['layer']               =   getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
    1271                 $atom_structure['alternate_group']     =   getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
    1272                 $atom_structure['volume']              =   getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
    1273                 $atom_structure['reserved3']           =   getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
    1274 // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
    1275 // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
    1276                 $atom_structure['matrix_a']            = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
    1277                 $atom_structure['matrix_b']            = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
    1278                 $atom_structure['matrix_u']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
    1279                 $atom_structure['matrix_c']            = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
    1280                 $atom_structure['matrix_d']            = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
    1281                 $atom_structure['matrix_v']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
    1282                 $atom_structure['matrix_x']            = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
    1283                 $atom_structure['matrix_y']            = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
    1284                 $atom_structure['matrix_w']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
    1285                 $atom_structure['width']               = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
    1286                 $atom_structure['height']              = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
    1287                 $atom_structure['flags']['enabled']    = (bool) ($atom_structure['flags_raw'] & 0x0001);
    1288                 $atom_structure['flags']['in_movie']   = (bool) ($atom_structure['flags_raw'] & 0x0002);
    1289                 $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
    1290                 $atom_structure['flags']['in_poster']  = (bool) ($atom_structure['flags_raw'] & 0x0008);
    1291                 $atom_structure['creation_time_unix']  = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
    1292                 $atom_structure['modify_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
    1293 
    1294                 if ($atom_structure['flags']['enabled'] == 1) {
    1295                     if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
    1296                         $info['video']['resolution_x'] = $atom_structure['width'];
    1297                         $info['video']['resolution_y'] = $atom_structure['height'];
    1298                     }
    1299                     $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
    1300                     $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
    1301                     $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
    1302                     $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
    1303                 } else {
    1304                     // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295
    1305                     //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
    1306                     //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
    1307                     //if (isset($info['quicktime']['video']))    { unset($info['quicktime']['video']);    }
    1308                 }
    1309                 break;
    1310 
    1311 
    1312             case 'iods': // Initial Object DeScriptor atom
    1313                 // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
    1314                 // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
    1315                 $offset = 0;
    1316                 $atom_structure['version']                =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1317                 $offset += 1;
    1318                 $atom_structure['flags_raw']              =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
    1319                 $offset += 3;
    1320                 $atom_structure['mp4_iod_tag']            =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1321                 $offset += 1;
    1322                 $atom_structure['length']                 = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
    1323                 //$offset already adjusted by quicktime_read_mp4_descr_length()
    1324                 $atom_structure['object_descriptor_id']   =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
    1325                 $offset += 2;
    1326                 $atom_structure['od_profile_level']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1327                 $offset += 1;
    1328                 $atom_structure['scene_profile_level']    =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1329                 $offset += 1;
    1330                 $atom_structure['audio_profile_id']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1331                 $offset += 1;
    1332                 $atom_structure['video_profile_id']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1333                 $offset += 1;
    1334                 $atom_structure['graphics_profile_level'] =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    1335                 $offset += 1;
    1336 
    1337                 $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
    1338                 for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
    1339                     $atom_structure['track'][$i]['ES_ID_IncTag'] =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1213                    for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
     1214                        $atom_structure['edit_list'][$i]['track_duration'] =   getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
     1215                        $atom_structure['edit_list'][$i]['media_time']     =   getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
     1216                        $atom_structure['edit_list'][$i]['media_rate']     = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
     1217                    }
     1218                    break;
     1219
     1220
     1221                case 'kmat': // compressed MATte atom
     1222                    $atom_structure['version']        = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1223                    $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
     1224                    $atom_structure['matte_data_raw'] =               substr($atom_data,  4);
     1225                    break;
     1226
     1227
     1228                case 'ctab': // Color TABle atom
     1229                    $atom_structure['color_table_seed']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // hardcoded: 0x00000000
     1230                    $atom_structure['color_table_flags']  = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x8000
     1231                    $atom_structure['color_table_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  6, 2)) + 1;
     1232                    for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
     1233                        $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
     1234                        $atom_structure['color_table'][$colortableentry]['red']   = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
     1235                        $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
     1236                        $atom_structure['color_table'][$colortableentry]['blue']  = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
     1237                    }
     1238                    break;
     1239
     1240
     1241                case 'mvhd': // MoVie HeaDer atom
     1242                    $atom_structure['version']            =   getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1243                    $atom_structure['flags_raw']          =   getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
     1244                    $atom_structure['creation_time']      =   getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1245                    $atom_structure['modify_time']        =   getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     1246                    $atom_structure['time_scale']         =   getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
     1247                    $atom_structure['duration']           =   getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
     1248                    $atom_structure['preferred_rate']     = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
     1249                    $atom_structure['preferred_volume']   =   getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
     1250                    $atom_structure['reserved']           =                             substr($atom_data, 26, 10);
     1251                    $atom_structure['matrix_a']           = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
     1252                    $atom_structure['matrix_b']           = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
     1253                    $atom_structure['matrix_u']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
     1254                    $atom_structure['matrix_c']           = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
     1255                    $atom_structure['matrix_d']           = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
     1256                    $atom_structure['matrix_v']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
     1257                    $atom_structure['matrix_x']           = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
     1258                    $atom_structure['matrix_y']           = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
     1259                    $atom_structure['matrix_w']           =  getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
     1260                    $atom_structure['preview_time']       =   getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
     1261                    $atom_structure['preview_duration']   =   getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
     1262                    $atom_structure['poster_time']        =   getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
     1263                    $atom_structure['selection_time']     =   getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
     1264                    $atom_structure['selection_duration'] =   getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
     1265                    $atom_structure['current_time']       =   getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
     1266                    $atom_structure['next_track_id']      =   getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
     1267
     1268                    if ($atom_structure['time_scale'] == 0) {
     1269                        $this->error('Corrupt Quicktime file: mvhd.time_scale == zero');
     1270                        return false;
     1271                    }
     1272                    $atom_structure['creation_time_unix']        = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
     1273                    $atom_structure['modify_time_unix']          = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
     1274                    $info['quicktime']['time_scale']    = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
     1275                    $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
     1276                    $info['playtime_seconds']           = $atom_structure['duration'] / $atom_structure['time_scale'];
     1277                    break;
     1278
     1279
     1280                case 'tkhd': // TracK HeaDer atom
     1281                    $atom_structure['version']             =   getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1282                    $atom_structure['flags_raw']           =   getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
     1283                    $atom_structure['creation_time']       =   getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1284                    $atom_structure['modify_time']         =   getid3_lib::BigEndian2Int(substr($atom_data,  8, 4));
     1285                    $atom_structure['trackid']             =   getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
     1286                    $atom_structure['reserved1']           =   getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
     1287                    $atom_structure['duration']            =   getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
     1288                    $atom_structure['reserved2']           =   getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
     1289                    $atom_structure['layer']               =   getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
     1290                    $atom_structure['alternate_group']     =   getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
     1291                    $atom_structure['volume']              =   getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
     1292                    $atom_structure['reserved3']           =   getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
     1293                    // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
     1294                    // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
     1295                    $atom_structure['matrix_a']            = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
     1296                    $atom_structure['matrix_b']            = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
     1297                    $atom_structure['matrix_u']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
     1298                    $atom_structure['matrix_c']            = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
     1299                    $atom_structure['matrix_d']            = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
     1300                    $atom_structure['matrix_v']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
     1301                    $atom_structure['matrix_x']            = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
     1302                    $atom_structure['matrix_y']            = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
     1303                    $atom_structure['matrix_w']            =  getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
     1304                    $atom_structure['width']               = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
     1305                    $atom_structure['height']              = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
     1306                    $atom_structure['flags']['enabled']    = (bool) ($atom_structure['flags_raw'] & 0x0001);
     1307                    $atom_structure['flags']['in_movie']   = (bool) ($atom_structure['flags_raw'] & 0x0002);
     1308                    $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
     1309                    $atom_structure['flags']['in_poster']  = (bool) ($atom_structure['flags_raw'] & 0x0008);
     1310                    $atom_structure['creation_time_unix']  = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
     1311                    $atom_structure['modify_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
     1312
     1313                    // https://www.getid3.org/phpBB3/viewtopic.php?t=1908
     1314                    // attempt to compute rotation from matrix values
     1315                    // 2017-Dec-28: uncertain if 90/270 are correctly oriented; values returned by FixedPoint16_16 should perhaps be -1 instead of 65535(?)
     1316                    $matrixRotation = 0;
     1317                    switch ($atom_structure['matrix_a'].':'.$atom_structure['matrix_b'].':'.$atom_structure['matrix_c'].':'.$atom_structure['matrix_d']) {
     1318                        case '1:0:0:1':         $matrixRotation =   0; break;
     1319                        case '0:1:65535:0':     $matrixRotation =  90; break;
     1320                        case '65535:0:0:65535': $matrixRotation = 180; break;
     1321                        case '0:65535:1:0':     $matrixRotation = 270; break;
     1322                        default: break;
     1323                    }
     1324
     1325                    // https://www.getid3.org/phpBB3/viewtopic.php?t=2468
     1326                    // The rotation matrix can appear in the Quicktime file multiple times, at least once for each track,
     1327                    // and it's possible that only the video track (or, in theory, one of the video tracks) is flagged as
     1328                    // rotated while the other tracks (e.g. audio) is tagged as rotation=0 (behavior noted on iPhone 8 Plus)
     1329                    // The correct solution would be to check if the TrackID associated with the rotation matrix is indeed
     1330                    // a video track (or the main video track) and only set the rotation then, but since information about
     1331                    // what track is what is not trivially there to be examined, the lazy solution is to set the rotation
     1332                    // if it is found to be nonzero, on the assumption that tracks that don't need it will have rotation set
     1333                    // to zero (and be effectively ignored) and the video track will have rotation set correctly, which will
     1334                    // either be zero and automatically correct, or nonzero and be set correctly.
     1335                    if (!isset($info['video']['rotate']) || (($info['video']['rotate'] == 0) && ($matrixRotation > 0))) {
     1336                        $info['quicktime']['video']['rotate'] = $info['video']['rotate'] = $matrixRotation;
     1337                    }
     1338
     1339                    if ($atom_structure['flags']['enabled'] == 1) {
     1340                        if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
     1341                            $info['video']['resolution_x'] = $atom_structure['width'];
     1342                            $info['video']['resolution_y'] = $atom_structure['height'];
     1343                        }
     1344                        $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
     1345                        $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
     1346                        $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
     1347                        $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
     1348                    } else {
     1349                        // see: https://www.getid3.org/phpBB3/viewtopic.php?t=1295
     1350                        //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
     1351                        //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
     1352                        //if (isset($info['quicktime']['video']))    { unset($info['quicktime']['video']);    }
     1353                    }
     1354                    break;
     1355
     1356
     1357                case 'iods': // Initial Object DeScriptor atom
     1358                    // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
     1359                    // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
     1360                    $offset = 0;
     1361                    $atom_structure['version']                =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
    13401362                    $offset += 1;
    1341                     $atom_structure['track'][$i]['length']       = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
     1363                    $atom_structure['flags_raw']              =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
     1364                    $offset += 3;
     1365                    $atom_structure['mp4_iod_tag']            =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1366                    $offset += 1;
     1367                    $atom_structure['length']                 = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
    13421368                    //$offset already adjusted by quicktime_read_mp4_descr_length()
    1343                     $atom_structure['track'][$i]['track_id']     =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
    1344                     $offset += 4;
    1345                 }
    1346 
    1347                 $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
    1348                 $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
    1349                 break;
    1350 
    1351             case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
    1352                 $atom_structure['signature'] =                           substr($atom_data,  0, 4);
    1353                 $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1354                 $atom_structure['fourcc']    =                           substr($atom_data,  8, 4);
    1355                 break;
    1356 
    1357             case 'mdat': // Media DATa atom
    1358                 // 'mdat' contains the actual data for the audio/video, possibly also subtitles
    1359 
    1360 /* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */
    1361 
    1362                 // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
    1363                 $mdat_offset = 0;
    1364                 while (true) {
    1365                     if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
    1366                         $mdat_offset += 8;
    1367                     } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
    1368                         $mdat_offset += 8;
    1369                     } else {
    1370                         break;
    1371                     }
    1372                 }
    1373 
    1374                 // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
    1375                 while (($mdat_offset < (strlen($atom_data) - 8))
    1376                     && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
    1377                     && ($chapter_string_length < 1000)
    1378                     && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
    1379                     && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) {
    1380                         list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches;
    1381                         $mdat_offset += (2 + $chapter_string_length);
    1382                         @$info['quicktime']['comments']['chapters'][] = $chapter_string;
    1383 
    1384                         // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled)
    1385                         if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8
    1386                             $mdat_offset += 12;
     1369                    $atom_structure['object_descriptor_id']   =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
     1370                    $offset += 2;
     1371                    $atom_structure['od_profile_level']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1372                    $offset += 1;
     1373                    $atom_structure['scene_profile_level']    =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1374                    $offset += 1;
     1375                    $atom_structure['audio_profile_id']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1376                    $offset += 1;
     1377                    $atom_structure['video_profile_id']       =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1378                    $offset += 1;
     1379                    $atom_structure['graphics_profile_level'] =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1380                    $offset += 1;
     1381
     1382                    $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
     1383                    for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
     1384                        $atom_structure['track'][$i]['ES_ID_IncTag'] =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
     1385                        $offset += 1;
     1386                        $atom_structure['track'][$i]['length']       = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
     1387                        //$offset already adjusted by quicktime_read_mp4_descr_length()
     1388                        $atom_structure['track'][$i]['track_id']     =       getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
     1389                        $offset += 4;
     1390                    }
     1391
     1392                    $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
     1393                    $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
     1394                    break;
     1395
     1396                case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
     1397                    $atom_structure['signature'] =                           substr($atom_data,  0, 4);
     1398                    $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1399                    $atom_structure['fourcc']    =                           substr($atom_data,  8, 4);
     1400                    break;
     1401
     1402                case 'mdat': // Media DATa atom
     1403                    // 'mdat' contains the actual data for the audio/video, possibly also subtitles
     1404
     1405    /* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */
     1406
     1407                    // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
     1408                    $mdat_offset = 0;
     1409                    while (true) {
     1410                        if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
     1411                            $mdat_offset += 8;
     1412                        } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
     1413                            $mdat_offset += 8;
     1414                        } else {
     1415                            break;
    13871416                        }
    1388                 }
    1389 
    1390 
    1391                 if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
    1392 
    1393                     $info['avdataoffset'] = $atom_structure['offset'] + 8;                       // $info['quicktime'][$atomname]['offset'] + 8;
    1394                     $OldAVDataEnd         = $info['avdataend'];
    1395                     $info['avdataend']    = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
    1396 
    1397                     $getid3_temp = new getID3();
    1398                     $getid3_temp->openfile($this->getid3->filename);
    1399                     $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
    1400                     $getid3_temp->info['avdataend']    = $info['avdataend'];
    1401                     $getid3_mp3 = new getid3_mp3($getid3_temp);
    1402                     if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
    1403                         $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
    1404                         if (!empty($getid3_temp->info['warning'])) {
    1405                             foreach ($getid3_temp->info['warning'] as $value) {
    1406                                 $this->warning($value);
     1417                    }
     1418                    if (substr($atom_data, $mdat_offset, 4) == 'GPRO') {
     1419                        $GOPRO_chunk_length = getid3_lib::LittleEndian2Int(substr($atom_data, $mdat_offset + 4, 4));
     1420                        $GOPRO_offset = 8;
     1421                        $atom_structure['GPRO']['raw'] = substr($atom_data, $mdat_offset + 8, $GOPRO_chunk_length - 8);
     1422                        $atom_structure['GPRO']['firmware'] = substr($atom_structure['GPRO']['raw'],  0, 15);
     1423                        $atom_structure['GPRO']['unknown1'] = substr($atom_structure['GPRO']['raw'], 15, 16);
     1424                        $atom_structure['GPRO']['unknown2'] = substr($atom_structure['GPRO']['raw'], 31, 32);
     1425                        $atom_structure['GPRO']['unknown3'] = substr($atom_structure['GPRO']['raw'], 63, 16);
     1426                        $atom_structure['GPRO']['camera']   = substr($atom_structure['GPRO']['raw'], 79, 32);
     1427                        $info['quicktime']['camera']['model'] = rtrim($atom_structure['GPRO']['camera'], "\x00");
     1428                    }
     1429
     1430                    // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
     1431                    while (($mdat_offset < (strlen($atom_data) - 8))
     1432                        && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
     1433                        && ($chapter_string_length < 1000)
     1434                        && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
     1435                        && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) {
     1436                            list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches;
     1437                            $mdat_offset += (2 + $chapter_string_length);
     1438                            @$info['quicktime']['comments']['chapters'][] = $chapter_string;
     1439
     1440                            // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled)
     1441                            if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8
     1442                                $mdat_offset += 12;
     1443                            }
     1444                    }
     1445
     1446                    if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
     1447
     1448                        $info['avdataoffset'] = $atom_structure['offset'] + 8;                       // $info['quicktime'][$atomname]['offset'] + 8;
     1449                        $OldAVDataEnd         = $info['avdataend'];
     1450                        $info['avdataend']    = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
     1451
     1452                        $getid3_temp = new getID3();
     1453                        $getid3_temp->openfile($this->getid3->filename);
     1454                        $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
     1455                        $getid3_temp->info['avdataend']    = $info['avdataend'];
     1456                        $getid3_mp3 = new getid3_mp3($getid3_temp);
     1457                        if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
     1458                            $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
     1459                            if (!empty($getid3_temp->info['warning'])) {
     1460                                foreach ($getid3_temp->info['warning'] as $value) {
     1461                                    $this->warning($value);
     1462                                }
     1463                            }
     1464                            if (!empty($getid3_temp->info['mpeg'])) {
     1465                                $info['mpeg'] = $getid3_temp->info['mpeg'];
     1466                                if (isset($info['mpeg']['audio'])) {
     1467                                    $info['audio']['dataformat']   = 'mp3';
     1468                                    $info['audio']['codec']        = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
     1469                                    $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
     1470                                    $info['audio']['channels']     = $info['mpeg']['audio']['channels'];
     1471                                    $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
     1472                                    $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
     1473                                    $info['bitrate']               = $info['audio']['bitrate'];
     1474                                }
    14071475                            }
    14081476                        }
    1409                         if (!empty($getid3_temp->info['mpeg'])) {
    1410                             $info['mpeg'] = $getid3_temp->info['mpeg'];
    1411                             if (isset($info['mpeg']['audio'])) {
    1412                                 $info['audio']['dataformat']   = 'mp3';
    1413                                 $info['audio']['codec']        = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
    1414                                 $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
    1415                                 $info['audio']['channels']     = $info['mpeg']['audio']['channels'];
    1416                                 $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
    1417                                 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
    1418                                 $info['bitrate']               = $info['audio']['bitrate'];
     1477                        unset($getid3_mp3, $getid3_temp);
     1478                        $info['avdataend'] = $OldAVDataEnd;
     1479                        unset($OldAVDataEnd);
     1480
     1481                    }
     1482
     1483                    unset($mdat_offset, $chapter_string_length, $chapter_matches);
     1484                    break;
     1485
     1486                case 'free': // FREE space atom
     1487                case 'skip': // SKIP atom
     1488                case 'wide': // 64-bit expansion placeholder atom
     1489                    // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
     1490
     1491                    // When writing QuickTime files, it is sometimes necessary to update an atom's size.
     1492                    // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
     1493                    // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
     1494                    // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
     1495                    // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
     1496                    // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
     1497                    // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
     1498                    break;
     1499
     1500
     1501                case 'nsav': // NoSAVe atom
     1502                    // http://developer.apple.com/technotes/tn/tn2038.html
     1503                    $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
     1504                    break;
     1505
     1506                case 'ctyp': // Controller TYPe atom (seen on QTVR)
     1507                    // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
     1508                    // some controller names are:
     1509                    //   0x00 + 'std' for linear movie
     1510                    //   'none' for no controls
     1511                    $atom_structure['ctyp'] = substr($atom_data, 0, 4);
     1512                    $info['quicktime']['controller'] = $atom_structure['ctyp'];
     1513                    switch ($atom_structure['ctyp']) {
     1514                        case 'qtvr':
     1515                            $info['video']['dataformat'] = 'quicktimevr';
     1516                            break;
     1517                    }
     1518                    break;
     1519
     1520                case 'pano': // PANOrama track (seen on QTVR)
     1521                    $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
     1522                    break;
     1523
     1524                case 'hint': // HINT track
     1525                case 'hinf': //
     1526                case 'hinv': //
     1527                case 'hnti': //
     1528                    $info['quicktime']['hinting'] = true;
     1529                    break;
     1530
     1531                case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
     1532                    for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
     1533                        $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
     1534                    }
     1535                    break;
     1536
     1537
     1538                // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
     1539                case 'FXTC': // Something to do with Adobe After Effects (?)
     1540                case 'PrmA':
     1541                case 'code':
     1542                case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
     1543                case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
     1544                            // tapt seems to be used to compute the video size [https://www.getid3.org/phpBB3/viewtopic.php?t=838]
     1545                            // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
     1546                            // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
     1547                case 'ctts'://  STCompositionOffsetAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
     1548                case 'cslg'://  STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
     1549                case 'sdtp'://  STSampleDependencyAID              - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
     1550                case 'stps'://  STPartialSyncSampleAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
     1551                    //$atom_structure['data'] = $atom_data;
     1552                    break;
     1553
     1554                case "\xA9".'xyz':  // GPS latitude+longitude+altitude
     1555                    $atom_structure['data'] = $atom_data;
     1556                    if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
     1557                        @list($all, $latitude, $longitude, $altitude) = $matches;
     1558                        $info['quicktime']['comments']['gps_latitude'][]  = floatval($latitude);
     1559                        $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
     1560                        if (!empty($altitude)) {
     1561                            $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
     1562                        }
     1563                    } else {
     1564                        $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
     1565                    }
     1566                    break;
     1567
     1568                case 'NCDT':
     1569                    // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
     1570                    // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
     1571                    $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
     1572                    break;
     1573                case 'NCTH': // Nikon Camera THumbnail image
     1574                case 'NCVW': // Nikon Camera preVieW image
     1575                    // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
     1576                    if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
     1577                        $atom_structure['data'] = $atom_data;
     1578                        $atom_structure['image_mime'] = 'image/jpeg';
     1579                        $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
     1580                        $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
     1581                    }
     1582                    break;
     1583                case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
     1584                    $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
     1585                    break;
     1586                case 'NCHD': // Nikon:MakerNoteVersion  - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
     1587                case 'NCDB': // Nikon                   - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
     1588                case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
     1589                    $atom_structure['data'] = $atom_data;
     1590                    break;
     1591
     1592                case "\x00\x00\x00\x00":
     1593                    // some kind of metacontainer, may contain a big data dump such as:
     1594                    // 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
     1595                    // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
     1596
     1597                    $atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
     1598                    $atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
     1599                    $atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     1600                    //$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     1601                    break;
     1602
     1603                case 'meta': // METAdata atom
     1604                    // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
     1605
     1606                    $atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
     1607                    $atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
     1608                    $atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     1609                    break;
     1610
     1611                case 'data': // metaDATA atom
     1612                    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
     1613                    // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
     1614                    $atom_structure['language'] =                           substr($atom_data, 4 + 0, 2);
     1615                    $atom_structure['unknown']  = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
     1616                    $atom_structure['data']     =                           substr($atom_data, 4 + 4);
     1617                    $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++];
     1618
     1619                    if ($atom_structure['key_name'] && $atom_structure['data']) {
     1620                        @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
     1621                    }
     1622                    break;
     1623
     1624                case 'keys': // KEYS that may be present in the metadata atom.
     1625                    // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21
     1626                    // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom.
     1627                    // This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys".
     1628                    $atom_structure['version']       = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
     1629                    $atom_structure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
     1630                    $atom_structure['entry_count']   = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
     1631                    $keys_atom_offset = 8;
     1632                    for ($i = 1; $i <= $atom_structure['entry_count']; $i++) {
     1633                        $atom_structure['keys'][$i]['key_size']      = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4));
     1634                        $atom_structure['keys'][$i]['key_namespace'] =                           substr($atom_data, $keys_atom_offset + 4, 4);
     1635                        $atom_structure['keys'][$i]['key_value']     =                           substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8);
     1636                        $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace
     1637
     1638                        $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value'];
     1639                    }
     1640                    break;
     1641
     1642                case 'gps ':
     1643                    // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
     1644                    // The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data.
     1645                    // The first row is version/metadata/notsure, I skip that.
     1646                    // The following rows consist of 4byte address (absolute) and 4byte size (0x1000), these point to the GPS data in the file.
     1647
     1648                    $GPS_rowsize = 8; // 4 bytes for offset, 4 bytes for size
     1649                    if (strlen($atom_data) > 0) {
     1650                        if ((strlen($atom_data) % $GPS_rowsize) == 0) {
     1651                            $atom_structure['gps_toc'] = array();
     1652                            foreach (str_split($atom_data, $GPS_rowsize) as $counter => $datapair) {
     1653                                $atom_structure['gps_toc'][] = unpack('Noffset/Nsize', substr($atom_data, $counter * $GPS_rowsize, $GPS_rowsize));
    14191654                            }
     1655
     1656                            $atom_structure['gps_entries'] = array();
     1657                            $previous_offset = $this->ftell();
     1658                            foreach ($atom_structure['gps_toc'] as $key => $gps_pointer) {
     1659                                if ($key == 0) {
     1660                                    // "The first row is version/metadata/notsure, I skip that."
     1661                                    continue;
     1662                                }
     1663                                $this->fseek($gps_pointer['offset']);
     1664                                $GPS_free_data = $this->fread($gps_pointer['size']);
     1665
     1666                                /*
     1667                                // 2017-05-10: I see some of the data, notably the Hour-Minute-Second, but cannot reconcile the rest of the data. However, the NMEA "GPRMC" line is there and relatively easy to parse, so I'm using that instead
     1668
     1669                                // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
     1670                                // The structure of the GPS data atom (the 'free' atoms mentioned above) is following:
     1671                                // hour,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 48)
     1672                                // For those unfamiliar with python struct:
     1673                                // I = int
     1674                                // s = is string (size 1, in this case)
     1675                                // f = float
     1676
     1677                                //$atom_structure['gps_entries'][$key] = unpack('Vhour/Vminute/Vsecond/Vyear/Vmonth/Vday/Vactive/Vlatitude_b/Vlongitude_b/Vunknown2/flatitude/flongitude/fspeed', substr($GPS_free_data, 48));
     1678                                */
     1679
     1680                                // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
     1681                                // $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67
     1682                                // $GPRMC,002454,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3F
     1683                                // $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D
     1684                                if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) {
     1685                                    $GPS_this_GPRMC = array();
     1686                                    list(
     1687                                        $GPS_this_GPRMC['raw']['gprmc'],
     1688                                        $GPS_this_GPRMC['raw']['timestamp'],
     1689                                        $GPS_this_GPRMC['raw']['status'],
     1690                                        $GPS_this_GPRMC['raw']['latitude'],
     1691                                        $GPS_this_GPRMC['raw']['latitude_direction'],
     1692                                        $GPS_this_GPRMC['raw']['longitude'],
     1693                                        $GPS_this_GPRMC['raw']['longitude_direction'],
     1694                                        $GPS_this_GPRMC['raw']['knots'],
     1695                                        $GPS_this_GPRMC['raw']['angle'],
     1696                                        $GPS_this_GPRMC['raw']['datestamp'],
     1697                                        $GPS_this_GPRMC['raw']['variation'],
     1698                                        $GPS_this_GPRMC['raw']['variation_direction'],
     1699                                        $dummy,
     1700                                        $GPS_this_GPRMC['raw']['checksum'],
     1701                                    ) = $matches;
     1702
     1703                                    $hour   = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2);
     1704                                    $minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2);
     1705                                    $second = substr($GPS_this_GPRMC['raw']['timestamp'], 4, 2);
     1706                                    $ms     = substr($GPS_this_GPRMC['raw']['timestamp'], 6);    // may contain decimal seconds
     1707                                    $day    = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2);
     1708                                    $month  = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2);
     1709                                    $year   = substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2);
     1710                                    $year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess
     1711                                    $GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms;
     1712
     1713                                    $GPS_this_GPRMC['active'] = ($GPS_this_GPRMC['raw']['status'] == 'A'); // A=Active,V=Void
     1714
     1715                                    foreach (array('latitude','longitude') as $latlon) {
     1716                                        preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches);
     1717                                        list($dummy, $deg, $min) = $matches;
     1718                                        $GPS_this_GPRMC[$latlon] = $deg + ($min / 60);
     1719                                    }
     1720                                    $GPS_this_GPRMC['latitude']  *= (($GPS_this_GPRMC['raw']['latitude_direction']  == 'S') ? -1 : 1);
     1721                                    $GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1);
     1722
     1723                                    $GPS_this_GPRMC['heading']    = $GPS_this_GPRMC['raw']['angle'];
     1724                                    $GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots'];
     1725                                    $GPS_this_GPRMC['speed_kmh']  = $GPS_this_GPRMC['raw']['knots'] * 1.852;
     1726                                    if ($GPS_this_GPRMC['raw']['variation']) {
     1727                                        $GPS_this_GPRMC['variation']  = $GPS_this_GPRMC['raw']['variation'];
     1728                                        $GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1);
     1729                                    }
     1730
     1731                                    $atom_structure['gps_entries'][$key] = $GPS_this_GPRMC;
     1732
     1733                                    @$info['quicktime']['gps_track'][$GPS_this_GPRMC['timestamp']] = array(
     1734                                        'latitude'  => (float) $GPS_this_GPRMC['latitude'],
     1735                                        'longitude' => (float) $GPS_this_GPRMC['longitude'],
     1736                                        'speed_kmh' => (float) $GPS_this_GPRMC['speed_kmh'],
     1737                                        'heading'   => (float) $GPS_this_GPRMC['heading'],
     1738                                    );
     1739
     1740                                } else {
     1741                                    $this->warning('Unhandled GPS format in "free" atom at offset '.$gps_pointer['offset']);
     1742                                }
     1743                            }
     1744                            $this->fseek($previous_offset);
     1745
     1746                        } else {
     1747                            $this->warning('QuickTime atom "'.$atomname.'" is not mod-8 bytes long ('.$atomsize.' bytes) at offset '.$baseoffset);
    14201748                        }
    1421                     }
    1422                     unset($getid3_mp3, $getid3_temp);
    1423                     $info['avdataend'] = $OldAVDataEnd;
    1424                     unset($OldAVDataEnd);
    1425 
    1426                 }
    1427 
    1428                 unset($mdat_offset, $chapter_string_length, $chapter_matches);
    1429                 break;
    1430 
    1431             case 'free': // FREE space atom
    1432             case 'skip': // SKIP atom
    1433             case 'wide': // 64-bit expansion placeholder atom
    1434                 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
    1435 
    1436                 // When writing QuickTime files, it is sometimes necessary to update an atom's size.
    1437                 // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
    1438                 // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
    1439                 // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
    1440                 // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
    1441                 // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
    1442                 // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
    1443                 break;
    1444 
    1445 
    1446             case 'nsav': // NoSAVe atom
    1447                 // http://developer.apple.com/technotes/tn/tn2038.html
    1448                 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
    1449                 break;
    1450 
    1451             case 'ctyp': // Controller TYPe atom (seen on QTVR)
    1452                 // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
    1453                 // some controller names are:
    1454                 //   0x00 + 'std' for linear movie
    1455                 //   'none' for no controls
    1456                 $atom_structure['ctyp'] = substr($atom_data, 0, 4);
    1457                 $info['quicktime']['controller'] = $atom_structure['ctyp'];
    1458                 switch ($atom_structure['ctyp']) {
    1459                     case 'qtvr':
    1460                         $info['video']['dataformat'] = 'quicktimevr';
    1461                         break;
    1462                 }
    1463                 break;
    1464 
    1465             case 'pano': // PANOrama track (seen on QTVR)
    1466                 $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4));
    1467                 break;
    1468 
    1469             case 'hint': // HINT track
    1470             case 'hinf': //
    1471             case 'hinv': //
    1472             case 'hnti': //
    1473                 $info['quicktime']['hinting'] = true;
    1474                 break;
    1475 
    1476             case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
    1477                 for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
    1478                     $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
    1479                 }
    1480                 break;
    1481 
    1482 
    1483             // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
    1484             case 'FXTC': // Something to do with Adobe After Effects (?)
    1485             case 'PrmA':
    1486             case 'code':
    1487             case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
    1488             case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
    1489                         // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
    1490                         // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
    1491                         // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
    1492             case 'ctts'://  STCompositionOffsetAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
    1493             case 'cslg'://  STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
    1494             case 'sdtp'://  STSampleDependencyAID              - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
    1495             case 'stps'://  STPartialSyncSampleAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
    1496                 //$atom_structure['data'] = $atom_data;
    1497                 break;
    1498 
    1499             case "\xA9".'xyz':  // GPS latitude+longitude+altitude
    1500                 $atom_structure['data'] = $atom_data;
    1501                 if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
    1502                     @list($all, $latitude, $longitude, $altitude) = $matches;
    1503                     $info['quicktime']['comments']['gps_latitude'][]  = floatval($latitude);
    1504                     $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
    1505                     if (!empty($altitude)) {
    1506                         $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
    1507                     }
    1508                 } else {
    1509                     $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
    1510                 }
    1511                 break;
    1512 
    1513             case 'NCDT':
    1514                 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
    1515                 // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
    1516                 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
    1517                 break;
    1518             case 'NCTH': // Nikon Camera THumbnail image
    1519             case 'NCVW': // Nikon Camera preVieW image
    1520                 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
    1521                 if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
     1749                    } else {
     1750                        $this->warning('QuickTime atom "'.$atomname.'" is zero bytes long at offset '.$baseoffset);
     1751                    }
     1752                    break;
     1753
     1754                case 'loci':// 3GP location (El Loco)
     1755                    $loffset = 0;
     1756                    $info['quicktime']['comments']['gps_flags']     = array(  getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)));
     1757                    $info['quicktime']['comments']['gps_lang']      = array(  getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)));
     1758                    $info['quicktime']['comments']['gps_location']  = array(          $this->LociString(substr($atom_data, 6), $loffset));
     1759                    $loci_data = substr($atom_data, 6 + $loffset);
     1760                    $info['quicktime']['comments']['gps_role']      = array(  getid3_lib::BigEndian2Int(substr($loci_data, 0, 1)));
     1761                    $info['quicktime']['comments']['gps_longitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 1, 4)));
     1762                    $info['quicktime']['comments']['gps_latitude']  = array(getid3_lib::FixedPoint16_16(substr($loci_data, 5, 4)));
     1763                    $info['quicktime']['comments']['gps_altitude']  = array(getid3_lib::FixedPoint16_16(substr($loci_data, 9, 4)));
     1764                    $info['quicktime']['comments']['gps_body']      = array(          $this->LociString(substr($loci_data, 13           ), $loffset));
     1765                    $info['quicktime']['comments']['gps_notes']     = array(          $this->LociString(substr($loci_data, 13 + $loffset), $loffset));
     1766                    break;
     1767
     1768                case 'chpl': // CHaPter List
     1769                    // https://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf
     1770                    $chpl_version = getid3_lib::BigEndian2Int(substr($atom_data, 4, 1)); // Expected to be 0
     1771                    $chpl_flags   = getid3_lib::BigEndian2Int(substr($atom_data, 5, 3)); // Reserved, set to 0
     1772                    $chpl_count   = getid3_lib::BigEndian2Int(substr($atom_data, 8, 1));
     1773                    $chpl_offset = 9;
     1774                    for ($i = 0; $i < $chpl_count; $i++) {
     1775                        if (($chpl_offset + 9) >= strlen($atom_data)) {
     1776                            $this->warning('QuickTime chapter '.$i.' extends beyond end of "chpl" atom');
     1777                            break;
     1778                        }
     1779                        $info['quicktime']['chapters'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 8)) / 10000000; // timestamps are stored as 100-nanosecond units
     1780                        $chpl_offset += 8;
     1781                        $chpl_title_size = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 1));
     1782                        $chpl_offset += 1;
     1783                        $info['quicktime']['chapters'][$i]['title']     =                           substr($atom_data, $chpl_offset, $chpl_title_size);
     1784                        $chpl_offset += $chpl_title_size;
     1785                    }
     1786                    break;
     1787
     1788                case 'FIRM': // FIRMware version(?), seen on GoPro Hero4
     1789                    $info['quicktime']['camera']['firmware'] = $atom_data;
     1790                    break;
     1791
     1792                case 'CAME': // FIRMware version(?), seen on GoPro Hero4
     1793                    $info['quicktime']['camera']['serial_hash'] = unpack('H*', $atom_data);
     1794                    break;
     1795
     1796                case 'dscp':
     1797                case 'rcif':
     1798                    // https://www.getid3.org/phpBB3/viewtopic.php?t=1908
     1799                    if (substr($atom_data, 0, 7) == "\x00\x00\x00\x00\x55\xC4".'{') {
     1800                        if ($json_decoded = @json_decode(rtrim(substr($atom_data, 6), "\x00"), true)) {
     1801                            $info['quicktime']['camera'][$atomname] = $json_decoded;
     1802                            if (($atomname == 'rcif') && isset($info['quicktime']['camera']['rcif']['wxcamera']['rotate'])) {
     1803                                $info['video']['rotate'] = $info['quicktime']['video']['rotate'] = $info['quicktime']['camera']['rcif']['wxcamera']['rotate'];
     1804                            }
     1805                        } else {
     1806                            $this->warning('Failed to JSON decode atom "'.$atomname.'"');
     1807                            $atom_structure['data'] = $atom_data;
     1808                        }
     1809                        unset($json_decoded);
     1810                    } else {
     1811                        $this->warning('Expecting 55 C4 7B at start of atom "'.$atomname.'", found '.getid3_lib::PrintHexBytes(substr($atom_data, 4, 3)).' instead');
     1812                        $atom_structure['data'] = $atom_data;
     1813                    }
     1814                    break;
     1815
     1816                case 'frea':
     1817                    // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
     1818                    // may contain "scra" (PreviewImage) and/or "thma" (ThumbnailImage)
     1819                    $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
     1820                    break;
     1821                case 'tima': // subatom to "frea"
     1822                    // no idea what this does, the one sample file I've seen has a value of 0x00000027
    15221823                    $atom_structure['data'] = $atom_data;
    1523                     $atom_structure['image_mime'] = 'image/jpeg';
    1524                     $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
    1525                     $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
    1526                 }
    1527                 break;
    1528             case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
    1529                 $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
    1530                 break;
    1531             case 'NCHD': // Nikon:MakerNoteVersion  - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
    1532             case 'NCDB': // Nikon                   - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
    1533             case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
    1534                 $atom_structure['data'] = $atom_data;
    1535                 break;
    1536 
    1537             case "\x00\x00\x00\x00":
    1538                 // some kind of metacontainer, may contain a big data dump such as:
    1539                 // 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
    1540                 // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
    1541 
    1542                 $atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
    1543                 $atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
    1544                 $atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    1545                 //$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    1546                 break;
    1547 
    1548             case 'meta': // METAdata atom
    1549                 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
    1550 
    1551                 $atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
    1552                 $atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
    1553                 $atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    1554                 break;
    1555 
    1556             case 'data': // metaDATA atom
    1557                 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
    1558                 // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
    1559                 $atom_structure['language'] =                           substr($atom_data, 4 + 0, 2);
    1560                 $atom_structure['unknown']  = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
    1561                 $atom_structure['data']     =                           substr($atom_data, 4 + 4);
    1562                 $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++];
    1563 
    1564                 if ($atom_structure['key_name'] && $atom_structure['data']) {
    1565                     @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
    1566                 }
    1567                 break;
    1568 
    1569             case 'keys': // KEYS that may be present in the metadata atom.
    1570                 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21
    1571                 // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom.
    1572                 // This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys".
    1573                 $atom_structure['version']       = getid3_lib::BigEndian2Int(substr($atom_data,  0, 1));
    1574                 $atom_structure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3));
    1575                 $atom_structure['entry_count']   = getid3_lib::BigEndian2Int(substr($atom_data,  4, 4));
    1576                 $keys_atom_offset = 8;
    1577                 for ($i = 1; $i <= $atom_structure['entry_count']; $i++) {
    1578                     $atom_structure['keys'][$i]['key_size']      = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4));
    1579                     $atom_structure['keys'][$i]['key_namespace'] =                           substr($atom_data, $keys_atom_offset + 4, 4);
    1580                     $atom_structure['keys'][$i]['key_value']     =                           substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8);
    1581                     $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace
    1582 
    1583                     $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value'];
    1584                 }
    1585                 break;
    1586 
    1587             case 'gps ':
    1588                 // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
    1589                 // The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data.
    1590                 // The first row is version/metadata/notsure, I skip that.
    1591                 // The following rows consist of 4byte address (absolute) and 4byte size (0x1000), these point to the GPS data in the file.
    1592 
    1593                 $GPS_rowsize = 8; // 4 bytes for offset, 4 bytes for size
    1594                 if (strlen($atom_data) > 0) {
    1595                     if ((strlen($atom_data) % $GPS_rowsize) == 0) {
    1596                         $atom_structure['gps_toc'] = array();
    1597                         foreach (str_split($atom_data, $GPS_rowsize) as $counter => $datapair) {
    1598                             $atom_structure['gps_toc'][] = unpack('Noffset/Nsize', substr($atom_data, $counter * $GPS_rowsize, $GPS_rowsize));
    1599                         }
    1600 
    1601                         $atom_structure['gps_entries'] = array();
    1602                         $previous_offset = $this->ftell();
    1603                         foreach ($atom_structure['gps_toc'] as $key => $gps_pointer) {
    1604                             if ($key == 0) {
    1605                                 // "The first row is version/metadata/notsure, I skip that."
    1606                                 continue;
    1607                             }
    1608                             $this->fseek($gps_pointer['offset']);
    1609                             $GPS_free_data = $this->fread($gps_pointer['size']);
    1610 
    1611                             /*
    1612                             // 2017-05-10: I see some of the data, notably the Hour-Minute-Second, but cannot reconcile the rest of the data. However, the NMEA "GPRMC" line is there and relatively easy to parse, so I'm using that instead
    1613 
    1614                             // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
    1615                             // The structure of the GPS data atom (the 'free' atoms mentioned above) is following:
    1616                             // hour,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 48)
    1617                             // For those unfamiliar with python struct:
    1618                             // I = int
    1619                             // s = is string (size 1, in this case)
    1620                             // f = float
    1621 
    1622                             //$atom_structure['gps_entries'][$key] = unpack('Vhour/Vminute/Vsecond/Vyear/Vmonth/Vday/Vactive/Vlatitude_b/Vlongitude_b/Vunknown2/flatitude/flongitude/fspeed', substr($GPS_free_data, 48));
    1623                             */
    1624 
    1625                             // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
    1626                             // $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67
    1627                             // $GPRMC,002454,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3F
    1628                             // $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D
    1629                             if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) {
    1630                                 $GPS_this_GPRMC = array();
    1631                                 list(
    1632                                     $GPS_this_GPRMC['raw']['gprmc'],
    1633                                     $GPS_this_GPRMC['raw']['timestamp'],
    1634                                     $GPS_this_GPRMC['raw']['status'],
    1635                                     $GPS_this_GPRMC['raw']['latitude'],
    1636                                     $GPS_this_GPRMC['raw']['latitude_direction'],
    1637                                     $GPS_this_GPRMC['raw']['longitude'],
    1638                                     $GPS_this_GPRMC['raw']['longitude_direction'],
    1639                                     $GPS_this_GPRMC['raw']['knots'],
    1640                                     $GPS_this_GPRMC['raw']['angle'],
    1641                                     $GPS_this_GPRMC['raw']['datestamp'],
    1642                                     $GPS_this_GPRMC['raw']['variation'],
    1643                                     $GPS_this_GPRMC['raw']['variation_direction'],
    1644                                     $dummy,
    1645                                     $GPS_this_GPRMC['raw']['checksum'],
    1646                                 ) = $matches;
    1647 
    1648                                 $hour   = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2);
    1649                                 $minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2);
    1650                                 $second = substr($GPS_this_GPRMC['raw']['timestamp'], 4, 2);
    1651                                 $ms     = substr($GPS_this_GPRMC['raw']['timestamp'], 6);    // may contain decimal seconds
    1652                                 $day   = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2);
    1653                                 $month = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2);
    1654                                 $year  = substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2);
    1655                                 $year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess
    1656                                 $GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms;
    1657 
    1658                                 $GPS_this_GPRMC['active'] = ($GPS_this_GPRMC['raw']['status'] == 'A'); // A=Active,V=Void
    1659 
    1660                                 foreach (array('latitude','longitude') as $latlon) {
    1661                                     preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches);
    1662                                     list($dummy, $deg, $min) = $matches;
    1663                                     $GPS_this_GPRMC[$latlon] = $deg + ($min / 60);
    1664                                 }
    1665                                 $GPS_this_GPRMC['latitude']  *= (($GPS_this_GPRMC['raw']['latitude_direction']  == 'S') ? -1 : 1);
    1666                                 $GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1);
    1667 
    1668                                 $GPS_this_GPRMC['heading']    = $GPS_this_GPRMC['raw']['angle'];
    1669                                 $GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots'];
    1670                                 $GPS_this_GPRMC['speed_kmh']  = $GPS_this_GPRMC['raw']['knots'] * 1.852;
    1671                                 if ($GPS_this_GPRMC['raw']['variation']) {
    1672                                     $GPS_this_GPRMC['variation']  = $GPS_this_GPRMC['raw']['variation'];
    1673                                     $GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1);
    1674                                 }
    1675 
    1676                                 $atom_structure['gps_entries'][$key] = $GPS_this_GPRMC;
    1677 
    1678                                 @$info['quicktime']['gps_track'][$GPS_this_GPRMC['timestamp']] = array(
    1679                                     'latitude'  => $GPS_this_GPRMC['latitude'],
    1680                                     'longitude' => $GPS_this_GPRMC['longitude'],
    1681                                     'speed_kmh' => $GPS_this_GPRMC['speed_kmh'],
    1682                                     'heading'   => $GPS_this_GPRMC['heading'],
    1683                                 );
    1684 
    1685                             } else {
    1686                                 $this->warning('Unhandled GPS format in "free" atom at offset '.$gps_pointer['offset']);
    1687                             }
    1688                         }
    1689                         $this->fseek($previous_offset);
    1690 
    1691                     } else {
    1692                         $this->warning('QuickTime atom "'.$atomname.'" is not mod-8 bytes long ('.$atomsize.' bytes) at offset '.$baseoffset);
    1693                     }
    1694                 } else {
    1695                     $this->warning('QuickTime atom "'.$atomname.'" is zero bytes long at offset '.$baseoffset);
    1696                 }
    1697                 break;
    1698 
    1699             case 'loci':// 3GP location (El Loco)
    1700                                 $info['quicktime']['comments']['gps_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
    1701                                 $info['quicktime']['comments']['gps_lang'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
    1702                                 $loffset = 0;
    1703                                 $info['quicktime']['comments']['gps_location'] = $this->LociString(substr($atom_data, 6), $loffset);
    1704                                 $loci_data=substr($atom_data, 6 + $loffset);
    1705                                 $info['quicktime']['comments']['gps_role'] = getid3_lib::BigEndian2Int(substr($loci_data, 0, 1));
    1706                                 $info['quicktime']['comments']['gps_longitude'] = getid3_lib::FixedPoint16_16(substr($loci_data, 1, 4));
    1707                                 $info['quicktime']['comments']['gps_latitude'] = getid3_lib::FixedPoint16_16(substr($loci_data, 5, 4));
    1708                                 $info['quicktime']['comments']['gps_altitude'] = getid3_lib::FixedPoint16_16(substr($loci_data, 9, 4));
    1709                                 $info['quicktime']['comments']['gps_body'] = $this->LociString(substr($loci_data, 13), $loffset);
    1710                                 $info['quicktime']['comments']['gps_notes'] = $this->LociString(substr($loci_data, 13 + $loffset), $loffset);
    1711                                 break;
    1712 
    1713             default:
    1714                 $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset);
    1715                 $atom_structure['data'] = $atom_data;
    1716                 break;
     1824                    break;
     1825                case 'ver ': // subatom to "frea"
     1826                    // some kind of version number, the one sample file I've seen has a value of "3.00.073"
     1827                    $atom_structure['data'] = $atom_data;
     1828                    break;
     1829                case 'thma': // subatom to "frea" -- "ThumbnailImage"
     1830                    // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
     1831                    if (strlen($atom_data) > 0) {
     1832                        $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg');
     1833                    }
     1834                    break;
     1835                case 'scra': // subatom to "frea" -- "PreviewImage"
     1836                    // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
     1837                    // but the only sample file I've seen has no useful data here
     1838                    if (strlen($atom_data) > 0) {
     1839                        $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg');
     1840                    }
     1841                    break;
     1842
     1843
     1844                default:
     1845                    $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset);
     1846                    $atom_structure['data'] = $atom_data;
     1847                    break;
     1848            }
    17171849        }
    17181850        array_pop($atomHierarchy);
     
    17201852    }
    17211853
     1854    /**
     1855     * @param string $atom_data
     1856     * @param int    $baseoffset
     1857     * @param array  $atomHierarchy
     1858     * @param bool   $ParseAllPossibleAtoms
     1859     *
     1860     * @return array|false
     1861     */
    17221862    public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
    1723 //echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>';
    17241863        $atom_structure  = false;
    17251864        $subatomoffset  = 0;
     
    17421881                return $atom_structure;
    17431882            }
    1744 
    1745             $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
    1746 
     1883            $atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
    17471884            $subatomoffset += $subatomsize;
    1748             $subatomcounter++;
    17491885        }
    17501886        return $atom_structure;
    17511887    }
    17521888
    1753 
     1889    /**
     1890     * @param string $data
     1891     * @param int    $offset
     1892     *
     1893     * @return int
     1894     */
    17541895    public function quicktime_read_mp4_descr_length($data, &$offset) {
    17551896        // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
     
    17631904    }
    17641905
    1765 
     1906    /**
     1907     * @param int $languageid
     1908     *
     1909     * @return string
     1910     */
    17661911    public function QuicktimeLanguageLookup($languageid) {
    17671912        // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353
     
    19012046    }
    19022047
     2048    /**
     2049     * @param string $codecid
     2050     *
     2051     * @return string
     2052     */
    19032053    public function QuicktimeVideoCodecLookup($codecid) {
    19042054        static $QuicktimeVideoCodecLookup = array();
     
    19602110    }
    19612111
     2112    /**
     2113     * @param string $codecid
     2114     *
     2115     * @return mixed|string
     2116     */
    19622117    public function QuicktimeAudioCodecLookup($codecid) {
    19632118        static $QuicktimeAudioCodecLookup = array();
     
    20052160    }
    20062161
     2162    /**
     2163     * @param string $compressionid
     2164     *
     2165     * @return string
     2166     */
    20072167    public function QuicktimeDCOMLookup($compressionid) {
    20082168        static $QuicktimeDCOMLookup = array();
     
    20142174    }
    20152175
     2176    /**
     2177     * @param int $colordepthid
     2178     *
     2179     * @return string
     2180     */
    20162181    public function QuicktimeColorNameLookup($colordepthid) {
    20172182        static $QuicktimeColorNameLookup = array();
     
    20322197    }
    20332198
     2199    /**
     2200     * @param int $stik
     2201     *
     2202     * @return string
     2203     */
    20342204    public function QuicktimeSTIKLookup($stik) {
    20352205        static $QuicktimeSTIKLookup = array();
     
    20492219    }
    20502220
     2221    /**
     2222     * @param int $audio_profile_id
     2223     *
     2224     * @return string
     2225     */
    20512226    public function QuicktimeIODSaudioProfileName($audio_profile_id) {
    20522227        static $QuicktimeIODSaudioProfileNameLookup = array();
     
    21082283    }
    21092284
    2110 
     2285    /**
     2286     * @param int $video_profile_id
     2287     *
     2288     * @return string
     2289     */
    21112290    public function QuicktimeIODSvideoProfileName($video_profile_id) {
    21122291        static $QuicktimeIODSvideoProfileNameLookup = array();
     
    21802359    }
    21812360
    2182 
     2361    /**
     2362     * @param int $rtng
     2363     *
     2364     * @return string
     2365     */
    21832366    public function QuicktimeContentRatingLookup($rtng) {
    21842367        static $QuicktimeContentRatingLookup = array();
     
    21912374    }
    21922375
     2376    /**
     2377     * @param int $akid
     2378     *
     2379     * @return string
     2380     */
    21932381    public function QuicktimeStoreAccountTypeLookup($akid) {
    21942382        static $QuicktimeStoreAccountTypeLookup = array();
     
    22002388    }
    22012389
     2390    /**
     2391     * @param int $sfid
     2392     *
     2393     * @return string
     2394     */
    22022395    public function QuicktimeStoreFrontCodeLookup($sfid) {
    22032396        static $QuicktimeStoreFrontCodeLookup = array();
     
    22292422    }
    22302423
     2424    /**
     2425     * @param string $atom_data
     2426     *
     2427     * @return array
     2428     */
    22312429    public function QuicktimeParseNikonNCTG($atom_data) {
    22322430        // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
     
    22712469
    22722470        $offset = 0;
     2471        $data = null;
    22732472        $datalength = strlen($atom_data);
    22742473        $parsed = array();
    22752474        while ($offset < $datalength) {
    2276 //echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
    22772475            $record_type       = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));  $offset += 4;
    22782476            $data_size_type    = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));  $offset += 2;
     
    23292527                    break;
    23302528                default:
    2331 echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
     2529                    echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
    23322530                    break 2;
    23332531            }
     
    24072605    }
    24082606
    2409 
     2607    /**
     2608     * @param string $keyname
     2609     * @param string|array $data
     2610     * @param string $boxname
     2611     *
     2612     * @return bool
     2613     */
    24102614    public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
    24112615        static $handyatomtranslatorarray = array();
     
    24512655            $handyatomtranslatorarray["\xA9".'swr'] = 'software';
    24522656            $handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool';       // iTunes 4.0
    2453             $handyatomtranslatorarray["\xA9".'trk'] = 'track';
     2657            $handyatomtranslatorarray["\xA9".'trk'] = 'track_number';
    24542658            $handyatomtranslatorarray["\xA9".'url'] = 'url';
    24552659            $handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
     
    25042708
    25052709            // http://age.hobba.nl/audio/tag_frame_reference.html
    2506             $handyatomtranslatorarray['PLAY_COUNTER']                = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
    2507             $handyatomtranslatorarray['MEDIATYPE']                   = 'mediatype';    // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
     2710            $handyatomtranslatorarray['PLAY_COUNTER']                = 'play_counter'; // Foobar2000 - https://www.getid3.org/phpBB3/viewtopic.php?t=1355
     2711            $handyatomtranslatorarray['MEDIATYPE']                   = 'mediatype';    // Foobar2000 - https://www.getid3.org/phpBB3/viewtopic.php?t=1355
    25082712            */
    25092713        }
     
    25432747    }
    25442748
    2545     public function LociString($lstring, &$count) {
    2546             // Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
    2547             // Also need to return the number of bytes the string occupied so additional fields can be extracted
    2548             $len = strlen($lstring);
    2549             if ($len == 0) {
    2550                 $count = 0;
    2551                 return '';
    2552             }
    2553             if ($lstring[0] == "\x00") {
    2554                 $count = 1;
    2555                 return '';
    2556             }
    2557             //check for BOM
    2558             if ($len > 2 && (($lstring[0] == "\xFE" && $lstring[1] == "\xFF") || ($lstring[0] == "\xFF" && $lstring[1] == "\xFE"))) {
    2559                 //UTF-16
    2560                 if (preg_match('/(.*)\x00/', $lstring, $lmatches)){
    2561                      $count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
    2562                     return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
    2563                 } else {
    2564                     return '';
    2565                 }
    2566             } else {
    2567                 //UTF-8
    2568                 if (preg_match('/(.*)\x00/', $lstring, $lmatches)){
    2569                     $count = strlen($lmatches[1]) + 1; //account for trailing \x00
    2570                     return $lmatches[1];
    2571                 }else {
    2572                     return '';
    2573                 }
    2574 
    2575             }
    2576         }
    2577 
     2749    /**
     2750     * @param string $lstring
     2751     * @param int    $count
     2752     *
     2753     * @return string
     2754     */
     2755    public function LociString($lstring, &$count) {
     2756        // Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
     2757        // Also need to return the number of bytes the string occupied so additional fields can be extracted
     2758        $len = strlen($lstring);
     2759        if ($len == 0) {
     2760            $count = 0;
     2761            return '';
     2762        }
     2763        if ($lstring[0] == "\x00") {
     2764            $count = 1;
     2765            return '';
     2766        }
     2767        // check for BOM
     2768        if (($len > 2) && ((($lstring[0] == "\xFE") && ($lstring[1] == "\xFF")) || (($lstring[0] == "\xFF") && ($lstring[1] == "\xFE")))) {
     2769            // UTF-16
     2770            if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
     2771                $count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
     2772                return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
     2773            } else {
     2774                return '';
     2775            }
     2776        }
     2777        // UTF-8
     2778        if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
     2779            $count = strlen($lmatches[1]) + 1; //account for trailing \x00
     2780            return $lmatches[1];
     2781        }
     2782        return '';
     2783    }
     2784
     2785    /**
     2786     * @param string $nullterminatedstring
     2787     *
     2788     * @return string
     2789     */
    25782790    public function NoNullString($nullterminatedstring) {
    25792791        // remove the single null terminator on null terminated strings
     
    25842796    }
    25852797
     2798    /**
     2799     * @param string $pascalstring
     2800     *
     2801     * @return string
     2802     */
    25862803    public function Pascal2String($pascalstring) {
    25872804        // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
     
    25902807
    25912808
    2592     /*
    2593     // helper functions for m4b audiobook chapters
    2594     // code by Steffen Hartmann 2015-Nov-08
    2595     */
     2809    /**
     2810     * Helper functions for m4b audiobook chapters
     2811     * code by Steffen Hartmann 2015-Nov-08.
     2812     *
     2813     * @param array  $info
     2814     * @param string $tag
     2815     * @param string $history
     2816     * @param array  $result
     2817     */
    25962818    public function search_tag_by_key($info, $tag, $history, &$result) {
    25972819        foreach ($info as $key => $value) {
     
    26072829    }
    26082830
     2831    /**
     2832     * @param array  $info
     2833     * @param string $k
     2834     * @param string $v
     2835     * @param string $history
     2836     * @param array  $result
     2837     */
    26092838    public function search_tag_by_pair($info, $k, $v, $history, &$result) {
    26102839        foreach ($info as $key => $value) {
     
    26202849    }
    26212850
     2851    /**
     2852     * @param array $info
     2853     *
     2854     * @return array
     2855     */
    26222856    public function quicktime_time_to_sample_table($info) {
    26232857        $res = array();
     
    26372871    }
    26382872
    2639     function quicktime_bookmark_time_scale($info) {
     2873    /**
     2874     * @param array $info
     2875     *
     2876     * @return int
     2877     */
     2878    public function quicktime_bookmark_time_scale($info) {
    26402879        $time_scale = '';
    26412880        $ts_prefix_len = 0;
     
    26482887                $ts_res = array();
    26492888                $this->search_tag_by_key($info['quicktime']['moov'], 'time_scale', 'quicktime/moov', $ts_res);
    2650                 foreach ($ts_res as $value) {
    2651                     $prefix = substr($value[0], 0, -12);
     2889                foreach ($ts_res as $sub_value) {
     2890                    $prefix = substr($sub_value[0], 0, -12);
    26522891                    if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) {
    2653                         $time_scale = $value[1]['time_scale'];
     2892                        $time_scale = $sub_value[1]['time_scale'];
    26542893                        $ts_prefix_len = strlen($prefix);
    26552894                    }
Note: See TracChangeset for help on using the changeset viewer.