WordPress.org

Make WordPress Core

Changeset 41196


Ignore:
Timestamp:
07/31/2017 07:49:31 PM (3 years ago)
Author:
wonderboymusic
Message:

Media: update the getID3 library to version 1.9.14 to avoid fatal errors in PHP7.

Props MyThemeShop for the initial patch.
Fixes #41496.

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

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/ID3/getid3.lib.php

    r32979 r41196  
    294294    }
    295295
     296    public static function LittleEndian2Bin($byteword) {
     297        return self::BigEndian2Bin(strrev($byteword));
     298    }
    296299
    297300    public static function BigEndian2Bin($byteword) {
     
    413416        }
    414417        return $newarray;
     418    }
     419
     420    public static function flipped_array_merge_noclobber($array1, $array2) {
     421        if (!is_array($array1) || !is_array($array2)) {
     422            return false;
     423        }
     424        # naturally, this only works non-recursively
     425        $newarray = array_flip($array1);
     426        foreach (array_flip($array2) as $key => $val) {
     427            if (!isset($newarray[$key])) {
     428                $newarray[$key] = count($newarray);
     429            }
     430        }
     431        return array_flip($newarray);
    415432    }
    416433
     
    947964        }
    948965
     966        // mb_convert_encoding() availble
     967        if (function_exists('mb_convert_encoding')) {
     968            if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) {
     969                switch ($out_charset) {
     970                    case 'ISO-8859-1':
     971                        $converted_string = rtrim($converted_string, "\x00");
     972                        break;
     973                }
     974                return $converted_string;
     975            }
     976            return $string;
     977        }
    949978        // iconv() availble
    950         if (function_exists('iconv')) {
     979        else if (function_exists('iconv')) {
    951980            if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
    952981                switch ($out_charset) {
     
    964993
    965994
    966         // iconv() not available
     995        // neither mb_convert_encoding or iconv() is available
    967996        static $ConversionFunctionList = array();
    968997        if (empty($ConversionFunctionList)) {
     
    9861015            return self::$ConversionFunction($string);
    9871016        }
    988         throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
     1017        throw new Exception('PHP does not has mb_convert_encoding() or iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
    9891018    }
    9901019
     
    10071036        $HTMLstring = '';
    10081037
    1009         switch ($charset) {
     1038        switch (strtolower($charset)) {
    10101039            case '1251':
    10111040            case '1252':
     
    10141043            case '936':
    10151044            case '950':
    1016             case 'BIG5':
    1017             case 'BIG5-HKSCS':
     1045            case 'big5':
     1046            case 'big5-hkscs':
    10181047            case 'cp1251':
    10191048            case 'cp1252':
    10201049            case 'cp866':
    1021             case 'EUC-JP':
    1022             case 'EUCJP':
    1023             case 'GB2312':
     1050            case 'euc-jp':
     1051            case 'eucjp':
     1052            case 'gb2312':
    10241053            case 'ibm866':
    1025             case 'ISO-8859-1':
    1026             case 'ISO-8859-15':
    1027             case 'ISO8859-1':
    1028             case 'ISO8859-15':
    1029             case 'KOI8-R':
     1054            case 'iso-8859-1':
     1055            case 'iso-8859-15':
     1056            case 'iso8859-1':
     1057            case 'iso8859-15':
     1058            case 'koi8-r':
    10301059            case 'koi8-ru':
    10311060            case 'koi8r':
    1032             case 'Shift_JIS':
    1033             case 'SJIS':
     1061            case 'shift_jis':
     1062            case 'sjis':
    10341063            case 'win-1251':
    1035             case 'Windows-1251':
    1036             case 'Windows-1252':
     1064            case 'windows-1251':
     1065            case 'windows-1252':
    10371066                $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
    10381067                break;
    10391068
    1040             case 'UTF-8':
     1069            case 'utf-8':
    10411070                $strlen = strlen($string);
    10421071                for ($i = 0; $i < $strlen; $i++) {
     
    10661095                break;
    10671096
    1068             case 'UTF-16LE':
     1097            case 'utf-16le':
    10691098                for ($i = 0; $i < strlen($string); $i += 2) {
    10701099                    $charval = self::LittleEndian2Int(substr($string, $i, 2));
     
    10771106                break;
    10781107
    1079             case 'UTF-16BE':
     1108            case 'utf-16be':
    10801109                for ($i = 0; $i < strlen($string); $i += 2) {
    10811110                    $charval = self::BigEndian2Int(substr($string, $i, 2));
     
    11541183        static $tempdir = '';
    11551184        if (empty($tempdir)) {
     1185            if (function_exists('sys_get_temp_dir')) {
     1186                $tempdir = sys_get_temp_dir(); // https://github.com/JamesHeinrich/getID3/issues/52
     1187            }
     1188
    11561189            // yes this is ugly, feel free to suggest a better way
    1157             require_once(dirname(__FILE__).'/getid3.php');
    1158             $getid3_temp = new getID3();
    1159             $tempdir = $getid3_temp->tempdir;
    1160             unset($getid3_temp);
     1190            if (include_once(dirname(__FILE__).'/getid3.php')) {
     1191                if ($getid3_temp = new getID3()) {
     1192                    if ($getid3_temp_tempdir = $getid3_temp->tempdir) {
     1193                        $tempdir = $getid3_temp_tempdir;
     1194                    }
     1195                    unset($getid3_temp, $getid3_temp_tempdir);
     1196                }
     1197            }
    11611198        }
    11621199        $GetDataImageSize = false;
     
    11661203                fclose($tmp);
    11671204                $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
     1205                if (($GetDataImageSize === false) || !isset($GetDataImageSize[0]) || !isset($GetDataImageSize[1])) {
     1206                    return false;
     1207                }
    11681208                $GetDataImageSize['height'] = $GetDataImageSize[0];
    11691209                $GetDataImageSize['width']  = $GetDataImageSize[1];
     
    12381278                            if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
    12391279                                $value = (is_string($value) ? trim($value) : $value);
    1240                                 if (!is_numeric($key)) {
     1280                                if (!is_int($key) && !ctype_digit($key)) {
    12411281                                    $ThisFileInfo['comments'][$tagname][$key] = $value;
    12421282                                } else {
    1243                                     $ThisFileInfo['comments'][$tagname][]     = $value;
     1283                                    if (isset($ThisFileInfo['comments'][$tagname])) {
     1284                                        $ThisFileInfo['comments'][$tagname] = array($value);
     1285                                    } else {
     1286                                        $ThisFileInfo['comments'][$tagname][] = $value;
     1287                                    }
    12441288                                }
    12451289                            }
    12461290                        }
    12471291                    }
     1292                }
     1293            }
     1294
     1295            // attempt to standardize spelling of returned keys
     1296            $StandardizeFieldNames = array(
     1297                'tracknumber' => 'track_number',
     1298                'track'       => 'track_number',
     1299            );
     1300            foreach ($StandardizeFieldNames as $badkey => $goodkey) {
     1301                if (array_key_exists($badkey, $ThisFileInfo['comments']) && !array_key_exists($goodkey, $ThisFileInfo['comments'])) {
     1302                    $ThisFileInfo['comments'][$goodkey] = $ThisFileInfo['comments'][$badkey];
     1303                    unset($ThisFileInfo['comments'][$badkey]);
    12481304                }
    12491305            }
  • trunk/src/wp-includes/ID3/getid3.php

    r32979 r41196  
    2222if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
    2323    define('IMG_JPG', IMAGETYPE_JPEG);
     24}
     25if (!defined('ENT_SUBSTITUTE')) { // PHP5.3 adds ENT_IGNORE, PHP5.4 adds ENT_SUBSTITUTE
     26    define('ENT_SUBSTITUTE', (defined('ENT_IGNORE') ? ENT_IGNORE : 8));
    2427}
    2528
     
    110113    protected $startup_warning = '';
    111114
    112     const VERSION           = '1.9.9-20141121';
     115    const VERSION           = '1.9.14-201706111222';
    113116    const FREAD_BUFFER_SIZE = 32768;
    114117
     
    121124        // Check memory
    122125        $this->memory_limit = ini_get('memory_limit');
    123         if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
     126        if (preg_match('#([0-9]+) ?M#i', $this->memory_limit, $matches)) {
    124127            // could be stored as "16M" rather than 16777216 for example
    125128            $this->memory_limit = $matches[1] * 1048576;
    126         } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
     129        } elseif (preg_match('#([0-9]+) ?G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
    127130            // could be stored as "2G" rather than 2147483648 for example
    128131            $this->memory_limit = $matches[1] * 1073741824;
     
    131134            // memory limits probably disabled
    132135        } elseif ($this->memory_limit <= 4194304) {
    133             $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
     136            $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini'."\n";
    134137        } elseif ($this->memory_limit <= 12582912) {
    135             $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
     138            $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'."\n";
    136139        }
    137140
     
    141144        }
    142145
    143         if (intval(ini_get('mbstring.func_overload')) > 0) {
    144             $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
     146        if (($mbstring_func_overload = ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) {
     147            // http://php.net/manual/en/mbstring.overload.php
     148            // "mbstring.func_overload in php.ini is a positive value that represents a combination of bitmasks specifying the categories of functions to be overloaded. It should be set to 1 to overload the mail() function. 2 for string functions, 4 for regular expression functions"
     149            // getID3 cannot run when string functions are overloaded. It doesn't matter if mail() or ereg* functions are overloaded since getID3 does not use those.
     150            $this->startup_error .= 'WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", getID3 cannot run with this setting (bitmask 2 (string functions) cannot be set). Recommended to disable entirely.'."\n";
    145151        }
    146152
     
    148154        if (function_exists('get_magic_quotes_runtime')) {
    149155            if (get_magic_quotes_runtime()) {
    150                 return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
     156                $this->startup_error .= 'magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'."\n";
    151157            }
    152158        }
     
    155161        if (function_exists('magic_quotes_gpc')) {
    156162            if (get_magic_quotes_gpc()) {
    157                 return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
     163                $this->startup_error .= 'magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).'."\n";
    158164            }
    159165        }
     
    161167        // Load support library
    162168        if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
    163             $this->startup_error .= 'getid3.lib.php is missing or corrupt';
     169            $this->startup_error .= 'getid3.lib.php is missing or corrupt'."\n";
    164170        }
    165171
     
    180186
    181187            if (!is_dir($helperappsdir)) {
    182                 $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
     188                $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'."\n";
    183189            } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
    184190                $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
     
    200206                            }
    201207                        } else {
    202                             $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
     208                            $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.'."\n";
    203209                        }
    204210                    }
     
    208214            }
    209215            define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
     216        }
     217
     218        if (!empty($this->startup_error)) {
     219            echo $this->startup_error;
     220            throw new getid3_exception($this->startup_error);
    210221        }
    211222
     
    237248
    238249
    239     public function openfile($filename) {
     250    public function openfile($filename, $filesize=null) {
    240251        try {
    241252            if (!empty($this->startup_error)) {
     
    243254            }
    244255            if (!empty($this->startup_warning)) {
    245                 $this->warning($this->startup_warning);
     256                foreach (explode("\n", $this->startup_warning) as $startup_warning) {
     257                    $this->warning($startup_warning);
     258                }
    246259            }
    247260
     
    253266
    254267            // remote files not supported
    255             if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
     268            if (preg_match('#^(ht|f)tp://#', $filename)) {
    256269                throw new getid3_exception('Remote files are not supported - please copy the file locally first');
    257270            }
    258271
    259272            $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
    260             $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
     273            $filename = preg_replace('#(?<!gs:)('.preg_quote(DIRECTORY_SEPARATOR).'{2,})#', DIRECTORY_SEPARATOR, $filename);
    261274
    262275            // open local file
     
    281294            }
    282295
    283             $this->info['filesize'] = filesize($filename);
     296            $this->info['filesize'] = (!is_null($filesize) ? $filesize : filesize($filename));
    284297            // set redundant parameters - might be needed in some include file
    285298            // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
     
    289302            $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
    290303
     304            // set more parameters
     305            $this->info['avdataoffset']        = 0;
     306            $this->info['avdataend']           = $this->info['filesize'];
     307            $this->info['fileformat']          = '';                // filled in later
     308            $this->info['audio']['dataformat'] = '';                // filled in later, unset if not used
     309            $this->info['video']['dataformat'] = '';                // filled in later, unset if not used
     310            $this->info['tags']                = array();           // filled in later, unset if not used
     311            $this->info['error']               = array();           // filled in later, unset if not used
     312            $this->info['warning']             = array();           // filled in later, unset if not used
     313            $this->info['comments']            = array();           // filled in later, unset if not used
     314            $this->info['encoding']            = $this->encoding;   // required by id3v2 and iso modules - can be unset at the end if desired
    291315
    292316            // option_max_2gb_check
     
    315339            }
    316340
    317             // set more parameters
    318             $this->info['avdataoffset']        = 0;
    319             $this->info['avdataend']           = $this->info['filesize'];
    320             $this->info['fileformat']          = '';                // filled in later
    321             $this->info['audio']['dataformat'] = '';                // filled in later, unset if not used
    322             $this->info['video']['dataformat'] = '';                // filled in later, unset if not used
    323             $this->info['tags']                = array();           // filled in later, unset if not used
    324             $this->info['error']               = array();           // filled in later, unset if not used
    325             $this->info['warning']             = array();           // filled in later, unset if not used
    326             $this->info['comments']            = array();           // filled in later, unset if not used
    327             $this->info['encoding']            = $this->encoding;   // required by id3v2 and iso modules - can be unset at the end if desired
    328 
    329341            return true;
    330342
     
    336348
    337349    // public: analyze file
    338     public function analyze($filename) {
     350    public function analyze($filename, $filesize=null, $original_filename='') {
    339351        try {
    340             if (!$this->openfile($filename)) {
     352            if (!$this->openfile($filename, $filesize)) {
    341353                return $this->info;
    342354            }
     
    383395
    384396            // determine format
    385             $determined_format = $this->GetFileFormat($formattest, $filename);
     397            $determined_format = $this->GetFileFormat($formattest, ($original_filename ? $original_filename : $filename));
    386398
    387399            // unable to determine file format
     
    420432            }
    421433
    422             // module requires iconv support
     434            // module requires mb_convert_encoding/iconv support
    423435            // Check encoding/iconv support
    424             if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
    425                 $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
     436            if (!empty($determined_format['iconv_req']) && !function_exists('mb_convert_encoding') && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
     437                $errormessage = 'mb_convert_encoding() or iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
    426438                if (GETID3_OS_ISWINDOWS) {
    427                     $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
     439                    $errormessage .= 'PHP does not have mb_convert_encoding() or iconv() support. Please enable php_mbstring.dll / php_iconv.dll in php.ini, and copy php_mbstring.dll / iconv.dll from c:/php/dlls to c:/windows/system32';
    428440                } else {
    429                     $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
     441                    $errormessage .= 'PHP is not compiled with mb_convert_encoding() or iconv() support. Please recompile with the --enable-mbstring / --with-iconv switch';
    430442                }
    431443                return $this->error($errormessage);
     
    562574                // AC-3   - audio      - Dolby AC-3 / Dolby Digital
    563575                'ac3'  => array(
    564                             'pattern'   => '^\x0B\x77',
     576                            'pattern'   => '^\\x0B\\x77',
    565577                            'group'     => 'audio',
    566578                            'module'    => 'ac3',
     
    580592                // AA   - audio       - Audible Audiobook
    581593                'aa'   => array(
    582                             'pattern'   => '^.{4}\x57\x90\x75\x36',
     594                            'pattern'   => '^.{4}\\x57\\x90\\x75\\x36',
    583595                            'group'     => 'audio',
    584596                            'module'    => 'aa',
     
    588600                // AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
    589601                'adts' => array(
    590                             'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
     602                            'pattern'   => '^\\xFF[\\xF0-\\xF1\\xF8-\\xF9]',
    591603                            'group'     => 'audio',
    592604                            'module'    => 'aac',
     
    598610                // AU   - audio       - NeXT/Sun AUdio (AU)
    599611                'au'   => array(
    600                             'pattern'   => '^\.snd',
     612                            'pattern'   => '^\\.snd',
    601613                            'group'     => 'audio',
    602614                            'module'    => 'au',
     
    606618                // AMR  - audio       - Adaptive Multi Rate
    607619                'amr'  => array(
    608                             'pattern'   => '^\x23\x21AMR\x0A', // #!AMR[0A]
     620                            'pattern'   => '^\\x23\\x21AMR\\x0A', // #!AMR[0A]
    609621                            'group'     => 'audio',
    610622                            'module'    => 'amr',
     
    622634                // BONK - audio       - Bonk v0.9+
    623635                'bonk' => array(
    624                             'pattern'   => '^\x00(BONK|INFO|META| ID3)',
     636                            'pattern'   => '^\\x00(BONK|INFO|META| ID3)',
    625637                            'group'     => 'audio',
    626638                            'module'    => 'bonk',
     
    628640                        ),
    629641
     642                // DSF  - audio       - Direct Stream Digital (DSD) Storage Facility files (DSF) - https://en.wikipedia.org/wiki/Direct_Stream_Digital
     643                'dsf'  => array(
     644                            'pattern'   => '^DSD ',  // including trailing space: 44 53 44 20
     645                            'group'     => 'audio',
     646                            'module'    => 'dsf',
     647                            'mime_type' => 'audio/dsd',
     648                        ),
     649
    630650                // DSS  - audio       - Digital Speech Standard
    631651                'dss'  => array(
    632                             'pattern'   => '^[\x02-\x03]ds[s2]',
     652                            'pattern'   => '^[\\x02-\\x06]ds[s2]',
    633653                            'group'     => 'audio',
    634654                            'module'    => 'dss',
     
    638658                // DTS  - audio       - Dolby Theatre System
    639659                'dts'  => array(
    640                             'pattern'   => '^\x7F\xFE\x80\x01',
     660                            'pattern'   => '^\\x7F\\xFE\\x80\\x01',
    641661                            'group'     => 'audio',
    642662                            'module'    => 'dts',
     
    723743                // MPC  - audio       - Musepack / MPEGplus
    724744                'mpc'  => array(
    725                             'pattern'   => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
     745                            'pattern'   => '^(MPCK|MP\\+|[\\x00\\x01\\x10\\x11\\x40\\x41\\x50\\x51\\x80\\x81\\x90\\x91\\xC0\\xC1\\xD0\\xD1][\\x20-\\x37][\\x00\\x20\\x40\\x60\\x80\\xA0\\xC0\\xE0])',
    726746                            'group'     => 'audio',
    727747                            'module'    => 'mpc',
     
    731751                // MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
    732752                'mp3'  => array(
    733                             'pattern'   => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
     753                            'pattern'   => '^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\x0B\\x10-\\x1B\\x20-\\x2B\\x30-\\x3B\\x40-\\x4B\\x50-\\x5B\\x60-\\x6B\\x70-\\x7B\\x80-\\x8B\\x90-\\x9B\\xA0-\\xAB\\xB0-\\xBB\\xC0-\\xCB\\xD0-\\xDB\\xE0-\\xEB\\xF0-\\xFB]',
    734754                            'group'     => 'audio',
    735755                            'module'    => 'mp3',
     
    739759                // OFR  - audio       - OptimFROG
    740760                'ofr'  => array(
    741                             'pattern'   => '^(\*RIFF|OFR)',
     761                            'pattern'   => '^(\\*RIFF|OFR)',
    742762                            'group'     => 'audio',
    743763                            'module'    => 'optimfrog',
     
    765785                // TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
    766786                'tta'  => array(
    767                             'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
     787                            'pattern'   => '^TTA',  // could also be '^TTA(\\x01|\\x02|\\x03|2|1)'
    768788                            'group'     => 'audio',
    769789                            'module'    => 'tta',
     
    800820                // ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
    801821                'asf'  => array(
    802                             'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
     822                            'pattern'   => '^\\x30\\x26\\xB2\\x75\\x8E\\x66\\xCF\\x11\\xA6\\xD9\\x00\\xAA\\x00\\x62\\xCE\\x6C',
    803823                            'group'     => 'audio-video',
    804824                            'module'    => 'asf',
     
    817837                // FLV  - audio/video - FLash Video
    818838                'flv' => array(
    819                             'pattern'   => '^FLV\x01',
     839                            'pattern'   => '^FLV[\\x01]',
    820840                            'group'     => 'audio-video',
    821841                            'module'    => 'flv',
     
    825845                // MKAV - audio/video - Mastroka
    826846                'matroska' => array(
    827                             'pattern'   => '^\x1A\x45\xDF\xA3',
     847                            'pattern'   => '^\\x1A\\x45\\xDF\\xA3',
    828848                            'group'     => 'audio-video',
    829849                            'module'    => 'matroska',
     
    833853                // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
    834854                'mpeg' => array(
    835                             'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
     855                            'pattern'   => '^\\x00\\x00\\x01[\\xB3\\xBA]',
    836856                            'group'     => 'audio-video',
    837857                            'module'    => 'mpeg',
     
    870890                            'group'     => 'audio-video',
    871891                            'module'    => 'riff',
    872                             'mime_type' => 'audio/x-wave',
     892                            'mime_type' => 'audio/x-wav',
    873893                            'fail_ape'  => 'WARNING',
    874894                        ),
     
    876896                // Real - audio/video - RealAudio, RealVideo
    877897                'real' => array(
    878                             'pattern'   => '^(\\.RMF|\\.ra)',
     898                            'pattern'   => '^\\.(RMF|ra)',
    879899                            'group'     => 'audio-video',
    880900                            'module'    => 'real',
     
    892912                // TS - audio/video - MPEG-2 Transport Stream
    893913                'ts' => array(
    894                             'pattern'   => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G".  Check for at least 10 packets matching this pattern
     914                            'pattern'   => '^(\\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G".  Check for at least 10 packets matching this pattern
    895915                            'group'     => 'audio-video',
    896916                            'module'    => 'ts',
     
    923943                // JPEG - still image - Joint Photographic Experts Group (JPEG)
    924944                'jpg'  => array(
    925                             'pattern'   => '^\xFF\xD8\xFF',
     945                            'pattern'   => '^\\xFF\\xD8\\xFF',
    926946                            'group'     => 'graphic',
    927947                            'module'    => 'jpg',
     
    933953                // PCD  - still image - Kodak Photo CD
    934954                'pcd'  => array(
    935                             'pattern'   => '^.{2048}PCD_IPI\x00',
     955                            'pattern'   => '^.{2048}PCD_IPI\\x00',
    936956                            'group'     => 'graphic',
    937957                            'module'    => 'pcd',
     
    944964                // PNG  - still image - Portable Network Graphics (PNG)
    945965                'png'  => array(
    946                             'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
     966                            'pattern'   => '^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A',
    947967                            'group'     => 'graphic',
    948968                            'module'    => 'png',
     
    955975                // SVG  - still image - Scalable Vector Graphics (SVG)
    956976                'svg'  => array(
    957                             'pattern'   => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
     977                            'pattern'   => '(<!DOCTYPE svg PUBLIC |xmlns="http://www\\.w3\\.org/2000/svg")',
    958978                            'group'     => 'graphic',
    959979                            'module'    => 'svg',
     
    966986                // TIFF - still image - Tagged Information File Format (TIFF)
    967987                'tiff' => array(
    968                             'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
     988                            'pattern'   => '^(II\\x2A\\x00|MM\\x00\\x2A)',
    969989                            'group'     => 'graphic',
    970990                            'module'    => 'tiff',
     
    977997                // EFAX - still image - eFax (TIFF derivative)
    978998                'efax'  => array(
    979                             'pattern'   => '^\xDC\xFE',
     999                            'pattern'   => '^\\xDC\\xFE',
    9801000                            'group'     => 'graphic',
    9811001                            'module'    => 'efax',
     
    10011021                // RAR  - data        - RAR compressed data
    10021022                'rar'  => array(
    1003                             'pattern'   => '^Rar\!',
     1023                            'pattern'   => '^Rar\\!',
    10041024                            'group'     => 'archive',
    10051025                            'module'    => 'rar',
     
    10111031                // SZIP - audio/data  - SZIP compressed data
    10121032                'szip' => array(
    1013                             'pattern'   => '^SZ\x0A\x04',
     1033                            'pattern'   => '^SZ\\x0A\\x04',
    10141034                            'group'     => 'archive',
    10151035                            'module'    => 'szip',
     
    10211041                // TAR  - data        - TAR compressed data
    10221042                'tar'  => array(
    1023                             'pattern'   => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
     1043                            'pattern'   => '^.{100}[0-9\\x20]{7}\\x00[0-9\\x20]{7}\\x00[0-9\\x20]{7}\\x00[0-9\\x20\\x00]{12}[0-9\\x20\\x00]{12}',
    10241044                            'group'     => 'archive',
    10251045                            'module'    => 'tar',
     
    10311051                // GZIP  - data        - GZIP compressed data
    10321052                'gz'  => array(
    1033                             'pattern'   => '^\x1F\x8B\x08',
     1053                            'pattern'   => '^\\x1F\\x8B\\x08',
    10341054                            'group'     => 'archive',
    10351055                            'module'    => 'gzip',
     
    10411061                // ZIP  - data         - ZIP compressed data
    10421062                'zip'  => array(
    1043                             'pattern'   => '^PK\x03\x04',
     1063                            'pattern'   => '^PK\\x03\\x04',
    10441064                            'group'     => 'archive',
    10451065                            'module'    => 'zip',
     
    10541074                // PAR2 - data        - Parity Volume Set Specification 2.0
    10551075                'par2' => array (
    1056                             'pattern'   => '^PAR2\x00PKT',
     1076                            'pattern'   => '^PAR2\\x00PKT',
    10571077                            'group'     => 'misc',
    10581078                            'module'    => 'par2',
     
    10641084                // PDF  - data        - Portable Document Format
    10651085                'pdf'  => array(
    1066                             'pattern'   => '^\x25PDF',
     1086                            'pattern'   => '^\\x25PDF',
    10671087                            'group'     => 'misc',
    10681088                            'module'    => 'pdf',
     
    10741094                // MSOFFICE  - data   - ZIP compressed data
    10751095                'msoffice' => array(
    1076                             'pattern'   => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
     1096                            'pattern'   => '^\\xD0\\xCF\\x11\\xE0\\xA1\\xB1\\x1A\\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
    10771097                            'group'     => 'misc',
    10781098                            'module'    => 'msoffice',
     
    11151135
    11161136
    1117         if (preg_match('#\.mp[123a]$#i', $filename)) {
     1137        if (preg_match('#\\.mp[123a]$#i', $filename)) {
    11181138            // Too many mp3 encoders on the market put gabage in front of mpeg files
    11191139            // use assume format on these if format detection failed
     
    11221142            $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
    11231143            return $info;
    1124         } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
     1144        } elseif (preg_match('#\\.cue$#i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
    11251145            // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
    11261146            // so until I think of something better, just go by filename if all other format checks fail
     
    12231243                }
    12241244
     1245                $this->CharConvert($this->info['tags'][$tag_name], $this->info[$comment_name]['encoding']);           // only copy gets converted!
     1246
    12251247                if ($this->option_tags_html) {
    12261248                    foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
    1227                         $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
     1249                        $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
    12281250                    }
    12291251                }
    12301252
    1231                 $this->CharConvert($this->info['tags'][$tag_name], $encoding);           // only copy gets converted!
    12321253            }
    12331254
     
    13531374                if (!empty($VorbisCommentError)) {
    13541375
    1355                     $this->info['warning'][]         = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
    1356                     $this->info[$algorithm.'_data']  = false;
     1376                    $this->warning('Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError);
     1377                    $this->info[$algorithm.'_data'] = false;
    13571378
    13581379                } else {
     
    15831604    }
    15841605
     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    }
     1616
    15851617}
    15861618
     
    16621694            throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
    16631695        }
    1664         return fread($this->getid3->fp, $bytes);
     1696
     1697        //return fread($this->getid3->fp, $bytes);
     1698        /*
     1699        * http://www.getid3.org/phpBB3/viewtopic.php?t=1930
     1700        * "I found out that the root cause for the problem was how getID3 uses the PHP system function fread().
     1701        * It seems to assume that fread() would always return as many bytes as were requested.
     1702        * However, according the PHP manual (http://php.net/manual/en/function.fread.php), this is the case only with regular local files, but not e.g. with Linux pipes.
     1703        * The call may return only part of the requested data and a new call is needed to get more."
     1704        */
     1705        $contents = '';
     1706        do {
     1707            $part = fread($this->getid3->fp, $bytes);
     1708            $partLength  = strlen($part);
     1709            $bytes      -= $partLength;
     1710            $contents   .= $part;
     1711        } while (($bytes > 0) && ($partLength > 0));
     1712        return $contents;
    16651713    }
    16661714
     
    17421790                // set up destination path
    17431791                $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
    1744                 if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory
     1792                if (!is_dir($dir) || !getID3::is_writable($dir)) { // check supplied directory
    17451793                    throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
    17461794                }
  • trunk/src/wp-includes/ID3/module.audio-video.asf.php

    r32979 r41196  
    267267                    $thisfile_asf_headerextensionobject['reserved_1_guid']     = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']);
    268268                    if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) {
    269                         $info['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')';
     269                        $this->warning('header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')');
    270270                        //return false;
    271271                        break;
     
    274274                    $offset += 2;
    275275                    if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) {
    276                         $info['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"';
     276                        $this->warning('header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"');
    277277                        //return false;
    278278                        break;
     
    317317                    $thisfile_asf_codeclistobject['reserved_guid']             = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']);
    318318                    if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) {
    319                         $info['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}';
     319                        $this->warning('codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}');
    320320                        //return false;
    321321                        break;
     
    350350
    351351                            if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) {
    352                                 $info['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"';
     352                                $this->warning('[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-separated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"');
    353353                            } else {
    354354
     
    413413
    414414                                    default:
    415                                         $info['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')';
     415                                        $this->warning('unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')');
    416416                                        break;
    417417                                }
     
    459459                    $thisfile_asf_scriptcommandobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']);
    460460                    if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) {
    461                         $info['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}';
     461                        $this->warning('script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}');
    462462                        //return false;
    463463                        break;
     
    518518                    $thisfile_asf_markerobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']);
    519519                    if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) {
    520                         $info['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}';
     520                        $this->warning('marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}');
    521521                        break;
    522522                    }
     
    526526                    $offset += 2;
    527527                    if ($thisfile_asf_markerobject['reserved_2'] != 0) {
    528                         $info['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"';
     528                        $this->warning('marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"');
    529529                        break;
    530530                    }
     
    577577                    $offset += 16;
    578578                    if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) {
    579                         $info['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or  "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}';
     579                        $this->warning('bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or  "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}');
    580580                        //return false;
    581581                        break;
     
    638638
    639639                        default:
    640                             $info['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or  "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}';
     640                            $this->warning('error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or  "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}');
    641641                            //return false;
    642642                            break;
     
    762762
    763763                            default:
    764                                 $info['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')';
     764                                $this->warning('extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')');
    765765                                //return false;
    766766                                break;
     
    963963                    // Implementations shall ignore any standard or non-standard object that they do not know how to handle.
    964964                    if ($this->GUIDname($NextObjectGUIDtext)) {
    965                         $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8);
     965                        $this->warning('unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8));
    966966                    } else {
    967                         $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8);
     967                        $this->warning('unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8));
    968968                    }
    969969                    $offset += ($NextObjectSize - 16 - 8);
     
    11841184                    $offset += 2;
    11851185                    if ($thisfile_asf_dataobject['reserved'] != 0x0101) {
    1186                         $info['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"';
     1186                        $this->warning('data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"');
    11871187                        //return false;
    11881188                        break;
     
    13201320                    // Implementations shall ignore any standard or non-standard object that they do not know how to handle.
    13211321                    if ($this->GUIDname($NextObjectGUIDtext)) {
    1322                         $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8);
     1322                        $this->warning('unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8));
    13231323                    } else {
    1324                         $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.($this->ftell() - 16 - 8);
     1324                        $this->warning('unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.($this->ftell() - 16 - 8));
    13251325                    }
    13261326                    $this->fseek(($NextObjectSize - 16 - 8), SEEK_CUR);
     
    14061406
    14071407                    default:
    1408                         $info['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw'];
     1408                        $this->warning('Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw']);
    14091409                        break;
    14101410
     
    19181918                    $unhandled_sections++;
    19191919                    if ($this->GUIDname($thisObject['guid_text'])) {
    1920                         $this->getid3->info['warning'][] = 'unhandled Header Extension Object GUID "'.$this->GUIDname($thisObject['guid_text']).'" {'.$thisObject['guid_text'].'} at offset '.($offset - 16 - 8);
     1920                        $this->warning('unhandled Header Extension Object GUID "'.$this->GUIDname($thisObject['guid_text']).'" {'.$thisObject['guid_text'].'} at offset '.($offset - 16 - 8));
    19211921                    } else {
    1922                         $this->getid3->info['warning'][] = 'unknown Header Extension Object GUID {'.$thisObject['guid_text'].'} in at offset '.($offset - 16 - 8);
     1922                        $this->warning('unknown Header Extension Object GUID {'.$thisObject['guid_text'].'} in at offset '.($offset - 16 - 8));
    19231923                    }
    19241924                    break;
  • trunk/src/wp-includes/ID3/module.audio-video.flv.php

    r32979 r41196  
    9494
    9595        if ($info['flv']['header']['signature'] != self::magic) {
    96             $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"';
     96            $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"');
    9797            unset($info['flv'], $info['fileformat']);
    9898            return false;
     
    542542            default:
    543543                $value = '(unknown or unsupported data type)';
    544             break;
     544                break;
    545545        }
    546546
  • trunk/src/wp-includes/ID3/module.audio-video.matroska.php

    r32979 r41196  
    235235            $this->parseEBML($info);
    236236        } catch (Exception $e) {
    237             $info['error'][] = 'EBML parser: '.$e->getMessage();
     237            $this->error('EBML parser: '.$e->getMessage());
    238238        }
    239239
     
    331331
    332332                            case 'A_AC3':
     333                            case 'A_EAC3':
    333334                            case 'A_DTS':
    334335                            case 'A_MPEG/L3':
    335336                            case 'A_MPEG/L2':
    336337                            case 'A_FLAC':
    337                                 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, true);
     338                                $module_dataformat = ($track_info['dataformat'] == 'mp2' ? 'mp3' : ($track_info['dataformat'] == 'eac3' ? 'ac3' : $track_info['dataformat']));
     339                                getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.$module_dataformat.'.php', __FILE__, true);
    338340
    339341                                if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
     
    353355
    354356                                // analyze
    355                                 $class = 'getid3_'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']);
     357                                $class = 'getid3_'.$module_dataformat;
    356358                                $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat'];
    357359                                $getid3_audio = new $class($getid3_temp, __CLASS__);
     
    458460                            default:
    459461                                $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
     462                                break;
    460463                        }
    461464
     
    525528                            default:
    526529                                $this->unhandledElement('header', __LINE__, $element_data);
     530                                break;
    527531                        }
    528532
     
    563567                                                    default:
    564568                                                        $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry);                                                }
     569                                                        break;
    565570                                            }
    566 
    567                                             if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
     571                                            if (!isset($seek_entry['target_id'])) {
     572                                                $this->warning('seek_entry[target_id] unexpectedly not set at '.$seek_entry['offset']);
     573                                                break;
     574                                            }
     575                                            if (($seek_entry['target_id'] != EBML_ID_CLUSTER) || !self::$hide_clusters) { // collect clusters only if required
    568576                                                $info['matroska']['seek'][] = $seek_entry;
    569577                                            }
     
    572580                                        default:
    573581                                            $this->unhandledElement('seekhead', __LINE__, $seek_entry);
     582                                            break;
    574583                                    }
    575584                                }
     
    654663                                                                default:
    655664                                                                    $this->unhandledElement('track.video', __LINE__, $sub_subelement);
     665                                                                    break;
    656666                                                            }
    657667                                                        }
     
    679689                                                                default:
    680690                                                                    $this->unhandledElement('track.audio', __LINE__, $sub_subelement);
     691                                                                    break;
    681692                                                            }
    682693                                                        }
     
    714725                                                                                        default:
    715726                                                                                            $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
     727                                                                                            break;
    716728                                                                                    }
    717729                                                                                }
     
    737749                                                                                        default:
    738750                                                                                            $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
     751                                                                                            break;
    739752                                                                                    }
    740753                                                                                }
     
    743756                                                                            default:
    744757                                                                                $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
     758                                                                                break;
    745759                                                                        }
    746760                                                                    }
     
    749763                                                                default:
    750764                                                                    $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
     765                                                                    break;
    751766                                                            }
    752767                                                        }
     
    755770                                                    default:
    756771                                                        $this->unhandledElement('track', __LINE__, $subelement);
     772                                                        break;
    757773                                                }
    758774                                            }
     
    763779                                        default:
    764780                                            $this->unhandledElement('tracks', __LINE__, $track_entry);
     781                                            break;
    765782                                    }
    766783                                }
     
    826843                                                    default:
    827844                                                        $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
     845                                                        break;
    828846                                                }
    829847                                            }
     
    833851                                        default:
    834852                                            $this->unhandledElement('info', __LINE__, $subelement);
     853                                            break;
    835854                                    }
    836855                                }
     
    869888                                                                default:
    870889                                                                    $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
     890                                                                    break;
    871891                                                            }
    872892                                                        }
     
    880900                                                    default:
    881901                                                        $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
     902                                                        break;
    882903                                                }
    883904                                            }
     
    887908                                        default:
    888909                                            $this->unhandledElement('cues', __LINE__, $subelement);
     910                                            break;
    889911                                    }
    890912                                }
     
    928950                                                                default:
    929951                                                                    $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
     952                                                                    break;
    930953                                                            }
    931954                                                        }
     
    939962                                                    default:
    940963                                                        $this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
     964                                                        break;
    941965                                                }
    942966                                            }
     
    946970                                        default:
    947971                                            $this->unhandledElement('tags', __LINE__, $subelement);
     972                                            break;
    948973                                    }
    949974                                }
     
    9861011                                                    default:
    9871012                                                        $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
     1013                                                        break;
    9881014                                                }
    9891015                                            }
     
    9931019                                        default:
    9941020                                            $this->unhandledElement('attachments', __LINE__, $subelement);
     1021                                            break;
    9951022                                    }
    9961023                                }
     
    10521079                                                                            default:
    10531080                                                                                $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
     1081                                                                                break;
    10541082                                                                        }
    10551083                                                                    }
     
    10711099                                                                            default:
    10721100                                                                                $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
     1101                                                                                break;
    10731102                                                                        }
    10741103                                                                    }
     
    10781107                                                                default:
    10791108                                                                    $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
     1109                                                                    break;
    10801110                                                            }
    10811111                                                        }
     
    10851115                                                    default:
    10861116                                                        $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
     1117                                                        break;
    10871118                                                }
    10881119                                            }
     
    10921123                                        default:
    10931124                                            $this->unhandledElement('chapters', __LINE__, $subelement);
     1125                                            break;
    10941126                                    }
    10951127                                }
     
    11201152                                                    default:
    11211153                                                        $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
     1154                                                        break;
    11221155                                                }
    11231156                                            }
     
    11501183                                                    default:
    11511184                                                        $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
     1185                                                        break;
    11521186                                                }
    11531187                                            }
     
    11611195                                        default:
    11621196                                            $this->unhandledElement('cluster', __LINE__, $subelement);
     1197                                            break;
    11631198                                    }
    11641199                                    $this->current_offset = $subelement['end'];
     
    11821217                            default:
    11831218                                $this->unhandledElement('segment', __LINE__, $element_data);
     1219                                break;
    11841220                        }
    11851221                    }
     
    11881224                default:
    11891225                    $this->unhandledElement('root', __LINE__, $top_element);
     1226                    break;
    11901227            }
    11911228        }
     
    13401377                default:
    13411378                    $this->unhandledElement('tag.simpletag', __LINE__, $element);
     1379                    break;
    13421380            }
    13431381        }
     
    14911529            $CodecIDlist['A_AAC/MPEG2/LC']   = 'aac';
    14921530            $CodecIDlist['A_AC3']            = 'ac3';
     1531            $CodecIDlist['A_EAC3']           = 'eac3';
    14931532            $CodecIDlist['A_DTS']            = 'dts';
    14941533            $CodecIDlist['A_FLAC']           = 'flac';
  • trunk/src/wp-includes/ID3/module.audio-video.quicktime.php

    r32979 r41196  
    3636        $offset      = 0;
    3737        $atomcounter = 0;
    38         $atom_data_read_buffer_size = ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 2) : $this->getid3->option_fread_buffer_size * 1024); // allow [default: 32MB] if PHP configured with no memory_limit
     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]
    3939        while ($offset < $info['avdataend']) {
    4040            if (!getid3_lib::intValueSupported($offset)) {
    41                 $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
     41                $this->error('Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions');
    4242                break;
    4343            }
     
    5858
    5959            if (($offset + $atomsize) > $info['avdataend']) {
    60                 $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
     60                $this->error('Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)');
    6161                return false;
    6262            }
     
    8080            $info['avdataend'] = $info['avdataend_tmp'];
    8181            unset($info['avdataend_tmp']);
     82        }
     83
     84        if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
     85            $durations = $this->quicktime_time_to_sample_table($info);
     86            for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
     87                $bookmark = array();
     88                $bookmark['title'] = $info['quicktime']['comments']['chapters'][$i];
     89                if (isset($durations[$i])) {
     90                    $bookmark['duration_sample'] = $durations[$i]['sample_duration'];
     91                    if ($i > 0) {
     92                        $bookmark['start_sample'] = $info['quicktime']['bookmarks'][($i - 1)]['start_sample'] + $info['quicktime']['bookmarks'][($i - 1)]['duration_sample'];
     93                    } else {
     94                        $bookmark['start_sample'] = 0;
     95                    }
     96                    if ($time_scale = $this->quicktime_bookmark_time_scale($info)) {
     97                        $bookmark['duration_seconds'] = $bookmark['duration_sample'] / $time_scale;
     98                        $bookmark['start_seconds']    = $bookmark['start_sample']    / $time_scale;
     99                    }
     100                }
     101                $info['quicktime']['bookmarks'][] = $bookmark;
     102            }
     103        }
     104
     105        if (isset($info['quicktime']['temp_meta_key_names'])) {
     106            unset($info['quicktime']['temp_meta_key_names']);
     107        }
     108
     109        if (!empty($info['quicktime']['comments']['location.ISO6709'])) {
     110            // https://en.wikipedia.org/wiki/ISO_6709
     111            foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
     112                $latitude  = false;
     113                $longitude = false;
     114                $altitude  = false;
     115                if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) {
     116                    @list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches;
     117
     118                    if (strlen($lat_deg) == 2) {        // [+-]DD.D
     119                        $latitude = floatval(ltrim($lat_deg, '0').$lat_deg_dec);
     120                    } elseif (strlen($lat_deg) == 4) {  // [+-]DDMM.M
     121                        $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
     122                    } elseif (strlen($lat_deg) == 6) {  // [+-]DDMMSS.S
     123                        $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
     124                    }
     125
     126                    if (strlen($lon_deg) == 3) {        // [+-]DDD.D
     127                        $longitude = floatval(ltrim($lon_deg, '0').$lon_deg_dec);
     128                    } elseif (strlen($lon_deg) == 5) {  // [+-]DDDMM.M
     129                        $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
     130                    } elseif (strlen($lon_deg) == 7) {  // [+-]DDDMMSS.S
     131                        $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
     132                    }
     133
     134                    if (strlen($alt_deg) == 3) {        // [+-]DDD.D
     135                        $altitude = floatval(ltrim($alt_deg, '0').$alt_deg_dec);
     136                    } elseif (strlen($alt_deg) == 5) {  // [+-]DDDMM.M
     137                        $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
     138                    } elseif (strlen($alt_deg) == 7) {  // [+-]DDDMMSS.S
     139                        $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
     140                    }
     141
     142                    if ($latitude !== false) {
     143                        $info['quicktime']['comments']['gps_latitude'][]  = (($lat_sign == '-') ? -1 : 1) * floatval($latitude);
     144                    }
     145                    if ($longitude !== false) {
     146                        $info['quicktime']['comments']['gps_longitude'][] = (($lon_sign == '-') ? -1 : 1) * floatval($longitude);
     147                    }
     148                    if ($altitude !== false) {
     149                        $info['quicktime']['comments']['gps_altitude'][]  = (($alt_sign == '-') ? -1 : 1) * floatval($altitude);
     150                    }
     151                }
     152                if ($latitude === false) {
     153                    $this->warning('location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug');
     154                }
     155                break;
     156            }
    82157        }
    83158
     
    99174            }
    100175        }
    101         if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
     176        if ($info['audio']['dataformat'] == 'mp4') {
    102177            $info['fileformat'] = 'mp4';
    103             $info['mime_type']  = 'audio/mp4';
    104             unset($info['video']['dataformat']);
     178            if (empty($info['video']['resolution_x'])) {
     179                $info['mime_type']  = 'audio/mp4';
     180                unset($info['video']['dataformat']);
     181            } else {
     182                $info['mime_type']  = 'video/mp4';
     183            }
    105184        }
    106185
     
    121200    public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
    122201        // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
     202        // https://code.google.com/p/mp4v2/wiki/iTunesMetadata
    123203
    124204        $info = &$this->getid3->info;
     
    223303
    224304
     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
    225345            case 'aART': // Album ARTist
     346            case 'akID': // iTunes store account type
     347            case 'apID': // Purchase Account
     348            case 'atID': //
    226349            case 'catg': // CaTeGory
     350            case 'cmID': //
     351            case 'cnID': //
    227352            case 'covr': // COVeR artwork
    228353            case 'cpil': // ComPILation
     
    231356            case 'disk': // DISK number
    232357            case 'egid': // Episode Global ID
     358            case 'geID': //
    233359            case 'gnre': // GeNRE
     360            case 'hdvd': // HD ViDeo
    234361            case 'keyw': // KEYWord
    235             case 'ldes':
     362            case 'ldes': // Long DEScription
    236363            case 'pcst': // PodCaST
    237364            case 'pgap': // GAPless Playback
     365            case 'plID': //
    238366            case 'purd': // PURchase Date
    239367            case 'purl': // Podcast URL
    240             case 'rati':
    241             case 'rndu':
    242             case 'rpdu':
     368            case 'rati': //
     369            case 'rndu': //
     370            case 'rpdu': //
    243371            case 'rtng': // RaTiNG
    244             case 'stik':
     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': //
    245380            case 'tmpo': // TeMPO (BPM)
    246381            case 'trkn': // TRacK Number
     382            case 'tven': // tvEpisodeID
    247383            case 'tves': // TV EpiSode
    248384            case 'tvnn': // TV Network Name
    249385            case 'tvsh': // TV SHow Name
    250386            case 'tvsn': // TV SeasoN
    251             case 'akID': // iTunes store account type
    252             case 'apID':
    253             case 'atID':
    254             case 'cmID':
    255             case 'cnID':
    256             case 'geID':
    257             case 'plID':
    258             case 'sfID': // iTunes store country
    259             case "\xA9".'alb': // ALBum
    260             case "\xA9".'art': // ARTist
    261             case "\xA9".'ART':
    262             case "\xA9".'aut':
    263             case "\xA9".'cmt': // CoMmenT
    264             case "\xA9".'com': // COMposer
    265             case "\xA9".'cpy':
    266             case "\xA9".'day': // content created year
    267             case "\xA9".'dir':
    268             case "\xA9".'ed1':
    269             case "\xA9".'ed2':
    270             case "\xA9".'ed3':
    271             case "\xA9".'ed4':
    272             case "\xA9".'ed5':
    273             case "\xA9".'ed6':
    274             case "\xA9".'ed7':
    275             case "\xA9".'ed8':
    276             case "\xA9".'ed9':
    277             case "\xA9".'enc':
    278             case "\xA9".'fmt':
    279             case "\xA9".'gen': // GENre
    280             case "\xA9".'grp': // GRouPing
    281             case "\xA9".'hst':
    282             case "\xA9".'inf':
    283             case "\xA9".'lyr': // LYRics
    284             case "\xA9".'mak':
    285             case "\xA9".'mod':
    286             case "\xA9".'nam': // full NAMe
    287             case "\xA9".'ope':
    288             case "\xA9".'PRD':
    289             case "\xA9".'prd':
    290             case "\xA9".'prf':
    291             case "\xA9".'req':
    292             case "\xA9".'src':
    293             case "\xA9".'swr':
    294             case "\xA9".'too': // encoder
    295             case "\xA9".'trk': // TRacK
    296             case "\xA9".'url':
    297             case "\xA9".'wrn':
    298             case "\xA9".'wrt': // WRiTer
    299             case '----': // itunes specific
    300387                if ($atom_parent == 'udta') {
    301388                    // User data atom handler
     
    319406                            $boxsmalldata =                           substr($atom_data, $atomoffset + 4, $boxsmallsize);
    320407                            if ($boxsmallsize <= 1) {
    321                                 $info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
     408                                $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
    322409                                $atom_structure['data'] = null;
    323410                                $atomoffset = strlen($atom_data);
     
    329416                                    break;
    330417                                default:
    331                                     $info['warning'][] = 'Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset;
     418                                    $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset);
    332419                                    $atom_structure['data'] = $atom_data;
    333420                                    break;
     
    341428                            $boxdata =                           substr($atom_data, $atomoffset + 8, $boxsize - 8);
    342429                            if ($boxsize <= 1) {
    343                                 $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset);
     430                                $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
    344431                                $atom_structure['data'] = null;
    345432                                $atomoffset = strlen($atom_data);
     
    362449                                            switch ($atomname) {
    363450                                                case 'cpil':
     451                                                case 'hdvd':
    364452                                                case 'pcst':
    365453                                                case 'pgap':
     454                                                    // 8-bit integer (boolean)
    366455                                                    $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
    367456                                                    break;
    368457
    369458                                                case 'tmpo':
     459                                                    // 16-bit integer
    370460                                                    $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
    371461                                                    break;
     
    373463                                                case 'disk':
    374464                                                case 'trkn':
     465                                                    // binary
    375466                                                    $num       = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
    376467                                                    $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
     
    380471
    381472                                                case 'gnre':
     473                                                    // enum
    382474                                                    $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
    383475                                                    $atom_structure['data']    = getid3_id3v1::LookupGenreName($GenreID - 1);
     
    385477
    386478                                                case 'rtng':
     479                                                    // 8-bit integer
    387480                                                    $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
    388481                                                    $atom_structure['data']    = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
     
    390483
    391484                                                case 'stik':
     485                                                    // 8-bit integer (enum)
    392486                                                    $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
    393487                                                    $atom_structure['data']    = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
     
    395489
    396490                                                case 'sfID':
     491                                                    // 32-bit integer
    397492                                                    $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
    398493                                                    $atom_structure['data']    = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
     
    404499                                                    break;
    405500
     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);
     508                                                    // not a foolproof check, but better than nothing
     509                                                    if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
     510                                                        $atom_structure['image_mime'] = 'image/jpeg';
     511                                                    } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
     512                                                        $atom_structure['image_mime'] = 'image/png';
     513                                                    } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
     514                                                        $atom_structure['image_mime'] = 'image/gif';
     515                                                    }
     516                                                    break;
     517
     518                                                case 'atID':
     519                                                case 'cnID':
     520                                                case 'geID':
     521                                                case 'tves':
     522                                                case 'tvsn':
    406523                                                default:
     524                                                    // 32-bit integer
    407525                                                    $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
    408526                                            }
     
    415533                                            if ($atomname == 'covr') {
    416534                                                // not a foolproof check, but better than nothing
    417                                                 if (preg_match('#^\xFF\xD8\xFF#', $atom_structure['data'])) {
     535                                                if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
    418536                                                    $atom_structure['image_mime'] = 'image/jpeg';
    419                                                 } elseif (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $atom_structure['data'])) {
     537                                                } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
    420538                                                    $atom_structure['image_mime'] = 'image/png';
    421539                                                } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
     
    429547
    430548                                default:
    431                                     $info['warning'][] = 'Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset;
     549                                    $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset);
    432550                                    $atom_structure['data'] = $atom_data;
    433551
     
    477595                    $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
    478596                } else {
    479                     $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset'];
     597                    $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']);
    480598                }
    481599                break;
     
    596714                    $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
    597715                } else {
    598                     $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')';
     716                    $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
    599717                }
    600718                break;
     
    605723                $atom_structure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atom_data,  1, 3)); // hardcoded: 0x0000
    606724                $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);
     734                    } 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
    607739                $stsdEntriesDataOffset = 8;
    608740                for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
     
    802934                $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
    803935                if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
    804                     $info['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).';
     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).');
    805937                }
    806938                for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
     
    9291061                    $atom_structure['data_references'][$i]['size']                    = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
    9301062                    $drefDataOffset += 4;
    931                     $atom_structure['data_references'][$i]['type']                    =               substr($atom_data, $drefDataOffset, 4);
     1063                    $atom_structure['data_references'][$i]['type']                    =                           substr($atom_data, $drefDataOffset, 4);
    9321064                    $drefDataOffset += 4;
    9331065                    $atom_structure['data_references'][$i]['version']                 = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 1));
     
    9351067                    $atom_structure['data_references'][$i]['flags_raw']               = getid3_lib::BigEndian2Int(substr($atom_data,  $drefDataOffset, 3)); // hardcoded: 0x0000
    9361068                    $drefDataOffset += 3;
    937                     $atom_structure['data_references'][$i]['data']                    =               substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
     1069                    $atom_structure['data_references'][$i]['data']                    =                           substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
    9381070                    $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
    9391071
     
    10021134
    10031135                if ($atom_structure['time_scale'] == 0) {
    1004                     $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
     1136                    $this->error('Corrupt Quicktime file: mdhd.time_scale == zero');
    10051137                    return false;
    10061138                }
    1007                 $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
     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']);
    10081140
    10091141                $atom_structure['creation_time_unix']    = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
     
    10201152                $atom_structure['modification_date']      = getid3_lib::BigEndian2Int(substr($atom_data,  0, 4)); // "standard Macintosh format"
    10211153                $atom_structure['version_number']         = getid3_lib::BigEndian2Int(substr($atom_data,  4, 2)); // hardcoded: 0x00
    1022                 $atom_structure['atom_type']              =               substr($atom_data,  6, 4);        // usually: 'PICT'
     1154                $atom_structure['atom_type']              =                           substr($atom_data,  6, 4);        // usually: 'PICT'
    10231155                $atom_structure['atom_index']             = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
    10241156
     
    10301162                $atom_structure['region_size']   = getid3_lib::BigEndian2Int(substr($atom_data,  0, 2)); // The Region size, Region boundary box,
    10311163                $atom_structure['boundary_box']  = getid3_lib::BigEndian2Int(substr($atom_data,  2, 8)); // and Clipping region data fields
    1032                 $atom_structure['clipping_data'] =               substr($atom_data, 10);           // constitute a QuickDraw region.
     1164                $atom_structure['clipping_data'] =                           substr($atom_data, 10);           // constitute a QuickDraw region.
    10331165                break;
    10341166
     
    11161248
    11171249                if ($atom_structure['time_scale'] == 0) {
    1118                     $info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
     1250                    $this->error('Corrupt Quicktime file: mvhd.time_scale == zero');
    11191251                    return false;
    11201252                }
    11211253                $atom_structure['creation_time_unix']        = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
    11221254                $atom_structure['modify_time_unix']          = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
    1123                 $info['quicktime']['time_scale']    = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
     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']);
    11241256                $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
    11251257                $info['playtime_seconds']           = $atom_structure['duration'] / $atom_structure['time_scale'];
     
    12411373
    12421374                // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
    1243                 while  (($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
     1375                while (($mdat_offset < (strlen($atom_data) - 8))
     1376                    && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
    12441377                    && ($chapter_string_length < 1000)
    12451378                    && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
    1246                     && preg_match('#^[\x20-\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) {
     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;
    12471381                        $mdat_offset += (2 + $chapter_string_length);
    1248                         @$info['quicktime']['comments']['chapters'][] = $chapter_matches[0];
    1249                 }
    1250 
     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;
     1387                        }
     1388                }
    12511389
    12521390
     
    12661404                        if (!empty($getid3_temp->info['warning'])) {
    12671405                            foreach ($getid3_temp->info['warning'] as $value) {
    1268                                 $info['warning'][] = $value;
     1406                                $this->warning($value);
    12691407                            }
    12701408                        }
     
    13691507                    }
    13701508                } else {
    1371                     $info['warning'][] = 'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.';
     1509                    $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
    13721510                }
    13731511                break;
     
    13981536
    13991537            case "\x00\x00\x00\x00":
    1400             case 'meta': // METAdata atom
    14011538                // some kind of metacontainer, may contain a big data dump such as:
    14021539                // 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
    14031540                // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
    14041541
    1405                 $atom_structure['version']   =          getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
    1406                 $atom_structure['flags_raw'] =          getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
    1407                 $atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
     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);
    14081545                //$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
    14091546                break;
    14101547
     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
    14111556            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
    14121558                // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
    14131559                $atom_structure['language'] =                           substr($atom_data, 4 + 0, 2);
    14141560                $atom_structure['unknown']  = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
    14151561                $atom_structure['data']     =                           substr($atom_data, 4 + 4);
    1416                 break;
     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;
    14171712
    14181713            default:
    1419                 $info['warning'][] = 'Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset;
     1714                $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset);
    14201715                $atom_structure['data'] = $atom_data;
    14211716                break;
     
    14411736                // terminated by a 32-bit integer set to 0. If you are writing a program
    14421737                // to read user data atoms, you should allow for the terminating 0.
     1738                if (strlen($atom_data) > 12) {
     1739                    $subatomoffset += 4;
     1740                    continue;
     1741                }
    14431742                return $atom_structure;
    14441743            }
     
    17542053        if (empty($QuicktimeIODSaudioProfileNameLookup)) {
    17552054            $QuicktimeIODSaudioProfileNameLookup = array(
    1756                 0x00 => 'ISO Reserved (0x00)',
    1757                 0x01 => 'Main Audio Profile @ Level 1',
    1758                 0x02 => 'Main Audio Profile @ Level 2',
    1759                 0x03 => 'Main Audio Profile @ Level 3',
    1760                 0x04 => 'Main Audio Profile @ Level 4',
    1761                 0x05 => 'Scalable Audio Profile @ Level 1',
    1762                 0x06 => 'Scalable Audio Profile @ Level 2',
    1763                 0x07 => 'Scalable Audio Profile @ Level 3',
    1764                 0x08 => 'Scalable Audio Profile @ Level 4',
    1765                 0x09 => 'Speech Audio Profile @ Level 1',
    1766                 0x0A => 'Speech Audio Profile @ Level 2',
    1767                 0x0B => 'Synthetic Audio Profile @ Level 1',
    1768                 0x0C => 'Synthetic Audio Profile @ Level 2',
    1769                 0x0D => 'Synthetic Audio Profile @ Level 3',
    1770                 0x0E => 'High Quality Audio Profile @ Level 1',
    1771                 0x0F => 'High Quality Audio Profile @ Level 2',
    1772                 0x10 => 'High Quality Audio Profile @ Level 3',
    1773                 0x11 => 'High Quality Audio Profile @ Level 4',
    1774                 0x12 => 'High Quality Audio Profile @ Level 5',
    1775                 0x13 => 'High Quality Audio Profile @ Level 6',
    1776                 0x14 => 'High Quality Audio Profile @ Level 7',
    1777                 0x15 => 'High Quality Audio Profile @ Level 8',
    1778                 0x16 => 'Low Delay Audio Profile @ Level 1',
    1779                 0x17 => 'Low Delay Audio Profile @ Level 2',
    1780                 0x18 => 'Low Delay Audio Profile @ Level 3',
    1781                 0x19 => 'Low Delay Audio Profile @ Level 4',
    1782                 0x1A => 'Low Delay Audio Profile @ Level 5',
    1783                 0x1B => 'Low Delay Audio Profile @ Level 6',
    1784                 0x1C => 'Low Delay Audio Profile @ Level 7',
    1785                 0x1D => 'Low Delay Audio Profile @ Level 8',
    1786                 0x1E => 'Natural Audio Profile @ Level 1',
    1787                 0x1F => 'Natural Audio Profile @ Level 2',
    1788                 0x20 => 'Natural Audio Profile @ Level 3',
    1789                 0x21 => 'Natural Audio Profile @ Level 4',
    1790                 0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
    1791                 0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
    1792                 0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
    1793                 0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
    1794                 0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
    1795                 0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
    1796                 0x28 => 'AAC Profile @ Level 1',
    1797                 0x29 => 'AAC Profile @ Level 2',
    1798                 0x2A => 'AAC Profile @ Level 4',
    1799                 0x2B => 'AAC Profile @ Level 5',
    1800                 0x2C => 'High Efficiency AAC Profile @ Level 2',
    1801                 0x2D => 'High Efficiency AAC Profile @ Level 3',
    1802                 0x2E => 'High Efficiency AAC Profile @ Level 4',
    1803                 0x2F => 'High Efficiency AAC Profile @ Level 5',
    1804                 0xFE => 'Not part of MPEG-4 audio profiles',
    1805                 0xFF => 'No audio capability required',
     2055                0x00 => 'ISO Reserved (0x00)',
     2056                0x01 => 'Main Audio Profile @ Level 1',
     2057                0x02 => 'Main Audio Profile @ Level 2',
     2058                0x03 => 'Main Audio Profile @ Level 3',
     2059                0x04 => 'Main Audio Profile @ Level 4',
     2060                0x05 => 'Scalable Audio Profile @ Level 1',
     2061                0x06 => 'Scalable Audio Profile @ Level 2',
     2062                0x07 => 'Scalable Audio Profile @ Level 3',
     2063                0x08 => 'Scalable Audio Profile @ Level 4',
     2064                0x09 => 'Speech Audio Profile @ Level 1',
     2065                0x0A => 'Speech Audio Profile @ Level 2',
     2066                0x0B => 'Synthetic Audio Profile @ Level 1',
     2067                0x0C => 'Synthetic Audio Profile @ Level 2',
     2068                0x0D => 'Synthetic Audio Profile @ Level 3',
     2069                0x0E => 'High Quality Audio Profile @ Level 1',
     2070                0x0F => 'High Quality Audio Profile @ Level 2',
     2071                0x10 => 'High Quality Audio Profile @ Level 3',
     2072                0x11 => 'High Quality Audio Profile @ Level 4',
     2073                0x12 => 'High Quality Audio Profile @ Level 5',
     2074                0x13 => 'High Quality Audio Profile @ Level 6',
     2075                0x14 => 'High Quality Audio Profile @ Level 7',
     2076                0x15 => 'High Quality Audio Profile @ Level 8',
     2077                0x16 => 'Low Delay Audio Profile @ Level 1',
     2078                0x17 => 'Low Delay Audio Profile @ Level 2',
     2079                0x18 => 'Low Delay Audio Profile @ Level 3',
     2080                0x19 => 'Low Delay Audio Profile @ Level 4',
     2081                0x1A => 'Low Delay Audio Profile @ Level 5',
     2082                0x1B => 'Low Delay Audio Profile @ Level 6',
     2083                0x1C => 'Low Delay Audio Profile @ Level 7',
     2084                0x1D => 'Low Delay Audio Profile @ Level 8',
     2085                0x1E => 'Natural Audio Profile @ Level 1',
     2086                0x1F => 'Natural Audio Profile @ Level 2',
     2087                0x20 => 'Natural Audio Profile @ Level 3',
     2088                0x21 => 'Natural Audio Profile @ Level 4',
     2089                0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
     2090                0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
     2091                0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
     2092                0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
     2093                0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
     2094                0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
     2095                0x28 => 'AAC Profile @ Level 1',
     2096                0x29 => 'AAC Profile @ Level 2',
     2097                0x2A => 'AAC Profile @ Level 4',
     2098                0x2B => 'AAC Profile @ Level 5',
     2099                0x2C => 'High Efficiency AAC Profile @ Level 2',
     2100                0x2D => 'High Efficiency AAC Profile @ Level 3',
     2101                0x2E => 'High Efficiency AAC Profile @ Level 4',
     2102                0x2F => 'High Efficiency AAC Profile @ Level 5',
     2103                0xFE => 'Not part of MPEG-4 audio profiles',
     2104                0xFF => 'No audio capability required',
    18062105            );
    18072106        }
     
    21122411        static $handyatomtranslatorarray = array();
    21132412        if (empty($handyatomtranslatorarray)) {
     2413            // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
     2414            // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
     2415            // http://atomicparsley.sourceforge.net/mpeg-4files.html
     2416            // https://code.google.com/p/mp4v2/wiki/iTunesMetadata
     2417            $handyatomtranslatorarray["\xA9".'alb'] = 'album';               // iTunes 4.0
     2418            $handyatomtranslatorarray["\xA9".'ART'] = 'artist';
     2419            $handyatomtranslatorarray["\xA9".'art'] = 'artist';              // iTunes 4.0
     2420            $handyatomtranslatorarray["\xA9".'aut'] = 'author';
     2421            $handyatomtranslatorarray["\xA9".'cmt'] = 'comment';             // iTunes 4.0
     2422            $handyatomtranslatorarray["\xA9".'com'] = 'comment';
    21142423            $handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
    2115             $handyatomtranslatorarray["\xA9".'day'] = 'creation_date';    // iTunes 4.0
     2424            $handyatomtranslatorarray["\xA9".'day'] = 'creation_date';       // iTunes 4.0
    21162425            $handyatomtranslatorarray["\xA9".'dir'] = 'director';
    21172426            $handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
     
    21242433            $handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
    21252434            $handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
     2435            $handyatomtranslatorarray["\xA9".'enc'] = 'encoded_by';
    21262436            $handyatomtranslatorarray["\xA9".'fmt'] = 'format';
     2437            $handyatomtranslatorarray["\xA9".'gen'] = 'genre';               // iTunes 4.0
     2438            $handyatomtranslatorarray["\xA9".'grp'] = 'grouping';            // iTunes 4.2
     2439            $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
    21272440            $handyatomtranslatorarray["\xA9".'inf'] = 'information';
     2441            $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics';              // iTunes 5.0
     2442            $handyatomtranslatorarray["\xA9".'mak'] = 'make';
     2443            $handyatomtranslatorarray["\xA9".'mod'] = 'model';
     2444            $handyatomtranslatorarray["\xA9".'nam'] = 'title';               // iTunes 4.0
     2445            $handyatomtranslatorarray["\xA9".'ope'] = 'composer';
    21282446            $handyatomtranslatorarray["\xA9".'prd'] = 'producer';
     2447            $handyatomtranslatorarray["\xA9".'PRD'] = 'product';
    21292448            $handyatomtranslatorarray["\xA9".'prf'] = 'performers';
    21302449            $handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
    21312450            $handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
    2132             $handyatomtranslatorarray["\xA9".'wrt'] = 'writer';
    2133 
    2134             // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
    2135             $handyatomtranslatorarray["\xA9".'nam'] = 'title';           // iTunes 4.0
    2136             $handyatomtranslatorarray["\xA9".'cmt'] = 'comment';         // iTunes 4.0
     2451            $handyatomtranslatorarray["\xA9".'swr'] = 'software';
     2452            $handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool';       // iTunes 4.0
     2453            $handyatomtranslatorarray["\xA9".'trk'] = 'track';
     2454            $handyatomtranslatorarray["\xA9".'url'] = 'url';
    21372455            $handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
    2138             $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
    2139             $handyatomtranslatorarray["\xA9".'mak'] = 'make';
    2140             $handyatomtranslatorarray["\xA9".'mod'] = 'model';
    2141             $handyatomtranslatorarray["\xA9".'PRD'] = 'product';
    2142             $handyatomtranslatorarray["\xA9".'swr'] = 'software';
    2143             $handyatomtranslatorarray["\xA9".'aut'] = 'author';
    2144             $handyatomtranslatorarray["\xA9".'ART'] = 'artist';
    2145             $handyatomtranslatorarray["\xA9".'trk'] = 'track';
    2146             $handyatomtranslatorarray["\xA9".'alb'] = 'album';           // iTunes 4.0
    2147             $handyatomtranslatorarray["\xA9".'com'] = 'comment';
    2148             $handyatomtranslatorarray["\xA9".'gen'] = 'genre';           // iTunes 4.0
    2149             $handyatomtranslatorarray["\xA9".'ope'] = 'composer';
    2150             $handyatomtranslatorarray["\xA9".'url'] = 'url';
    2151             $handyatomtranslatorarray["\xA9".'enc'] = 'encoder';
    2152 
    2153             // http://atomicparsley.sourceforge.net/mpeg-4files.html
    2154             $handyatomtranslatorarray["\xA9".'art'] = 'artist';           // iTunes 4.0
     2456            $handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
    21552457            $handyatomtranslatorarray['aART'] = 'album_artist';
    2156             $handyatomtranslatorarray['trkn'] = 'track_number';     // iTunes 4.0
    2157             $handyatomtranslatorarray['disk'] = 'disc_number';      // iTunes 4.0
    2158             $handyatomtranslatorarray['gnre'] = 'genre';            // iTunes 4.0
    2159             $handyatomtranslatorarray["\xA9".'too'] = 'encoder';          // iTunes 4.0
    2160             $handyatomtranslatorarray['tmpo'] = 'bpm';              // iTunes 4.0
    2161             $handyatomtranslatorarray['cprt'] = 'copyright';        // iTunes 4.0?
    2162             $handyatomtranslatorarray['cpil'] = 'compilation';      // iTunes 4.0
    2163             $handyatomtranslatorarray['covr'] = 'picture';          // iTunes 4.0
    2164             $handyatomtranslatorarray['rtng'] = 'rating';           // iTunes 4.0
    2165             $handyatomtranslatorarray["\xA9".'grp'] = 'grouping';         // iTunes 4.2
    2166             $handyatomtranslatorarray['stik'] = 'stik';             // iTunes 4.9
    2167             $handyatomtranslatorarray['pcst'] = 'podcast';          // iTunes 4.9
    2168             $handyatomtranslatorarray['catg'] = 'category';         // iTunes 4.9
    2169             $handyatomtranslatorarray['keyw'] = 'keyword';          // iTunes 4.9
    2170             $handyatomtranslatorarray['purl'] = 'podcast_url';      // iTunes 4.9
    2171             $handyatomtranslatorarray['egid'] = 'episode_guid';     // iTunes 4.9
    2172             $handyatomtranslatorarray['desc'] = 'description';      // iTunes 5.0
    2173             $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics';           // iTunes 5.0
    2174             $handyatomtranslatorarray['tvnn'] = 'tv_network_name';  // iTunes 6.0
    2175             $handyatomtranslatorarray['tvsh'] = 'tv_show_name';     // iTunes 6.0
    2176             $handyatomtranslatorarray['tvsn'] = 'tv_season';        // iTunes 6.0
    2177             $handyatomtranslatorarray['tves'] = 'tv_episode';       // iTunes 6.0
    2178             $handyatomtranslatorarray['purd'] = 'purchase_date';    // iTunes 6.0.2
    2179             $handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
    2180 
    2181             // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
    2182 
    2183 
     2458            $handyatomtranslatorarray['apID'] = 'purchase_account';
     2459            $handyatomtranslatorarray['catg'] = 'category';            // iTunes 4.9
     2460            $handyatomtranslatorarray['covr'] = 'picture';             // iTunes 4.0
     2461            $handyatomtranslatorarray['cpil'] = 'compilation';         // iTunes 4.0
     2462            $handyatomtranslatorarray['cprt'] = 'copyright';           // iTunes 4.0?
     2463            $handyatomtranslatorarray['desc'] = 'description';         // iTunes 5.0
     2464            $handyatomtranslatorarray['disk'] = 'disc_number';         // iTunes 4.0
     2465            $handyatomtranslatorarray['egid'] = 'episode_guid';        // iTunes 4.9
     2466            $handyatomtranslatorarray['gnre'] = 'genre';               // iTunes 4.0
     2467            $handyatomtranslatorarray['hdvd'] = 'hd_video';            // iTunes 4.0
     2468            $handyatomtranslatorarray['ldes'] = 'description_long';    //
     2469            $handyatomtranslatorarray['keyw'] = 'keyword';             // iTunes 4.9
     2470            $handyatomtranslatorarray['pcst'] = 'podcast';             // iTunes 4.9
     2471            $handyatomtranslatorarray['pgap'] = 'gapless_playback';    // iTunes 7.0
     2472            $handyatomtranslatorarray['purd'] = 'purchase_date';       // iTunes 6.0.2
     2473            $handyatomtranslatorarray['purl'] = 'podcast_url';         // iTunes 4.9
     2474            $handyatomtranslatorarray['rtng'] = 'rating';              // iTunes 4.0
     2475            $handyatomtranslatorarray['soaa'] = 'sort_album_artist';   //
     2476            $handyatomtranslatorarray['soal'] = 'sort_album';          //
     2477            $handyatomtranslatorarray['soar'] = 'sort_artist';         //
     2478            $handyatomtranslatorarray['soco'] = 'sort_composer';       //
     2479            $handyatomtranslatorarray['sonm'] = 'sort_title';          //
     2480            $handyatomtranslatorarray['sosn'] = 'sort_show';           //
     2481            $handyatomtranslatorarray['stik'] = 'stik';                // iTunes 4.9
     2482            $handyatomtranslatorarray['tmpo'] = 'bpm';                 // iTunes 4.0
     2483            $handyatomtranslatorarray['trkn'] = 'track_number';        // iTunes 4.0
     2484            $handyatomtranslatorarray['tven'] = 'tv_episode_id';       //
     2485            $handyatomtranslatorarray['tves'] = 'tv_episode';          // iTunes 6.0
     2486            $handyatomtranslatorarray['tvnn'] = 'tv_network_name';     // iTunes 6.0
     2487            $handyatomtranslatorarray['tvsh'] = 'tv_show_name';        // iTunes 6.0
     2488            $handyatomtranslatorarray['tvsn'] = 'tv_season';           // iTunes 6.0
    21842489
    21852490            // boxnames:
     
    22262531                }
    22272532            }
    2228             $info['quicktime']['comments'][$comment_key][] = $data;
     2533            $gooddata = array($data);
     2534            if ($comment_key == 'genre') {
     2535                // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
     2536                $gooddata = explode(';', $data);
     2537            }
     2538            foreach ($gooddata as $data) {
     2539                $info['quicktime']['comments'][$comment_key][] = $data;
     2540            }
    22292541        }
    22302542        return true;
    22312543    }
     2544
     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        }
    22322577
    22332578    public function NoNullString($nullterminatedstring) {
     
    22442589    }
    22452590
     2591
     2592    /*
     2593    // helper functions for m4b audiobook chapters
     2594    // code by Steffen Hartmann 2015-Nov-08
     2595    */
     2596    public function search_tag_by_key($info, $tag, $history, &$result) {
     2597        foreach ($info as $key => $value) {
     2598            $key_history = $history.'/'.$key;
     2599            if ($key === $tag) {
     2600                $result[] = array($key_history, $info);
     2601            } else {
     2602                if (is_array($value)) {
     2603                    $this->search_tag_by_key($value, $tag, $key_history, $result);
     2604                }
     2605            }
     2606        }
     2607    }
     2608
     2609    public function search_tag_by_pair($info, $k, $v, $history, &$result) {
     2610        foreach ($info as $key => $value) {
     2611            $key_history = $history.'/'.$key;
     2612            if (($key === $k) && ($value === $v)) {
     2613                $result[] = array($key_history, $info);
     2614            } else {
     2615                if (is_array($value)) {
     2616                    $this->search_tag_by_pair($value, $k, $v, $key_history, $result);
     2617                }
     2618            }
     2619        }
     2620    }
     2621
     2622    public function quicktime_time_to_sample_table($info) {
     2623        $res = array();
     2624        $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
     2625        foreach ($res as $value) {
     2626            $stbl_res = array();
     2627            $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
     2628            if (count($stbl_res) > 0) {
     2629                $stts_res = array();
     2630                $this->search_tag_by_key($value[1], 'time_to_sample_table', $value[0], $stts_res);
     2631                if (count($stts_res) > 0) {
     2632                    return $stts_res[0][1]['time_to_sample_table'];
     2633                }
     2634            }
     2635        }
     2636        return array();
     2637    }
     2638
     2639    function quicktime_bookmark_time_scale($info) {
     2640        $time_scale = '';
     2641        $ts_prefix_len = 0;
     2642        $res = array();
     2643        $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
     2644        foreach ($res as $value) {
     2645            $stbl_res = array();
     2646            $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
     2647            if (count($stbl_res) > 0) {
     2648                $ts_res = array();
     2649                $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);
     2652                    if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) {
     2653                        $time_scale = $value[1]['time_scale'];
     2654                        $ts_prefix_len = strlen($prefix);
     2655                    }
     2656                }
     2657            }
     2658        }
     2659        return $time_scale;
     2660    }
     2661    /*
     2662    // END helper functions for m4b audiobook chapters
     2663    */
     2664
     2665
    22462666}
  • trunk/src/wp-includes/ID3/module.audio-video.riff.php

    r29734 r41196  
    191191                    $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
    192192                    if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
    193                         $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
     193                        $this->error('Corrupt RIFF file: bitrate_audio == zero');
    194194                        return false;
    195195                    }
     
    200200                    $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
    201201                    if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
    202                         $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
     202                        $this->warning('Audio codec = '.$thisfile_audio['codec']);
    203203                    }
    204204                    $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
     
    303303                            $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']);
    304304                        } else {
    305                             $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
     305                            $this->warning('RIFF.WAVE.BEXT.origin_time is invalid');
    306306                        }
    307307                    } else {
    308                         $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
     308                        $this->warning('RIFF.WAVE.BEXT.origin_date is invalid');
    309309                    }
    310310                    $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
     
    386386
    387387                        if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
    388                             $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
     388                            $this->warning('RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')');
    389389                            break;
    390390                        } elseif ($SNDM_thisTagSize <= 0) {
    391                             $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
     391                            $this->warning('RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')');
    392392                            break;
    393393                        }
     
    398398                            $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
    399399                        } else {
    400                             $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
     400                            $this->warning('RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')');
    401401                        }
    402402                    }
     
    429429                        if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
    430430                            $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
    431                             $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
     431                            $timestamp_sample_rate = (is_array($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) ? max($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) : $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']); // XML could possibly contain more than one TIMESTAMP_SAMPLE_RATE tag, returning as array instead of integer [why? does it make sense? perhaps doesn't matter but getID3 needs to deal with it] - see https://github.com/JamesHeinrich/getID3/issues/105
     432                            $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $timestamp_sample_rate;
    432433                            $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds']       / 3600);
    433434                            $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600))      / 60);
     
    436437                            $thisfile_riff_WAVE['iXML'][0]['timecode_string']       = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s,       $f);
    437438                            $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d',   $h, $m, $s, round($f));
     439                            unset($samples_since_midnight, $timestamp_sample_rate, $h, $m, $s, $f);
    438440                        }
    439441                        unset($parsedXML);
     
    571573                            } else {
    572574                                // Short by more than one byte, throw warning
    573                                 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
     575                                $this->warning('Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)');
    574576                                $info['avdataend'] = $info['filesize'];
    575577                            }
     
    580582                                // output file appears to be incorrectly *not* padded to nearest WORD boundary
    581583                                // Output less severe warning
    582                                 $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
     584                                $this->warning('File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)');
    583585                                $info['avdataend'] = $info['filesize'];
    584586                            } else {
    585587                                // Short by more than one byte, throw warning
    586                                 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
     588                                $this->warning('Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)');
    587589                                $info['avdataend'] = $info['filesize'];
    588590                            }
     
    593595                    if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
    594596                        $info['avdataend']--;
    595                         $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
     597                        $this->warning('Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored');
    596598                    }
    597599                }
     
    620622                    }
    621623                    if ($info['avdataend'] > $info['filesize']) {
    622                         $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)';
     624                        $this->warning('Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)');
    623625                        $info['avdataend'] = $info['filesize'];
    624626                    }
     
    661663                    $thisfile_riff_raw_avih['dwMicroSecPerFrame']    = $this->EitherEndian2Int(substr($avihData,  0, 4)); // frame display rate (or 0L)
    662664                    if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
    663                         $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
     665                        $this->error('Corrupt RIFF file: avih.dwMicroSecPerFrame == zero');
    664666                        return false;
    665667                    }
     
    859861
    860862                                        default:
    861                                             $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
     863                                            $this->warning('Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"');
    862864                                            break;
    863865
     
    964966                            // forget to pad end of file to make this actually work
    965967                        } else {
    966                             $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
     968                            $this->warning('Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found');
    967969                        }
    968970                        $info['avdataend'] = $info['filesize'];
     
    10211023                    $thisfile_audio['sample_rate']     = $thisfile_riff_audio['sample_rate'];
    10221024                    if ($thisfile_audio['sample_rate'] == 0) {
    1023                         $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
     1025                        $this->error('Corrupted AIFF file: sample_rate == zero');
    10241026                        return false;
    10251027                    }
     
    10811083                    $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
    10821084                    if ($info['avdataend'] > $info['filesize']) {
    1083                         $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
     1085                        $this->warning('Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found');
    10841086                    }
    10851087                }
     
    11131115
    11141116                        default:
    1115                             $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
     1117                            $this->warning('Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"');
    11161118                            break;
    11171119                    }
     
    11311133
    11321134                        default:
    1133                             $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
     1135                            $this->warning('Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"');
    11341136                            break;
    11351137                    }
     
    11711173                break;
    11721174
     1175            case 'WEBP':
     1176                // https://developers.google.com/speed/webp/docs/riff_container
     1177                // https://tools.ietf.org/html/rfc6386
     1178                // https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
     1179                $info['fileformat'] = 'webp';
     1180                $info['mime_type']  = 'image/webp';
     1181
     1182                if (!empty($thisfile_riff['WEBP']['VP8 '][0]['size'])) {
     1183                    $old_offset = $this->ftell();
     1184                    $this->fseek($thisfile_riff['WEBP']['VP8 '][0]['offset'] + 8); // 4 bytes "VP8 " + 4 bytes chunk size
     1185                    $WEBP_VP8_header = $this->fread(10);
     1186                    $this->fseek($old_offset);
     1187                    if (substr($WEBP_VP8_header, 3, 3) == "\x9D\x01\x2A") {
     1188                        $thisfile_riff['WEBP']['VP8 '][0]['keyframe']   = !(getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x800000);
     1189                        $thisfile_riff['WEBP']['VP8 '][0]['version']    =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x700000) >> 20;
     1190                        $thisfile_riff['WEBP']['VP8 '][0]['show_frame'] =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x080000);
     1191                        $thisfile_riff['WEBP']['VP8 '][0]['data_bytes'] =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 0, 3)) & 0x07FFFF) >>  0;
     1192
     1193                        $thisfile_riff['WEBP']['VP8 '][0]['scale_x']    =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 6, 2)) & 0xC000) >> 14;
     1194                        $thisfile_riff['WEBP']['VP8 '][0]['width']      =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 6, 2)) & 0x3FFF);
     1195                        $thisfile_riff['WEBP']['VP8 '][0]['scale_y']    =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 8, 2)) & 0xC000) >> 14;
     1196                        $thisfile_riff['WEBP']['VP8 '][0]['height']     =  (getid3_lib::LittleEndian2Int(substr($WEBP_VP8_header, 8, 2)) & 0x3FFF);
     1197
     1198                        $info['video']['resolution_x'] = $thisfile_riff['WEBP']['VP8 '][0]['width'];
     1199                        $info['video']['resolution_y'] = $thisfile_riff['WEBP']['VP8 '][0]['height'];
     1200                    } else {
     1201                        $this->error('Expecting 9D 01 2A at offset '.($thisfile_riff['WEBP']['VP8 '][0]['offset'] + 8 + 3).', found "'.getid3_lib::PrintHexBytes(substr($WEBP_VP8_header, 3, 3)).'"');
     1202                    }
     1203
     1204                }
     1205                if (!empty($thisfile_riff['WEBP']['VP8L'][0]['size'])) {
     1206                    $old_offset = $this->ftell();
     1207                    $this->fseek($thisfile_riff['WEBP']['VP8L'][0]['offset'] + 8); // 4 bytes "VP8L" + 4 bytes chunk size
     1208                    $WEBP_VP8L_header = $this->fread(10);
     1209                    $this->fseek($old_offset);
     1210                    if (substr($WEBP_VP8L_header, 0, 1) == "\x2F") {
     1211                        $width_height_flags = getid3_lib::LittleEndian2Bin(substr($WEBP_VP8L_header, 1, 4));
     1212                        $thisfile_riff['WEBP']['VP8L'][0]['width']         =        bindec(substr($width_height_flags, 18, 14)) + 1;
     1213                        $thisfile_riff['WEBP']['VP8L'][0]['height']        =        bindec(substr($width_height_flags,  4, 14)) + 1;
     1214                        $thisfile_riff['WEBP']['VP8L'][0]['alpha_is_used'] = (bool) bindec(substr($width_height_flags,  3,  1));
     1215                        $thisfile_riff['WEBP']['VP8L'][0]['version']       =        bindec(substr($width_height_flags,  0,  3));
     1216
     1217                        $info['video']['resolution_x'] = $thisfile_riff['WEBP']['VP8L'][0]['width'];
     1218                        $info['video']['resolution_y'] = $thisfile_riff['WEBP']['VP8L'][0]['height'];
     1219                    } else {
     1220                        $this->error('Expecting 2F at offset '.($thisfile_riff['WEBP']['VP8L'][0]['offset'] + 8).', found "'.getid3_lib::PrintHexBytes(substr($WEBP_VP8L_header, 0, 1)).'"');
     1221                    }
     1222
     1223                }
     1224                break;
    11731225
    11741226            default:
    1175                 $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
     1227                $this->error('Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA|WEBP), found "'.$RIFFsubtype.'" instead');
    11761228                //unset($info['fileformat']);
    11771229        }
     
    11861238                    if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
    11871239                        $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
    1188                         $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
     1240                        $this->warning('mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"');
    11891241                    }
    11901242                }
     
    15101562                                            if (!empty($getid3_temp->info['warning'])) {
    15111563                                                foreach ($getid3_temp->info['warning'] as $key => $value) {
    1512                                                     $info['warning'][] = $value;
     1564                                                    $this->warning($value);
    15131565                                                }
    15141566                                            }
  • trunk/src/wp-includes/ID3/module.audio.ac3.php

    r32979 r41196  
    2121    private $BSIoffset = 0;
    2222
    23     const syncword = "\x0B\x77";
     23    const syncword = 0x0B77;
    2424
    2525    public function Analyze() {
     
    5656
    5757        $this->fseek($info['avdataoffset']);
    58         $this->AC3header['syncinfo'] = $this->fread(5);
    59 
    60         if (strpos($this->AC3header['syncinfo'], self::syncword) === 0) {
    61             $thisfile_ac3_raw['synchinfo']['synchword'] = self::syncword;
    62             $offset = 2;
    63         } else {
     58        $tempAC3header = $this->fread(100); // should be enough to cover all data, there are some variable-length fields...?
     59        $this->AC3header['syncinfo']  =     getid3_lib::BigEndian2Int(substr($tempAC3header, 0, 2));
     60        $this->AC3header['bsi']       =     getid3_lib::BigEndian2Bin(substr($tempAC3header, 2));
     61        $thisfile_ac3_raw_bsi['bsid'] = (getid3_lib::LittleEndian2Int(substr($tempAC3header, 5, 1)) & 0xF8) >> 3; // AC3 and E-AC3 put the "bsid" version identifier in the same place, but unfortnately the 4 bytes between the syncword and the version identifier are interpreted differently, so grab it here so the following code structure can make sense
     62        unset($tempAC3header);
     63
     64        if ($this->AC3header['syncinfo'] !== self::syncword) {
    6465            if (!$this->isDependencyFor('matroska')) {
    6566                unset($info['fileformat'], $info['ac3']);
    66                 return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)).'"');
    67             }
    68             $offset = 0;
    69             $this->fseek(-2, SEEK_CUR);
     67                return $this->error('Expecting "'.dechex(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.dechex($this->AC3header['syncinfo']).'"');
     68            }
    7069        }
    7170
     
    7473        $info['audio']['lossless']     = false;
    7574
    76         $thisfile_ac3_raw['synchinfo']['crc1']       = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2));
    77         $ac3_synchinfo_fscod_frmsizecod              = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], ($offset + 2), 1));
    78         $thisfile_ac3_raw['synchinfo']['fscod']      = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
    79         $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
    80 
    81         $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
    82         if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
    83             $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
    84         }
    85 
    86         $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
    87         $thisfile_ac3['bitrate']      = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
    88         $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
    89 
    90         $this->AC3header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15));
    91         $ac3_bsi_offset = 0;
    92 
    93         $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
    94         if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
    95             // Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
    96             // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
    97             // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
    98             $this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8');
     75        if ($thisfile_ac3_raw_bsi['bsid'] <= 8) {
     76
     77            $thisfile_ac3_raw_bsi['crc1']       = getid3_lib::Bin2Dec($this->readHeaderBSI(16));
     78            $thisfile_ac3_raw_bsi['fscod']      =                     $this->readHeaderBSI(2);   // 5.4.1.3
     79            $thisfile_ac3_raw_bsi['frmsizecod'] =                     $this->readHeaderBSI(6);   // 5.4.1.4
     80            if ($thisfile_ac3_raw_bsi['frmsizecod'] > 37) { // binary: 100101 - see Table 5.18 Frame Size Code Table (1 word = 16 bits)
     81                $this->warning('Unexpected ac3.bsi.frmsizecod value: '.$thisfile_ac3_raw_bsi['frmsizecod'].', bitrate not set correctly');
     82            }
     83
     84            $thisfile_ac3_raw_bsi['bsid']  = $this->readHeaderBSI(5); // we already know this from pre-parsing the version identifier, but re-read it to let the bitstream flow as intended
     85            $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
     86            $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
     87
     88            if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
     89                // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
     90                $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
     91                $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
     92            }
     93
     94            if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
     95                // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
     96                $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
     97                $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
     98            }
     99
     100            if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
     101                // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
     102                $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
     103                $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
     104            }
     105
     106            $thisfile_ac3_raw_bsi['flags']['lfeon'] = (bool) $this->readHeaderBSI(1);
     107
     108            // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
     109            // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
     110            $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);                 // 5.4.2.8 dialnorm: Dialogue Normalization, 5 Bits
     111
     112            $thisfile_ac3_raw_bsi['flags']['compr'] = (bool) $this->readHeaderBSI(1);       // 5.4.2.9 compre: Compression Gain Word Exists, 1 Bit
     113            if ($thisfile_ac3_raw_bsi['flags']['compr']) {
     114                $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);                // 5.4.2.10 compr: Compression Gain Word, 8 Bits
     115                $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
     116            }
     117
     118            $thisfile_ac3_raw_bsi['flags']['langcod'] = (bool) $this->readHeaderBSI(1);     // 5.4.2.11 langcode: Language Code Exists, 1 Bit
     119            if ($thisfile_ac3_raw_bsi['flags']['langcod']) {
     120                $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);              // 5.4.2.12 langcod: Language Code, 8 Bits
     121            }
     122
     123            $thisfile_ac3_raw_bsi['flags']['audprodinfo'] = (bool) $this->readHeaderBSI(1);  // 5.4.2.13 audprodie: Audio Production Information Exists, 1 Bit
     124            if ($thisfile_ac3_raw_bsi['flags']['audprodinfo']) {
     125                $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);             // 5.4.2.14 mixlevel: Mixing Level, 5 Bits
     126                $thisfile_ac3_raw_bsi['roomtyp']  = $this->readHeaderBSI(2);             // 5.4.2.15 roomtyp: Room Type, 2 Bits
     127
     128                $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
     129                $thisfile_ac3['room_type']    = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
     130            }
     131
     132
     133            $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);                // 5.4.2.16 dialnorm2: Dialogue Normalization, ch2, 5 Bits
     134            $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';  // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31. The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
     135
     136            $thisfile_ac3_raw_bsi['flags']['compr2'] = (bool) $this->readHeaderBSI(1);       // 5.4.2.17 compr2e: Compression Gain Word Exists, ch2, 1 Bit
     137            if ($thisfile_ac3_raw_bsi['flags']['compr2']) {
     138                $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);               // 5.4.2.18 compr2: Compression Gain Word, ch2, 8 Bits
     139                $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
     140            }
     141
     142            $thisfile_ac3_raw_bsi['flags']['langcod2'] = (bool) $this->readHeaderBSI(1);    // 5.4.2.19 langcod2e: Language Code Exists, ch2, 1 Bit
     143            if ($thisfile_ac3_raw_bsi['flags']['langcod2']) {
     144                $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);             // 5.4.2.20 langcod2: Language Code, ch2, 8 Bits
     145            }
     146
     147            $thisfile_ac3_raw_bsi['flags']['audprodinfo2'] = (bool) $this->readHeaderBSI(1); // 5.4.2.21 audprodi2e: Audio Production Information Exists, ch2, 1 Bit
     148            if ($thisfile_ac3_raw_bsi['flags']['audprodinfo2']) {
     149                $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);            // 5.4.2.22 mixlevel2: Mixing Level, ch2, 5 Bits
     150                $thisfile_ac3_raw_bsi['roomtyp2']  = $this->readHeaderBSI(2);            // 5.4.2.23 roomtyp2: Room Type, ch2, 2 Bits
     151
     152                $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
     153                $thisfile_ac3['room_type2']    = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
     154            }
     155
     156            $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);         // 5.4.2.24 copyrightb: Copyright Bit, 1 Bit
     157
     158            $thisfile_ac3_raw_bsi['original']  = (bool) $this->readHeaderBSI(1);         // 5.4.2.25 origbs: Original Bit Stream, 1 Bit
     159
     160            $thisfile_ac3_raw_bsi['flags']['timecod1'] = $this->readHeaderBSI(2);            // 5.4.2.26 timecod1e, timcode2e: Time Code (first and second) Halves Exist, 2 Bits
     161            if ($thisfile_ac3_raw_bsi['flags']['timecod1'] & 0x01) {
     162                $thisfile_ac3_raw_bsi['timecod1'] = $this->readHeaderBSI(14);            // 5.4.2.27 timecod1: Time code first half, 14 bits
     163                $thisfile_ac3['timecode1'] = 0;
     164                $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x3E00) >>  9) * 3600;  // The first 5 bits of this 14-bit field represent the time in hours, with valid values of 0�23
     165                $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x01F8) >>  3) *   60;  // The next 6 bits represent the time in minutes, with valid values of 0�59
     166                $thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x0003) >>  0) *    8;  // The final 3 bits represents the time in 8 second increments, with valid values of 0�7 (representing 0, 8, 16, ... 56 seconds)
     167            }
     168            if ($thisfile_ac3_raw_bsi['flags']['timecod1'] & 0x02) {
     169                $thisfile_ac3_raw_bsi['timecod2'] = $this->readHeaderBSI(14);            // 5.4.2.28 timecod2: Time code second half, 14 bits
     170                $thisfile_ac3['timecode2'] = 0;
     171                $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x3800) >> 11) *   1;              // The first 3 bits of this 14-bit field represent the time in seconds, with valid values from 0�7 (representing 0-7 seconds)
     172                $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x07C0) >>  6) *  (1 / 30);        // The next 5 bits represents the time in frames, with valid values from 0�29 (one frame = 1/30th of a second)
     173                $thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x003F) >>  0) * ((1 / 30) / 60);  // The final 6 bits represents fractions of 1/64 of a frame, with valid values from 0�63
     174            }
     175
     176            $thisfile_ac3_raw_bsi['flags']['addbsi'] = (bool) $this->readHeaderBSI(1);
     177            if ($thisfile_ac3_raw_bsi['flags']['addbsi']) {
     178                $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6) + 1; // This 6-bit code, which exists only if addbside is a 1, indicates the length in bytes of additional bit stream information. The valid range of addbsil is 0�63, indicating 1�64 additional bytes, respectively.
     179
     180                $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
     181
     182                $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
     183                $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
     184            }
     185
     186
     187        } elseif ($thisfile_ac3_raw_bsi['bsid'] <= 16) { // E-AC3
     188
     189
     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.');
     191            $info['audio']['dataformat'] = 'eac3';
     192
     193            $thisfile_ac3_raw_bsi['strmtyp']          =        $this->readHeaderBSI(2);
     194            $thisfile_ac3_raw_bsi['substreamid']      =        $this->readHeaderBSI(3);
     195            $thisfile_ac3_raw_bsi['frmsiz']           =        $this->readHeaderBSI(11);
     196            $thisfile_ac3_raw_bsi['fscod']            =        $this->readHeaderBSI(2);
     197            if ($thisfile_ac3_raw_bsi['fscod'] == 3) {
     198                $thisfile_ac3_raw_bsi['fscod2']       =        $this->readHeaderBSI(2);
     199                $thisfile_ac3_raw_bsi['numblkscod'] = 3; // six blocks per syncframe
     200            } else {
     201                $thisfile_ac3_raw_bsi['numblkscod']   =        $this->readHeaderBSI(2);
     202            }
     203            $thisfile_ac3['bsi']['blocks_per_sync_frame'] = self::blocksPerSyncFrame($thisfile_ac3_raw_bsi['numblkscod']);
     204            $thisfile_ac3_raw_bsi['acmod']            =        $this->readHeaderBSI(3);
     205            $thisfile_ac3_raw_bsi['flags']['lfeon']   = (bool) $this->readHeaderBSI(1);
     206            $thisfile_ac3_raw_bsi['bsid']             =        $this->readHeaderBSI(5); // we already know this from pre-parsing the version identifier, but re-read it to let the bitstream flow as intended
     207            $thisfile_ac3_raw_bsi['dialnorm']         =        $this->readHeaderBSI(5);
     208            $thisfile_ac3_raw_bsi['flags']['compr']       = (bool) $this->readHeaderBSI(1);
     209            if ($thisfile_ac3_raw_bsi['flags']['compr']) {
     210                $thisfile_ac3_raw_bsi['compr']        =        $this->readHeaderBSI(8);
     211            }
     212            if ($thisfile_ac3_raw_bsi['acmod'] == 0) { // if 1+1 mode (dual mono, so some items need a second value)
     213                $thisfile_ac3_raw_bsi['dialnorm2']    =        $this->readHeaderBSI(5);
     214                $thisfile_ac3_raw_bsi['flags']['compr2']  = (bool) $this->readHeaderBSI(1);
     215                if ($thisfile_ac3_raw_bsi['flags']['compr2']) {
     216                    $thisfile_ac3_raw_bsi['compr2']   =        $this->readHeaderBSI(8);
     217                }
     218            }
     219            if ($thisfile_ac3_raw_bsi['strmtyp'] == 1) { // if dependent stream
     220                $thisfile_ac3_raw_bsi['flags']['chanmap'] = (bool) $this->readHeaderBSI(1);
     221                if ($thisfile_ac3_raw_bsi['flags']['chanmap']) {
     222                    $thisfile_ac3_raw_bsi['chanmap']  =        $this->readHeaderBSI(8);
     223                }
     224            }
     225            $thisfile_ac3_raw_bsi['flags']['mixmdat']     = (bool) $this->readHeaderBSI(1);
     226            if ($thisfile_ac3_raw_bsi['flags']['mixmdat']) { // Mixing metadata
     227                if ($thisfile_ac3_raw_bsi['acmod'] > 2) { // if more than 2 channels
     228                    $thisfile_ac3_raw_bsi['dmixmod']  =        $this->readHeaderBSI(2);
     229                }
     230                if (($thisfile_ac3_raw_bsi['acmod'] & 0x01) && ($thisfile_ac3_raw_bsi['acmod'] > 2)) { // if three front channels exist
     231                    $thisfile_ac3_raw_bsi['ltrtcmixlev'] =        $this->readHeaderBSI(3);
     232                    $thisfile_ac3_raw_bsi['lorocmixlev'] =        $this->readHeaderBSI(3);
     233                }
     234                if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) { // if a surround channel exists
     235                    $thisfile_ac3_raw_bsi['ltrtsurmixlev'] =        $this->readHeaderBSI(3);
     236                    $thisfile_ac3_raw_bsi['lorosurmixlev'] =        $this->readHeaderBSI(3);
     237                }
     238                if ($thisfile_ac3_raw_bsi['flags']['lfeon']) { // if the LFE channel exists
     239                    $thisfile_ac3_raw_bsi['flags']['lfemixlevcod'] = (bool) $this->readHeaderBSI(1);
     240                    if ($thisfile_ac3_raw_bsi['flags']['lfemixlevcod']) {
     241                        $thisfile_ac3_raw_bsi['lfemixlevcod']  =        $this->readHeaderBSI(5);
     242                    }
     243                }
     244                if ($thisfile_ac3_raw_bsi['strmtyp'] == 0) { // if independent stream
     245                    $thisfile_ac3_raw_bsi['flags']['pgmscl'] = (bool) $this->readHeaderBSI(1);
     246                    if ($thisfile_ac3_raw_bsi['flags']['pgmscl']) {
     247                        $thisfile_ac3_raw_bsi['pgmscl']  =        $this->readHeaderBSI(6);
     248                    }
     249                    if ($thisfile_ac3_raw_bsi['acmod'] == 0) { // if 1+1 mode (dual mono, so some items need a second value)
     250                        $thisfile_ac3_raw_bsi['flags']['pgmscl2'] = (bool) $this->readHeaderBSI(1);
     251                        if ($thisfile_ac3_raw_bsi['flags']['pgmscl2']) {
     252                            $thisfile_ac3_raw_bsi['pgmscl2']  =        $this->readHeaderBSI(6);
     253                        }
     254                    }
     255                    $thisfile_ac3_raw_bsi['flags']['extpgmscl'] = (bool) $this->readHeaderBSI(1);
     256                    if ($thisfile_ac3_raw_bsi['flags']['extpgmscl']) {
     257                        $thisfile_ac3_raw_bsi['extpgmscl']  =        $this->readHeaderBSI(6);
     258                    }
     259                    $thisfile_ac3_raw_bsi['mixdef']  =        $this->readHeaderBSI(2);
     260                    if ($thisfile_ac3_raw_bsi['mixdef'] == 1) { // mixing option 2
     261                        $thisfile_ac3_raw_bsi['premixcmpsel']  = (bool) $this->readHeaderBSI(1);
     262                        $thisfile_ac3_raw_bsi['drcsrc']        = (bool) $this->readHeaderBSI(1);
     263                        $thisfile_ac3_raw_bsi['premixcmpscl']  =        $this->readHeaderBSI(3);
     264                    } elseif ($thisfile_ac3_raw_bsi['mixdef'] == 2) { // mixing option 3
     265                        $thisfile_ac3_raw_bsi['mixdata']       =        $this->readHeaderBSI(12);
     266                    } elseif ($thisfile_ac3_raw_bsi['mixdef'] == 3) { // mixing option 4
     267                        $mixdefbitsread = 0;
     268                        $thisfile_ac3_raw_bsi['mixdeflen']     =        $this->readHeaderBSI(5); $mixdefbitsread += 5;
     269                        $thisfile_ac3_raw_bsi['flags']['mixdata2'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     270                        if ($thisfile_ac3_raw_bsi['flags']['mixdata2']) {
     271                            $thisfile_ac3_raw_bsi['premixcmpsel']  = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     272                            $thisfile_ac3_raw_bsi['drcsrc']        = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     273                            $thisfile_ac3_raw_bsi['premixcmpscl']  =        $this->readHeaderBSI(3); $mixdefbitsread += 3;
     274                            $thisfile_ac3_raw_bsi['flags']['extpgmlscl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     275                            if ($thisfile_ac3_raw_bsi['flags']['extpgmlscl']) {
     276                                $thisfile_ac3_raw_bsi['extpgmlscl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     277                            }
     278                            $thisfile_ac3_raw_bsi['flags']['extpgmcscl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     279                            if ($thisfile_ac3_raw_bsi['flags']['extpgmcscl']) {
     280                                $thisfile_ac3_raw_bsi['extpgmcscl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     281                            }
     282                            $thisfile_ac3_raw_bsi['flags']['extpgmrscl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     283                            if ($thisfile_ac3_raw_bsi['flags']['extpgmrscl']) {
     284                                $thisfile_ac3_raw_bsi['extpgmrscl']    =        $this->readHeaderBSI(4);
     285                            }
     286                            $thisfile_ac3_raw_bsi['flags']['extpgmlsscl']  = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     287                            if ($thisfile_ac3_raw_bsi['flags']['extpgmlsscl']) {
     288                                $thisfile_ac3_raw_bsi['extpgmlsscl']   =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     289                            }
     290                            $thisfile_ac3_raw_bsi['flags']['extpgmrsscl']  = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     291                            if ($thisfile_ac3_raw_bsi['flags']['extpgmrsscl']) {
     292                                $thisfile_ac3_raw_bsi['extpgmrsscl']   =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     293                            }
     294                            $thisfile_ac3_raw_bsi['flags']['extpgmlfescl'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     295                            if ($thisfile_ac3_raw_bsi['flags']['extpgmlfescl']) {
     296                                $thisfile_ac3_raw_bsi['extpgmlfescl']  =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     297                            }
     298                            $thisfile_ac3_raw_bsi['flags']['dmixscl']      = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     299                            if ($thisfile_ac3_raw_bsi['flags']['dmixscl']) {
     300                                $thisfile_ac3_raw_bsi['dmixscl']       =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     301                            }
     302                            $thisfile_ac3_raw_bsi['flags']['addch']        = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     303                            if ($thisfile_ac3_raw_bsi['flags']['addch']) {
     304                                $thisfile_ac3_raw_bsi['flags']['extpgmaux1scl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     305                                if ($thisfile_ac3_raw_bsi['flags']['extpgmaux1scl']) {
     306                                    $thisfile_ac3_raw_bsi['extpgmaux1scl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     307                                }
     308                                $thisfile_ac3_raw_bsi['flags']['extpgmaux2scl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     309                                if ($thisfile_ac3_raw_bsi['flags']['extpgmaux2scl']) {
     310                                    $thisfile_ac3_raw_bsi['extpgmaux2scl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
     311                                }
     312                            }
     313                        }
     314                        $thisfile_ac3_raw_bsi['flags']['mixdata3'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     315                        if ($thisfile_ac3_raw_bsi['flags']['mixdata3']) {
     316                            $thisfile_ac3_raw_bsi['spchdat']   =        $this->readHeaderBSI(5); $mixdefbitsread += 5;
     317                            $thisfile_ac3_raw_bsi['flags']['addspchdat'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     318                            if ($thisfile_ac3_raw_bsi['flags']['addspchdat']) {
     319                                $thisfile_ac3_raw_bsi['spchdat1']   =         $this->readHeaderBSI(5); $mixdefbitsread += 5;
     320                                $thisfile_ac3_raw_bsi['spchan1att'] =         $this->readHeaderBSI(2); $mixdefbitsread += 2;
     321                                $thisfile_ac3_raw_bsi['flags']['addspchdat1'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
     322                                if ($thisfile_ac3_raw_bsi['flags']['addspchdat1']) {
     323                                    $thisfile_ac3_raw_bsi['spchdat2']   =         $this->readHeaderBSI(5); $mixdefbitsread += 5;
     324                                    $thisfile_ac3_raw_bsi['spchan2att'] =         $this->readHeaderBSI(3); $mixdefbitsread += 3;
     325                                }
     326                            }
     327                        }
     328                        $mixdata_bits = (8 * ($thisfile_ac3_raw_bsi['mixdeflen'] + 2)) - $mixdefbitsread;
     329                        $mixdata_fill = (($mixdata_bits % 8) ? 8 - ($mixdata_bits % 8) : 0);
     330                        $thisfile_ac3_raw_bsi['mixdata']     =        $this->readHeaderBSI($mixdata_bits);
     331                        $thisfile_ac3_raw_bsi['mixdatafill'] =        $this->readHeaderBSI($mixdata_fill);
     332                        unset($mixdefbitsread, $mixdata_bits, $mixdata_fill);
     333                    }
     334                    if ($thisfile_ac3_raw_bsi['acmod'] < 2) { // if mono or dual mono source
     335                        $thisfile_ac3_raw_bsi['flags']['paninfo'] = (bool) $this->readHeaderBSI(1);
     336                        if ($thisfile_ac3_raw_bsi['flags']['paninfo']) {
     337                            $thisfile_ac3_raw_bsi['panmean']   =        $this->readHeaderBSI(8);
     338                            $thisfile_ac3_raw_bsi['paninfo']   =        $this->readHeaderBSI(6);
     339                        }
     340                        if ($thisfile_ac3_raw_bsi['acmod'] == 0) { // if 1+1 mode (dual mono, so some items need a second value)
     341                            $thisfile_ac3_raw_bsi['flags']['paninfo2'] = (bool) $this->readHeaderBSI(1);
     342                            if ($thisfile_ac3_raw_bsi['flags']['paninfo2']) {
     343                                $thisfile_ac3_raw_bsi['panmean2']   =        $this->readHeaderBSI(8);
     344                                $thisfile_ac3_raw_bsi['paninfo2']   =        $this->readHeaderBSI(6);
     345                            }
     346                        }
     347                    }
     348                    $thisfile_ac3_raw_bsi['flags']['frmmixcfginfo'] = (bool) $this->readHeaderBSI(1);
     349                    if ($thisfile_ac3_raw_bsi['flags']['frmmixcfginfo']) { // mixing configuration information
     350                        if ($thisfile_ac3_raw_bsi['numblkscod'] == 0) {
     351                            $thisfile_ac3_raw_bsi['blkmixcfginfo'][0]  =        $this->readHeaderBSI(5);
     352                        } else {
     353                            for ($blk = 0; $blk < $thisfile_ac3_raw_bsi['numblkscod']; $blk++) {
     354                                $thisfile_ac3_raw_bsi['flags']['blkmixcfginfo'.$blk] = (bool) $this->readHeaderBSI(1);
     355                                if ($thisfile_ac3_raw_bsi['flags']['blkmixcfginfo'.$blk]) { // mixing configuration information
     356                                    $thisfile_ac3_raw_bsi['blkmixcfginfo'][$blk]  =        $this->readHeaderBSI(5);
     357                                }
     358                            }
     359                        }
     360                    }
     361                }
     362            }
     363            $thisfile_ac3_raw_bsi['flags']['infomdat']          = (bool) $this->readHeaderBSI(1);
     364            if ($thisfile_ac3_raw_bsi['flags']['infomdat']) { // Informational metadata
     365                $thisfile_ac3_raw_bsi['bsmod']                  =        $this->readHeaderBSI(3);
     366                $thisfile_ac3_raw_bsi['flags']['copyrightb']    = (bool) $this->readHeaderBSI(1);
     367                $thisfile_ac3_raw_bsi['flags']['origbs']        = (bool) $this->readHeaderBSI(1);
     368                if ($thisfile_ac3_raw_bsi['acmod'] == 2) { //  if in 2/0 mode
     369                    $thisfile_ac3_raw_bsi['dsurmod']            =        $this->readHeaderBSI(2);
     370                    $thisfile_ac3_raw_bsi['dheadphonmod']       =        $this->readHeaderBSI(2);
     371                }
     372                if ($thisfile_ac3_raw_bsi['acmod'] >= 6) { //  if both surround channels exist
     373                    $thisfile_ac3_raw_bsi['dsurexmod']          =        $this->readHeaderBSI(2);
     374                }
     375                $thisfile_ac3_raw_bsi['flags']['audprodi']      = (bool) $this->readHeaderBSI(1);
     376                if ($thisfile_ac3_raw_bsi['flags']['audprodi']) {
     377                    $thisfile_ac3_raw_bsi['mixlevel']           =        $this->readHeaderBSI(5);
     378                    $thisfile_ac3_raw_bsi['roomtyp']            =        $this->readHeaderBSI(2);
     379                    $thisfile_ac3_raw_bsi['flags']['adconvtyp'] = (bool) $this->readHeaderBSI(1);
     380                }
     381                if ($thisfile_ac3_raw_bsi['acmod'] == 0) { //  if 1+1 mode (dual mono, so some items need a second value)
     382                    $thisfile_ac3_raw_bsi['flags']['audprodi2']      = (bool) $this->readHeaderBSI(1);
     383                    if ($thisfile_ac3_raw_bsi['flags']['audprodi2']) {
     384                        $thisfile_ac3_raw_bsi['mixlevel2']           =        $this->readHeaderBSI(5);
     385                        $thisfile_ac3_raw_bsi['roomtyp2']            =        $this->readHeaderBSI(2);
     386                        $thisfile_ac3_raw_bsi['flags']['adconvtyp2'] = (bool) $this->readHeaderBSI(1);
     387                    }
     388                }
     389                if ($thisfile_ac3_raw_bsi['fscod'] < 3) { // if not half sample rate
     390                    $thisfile_ac3_raw_bsi['flags']['sourcefscod'] = (bool) $this->readHeaderBSI(1);
     391                }
     392            }
     393            if (($thisfile_ac3_raw_bsi['strmtyp'] == 0) && ($thisfile_ac3_raw_bsi['numblkscod'] != 3)) { //  if both surround channels exist
     394                $thisfile_ac3_raw_bsi['flags']['convsync'] = (bool) $this->readHeaderBSI(1);
     395            }
     396            if ($thisfile_ac3_raw_bsi['strmtyp'] == 2) { //  if bit stream converted from AC-3
     397                if ($thisfile_ac3_raw_bsi['numblkscod'] != 3) { // 6 blocks per syncframe
     398                    $thisfile_ac3_raw_bsi['flags']['blkid']  = 1;
     399                } else {
     400                    $thisfile_ac3_raw_bsi['flags']['blkid']  = (bool) $this->readHeaderBSI(1);
     401                }
     402                if ($thisfile_ac3_raw_bsi['flags']['blkid']) {
     403                    $thisfile_ac3_raw_bsi['frmsizecod']  =        $this->readHeaderBSI(6);
     404                }
     405            }
     406            $thisfile_ac3_raw_bsi['flags']['addbsi']  = (bool) $this->readHeaderBSI(1);
     407            if ($thisfile_ac3_raw_bsi['flags']['addbsi']) {
     408                $thisfile_ac3_raw_bsi['addbsil']  =        $this->readHeaderBSI(6);
     409                $thisfile_ac3_raw_bsi['addbsi']   =        $this->readHeaderBSI(($thisfile_ac3_raw_bsi['addbsil'] + 1) * 8);
     410            }
     411
     412        } else {
     413
     414            $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.');
    99415            unset($info['ac3']);
    100416            return false;
    101         }
    102 
    103         $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
    104         $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
     417
     418        }
     419
     420        if (isset($thisfile_ac3_raw_bsi['fscod2'])) {
     421            $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup2($thisfile_ac3_raw_bsi['fscod2']);
     422        } else {
     423            $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw_bsi['fscod']);
     424        }
     425        if ($thisfile_ac3_raw_bsi['fscod'] <= 3) {
     426            $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
     427        } else {
     428            $this->warning('Unexpected ac3.bsi.fscod value: '.$thisfile_ac3_raw_bsi['fscod']);
     429        }
     430        if (isset($thisfile_ac3_raw_bsi['frmsizecod'])) {
     431            $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw_bsi['frmsizecod'], $thisfile_ac3_raw_bsi['fscod']);
     432            $thisfile_ac3['bitrate']      = self::bitrateLookup($thisfile_ac3_raw_bsi['frmsizecod']);
     433        } 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!
     436            $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;
     439        }
     440        $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
    105441
    106442        $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
     
    124460        $info['audio']['channels'] = $thisfile_ac3['num_channels'];
    125461
    126         if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
    127             // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
    128             $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
    129             $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
    130         }
    131 
    132         if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
    133             // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
    134             $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
    135             $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
    136         }
    137 
    138         if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
    139             // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
    140             $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
    141             $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
    142         }
    143 
    144         $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
    145         $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
    146         if ($thisfile_ac3_raw_bsi['lfeon']) {
    147             //$info['audio']['channels']++;
     462        $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['flags']['lfeon'];
     463        if ($thisfile_ac3_raw_bsi['flags']['lfeon']) {
    148464            $info['audio']['channels'] .= '.1';
    149465        }
    150466
    151         $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
    152 
    153         // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
    154         // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
    155         $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
     467        $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['flags']['lfeon']);
    156468        $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
    157 
    158         $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
    159         if ($thisfile_ac3_raw_bsi['compre_flag']) {
    160             $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
    161             $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
    162         }
    163 
    164         $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
    165         if ($thisfile_ac3_raw_bsi['langcode_flag']) {
    166             $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);
    167         }
    168 
    169         $thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1);
    170         if ($thisfile_ac3_raw_bsi['audprodie']) {
    171             $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);
    172             $thisfile_ac3_raw_bsi['roomtyp']  = $this->readHeaderBSI(2);
    173 
    174             $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
    175             $thisfile_ac3['room_type']    = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
    176         }
    177 
    178         if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
    179             // If acmod is 0, then two completely independent program channels (dual mono)
    180             // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
    181             // a number of additional items are present in BSI or audblk to fully describe Ch2.
    182 
    183             // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
    184             // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
    185             $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
    186             $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
    187 
    188             $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
    189             if ($thisfile_ac3_raw_bsi['compre_flag2']) {
    190                 $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
    191                 $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
    192             }
    193 
    194             $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
    195             if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
    196                 $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);
    197             }
    198 
    199             $thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1);
    200             if ($thisfile_ac3_raw_bsi['audprodie2']) {
    201                 $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);
    202                 $thisfile_ac3_raw_bsi['roomtyp2']  = $this->readHeaderBSI(2);
    203 
    204                 $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
    205                 $thisfile_ac3['room_type2']    = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
    206             }
    207 
    208         }
    209 
    210         $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);
    211 
    212         $thisfile_ac3_raw_bsi['original']  = (bool) $this->readHeaderBSI(1);
    213 
    214         $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1);
    215         if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
    216             $thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14);
    217         }
    218 
    219         $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1);
    220         if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
    221             $thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14);
    222         }
    223 
    224         $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1);
    225         if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
    226             $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
    227 
    228             $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
    229 
    230             $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
    231             $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
    232         }
    233469
    234470        return true;
     
    250486        );
    251487        return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false);
     488    }
     489
     490    public static function sampleRateCodeLookup2($fscod2) {
     491        static $sampleRateCodeLookup2 = array(
     492            0 => 24000,
     493            1 => 22050,
     494            2 => 16000,
     495            3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
     496        );
     497        return (isset($sampleRateCodeLookup2[$fscod2]) ? $sampleRateCodeLookup2[$fscod2] : false);
    252498    }
    253499
     
    410656
    411657    public static function frameSizeLookup($frmsizecod, $fscod) {
    412         $padding     = (bool) ($frmsizecod % 2);
    413         $framesizeid =   floor($frmsizecod / 2);
     658        // LSB is whether padding is used or not
     659        $padding     = (bool) ($frmsizecod & 0x01);
     660        $framesizeid =        ($frmsizecod & 0x3E) >> 1;
    414661
    415662        static $frameSizeLookup = array();
    416663        if (empty($frameSizeLookup)) {
    417664            $frameSizeLookup = array (
    418                 0  => array(128, 138, 192),
    419                 1  => array(40, 160, 174, 240),
    420                 2  => array(48, 192, 208, 288),
    421                 3  => array(56, 224, 242, 336),
    422                 4  => array(64, 256, 278, 384),
    423                 5  => array(80, 320, 348, 480),
    424                 6  => array(96, 384, 416, 576),
    425                 7  => array(112, 448, 486, 672),
    426                 8  => array(128, 512, 556, 768),
    427                 9  => array(160, 640, 696, 960),
    428                 10 => array(192, 768, 834, 1152),
    429                 11 => array(224, 896, 974, 1344),
    430                 12 => array(256, 1024, 1114, 1536),
    431                 13 => array(320, 1280, 1392, 1920),
    432                 14 => array(384, 1536, 1670, 2304),
    433                 15 => array(448, 1792, 1950, 2688),
    434                 16 => array(512, 2048, 2228, 3072),
    435                 17 => array(576, 2304, 2506, 3456),
    436                 18 => array(640, 2560, 2786, 3840)
     665                0  => array( 128,  138,  192),  //  32 kbps
     666                1  => array( 160,  174,  240),  //  40 kbps
     667                2  => array( 192,  208,  288),  //  48 kbps
     668                3  => array( 224,  242,  336),  //  56 kbps
     669                4  => array( 256,  278,  384),  //  64 kbps
     670                5  => array( 320,  348,  480),  //  80 kbps
     671                6  => array( 384,  416,  576),  //  96 kbps
     672                7  => array( 448,  486,  672),  // 112 kbps
     673                8  => array( 512,  556,  768),  // 128 kbps
     674                9  => array( 640,  696,  960),  // 160 kbps
     675                10 => array( 768,  834, 1152),  // 192 kbps
     676                11 => array( 896,  974, 1344),  // 224 kbps
     677                12 => array(1024, 1114, 1536),  // 256 kbps
     678                13 => array(1280, 1392, 1920),  // 320 kbps
     679                14 => array(1536, 1670, 2304),  // 384 kbps
     680                15 => array(1792, 1950, 2688),  // 448 kbps
     681                16 => array(2048, 2228, 3072),  // 512 kbps
     682                17 => array(2304, 2506, 3456),  // 576 kbps
     683                18 => array(2560, 2786, 3840)   // 640 kbps
    437684            );
    438685        }
     
    445692
    446693    public static function bitrateLookup($frmsizecod) {
    447         $framesizeid =   floor($frmsizecod / 2);
     694        // LSB is whether padding is used or not
     695        $padding     = (bool) ($frmsizecod & 0x01);
     696        $framesizeid =        ($frmsizecod & 0x3E) >> 1;
    448697
    449698        static $bitrateLookup = array(
    450             0  => 32000,
    451             1  => 40000,
    452             2  => 48000,
    453             3  => 56000,
    454             4  => 64000,
    455             5  => 80000,
    456             6  => 96000,
    457             7 => 112000,
    458             8 => 128000,
    459             9 => 160000,
     699             0 => 32000,
     700             1 => 40000,
     701             2 => 48000,
     702             3 => 56000,
     703             4 => 64000,
     704             5 => 80000,
     705             6 => 96000,
     706             7 => 112000,
     707             8 => 128000,
     708             9 => 160000,
    460709            10 => 192000,
    461710            11 => 224000,
     
    466715            16 => 512000,
    467716            17 => 576000,
    468             18 => 640000
     717            18 => 640000,
    469718        );
    470719        return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false);
    471720    }
    472721
     722    public static function blocksPerSyncFrame($numblkscod) {
     723        static $blocksPerSyncFrameLookup = array(
     724            0 => 1,
     725            1 => 2,
     726            2 => 3,
     727            3 => 6,
     728        );
     729        return (isset($blocksPerSyncFrameLookup[$numblkscod]) ? $blocksPerSyncFrameLookup[$numblkscod] : false);
     730    }
     731
    473732
    474733}
  • trunk/src/wp-includes/ID3/module.audio.mp3.php

    r32979 r41196  
    3535        if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
    3636            if ($this->allow_bruteforce) {
    37                 $info['error'][] = 'Rescanning file in BruteForce mode';
     37                $this->error('Rescanning file in BruteForce mode');
    3838                $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
    3939            }
     
    7373
    7474            }
    75             $info['warning'][] = $synchoffsetwarning;
     75            $this->warning($synchoffsetwarning);
    7676
    7777        }
     
    135135
    136136                default:
    137                     $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
     137                    $this->warning('Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"');
    138138                    break;
    139139            }
     
    425425
    426426        if ($this->fseek($offset) != 0) {
    427             $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
     427            $this->error('decodeMPEGaudioHeader() failed to seek to next offset at '.$offset);
    428428            return false;
    429429        }
     
    438438
    439439        $head4 = substr($headerstring, 0, 4);
    440 
     440        $head4_key = getid3_lib::PrintHexBytes($head4, true, false, false);
    441441        static $MPEGaudioHeaderDecodeCache = array();
    442         if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
    443             $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
     442        if (isset($MPEGaudioHeaderDecodeCache[$head4_key])) {
     443            $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4_key];
    444444        } else {
    445445            $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
    446             $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
     446            $MPEGaudioHeaderDecodeCache[$head4_key] = $MPEGheaderRawArray;
    447447        }
    448448
    449449        static $MPEGaudioHeaderValidCache = array();
    450         if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
    451             //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
    452             $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
     450        if (!isset($MPEGaudioHeaderValidCache[$head4_key])) { // Not in cache
     451            //$MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
     452            $MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
    453453        }
    454454
     
    460460
    461461
    462         if ($MPEGaudioHeaderValidCache[$head4]) {
     462        if ($MPEGaudioHeaderValidCache[$head4_key]) {
    463463            $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
    464464        } else {
    465             $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
     465            $this->error('Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset);
    466466            return false;
    467467        }
     
    491491        if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
    492492            // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
    493             $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
     493            $this->warning('Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1');
    494494            $thisfile_mpeg_audio['raw']['bitrate'] = 0;
    495495        }
     
    513513                        // these are ok
    514514                    } else {
    515                         $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
     515                        $this->error($thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.');
    516516                        return false;
    517517                    }
     
    524524                        // these are ok
    525525                    } else {
    526                         $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
     526                        $this->error(intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.');
    527527                        return false;
    528528                    }
     
    546546                $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
    547547            } else {
    548                 $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
     548                $this->error('Frame at offset('.$offset.') is has an invalid frame length.');
    549549                return false;
    550550            }
     
    649649
    650650                //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
    651                 if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
    652 
    653                     $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
     651                //if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
     652                if (!empty($thisfile_mpeg_audio['VBR_frames'])) {
     653                    $used_filesize  = 0;
     654                    if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
     655                        $used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
     656                    } elseif (!empty($info['filesize'])) {
     657                        $used_filesize  = $info['filesize'];
     658                        $used_filesize -= intval(@$info['id3v2']['headerlength']);
     659                        $used_filesize -= (isset($info['id3v1']) ? 128 : 0);
     660                        $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
     661                        $this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes');
     662                    }
     663
     664                    $framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames'];
    654665
    655666                    if ($thisfile_mpeg_audio['layer'] == '1') {
     
    838849                        $thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
    839850                        if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
    840                             $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
     851                            $this->warning('Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org');
    841852                        }
    842853                        if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
     
    882893                    }
    883894                    if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
    884                         $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
     895                        $this->warning('VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.');
    885896                    }
    886897                }
     
    909920                //  if ($PossibleNullByte === "\x00") {
    910921                        $info['avdataend']--;
    911                 //      $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
     922                //      $this->warning('Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored');
    912923                //  } else {
    913                 //      $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
     924                //      $this->warning('Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)');
    914925                //  }
    915926                } else {
    916                     $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
     927                    $this->warning('Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)');
    917928                }
    918929            }
     
    932943                    }
    933944                } else {
    934                     $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
     945                    $this->error('Error calculating frame length of free-format MP3 without Xing/LAME header');
    935946                }
    936947            }
     
    949960                    $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
    950961                    if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
    951                         $info['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
     962                        $info['audio']['bitrate']       = $thisfile_mpeg_audio['VBR_bitrate'];
    952963                        $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
    953964                    }
     
    10751086    public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
    10761087        $info = &$this->getid3->info;
    1077         $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
     1088        $firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
    10781089        $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
    10791090
     
    10851096            }
    10861097
    1087             $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
     1098            $nextframetestarray = array('error' => array(), 'warning' => array(), 'avdataend' => $info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
    10881099            if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
    10891100                if ($ScanAsCBR) {
     
    10991110                    $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
    11001111                } else {
    1101                     $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
     1112                    $this->error('Frame at offset ('.$offset.') is has an invalid frame length.');
    11021113                    return false;
    11031114                }
     
    11111122
    11121123                // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
    1113                 $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
     1124                $this->warning('Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.');
    11141125
    11151126                return false;
     
    11541165            }
    11551166            if (!$framelength) {
    1156                 $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
     1167                $this->error('Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset);
    11571168                return false;
    11581169            } else {
    1159                 $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
     1170                $this->warning('ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)');
    11601171                $info['audio']['codec']   = 'LAME';
    11611172                $info['audio']['encoder'] = 'LAME3.88';
     
    11841195                    $nextoffset++;
    11851196                } else {
    1186                     $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
     1197                    $this->error('Did not find expected free-format sync pattern at offset '.$nextoffset);
    11871198                    return false;
    11881199                }
     
    12821293                            if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
    12831294                                $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
    1284                                 $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
     1295                                $this->warning('too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.');
    12851296                                foreach ($Distribution as $key1 => $value1) {
    12861297                                    foreach ($value1 as $key2 => $value2) {
     
    13091320        $info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
    13101321        if (count($Distribution['version']) > 1) {
    1311             $info['error'][] = 'Corrupt file - more than one MPEG version detected';
     1322            $this->error('Corrupt file - more than one MPEG version detected');
    13121323        }
    13131324        if (count($Distribution['layer']) > 1) {
    1314             $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
     1325            $this->error('Corrupt file - more than one MPEG layer detected');
    13151326        }
    13161327        if (count($Distribution['frequency']) > 1) {
    1317             $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
     1328            $this->error('Corrupt file - more than one MPEG sample rate detected');
    13181329        }
    13191330
     
    13271338        $info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
    13281339        if ($info['mpeg']['audio']['frame_count'] == 0) {
    1329             $info['error'][] = 'no MPEG audio frames found';
     1340            $this->error('no MPEG audio frames found');
    13301341            return false;
    13311342        }
     
    13621373        $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
    13631374        if ($sync_seek_buffer_size <= 0) {
    1364             $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
     1375            $this->error('Invalid $sync_seek_buffer_size at offset '.$avdataoffset);
    13651376            return false;
    13661377        }
     
    13731384                if ($SynchSeekOffset > $sync_seek_buffer_size) {
    13741385                    // if a synch's not found within the first 128k bytes, then give up
    1375                     $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
     1386                    $this->error('Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB');
    13761387                    if (isset($info['audio']['bitrate'])) {
    13771388                        unset($info['audio']['bitrate']);
     
    13871398                } elseif (feof($this->getid3->fp)) {
    13881399
    1389                     $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
     1400                    $this->error('Could not find valid MPEG audio synch before end of file');
    13901401                    if (isset($info['audio']['bitrate'])) {
    13911402                        unset($info['audio']['bitrate']);
     
    14021413
    14031414            if (($SynchSeekOffset + 1) >= strlen($header)) {
    1404                 $info['error'][] = 'Could not find valid MPEG synch before end of file';
     1415                $this->error('Could not find valid MPEG synch before end of file');
    14051416                return false;
    14061417            }
     
    14451456                                $info = $dummy;
    14461457                                $info['avdataoffset'] = $GarbageOffsetEnd;
    1447                                 $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
     1458                                $this->warning('apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd);
    14481459                            } else {
    1449                                 $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
     1460                                $this->warning('using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')');
    14501461                            }
    14511462                        }
     
    15401551                        }
    15411552                        if ($pct_data_scanned > 0) {
    1542                             $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
     1553                            $this->warning('too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.');
    15431554                            foreach ($info['mpeg']['audio'] as $key1 => $value1) {
    15441555                                if (!preg_match('#_distribution$#i', $key1)) {
     
    15521563
    15531564                        if ($SynchErrorsFound > 0) {
    1554                             $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
     1565                            $this->warning('Found '.$SynchErrorsFound.' synch errors in histogram analysis');
    15551566                            //return false;
    15561567                        }
     
    15651576                        }
    15661577                        if ($framecounter == 0) {
    1567                             $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
     1578                            $this->error('Corrupt MP3 file: framecounter == zero');
    15681579                            return false;
    15691580                        }
     
    16001611                if (empty($info['mpeg']['audio'])) {
    16011612
    1602                     $info['error'][] = 'could not find valid MPEG synch before end of file';
     1613                    $this->error('could not find valid MPEG synch before end of file');
    16031614                    if (isset($info['audio']['bitrate'])) {
    16041615                        unset($info['audio']['bitrate']);
  • trunk/src/wp-includes/ID3/module.audio.ogg.php

    r32979 r41196  
    2727        // Warn about illegal tags - only vorbiscomments are allowed
    2828        if (isset($info['id3v2'])) {
    29             $info['warning'][] = 'Illegal ID3v2 tag present.';
     29            $this->warning('Illegal ID3v2 tag present.');
    3030        }
    3131        if (isset($info['id3v1'])) {
    32             $info['warning'][] = 'Illegal ID3v1 tag present.';
     32            $this->warning('Illegal ID3v1 tag present.');
    3333        }
    3434        if (isset($info['ape'])) {
    35             $info['warning'][] = 'Illegal APE tag present.';
     35            $this->warning('Illegal APE tag present.');
    3636        }
    3737
     
    4545
    4646        if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
    47             $info['error'][] = 'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)';
     47            $this->error('Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)');
    4848            unset($info['fileformat']);
    4949            unset($info['ogg']);
     
    180180                $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
    181181            }
    182 $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable';
     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');
    183183
    184184
     
    241241
    242242                    $info['video']['dataformat'] = 'theora1';
    243                     $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']';
     243                    $this->error('Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']');
    244244                    //break;
    245245
     
    249249
    250250                } else {
    251                     $info['error'][] = 'unexpected';
     251                    $this->error('unexpected');
    252252                    //break;
    253253                }
     
    257257            $this->fseek($oggpageinfo['page_start_offset']);
    258258
    259             $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']';
     259            $this->error('Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']');
    260260            //return false;
    261261
    262262        } else {
    263263
    264             $info['error'][] = 'Expecting either "Speex   ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
     264            $this->error('Expecting either "Speex   ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"');
    265265            unset($info['ogg']);
    266266            unset($info['mime_type']);
     
    285285                $flac = new getid3_flac($this->getid3);
    286286                if (!$flac->parseMETAdata()) {
    287                     $info['error'][] = 'Failed to parse FLAC headers';
     287                    $this->error('Failed to parse FLAC headers');
    288288                    return false;
    289289                }
     
    300300                $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 0, 8); // hard-coded to 'OpusTags'
    301301                if(substr($filedata, 0, 8)  != 'OpusTags') {
    302                     $info['error'][] = 'Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"';
     302                    $this->error('Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"');
    303303                    return false;
    304304                }
     
    312312        if (!getid3_lib::intValueSupported($info['avdataend'])) {
    313313
    314             $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
     314            $this->warning('Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)');
    315315
    316316        } else {
     
    324324                $info['ogg']['samples']   = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
    325325                if ($info['ogg']['samples'] == 0) {
    326                     $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
     326                    $this->error('Corrupt Ogg file: eos.number of samples == zero');
    327327                    return false;
    328328                }
     
    343343        if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
    344344            if ($info['audio']['bitrate'] == 0) {
    345                 $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
     345                $this->error('Corrupt Ogg file: bitrate_audio == zero');
    346346                return false;
    347347            }
     
    396396        $filedataoffset += 4;
    397397        if ($info['ogg']['samplerate'] == 0) {
    398             $info['error'][] = 'Corrupt Ogg file: sample rate == zero';
     398            $this->error('Corrupt Ogg file: sample rate == zero');
    399399            return false;
    400400        }
     
    444444
    445445        if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
    446             $info['error'][] = 'Unknown opus version number (only accepting 1-15)';
     446            $this->error('Unknown opus version number (only accepting 1-15)');
    447447            return false;
    448448        }
     
    452452
    453453        if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
    454             $info['error'][] = 'Invalid channel count in opus header (must not be zero)';
     454            $this->error('Invalid channel count in opus header (must not be zero)');
    455455            return false;
    456456        }
     
    563563            default:
    564564                return false;
     565                break;
    565566        }
    566567
     
    581582            if ($i >= 10000) {
    582583                // https://github.com/owncloud/music/issues/212#issuecomment-43082336
    583                 $info['warning'][] = 'Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments';
     584                $this->warning('Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments');
    584585                break;
    585586            }
     
    619620            while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
    620621                if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
    621                     $info['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments';
     622                    $this->warning('Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments');
    622623                    break 2;
    623624                }
     
    643644                //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
    644645                if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
    645                     $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
     646                    $this->warning('undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
    646647                    break;
    647648                }
    648649                $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
    649650                if ($readlength <= 0) {
    650                     $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
     651                    $this->warning('invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell());
    651652                    break;
    652653                }
     
    662663
    663664                // no comment?
    664                 $info['warning'][] = 'Blank Ogg comment ['.$i.']';
     665                $this->warning('Blank Ogg comment ['.$i.']');
    665666
    666667            } elseif (strstr($commentstring, '=')) {
     
    712713            } else {
    713714
    714                 $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
     715                $this->warning('[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring);
    715716
    716717            }
  • trunk/src/wp-includes/ID3/module.tag.apetag.php

    r32979 r41196  
    2424
    2525        if (!getid3_lib::intValueSupported($info['filesize'])) {
    26             $info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
     26            $this->warning('Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB');
    2727            return false;
    2828        }
     
    7373        $APEfooterData = $this->fread(32);
    7474        if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
    75             $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
     75            $this->error('Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']);
    7676            return false;
    7777        }
     
    8989
    9090        if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
    91             $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
     91            $this->warning('ID3v1 tag information ignored since it appears to be a false synch in APEtag data');
    9292            unset($info['id3v1']);
    9393            foreach ($info['warning'] as $key => $value) {
     
    105105                $offset += $apetagheadersize;
    106106            } else {
    107                 $info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
     107                $this->error('Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']);
    108108                return false;
    109109            }
     
    120120            $offset += 4;
    121121            if (strstr(substr($APEtagData, $offset), "\x00") === false) {
    122                 $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
     122                $this->error('Cannot find null-byte (0x00) separator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset));
    123123                return false;
    124124            }
     
    155155                        $thisfile_replaygain['track']['originator'] = 'unspecified';
    156156                    } else {
    157                         $info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     157                        $this->warning('MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    158158                    }
    159159                    break;
     
    164164                        $thisfile_replaygain['track']['originator'] = 'unspecified';
    165165                        if ($thisfile_replaygain['track']['peak'] <= 0) {
    166                             $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
    167                         }
    168                     } else {
    169                         $info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     166                            $this->warning('ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
     167                        }
     168                    } else {
     169                        $this->warning('MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    170170                    }
    171171                    break;
     
    176176                        $thisfile_replaygain['album']['originator'] = 'unspecified';
    177177                    } else {
    178                         $info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     178                        $this->warning('MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    179179                    }
    180180                    break;
     
    185185                        $thisfile_replaygain['album']['originator'] = 'unspecified';
    186186                        if ($thisfile_replaygain['album']['peak'] <= 0) {
    187                             $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
    188                         }
    189                     } else {
    190                         $info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     187                            $this->warning('ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
     188                        }
     189                    } else {
     190                        $this->warning('MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    191191                    }
    192192                    break;
     
    199199                        $thisfile_replaygain['mp3gain']['undo_wrap']  = (($mp3gain_undo_wrap == 'Y') ? true : false);
    200200                    } else {
    201                         $info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     201                        $this->warning('MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    202202                    }
    203203                    break;
     
    209209                        $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
    210210                    } else {
    211                         $info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     211                        $this->warning('MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    212212                    }
    213213                    break;
     
    219219                        $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
    220220                    } else {
    221                         $info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
     221                        $this->warning('MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
    222222                    }
    223223                    break;
     
    254254                    // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
    255255                    if (is_array($thisfile_ape_items_current['data'])) {
    256                         $info['warning'][] = 'APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
     256                        $this->warning('APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8');
    257257                        $thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
    258258                    }
     
    261261                    $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
    262262
    263                     $thisfile_ape_items_current['image_mime'] = '';
    264                     $imageinfo = array();
    265                     $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
    266                     $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
    267 
    268263                    do {
     264                        $thisfile_ape_items_current['image_mime'] = '';
     265                        $imageinfo = array();
     266                        $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
     267                        if (($imagechunkcheck === false) || !isset($imagechunkcheck[2])) {
     268                            $this->warning('APEtag "'.$item_key.'" contains invalid image data');
     269                            break;
     270                        }
     271                        $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
     272
    269273                        if ($this->inline_attachments === false) {
    270274                            // skip entirely
     
    277281                            if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
    278282                                // too big, skip
    279                                 $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)';
     283                                $this->warning('attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)');
    280284                                unset($thisfile_ape_items_current['data']);
    281285                                break;
     
    283287                        } elseif (is_string($this->inline_attachments)) {
    284288                            $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
    285                             if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
     289                            if (!is_dir($this->inline_attachments) || !getID3::is_writable($this->inline_attachments)) {
    286290                                // cannot write, skip
    287                                 $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
     291                                $this->warning('attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)');
    288292                                unset($thisfile_ape_items_current['data']);
    289293                                break;
     
    293297                        if (is_string($this->inline_attachments)) {
    294298                            $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset'];
    295                             if (!file_exists($destination_filename) || is_writable($destination_filename)) {
     299                            if (!file_exists($destination_filename) || getID3::is_writable($destination_filename)) {
    296300                                file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
    297301                            } else {
    298                                 $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)';
     302                                $this->warning('attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)');
    299303                            }
    300304                            $thisfile_ape_items_current['data_filename'] = $destination_filename;
  • trunk/src/wp-includes/ID3/module.tag.id3v1.php

    r32979 r41196  
    2323
    2424        if (!getid3_lib::intValueSupported($info['filesize'])) {
    25             $info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
     25            $this->warning('Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB');
    2626            return false;
    2727        }
     
    6161                $ParsedID3v1['comments'][$key][0] = $value;
    6262            }
     63            // ID3v1 encoding detection hack START
     64            // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
     65            // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
     66            $ID3v1encoding = 'ISO-8859-1';
     67            foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
     68                foreach ($valuearray as $key => $value) {
     69                    if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
     70                        foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
     71                            if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
     72                                $ID3v1encoding = $id3v1_bad_encoding;
     73                                break 3;
     74                            } elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
     75                                $ID3v1encoding = $id3v1_bad_encoding;
     76                                break 3;
     77                            }
     78                        }
     79                    }
     80                }
     81            }
     82            // ID3v1 encoding detection hack END
    6383
    6484            // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
     
    7494            if ($id3v1tag !== $GoodFormatID3v1tag) {
    7595                $ParsedID3v1['padding_valid'] = false;
    76                 $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
     96                $this->warning('Some ID3v1 fields do not use NULL characters for padding');
    7797            }
    7898
     
    81101
    82102            $info['id3v1'] = $ParsedID3v1;
     103            $info['id3v1']['encoding'] = $ID3v1encoding;
    83104        }
    84105
     
    96117            } else {
    97118                // APE and Lyrics3 footers not found - assume double ID3v1
    98                 $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
     119                $this->warning('Duplicate ID3v1 tag detected - this has been known to happen with iTunes');
    99120                $info['avdataend'] -= 128;
    100121            }
  • trunk/src/wp-includes/ID3/module.tag.id3v2.php

    r32979 r41196  
    7272        if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
    7373
    74             $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion'];
     74            $this->error('this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']);
    7575            return false;
    7676
     
    242242
    243243                    if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
    244                         $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')';
     244                        $this->warning('ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')');
    245245                    }
    246246                }
     
    261261                            $thisfile_id3v2['padding']['valid'] = false;
    262262                            $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
    263                             $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
     263                            $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
    264264                            break;
    265265                        }
     
    301301                            // MP3ext known broken frames - "ok" for the purposes of this test
    302302                        } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
    303                             $info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3';
     303                            $this->warning('ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3');
    304304                            $id3v2_majorversion = 3;
    305305                            $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
     
    323323                            $thisfile_id3v2['padding']['valid'] = false;
    324324                            $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
    325                             $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
     325                            $this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
    326326                            break;
    327327                        }
     
    330330                }
    331331
    332                 if ($frame_name == 'COM ') {
    333                     $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]';
    334                     $frame_name = 'COMM';
     332                if ($iTunesBrokenFrameNameFixed = self::ID3v22iTunesBrokenFrameName($frame_name)) {
     333                    $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1", "v7.0.0.70" are known-guilty, probably others too)]. Translated frame name from "'.str_replace("\x00", ' ', $frame_name).'" to "'.$iTunesBrokenFrameNameFixed.'" for parsing.');
     334                    $frame_name = $iTunesBrokenFrameNameFixed;
    335335                }
    336336                if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
     
    356356                            // next frame is valid, just skip the current frame
    357357                            $framedata = substr($framedata, $frame_size);
    358                             $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.';
     358                            $this->warning('Next ID3v2 frame is valid, skipping current frame.');
    359359
    360360                        } else {
     
    363363                            //unset($framedata);
    364364                            $framedata = null;
    365                             $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.';
     365                            $this->error('Next ID3v2 frame is also invalid, aborting processing.');
    366366
    367367                        }
     
    370370
    371371                        // this is the last frame, just skip
    372                         $info['warning'][] = 'This was the last ID3v2 frame.';
     372                        $this->warning('This was the last ID3v2 frame.');
    373373
    374374                    } else {
     
    377377                        //unset($framedata);
    378378                        $framedata = null;
    379                         $info['warning'][] = 'Invalid ID3v2 frame size, aborting.';
     379                        $this->warning('Invalid ID3v2 frame size, aborting.');
    380380
    381381                    }
     
    390390                            case ' MP':
    391391                            case 'MP3':
    392                                 $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]';
     392                                $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]');
    393393                                break;
    394394
    395395                            default:
    396                                 $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).';
     396                                $this->warning('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).');
    397397                                break;
    398398                        }
     
    400400                    } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
    401401
    402                         $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).';
     402                        $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).');
    403403
    404404                    } else {
    405405
    406                         $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).';
     406                        $this->error('error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).');
    407407
    408408                    }
     
    443443
    444444        if (isset($thisfile_id3v2['comments']['genre'])) {
     445            $genres = array();
    445446            foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
    446                 unset($thisfile_id3v2['comments']['genre'][$key]);
    447                 $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value)));
    448             }
     447                foreach ($this->ParseID3v2GenreString($value) as $genre) {
     448                    $genres[] = $genre;
     449                }
     450            }
     451            $thisfile_id3v2['comments']['genre'] = array_unique($genres);
     452            unset($key, $value, $genres, $genre);
    449453        }
    450454
     
    501505        // ID3v2.4.x: '21' $00 'Eurodisco' $00
    502506        $clean_genres = array();
     507
     508        // hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags
     509        if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
     510            // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
     511            // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
     512            if (preg_match('#/#', $genrestring)) {
     513                $genrestring = str_replace('/', "\x00", $genrestring);
     514                $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
     515                $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
     516            }
     517
     518            // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
     519            if (preg_match('#;#', $genrestring)) {
     520                $genrestring = str_replace(';', "\x00", $genrestring);
     521            }
     522        }
     523
     524
    503525        if (strpos($genrestring, "\x00") === false) {
    504526            $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
    505527        }
     528
    506529        $genre_elements = explode("\x00", $genrestring);
    507530        foreach ($genre_elements as $element) {
     
    572595                $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
    573596                if (!function_exists('gzuncompress')) {
    574                     $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"';
     597                    $this->warning('gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"');
    575598                } else {
    576599                    if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
     
    579602                        unset($decompresseddata);
    580603                    } else {
    581                         $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"';
     604                        $this->warning('gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"');
    582605                    }
    583606                }
     
    587610        if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
    588611            if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
    589                 $info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data';
     612                $this->warning('ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data');
    590613            }
    591614        }
     
    602625                    break;
    603626            }
    604             $info['warning'][] = $warning;
     627            $this->warning($warning);
    605628
    606629        } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1   UFID Unique file identifier
     
    628651            $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
    629652            if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
    630                 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
     653                $this->warning('Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding');
    631654                $frame_textencoding_terminator = "\x00";
    632655            }
     
    636659            }
    637660            $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
    638             if (ord($frame_description) === 0) {
     661            if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {