Make WordPress Core

Changeset 46112


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