Make WordPress Core

Changeset 46112


Ignore:
Timestamp:
09/14/2019 07:06:09 PM (7 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.

Location:
trunk/src/wp-includes/ID3
Files:
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/ID3/getid3.lib.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 /////////////////////////////////////////////////////////////////
     5//  available at https://github.com/JamesHeinrich/getID3       //
     6//            or https://www.getid3.org                        //
     7//            or http://getid3.sourceforge.net                 //
    88//                                                             //
    99// getid3.lib.php - part of getID3()                           //
    10 // See readme.txt for more details                             //
     10//  see readme.txt for more details                            //
    1111//                                                            ///
    1212/////////////////////////////////////////////////////////////////
     
    1515class getid3_lib
    1616{
    17 
     17        /**
     18         * @param string $string
     19         * @param bool   $hex
     20         * @param bool   $spaces
     21         * @param string $htmlencoding
     22         *
     23         * @return string
     24         */
    1825        public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
    1926                $returnstring = '';
    2027                for ($i = 0; $i < strlen($string); $i++) {
    2128                        if ($hex) {
    22                                 $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
     29                                $returnstring .= str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT);
    2330                        } else {
    24                                 $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
     31                                $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string[$i]) ? $string[$i] : '¤');
    2532                        }
    2633                        if ($spaces) {
     
    3744        }
    3845
     46        /**
     47         * Truncates a floating-point number at the decimal point.
     48         *
     49         * @param float $floatnumber
     50         *
     51         * @return float|int returns int (if possible, otherwise float)
     52         */
    3953        public static function trunc($floatnumber) {
    40                 // truncates a floating-point number at the decimal point
    41                 // returns int (if possible, otherwise float)
    4254                if ($floatnumber >= 1) {
    4355                        $truncatednumber = floor($floatnumber);
     
    5365        }
    5466
    55 
     67        /**
     68         * @param int|null $variable
     69         * @param int      $increment
     70         *
     71         * @return bool
     72         */
    5673        public static function safe_inc(&$variable, $increment=1) {
    5774                if (isset($variable)) {
     
    6380        }
    6481
     82        /**
     83         * @param int|float $floatnum
     84         *
     85         * @return int|float
     86         */
    6587        public static function CastAsInt($floatnum) {
    6688                // convert to float if not already
     
    78100        }
    79101
     102        /**
     103         * @param int $num
     104         *
     105         * @return bool
     106         */
    80107        public static function intValueSupported($num) {
    81108                // check if integers are 64-bit
     
    94121        }
    95122
     123        /**
     124         * @param string $fraction
     125         *
     126         * @return float
     127         */
    96128        public static function DecimalizeFraction($fraction) {
    97129                list($numerator, $denominator) = explode('/', $fraction);
     
    99131        }
    100132
    101 
     133        /**
     134         * @param string $binarynumerator
     135         *
     136         * @return float
     137         */
    102138        public static function DecimalBinary2Float($binarynumerator) {
    103139                $numerator   = self::Bin2Dec($binarynumerator);
     
    106142        }
    107143
    108 
     144        /**
     145         * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
     146         *
     147         * @param string $binarypointnumber
     148         * @param int    $maxbits
     149         *
     150         * @return array
     151         */
    109152        public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
    110                 // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
    111153                if (strpos($binarypointnumber, '.') === false) {
    112154                        $binarypointnumber = '0.'.$binarypointnumber;
    113                 } elseif ($binarypointnumber{0} == '.') {
     155                } elseif ($binarypointnumber[0] == '.') {
    114156                        $binarypointnumber = '0'.$binarypointnumber;
    115157                }
    116158                $exponent = 0;
    117                 while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
     159                while (($binarypointnumber[0] != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
    118160                        if (substr($binarypointnumber, 1, 1) == '.') {
    119161                                $exponent--;
     
    123165                                $exponent += ($pointpos - 1);
    124166                                $binarypointnumber = str_replace('.', '', $binarypointnumber);
    125                                 $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
     167                                $binarypointnumber = $binarypointnumber[0].'.'.substr($binarypointnumber, 1);
    126168                        }
    127169                }
     
    130172        }
    131173
    132 
     174        /**
     175         * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
     176         *
     177         * @param float $floatvalue
     178         *
     179         * @return string
     180         */
    133181        public static function Float2BinaryDecimal($floatvalue) {
    134                 // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
    135182                $maxbits = 128; // to how many bits of precision should the calculations be taken?
    136183                $intpart   = self::trunc($floatvalue);
     
    146193        }
    147194
    148 
     195        /**
     196         * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
     197         *
     198         * @param float $floatvalue
     199         * @param int $bits
     200         *
     201         * @return string|false
     202         */
    149203        public static function Float2String($floatvalue, $bits) {
    150                 // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
     204                $exponentbits = 0;
     205                $fractionbits = 0;
    151206                switch ($bits) {
    152207                        case 32:
     
    177232        }
    178233
    179 
     234        /**
     235         * @param string $byteword
     236         *
     237         * @return float|false
     238         */
    180239        public static function LittleEndian2Float($byteword) {
    181240                return self::BigEndian2Float(strrev($byteword));
    182241        }
    183242
    184 
     243        /**
     244         * ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
     245         *
     246         * @link http://www.psc.edu/general/software/packages/ieee/ieee.html
     247         * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
     248         *
     249         * @param string $byteword
     250         *
     251         * @return float|false
     252         */
    185253        public static function BigEndian2Float($byteword) {
    186                 // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
    187                 // http://www.psc.edu/general/software/packages/ieee/ieee.html
    188                 // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
    189 
    190254                $bitword = self::BigEndian2Bin($byteword);
    191255                if (!$bitword) {
    192256                        return 0;
    193257                }
    194                 $signbit = $bitword{0};
     258                $signbit = $bitword[0];
     259                $floatvalue = 0;
     260                $exponentbits = 0;
     261                $fractionbits = 0;
    195262
    196263                switch (strlen($byteword) * 8) {
     
    209276                                // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
    210277                                $exponentstring = substr($bitword, 1, 15);
    211                                 $isnormalized = intval($bitword{16});
     278                                $isnormalized = intval($bitword[16]);
    212279                                $fractionstring = substr($bitword, 17, 63);
    213280                                $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
     
    260327        }
    261328
    262 
     329        /**
     330         * @param string $byteword
     331         * @param bool   $synchsafe
     332         * @param bool   $signed
     333         *
     334         * @return int|float|false
     335         * @throws Exception
     336         */
    263337        public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
    264338                $intvalue = 0;
     
    270344                        if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
    271345                                //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
    272                                 $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
     346                                $intvalue += (ord($byteword[$i]) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
    273347                        } else {
    274                                 $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
     348                                $intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i));
    275349                        }
    276350                }
     
    289363        }
    290364
    291 
     365        /**
     366         * @param string $byteword
     367         * @param bool   $signed
     368         *
     369         * @return int|float|false
     370         */
    292371        public static function LittleEndian2Int($byteword, $signed=false) {
    293372                return self::BigEndian2Int(strrev($byteword), false, $signed);
    294373        }
    295374
     375        /**
     376         * @param string $byteword
     377         *
     378         * @return string
     379         */
    296380        public static function LittleEndian2Bin($byteword) {
    297381                return self::BigEndian2Bin(strrev($byteword));
    298382        }
    299383
     384        /**
     385         * @param string $byteword
     386         *
     387         * @return string
     388         */
    300389        public static function BigEndian2Bin($byteword) {
    301390                $binvalue = '';
    302391                $bytewordlen = strlen($byteword);
    303392                for ($i = 0; $i < $bytewordlen; $i++) {
    304                         $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
     393                        $binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT);
    305394                }
    306395                return $binvalue;
    307396        }
    308397
    309 
     398        /**
     399         * @param int  $number
     400         * @param int  $minbytes
     401         * @param bool $synchsafe
     402         * @param bool $signed
     403         *
     404         * @return string
     405         * @throws Exception
     406         */
    310407        public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
    311408                if ($number < 0) {
     
    328425        }
    329426
    330 
     427        /**
     428         * @param int $number
     429         *
     430         * @return string
     431         */
    331432        public static function Dec2Bin($number) {
    332433                while ($number >= 256) {
     
    342443        }
    343444
    344 
     445        /**
     446         * @param string $binstring
     447         * @param bool   $signed
     448         *
     449         * @return int|float
     450         */
    345451        public static function Bin2Dec($binstring, $signed=false) {
    346452                $signmult = 1;
    347453                if ($signed) {
    348                         if ($binstring{0} == '1') {
     454                        if ($binstring[0] == '1') {
    349455                                $signmult = -1;
    350456                        }
     
    358464        }
    359465
    360 
     466        /**
     467         * @param string $binstring
     468         *
     469         * @return string
     470         */
    361471        public static function Bin2String($binstring) {
    362472                // return 'hi' for input of '0110100001101001'
     
    369479        }
    370480
    371 
     481        /**
     482         * @param int  $number
     483         * @param int  $minbytes
     484         * @param bool $synchsafe
     485         *
     486         * @return string
     487         */
    372488        public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
    373489                $intstring = '';
     
    384500        }
    385501
    386 
     502        /**
     503         * @param array $array1
     504         * @param array $array2
     505         *
     506         * @return array|false
     507         */
    387508        public static function array_merge_clobber($array1, $array2) {
    388509                // written by kcØhireability*com
     
    402523        }
    403524
    404 
     525        /**
     526         * @param array $array1
     527         * @param array $array2
     528         *
     529         * @return array|false
     530         */
    405531        public static function array_merge_noclobber($array1, $array2) {
    406532                if (!is_array($array1) || !is_array($array2)) {
     
    418544        }
    419545
     546        /**
     547         * @param array $array1
     548         * @param array $array2
     549         *
     550         * @return array|false|null
     551         */
    420552        public static function flipped_array_merge_noclobber($array1, $array2) {
    421553                if (!is_array($array1) || !is_array($array2)) {
     
    432564        }
    433565
    434 
     566        /**
     567         * @param array $theArray
     568         *
     569         * @return bool
     570         */
    435571        public static function ksort_recursive(&$theArray) {
    436572                ksort($theArray);
     
    443579        }
    444580
     581        /**
     582         * @param string $filename
     583         * @param int    $numextensions
     584         *
     585         * @return string
     586         */
    445587        public static function fileextension($filename, $numextensions=1) {
    446588                if (strstr($filename, '.')) {
     
    458600        }
    459601
    460 
     602        /**
     603         * @param int $seconds
     604         *
     605         * @return string
     606         */
    461607        public static function PlaytimeString($seconds) {
    462608                $sign = (($seconds < 0) ? '-' : '');
     
    468614        }
    469615
    470 
     616        /**
     617         * @param int $macdate
     618         *
     619         * @return int|float
     620         */
    471621        public static function DateMac2Unix($macdate) {
    472622                // Macintosh timestamp: seconds since 00:00h January 1, 1904
     
    475625        }
    476626
    477 
     627        /**
     628         * @param string $rawdata
     629         *
     630         * @return float
     631         */
    478632        public static function FixedPoint8_8($rawdata) {
    479633                return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
    480634        }
    481635
    482 
     636        /**
     637         * @param string $rawdata
     638         *
     639         * @return float
     640         */
    483641        public static function FixedPoint16_16($rawdata) {
    484642                return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
    485643        }
    486644
    487 
     645        /**
     646         * @param string $rawdata
     647         *
     648         * @return float
     649         */
    488650        public static function FixedPoint2_30($rawdata) {
    489651                $binarystring = self::BigEndian2Bin($rawdata);
     
    492654
    493655
     656        /**
     657         * @param string $ArrayPath
     658         * @param string $Separator
     659         * @param mixed $Value
     660         *
     661         * @return array
     662         */
    494663        public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
    495664                // assigns $Value to a nested array path:
     
    508677        }
    509678
     679        /**
     680         * @param array $arraydata
     681         * @param bool  $returnkey
     682         *
     683         * @return int|false
     684         */
    510685        public static function array_max($arraydata, $returnkey=false) {
    511686                $maxvalue = false;
     
    522697        }
    523698
     699        /**
     700         * @param array $arraydata
     701         * @param bool  $returnkey
     702         *
     703         * @return int|false
     704         */
    524705        public static function array_min($arraydata, $returnkey=false) {
    525706                $minvalue = false;
     
    536717        }
    537718
     719        /**
     720         * @param string $XMLstring
     721         *
     722         * @return array|false
     723         */
    538724        public static function XML2array($XMLstring) {
    539725                if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
     
    549735        }
    550736
     737        /**
     738        * @param SimpleXMLElement|array $XMLobject
     739        *
     740        * @return array
     741        */
    551742        public static function SimpleXMLelement2array($XMLobject) {
    552743                if (!is_object($XMLobject) && !is_array($XMLobject)) {
    553744                        return $XMLobject;
    554745                }
    555                 $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject);
     746                $XMLarray = $XMLobject instanceof SimpleXMLElement ? get_object_vars($XMLobject) : $XMLobject;
    556747                foreach ($XMLarray as $key => $value) {
    557748                        $XMLarray[$key] = self::SimpleXMLelement2array($value);
     
    560751        }
    561752
    562 
    563         // Allan Hansen <ahØartemis*dk>
    564         // self::md5_data() - returns md5sum for a file from startuing position to absolute end position
     753        /**
     754         * Returns checksum for a file from starting position to absolute end position.
     755         *
     756         * @param string $file
     757         * @param int    $offset
     758         * @param int    $end
     759         * @param string $algorithm
     760         *
     761         * @return string|false
     762         * @throws getid3_exception
     763         */
    565764        public static function hash_data($file, $offset, $end, $algorithm) {
    566                 static $tempdir = '';
    567765                if (!self::intValueSupported($end)) {
    568766                        return false;
    569767                }
    570                 switch ($algorithm) {
    571                         case 'md5':
    572                                 $hash_function = 'md5_file';
    573                                 $unix_call     = 'md5sum';
    574                                 $windows_call  = 'md5sum.exe';
    575                                 $hash_length   = 32;
    576                                 break;
    577 
    578                         case 'sha1':
    579                                 $hash_function = 'sha1_file';
    580                                 $unix_call     = 'sha1sum';
    581                                 $windows_call  = 'sha1sum.exe';
    582                                 $hash_length   = 40;
    583                                 break;
    584 
    585                         default:
    586                                 throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
    587                                 break;
    588                 }
     768                if (!in_array($algorithm, array('md5', 'sha1'))) {
     769                        throw new getid3_exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
     770                }
     771
    589772                $size = $end - $offset;
    590                 while (true) {
    591                         if (GETID3_OS_ISWINDOWS) {
    592 
    593                                 // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
    594                                 // Fall back to create-temp-file method:
    595                                 if ($algorithm == 'sha1') {
    596                                         break;
    597                                 }
    598 
    599                                 $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
    600                                 foreach ($RequiredFiles as $required_file) {
    601                                         if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
    602                                                 // helper apps not available - fall back to old method
    603                                                 break 2;
    604                                         }
    605                                 }
    606                                 $commandline  = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
    607                                 $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
    608                                 $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
    609 
    610                         } else {
    611 
    612                                 $commandline  = 'head -c'.$end.' '.escapeshellarg($file).' | ';
    613                                 $commandline .= 'tail -c'.$size.' | ';
    614                                 $commandline .= $unix_call;
    615 
    616                         }
    617                         if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
    618                                 //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
    619                                 break;
    620                         }
    621                         return substr(`$commandline`, 0, $hash_length);
    622                 }
    623 
    624                 if (empty($tempdir)) {
    625                         // yes this is ugly, feel free to suggest a better way
    626                         require_once(dirname(__FILE__).'/getid3.php');
    627                         $getid3_temp = new getID3();
    628                         $tempdir = $getid3_temp->tempdir;
    629                         unset($getid3_temp);
    630                 }
    631                 // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
    632                 if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
    633                         // can't find anywhere to create a temp file, just fail
    634                         return false;
    635                 }
    636 
    637                 // Init
    638                 $result = false;
    639 
    640                 // copy parts of file
    641                 try {
    642                         self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
    643                         $result = $hash_function($data_filename);
    644                 } catch (Exception $e) {
    645                         throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
    646                 }
    647                 unlink($data_filename);
    648                 return $result;
    649         }
    650 
     773
     774                $fp = fopen($file, 'rb');
     775                fseek($fp, $offset);
     776                $ctx = hash_init($algorithm);
     777                while ($size > 0) {
     778                        $buffer = fread($fp, min($size, getID3::FREAD_BUFFER_SIZE));
     779                        hash_update($ctx, $buffer);
     780                        $size -= getID3::FREAD_BUFFER_SIZE;
     781                }
     782                $hash = hash_final($ctx);
     783                fclose($fp);
     784
     785                return $hash;
     786        }
     787
     788        /**
     789         * @param string $filename_source
     790         * @param string $filename_dest
     791         * @param int    $offset
     792         * @param int    $length
     793         *
     794         * @return bool
     795         * @throws Exception
     796         *
     797         * @deprecated Unused, may be removed in future versions of getID3
     798         */
    651799        public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
    652800                if (!self::intValueSupported($offset + $length)) {
     
    661809                                                $byteslefttowrite -= $byteswritten;
    662810                                        }
     811                                        fclose($fp_dest);
    663812                                        return true;
    664813                                } else {
     814                                        fclose($fp_src);
    665815                                        throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
    666816                                }
    667                                 fclose($fp_dest);
    668817                        } else {
    669818                                throw new Exception('failed to create file for writing '.$filename_dest);
    670819                        }
    671                         fclose($fp_src);
    672820                } else {
    673821                        throw new Exception('failed to open file for reading '.$filename_source);
    674822                }
    675                 return false;
    676         }
    677 
     823        }
     824
     825        /**
     826         * @param int $charval
     827         *
     828         * @return string
     829         */
    678830        public static function iconv_fallback_int_utf8($charval) {
    679831                if ($charval < 128) {
     
    699851        }
    700852
    701         // ISO-8859-1 => UTF-8
     853        /**
     854         * ISO-8859-1 => UTF-8
     855         *
     856         * @param string $string
     857         * @param bool   $bom
     858         *
     859         * @return string
     860         */
    702861        public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
    703862                if (function_exists('utf8_encode')) {
     
    710869                }
    711870                for ($i = 0; $i < strlen($string); $i++) {
    712                         $charval = ord($string{$i});
     871                        $charval = ord($string[$i]);
    713872                        $newcharstring .= self::iconv_fallback_int_utf8($charval);
    714873                }
     
    716875        }
    717876
    718         // ISO-8859-1 => UTF-16BE
     877        /**
     878         * ISO-8859-1 => UTF-16BE
     879         *
     880         * @param string $string
     881         * @param bool   $bom
     882         *
     883         * @return string
     884         */
    719885        public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
    720886                $newcharstring = '';
     
    723889                }
    724890                for ($i = 0; $i < strlen($string); $i++) {
    725                         $newcharstring .= "\x00".$string{$i};
     891                        $newcharstring .= "\x00".$string[$i];
    726892                }
    727893                return $newcharstring;
    728894        }
    729895
    730         // ISO-8859-1 => UTF-16LE
     896        /**
     897         * ISO-8859-1 => UTF-16LE
     898         *
     899         * @param string $string
     900         * @param bool   $bom
     901         *
     902         * @return string
     903         */
    731904        public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
    732905                $newcharstring = '';
     
    735908                }
    736909                for ($i = 0; $i < strlen($string); $i++) {
    737                         $newcharstring .= $string{$i}."\x00";
     910                        $newcharstring .= $string[$i]."\x00";
    738911                }
    739912                return $newcharstring;
    740913        }
    741914
    742         // ISO-8859-1 => UTF-16LE (BOM)
     915        /**
     916         * ISO-8859-1 => UTF-16LE (BOM)
     917         *
     918         * @param string $string
     919         *
     920         * @return string
     921         */
    743922        public static function iconv_fallback_iso88591_utf16($string) {
    744923                return self::iconv_fallback_iso88591_utf16le($string, true);
    745924        }
    746925
    747         // UTF-8 => ISO-8859-1
     926        /**
     927         * UTF-8 => ISO-8859-1
     928         *
     929         * @param string $string
     930         *
     931         * @return string
     932         */
    748933        public static function iconv_fallback_utf8_iso88591($string) {
    749934                if (function_exists('utf8_decode')) {
     
    755940                $stringlength = strlen($string);
    756941                while ($offset < $stringlength) {
    757                         if ((ord($string{$offset}) | 0x07) == 0xF7) {
     942                        if ((ord($string[$offset]) | 0x07) == 0xF7) {
    758943                                // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
    759                                 $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
    760                                                    ((ord($string{($offset + 1)}) & 0x3F) << 12) &
    761                                                    ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
    762                                                         (ord($string{($offset + 3)}) & 0x3F);
     944                                $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
     945                                                   ((ord($string[($offset + 1)]) & 0x3F) << 12) &
     946                                                   ((ord($string[($offset + 2)]) & 0x3F) <<  6) &
     947                                                        (ord($string[($offset + 3)]) & 0x3F);
    763948                                $offset += 4;
    764                         } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
     949                        } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
    765950                                // 1110bbbb 10bbbbbb 10bbbbbb
    766                                 $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
    767                                                    ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
    768                                                         (ord($string{($offset + 2)}) & 0x3F);
     951                                $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
     952                                                   ((ord($string[($offset + 1)]) & 0x3F) <<  6) &
     953                                                        (ord($string[($offset + 2)]) & 0x3F);
    769954                                $offset += 3;
    770                         } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
     955                        } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
    771956                                // 110bbbbb 10bbbbbb
    772                                 $charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
    773                                                         (ord($string{($offset + 1)}) & 0x3F);
     957                                $charval = ((ord($string[($offset + 0)]) & 0x1F) <<  6) &
     958                                                        (ord($string[($offset + 1)]) & 0x3F);
    774959                                $offset += 2;
    775                         } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
     960                        } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
    776961                                // 0bbbbbbb
    777                                 $charval = ord($string{$offset});
     962                                $charval = ord($string[$offset]);
    778963                                $offset += 1;
    779964                        } else {
     
    789974        }
    790975
    791         // UTF-8 => UTF-16BE
     976        /**
     977         * UTF-8 => UTF-16BE
     978         *
     979         * @param string $string
     980         * @param bool   $bom
     981         *
     982         * @return string
     983         */
    792984        public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
    793985                $newcharstring = '';
     
    798990                $stringlength = strlen($string);
    799991                while ($offset < $stringlength) {
    800                         if ((ord($string{$offset}) | 0x07) == 0xF7) {
     992                        if ((ord($string[$offset]) | 0x07) == 0xF7) {
    801993                                // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
    802                                 $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
    803                                                    ((ord($string{($offset + 1)}) & 0x3F) << 12) &
    804                                                    ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
    805                                                         (ord($string{($offset + 3)}) & 0x3F);
     994                                $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
     995                                                   ((ord($string[($offset + 1)]) & 0x3F) << 12) &
     996                                                   ((ord($string[($offset + 2)]) & 0x3F) <<  6) &
     997                                                        (ord($string[($offset + 3)]) & 0x3F);
    806998                                $offset += 4;
    807                         } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
     999                        } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
    8081000                                // 1110bbbb 10bbbbbb 10bbbbbb
    809                                 $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
    810                                                    ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
    811                                                         (ord($string{($offset + 2)}) & 0x3F);
     1001                                $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
     1002                                                   ((ord($string[($offset + 1)]) & 0x3F) <<  6) &
     1003                                                        (ord($string[($offset + 2)]) & 0x3F);
    8121004                                $offset += 3;
    813                         } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
     1005                        } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
    8141006                                // 110bbbbb 10bbbbbb
    815                                 $charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
    816                                                         (ord($string{($offset + 1)}) & 0x3F);
     1007                                $charval = ((ord($string[($offset + 0)]) & 0x1F) <<  6) &
     1008                                                        (ord($string[($offset + 1)]) & 0x3F);
    8171009                                $offset += 2;
    818                         } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
     1010                        } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
    8191011                                // 0bbbbbbb
    820                                 $charval = ord($string{$offset});
     1012                                $charval = ord($string[$offset]);
    8211013                                $offset += 1;
    8221014                        } else {
     
    8321024        }
    8331025
    834         // UTF-8 => UTF-16LE
     1026        /**
     1027         * UTF-8 => UTF-16LE
     1028         *
     1029         * @param string $string
     1030         * @param bool   $bom
     1031         *
     1032         * @return string
     1033         */
    8351034        public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
    8361035                $newcharstring = '';
     
    8411040                $stringlength = strlen($string);
    8421041                while ($offset < $stringlength) {
    843                         if ((ord($string{$offset}) | 0x07) == 0xF7) {
     1042                        if ((ord($string[$offset]) | 0x07) == 0xF7) {
    8441043                                // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
    845                                 $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
    846                                                    ((ord($string{($offset + 1)}) & 0x3F) << 12) &
    847                                                    ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
    848                                                         (ord($string{($offset + 3)}) & 0x3F);
     1044                                $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
     1045                                                   ((ord($string[($offset + 1)]) & 0x3F) << 12) &
     1046                                                   ((ord($string[($offset + 2)]) & 0x3F) <<  6) &
     1047                                                        (ord($string[($offset + 3)]) & 0x3F);
    8491048                                $offset += 4;
    850                         } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
     1049                        } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
    8511050                                // 1110bbbb 10bbbbbb 10bbbbbb
    852                                 $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
    853                                                    ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
    854                                                         (ord($string{($offset + 2)}) & 0x3F);
     1051                                $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
     1052                                                   ((ord($string[($offset + 1)]) & 0x3F) <<  6) &
     1053                                                        (ord($string[($offset + 2)]) & 0x3F);
    8551054                                $offset += 3;
    856                         } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
     1055                        } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
    8571056                                // 110bbbbb 10bbbbbb
    858                                 $charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
    859                                                         (ord($string{($offset + 1)}) & 0x3F);
     1057                                $charval = ((ord($string[($offset + 0)]) & 0x1F) <<  6) &
     1058                                                        (ord($string[($offset + 1)]) & 0x3F);
    8601059                                $offset += 2;
    861                         } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
     1060                        } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
    8621061                                // 0bbbbbbb
    863                                 $charval = ord($string{$offset});
     1062                                $charval = ord($string[$offset]);
    8641063                                $offset += 1;
    8651064                        } else {
     
    8751074        }
    8761075
    877         // UTF-8 => UTF-16LE (BOM)
     1076        /**
     1077         * UTF-8 => UTF-16LE (BOM)
     1078         *
     1079         * @param string $string
     1080         *
     1081         * @return string
     1082         */
    8781083        public static function iconv_fallback_utf8_utf16($string) {
    8791084                return self::iconv_fallback_utf8_utf16le($string, true);
    8801085        }
    8811086
    882         // UTF-16BE => UTF-8
     1087        /**
     1088         * UTF-16BE => UTF-8
     1089         *
     1090         * @param string $string
     1091         *
     1092         * @return string
     1093         */
    8831094        public static function iconv_fallback_utf16be_utf8($string) {
    8841095                if (substr($string, 0, 2) == "\xFE\xFF") {
     
    8941105        }
    8951106
    896         // UTF-16LE => UTF-8
     1107        /**
     1108         * UTF-16LE => UTF-8
     1109         *
     1110         * @param string $string
     1111         *
     1112         * @return string
     1113         */
    8971114        public static function iconv_fallback_utf16le_utf8($string) {
    8981115                if (substr($string, 0, 2) == "\xFF\xFE") {
     
    9081125        }
    9091126
    910         // UTF-16BE => ISO-8859-1
     1127        /**
     1128         * UTF-16BE => ISO-8859-1
     1129         *
     1130         * @param string $string
     1131         *
     1132         * @return string
     1133         */
    9111134        public static function iconv_fallback_utf16be_iso88591($string) {
    9121135                if (substr($string, 0, 2) == "\xFE\xFF") {
     
    9221145        }
    9231146
    924         // UTF-16LE => ISO-8859-1
     1147        /**
     1148         * UTF-16LE => ISO-8859-1
     1149         *
     1150         * @param string $string
     1151         *
     1152         * @return string
     1153         */
    9251154        public static function iconv_fallback_utf16le_iso88591($string) {
    9261155                if (substr($string, 0, 2) == "\xFF\xFE") {
     
    9361165        }
    9371166
    938         // UTF-16 (BOM) => ISO-8859-1
     1167        /**
     1168         * UTF-16 (BOM) => ISO-8859-1
     1169         *
     1170         * @param string $string
     1171         *
     1172         * @return string
     1173         */
    9391174        public static function iconv_fallback_utf16_iso88591($string) {
    9401175                $bom = substr($string, 0, 2);
     
    9471182        }
    9481183
    949         // UTF-16 (BOM) => UTF-8
     1184        /**
     1185         * UTF-16 (BOM) => UTF-8
     1186         *
     1187         * @param string $string
     1188         *
     1189         * @return string
     1190         */
    9501191        public static function iconv_fallback_utf16_utf8($string) {
    9511192                $bom = substr($string, 0, 2);
     
    9581199        }
    9591200
     1201        /**
     1202         * @param string $in_charset
     1203         * @param string $out_charset
     1204         * @param string $string
     1205         *
     1206         * @return string
     1207         * @throws Exception
     1208         */
    9601209        public static function iconv_fallback($in_charset, $out_charset, $string) {
    9611210
     
    9641213                }
    9651214
    966                 // mb_convert_encoding() availble
     1215                // mb_convert_encoding() available
    9671216                if (function_exists('mb_convert_encoding')) {
     1217                        if ((strtoupper($in_charset) == 'UTF-16') && (substr($string, 0, 2) != "\xFE\xFF") && (substr($string, 0, 2) != "\xFF\xFE")) {
     1218                                // if BOM missing, mb_convert_encoding will mishandle the conversion, assume UTF-16BE and prepend appropriate BOM
     1219                                $string = "\xFF\xFE".$string;
     1220                        }
     1221                        if ((strtoupper($in_charset) == 'UTF-16') && (strtoupper($out_charset) == 'UTF-8')) {
     1222                                if (($string == "\xFF\xFE") || ($string == "\xFE\xFF")) {
     1223                                        // if string consists of only BOM, mb_convert_encoding will return the BOM unmodified
     1224                                        return '';
     1225                                }
     1226                        }
    9681227                        if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) {
    9691228                                switch ($out_charset) {
     
    9751234                        }
    9761235                        return $string;
    977                 }
    978                 // iconv() availble
    979                 else if (function_exists('iconv')) {
     1236
     1237                // iconv() available
     1238                } elseif (function_exists('iconv')) {
    9801239                        if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
    9811240                                switch ($out_charset) {
     
    10181277        }
    10191278
     1279        /**
     1280         * @param mixed  $data
     1281         * @param string $charset
     1282         *
     1283         * @return mixed
     1284         */
    10201285        public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
    10211286                if (is_string($data)) {
     
    10321297        }
    10331298
     1299        /**
     1300         * @param string|int|float $string
     1301         * @param string           $charset
     1302         *
     1303         * @return string
     1304         */
    10341305        public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
    10351306                $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
     
    10701341                                $strlen = strlen($string);
    10711342                                for ($i = 0; $i < $strlen; $i++) {
    1072                                         $char_ord_val = ord($string{$i});
     1343                                        $char_ord_val = ord($string[$i]);
    10731344                                        $charval = 0;
    10741345                                        if ($char_ord_val < 0x80) {
     
    10761347                                        } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F  &&  $i+3 < $strlen) {
    10771348                                                $charval  = (($char_ord_val & 0x07) << 18);
    1078                                                 $charval += ((ord($string{++$i}) & 0x3F) << 12);
    1079                                                 $charval += ((ord($string{++$i}) & 0x3F) << 6);
    1080                                                 $charval +=  (ord($string{++$i}) & 0x3F);
     1349                                                $charval += ((ord($string[++$i]) & 0x3F) << 12);
     1350                                                $charval += ((ord($string[++$i]) & 0x3F) << 6);
     1351                                                $charval +=  (ord($string[++$i]) & 0x3F);
    10811352                                        } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07  &&  $i+2 < $strlen) {
    10821353                                                $charval  = (($char_ord_val & 0x0F) << 12);
    1083                                                 $charval += ((ord($string{++$i}) & 0x3F) << 6);
    1084                                                 $charval +=  (ord($string{++$i}) & 0x3F);
     1354                                                $charval += ((ord($string[++$i]) & 0x3F) << 6);
     1355                                                $charval +=  (ord($string[++$i]) & 0x3F);
    10851356                                        } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03  &&  $i+1 < $strlen) {
    10861357                                                $charval  = (($char_ord_val & 0x1F) << 6);
    1087                                                 $charval += (ord($string{++$i}) & 0x3F);
     1358                                                $charval += (ord($string[++$i]) & 0x3F);
    10881359                                        }
    10891360                                        if (($charval >= 32) && ($charval <= 127)) {
     
    11241395        }
    11251396
    1126 
    1127 
     1397        /**
     1398         * @param int $namecode
     1399         *
     1400         * @return string
     1401         */
    11281402        public static function RGADnameLookup($namecode) {
    11291403                static $RGADname = array();
     
    11371411        }
    11381412
    1139 
     1413        /**
     1414         * @param int $originatorcode
     1415         *
     1416         * @return string
     1417         */
    11401418        public static function RGADoriginatorLookup($originatorcode) {
    11411419                static $RGADoriginator = array();
     
    11501428        }
    11511429
    1152 
     1430        /**
     1431         * @param int $rawadjustment
     1432         * @param int $signbit
     1433         *
     1434         * @return float
     1435         */
    11531436        public static function RGADadjustmentLookup($rawadjustment, $signbit) {
    1154                 $adjustment = $rawadjustment / 10;
     1437                $adjustment = (float) $rawadjustment / 10;
    11551438                if ($signbit == 1) {
    11561439                        $adjustment *= -1;
    11571440                }
    1158                 return (float) $adjustment;
    1159         }
    1160 
    1161 
     1441                return $adjustment;
     1442        }
     1443
     1444        /**
     1445         * @param int $namecode
     1446         * @param int $originatorcode
     1447         * @param int $replaygain
     1448         *
     1449         * @return string
     1450         */
    11621451        public static function RGADgainString($namecode, $originatorcode, $replaygain) {
    11631452                if ($replaygain < 0) {
     
    11751464        }
    11761465
     1466        /**
     1467         * @param float $amplitude
     1468         *
     1469         * @return float
     1470         */
    11771471        public static function RGADamplitude2dB($amplitude) {
    11781472                return 20 * log10($amplitude);
    11791473        }
    11801474
    1181 
     1475        /**
     1476         * @param string $imgData
     1477         * @param array  $imageinfo
     1478         *
     1479         * @return array|false
     1480         */
    11821481        public static function GetDataImageSize($imgData, &$imageinfo=array()) {
    11831482                static $tempdir = '';
     
    12141513        }
    12151514
     1515        /**
     1516         * @param string $mime_type
     1517         *
     1518         * @return string
     1519         */
    12161520        public static function ImageExtFromMime($mime_type) {
    12171521                // temporary way, works OK for now, but should be reworked in the future
     
    12191523        }
    12201524
    1221         public static function ImageTypesLookup($imagetypeid) {
    1222                 static $ImageTypesLookup = array();
    1223                 if (empty($ImageTypesLookup)) {
    1224                         $ImageTypesLookup[1]  = 'gif';
    1225                         $ImageTypesLookup[2]  = 'jpeg';
    1226                         $ImageTypesLookup[3]  = 'png';
    1227                         $ImageTypesLookup[4]  = 'swf';
    1228                         $ImageTypesLookup[5]  = 'psd';
    1229                         $ImageTypesLookup[6]  = 'bmp';
    1230                         $ImageTypesLookup[7]  = 'tiff (little-endian)';
    1231                         $ImageTypesLookup[8]  = 'tiff (big-endian)';
    1232                         $ImageTypesLookup[9]  = 'jpc';
    1233                         $ImageTypesLookup[10] = 'jp2';
    1234                         $ImageTypesLookup[11] = 'jpx';
    1235                         $ImageTypesLookup[12] = 'jb2';
    1236                         $ImageTypesLookup[13] = 'swc';
    1237                         $ImageTypesLookup[14] = 'iff';
    1238                 }
    1239                 return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
    1240         }
    1241 
     1525        /**
     1526         * @param array $ThisFileInfo
     1527         *
     1528         * @return bool
     1529         */
    12421530        public static function CopyTagsToComments(&$ThisFileInfo) {
    12431531
     
    13271615        }
    13281616
    1329 
     1617        /**
     1618         * @param string $key
     1619         * @param int    $begin
     1620         * @param int    $end
     1621         * @param string $file
     1622         * @param string $name
     1623         *
     1624         * @return string
     1625         */
    13301626        public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
    13311627
     
    13741670        }
    13751671
     1672        /**
     1673         * @param string $filename
     1674         * @param string $sourcefile
     1675         * @param bool   $DieOnFailure
     1676         *
     1677         * @return bool
     1678         * @throws Exception
     1679         */
    13761680        public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
    13771681                global $GETID3_ERRORARRAY;
     
    13941698        }
    13951699
     1700        /**
     1701         * @param string $string
     1702         *
     1703         * @return string
     1704         */
    13961705        public static function trimNullByte($string) {
    13971706                return trim($string, "\x00");
    13981707        }
    13991708
     1709        /**
     1710         * @param string $path
     1711         *
     1712         * @return float|bool
     1713         */
    14001714        public static function getFileSizeSyscall($path) {
    14011715                $filesize = false;
     
    14221736        }
    14231737
    1424 
    1425         /**
    1426         * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
    1427         * @param string $path A path.
    1428         * @param string $suffix If the name component ends in suffix this will also be cut off.
    1429         * @return string
    1430         */
     1738        /**
     1739         * @param string $filename
     1740         *
     1741         * @return string|false
     1742         */
     1743        public static function truepath($filename) {
     1744                // 2017-11-08: this could use some improvement, patches welcome
     1745                if (preg_match('#^(\\\\\\\\|//)[a-z0-9]#i', $filename, $matches)) {
     1746                        // PHP's built-in realpath function does not work on UNC Windows shares
     1747                        $goodpath = array();
     1748                        foreach (explode('/', str_replace('\\', '/', $filename)) as $part) {
     1749                                if ($part == '.') {
     1750                                        continue;
     1751                                }
     1752                                if ($part == '..') {
     1753                                        if (count($goodpath)) {
     1754                                                array_pop($goodpath);
     1755                                        } else {
     1756                                                // cannot step above this level, already at top level
     1757                                                return false;
     1758                                        }
     1759                                } else {
     1760                                        $goodpath[] = $part;
     1761                                }
     1762                        }
     1763                        return implode(DIRECTORY_SEPARATOR, $goodpath);
     1764                }
     1765                return realpath($filename);
     1766        }
     1767
     1768        /**
     1769         * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
     1770         *
     1771         * @param string $path A path.
     1772         * @param string $suffix If the name component ends in suffix this will also be cut off.
     1773         *
     1774         * @return string
     1775         */
    14311776        public static function mb_basename($path, $suffix = null) {
    14321777                $splited = preg_split('#/#', rtrim($path, '/ '));
  • trunk/src/wp-includes/ID3/getid3.php

    r41196 r46112  
    22/////////////////////////////////////////////////////////////////
    33/// 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 /////////////////////////////////////////////////////////////////
     4//  available at https://github.com/JamesHeinrich/getID3       //
     5//            or https://www.getid3.org                        //
     6//            or http://getid3.sourceforge.net                 //
    87//                                                             //
    98// Please see readme.txt for more information                  //
     
    2625        define('ENT_SUBSTITUTE', (defined('ENT_IGNORE') ? ENT_IGNORE : 8));
    2726}
     27
     28/*
     29https://www.getid3.org/phpBB3/viewtopic.php?t=2114
     30If you are running into a the problem where filenames with special characters are being handled
     31incorrectly by external helper programs (e.g. metaflac), notably with the special characters removed,
     32and you are passing in the filename in UTF8 (typically via a HTML form), try uncommenting this line:
     33*/
     34//setlocale(LC_CTYPE, 'en_US.UTF-8');
    2835
    2936// attempt to define temp dir as something flexible but reliable
     
    7582class getID3
    7683{
    77         // public: Settings
    78         public $encoding        = 'UTF-8';        // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
    79         public $encoding_id3v1  = 'ISO-8859-1';   // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
    80 
    81         // public: Optional tag checks - disable for speed.
    82         public $option_tag_id3v1         = true;  // Read and process ID3v1 tags
    83         public $option_tag_id3v2         = true;  // Read and process ID3v2 tags
    84         public $option_tag_lyrics3       = true;  // Read and process Lyrics3 tags
    85         public $option_tag_apetag        = true;  // Read and process APE tags
    86         public $option_tags_process      = true;  // Copy tags to root key 'tags' and encode to $this->encoding
    87         public $option_tags_html         = true;  // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
    88 
    89         // public: Optional tag/comment calucations
    90         public $option_extra_info        = true;  // Calculate additional info such as bitrate, channelmode etc
    91 
    92         // public: Optional handling of embedded attachments (e.g. images)
    93         public $option_save_attachments  = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
    94 
    95         // public: Optional calculations
    96         public $option_md5_data          = false; // Get MD5 sum of data part - slow
    97         public $option_md5_data_source   = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
    98         public $option_sha1_data         = false; // Get SHA1 sum of data part - slow
    99         public $option_max_2gb_check     = null;  // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX)
    100 
    101         // public: Read buffer size in bytes
     84        /*
     85         * Settings
     86         */
     87
     88        /**
     89         * CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
     90         *
     91         * @var string
     92         */
     93        public $encoding        = 'UTF-8';
     94
     95        /**
     96         * Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
     97         *
     98         * @var string
     99         */
     100        public $encoding_id3v1  = 'ISO-8859-1';
     101
     102        /*
     103         * Optional tag checks - disable for speed.
     104         */
     105
     106        /**
     107         * Read and process ID3v1 tags
     108         *
     109         * @var bool
     110         */
     111        public $option_tag_id3v1         = true;
     112
     113        /**
     114         * Read and process ID3v2 tags
     115         *
     116         * @var bool
     117         */
     118        public $option_tag_id3v2         = true;
     119
     120        /**
     121         * Read and process Lyrics3 tags
     122         *
     123         * @var bool
     124         */
     125        public $option_tag_lyrics3       = true;
     126
     127        /**
     128         * Read and process APE tags
     129         *
     130         * @var bool
     131         */
     132        public $option_tag_apetag        = true;
     133
     134        /**
     135         * Copy tags to root key 'tags' and encode to $this->encoding
     136         *
     137         * @var bool
     138         */
     139        public $option_tags_process      = true;
     140
     141        /**
     142         * Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
     143         *
     144         * @var bool
     145         */
     146        public $option_tags_html         = true;
     147
     148        /*
     149         * Optional tag/comment calculations
     150         */
     151
     152        /**
     153         * Calculate additional info such as bitrate, channelmode etc
     154         *
     155         * @var bool
     156         */
     157        public $option_extra_info        = true;
     158
     159        /*
     160         * Optional handling of embedded attachments (e.g. images)
     161         */
     162
     163        /**
     164         * Defaults to true (ATTACHMENTS_INLINE) for backward compatibility
     165         *
     166         * @var bool|string
     167         */
     168        public $option_save_attachments  = true;
     169
     170        /*
     171         * Optional calculations
     172         */
     173
     174        /**
     175         * Get MD5 sum of data part - slow
     176         *
     177         * @var bool
     178         */
     179        public $option_md5_data          = false;
     180
     181        /**
     182         * Use MD5 of source file if availble - only FLAC and OptimFROG
     183         *
     184         * @var bool
     185         */
     186        public $option_md5_data_source   = false;
     187
     188        /**
     189         * Get SHA1 sum of data part - slow
     190         *
     191         * @var bool
     192         */
     193        public $option_sha1_data         = false;
     194
     195        /**
     196         * Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on
     197         * PHP_INT_MAX)
     198         *
     199         * @var bool|null
     200         */
     201        public $option_max_2gb_check;
     202
     203        /**
     204         * Read buffer size in bytes
     205         *
     206         * @var int
     207         */
    102208        public $option_fread_buffer_size = 32768;
    103209
    104210        // Public variables
    105         public $filename;                         // Filename of file being analysed.
    106         public $fp;                               // Filepointer to file being analysed.
    107         public $info;                             // Result array.
     211
     212        /**
     213         * Filename of file being analysed.
     214         *
     215         * @var string
     216         */
     217        public $filename;
     218
     219        /**
     220         * Filepointer to file being analysed.
     221         *
     222         * @var resource
     223         */
     224        public $fp;
     225
     226        /**
     227         * Result array.
     228         *
     229         * @var array
     230         */
     231        public $info;
     232
     233        /**
     234         * @var string
     235         */
    108236        public $tempdir = GETID3_TEMP_DIR;
     237
     238        /**
     239         * @var int
     240         */
    109241        public $memory_limit = 0;
    110242
    111         // Protected variables
     243        /**
     244         * @var string
     245         */
    112246        protected $startup_error   = '';
     247
     248        /**
     249         * @var string
     250         */
    113251        protected $startup_warning = '';
    114252
    115         const VERSION           = '1.9.14-201706111222';
     253        const VERSION           = '1.9.17-201907240906';
    116254        const FREAD_BUFFER_SIZE = 32768;
    117255
     
    119257        const ATTACHMENTS_INLINE = true;
    120258
    121         // public: constructor
    122259        public function __construct() {
     260
     261                // Check for PHP version
     262                $required_php_version = '5.3.0';
     263                if (version_compare(PHP_VERSION, $required_php_version, '<')) {
     264                        $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION."\n";
     265                        return;
     266                }
    123267
    124268                // Check memory
     
    177321                // Needed for Windows only:
    178322                // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
    179                 //   as well as other helper functions such as head, tail, md5sum, etc
     323                //   as well as other helper functions such as head, etc
    180324                // This path cannot contain spaces, but the below code will attempt to get the
    181325                //   8.3-equivalent path automatically
     
    220364                        throw new getid3_exception($this->startup_error);
    221365                }
    222 
    223                 return true;
    224         }
    225 
     366        }
     367
     368        /**
     369         * @return string
     370         */
    226371        public function version() {
    227372                return self::VERSION;
    228373        }
    229374
     375        /**
     376         * @return int
     377         */
    230378        public function fread_buffer_size() {
    231379                return $this->option_fread_buffer_size;
    232380        }
    233381
    234 
    235         // public: setOption
     382        /**
     383         * @param array $optArray
     384         *
     385         * @return bool
     386         */
    236387        public function setOption($optArray) {
    237388                if (!is_array($optArray) || empty($optArray)) {
     
    247398        }
    248399
    249 
    250         public function openfile($filename, $filesize=null) {
     400        /**
     401         * @param string $filename
     402         * @param int    $filesize
     403         *
     404         * @return bool
     405         *
     406         * @throws getid3_exception
     407         */
     408        public function openfile($filename, $filesize=null, $fp=null) {
    251409                try {
    252410                        if (!empty($this->startup_error)) {
     
    271429
    272430                        $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
    273                         $filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename);
     431                        //$filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename);
    274432
    275433                        // open local file
    276                         //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720
    277                         if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
     434                        //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see https://www.getid3.org/phpBB3/viewtopic.php?t=1720
     435                        if (($fp != null) && ((get_resource_type($fp) == 'file') || (get_resource_type($fp) == 'stream'))) {
     436                                $this->fp = $fp;
     437                        } elseif ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
    278438                                // great
    279439                        } else {
     
    332492                                                        unset($this->info['filesize']);
    333493                                                        fclose($this->fp);
    334                                                         throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
     494                                                        throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize / 1073741824, 3).'GB, please report to info@getid3.org');
    335495                                                }
    336496                                                $this->info['filesize'] = $real_filesize;
    337                                                 $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
     497                                                $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize / 1073741824, 3).'GB) and is not properly supported by PHP.');
    338498                                }
    339499                        }
     
    347507        }
    348508
    349         // public: analyze file
    350         public function analyze($filename, $filesize=null, $original_filename='') {
     509        /**
     510         * analyze file
     511         *
     512         * @param string $filename
     513         * @param int    $filesize
     514         * @param string $original_filename
     515         *
     516         * @return array
     517         */
     518        public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
    351519                try {
    352                         if (!$this->openfile($filename, $filesize)) {
     520                        if (!$this->openfile($filename, $filesize, $fp)) {
    353521                                return $this->info;
    354522                        }
     
    384552                                if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
    385553                                        $this->info['id3v2']['header']        = true;
    386                                         $this->info['id3v2']['majorversion']  = ord($header{3});
    387                                         $this->info['id3v2']['minorversion']  = ord($header{4});
     554                                        $this->info['id3v2']['majorversion']  = ord($header[3]);
     555                                        $this->info['id3v2']['minorversion']  = ord($header[4]);
    388556                                        $this->info['avdataoffset']          += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
    389557                                }
     
    498666
    499667
    500         // private: error handling
     668        /**
     669         * Error handling.
     670         *
     671         * @param string $message
     672         *
     673         * @return array
     674         */
    501675        public function error($message) {
    502676                $this->CleanUp();
     
    509683
    510684
    511         // private: warning handling
     685        /**
     686         * Warning handling.
     687         *
     688         * @param string $message
     689         *
     690         * @return bool
     691         */
    512692        public function warning($message) {
    513693                $this->info['warning'][] = $message;
     
    516696
    517697
    518         // private: CleanUp
     698        /**
     699         * @return bool
     700         */
    519701        private function CleanUp() {
    520702
     
    563745        }
    564746
    565 
    566         // return array containing information about all supported formats
     747        /**
     748         * Return array containing information about all supported formats.
     749         *
     750         * @return array
     751         */
    567752        public function GetFileFormatArray() {
    568753                static $format_info = array();
     
    585770                                                        'group'     => 'audio',
    586771                                                        'module'    => 'aac',
    587                                                         'mime_type' => 'application/octet-stream',
     772                                                        'mime_type' => 'audio/aac',
    588773                                                        'fail_ape'  => 'WARNING',
    589774                                                ),
     
    603788                                                        'group'     => 'audio',
    604789                                                        'module'    => 'aac',
    605                                                         'mime_type' => 'application/octet-stream',
     790                                                        'mime_type' => 'audio/aac',
    606791                                                        'fail_ape'  => 'WARNING',
    607792                                                ),
     
    650835                                // DSS  - audio       - Digital Speech Standard
    651836                                'dss'  => array(
    652                                                         'pattern'   => '^[\\x02-\\x06]ds[s2]',
     837                                                        'pattern'   => '^[\\x02-\\x08]ds[s2]',
    653838                                                        'group'     => 'audio',
    654839                                                        'module'    => 'dss',
     
    669854                                                        'group'     => 'audio',
    670855                                                        'module'    => 'flac',
    671                                                         'mime_type' => 'audio/x-flac',
     856                                                        'mime_type' => 'audio/flac',
    672857                                                ),
    673858
     
    701886                                                        'group'     => 'audio',
    702887                                                        'module'    => 'monkey',
    703                                                         'mime_type' => 'application/octet-stream',
     888                                                        'mime_type' => 'audio/x-monkeys-audio',
    704889                                                ),
    705890
     
    8901075                                                        'group'     => 'audio-video',
    8911076                                                        'module'    => 'riff',
    892                                                         'mime_type' => 'audio/x-wav',
     1077                                                        'mime_type' => 'audio/wav',
    8931078                                                        'fail_ape'  => 'WARNING',
    8941079                                                ),
     
    10541239                                                        'group'     => 'archive',
    10551240                                                        'module'    => 'gzip',
    1056                                                         'mime_type' => 'application/x-gzip',
     1241                                                        'mime_type' => 'application/gzip',
    10571242                                                        'fail_id3'  => 'ERROR',
    10581243                                                        'fail_ape'  => 'ERROR',
     
    10691254                                                ),
    10701255
     1256                                // XZ   - data         - XZ compressed data
     1257                                'xz'  => array(
     1258                                                        'pattern'   => '^\\xFD7zXZ\\x00',
     1259                                                        'group'     => 'archive',
     1260                                                        'module'    => 'xz',
     1261                                                        'mime_type' => 'application/x-xz',
     1262                                                        'fail_id3'  => 'ERROR',
     1263                                                        'fail_ape'  => 'ERROR',
     1264                                                ),
     1265
    10711266
    10721267                                // Misc other formats
     
    11161311        }
    11171312
    1118 
    1119 
     1313        /**
     1314         * @param string $filedata
     1315         * @param string $filename
     1316         *
     1317         * @return mixed|false
     1318         */
    11201319        public function GetFileFormat(&$filedata, $filename='') {
    11211320                // this function will determine the format of a file based on usually
     
    11361335
    11371336                if (preg_match('#\\.mp[123a]$#i', $filename)) {
    1138                         // Too many mp3 encoders on the market put gabage in front of mpeg files
     1337                        // Too many mp3 encoders on the market put garbage in front of mpeg files
    11391338                        // use assume format on these if format detection failed
    11401339                        $GetFileFormatArray = $this->GetFileFormatArray();
     
    11551354        }
    11561355
    1157 
    1158         // converts array to $encoding charset from $this->encoding
     1356        /**
     1357         * Converts array to $encoding charset from $this->encoding.
     1358         *
     1359         * @param array  $array
     1360         * @param string $encoding
     1361         */
    11591362        public function CharConvert(&$array, $encoding) {
    11601363
     
    11791382        }
    11801383
    1181 
     1384        /**
     1385         * @return bool
     1386         */
    11821387        public function HandleAllTags() {
    11831388
     
    12341439                                        }
    12351440                                        if ($tag_key == 'picture') {
     1441                                                // pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
    12361442                                                unset($this->info[$comment_name]['comments'][$tag_key]);
    12371443                                        }
     
    12471453                                if ($this->option_tags_html) {
    12481454                                        foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
     1455                                                if ($tag_key == 'picture') {
     1456                                                        // Do not to try to convert binary picture data to HTML
     1457                                                        // https://github.com/JamesHeinrich/getID3/issues/178
     1458                                                        continue;
     1459                                                }
    12491460                                                $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
    12501461                                        }
     
    12551466                }
    12561467
    1257                 // pictures can take up a lot of space, and we don't need multiple copies of them
    1258                 // let there be a single copy in [comments][picture], and not elsewhere
     1468                // pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
    12591469                if (!empty($this->info['tags'])) {
    12601470                        $unset_keys = array('tags', 'tags_html');
     
    13021512        }
    13031513
     1514        /**
     1515         * @param string $algorithm
     1516         *
     1517         * @return array|bool
     1518         */
    13041519        public function getHashdata($algorithm) {
    13051520                switch ($algorithm) {
     
    13661581                                } else {
    13671582
    1368                                         $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
    13691583                                        $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
    13701584                                        $VorbisCommentError = `$commandline`;
     
    14241638                return true;
    14251639        }
    1426 
    14271640
    14281641        public function ChannelsBitratePlaytimeCalculations() {
     
    14901703        }
    14911704
    1492 
     1705        /**
     1706         * @return bool
     1707         */
    14931708        public function CalculateCompressionRatioVideo() {
    14941709                if (empty($this->info['video'])) {
     
    15381753        }
    15391754
    1540 
     1755        /**
     1756         * @return bool
     1757         */
    15411758        public function CalculateCompressionRatioAudio() {
    15421759                if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
     
    15551772        }
    15561773
    1557 
     1774        /**
     1775         * @return bool
     1776         */
    15581777        public function CalculateReplayGain() {
    15591778                if (isset($this->info['replay_gain'])) {
    15601779                        if (!isset($this->info['replay_gain']['reference_volume'])) {
    1561                                 $this->info['replay_gain']['reference_volume'] = (double) 89.0;
     1780                                $this->info['replay_gain']['reference_volume'] = 89.0;
    15621781                        }
    15631782                        if (isset($this->info['replay_gain']['track']['adjustment'])) {
     
    15781797        }
    15791798
     1799        /**
     1800         * @return bool
     1801         */
    15801802        public function ProcessAudioStreams() {
    15811803                if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
     
    15911813        }
    15921814
     1815        /**
     1816         * @return string|bool
     1817         */
    15931818        public function getid3_tempnam() {
    15941819                return tempnam($this->tempdir, 'gI3');
    15951820        }
    15961821
     1822        /**
     1823         * @param string $name
     1824         *
     1825         * @return bool
     1826         *
     1827         * @throws getid3_exception
     1828         */
    15971829        public function include_module($name) {
    15981830                //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
     
    16041836        }
    16051837
    1606     public static function is_writable ($filename) {
    1607         $ret = is_writable($filename);
    1608 
    1609         if (!$ret) {
    1610             $perms = fileperms($filename);
    1611             $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
    1612         }
    1613 
    1614         return $ret;
    1615     }
     1838        /**
     1839         * @param string $filename
     1840         *
     1841         * @return bool
     1842         */
     1843        public static function is_writable ($filename) {
     1844                $ret = is_writable($filename);
     1845                if (!$ret) {
     1846                        $perms = fileperms($filename);
     1847                        $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
     1848                }
     1849                return $ret;
     1850        }
    16161851
    16171852}
    16181853
    16191854
    1620 abstract class getid3_handler {
     1855abstract class getid3_handler
     1856{
    16211857
    16221858        /**
     
    16251861        protected $getid3;                       // pointer
    16261862
    1627         protected $data_string_flag     = false; // analyzing filepointer or string
    1628         protected $data_string          = '';    // string to analyze
    1629         protected $data_string_position = 0;     // seek position in string
    1630         protected $data_string_length   = 0;     // string length
    1631 
    1632         private $dependency_to = null;
    1633 
    1634 
     1863        /**
     1864         * Analyzing filepointer or string.
     1865         *
     1866         * @var bool
     1867         */
     1868        protected $data_string_flag     = false;
     1869
     1870        /**
     1871         * String to analyze.
     1872         *
     1873         * @var string
     1874         */
     1875        protected $data_string          = '';
     1876
     1877        /**
     1878         * Seek position in string.
     1879         *
     1880         * @var int
     1881         */
     1882        protected $data_string_position = 0;
     1883
     1884        /**
     1885         * String length.
     1886         *
     1887         * @var int
     1888         */
     1889        protected $data_string_length   = 0;
     1890
     1891        /**
     1892         * @var string
     1893         */
     1894        private $dependency_to;
     1895
     1896        /**
     1897         * getid3_handler constructor.
     1898         *
     1899         * @param getID3 $getid3
     1900         * @param string $call_module
     1901         */
    16351902        public function __construct(getID3 $getid3, $call_module=null) {
    16361903                $this->getid3 = $getid3;
     
    16411908        }
    16421909
    1643 
    1644         // Analyze from file pointer
     1910        /**
     1911         * Analyze from file pointer.
     1912         *
     1913         * @return bool
     1914         */
    16451915        abstract public function Analyze();
    16461916
    1647 
    1648         // Analyze from string instead
     1917        /**
     1918         * Analyze from string instead.
     1919         *
     1920         * @param string $string
     1921         */
    16491922        public function AnalyzeString($string) {
    16501923                // Enter string mode
     
    16721945        }
    16731946
     1947        /**
     1948         * @param string $string
     1949         */
    16741950        public function setStringMode($string) {
    16751951                $this->data_string_flag   = true;
     
    16781954        }
    16791955
     1956        /**
     1957         * @return int|bool
     1958         */
    16801959        protected function ftell() {
    16811960                if ($this->data_string_flag) {
     
    16851964        }
    16861965
     1966        /**
     1967         * @param int $bytes
     1968         *
     1969         * @return string|false
     1970         *
     1971         * @throws getid3_exception
     1972         */
    16871973        protected function fread($bytes) {
    16881974                if ($this->data_string_flag) {
     
    16971983                //return fread($this->getid3->fp, $bytes);
    16981984                /*
    1699                 * http://www.getid3.org/phpBB3/viewtopic.php?t=1930
     1985                * https://www.getid3.org/phpBB3/viewtopic.php?t=1930
    17001986                * "I found out that the root cause for the problem was how getID3 uses the PHP system function fread().
    17011987                * It seems to assume that fread() would always return as many bytes as were requested.
     
    17051991                $contents = '';
    17061992                do {
     1993                        //if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
     1994                        if (($this->getid3->memory_limit > 0) && (($bytes / $this->getid3->memory_limit) > 0.99)) { // enable a more-fuzzy match to prevent close misses generating errors like "PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33554464 bytes)"
     1995                                throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10);
     1996                        }
    17071997                        $part = fread($this->getid3->fp, $bytes);
    17081998                        $partLength  = strlen($part);
     
    17132003        }
    17142004
     2005        /**
     2006         * @param int $bytes
     2007         * @param int $whence
     2008         *
     2009         * @return int
     2010         *
     2011         * @throws getid3_exception
     2012         */
    17152013        protected function fseek($bytes, $whence=SEEK_SET) {
    17162014                if ($this->data_string_flag) {
     
    17432041        }
    17442042
     2043        /**
     2044         * @return bool
     2045         */
    17452046        protected function feof() {
    17462047                if ($this->data_string_flag) {
     
    17502051        }
    17512052
     2053        /**
     2054         * @param string $module
     2055         *
     2056         * @return bool
     2057         */
    17522058        final protected function isDependencyFor($module) {
    17532059                return $this->dependency_to == $module;
    17542060        }
    17552061
     2062        /**
     2063         * @param string $text
     2064         *
     2065         * @return bool
     2066         */
    17562067        protected function error($text) {
    17572068                $this->getid3->info['error'][] = $text;
     
    17602071        }
    17612072
     2073        /**
     2074         * @param string $text
     2075         *
     2076         * @return bool
     2077         */
    17622078        protected function warning($text) {
    17632079                return $this->getid3->warning($text);
    17642080        }
    17652081
     2082        /**
     2083         * @param string $text
     2084         */
    17662085        protected function notice($text) {
    17672086                // does nothing for now
    17682087        }
    17692088
     2089        /**
     2090         * @param string $name
     2091         * @param int    $offset
     2092         * @param int    $length
     2093         * @param string $image_mime
     2094         *
     2095         * @return string|null
     2096         *
     2097         * @throws Exception
     2098         * @throws getid3_exception
     2099         */
    17702100        public function saveAttachment($name, $offset, $length, $image_mime=null) {
    17712101                try {
     
    18212151                        if (isset($fp_dest) && is_resource($fp_dest)) {
    18222152                                fclose($fp_dest);
     2153                        }
     2154
     2155                        if (isset($dest) && file_exists($dest)) {
    18232156                                unlink($dest);
    18242157                        }
  • trunk/src/wp-includes/ID3/license.txt

    r32979 r46112  
    22/// getID3() by James Heinrich <info@getid3.org>               //
    33//  available at http://getid3.sourceforge.net                 //
    4 //            or http://www.getid3.org                         //
     4//            or https://www.getid3.org                        //
    55//          also https://github.com/JamesHeinrich/getID3       //
    66/////////////////////////////////////////////////////////////////
     
    1919GNU LGPL: https://gnu.org/licenses/lgpl.html                 (v3)
    2020
    21 Mozilla MPL: http://www.mozilla.org/MPL/2.0/                 (v2)
     21Mozilla MPL: https://www.mozilla.org/MPL/2.0/                (v2)
    2222
    23 getID3 Commercial License: http://getid3.org/#gCL (payment required)
     23getID3 Commercial License: https://www.getid3.org/#gCL (payment required)
    2424
    2525*****************************************************************
  • trunk/src/wp-includes/ID3/module.audio-video.asf.php

    r41196 r46112  
    22/////////////////////////////////////////////////////////////////
    33/// 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                             //
     4//  available at https://github.com/JamesHeinrich/getID3       //
     5//            or https://www.getid3.org                        //
     6//            or http://getid3.sourceforge.net                 //
     7//  see readme.txt for more details                            //
    98/////////////////////////////////////////////////////////////////
    109//                                                             //
     
    1716getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
    1817
    19 class getid3_asf extends getid3_handler {
    20 
     18class getid3_asf extends getid3_handler
     19{
     20        /**
     21         * @param getID3 $getid3
     22         */
    2123        public function __construct(getID3 $getid3) {
    2224                parent::__construct($getid3);  // extends getid3_handler::__construct()
     
    3133        }
    3234
     35        /**
     36         * @return bool
     37         */
    3338        public function Analyze() {
    3439                $info = &$this->getid3->info;
     
    8489                $ASFHeaderData = $this->fread($thisfile_asf_headerobject['objectsize'] - 30);
    8590                $offset = 0;
     91                $thisfile_asf_streambitratepropertiesobject = array();
     92                $thisfile_asf_codeclistobject = array();
    8693
    8794                for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) {
     
    791798                                                        case 'tracknumber':
    792799                                                                // be careful casting to int: casting unicode strings to int gives unexpected results (stops parsing at first non-numeric character)
    793                                                                 $thisfile_asf_comments['track'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
    794                                                                 foreach ($thisfile_asf_comments['track'] as $key => $value) {
     800                                                                $thisfile_asf_comments['track_number'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
     801                                                                foreach ($thisfile_asf_comments['track_number'] as $key => $value) {
    795802                                                                        if (preg_match('/^[0-9\x00]+$/', $value)) {
    796                                                                                 $thisfile_asf_comments['track'][$key] = intval(str_replace("\x00", '', $value));
     803                                                                                $thisfile_asf_comments['track_number'][$key] = intval(str_replace("\x00", '', $value));
    797804                                                                        }
    798805                                                                }
     
    800807
    801808                                                        case 'wm/track':
    802                                                                 if (empty($thisfile_asf_comments['track'])) {
    803                                                                         $thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
     809                                                                if (empty($thisfile_asf_comments['track_number'])) {
     810                                                                        $thisfile_asf_comments['track_number'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
    804811                                                                }
    805812                                                                break;
     
    971978                        }
    972979                }
    973                 if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) {
     980                if (isset($thisfile_asf_streambitratepropertiesobject['bitrate_records_count'])) {
    974981                        $ASFbitrateAudio = 0;
    975982                        $ASFbitrateVideo = 0;
    976                         for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) {
     983                        for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) {
    977984                                if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) {
    978985                                        switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) {
    979986                                                case 1:
    980                                                         $ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
     987                                                        $ASFbitrateVideo += $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
    981988                                                        break;
    982989
    983990                                                case 2:
    984                                                         $ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
     991                                                        $ASFbitrateAudio += $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
    985992                                                        break;
    986993
     
    14411448        }
    14421449
     1450        /**
     1451         * @param int $CodecListType
     1452         *
     1453         * @return string
     1454         */
    14431455        public static function codecListObjectTypeLookup($CodecListType) {
    14441456                static $lookup = array(
     
    14511463        }
    14521464
     1465        /**
     1466         * @return array
     1467         */
    14531468        public static function KnownGUIDs() {
    14541469                static $GUIDarray = array(
     
    15651580        }
    15661581
     1582        /**
     1583         * @param string $GUIDstring
     1584         *
     1585         * @return string|false
     1586         */
    15671587        public static function GUIDname($GUIDstring) {
    15681588                static $GUIDarray = array();
     
    15731593        }
    15741594
     1595        /**
     1596         * @param int $id
     1597         *
     1598         * @return string
     1599         */
    15751600        public static function ASFIndexObjectIndexTypeLookup($id) {
    15761601                static $ASFIndexObjectIndexTypeLookup = array();
     
    15831608        }
    15841609
     1610        /**
     1611         * @param string $GUIDstring
     1612         *
     1613         * @return string
     1614         */
    15851615        public static function GUIDtoBytestring($GUIDstring) {
    15861616                // Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way:
     
    16181648        }
    16191649
     1650        /**
     1651         * @param string $Bytestring
     1652         *
     1653         * @return string
     1654         */
    16201655        public static function BytestringToGUID($Bytestring) {
    1621                 $GUIDstring  = str_pad(dechex(ord($Bytestring{3})),  2, '0', STR_PAD_LEFT);
    1622                 $GUIDstring .= str_pad(dechex(ord($Bytestring{2})),  2, '0', STR_PAD_LEFT);
    1623                 $GUIDstring .= str_pad(dechex(ord($Bytestring{1})),  2, '0', STR_PAD_LEFT);
    1624                 $GUIDstring .= str_pad(dechex(ord($Bytestring{0})),  2, '0', STR_PAD_LEFT);
     1656                $GUIDstring  = str_pad(dechex(ord($Bytestring[3])),  2, '0', STR_PAD_LEFT);
     1657                $GUIDstring .= str_pad(dechex(ord($Bytestring[2])),  2, '0', STR_PAD_LEFT);
     1658                $GUIDstring .= str_pad(dechex(ord($Bytestring[1])),  2, '0', STR_PAD_LEFT);
     1659                $GUIDstring .= str_pad(dechex(ord($Bytestring[0])),  2, '0', STR_PAD_LEFT);
    16251660                $GUIDstring .= '-';
    1626                 $GUIDstring .= str_pad(dechex(ord($Bytestring{5})),  2, '0', STR_PAD_LEFT);
    1627                 $GUIDstring .= str_pad(dechex(ord($Bytestring{4})),  2, '0', STR_PAD_LEFT);
     1661                $GUIDstring .= str_pad(dechex(ord($Bytestring[5])),  2, '0', STR_PAD_LEFT);
     1662                $GUIDstring .= str_pad(dechex(ord($Bytestring[4])),  2, '0', STR_PAD_LEFT);
    16281663                $GUIDstring .= '-';
    1629                 $GUIDstring .= str_pad(dechex(ord($Bytestring{7})),  2, '0', STR_PAD_LEFT);
    1630                 $GUIDstring .= str_pad(dechex(ord($Bytestring{6})),  2, '0', STR_PAD_LEFT);
     1664                $GUIDstring .= str_pad(dechex(ord($Bytestring[7])),  2, '0', STR_PAD_LEFT);
     1665                $GUIDstring .= str_pad(dechex(ord($Bytestring[6])),  2, '0', STR_PAD_LEFT);
    16311666                $GUIDstring .= '-';
    1632                 $GUIDstring .= str_pad(dechex(ord($Bytestring{8})),  2, '0', STR_PAD_LEFT);
    1633                 $GUIDstring .= str_pad(dechex(ord($Bytestring{9})),  2, '0', STR_PAD_LEFT);
     1667                $GUIDstring .= str_pad(dechex(ord($Bytestring[8])),  2, '0', STR_PAD_LEFT);
     1668                $GUIDstring .= str_pad(dechex(ord($Bytestring[9])),  2, '0', STR_PAD_LEFT);
    16341669                $GUIDstring .= '-';
    1635                 $GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT);
    1636                 $GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT);
    1637                 $GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT);
    1638                 $GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT);
    1639                 $GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT);
    1640                 $GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT);
     1670                $GUIDstring .= str_pad(dechex(ord($Bytestring[10])), 2, '0', STR_PAD_LEFT);
     1671                $GUIDstring .= str_pad(dechex(ord($Bytestring[11])), 2, '0', STR_PAD_LEFT);
     1672                $GUIDstring .= str_pad(dechex(ord($Bytestring[12])), 2, '0', STR_PAD_LEFT);
     1673                $GUIDstring .= str_pad(dechex(ord($Bytestring[13])), 2, '0', STR_PAD_LEFT);
     1674                $GUIDstring .= str_pad(dechex(ord($Bytestring[14])), 2, '0', STR_PAD_LEFT);
     1675                $GUIDstring .= str_pad(dechex(ord($Bytestring[15])), 2, '0', STR_PAD_LEFT);
    16411676
    16421677                return strtoupper($GUIDstring);
    16431678        }
    16441679
     1680        /**
     1681         * @param int  $FILETIME
     1682         * @param bool $round
     1683         *
     1684         * @return float|int
     1685         */
    16451686        public static function FILETIMEtoUNIXtime($FILETIME, $round=true) {
    16461687                // FILETIME is a 64-bit unsigned integer representing
     
    16541695        }
    16551696
     1697        /**
     1698         * @param int $WMpictureType
     1699         *
     1700         * @return string
     1701         */
    16561702        public static function WMpictureTypeLookup($WMpictureType) {
    16571703                static $lookup = null;
     
    16851731        }
    16861732
     1733        /**
     1734         * @param string $asf_header_extension_object_data
     1735         * @param int    $unhandled_sections
     1736         *
     1737         * @return array
     1738         */
    16871739        public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) {
    16881740                // http://msdn.microsoft.com/en-us/library/bb643323.aspx
     
    19311983        }
    19321984
    1933 
     1985        /**
     1986         * @param int $id
     1987         *
     1988         * @return string
     1989         */
    19341990        public static function metadataLibraryObjectDataTypeLookup($id) {
    19351991                static $lookup = array(
     
    19452001        }
    19462002
     2003        /**
     2004         * @param string $data
     2005         *
     2006         * @return array
     2007         */
    19472008        public function ASF_WMpicture(&$data) {
    19482009                //typedef struct _WMPicture{
     
    19952056        }
    19962057
    1997 
    1998         // Remove terminator 00 00 and convert UTF-16LE to Latin-1
     2058        /**
     2059         * Remove terminator 00 00 and convert UTF-16LE to Latin-1.
     2060         *
     2061         * @param string $string
     2062         *
     2063         * @return string
     2064         */
    19992065        public static function TrimConvert($string) {
    20002066                return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', self::TrimTerm($string)), ' ');
    20012067        }
    20022068
    2003 
    2004         // Remove terminator 00 00
     2069        /**
     2070         * Remove terminator 00 00.
     2071         *
     2072         * @param string $string
     2073         *
     2074         * @return string
     2075         */
    20052076        public static function TrimTerm($string) {
    20062077                // remove terminator, only if present (it should be, but...)
  • trunk/src/wp-includes/ID3/module.audio-video.flv.php

    r41196 r46112  
    22/////////////////////////////////////////////////////////////////
    33/// 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       //
     4//  available at https://github.com/JamesHeinrich/getID3       //
     5//            or https://www.getid3.org                        //
     6//            or http://getid3.sourceforge.net                 //
     7//  see readme.txt for more details                            //
     8/////////////////////////////////////////////////////////////////
     9//                                                             //
     10// module.audio-video.flv.php                                  //
     11// module for analyzing Shockwave Flash Video files            //
     12// dependencies: NONE                                          //
     13//                                                             //
     14/////////////////////////////////////////////////////////////////
    715//                                                             //
    816//  FLV module by Seth Kaufman <sethØwhirl-i-gig*com>          //
    917//                                                             //
    1018//  * version 0.1 (26 June 2005)                               //
    11 //                                                             //
    1219//                                                             //
    1320//  * version 0.1.1 (15 July 2005)                             //
     
    4451//  improved AVCSequenceParameterSetReader::readData()         //
    4552//    by Xander Schouwerwou <schouwerwouØgmail*com>            //
    46 //                                                             //
    47 /////////////////////////////////////////////////////////////////
    48 //                                                             //
    49 // module.audio-video.flv.php                                  //
    50 // module for analyzing Shockwave Flash Video files            //
    51 // dependencies: NONE                                          //
    5253//                                                            ///
    5354/////////////////////////////////////////////////////////////////
     
    7475define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
    7576
    76 class getid3_flv extends getid3_handler {
    77 
     77class getid3_flv extends getid3_handler
     78{
    7879        const magic = 'FLV';
    7980
    80         public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
    81 
     81        /**
     82         * Break out of the loop if too many frames have been scanned; only scan this
     83         * many if meta frame does not contain useful duration.
     84         *
     85         * @var int
     86         */
     87        public $max_frames = 100000;
     88
     89        /**
     90         * @return bool
     91         */
    8292        public function Analyze() {
    8393                $info = &$this->getid3->info;
     
    333343        }
    334344
    335 
     345        /**
     346         * @param int $id
     347         *
     348         * @return string|false
     349         */
    336350        public static function audioFormatLookup($id) {
    337351                static $lookup = array(
     
    356370        }
    357371
     372        /**
     373         * @param int $id
     374         *
     375         * @return int|false
     376         */
    358377        public static function audioRateLookup($id) {
    359378                static $lookup = array(
     
    366385        }
    367386
     387        /**
     388         * @param int $id
     389         *
     390         * @return int|false
     391         */
    368392        public static function audioBitDepthLookup($id) {
    369393                static $lookup = array(
     
    374398        }
    375399
     400        /**
     401         * @param int $id
     402         *
     403         * @return string|false
     404         */
    376405        public static function videoCodecLookup($id) {
    377406                static $lookup = array(
     
    387416}
    388417
    389 class AMFStream {
     418class AMFStream
     419{
     420        /**
     421         * @var string
     422         */
    390423        public $bytes;
     424
     425        /**
     426         * @var int
     427         */
    391428        public $pos;
    392429
     430        /**
     431         * @param string $bytes
     432         */
    393433        public function __construct(&$bytes) {
    394434                $this->bytes =& $bytes;
     
    396436        }
    397437
    398         public function readByte() {
    399                 return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
    400         }
    401 
    402         public function readInt() {
     438        /**
     439         * @return int
     440         */
     441        public function readByte() { //  8-bit
     442                return ord(substr($this->bytes, $this->pos++, 1));
     443        }
     444
     445        /**
     446         * @return int
     447         */
     448        public function readInt() { // 16-bit
    403449                return ($this->readByte() << 8) + $this->readByte();
    404450        }
    405451
    406         public function readLong() {
     452        /**
     453         * @return int
     454         */
     455        public function readLong() { // 32-bit
    407456                return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
    408457        }
    409458
     459        /**
     460         * @return float|false
     461         */
    410462        public function readDouble() {
    411463                return getid3_lib::BigEndian2Float($this->read(8));
    412464        }
    413465
     466        /**
     467         * @return string
     468         */
    414469        public function readUTF() {
    415470                $length = $this->readInt();
     
    417472        }
    418473
     474        /**
     475         * @return string
     476         */
    419477        public function readLongUTF() {
    420478                $length = $this->readLong();
     
    422480        }
    423481
     482        /**
     483         * @param int $length
     484         *
     485         * @return string
     486         */
    424487        public function read($length) {
    425488                $val = substr($this->bytes, $this->pos, $length);
     
    428491        }
    429492
     493        /**
     494         * @return int
     495         */
    430496        public function peekByte() {
    431497                $pos = $this->pos;
     
    435501        }
    436502
     503        /**
     504         * @return int
     505         */
    437506        public function peekInt() {
    438507                $pos = $this->pos;
     
    442511        }
    443512
     513        /**
     514         * @return int
     515         */
    444516        public function peekLong() {
    445517                $pos = $this->pos;
     
    449521        }
    450522
     523        /**
     524         * @return float|false
     525         */
    451526        public function peekDouble() {
    452527                $pos = $this->pos;
     
    456531        }
    457532
     533        /**
     534         * @return string
     535         */
    458536        public function peekUTF() {
    459537                $pos = $this->pos;
     
    463541        }
    464542
     543        /**
     544         * @return string
     545         */
    465546        public function peekLongUTF() {
    466547                $pos = $this->pos;
     
    471552}
    472553
    473 class AMFReader {
     554class AMFReader
     555{
     556        /**
     557        * @var AMFStream
     558        */
    474559        public $stream;
    475560
    476         public function __construct(&$stream) {
    477                 $this->stream =& $stream;
    478         }
    479 
     561        /**
     562         * @param AMFStream $stream
     563         */
     564        public function __construct(AMFStream $stream) {
     565                $this->stream = $stream;
     566        }
     567
     568        /**
     569         * @return mixed
     570         */
    480571        public function readData() {
    481572                $value = null;
     
    548639        }
    549640
     641        /**
     642         * @return float|false
     643         */
    550644        public function readDouble() {
    551645                return $this->stream->readDouble();
    552646        }
    553647
     648        /**
     649         * @return bool
     650         */
    554651        public function readBoolean() {
    555652                return $this->stream->readByte() == 1;
    556653        }
    557654
     655        /**
     656         * @return string
     657         */
    558658        public function readString() {
    559659                return $this->stream->readUTF();
    560660        }
    561661
     662        /**
     663         * @return array
     664         */
    562665        public function readObject() {
    563666                // Get highest numerical index - ignored
     
    565668
    566669                $data = array();
     670                $key = null;
    567671
    568672                while ($key = $this->stream->readUTF()) {
     
    577681        }
    578682
     683        /**
     684         * @return array
     685         */
    579686        public function readMixedArray() {
    580687                // Get highest numerical index - ignored
     
    582689
    583690                $data = array();
     691                $key = null;
    584692
    585693                while ($key = $this->stream->readUTF()) {
    586694                        if (is_numeric($key)) {
    587                                 $key = (float) $key;
     695                                $key = (int) $key;
    588696                        }
    589697                        $data[$key] = $this->readData();
     
    598706        }
    599707
     708        /**
     709         * @return array
     710         */
    600711        public function readArray() {
    601712                $length = $this->stream->readLong();
     
    608719        }
    609720
     721        /**
     722         * @return float|false
     723         */
    610724        public function readDate() {
    611725                $timestamp = $this->stream->readDouble();
     
    614728        }
    615729
     730        /**
     731         * @return string
     732         */
    616733        public function readLongString() {
    617734                return $this->stream->readLongUTF();
    618735        }
    619736
     737        /**
     738         * @return string
     739         */
    620740        public function readXML() {
    621741                return $this->stream->readLongUTF();
    622742        }
    623743
     744        /**
     745         * @return array
     746         */
    624747        public function readTypedObject() {
    625748                $className = $this->stream->readUTF();
     
    628751}
    629752
    630 class AVCSequenceParameterSetReader {
     753class AVCSequenceParameterSetReader
     754{
     755        /**
     756         * @var string
     757         */
    631758        public $sps;
    632759        public $start = 0;
    633760        public $currentBytes = 0;
    634761        public $currentBits = 0;
     762
     763        /**
     764         * @var int
     765         */
    635766        public $width;
     767
     768        /**
     769         * @var int
     770         */
    636771        public $height;
    637772
     773        /**
     774         * @param string $sps
     775         */
    638776        public function __construct($sps) {
    639777                $this->sps = $sps;
     
    692830        }
    693831
     832        /**
     833         * @param int $bits
     834         */
    694835        public function skipBits($bits) {
    695836                $newBits = $this->currentBits + $bits;
     
    698839        }
    699840
     841        /**
     842         * @return int
     843         */
    700844        public function getBit() {
    701845                $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
     
    704848        }
    705849
     850        /**
     851         * @param int $bits
     852         *
     853         * @return int
     854         */
    706855        public function getBits($bits) {
    707856                $result = 0;
     
    712861        }
    713862
     863        /**
     864         * @return int
     865         */
    714866        public function expGolombUe() {
    715867                $significantBits = 0;
     
    727879        }
    728880
     881        /**
     882         * @return int
     883         */
    729884        public function expGolombSe() {
    730885                $result = $this->expGolombUe();
     
    736891        }
    737892
     893        /**
     894         * @return int
     895         */
    738896        public function getWidth() {
    739897                return $this->width;
    740898        }
    741899
     900        /**
     901         * @return int
     902         */
    742903        public function getHeight() {
    743904                return $this->height;
  • trunk/src/wp-includes/ID3/module.audio-video.matroska.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//                                                             //
     
    7373define('EBML_ID_FILEUID',                       0x06AE); //         [46][AE] -- Unique ID representing the file, as random as possible.
    7474define('EBML_ID_CONTENTENCALGO',                0x07E1); //         [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
    75 define('EBML_ID_CONTENTENCKEYID',               0x07E2); //         [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with.
     75define('EBML_ID_CONTENTENCKEYID',               0x07E2); //         [47][E2] -- For public key algorithms this is the ID of the public key the data was encrypted with.
    7676define('EBML_ID_CONTENTSIGNATURE',              0x07E3); //         [47][E3] -- A cryptographic signature of the contents.
    7777define('EBML_ID_CONTENTSIGKEYID',               0x07E4); //         [47][E4] -- This is the ID of the private key the data was signed with.
     
    216216class getid3_matroska extends getid3_handler
    217217{
    218         // public options
    219         public static $hide_clusters    = true;  // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE]
    220         public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE]
    221 
    222         // private parser settings/placeholders
     218        /**
     219         * If true, do not return information about CLUSTER chunks, since there's a lot of them
     220         * and they're not usually useful [default: TRUE].
     221         *
     222         * @var bool
     223         */
     224        public static $hide_clusters    = true;
     225
     226        /**
     227         * True to parse the whole file, not only header [default: FALSE].
     228         *
     229         * @var bool
     230         */
     231        public static $parse_whole_file = false;
     232
     233        /*
     234         * Private parser settings/placeholders.
     235         */
    223236        private $EBMLbuffer        = '';
    224237        private $EBMLbuffer_offset = 0;
     
    227240        private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
    228241
     242        /**
     243         * @return bool
     244         */
    229245        public function Analyze()
    230246        {
     
    367383                                                                        $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
    368384                                                                        if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
    369                                                                                 foreach ($getid3_temp->info['audio'] as $key => $value) {
    370                                                                                         $track_info[$key] = $value;
     385                                                                                foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
     386                                                                                        $track_info[$sub_key] = $value;
    371387                                                                                }
    372388                                                                        }
     
    422438                                                                        $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
    423439                                                                        if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
    424                                                                                 foreach ($getid3_temp->info['audio'] as $key => $value) {
    425                                                                                         $track_info[$key] = $value;
     440                                                                                foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
     441                                                                                        $track_info[$sub_key] = $value;
    426442                                                                                }
    427443                                                                        }
     
    450466
    451467                                                                $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
    452                                                                 foreach ($parsed as $key => $value) {
    453                                                                         if ($key != 'raw') {
    454                                                                                 $track_info[$key] = $value;
     468                                                                foreach ($parsed as $sub_key => $value) {
     469                                                                        if ($sub_key != 'raw') {
     470                                                                                $track_info[$sub_key] = $value;
    455471                                                                        }
    456472                                                                }
     
    497513        }
    498514
     515        /**
     516         * @param array $info
     517         */
    499518        private function parseEBML(&$info) {
    500519                // http://www.matroska.org/technical/specs/index.html#EBMLBasics
     
    12291248        }
    12301249
     1250        /**
     1251         * @param int $min_data
     1252         *
     1253         * @return bool
     1254         */
    12311255        private function EnsureBufferHasEnoughData($min_data=1024) {
    12321256                if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
     
    12501274        }
    12511275
     1276        /**
     1277         * @return int|float|false
     1278         */
    12521279        private function readEBMLint() {
    12531280                $actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
     
    12821309        }
    12831310
     1311        /**
     1312         * @param int  $length
     1313         * @param bool $check_buffer
     1314         *
     1315         * @return string|false
     1316         */
    12841317        private function readEBMLelementData($length, $check_buffer=false) {
    12851318                if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
     
    12911324        }
    12921325
     1326        /**
     1327         * @param array      $element
     1328         * @param int        $parent_end
     1329         * @param array|bool $get_data
     1330         *
     1331         * @return bool
     1332         */
    12931333        private function getEBMLelement(&$element, $parent_end, $get_data=false) {
    12941334                if ($this->current_offset >= $parent_end) {
     
    13271367        }
    13281368
     1369        /**
     1370         * @param string $type
     1371         * @param int    $line
     1372         * @param array  $element
     1373         */
    13291374        private function unhandledElement($type, $line, $element) {
    13301375                // warn only about unknown and missed elements, not about unuseful
     
    13391384        }
    13401385
     1386        /**
     1387         * @param array $SimpleTagArray
     1388         *
     1389         * @return bool
     1390         */
    13411391        private function ExtractCommentsSimpleTag($SimpleTagArray) {
    13421392                if (!empty($SimpleTagArray['SimpleTag'])) {
     
    13541404        }
    13551405
     1406        /**
     1407         * @param int $parent_end
     1408         *
     1409         * @return array
     1410         */
    13561411        private function HandleEMBLSimpleTag($parent_end) {
    13571412                $simpletag_entry = array();
     
    13841439        }
    13851440
     1441        /**
     1442         * @param array $element
     1443         * @param int   $block_type
     1444         * @param array $info
     1445         *
     1446         * @return array
     1447         */
    13861448        private function HandleEMBLClusterBlock($element, $block_type, &$info) {
    13871449                // http://www.matroska.org/technical/specs/index.html#block_structure
     
    14471509        }
    14481510
     1511        /**
     1512         * @param string $EBMLstring
     1513         *
     1514         * @return int|float|false
     1515         */
    14491516        private static function EBML2Int($EBMLstring) {
    14501517                // http://matroska.org/specs/
     
    14891556        }
    14901557
     1558        /**
     1559         * @param int $EBMLdatestamp
     1560         *
     1561         * @return float
     1562         */
    14911563        private static function EBMLdate2unix($EBMLdatestamp) {
    14921564                // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
     
    14951567        }
    14961568
     1569        /**
     1570         * @param int $target_type
     1571         *
     1572         * @return string|int
     1573         */
    14971574        public static function TargetTypeValue($target_type) {
    14981575                // http://www.matroska.org/technical/specs/tagging/index.html
     
    15101587        }
    15111588
     1589        /**
     1590         * @param int $lacingtype
     1591         *
     1592         * @return string|int
     1593         */
    15121594        public static function BlockLacingType($lacingtype) {
    15131595                // http://matroska.org/technical/specs/index.html#block_structure
     
    15221604        }
    15231605
     1606        /**
     1607         * @param string $codecid
     1608         *
     1609         * @return string
     1610         */
    15241611        public static function CodecIDtoCommonName($codecid) {
    15251612                // http://www.matroska.org/technical/specs/codecid/index.html
     
    15581645        }
    15591646
     1647        /**
     1648         * @param int $value
     1649         *
     1650         * @return string
     1651         */
    15601652        private static function EBMLidName($value) {
    15611653                static $EBMLidList = array();
     
    17561848        }
    17571849
     1850        /**
     1851         * @param int $value
     1852         *
     1853         * @return string
     1854         */
    17581855        public static function displayUnit($value) {
    17591856                // http://www.matroska.org/technical/specs/index.html#DisplayUnit
     
    17671864        }
    17681865
     1866        /**
     1867         * @param array $streams
     1868         *
     1869         * @return array
     1870         */
    17691871        private static function getDefaultStreamInfo($streams)
    17701872        {
     1873                $stream = array();
    17711874                foreach (array_reverse($streams) as $stream) {
    17721875                        if ($stream['default']) {
  • 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                                        }
  • trunk/src/wp-includes/ID3/module.audio-video.riff.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//                                                             //
     
    2828getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
    2929
    30 class getid3_riff extends getid3_handler {
    31 
     30class getid3_riff extends getid3_handler
     31{
    3232        protected $container = 'riff'; // default
    3333
     34        /**
     35         * @return bool
     36         *
     37         * @throws getid3_exception
     38         */
    3439        public function Analyze() {
    3540                $info = &$this->getid3->info;
     
    4752                $thisfile_riff_audio       = &$thisfile_riff['audio'];
    4853                $thisfile_riff_video       = &$thisfile_riff['video'];
     54                $thisfile_riff_WAVE        = array();
    4955
    5056                $Original['avdataoffset'] = $info['avdataoffset'];
     
    358364                                        $thisfile_riff_WAVE_cart_0['url']              =                 trim(substr($thisfile_riff_WAVE_cart_0['data'],  748, 1024));
    359365                                        $thisfile_riff_WAVE_cart_0['tag_text']         = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
     366                                        $thisfile_riff['comments']['tag_text'][]       =                      substr($thisfile_riff_WAVE_cart_0['data'], 1772);
    360367
    361368                                        $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
     
    406413                                                'category'  =>'genre',
    407414                                                'cdtitle'   =>'album',
    408                                                 'tracktitle'=>'title',
    409415                                        );
    410416                                        foreach ($tagmapping as $fromkey => $tokey) {
     
    614620                                $thisfile_video['dataformat']   = 'avi';
    615621
     622                                $thisfile_riff_video_current = array();
     623
    616624                                if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
    617625                                        $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
     
    696704                                                'copyrighted'  => 0x00020010,
    697705                                        );
    698                     foreach ($flags as $flag => $value) {
     706                                        foreach ($flags as $flag => $value) {
    699707                                                $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
    700708                                        }
     
    702710                                        // shortcut
    703711                                        $thisfile_riff_video[$streamindex] = array();
     712                                        /** @var array $thisfile_riff_video_current */
    704713                                        $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
    705714
     
    868877                                                        }
    869878
    870                                                         if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
     879                                                        if (isset($thisfile_riff_raw_strf_strhfccType_streamindex) && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
    871880
    872881                                                                $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
     
    915924                        case 'CDDA':
    916925                                $info['fileformat'] = 'cda';
    917                             unset($info['mime_type']);
     926                                unset($info['mime_type']);
    918927
    919928                                $thisfile_audio_dataformat      = 'cda';
     
    935944                                        $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
    936945                                        $thisfile_riff_CDDA_fmt_0['playtime_seconds']     = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
    937                                         $info['comments']['track']                = $thisfile_riff_CDDA_fmt_0['track_num'];
     946                                        $info['comments']['track_number']         = $thisfile_riff_CDDA_fmt_0['track_num'];
    938947                                        $info['playtime_seconds']                 = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
    939948
     
    948957                                break;
    949958
    950             // http://en.wikipedia.org/wiki/AIFF
     959                        // http://en.wikipedia.org/wiki/AIFF
    951960                        case 'AIFF':
    952961                        case 'AIFC':
     
    10581067                                        getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
    10591068                                        $getid3_temp = new getID3();
    1060                                         $getid3_temp->openfile($this->getid3->filename);
     1069                                        $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    10611070                                        $getid3_id3v2 = new getid3_id3v2($getid3_temp);
    10621071                                        $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
     
    10781087                                $thisfile_audio['bits_per_sample'] = 8;
    10791088                                $thisfile_audio['channels']        = 1; // overridden below, if need be
     1089                                $ActualBitsPerSample               = 0;
    10801090
    10811091                                if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
     
    11151125
    11161126                                                default:
    1117                                                         $this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"');
     1127                                                        $this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.$thisfile_riff_RIFFsubtype_VHDR_0['sCompression'].'"');
    11181128                                                        break;
    11191129                                        }
     
    11601170
    11611171                                        $getid3_temp = new getID3();
    1162                                         $getid3_temp->openfile($this->getid3->filename);
     1172                                        $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    11631173                                        $getid3_mpeg = new getid3_mpeg($getid3_temp);
    11641174                                        $getid3_mpeg->Analyze();
     
    12461256
    12471257                                        $getid3_temp = new getID3();
    1248                                         $getid3_temp->openfile($this->getid3->filename);
     1258                                        $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    12491259                                        $getid3_id3v2 = new getid3_id3v2($getid3_temp);
    12501260                                        $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
     
    13731383        }
    13741384
     1385        /**
     1386         * @param int $startoffset
     1387         * @param int $maxoffset
     1388         *
     1389         * @return array|false
     1390         *
     1391         * @throws Exception
     1392         * @throws getid3_exception
     1393         */
    13751394        public function ParseRIFFAMV($startoffset, $maxoffset) {
    13761395                // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
     
    14811500        }
    14821501
    1483 
     1502        /**
     1503         * @param int $startoffset
     1504         * @param int $maxoffset
     1505         *
     1506         * @return array|false
     1507         * @throws getid3_exception
     1508         */
    14841509        public function ParseRIFF($startoffset, $maxoffset) {
    14851510                $info = &$this->getid3->info;
     
    15301555                                                                                if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
    15311556                                                                                        $getid3_temp = new getID3();
    1532                                                                                         $getid3_temp->openfile($this->getid3->filename);
     1557                                                                                        $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    15331558                                                                                        $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
    15341559                                                                                        $getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
     
    15521577                                                                                // AC3
    15531578                                                                                $getid3_temp = new getID3();
    1554                                                                                 $getid3_temp->openfile($this->getid3->filename);
     1579                                                                                $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    15551580                                                                                $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
    15561581                                                                                $getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
     
    16131638                                                                        if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
    16141639                                                                                $getid3_temp = new getID3();
    1615                                                                                 $getid3_temp->openfile($this->getid3->filename);
     1640                                                                                $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    16161641                                                                                $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
    16171642                                                                                $getid3_temp->info['avdataend']    = $info['avdataend'];
     
    16301655                                                                        $getid3_temp = new getID3();
    16311656                                                                        if ($isRegularAC3) {
    1632                                                                                 $getid3_temp->openfile($this->getid3->filename);
     1657                                                                                $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    16331658                                                                                $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
    16341659                                                                                $getid3_temp->info['avdataend']    = $info['avdataend'];
     
    16641689                                                                        // This is probably DTS data
    16651690                                                                        $getid3_temp = new getID3();
    1666                                                                         $getid3_temp->openfile($this->getid3->filename);
     1691                                                                        $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
    16671692                                                                        $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
    16681693                                                                        $getid3_dts = new getid3_dts($getid3_temp);
     
    17321757                                                        //      break;
    17331758
     1759                                                        case 'scot':
     1760                                                                // https://cmsdk.com/node-js/adding-scot-chunk-to-wav-file.html
     1761                                                                $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
     1762                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['alter']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],   0,   1);
     1763                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['attrib']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],   1,   1);
     1764                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['artnum']          = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],   2,   2));
     1765                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['title']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],   4,  43);  // "name" in other documentation
     1766                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['copy']            =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  47,   4);
     1767                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['padd']            =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  51,   1);
     1768                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['asclen']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  52,   5);
     1769                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['startseconds']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  57,   2));
     1770                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['starthundredths'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  59,   2));
     1771                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['endseconds']      = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  61,   2));
     1772                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['endhundreths']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  63,   2));
     1773                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['sdate']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  65,   6);
     1774                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['kdate']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  71,   6);
     1775                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['start_hr']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  77,   1);
     1776                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['kill_hr']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  78,   1);
     1777                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['digital']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  79,   1);
     1778                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['sample_rate']     = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  80,   2));
     1779                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['stereo']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  82,   1);
     1780                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['compress']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  83,   1);
     1781                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['eomstrt']         = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  84,   4));
     1782                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['eomlen']          = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  88,   2));
     1783                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['attrib2']         = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'],  90,   4));
     1784                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['future1']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'],  94,  12);
     1785                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['catfontcolor']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 106,   4));
     1786                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['catcolor']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 110,   4));
     1787                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['segeompos']       = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 114,   4));
     1788                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['vt_startsecs']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 118,   2));
     1789                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['vt_starthunds']   = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 120,   2));
     1790                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['priorcat']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 122,   3);
     1791                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['priorcopy']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 125,   4);
     1792                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['priorpadd']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 129,   1);
     1793                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['postcat']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 130,   3);
     1794                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['postcopy']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 133,   4);
     1795                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['postpadd']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 137,   1);
     1796                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['hrcanplay']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 138,  21);
     1797                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['future2']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 159, 108);
     1798                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['artist']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 267,  34);
     1799                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['comment']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 301,  34); // "trivia" in other documentation
     1800                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['intro']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 335,   2);
     1801                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['end']             =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 337,   1);
     1802                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['year']            =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 338,   4);
     1803                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['obsolete2']       =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 342,   1);
     1804                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['rec_hr']          =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 343,   1);
     1805                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['rdate']           =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 344,   6);
     1806                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['mpeg_bitrate']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 350,   2));
     1807                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['pitch']           = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 352,   2));
     1808                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['playlevel']       = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 354,   2));
     1809                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['lenvalid']        =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 356,   1);
     1810                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['filelength']      = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 357,   4));
     1811                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['newplaylevel']    = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 361,   2));
     1812                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['chopsize']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 363,   4));
     1813                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['vteomovr']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 367,   4));
     1814                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['desiredlen']      = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 371,   4));
     1815                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['triggers']        = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 375,   4));
     1816                                                                $RIFFchunk[$chunkname][$thisindex]['parsed']['fillout']         =                              substr($RIFFchunk[$chunkname][$thisindex]['data'], 379,   33);
     1817
     1818                                                                foreach (array('title', 'artist', 'comment') as $key) {
     1819                                                                        if (trim($RIFFchunk[$chunkname][$thisindex]['parsed'][$key])) {
     1820                                                                                $info['riff']['comments'][$key] = array($RIFFchunk[$chunkname][$thisindex]['parsed'][$key]);
     1821                                                                        }
     1822                                                                }
     1823                                                                if ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] && !empty($info['filesize']) && ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] != $info['filesize'])) {
     1824                                                                        $this->warning('RIFF.WAVE.scot.filelength ('.$RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'].') different from actual filesize ('.$info['filesize'].')');
     1825                                                                }
     1826                                                                break;
     1827
    17341828                                                        default:
    1735                                                                 if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
     1829                                                                if (!empty($LISTchunkParent) && isset($LISTchunkMaxOffset) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
    17361830                                                                        $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
    17371831                                                                        $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size']   = $RIFFchunk[$chunkname][$thisindex]['size'];
     
    17681862        }
    17691863
     1864        /**
     1865         * @param string $RIFFdata
     1866         *
     1867         * @return bool
     1868         */
    17701869        public function ParseRIFFdata(&$RIFFdata) {
    17711870                $info = &$this->getid3->info;
     
    18051904        }
    18061905
     1906        /**
     1907         * @param array $RIFFinfoArray
     1908         * @param array $CommentsTargetArray
     1909         *
     1910         * @return bool
     1911         */
    18071912        public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
    18081913                $RIFFinfoKeyLookup = array(
     
    18641969        }
    18651970
     1971        /**
     1972         * @param string $WaveFormatExData
     1973         *
     1974         * @return array
     1975         */
    18661976        public static function parseWAVEFORMATex($WaveFormatExData) {
    18671977                // shortcut
     1978                $WaveFormatEx        = array();
    18681979                $WaveFormatEx['raw'] = array();
    18691980                $WaveFormatEx_raw    = &$WaveFormatEx['raw'];
     
    18892000        }
    18902001
     2002        /**
     2003         * @param string $WavPackChunkData
     2004         *
     2005         * @return bool
     2006         */
    18912007        public function parseWavPackHeader($WavPackChunkData) {
    18922008                // typedef struct {
     
    19502066        }
    19512067
     2068        /**
     2069         * @param string $BITMAPINFOHEADER
     2070         * @param bool   $littleEndian
     2071         *
     2072         * @return array
     2073         */
    19522074        public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
    19532075
     
    19692091        }
    19702092
     2093        /**
     2094         * @param string $DIVXTAG
     2095         * @param bool   $raw
     2096         *
     2097         * @return array
     2098         */
    19712099        public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
    19722100                // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
     
    20152143                );
    20162144
     2145                $parsed              = array();
    20172146                $parsed['title']     =        trim(substr($DIVXTAG,   0, 32));
    20182147                $parsed['artist']    =        trim(substr($DIVXTAG,  32, 28));
     
    20302159                        unset($parsed['genre_id'], $parsed['rating_id']);
    20312160                        foreach ($parsed as $key => $value) {
    2032                                 if (!$value === '') {
    2033                                         unset($parsed['key']);
     2161                                if (empty($value)) {
     2162                                        unset($parsed[$key]);
    20342163                                }
    20352164                        }
     
    20432172        }
    20442173
     2174        /**
     2175         * @param string $tagshortname
     2176         *
     2177         * @return string
     2178         */
    20452179        public static function waveSNDMtagLookup($tagshortname) {
    20462180                $begin = __LINE__;
     
    20662200        }
    20672201
     2202        /**
     2203         * @param int $wFormatTag
     2204         *
     2205         * @return string
     2206         */
    20682207        public static function wFormatTagLookup($wFormatTag) {
    20692208
     
    22352374        }
    22362375
     2376        /**
     2377         * @param string $fourcc
     2378         *
     2379         * @return string
     2380         */
    22372381        public static function fourccLookup($fourcc) {
    22382382
     
    26292773        }
    26302774
     2775        /**
     2776         * @param string $byteword
     2777         * @param bool   $signed
     2778         *
     2779         * @return int|float|false
     2780         */
    26312781        private function EitherEndian2Int($byteword, $signed=false) {
    26322782                if ($this->container == 'riff') {
  • trunk/src/wp-includes/ID3/module.audio.ac3.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//                                                             //
     
    1818class getid3_ac3 extends getid3_handler
    1919{
    20     private $AC3header = array();
    21     private $BSIoffset = 0;
    22 
    23     const syncword = 0x0B77;
    24 
     20        /**
     21         * @var array
     22         */
     23        private $AC3header = array();
     24
     25        /**
     26         * @var int
     27         */
     28        private $BSIoffset = 0;
     29
     30        const syncword = 0x0B77;
     31
     32        /**
     33         * @return bool
     34         */
    2535        public function Analyze() {
    2636                $info = &$this->getid3->info;
     
    188198
    189199
    190 $this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.');
     200                        $this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.');
    191201                        $info['audio']['dataformat'] = 'eac3';
    192202
     
    413423
    414424                        $this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 16. Please submit a support ticket with a sample file.');
    415                     unset($info['ac3']);
     425                        unset($info['ac3']);
    416426                        return false;
    417427
     
    432442                        $thisfile_ac3['bitrate']      = self::bitrateLookup($thisfile_ac3_raw_bsi['frmsizecod']);
    433443                } elseif (!empty($thisfile_ac3_raw_bsi['frmsiz'])) {
    434 // this isn't right, but it's (usually) close, roughly 5% less than it should be.
    435 // but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know!
     444                        // this isn't right, but it's (usually) close, roughly 5% less than it should be.
     445                        // but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know!
    436446                        $thisfile_ac3['bitrate']      = ($thisfile_ac3_raw_bsi['frmsiz'] + 1) * 16 * 30; // The frmsiz field shall contain a value one less than the overall size of the coded syncframe in 16-bit words. That is, this field may assume a value ranging from 0 to 2047, and these values correspond to syncframe sizes ranging from 1 to 2048.
    437 // kludge-fix to make it approximately the expected value, still not "right":
    438 $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000;
     447                        // kludge-fix to make it approximately the expected value, still not "right":
     448                        $thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000;
    439449                }
    440450                $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
    441451
    442                 $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
     452                if (isset($thisfile_ac3_raw_bsi['bsmod']) && isset($thisfile_ac3_raw_bsi['acmod'])) {
     453                        $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
     454                }
    443455                $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
    444456                foreach($ac3_coding_mode as $key => $value) {
     
    471483        }
    472484
     485        /**
     486         * @param int $length
     487         *
     488         * @return float|int
     489         */
    473490        private function readHeaderBSI($length) {
    474491                $data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
     
    478495        }
    479496
     497        /**
     498         * @param int $fscod
     499         *
     500         * @return int|string|false
     501         */
    480502        public static function sampleRateCodeLookup($fscod) {
    481503                static $sampleRateCodeLookup = array(
     
    488510        }
    489511
     512        /**
     513         * @param int $fscod2
     514         *
     515         * @return int|string|false
     516         */
    490517        public static function sampleRateCodeLookup2($fscod2) {
    491518                static $sampleRateCodeLookup2 = array(
     
    498525        }
    499526
     527        /**
     528         * @param int $bsmod
     529         * @param int $acmod
     530         *
     531         * @return string|false
     532         */
    500533        public static function serviceTypeLookup($bsmod, $acmod) {
    501534                static $serviceTypeLookup = array();
     
    519552        }
    520553
     554        /**
     555         * @param int $acmod
     556         *
     557         * @return array|false
     558         */
    521559        public static function audioCodingModeLookup($acmod) {
    522560                // array(channel configuration, # channels (not incl LFE), channel order)
     
    534572        }
    535573
     574        /**
     575         * @param int $cmixlev
     576         *
     577         * @return int|float|string|false
     578         */
    536579        public static function centerMixLevelLookup($cmixlev) {
    537580                static $centerMixLevelLookup;
     
    547590        }
    548591
     592        /**
     593         * @param int $surmixlev
     594         *
     595         * @return int|float|string|false
     596         */
    549597        public static function surroundMixLevelLookup($surmixlev) {
    550598                static $surroundMixLevelLookup;
     
    560608        }
    561609
     610        /**
     611         * @param int $dsurmod
     612         *
     613         * @return string|false
     614         */
    562615        public static function dolbySurroundModeLookup($dsurmod) {
    563616                static $dolbySurroundModeLookup = array(
     
    570623        }
    571624
     625        /**
     626         * @param int  $acmod
     627         * @param bool $lfeon
     628         *
     629         * @return array
     630         */
    572631        public static function channelsEnabledLookup($acmod, $lfeon) {
    573632                $lookup = array(
    574                         'ch1'=>(bool) ($acmod == 0),
    575                         'ch2'=>(bool) ($acmod == 0),
    576                         'left'=>(bool) ($acmod > 1),
    577                         'right'=>(bool) ($acmod > 1),
     633                        'ch1'=>($acmod == 0),
     634                        'ch2'=>($acmod == 0),
     635                        'left'=>($acmod > 1),
     636                        'right'=>($acmod > 1),
    578637                        'center'=>(bool) ($acmod & 0x01),
    579638                        'surround_mono'=>false,
     
    595654        }
    596655
     656        /**
     657         * @param int $compre
     658         *
     659         * @return float|int
     660         */
    597661        public static function heavyCompression($compre) {
    598662                // The first four bits indicate gain changes in 6.02dB increments which can be
     
    624688
    625689                $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
    626                 if ($fourbit{0} == '1') {
     690                if ($fourbit[0] == '1') {
    627691                        $log_gain = -8 + bindec(substr($fourbit, 1));
    628692                } else {
     
    645709        }
    646710
     711        /**
     712         * @param int $roomtyp
     713         *
     714         * @return string|false
     715         */
    647716        public static function roomTypeLookup($roomtyp) {
    648717                static $roomTypeLookup = array(
     
    655724        }
    656725
     726        /**
     727         * @param int $frmsizecod
     728         * @param int $fscod
     729         *
     730         * @return int|false
     731         */
    657732        public static function frameSizeLookup($frmsizecod, $fscod) {
    658733                // LSB is whether padding is used or not
     
    684759                        );
    685760                }
     761                $paddingBytes = 0;
    686762                if (($fscod == 1) && $padding) {
    687763                        // frame lengths are padded by 1 word (16 bits) at 44100
    688                         $frameSizeLookup[$frmsizecod] += 2;
    689                 }
    690                 return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
    691         }
    692 
     764                        // (fscode==1) means 44100Hz (see sampleRateCodeLookup)
     765                        $paddingBytes = 2;
     766                }
     767                return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] + $paddingBytes : false);
     768        }
     769
     770        /**
     771         * @param int $frmsizecod
     772         *
     773         * @return int|false
     774         */
    693775        public static function bitrateLookup($frmsizecod) {
    694776                // LSB is whether padding is used or not
     
    720802        }
    721803
     804        /**
     805         * @param int $numblkscod
     806         *
     807         * @return int|false
     808         */
    722809        public static function blocksPerSyncFrame($numblkscod) {
    723810                static $blocksPerSyncFrameLookup = array(
  • trunk/src/wp-includes/ID3/module.audio.dts.php

    r32979 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//                                                             //
     
    2222{
    2323        /**
    24         * Default DTS syncword used in native .cpt or .dts formats
    25         */
    26     const syncword = "\x7F\xFE\x80\x01";
    27 
     24         * Default DTS syncword used in native .cpt or .dts formats.
     25         */
     26        const syncword = "\x7F\xFE\x80\x01";
     27
     28        /**
     29         * @var int
     30         */
    2831        private $readBinDataOffset = 0;
    2932
    30     /**
    31     * Possible syncwords indicating bitstream encoding
    32     */
    33     public static $syncwords = array(
    34         0 => "\x7F\xFE\x80\x01",  // raw big-endian
    35         1 => "\xFE\x7F\x01\x80",  // raw little-endian
    36         2 => "\x1F\xFF\xE8\x00",  // 14-bit big-endian
    37         3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
    38 
     33        /**
     34         * Possible syncwords indicating bitstream encoding.
     35         */
     36        public static $syncwords = array(
     37                0 => "\x7F\xFE\x80\x01",  // raw big-endian
     38                1 => "\xFE\x7F\x01\x80",  // raw little-endian
     39                2 => "\x1F\xFF\xE8\x00",  // 14-bit big-endian
     40                3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
     41
     42        /**
     43         * @return bool
     44         */
    3945        public function Analyze() {
    4046                $info = &$this->getid3->info;
     
    4652                // check syncword
    4753                $sync = substr($DTSheader, 0, 4);
    48         if (($encoding = array_search($sync, self::$syncwords)) !== false) {
    49 
    50                 $info['dts']['raw']['magic'] = $sync;
     54                if (($encoding = array_search($sync, self::$syncwords)) !== false) {
     55
     56                        $info['dts']['raw']['magic'] = $sync;
    5157                        $this->readBinDataOffset = 32;
    5258
    53         } elseif ($this->isDependencyFor('matroska')) {
     59                } elseif ($this->isDependencyFor('matroska')) {
    5460
    5561                        // Matroska contains DTS without syncword encoded as raw big-endian format
     
    5763                        $this->readBinDataOffset = 0;
    5864
    59         } else {
     65                } else {
    6066
    6167                        unset($info['fileformat']);
     
    140146        }
    141147
     148        /**
     149         * @param string $bin
     150         * @param int $length
     151         *
     152         * @return float|int
     153         */
    142154        private function readBinData($bin, $length) {
    143155                $data = substr($bin, $this->readBinDataOffset, $length);
     
    147159        }
    148160
     161        /**
     162         * @param int $index
     163         *
     164         * @return int|string|false
     165         */
    149166        public static function bitrateLookup($index) {
    150167                static $lookup = array(
     
    185202        }
    186203
     204        /**
     205         * @param int $index
     206         *
     207         * @return int|string|false
     208         */
    187209        public static function sampleRateLookup($index) {
    188210                static $lookup = array(
     
    207229        }
    208230
     231        /**
     232         * @param int $index
     233         *
     234         * @return int|false
     235         */
    209236        public static function bitPerSampleLookup($index) {
    210237                static $lookup = array(
     
    217244        }
    218245
     246        /**
     247         * @param int $index
     248         *
     249         * @return int|false
     250         */
    219251        public static function numChannelsLookup($index) {
    220252                switch ($index) {
     
    255287        }
    256288
     289        /**
     290         * @param int $index
     291         *
     292         * @return string
     293         */
    257294        public static function channelArrangementLookup($index) {
    258295                static $lookup = array(
     
    277314        }
    278315
     316        /**
     317         * @param int $index
     318         * @param int $version
     319         *
     320         * @return int|false
     321         */
    279322        public static function dialogNormalization($index, $version) {
    280323                switch ($version) {
  • trunk/src/wp-includes/ID3/module.audio.flac.php

    r32979 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        const syncword = 'fLaC';
    2626
     27        /**
     28         * @return bool
     29         */
    2730        public function Analyze() {
    2831                $info = &$this->getid3->info;
     
    4245        }
    4346
     47        /**
     48         * @return bool
     49         */
    4450        public function parseMETAdata() {
    4551                $info = &$this->getid3->info;
     
    4753                        $BlockOffset   = $this->ftell();
    4854                        $BlockHeader   = $this->fread(4);
    49                         $LBFBT         = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
     55                        $LBFBT         = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));  // LBFBT = LastBlockFlag + BlockType
    5056                        $LastBlockFlag = (bool) ($LBFBT & 0x80);
    5157                        $BlockType     =        ($LBFBT & 0x7F);
     
    5460
    5561                        if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
    56                                 $this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
     62                                $this->warning('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
    5763                                break;
    5864                        }
    5965                        if ($BlockLength < 1) {
     66                                if ($BlockTypeText != 'reserved') {
     67                                        // probably supposed to be zero-length
     68                                        $this->warning('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockTypeText.') at offset '.$BlockOffset.' is zero bytes');
     69                                        continue;
     70                                }
    6071                                $this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
    6172                                break;
     
    168179
    169180                        if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
    170                 $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
     181                                $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
    171182                        }
    172183                        else {
     
    195206        }
    196207
    197         private function parseSTREAMINFO($BlockData) {
    198                 $info = &$this->getid3->info;
    199 
    200                 $info['flac']['STREAMINFO'] = array();
    201                 $streaminfo = &$info['flac']['STREAMINFO'];
    202 
     208
     209        /**
     210         * @param string $BlockData
     211         *
     212         * @return array
     213         */
     214        public static function parseSTREAMINFOdata($BlockData) {
     215                $streaminfo = array();
    203216                $streaminfo['min_block_size']  = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
    204217                $streaminfo['max_block_size']  = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
     
    212225                $streaminfo['samples_stream']  = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
    213226
    214                 $streaminfo['audio_signature'] = substr($BlockData, 18, 16);
    215 
    216                 if (!empty($streaminfo['sample_rate'])) {
     227                $streaminfo['audio_signature'] =                           substr($BlockData, 18, 16);
     228
     229                return $streaminfo;
     230        }
     231
     232        /**
     233         * @param string $BlockData
     234         *
     235         * @return bool
     236         */
     237        private function parseSTREAMINFO($BlockData) {
     238                $info = &$this->getid3->info;
     239
     240                $info['flac']['STREAMINFO'] = self::parseSTREAMINFOdata($BlockData);
     241
     242                if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
    217243
    218244                        $info['audio']['bitrate_mode']    = 'vbr';
    219                         $info['audio']['sample_rate']     = $streaminfo['sample_rate'];
    220                         $info['audio']['channels']        = $streaminfo['channels'];
    221                         $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
    222                         $info['playtime_seconds']         = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
     245                        $info['audio']['sample_rate']     = $info['flac']['STREAMINFO']['sample_rate'];
     246                        $info['audio']['channels']        = $info['flac']['STREAMINFO']['channels'];
     247                        $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
     248                        $info['playtime_seconds']         = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
    223249                        if ($info['playtime_seconds'] > 0) {
    224250                                if (!$this->isDependencyFor('matroska')) {
     
    237263        }
    238264
     265        /**
     266         * @param string $BlockData
     267         *
     268         * @return bool
     269         */
    239270        private function parseAPPLICATION($BlockData) {
    240271                $info = &$this->getid3->info;
     
    247278        }
    248279
     280        /**
     281         * @param string $BlockData
     282         *
     283         * @return bool
     284         */
    249285        private function parseSEEKTABLE($BlockData) {
    250286                $info = &$this->getid3->info;
     
    276312        }
    277313
     314        /**
     315         * @param string $BlockData
     316         *
     317         * @return bool
     318         */
    278319        private function parseVORBIS_COMMENT($BlockData) {
    279320                $info = &$this->getid3->info;
     
    295336        }
    296337
     338        /**
     339         * @param string $BlockData
     340         *
     341         * @return bool
     342         */
    297343        private function parseCUESHEET($BlockData) {
    298344                $info = &$this->getid3->info;
     
    347393
    348394        /**
    349         * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
    350         * External usage: audio.ogg
    351         */
     395         * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
     396         * External usage: audio.ogg
     397         *
     398         * @return bool
     399         */
    352400        public function parsePICTURE() {
    353401                $info = &$this->getid3->info;
     
    381429        }
    382430
     431        /**
     432         * @param int $blocktype
     433         *
     434         * @return string
     435         */
    383436        public static function metaBlockTypeLookup($blocktype) {
    384437                static $lookup = array(
     
    394447        }
    395448
     449        /**
     450         * @param int $applicationid
     451         *
     452         * @return string
     453         */
    396454        public static function applicationIDLookup($applicationid) {
    397455                // http://flac.sourceforge.net/id.html
     
    424482        }
    425483
     484        /**
     485         * @param int $type_id
     486         *
     487         * @return string
     488         */
    426489        public static function pictureTypeLookup($type_id) {
    427490                static $lookup = array (
  • trunk/src/wp-includes/ID3/module.audio.mp3.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//                                                             //
     
    2525class getid3_mp3 extends getid3_handler
    2626{
    27 
    28         public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
    29 
     27        /**
     28         * Forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow,
     29         * unrecommended, but may provide data from otherwise-unusable files.
     30         *
     31         * @var bool
     32         */
     33        public $allow_bruteforce = false;
     34
     35        /**
     36         * @return bool
     37         */
    3038        public function Analyze() {
    3139                $info = &$this->getid3->info;
     
    3644                        if ($this->allow_bruteforce) {
    3745                                $this->error('Rescanning file in BruteForce mode');
    38                                 $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
     46                                $this->getOnlyMPEGaudioInfoBruteForce();
    3947                        }
    4048                }
     
    153161                // Calculate playtime
    154162                if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
    155                         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
     163                        // https://github.com/JamesHeinrich/getID3/issues/161
     164                        // VBR header frame contains ~0.026s of silent audio data, but is not actually part of the original encoding and should be ignored
     165                        $xingVBRheaderFrameLength = ((isset($info['mpeg']['audio']['VBR_frames']) && isset($info['mpeg']['audio']['framelength'])) ? $info['mpeg']['audio']['framelength'] : 0);
     166
     167                        $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset'] - $xingVBRheaderFrameLength) * 8 / $info['audio']['bitrate'];
    156168                }
    157169
     
    161173        }
    162174
    163 
     175        /**
     176         * @return string
     177         */
    164178        public function GuessEncoderOptions() {
    165179                // shortcuts
    166180                $info = &$this->getid3->info;
     181                $thisfile_mpeg_audio = array();
     182                $thisfile_mpeg_audio_lame = array();
    167183                if (!empty($info['mpeg']['audio'])) {
    168184                        $thisfile_mpeg_audio = &$info['mpeg']['audio'];
     
    179195                        $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
    180196
    181                 } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
     197                } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && isset($thisfile_mpeg_audio_lame['preset_used_id']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
    182198
    183199                        $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
     
    405421        }
    406422
    407 
     423        /**
     424         * @param int   $offset
     425         * @param array $info
     426         * @param bool  $recursivesearch
     427         * @param bool  $ScanAsCBR
     428         * @param bool  $FastMPEGheaderScan
     429         *
     430         * @return bool
     431         */
    408432        public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
    409433                static $MPEGaudioVersionLookup;
     
    459483                $thisfile_mpeg_audio = &$info['mpeg']['audio'];
    460484
    461 
    462485                if ($MPEGaudioHeaderValidCache[$head4_key]) {
    463486                        $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
     
    563586                        $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
    564587                        $thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
    565                         $info['audio']['codec']                = 'Fraunhofer';
     588                        $info['audio']['codec']              = 'Fraunhofer';
    566589
    567590                        $SideInfoData = substr($headerstring, 4 + 2, 32);
     
    656679                                        } elseif (!empty($info['filesize'])) {
    657680                                                $used_filesize  = $info['filesize'];
    658                                                 $used_filesize -= intval(@$info['id3v2']['headerlength']);
     681                                                $used_filesize -= (isset($info['id3v2']['headerlength']) ? intval($info['id3v2']['headerlength']) : 0);
    659682                                                $used_filesize -= (isset($info['id3v1']) ? 128 : 0);
    660683                                                $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
     
    679702                                        $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
    680703                                        for ($i = 0; $i < 100; $i++) {
    681                                                 $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
     704                                                $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData[$i]);
    682705                                        }
    683706                                }
     
    10841107        }
    10851108
     1109        /**
     1110         * @param int $offset
     1111         * @param int $nextframetestoffset
     1112         * @param bool $ScanAsCBR
     1113         *
     1114         * @return bool
     1115         */
    10861116        public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
    10871117                $info = &$this->getid3->info;
     
    11301160        }
    11311161
     1162        /**
     1163         * @param int  $offset
     1164         * @param bool $deepscan
     1165         *
     1166         * @return int|false
     1167         */
    11321168        public function FreeFormatFrameLength($offset, $deepscan=false) {
    11331169                $info = &$this->getid3->info;
     
    11381174                $SyncPattern1 = substr($MPEGaudioData, 0, 4);
    11391175                // may be different pattern due to padding
    1140                 $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
     1176                $SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3];
    11411177                if ($SyncPattern2 === $SyncPattern1) {
    1142                         $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
     1178                        $SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3];
    11431179                }
    11441180
     
    12071243        }
    12081244
     1245        /**
     1246         * @return bool
     1247         */
    12091248        public function getOnlyMPEGaudioInfoBruteForce() {
    12101249                $MPEGaudioHeaderDecodeCache   = array();
     
    12421281                                break;
    12431282                        }
    1244                         if ($head4{0} != "\xFF") {
     1283                        if ($head4[0] != "\xFF") {
    12451284                                for ($i = 1; $i < 4; $i++) {
    1246                                         if ($head4{$i} == "\xFF") {
     1285                                        if ($head4[$i] == "\xFF") {
    12471286                                                $this->fseek($i - 4, SEEK_CUR);
    12481287                                                continue 2;
     
    12761315                                        $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
    12771316                                        $next4 = $this->fread(4);
    1278                                         if ($next4{0} == "\xFF") {
     1317                                        if ($next4[0] == "\xFF") {
    12791318                                                if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
    12801319                                                        $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
     
    13541393        }
    13551394
    1356 
     1395        /**
     1396         * @param int  $avdataoffset
     1397         * @param bool $BitrateHistogram
     1398         *
     1399         * @return bool
     1400         */
    13571401        public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
    13581402                // looks for synch, decodes MPEG audio header
     
    13641408                static $MPEGaudioBitrateLookup;
    13651409                if (empty($MPEGaudioVersionLookup)) {
    1366                    $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
    1367                    $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
    1368                    $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
    1369 
     1410                        $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
     1411                        $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
     1412                        $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
    13701413                }
    13711414
     
    14171460                        }
    14181461
    1419                         if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
     1462                        if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // synch detected
     1463                                $FirstFrameAVDataOffset = null;
    14201464                                if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
    14211465                                        $FirstFrameThisfileInfo = $info;
     
    14411485                                                        break;
    14421486                                        }
    1443                                         if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
     1487                                        if (isset($FirstFrameThisfileInfo) && isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
    14441488                                                if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
    14451489                                                        // If there is garbage data between a valid VBR header frame and a sequence
     
    15111555                                                                $buffer_4k = $this->fread(4096);
    15121556                                                                for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
    1513                                                                         if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
     1557                                                                        if (($buffer_4k[$j] == "\xFF") && ($buffer_4k[($j + 1)] > "\xE0")) { // synch detected
    15141558                                                                                if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
    15151559                                                                                        $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
     
    15231567                                                        }
    15241568                                                        $synchstartoffset = $scan_start_offset[$current_segment];
    1525                                                         while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
     1569                                                        while (($synchstartoffset < $info['avdataend']) && $this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
    15261570                                                                $FastMode = true;
    15271571                                                                $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
     
    16341678        }
    16351679
    1636 
     1680        /**
     1681         * @return array
     1682         */
    16371683        public static function MPEGaudioVersionArray() {
    16381684                static $MPEGaudioVersion = array('2.5', false, '2', '1');
     
    16401686        }
    16411687
     1688        /**
     1689         * @return array
     1690         */
    16421691        public static function MPEGaudioLayerArray() {
    16431692                static $MPEGaudioLayer = array(false, 3, 2, 1);
     
    16451694        }
    16461695
     1696        /**
     1697         * @return array
     1698         */
    16471699        public static function MPEGaudioBitrateArray() {
    16481700                static $MPEGaudioBitrate;
     
    16641716        }
    16651717
     1718        /**
     1719         * @return array
     1720         */
    16661721        public static function MPEGaudioFrequencyArray() {
    16671722                static $MPEGaudioFrequency;
     
    16761731        }
    16771732
     1733        /**
     1734         * @return array
     1735         */
    16781736        public static function MPEGaudioChannelModeArray() {
    16791737                static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
     
    16811739        }
    16821740
     1741        /**
     1742         * @return array
     1743         */
    16831744        public static function MPEGaudioModeExtensionArray() {
    16841745                static $MPEGaudioModeExtension;
     
    16931754        }
    16941755
     1756        /**
     1757         * @return array
     1758         */
    16951759        public static function MPEGaudioEmphasisArray() {
    16961760                static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
     
    16981762        }
    16991763
     1764        /**
     1765         * @param string $head4
     1766         * @param bool   $allowBitrate15
     1767         *
     1768         * @return bool
     1769         */
    17001770        public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
    17011771                return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
    17021772        }
    17031773
     1774        /**
     1775         * @param array $rawarray
     1776         * @param bool  $echoerrors
     1777         * @param bool  $allowBitrate15
     1778         *
     1779         * @return bool
     1780         */
    17041781        public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
    17051782                if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
     
    17741851        }
    17751852
     1853        /**
     1854         * @param string $Header4Bytes
     1855         *
     1856         * @return array|false
     1857         */
    17761858        public static function MPEGaudioHeaderDecode($Header4Bytes) {
    17771859                // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
     
    17951877
    17961878                $MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
    1797                 $MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
    1798                 $MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
    1799                 $MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
    1800                 $MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
    1801                 $MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
    1802                 $MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
    1803                 $MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
    1804                 $MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
    1805                 $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
    1806                 $MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
    1807                 $MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
    1808                 $MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
     1879                $MPEGrawHeader['version']       = (ord($Header4Bytes[1]) & 0x18) >> 3; //    BB
     1880                $MPEGrawHeader['layer']         = (ord($Header4Bytes[1]) & 0x06) >> 1; //      CC
     1881                $MPEGrawHeader['protection']    = (ord($Header4Bytes[1]) & 0x01);      //        D
     1882                $MPEGrawHeader['bitrate']       = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE
     1883                $MPEGrawHeader['sample_rate']   = (ord($Header4Bytes[2]) & 0x0C) >> 2; //     FF
     1884                $MPEGrawHeader['padding']       = (ord($Header4Bytes[2]) & 0x02) >> 1; //       G
     1885                $MPEGrawHeader['private']       = (ord($Header4Bytes[2]) & 0x01);      //        H
     1886                $MPEGrawHeader['channelmode']   = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II
     1887                $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; //   JJ
     1888                $MPEGrawHeader['copyright']     = (ord($Header4Bytes[3]) & 0x08) >> 3; //     K
     1889                $MPEGrawHeader['original']      = (ord($Header4Bytes[3]) & 0x04) >> 2; //      L
     1890                $MPEGrawHeader['emphasis']      = (ord($Header4Bytes[3]) & 0x03);      //       MM
    18091891
    18101892                return $MPEGrawHeader;
    18111893        }
    18121894
     1895        /**
     1896         * @param int|string $bitrate
     1897         * @param string     $version
     1898         * @param string     $layer
     1899         * @param bool       $padding
     1900         * @param int        $samplerate
     1901         *
     1902         * @return int|false
     1903         */
    18131904        public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
    18141905                static $AudioFrameLengthCache = array();
     
    18721963        }
    18731964
     1965        /**
     1966         * @param float|int $bit_rate
     1967         *
     1968         * @return int|float|string
     1969         */
    18741970        public static function ClosestStandardMP3Bitrate($bit_rate) {
    18751971                static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
     
    18921988        }
    18931989
     1990        /**
     1991         * @param string $version
     1992         * @param string $channelmode
     1993         *
     1994         * @return int
     1995         */
    18941996        public static function XingVBRidOffset($version, $channelmode) {
    18951997                static $XingVBRidOffsetCache = array();
    1896                 if (empty($XingVBRidOffset)) {
    1897                         $XingVBRidOffset = array (
     1998                if (empty($XingVBRidOffsetCache)) {
     1999                        $XingVBRidOffsetCache = array (
    18982000                                '1'   => array ('mono'          => 0x15, // 4 + 17 = 21
    18992001                                                                'stereo'        => 0x24, // 4 + 32 = 36
     
    19152017                        );
    19162018                }
    1917                 return $XingVBRidOffset[$version][$channelmode];
    1918         }
    1919 
     2019                return $XingVBRidOffsetCache[$version][$channelmode];
     2020        }
     2021
     2022        /**
     2023         * @param int $VBRmethodID
     2024         *
     2025         * @return string
     2026         */
    19202027        public static function LAMEvbrMethodLookup($VBRmethodID) {
    19212028                static $LAMEvbrMethodLookup = array(
     
    19342041        }
    19352042
     2043        /**
     2044         * @param int $StereoModeID
     2045         *
     2046         * @return string
     2047         */
    19362048        public static function LAMEmiscStereoModeLookup($StereoModeID) {
    19372049                static $LAMEmiscStereoModeLookup = array(
     
    19482060        }
    19492061
     2062        /**
     2063         * @param int $SourceSampleFrequencyID
     2064         *
     2065         * @return string
     2066         */
    19502067        public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
    19512068                static $LAMEmiscSourceSampleFrequencyLookup = array(
     
    19582075        }
    19592076
     2077        /**
     2078         * @param int $SurroundInfoID
     2079         *
     2080         * @return string
     2081         */
    19602082        public static function LAMEsurroundInfoLookup($SurroundInfoID) {
    19612083                static $LAMEsurroundInfoLookup = array(
     
    19682090        }
    19692091
     2092        /**
     2093         * @param array $LAMEtag
     2094         *
     2095         * @return string
     2096         */
    19702097        public static function LAMEpresetUsedLookup($LAMEtag) {
    19712098
  • trunk/src/wp-includes/ID3/module.audio.ogg.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//                                                             //
     
    1919class getid3_ogg extends getid3_handler
    2020{
    21         // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
     21        /**
     22         * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html
     23         *
     24         * @return bool
     25         */
    2226        public function Analyze() {
    2327                $info = &$this->getid3->info;
     
    6670                } elseif (substr($filedata, 0, 8) == 'OpusHead') {
    6771
    68                         if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
     72                        if ($this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) === false) {
    6973                                return false;
    7074                        }
     
    180184                                $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
    181185                        }
    182 $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
     186                        $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable');
    183187
    184188
     
    260264                        //return false;
    261265
     266                } elseif (substr($filedata, 0, 5) == "\x7F".'FLAC') {
     267                        // https://xiph.org/flac/ogg_mapping.html
     268
     269                        $info['audio']['dataformat']   = 'flac';
     270                        $info['audio']['bitrate_mode'] = 'vbr';
     271                        $info['audio']['lossless']     = true;
     272
     273                        $info['ogg']['flac']['header']['version_major']  =                         ord(substr($filedata,  5, 1));
     274                        $info['ogg']['flac']['header']['version_minor']  =                         ord(substr($filedata,  6, 1));
     275                        $info['ogg']['flac']['header']['header_packets'] =   getid3_lib::BigEndian2Int(substr($filedata,  7, 2)) + 1; // "A two-byte, big-endian binary number signifying the number of header (non-audio) packets, not including this one. This number may be zero (0x0000) to signify 'unknown' but be aware that some decoders may not be able to handle such streams."
     276                        $info['ogg']['flac']['header']['magic']          =                             substr($filedata,  9, 4);
     277                        if ($info['ogg']['flac']['header']['magic'] != 'fLaC') {
     278                                $this->error('Ogg-FLAC expecting "fLaC", found "'.$info['ogg']['flac']['header']['magic'].'" ('.trim(getid3_lib::PrintHexBytes($info['ogg']['flac']['header']['magic'])).')');
     279                                return false;
     280                        }
     281                        $info['ogg']['flac']['header']['STREAMINFO_bytes'] = getid3_lib::BigEndian2Int(substr($filedata, 13, 4));
     282                        $info['flac']['STREAMINFO'] = getid3_flac::parseSTREAMINFOdata(substr($filedata, 17, 34));
     283                        if (!empty($info['flac']['STREAMINFO']['sample_rate'])) {
     284                                $info['audio']['bitrate_mode']    = 'vbr';
     285                                $info['audio']['sample_rate']     = $info['flac']['STREAMINFO']['sample_rate'];
     286                                $info['audio']['channels']        = $info['flac']['STREAMINFO']['channels'];
     287                                $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
     288                                $info['playtime_seconds']         = $info['flac']['STREAMINFO']['samples_stream'] / $info['flac']['STREAMINFO']['sample_rate'];
     289                        }
     290
    262291                } else {
    263292
    264                         $this->error('Expecting either "Speex   ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"');
     293                        $this->error('Expecting one of "vorbis", "Speex", "OpusHead", "vorbis", "fishhead", "theora", "fLaC" identifier strings, found "'.substr($filedata, 0, 8).'"');
    265294                        unset($info['ogg']);
    266295                        unset($info['mime_type']);
     
    379408        }
    380409
     410        /**
     411         * @param string $filedata
     412         * @param int    $filedataoffset
     413         * @param array  $oggpageinfo
     414         *
     415         * @return bool
     416         */
    381417        public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
    382418                $info = &$this->getid3->info;
     
    427463        }
    428464
    429         // http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
     465        /**
     466         * @link http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
     467         *
     468         * @param string $filedata
     469         * @param int    $filedataoffset
     470         * @param array  $oggpageinfo
     471         *
     472         * @return bool
     473         */
    430474        public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
    431475                $info = &$this->getid3->info;
     
    459503                $filedataoffset += 2;
    460504
    461                 $info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
     505                $info['ogg']['pageheader']['opus']['input_sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset,  4));
    462506                $filedataoffset += 4;
    463507
     
    468512                //$filedataoffset += 1;
    469513
    470                 $info['opus']['opus_version']      = $info['ogg']['pageheader']['opus']['version'];
    471                 $info['opus']['sample_rate']       = $info['ogg']['pageheader']['opus']['sample_rate'];
    472                 $info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
    473 
    474                 $info['audio']['channels']      = $info['opus']['out_channel_count'];
    475                 $info['audio']['sample_rate']   = $info['opus']['sample_rate'];
     514                $info['opus']['opus_version']       = $info['ogg']['pageheader']['opus']['version'];
     515                $info['opus']['sample_rate_input']  = $info['ogg']['pageheader']['opus']['input_sample_rate'];
     516                $info['opus']['out_channel_count']  = $info['ogg']['pageheader']['opus']['out_channel_count'];
     517
     518                $info['audio']['channels']          = $info['opus']['out_channel_count'];
     519                $info['audio']['sample_rate_input'] = $info['opus']['sample_rate_input'];
     520                $info['audio']['sample_rate']       = 48000; // "All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz for playback (unless the target hardware does not support this sampling rate). However, this field may be used to resample the audio back to the original sampling rate, for example, when saving the output to a file." -- https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/structOpusHead.html
    476521                return true;
    477522        }
    478523
    479 
     524        /**
     525         * @return array|false
     526         */
    480527        public function ParseOggPageHeader() {
    481528                // http://xiph.org/ogg/vorbis/doc/framing.html
     
    490537                        }
    491538                        if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
    492                                 if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
     539                                if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === '')) {
    493540                                        // get some more data, unless eof, in which case fail
    494541                                        return false;
     
    529576        }
    530577
    531     // http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
     578        /**
     579         * @link http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
     580         *
     581         * @return bool
     582         */
    532583        public function ParseVorbisComments() {
    533584                $info = &$this->getid3->info;
    534585
    535586                $OriginalOffset = $this->ftell();
     587                $commentdata = null;
    536588                $commentdataoffset = 0;
    537589                $VorbisCommentPage = 1;
     590                $CommentStartOffset = 0;
    538591
    539592                switch ($info['audio']['dataformat']) {
     
    766819        }
    767820
     821        /**
     822         * @param int $mode
     823         *
     824         * @return string|null
     825         */
    768826        public static function SpeexBandModeLookup($mode) {
    769827                static $SpeexBandModeLookup = array();
     
    776834        }
    777835
    778 
     836        /**
     837         * @param array $OggInfoArray
     838         * @param int   $SegmentNumber
     839         *
     840         * @return int
     841         */
    779842        public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
     843                $segmentlength = 0;
    780844                for ($i = 0; $i < $SegmentNumber; $i++) {
    781845                        $segmentlength = 0;
     
    790854        }
    791855
    792 
     856        /**
     857         * @param int $nominal_bitrate
     858         *
     859         * @return float
     860         */
    793861        public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
    794862
     
    814882        }
    815883
     884        /**
     885         * @param int $colorspace_id
     886         *
     887         * @return string|null
     888         */
    816889        public static function TheoraColorSpace($colorspace_id) {
    817890                // http://www.theora.org/doc/Theora.pdf (table 6.3)
     
    826899        }
    827900
     901        /**
     902         * @param int $pixelformat_id
     903         *
     904         * @return string|null
     905         */
    828906        public static function TheoraPixelFormat($pixelformat_id) {
    829907                // http://www.theora.org/doc/Theora.pdf (table 6.4)
  • trunk/src/wp-includes/ID3/module.tag.apetag.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//                                                             //
     
    1717class getid3_apetag extends getid3_handler
    1818{
    19         public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
     19        /**
     20         * true: return full data for all attachments;
     21         * false: return no data for all attachments;
     22         * integer: return data for attachments <= than this;
     23         * string: save as file to this directory.
     24         *
     25         * @var int|bool|string
     26         */
     27        public $inline_attachments = true;
     28
    2029        public $overrideendoffset  = 0;
    2130
     31        /**
     32         * @return bool
     33         */
    2234        public function Analyze() {
    2335                $info = &$this->getid3->info;
     
    151163                                // http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
    152164                                case 'replaygain_track_gain':
    153                                         if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
    154                                                 $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
     165                                        if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
     166                                                $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
    155167                                                $thisfile_replaygain['track']['originator'] = 'unspecified';
    156168                                        } else {
     
    160172
    161173                                case 'replaygain_track_peak':
    162                                         if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
    163                                                 $thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
     174                                        if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
     175                                                $thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
    164176                                                $thisfile_replaygain['track']['originator'] = 'unspecified';
    165177                                                if ($thisfile_replaygain['track']['peak'] <= 0) {
     
    172184
    173185                                case 'replaygain_album_gain':
    174                                         if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
    175                                                 $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
     186                                        if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
     187                                                $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
    176188                                                $thisfile_replaygain['album']['originator'] = 'unspecified';
    177189                                        } else {
     
    181193
    182194                                case 'replaygain_album_peak':
    183                                         if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
    184                                                 $thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
     195                                        if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
     196                                                $thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
    185197                                                $thisfile_replaygain['album']['originator'] = 'unspecified';
    186198                                                if ($thisfile_replaygain['album']['peak'] <= 0) {
     
    226238                                        if (is_array($thisfile_ape_items_current['data'])) {
    227239                                                foreach ($thisfile_ape_items_current['data'] as $comment) {
    228                                                         $thisfile_ape['comments']['track'][] = $comment;
     240                                                        $thisfile_ape['comments']['track_number'][] = $comment;
    229241                                                }
    230242                                        }
     
    336348        }
    337349
     350        /**
     351         * @param string $APEheaderFooterData
     352         *
     353         * @return array|false
     354         */
    338355        public function parseAPEheaderFooter($APEheaderFooterData) {
    339356                // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
     
    360377        }
    361378
     379        /**
     380         * @param int $rawflagint
     381         *
     382         * @return array
     383         */
    362384        public function parseAPEtagFlags($rawflagint) {
    363385                // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
     
    375397        }
    376398
     399        /**
     400         * @param int $contenttypeid
     401         *
     402         * @return string
     403         */
    377404        public function APEcontentTypeFlagLookup($contenttypeid) {
    378405                static $APEcontentTypeFlagLookup = array(
     
    385412        }
    386413
     414        /**
     415         * @param string $itemkey
     416         *
     417         * @return bool
     418         */
    387419        public function APEtagItemIsUTF8Lookup($itemkey) {
    388420                static $APEtagItemIsUTF8Lookup = array(
  • trunk/src/wp-includes/ID3/module.tag.id3v1.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//                                                             //
     
    1818class getid3_id3v1 extends getid3_handler
    1919{
    20 
     20        /**
     21         * @return bool
     22         */
    2123        public function Analyze() {
    2224                $info = &$this->getid3->info;
     
    4446                        // If second-last byte of comment field is null and last byte of comment field is non-null
    4547                        // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
    46                         if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
    47                                 $ParsedID3v1['track']  = ord(substr($ParsedID3v1['comment'], 29,  1));
    48                                 $ParsedID3v1['comment'] =     substr($ParsedID3v1['comment'],  0, 28);
     48                        if (($id3v1tag[125] === "\x00") && ($id3v1tag[126] !== "\x00")) {
     49                                $ParsedID3v1['track_number'] = ord(substr($ParsedID3v1['comment'], 29,  1));
     50                                $ParsedID3v1['comment']      =     substr($ParsedID3v1['comment'],  0, 28);
    4951                        }
    5052                        $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
     
    6769                        foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
    6870                                foreach ($valuearray as $key => $value) {
    69                                         if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
     71                                        if (preg_match('#^[\\x00-\\x40\\xA8\\xB8\\x80-\\xFF]+$#', $value)) {
    7072                                                foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
    7173                                                        if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
     
    9092                                                                                        (isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
    9193                                                                                        $ParsedID3v1['comment'],
    92                                                                                         (!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
     94                                                                                        (!empty($ParsedID3v1['track_number']) ? $ParsedID3v1['track_number'] : ''));
    9395                        $ParsedID3v1['padding_valid'] = true;
    9496                        if ($id3v1tag !== $GoodFormatID3v1tag) {
     
    125127        }
    126128
     129        /**
     130         * @param string $str
     131         *
     132         * @return string
     133         */
    127134        public static function cutfield($str) {
    128135                return trim(substr($str, 0, strcspn($str, "\x00")));
    129136        }
    130137
     138        /**
     139         * @param bool $allowSCMPXextended
     140         *
     141         * @return string[]
     142         */
    131143        public static function ArrayOfGenres($allowSCMPXextended=false) {
    132144                static $GenreLookup = array(
     
    313325        }
    314326
     327        /**
     328         * @param string $genreid
     329         * @param bool   $allowSCMPXextended
     330         *
     331         * @return string|false
     332         */
    315333        public static function LookupGenreName($genreid, $allowSCMPXextended=true) {
    316334                switch ($genreid) {
     
    329347        }
    330348
     349        /**
     350         * @param string $genre
     351         * @param bool   $allowSCMPXextended
     352         *
     353         * @return string|false
     354         */
    331355        public static function LookupGenreID($genre, $allowSCMPXextended=false) {
    332356                $GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
     
    340364        }
    341365
     366        /**
     367         * @param string $OriginalGenre
     368         *
     369         * @return string|false
     370         */
    342371        public static function StandardiseID3v1GenreName($OriginalGenre) {
    343372                if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) {
     
    347376        }
    348377
     378        /**
     379         * @param string     $title
     380         * @param string     $artist
     381         * @param string     $album
     382         * @param string     $year
     383         * @param int        $genreid
     384         * @param string     $comment
     385         * @param int|string $track
     386         *
     387         * @return string
     388         */
    349389        public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
    350390                $ID3v1Tag  = 'TAG';
  • trunk/src/wp-includes/ID3/module.tag.id3v2.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///                                                            //
     
    2121        public $StartingOffset = 0;
    2222
     23        /**
     24         * @return bool
     25         */
    2326        public function Analyze() {
    2427                $info = &$this->getid3->info;
     
    5760                if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
    5861
    59                         $thisfile_id3v2['majorversion'] = ord($header{3});
    60                         $thisfile_id3v2['minorversion'] = ord($header{4});
     62                        $thisfile_id3v2['majorversion'] = ord($header[3]);
     63                        $thisfile_id3v2['minorversion'] = ord($header[4]);
    6164
    6265                        // shortcut
     
    7780                }
    7881
    79                 $id3_flags = ord($header{5});
     82                $id3_flags = ord($header[5]);
    8083                switch ($id3v2_majorversion) {
    8184                        case 2:
     
    258261                                        $thisfile_id3v2['padding']['valid']  = true;
    259262                                        for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
    260                                                 if ($framedata{$i} != "\x00") {
     263                                                if ($framedata[$i] != "\x00") {
    261264                                                        $thisfile_id3v2['padding']['valid'] = false;
    262265                                                        $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
     
    267270                                        break; // skip rest of ID3v2 header
    268271                                }
     272                                $frame_header = null;
     273                                $frame_name   = null;
     274                                $frame_size   = null;
     275                                $frame_flags  = null;
    269276                                if ($id3v2_majorversion == 2) {
    270277                                        // Frame ID  $xx xx xx (three characters)
     
    320327                                        $len = strlen($framedata);
    321328                                        for ($i = 0; $i < $len; $i++) {
    322                                                 if ($framedata{$i} != "\x00") {
     329                                                if ($framedata[$i] != "\x00") {
    323330                                                        $thisfile_id3v2['padding']['valid'] = false;
    324331                                                        $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
     
    428435                        if (substr($footer, 0, 3) == '3DI') {
    429436                                $thisfile_id3v2['footer'] = true;
    430                                 $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
    431                                 $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
     437                                $thisfile_id3v2['majorversion_footer'] = ord($footer[3]);
     438                                $thisfile_id3v2['minorversion_footer'] = ord($footer[4]);
    432439                        }
    433440                        if ($thisfile_id3v2['majorversion_footer'] <= 4) {
    434                                 $id3_flags = ord(substr($footer{5}));
     441                                $id3_flags = ord($footer[5]);
    435442                                $thisfile_id3v2_flags['unsynch_footer']  = (bool) ($id3_flags & 0x80);
    436443                                $thisfile_id3v2_flags['extfoot_footer']  = (bool) ($id3_flags & 0x40);
     
    453460                }
    454461
    455                 if (isset($thisfile_id3v2['comments']['track'])) {
    456                         foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
     462                if (isset($thisfile_id3v2['comments']['track_number'])) {
     463                        foreach ($thisfile_id3v2['comments']['track_number'] as $key => $value) {
    457464                                if (strstr($value, '/')) {
    458                                         list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
     465                                        list($thisfile_id3v2['comments']['track_number'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track_number'][$key]);
    459466                                }
    460467                        }
     
    499506        }
    500507
    501 
     508        /**
     509         * @param string $genrestring
     510         *
     511         * @return array
     512         */
    502513        public function ParseID3v2GenreString($genrestring) {
    503514                // Parse genres into arrays of genreName and genreID
     
    531542                        $element = trim($element);
    532543                        if ($element) {
    533                                 if (preg_match('#^[0-9]{1,3}#', $element)) {
     544                                if (preg_match('#^[0-9]{1,3}$#', $element)) {
    534545                                        $clean_genres[] = getid3_id3v1::LookupGenreName($element);
    535546                                } else {
     
    541552        }
    542553
    543 
     554        /**
     555         * @param array $parsedFrame
     556         *
     557         * @return bool
     558         */
    544559        public function ParseID3v2Frame(&$parsedFrame) {
    545560
     
    658673                                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    659674                        }
    660                         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    661                         if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    662                                 // if description only contains a BOM or terminator then make it blank
    663                                 $frame_description = '';
    664                         }
     675                        $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     676                        $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
    665677                        $parsedFrame['encodingid']  = $frame_textencoding;
    666678                        $parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
    667679
    668                         $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
     680                        $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description']));
    669681                        $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     682                        $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
    670683                        if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    671684                                $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
     
    679692
    680693
    681                 } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
     694                } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame
    682695                        //   There may only be one text information frame of its kind in an tag.
    683696                        // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
     
    693706
    694707                        $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     708                        $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
    695709
    696710                        $parsedFrame['encodingid'] = $frame_textencoding;
    697711                        $parsedFrame['encoding']   = $this->TextEncodingNameLookup($frame_textencoding);
    698 
    699712                        if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    700713                                // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
     
    752765                                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    753766                        }
    754                         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    755                         if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    756                                 // if description only contains a BOM or terminator then make it blank
    757                                 $frame_description = '';
    758                         }
    759                         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
    760 
    761                         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
    762                         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
    763                                 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    764                         }
    765                         if ($frame_terminatorpos) {
    766                                 // there are null bytes after the data - this is not according to spec
    767                                 // only use data up to first null byte
    768                                 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
    769                         } else {
    770                                 // no null bytes following data, just use all data
    771                                 $frame_urldata = (string) $parsedFrame['data'];
    772                         }
    773 
    774767                        $parsedFrame['encodingid']  = $frame_textencoding;
    775768                        $parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
    776 
    777                         $parsedFrame['url']         = $frame_urldata;
    778                         $parsedFrame['description'] = $frame_description;
     769                        $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);           // according to the frame text encoding
     770                        $parsedFrame['url']         = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1
     771                        $parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator);
     772                        $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
     773
    779774                        if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
    780                                 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
     775                                $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']);
    781776                        }
    782777                        unset($parsedFrame['data']);
    783778
    784779
    785                 } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
     780                } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames
    786781                        //   There may only be one URL link frame of its kind in a tag,
    787782                        //   except when stated otherwise in the frame description
     
    790785                        // URL              <text string>
    791786
    792                         $parsedFrame['url'] = trim($parsedFrame['data']);
     787                        $parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1
    793788                        if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
    794                                 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
     789                                $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']);
    795790                        }
    796791                        unset($parsedFrame['data']);
     
    814809                        $parsedFrame['data_raw']   = (string) substr($parsedFrame['data'], $frame_offset);
    815810
    816                         // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
     811                        // https://www.getid3.org/phpBB3/viewtopic.php?t=1369
    817812                        // "this tag typically contains null terminated strings, which are associated in pairs"
    818813                        // "there are users that use the tag incorrectly"
     
    934929                        $parsedFrame['bitsformsdeviation']      = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
    935930                        $parsedFrame['data'] = substr($parsedFrame['data'], 10);
     931                        $deviationbitstream = '';
    936932                        while ($frame_offset < strlen($parsedFrame['data'])) {
    937933                                $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
     
    995991                                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    996992                        }
    997                         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    998                         if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    999                                 // if description only contains a BOM or terminator then make it blank
    1000                                 $frame_description = '';
    1001                         }
     993                        $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     994                        $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
    1002995                        $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     996                        $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
    1003997
    1004998                        $parsedFrame['encodingid']   = $frame_textencoding;
     
    10071001                        $parsedFrame['language']     = $frame_language;
    10081002                        $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
    1009                         $parsedFrame['description']  = $frame_description;
    10101003                        if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    10111004                                $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
     
    10621055
    10631056                                        $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
    1064                                         if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
     1057                                        if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) {
    10651058                                                // timestamp probably omitted for first data item
    10661059                                        } else {
     
    11031096                                        $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    11041097                                }
    1105                                 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    1106                                 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    1107                                         // if description only contains a BOM or terminator then make it blank
    1108                                         $frame_description = '';
    1109                                 }
     1098                                $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     1099                                $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
    11101100                                $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     1101                                $frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator);
    11111102
    11121103                                $parsedFrame['encodingid']   = $frame_textencoding;
     
    11151106                                $parsedFrame['language']     = $frame_language;
    11161107                                $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
    1117                                 $parsedFrame['description']  = $frame_description;
    11181108                                $parsedFrame['data']         = $frame_text;
    11191109                                if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
     
    14081398                                        $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    14091399                                }
    1410                                 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    1411                                 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    1412                                         // if description only contains a BOM or terminator then make it blank
    1413                                         $frame_description = '';
     1400                                $parsedFrame['description']   = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     1401                                $parsedFrame['description']   = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
     1402                                $parsedFrame['encodingid']    = $frame_textencoding;
     1403                                $parsedFrame['encoding']      = $this->TextEncodingNameLookup($frame_textencoding);
     1404
     1405                                if ($id3v2_majorversion == 2) {
     1406                                        $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null;
     1407                                } else {
     1408                                        $parsedFrame['mime']      = isset($frame_mimetype) ? $frame_mimetype : null;
    14141409                                }
    1415                                 $parsedFrame['encodingid']       = $frame_textencoding;
    1416                                 $parsedFrame['encoding']         = $this->TextEncodingNameLookup($frame_textencoding);
    1417 
    1418                                 if ($id3v2_majorversion == 2) {
    1419                                         $parsedFrame['imagetype']    = $frame_imagetype;
    1420                                 } else {
    1421                                         $parsedFrame['mime']         = $frame_mimetype;
    1422                                 }
    1423                                 $parsedFrame['picturetypeid']    = $frame_picturetype;
    1424                                 $parsedFrame['picturetype']      = $this->APICPictureTypeLookup($frame_picturetype);
    1425                                 $parsedFrame['description']      = $frame_description;
    1426                                 $parsedFrame['data']             = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
    1427                                 $parsedFrame['datalength']       = strlen($parsedFrame['data']);
    1428 
    1429                                 $parsedFrame['image_mime'] = '';
     1410                                $parsedFrame['picturetypeid'] = $frame_picturetype;
     1411                                $parsedFrame['picturetype']   = $this->APICPictureTypeLookup($frame_picturetype);
     1412                                $parsedFrame['data']          = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     1413                                $parsedFrame['datalength']    = strlen($parsedFrame['data']);
     1414
     1415                                $parsedFrame['image_mime']    = '';
    14301416                                $imageinfo = array();
    14311417                                if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
    14321418                                        if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
    1433                                                 $parsedFrame['image_mime']       = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
     1419                                                $parsedFrame['image_mime']       = image_type_to_mime_type($imagechunkcheck[2]);
    14341420                                                if ($imagechunkcheck[0]) {
    14351421                                                        $parsedFrame['image_width']  = $imagechunkcheck[0];
     
    14471433                                                break;
    14481434                                        }
     1435                                        $dir = '';
    14491436                                        if ($this->getid3->option_save_attachments === true) {
    14501437                                                // great
     
    15341521                                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    15351522                        }
    1536                         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    1537                         if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    1538                                 // if description only contains a BOM or terminator then make it blank
    1539                                 $frame_description = '';
    1540                         }
     1523                        $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     1524                        $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
    15411525                        $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
    15421526
     
    15471531                        $parsedFrame['mime']        = $frame_mimetype;
    15481532                        $parsedFrame['filename']    = $frame_filename;
    1549                         $parsedFrame['description'] = $frame_description;
    15501533                        unset($parsedFrame['data']);
    15511534
     
    16171600
    16181601                        $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
    1619                         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    1620                         if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    1621                                 // if description only contains a BOM or terminator then make it blank
    1622                                 $frame_description = '';
    1623                         }
     1602                        $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     1603                        $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
    16241604                        $frame_offset = $frame_terminatorpos + strlen("\x00");
    16251605
    16261606                        $parsedFrame['ownerid']     = $frame_ownerid;
    16271607                        $parsedFrame['data']        = (string) substr($parsedFrame['data'], $frame_offset);
    1628                         $parsedFrame['description'] = $frame_description;
    16291608                        unset($parsedFrame['data']);
    16301609
     
    17221701                        $parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
    17231702
    1724                         $parsedFrame['data']         = (string) substr($parsedFrame['data'], $frame_offset);
     1703                        $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     1704                        $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
    17251705                        if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    17261706                                $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
     
    17601740
    17611741                        $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
     1742                        $parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding));
    17621743                        unset($parsedFrame['data']);
    17631744
     
    18181799                                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    18191800                        }
    1820                         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    1821                         if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
    1822                                 // if description only contains a BOM or terminator then make it blank
    1823                                 $frame_description = '';
    1824                         }
     1801                        $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
     1802                        $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
    18251803                        $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
    18261804
     
    18391817                        $parsedFrame['receivedas']        = $this->COMRReceivedAsLookup($frame_receivedasid);
    18401818                        $parsedFrame['sellername']        = $frame_sellername;
    1841                         $parsedFrame['description']       = $frame_description;
    18421819                        $parsedFrame['mime']              = $frame_mimetype;
    18431820                        $parsedFrame['logo']              = $frame_sellerlogo;
     
    20031980                        // Start time      $xx xx xx xx
    20041981                        // End time        $xx xx xx xx
    2005             // Start offset    $xx xx xx xx
    2006             // End offset      $xx xx xx xx
    2007             // <Optional embedded sub-frames>
     1982                        // Start offset    $xx xx xx xx
     1983                        // End offset      $xx xx xx xx
     1984                        // <Optional embedded sub-frames>
    20081985
    20091986                        $frame_offset = 0;
     
    20382015                                        if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
    20392016                                                $this->warning('CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
     2017                                                break;
     2018                                        }
     2019                                        $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
     2020                                        $frame_offset += $subframe['size'];
     2021
     2022                                        $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
     2023                                        $subframe['text']       =     substr($subframe_rawdata, 1);
     2024                                        $subframe['encoding']   = $this->TextEncodingNameLookup($subframe['encodingid']);
     2025                                        $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));
     2026                                        switch (substr($encoding_converted_text, 0, 2)) {
     2027                                                case "\xFF\xFE":
     2028                                                case "\xFE\xFF":
     2029                                                        switch (strtoupper($info['id3v2']['encoding'])) {
     2030                                                                case 'ISO-8859-1':
     2031                                                                case 'UTF-8':
     2032                                                                        $encoding_converted_text = substr($encoding_converted_text, 2);
     2033                                                                        // remove unwanted byte-order-marks
     2034                                                                        break;
     2035                                                                default:
     2036                                                                        // ignore
     2037                                                                        break;
     2038                                                        }
     2039                                                        break;
     2040                                                default:
     2041                                                        // do not remove BOM
     2042                                                        break;
     2043                                        }
     2044
     2045                                        switch ($subframe['name']) {
     2046                                                case 'TIT2':
     2047                                                        $parsedFrame['chapter_name']        = $encoding_converted_text;
     2048                                                        $parsedFrame['subframes'][] = $subframe;
     2049                                                        break;
     2050                                                case 'TIT3':
     2051                                                        $parsedFrame['chapter_description'] = $encoding_converted_text;
     2052                                                        $parsedFrame['subframes'][] = $subframe;
     2053                                                        break;
     2054                                                case 'WXXX':
     2055                                                        list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2);
     2056                                                        $parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url'];
     2057                                                        $parsedFrame['subframes'][] = $subframe;
     2058                                                        break;
     2059                                                case 'APIC':
     2060                                                        if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) {
     2061                                                                list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches;
     2062                                                                $subframe['image_mime']   = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime));
     2063                                                                $subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype);
     2064                                                                $subframe['description']  = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description));
     2065                                                                if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) {
     2066                                                                        // the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16)
     2067                                                                        // the above regex assumes one byte, if it's actually two then strip the second one here
     2068                                                                        $subframe_apic_picturedata = substr($subframe_apic_picturedata, 1);
     2069                                                                }
     2070                                                                $subframe['data'] = $subframe_apic_picturedata;
     2071                                                                unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata);
     2072                                                                unset($subframe['text'], $parsedFrame['text']);
     2073                                                                $parsedFrame['subframes'][] = $subframe;
     2074                                                                $parsedFrame['picture_present'] = true;
     2075                                                        } else {
     2076                                                                $this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format');
     2077                                                        }
     2078                                                        break;
     2079                                                default:
     2080                                                        $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)');
     2081                                                        break;
     2082                                        }
     2083                                }
     2084                                unset($subframe_rawdata, $subframe, $encoding_converted_text);
     2085                                unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC
     2086                        }
     2087
     2088                        $id3v2_chapter_entry = array();
     2089                        foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) {
     2090                                if (isset($parsedFrame[$id3v2_chapter_key])) {
     2091                                        $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
     2092                                }
     2093                        }
     2094                        if (!isset($info['id3v2']['chapters'])) {
     2095                                $info['id3v2']['chapters'] = array();
     2096                        }
     2097                        $info['id3v2']['chapters'][] = $id3v2_chapter_entry;
     2098                        unset($id3v2_chapter_entry, $id3v2_chapter_key);
     2099
     2100
     2101                } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
     2102                        // http://id3.org/id3v2-chapters-1.0
     2103                        // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC">           (10 bytes)
     2104                        // Element ID      <text string> $00
     2105                        // CTOC flags        %xx
     2106                        // Entry count       $xx
     2107                        // Child Element ID  <string>$00   /* zero or more child CHAP or CTOC entries */
     2108                        // <Optional embedded sub-frames>
     2109
     2110                        $frame_offset = 0;
     2111                        @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
     2112                        $frame_offset += strlen($parsedFrame['element_id']."\x00");
     2113                        $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
     2114                        $frame_offset += 1;
     2115                        $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
     2116                        $frame_offset += 1;
     2117
     2118                        $terminator_position = null;
     2119                        for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
     2120                                $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
     2121                                $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
     2122                                $frame_offset = $terminator_position + 1;
     2123                        }
     2124
     2125                        $parsedFrame['ctoc_flags']['ordered']   = (bool) ($ctoc_flags_raw & 0x01);
     2126                        $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
     2127
     2128                        unset($ctoc_flags_raw, $terminator_position);
     2129
     2130                        if ($frame_offset < strlen($parsedFrame['data'])) {
     2131                                $parsedFrame['subframes'] = array();
     2132                                while ($frame_offset < strlen($parsedFrame['data'])) {
     2133                                        // <Optional embedded sub-frames>
     2134                                        $subframe = array();
     2135                                        $subframe['name']      =                           substr($parsedFrame['data'], $frame_offset, 4);
     2136                                        $frame_offset += 4;
     2137                                        $subframe['size']      = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
     2138                                        $frame_offset += 4;
     2139                                        $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
     2140                                        $frame_offset += 2;
     2141                                        if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
     2142                                                $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
    20402143                                                break;
    20412144                                        }
     
    20682171                                        if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
    20692172                                                if ($subframe['name'] == 'TIT2') {
    2070                                                         $parsedFrame['chapter_name']        = $encoding_converted_text;
    2071                                                 } elseif ($subframe['name'] == 'TIT3') {
    2072                                                         $parsedFrame['chapter_description'] = $encoding_converted_text;
    2073                                                 }
    2074                                                 $parsedFrame['subframes'][] = $subframe;
    2075                                         } else {
    2076                                                 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
    2077                                         }
    2078                                 }
    2079                                 unset($subframe_rawdata, $subframe, $encoding_converted_text);
    2080                         }
    2081 
    2082                         $id3v2_chapter_entry = array();
    2083                         foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
    2084                                 if (isset($parsedFrame[$id3v2_chapter_key])) {
    2085                                         $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
    2086                                 }
    2087                         }
    2088                         if (!isset($info['id3v2']['chapters'])) {
    2089                                 $info['id3v2']['chapters'] = array();
    2090                         }
    2091                         $info['id3v2']['chapters'][] = $id3v2_chapter_entry;
    2092                         unset($id3v2_chapter_entry, $id3v2_chapter_key);
    2093 
    2094 
    2095                 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
    2096                         // http://id3.org/id3v2-chapters-1.0
    2097                         // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC">           (10 bytes)
    2098                         // Element ID      <text string> $00
    2099                         // CTOC flags        %xx
    2100                         // Entry count       $xx
    2101                         // Child Element ID  <string>$00   /* zero or more child CHAP or CTOC entries */
    2102             // <Optional embedded sub-frames>
    2103 
    2104                         $frame_offset = 0;
    2105                         @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
    2106                         $frame_offset += strlen($parsedFrame['element_id']."\x00");
    2107                         $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
    2108                         $frame_offset += 1;
    2109                         $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
    2110                         $frame_offset += 1;
    2111 
    2112                         $terminator_position = null;
    2113                         for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
    2114                                 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
    2115                                 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
    2116                                 $frame_offset = $terminator_position + 1;
    2117                         }
    2118 
    2119                         $parsedFrame['ctoc_flags']['ordered']   = (bool) ($ctoc_flags_raw & 0x01);
    2120                         $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
    2121 
    2122                         unset($ctoc_flags_raw, $terminator_position);
    2123 
    2124                         if ($frame_offset < strlen($parsedFrame['data'])) {
    2125                                 $parsedFrame['subframes'] = array();
    2126                                 while ($frame_offset < strlen($parsedFrame['data'])) {
    2127                                         // <Optional embedded sub-frames>
    2128                                         $subframe = array();
    2129                                         $subframe['name']      =                           substr($parsedFrame['data'], $frame_offset, 4);
    2130                                         $frame_offset += 4;
    2131                                         $subframe['size']      = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
    2132                                         $frame_offset += 4;
    2133                                         $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
    2134                                         $frame_offset += 2;
    2135                                         if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
    2136                                                 $this->warning('CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)');
    2137                                                 break;
    2138                                         }
    2139                                         $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
    2140                                         $frame_offset += $subframe['size'];
    2141 
    2142                                         $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
    2143                                         $subframe['text']       =     substr($subframe_rawdata, 1);
    2144                                         $subframe['encoding']   = $this->TextEncodingNameLookup($subframe['encodingid']);
    2145                                         $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
    2146                                         switch (substr($encoding_converted_text, 0, 2)) {
    2147                                                 case "\xFF\xFE":
    2148                                                 case "\xFE\xFF":
    2149                                                         switch (strtoupper($info['id3v2']['encoding'])) {
    2150                                                                 case 'ISO-8859-1':
    2151                                                                 case 'UTF-8':
    2152                                                                         $encoding_converted_text = substr($encoding_converted_text, 2);
    2153                                                                         // remove unwanted byte-order-marks
    2154                                                                         break;
    2155                                                                 default:
    2156                                                                         // ignore
    2157                                                                         break;
    2158                                                         }
    2159                                                         break;
    2160                                                 default:
    2161                                                         // do not remove BOM
    2162                                                         break;
    2163                                         }
    2164 
    2165                                         if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
    2166                                                 if ($subframe['name'] == 'TIT2') {
    21672173                                                        $parsedFrame['toc_name']        = $encoding_converted_text;
    21682174                                                } elseif ($subframe['name'] == 'TIT3') {
     
    21822188        }
    21832189
    2184 
     2190        /**
     2191         * @param string $data
     2192         *
     2193         * @return string
     2194         */
    21852195        public function DeUnsynchronise($data) {
    21862196                return str_replace("\xFF\x00", "\xFF", $data);
    21872197        }
    21882198
     2199        /**
     2200         * @param int $index
     2201         *
     2202         * @return string
     2203         */
    21892204        public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
    21902205                static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
     
    21972212        }
    21982213
     2214        /**
     2215         * @param int $index
     2216         *
     2217         * @return string
     2218         */
    21992219        public function LookupExtendedHeaderRestrictionsTextEncodings($index) {
    22002220                static $LookupExtendedHeaderRestrictionsTextEncodings = array(
     
    22052225        }
    22062226
     2227        /**
     2228         * @param int $index
     2229         *
     2230         * @return string
     2231         */
    22072232        public function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
    22082233                static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
     
    22152240        }
    22162241
     2242        /**
     2243         * @param int $index
     2244         *
     2245         * @return string
     2246         */
    22172247        public function LookupExtendedHeaderRestrictionsImageEncoding($index) {
    22182248                static $LookupExtendedHeaderRestrictionsImageEncoding = array(
     
    22232253        }
    22242254
     2255        /**
     2256         * @param int $index
     2257         *
     2258         * @return string
     2259         */
    22252260        public function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
    22262261                static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
     
    22332268        }
    22342269
     2270        /**
     2271         * @param string $currencyid
     2272         *
     2273         * @return string
     2274         */
    22352275        public function LookupCurrencyUnits($currencyid) {
    22362276
     
    24292469        }
    24302470
    2431 
     2471        /**
     2472         * @param string $currencyid
     2473         *
     2474         * @return string
     2475         */
    24322476        public function LookupCurrencyCountry($currencyid) {
    24332477
     
    26252669        }
    26262670
    2627 
    2628 
     2671        /**
     2672         * @param string $languagecode
     2673         * @param bool   $casesensitive
     2674         *
     2675         * @return string
     2676         */
    26292677        public static function LanguageLookup($languagecode, $casesensitive=false) {
    26302678
     
    30823130        }
    30833131
    3084 
     3132        /**
     3133         * @param int $index
     3134         *
     3135         * @return string
     3136         */
    30853137        public static function ETCOEventLookup($index) {
    30863138                if (($index >= 0x17) && ($index <= 0xDF)) {
     
    31263178        }
    31273179
     3180        /**
     3181         * @param int $index
     3182         *
     3183         * @return string
     3184         */
    31283185        public static function SYTLContentTypeLookup($index) {
    31293186                static $SYTLContentTypeLookup = array(
     
    31423199        }
    31433200
     3201        /**
     3202         * @param int   $index
     3203         * @param bool $returnarray
     3204         *
     3205         * @return array|string
     3206         */
    31443207        public static function APICPictureTypeLookup($index, $returnarray=false) {
    31453208                static $APICPictureTypeLookup = array(
     
    31723235        }
    31733236
     3237        /**
     3238         * @param int $index
     3239         *
     3240         * @return string
     3241         */
    31743242        public static function COMRReceivedAsLookup($index) {
    31753243                static $COMRReceivedAsLookup = array(
     
    31883256        }
    31893257
     3258        /**
     3259         * @param int $index
     3260         *
     3261         * @return string
     3262         */
    31903263        public static function RVA2ChannelTypeLookup($index) {
    31913264                static $RVA2ChannelTypeLookup = array(
     
    32043277        }
    32053278
     3279        /**
     3280         * @param string $framename
     3281         *
     3282         * @return string
     3283         */
    32063284        public static function FrameNameLongLookup($framename) {
    32073285
     
    33553433                        UFI     Unique file identifier
    33563434                        UFID    Unique file identifier
    3357                         ULT     Unsychronised lyric/text transcription
     3435                        ULT     Unsynchronised lyric/text transcription
    33583436                        USER    Terms of use
    33593437                        USLT    Unsynchronised lyric/text transcription
     
    33873465        }
    33883466
    3389 
     3467        /**
     3468         * @param string $framename
     3469         *
     3470         * @return string
     3471         */
    33903472        public static function FrameNameShortLookup($framename) {
    33913473
     
    35393621                        UFI     unique_file_identifier
    35403622                        UFID    unique_file_identifier
    3541                         ULT     unsychronised_lyric
     3623                        ULT     unsynchronised_lyric
    35423624                        USER    terms_of_use
    35433625                        USLT    unsynchronised_lyric
     
    35673649        }
    35683650
     3651        /**
     3652         * @param string $encoding
     3653         *
     3654         * @return string
     3655         */
    35693656        public static function TextEncodingTerminatorLookup($encoding) {
    35703657                // http://www.id3.org/id3v2.4.0-structure.txt
     
    35803667        }
    35813668
     3669        /**
     3670         * @param int $encoding
     3671         *
     3672         * @return string
     3673         */
    35823674        public static function TextEncodingNameLookup($encoding) {
    35833675                // http://www.id3.org/id3v2.4.0-structure.txt
     
    35933685        }
    35943686
     3687        /**
     3688         * @param string $string
     3689         * @param string $terminator
     3690         *
     3691         * @return string
     3692         */
     3693        public static function RemoveStringTerminator($string, $terminator) {
     3694                // Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present.
     3695                // https://github.com/JamesHeinrich/getID3/issues/121
     3696                // https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227
     3697                if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) {
     3698                        $string = substr($string, 0, -strlen($terminator));
     3699                }
     3700                return $string;
     3701        }
     3702
     3703        /**
     3704         * @param string $string
     3705         *
     3706         * @return string
     3707         */
     3708        public static function MakeUTF16emptyStringEmpty($string) {
     3709                if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
     3710                        // if string only contains a BOM or terminator then make it actually an empty string
     3711                        $string = '';
     3712                }
     3713                return $string;
     3714        }
     3715
     3716        /**
     3717         * @param string $framename
     3718         * @param int    $id3v2majorversion
     3719         *
     3720         * @return bool|int
     3721         */
    35953722        public static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
    35963723                switch ($id3v2majorversion) {
     
    36073734        }
    36083735
     3736        /**
     3737         * @param string $numberstring
     3738         * @param bool   $allowdecimal
     3739         * @param bool   $allownegative
     3740         *
     3741         * @return bool
     3742         */
    36093743        public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
    36103744                for ($i = 0; $i < strlen($numberstring); $i++) {
    3611                         if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
    3612                                 if (($numberstring{$i} == '.') && $allowdecimal) {
     3745                        if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) {
     3746                                if (($numberstring[$i] == '.') && $allowdecimal) {
    36133747                                        // allowed
    3614                                 } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
     3748                                } elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) {
    36153749                                        // allowed
    36163750                                } else {
     
    36223756        }
    36233757
     3758        /**
     3759         * @param string $datestamp
     3760         *
     3761         * @return bool
     3762         */
    36243763        public static function IsValidDateStampString($datestamp) {
    36253764                if (strlen($datestamp) != 8) {
     
    36503789        }
    36513790
     3791        /**
     3792         * @param int $majorversion
     3793         *
     3794         * @return int
     3795         */
    36523796        public static function ID3v2HeaderLength($majorversion) {
    36533797                return (($majorversion == 2) ? 6 : 10);
    36543798        }
    36553799
     3800        /**
     3801         * @param string $frame_name
     3802         *
     3803         * @return string|false
     3804         */
    36563805        public static function ID3v22iTunesBrokenFrameName($frame_name) {
    36573806                // iTunes (multiple versions) has been known to write ID3v2.3 style frames
     
    37403889
    37413890}
     3891
  • trunk/src/wp-includes/ID3/module.tag.lyrics3.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///                                                            //
     
    1818class getid3_lyrics3 extends getid3_handler
    1919{
    20 
     20        /**
     21         * @return bool
     22         */
    2123        public function Analyze() {
    2224                $info = &$this->getid3->info;
     
    6264                        // Lyrics3v2, no ID3v1, no APE
    6365
    64                         $lyrics3size    = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
     66                        $lyrics3size    = (int) strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
    6567                        $lyrics3offset  = $info['filesize'] - $lyrics3size;
    6668                        $lyrics3version = 2;
     
    9799                }
    98100
    99                 if (isset($lyrics3offset)) {
     101                if (isset($lyrics3offset) && isset($lyrics3version) && isset($lyrics3size)) {
    100102                        $info['avdataend'] = $lyrics3offset;
    101103                        $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
     
    127129        }
    128130
     131        /**
     132         * @param int $endoffset
     133         * @param int $version
     134         * @param int $length
     135         *
     136         * @return bool
     137         */
    129138        public function getLyrics3Data($endoffset, $version, $length) {
    130139                // http://www.volweb.cz/str/tags.htm
     
    142151                }
    143152                $rawdata = $this->fread($length);
     153
     154                $ParsedLyrics3 = array();
    144155
    145156                $ParsedLyrics3['raw']['lyrics3version'] = $version;
     
    251262        }
    252263
     264        /**
     265         * @param string $rawtimestamp
     266         *
     267         * @return int|false
     268         */
    253269        public function Lyrics3Timestamp2Seconds($rawtimestamp) {
    254270                if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
     
    258274        }
    259275
     276        /**
     277         * @param array $Lyrics3data
     278         *
     279         * @return bool
     280         */
    260281        public function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
    261282                $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
     283                $notimestamplyricsarray = array();
    262284                foreach ($lyricsarray as $key => $lyricline) {
    263285                        $regs = array();
     
    288310        }
    289311
     312        /**
     313         * @param string $char
     314         *
     315         * @return bool|null
     316         */
    290317        public function IntString2Bool($char) {
    291318                if ($char == '1') {
  • trunk/src/wp-includes/ID3/readme.txt

    r29734 r46112  
    22/// getID3() by James Heinrich <info@getid3.org>               //
    33//  available at http://getid3.sourceforge.net                 //
    4 //            or http://www.getid3.org                         //
     4//            or https://www.getid3.org                        //
    55//          also https://github.com/JamesHeinrich/getID3       //
    66/////////////////////////////////////////////////////////////////
     
    1919GNU LGPL: https://gnu.org/licenses/lgpl.html                 (v3)
    2020
    21 Mozilla MPL: http://www.mozilla.org/MPL/2.0/                 (v2)
    22 
    23 getID3 Commercial License: http://getid3.org/#gCL (payment required)
     21Mozilla MPL: https://www.mozilla.org/MPL/2.0/                (v2)
     22
     23getID3 Commercial License: https://www.getid3.org/#gCL (payment required)
    2424
    2525*****************************************************************
     
    2929
    3030
    31        +---------------------------------------------+
    32        | If you want to donate, there is a link on   |
    33        | http://www.getid3.org for PayPal donations. |
    34        +---------------------------------------------+
     31       +----------------------------------------------+
     32       | If you want to donate, there is a link on    |
     33       | https://www.getid3.org for PayPal donations. |
     34       +----------------------------------------------+
    3535
    3636
     
    7878  * MP3/MP2/MP1
    7979  * MPC / Musepack
    80   * Ogg (Vorbis, OggFLAC, Speex)
     80  * Ogg (Vorbis, OggFLAC, Speex, Opus)
    8181  * AAC / MP4
    8282  * AC3
     
    146146===========================================================================
    147147
    148 * PHP 4.2.0 up to 5.2.x for getID3() 1.7.x (and earlier)
    149 * PHP 5.0.5 (or higher) for getID3() 1.8.x (and up)
    150 * PHP 5.0.5 (or higher) for getID3() 2.0.x (and up)
     148* PHP 4.2.0 up to 5.2.x for getID3() 1.7.x  (and earlier)
     149* PHP 5.0.5 (or higher) for getID3() 1.8.x  (and up)
     150* PHP 5.3.0 (or higher) for getID3() 1.9.17 (and up)
     151* PHP 5.3.0 (or higher) for getID3() 2.0.x  (and up)
    151152* at least 4MB memory for PHP. 8MB or more is highly recommended.
    152153  12MB is required with all modules loaded.
     
    178179$remotefilename = 'http://www.example.com/filename.mp3';
    179180if ($fp_remote = fopen($remotefilename, 'rb')) {
    180     $localtempfilename = tempnam('/tmp', 'getID3');
    181     if ($fp_local = fopen($localtempfilename, 'wb')) {
    182         while ($buffer = fread($fp_remote, 8192)) {
    183             fwrite($fp_local, $buffer);
    184         }
    185         fclose($fp_local);
     181        $localtempfilename = tempnam('/tmp', 'getID3');
     182        if ($fp_local = fopen($localtempfilename, 'wb')) {
     183                while ($buffer = fread($fp_remote, 32768)) {
     184                        fwrite($fp_local, $buffer);
     185                }
     186                fclose($fp_local);
     187
     188                $remote_headers = array_change_key_case(get_headers($remotefilename, 1), CASE_LOWER);
     189                $remote_filesize = (isset($remote_headers['content-length']) ? (is_array($remote_headers['content-length']) ? $remote_headers['content-length'][count($remote_headers['content-length']) - 1] : $remote_headers['content-length']) : null);
    186190
    187191                // Initialize getID3 engine
    188192                $getID3 = new getID3;
    189193
    190                 $ThisFileInfo = $getID3->analyze($filename);
    191 
    192         // Delete temporary file
    193         unlink($localtempfilename);
    194     }
    195     fclose($fp_remote);
     194                $ThisFileInfo = $getID3->analyze($localtempfilename, $remote_filesize, basename($remotefilename));
     195
     196                // Delete temporary file
     197                unlink($localtempfilename);
     198        }
     199        fclose($fp_remote);
    196200}
    197201
     202Note: since v1.9.9-20150212 it is possible a second and third parameter
     203to $getID3->analyze(), for original filesize and original filename
     204respectively. This permits you to download only a portion of a large remote
     205file but get accurate playtime estimates, assuming the format only requires
     206the beginning of the file for correct format analysis.
    198207
    199208See /demos/demo.write.php for how to write tags.
     
    293302Future Plans
    294303===========================================================================
    295 http://www.getid3.org/phpBB3/viewforum.php?f=7
     304https://www.getid3.org/phpBB3/viewforum.php?f=7
    296305
    297306* Better support for MP4 container format
     
    335344  (thanks rockcohenØmassive-interactive*nl)
    336345* Support for TTF (thanks infoØbutterflyx*com)
    337 * Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171)
     346* Support for DSS (https://www.getid3.org/phpBB3/viewtopic.php?t=171)
    338347* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
    339   http://www.getid3.org/phpBB3/viewtopic.php?t=182
    340 * Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
    341 * Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
     348  https://www.getid3.org/phpBB3/viewtopic.php?t=182
     349* Support for AMR (https://www.getid3.org/phpBB3/viewtopic.php?t=195)
     350* Support for 3gpp (https://www.getid3.org/phpBB3/viewtopic.php?t=195)
    342351* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
    343352* Parse XML data returned in Ogg comments
     
    375384Known Bugs/Issues in getID3() that may be fixed eventually
    376385===========================================================================
    377 http://www.getid3.org/phpBB3/viewtopic.php?t=25
     386https://www.getid3.org/phpBB3/viewtopic.php?t=25
    378387
    379388* Cannot determine bitrate for MPEG video with VBR video data
     
    401410Known Bugs/Issues in getID3() that cannot be fixed
    402411--------------------------------------------------
    403 http://www.getid3.org/phpBB3/viewtopic.php?t=25
     412https://www.getid3.org/phpBB3/viewtopic.php?t=25
    404413
    405414* 32-bit PHP installations only:
     
    431440Known Bugs/Issues in other programs
    432441-----------------------------------
    433 http://www.getid3.org/phpBB3/viewtopic.php?t=25
    434 
     442https://www.getid3.org/phpBB3/viewtopic.php?t=25
     443
     444* MusicBrainz Picard (at least up to v1.3.2) writes multiple
     445  ID3v2.3 genres in non-standard forward-slash separated text
     446  rather than parenthesis-numeric+refinement style per the ID3v2.3
     447  specs. Tags written in ID3v2.4 mode are written correctly.
     448  (detected and worked around by getID3())
     449* PZ TagEditor v4.53.408 has been known to insert ID3v2.3 frames
     450  into an existing ID3v2.2 tag which, of course, breaks things
    435451* Windows Media Player (up to v11) and iTunes (up to v10+) do
    436452    not correctly handle ID3v2.3 tags with UTF-16BE+BOM
     
    452468* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
    453469    actually ABR or VBR.
     470* iTunes (versions "v7.0.0.70" is known-guilty, probably
     471    other versions are too) writes ID3v2.3 comment tags using an
     472    ID3v2.2 frame name (3-bytes) null-padded to 4 bytes which is
     473    not valid for ID3v2.3+
     474    (detected by getID3() since 1.9.12-201603221746)
    454475* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
    455476    other versions are too) writes ID3v2.3 comment tags using a
     
    603624* http://wyday.com/cuesharp/specification.php
    604625* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
     626* http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
     627* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
Note: See TracChangeset for help on using the changeset viewer.