Make WordPress Core

Ticket #23673: 23673.diff

File 23673.diff, 939.7 KB (added by wonderboymusic, 12 years ago)
  • wp-admin/includes/image.php

    diff --git wp-admin/includes/image.php wp-admin/includes/image.php
    index a842287..34e726c 100644
    function wp_generate_attachment_metadata( $attachment_id, $file ) { 
    116116                if ( $image_meta )
    117117                        $metadata['image_meta'] = $image_meta;
    118118
     119        } elseif ( preg_match( '#^video/#', get_post_mime_type( $attachment ) ) ) {
     120                $metadata = wp_read_video_metadata( $file );
     121        } elseif ( preg_match( '#^audio/#', get_post_mime_type( $attachment ) ) ) {
     122                $metadata = wp_read_audio_metadata( $file );
    119123        }
    120124
    121125        return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id );
  • new file wp-includes/ID3/class-getid3.php

    diff --git wp-includes/ID3/class-getid3.php wp-includes/ID3/class-getid3.php
    new file mode 100644
    index 0000000..8c18163
    - +  
     1<?php
     2/////////////////////////////////////////////////////////////////
     3/// getID3() by James Heinrich <info@getid3.org>               //
     4//  available at http://getid3.sourceforge.net                 //
     5//            or http://www.getid3.org                         //
     6/////////////////////////////////////////////////////////////////
     7//                                                             //
     8// Please see readme.txt for more information                  //
     9//                                                            ///
     10/////////////////////////////////////////////////////////////////
     11
     12// define a constant rather than looking up every time it is needed
     13if (!defined('GETID3_OS_ISWINDOWS')) {
     14        define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
     15}
     16// Get base path of getID3() - ONCE
     17if (!defined('GETID3_INCLUDEPATH')) {
     18        define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
     19}
     20
     21// attempt to define temp dir as something flexible but reliable
     22$temp_dir = ini_get('upload_tmp_dir');
     23if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
     24        $temp_dir = '';
     25}
     26if (!$temp_dir && function_exists('sys_get_temp_dir')) {
     27        // PHP v5.2.1+
     28        // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
     29        $temp_dir = sys_get_temp_dir();
     30}
     31$temp_dir = realpath($temp_dir);
     32$open_basedir = ini_get('open_basedir');
     33if ($open_basedir) {
     34        // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
     35        $temp_dir     = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
     36        $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
     37        if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
     38                $temp_dir .= DIRECTORY_SEPARATOR;
     39        }
     40        $found_valid_tempdir = false;
     41        $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
     42        foreach ($open_basedirs as $basedir) {
     43                if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
     44                        $basedir .= DIRECTORY_SEPARATOR;
     45                }
     46                if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
     47                        $found_valid_tempdir = true;
     48                        break;
     49                }
     50        }
     51        if (!$found_valid_tempdir) {
     52                $temp_dir = '';
     53        }
     54        unset($open_basedirs, $found_valid_tempdir, $basedir);
     55}
     56if (!$temp_dir) {
     57        $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
     58}
     59// $temp_dir = '/something/else/';  // feel free to override temp dir here if it works better for your system
     60define('GETID3_TEMP_DIR', $temp_dir);
     61unset($open_basedir, $temp_dir);
     62
     63// End: Defines
     64
     65
     66class getID3
     67{
     68        // public: Settings
     69        public $encoding        = 'UTF-8';        // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
     70        public $encoding_id3v1  = 'ISO-8859-1';   // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
     71
     72        // public: Optional tag checks - disable for speed.
     73        public $option_tag_id3v1         = true;  // Read and process ID3v1 tags
     74        public $option_tag_id3v2         = true;  // Read and process ID3v2 tags
     75        public $option_tag_lyrics3       = true;  // Read and process Lyrics3 tags
     76        public $option_tag_apetag        = true;  // Read and process APE tags
     77        public $option_tags_process      = true;  // Copy tags to root key 'tags' and encode to $this->encoding
     78        public $option_tags_html         = true;  // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
     79
     80        // public: Optional tag/comment calucations
     81        public $option_extra_info        = true;  // Calculate additional info such as bitrate, channelmode etc
     82
     83        // public: Optional handling of embedded attachments (e.g. images)
     84        public $option_save_attachments  = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
     85
     86        // public: Optional calculations
     87        public $option_md5_data          = false; // Get MD5 sum of data part - slow
     88        public $option_md5_data_source   = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
     89        public $option_sha1_data         = false; // Get SHA1 sum of data part - slow
     90        public $option_max_2gb_check     = null;  // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX)
     91
     92        // public: Read buffer size in bytes
     93        public $option_fread_buffer_size = 32768;
     94
     95        // Public variables
     96        public $filename;                         // Filename of file being analysed.
     97        public $fp;                               // Filepointer to file being analysed.
     98        public $info;                             // Result array.
     99        public $tempdir = GETID3_TEMP_DIR;
     100
     101        // Protected variables
     102        protected $startup_error   = '';
     103        protected $startup_warning = '';
     104        protected $memory_limit    = 0;
     105
     106        const VERSION           = '1.9.5-20130220';
     107        const FREAD_BUFFER_SIZE = 32768;
     108
     109        const ATTACHMENTS_NONE   = false;
     110        const ATTACHMENTS_INLINE = true;
     111
     112        // public: constructor
     113        public function __construct() {
     114
     115                // Check for PHP version
     116                $required_php_version = '5.0.5';
     117                if (version_compare(PHP_VERSION, $required_php_version, '<')) {
     118                        $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
     119                        return false;
     120                }
     121
     122                // Check memory
     123                $this->memory_limit = ini_get('memory_limit');
     124                if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
     125                        // could be stored as "16M" rather than 16777216 for example
     126                        $this->memory_limit = $matches[1] * 1048576;
     127                } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
     128                        // could be stored as "2G" rather than 2147483648 for example
     129                        $this->memory_limit = $matches[1] * 1073741824;
     130                }
     131                if ($this->memory_limit <= 0) {
     132                        // memory limits probably disabled
     133                } elseif ($this->memory_limit <= 4194304) {
     134                        $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
     135                } elseif ($this->memory_limit <= 12582912) {
     136                        $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';
     137                }
     138
     139                // Check safe_mode off
     140                if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
     141                        $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
     142                }
     143
     144                if (intval(ini_get('mbstring.func_overload')) > 0) {
     145                        $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
     146                }
     147
     148                // Check for magic_quotes_runtime
     149                if (function_exists('get_magic_quotes_runtime')) {
     150                        if (get_magic_quotes_runtime()) {
     151                                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).');
     152                        }
     153                }
     154
     155                // Check for magic_quotes_gpc
     156                if (function_exists('magic_quotes_gpc')) {
     157                        if (get_magic_quotes_gpc()) {
     158                                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).');
     159                        }
     160                }
     161
     162                // Load support library
     163                if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
     164                        $this->startup_error .= 'getid3.lib.php is missing or corrupt';
     165                }
     166
     167                if ($this->option_max_2gb_check === null) {
     168                        $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
     169                }
     170
     171
     172                // Needed for Windows only:
     173                // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
     174                //   as well as other helper functions such as head, tail, md5sum, etc
     175                // This path cannot contain spaces, but the below code will attempt to get the
     176                //   8.3-equivalent path automatically
     177                // IMPORTANT: This path must include the trailing slash
     178                if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
     179
     180                        $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
     181
     182                        if (!is_dir($helperappsdir)) {
     183                                $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
     184                        } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
     185                                $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
     186                                $path_so_far = array();
     187                                foreach ($DirPieces as $key => $value) {
     188                                        if (strpos($value, ' ') !== false) {
     189                                                if (!empty($path_so_far)) {
     190                                                        $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
     191                                                        $dir_listing = `$commandline`;
     192                                                        $lines = explode("\n", $dir_listing);
     193                                                        foreach ($lines as $line) {
     194                                                                $line = trim($line);
     195                                                                if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
     196                                                                        list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
     197                                                                        if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
     198                                                                                $value = $shortname;
     199                                                                        }
     200                                                                }
     201                                                        }
     202                                                } else {
     203                                                        $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.';
     204                                                }
     205                                        }
     206                                        $path_so_far[] = $value;
     207                                }
     208                                $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
     209                        }
     210                        define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
     211                }
     212
     213                return true;
     214        }
     215
     216        public function version() {
     217                return self::VERSION;
     218        }
     219
     220        public function fread_buffer_size() {
     221                return $this->option_fread_buffer_size;
     222        }
     223
     224        // public: setOption
     225        public function setOption($optArray) {
     226                if (!is_array($optArray) || empty($optArray)) {
     227                        return false;
     228                }
     229                foreach ($optArray as $opt => $val) {
     230                        if (isset($this->$opt) === false) {
     231                                continue;
     232                        }
     233                        $this->$opt = $val;
     234                }
     235                return true;
     236        }
     237
     238        public function openfile($filename) {
     239                try {
     240                        if (!empty($this->startup_error)) {
     241                                throw new getid3_exception($this->startup_error);
     242                        }
     243                        if (!empty($this->startup_warning)) {
     244                                $this->warning($this->startup_warning);
     245                        }
     246
     247                        // init result array and set parameters
     248                        $this->filename = $filename;
     249                        $this->info = array();
     250                        $this->info['GETID3_VERSION']   = $this->version();
     251                        $this->info['php_memory_limit'] = $this->memory_limit;
     252
     253                        // remote files not supported
     254                        if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
     255                                throw new getid3_exception('Remote files are not supported - please copy the file locally first');
     256                        }
     257
     258                        $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
     259                        $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
     260
     261                        // open local file
     262                        if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
     263                                // great
     264                        } else {
     265                                throw new getid3_exception('Could not open "'.$filename.'" (does not exist, or is not a file)');
     266                        }
     267
     268                        $this->info['filesize'] = filesize($filename);
     269                        // set redundant parameters - might be needed in some include file
     270                        $this->info['filename']     = basename($filename);
     271                        $this->info['filepath']     = str_replace('\\', '/', realpath(dirname($filename)));
     272                        $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
     273
     274
     275                        // option_max_2gb_check
     276                        if ($this->option_max_2gb_check) {
     277                                // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
     278                                // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
     279                                // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
     280                                $fseek = fseek($this->fp, 0, SEEK_END);
     281                                if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
     282                                        ($this->info['filesize'] < 0) ||
     283                                        (ftell($this->fp) < 0)) {
     284                                                $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
     285
     286                                                if ($real_filesize === false) {
     287                                                        unset($this->info['filesize']);
     288                                                        fclose($this->fp);
     289                                                        throw new getid3_exception('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.');
     290                                                } elseif (getid3_lib::intValueSupported($real_filesize)) {
     291                                                        unset($this->info['filesize']);
     292                                                        fclose($this->fp);
     293                                                        throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
     294                                                }
     295                                                $this->info['filesize'] = $real_filesize;
     296                                                $this->error('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
     297                                }
     298                        }
     299
     300                        // set more parameters
     301                        $this->info['avdataoffset']        = 0;
     302                        $this->info['avdataend']           = $this->info['filesize'];
     303                        $this->info['fileformat']          = '';                // filled in later
     304                        $this->info['audio']['dataformat'] = '';                // filled in later, unset if not used
     305                        $this->info['video']['dataformat'] = '';                // filled in later, unset if not used
     306                        $this->info['tags']                = array();           // filled in later, unset if not used
     307                        $this->info['error']               = array();           // filled in later, unset if not used
     308                        $this->info['warning']             = array();           // filled in later, unset if not used
     309                        $this->info['comments']            = array();           // filled in later, unset if not used
     310                        $this->info['encoding']            = $this->encoding;   // required by id3v2 and iso modules - can be unset at the end if desired
     311
     312                        return true;
     313
     314                } catch (Exception $e) {
     315                        $this->error($e->getMessage());
     316                }
     317                return false;
     318        }
     319
     320        // public: analyze file
     321        public function analyze($filename) {
     322                try {
     323                        if (!$this->openfile($filename)) {
     324                                return $this->info;
     325                        }
     326
     327                        // Handle tags
     328                        foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
     329                                $option_tag = 'option_tag_'.$tag_name;
     330                                if ($this->$option_tag) {
     331                                        $this->include_module('tag.'.$tag_name);
     332                                        try {
     333                                                $tag_class = 'getid3_'.$tag_name;
     334                                                $tag = new $tag_class($this);
     335                                                $tag->Analyze();
     336                                        }
     337                                        catch (getid3_exception $e) {
     338                                                throw $e;
     339                                        }
     340                                }
     341                        }
     342                        if (isset($this->info['id3v2']['tag_offset_start'])) {
     343                                $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
     344                        }
     345                        foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
     346                                if (isset($this->info[$tag_key]['tag_offset_start'])) {
     347                                        $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
     348                                }
     349                        }
     350
     351                        // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
     352                        if (!$this->option_tag_id3v2) {
     353                                fseek($this->fp, 0, SEEK_SET);
     354                                $header = fread($this->fp, 10);
     355                                if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
     356                                        $this->info['id3v2']['header']        = true;
     357                                        $this->info['id3v2']['majorversion']  = ord($header{3});
     358                                        $this->info['id3v2']['minorversion']  = ord($header{4});
     359                                        $this->info['avdataoffset']          += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
     360                                }
     361                        }
     362
     363                        // read 32 kb file data
     364                        fseek($this->fp, $this->info['avdataoffset'], SEEK_SET);
     365                        $formattest = fread($this->fp, 32774);
     366
     367                        // determine format
     368                        $determined_format = $this->GetFileFormat($formattest, $filename);
     369
     370                        // unable to determine file format
     371                        if (!$determined_format) {
     372                                fclose($this->fp);
     373                                return $this->error('unable to determine file format');
     374                        }
     375
     376                        // check for illegal ID3 tags
     377                        if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
     378                                if ($determined_format['fail_id3'] === 'ERROR') {
     379                                        fclose($this->fp);
     380                                        return $this->error('ID3 tags not allowed on this file type.');
     381                                } elseif ($determined_format['fail_id3'] === 'WARNING') {
     382                                        $this->warning('ID3 tags not allowed on this file type.');
     383                                }
     384                        }
     385
     386                        // check for illegal APE tags
     387                        if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
     388                                if ($determined_format['fail_ape'] === 'ERROR') {
     389                                        fclose($this->fp);
     390                                        return $this->error('APE tags not allowed on this file type.');
     391                                } elseif ($determined_format['fail_ape'] === 'WARNING') {
     392                                        $this->warning('APE tags not allowed on this file type.');
     393                                }
     394                        }
     395
     396                        // set mime type
     397                        $this->info['mime_type'] = $determined_format['mime_type'];
     398
     399                        // supported format signature pattern detected, but module deleted
     400                        if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
     401                                fclose($this->fp);
     402                                return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
     403                        }
     404
     405                        // module requires iconv support
     406                        // Check encoding/iconv support
     407                        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'))) {
     408                                $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. ';
     409                                if (GETID3_OS_ISWINDOWS) {
     410                                        $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';
     411                                } else {
     412                                        $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
     413                                }
     414                                return $this->error($errormessage);
     415                        }
     416
     417                        // include module
     418                        include_once(GETID3_INCLUDEPATH.$determined_format['include']);
     419
     420                        // instantiate module class
     421                        $class_name = 'getid3_'.$determined_format['module'];
     422                        if (!class_exists($class_name)) {
     423                                return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
     424                        }
     425                        $class = new $class_name($this);
     426                        $class->Analyze();
     427                        unset($class);
     428
     429                        // close file
     430                        fclose($this->fp);
     431
     432                        // process all tags - copy to 'tags' and convert charsets
     433                        if ($this->option_tags_process) {
     434                                $this->HandleAllTags();
     435                        }
     436
     437                        // perform more calculations
     438                        if ($this->option_extra_info) {
     439                                $this->ChannelsBitratePlaytimeCalculations();
     440                                $this->CalculateCompressionRatioVideo();
     441                                $this->CalculateCompressionRatioAudio();
     442                                $this->CalculateReplayGain();
     443                                $this->ProcessAudioStreams();
     444                        }
     445
     446                        // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
     447                        if ($this->option_md5_data) {
     448                                // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
     449                                if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
     450                                        $this->getHashdata('md5');
     451                                }
     452                        }
     453
     454                        // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
     455                        if ($this->option_sha1_data) {
     456                                $this->getHashdata('sha1');
     457                        }
     458
     459                        // remove undesired keys
     460                        $this->CleanUp();
     461
     462                } catch (Exception $e) {
     463                        $this->error('Caught exception: '.$e->getMessage());
     464                }
     465
     466                // return info array
     467                return $this->info;
     468        }
     469
     470
     471        // private: error handling
     472        public function error($message) {
     473                $this->CleanUp();
     474                if (!isset($this->info['error'])) {
     475                        $this->info['error'] = array();
     476                }
     477                $this->info['error'][] = $message;
     478                return $this->info;
     479        }
     480
     481
     482        // private: warning handling
     483        public function warning($message) {
     484                $this->info['warning'][] = $message;
     485                return true;
     486        }
     487
     488
     489        // private: CleanUp
     490        private function CleanUp() {
     491
     492                // remove possible empty keys
     493                $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
     494                foreach ($AVpossibleEmptyKeys as $dummy => $key) {
     495                        if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
     496                                unset($this->info['audio'][$key]);
     497                        }
     498                        if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
     499                                unset($this->info['video'][$key]);
     500                        }
     501                }
     502
     503                // remove empty root keys
     504                if (!empty($this->info)) {
     505                        foreach ($this->info as $key => $value) {
     506                                if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
     507                                        unset($this->info[$key]);
     508                                }
     509                        }
     510                }
     511
     512                // remove meaningless entries from unknown-format files
     513                if (empty($this->info['fileformat'])) {
     514                        if (isset($this->info['avdataoffset'])) {
     515                                unset($this->info['avdataoffset']);
     516                        }
     517                        if (isset($this->info['avdataend'])) {
     518                                unset($this->info['avdataend']);
     519                        }
     520                }
     521
     522                // remove possible duplicated identical entries
     523                if (!empty($this->info['error'])) {
     524                        $this->info['error'] = array_values(array_unique($this->info['error']));
     525                }
     526                if (!empty($this->info['warning'])) {
     527                        $this->info['warning'] = array_values(array_unique($this->info['warning']));
     528                }
     529
     530                // remove "global variable" type keys
     531                unset($this->info['php_memory_limit']);
     532
     533                return true;
     534        }
     535
     536
     537        // return array containing information about all supported formats
     538        public function GetFileFormatArray() {
     539                static $format_info = array();
     540                if (empty($format_info)) {
     541                        $format_info = array(
     542
     543                                // Audio formats
     544
     545                                // AC-3   - audio      - Dolby AC-3 / Dolby Digital
     546                                'ac3'  => array(
     547                                                        'pattern'   => '^\x0B\x77',
     548                                                        'group'     => 'audio',
     549                                                        'module'    => 'ac3',
     550                                                        'mime_type' => 'audio/ac3',
     551                                                ),
     552
     553                                // AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
     554                                'adif' => array(
     555                                                        'pattern'   => '^ADIF',
     556                                                        'group'     => 'audio',
     557                                                        'module'    => 'aac',
     558                                                        'mime_type' => 'application/octet-stream',
     559                                                        'fail_ape'  => 'WARNING',
     560                                                ),
     561
     562/*
     563                                // AA   - audio       - Audible Audiobook
     564                                'aa'   => array(
     565                                                        'pattern'   => '^.{4}\x57\x90\x75\x36',
     566                                                        'group'     => 'audio',
     567                                                        'module'    => 'aa',
     568                                                        'mime_type' => 'audio/audible',
     569                                                ),
     570*/
     571                                // AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
     572                                'adts' => array(
     573                                                        'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
     574                                                        'group'     => 'audio',
     575                                                        'module'    => 'aac',
     576                                                        'mime_type' => 'application/octet-stream',
     577                                                        'fail_ape'  => 'WARNING',
     578                                                ),
     579
     580
     581                                // AU   - audio       - NeXT/Sun AUdio (AU)
     582                                'au'   => array(
     583                                                        'pattern'   => '^\.snd',
     584                                                        'group'     => 'audio',
     585                                                        'module'    => 'au',
     586                                                        'mime_type' => 'audio/basic',
     587                                                ),
     588
     589                                // AVR  - audio       - Audio Visual Research
     590                                'avr'  => array(
     591                                                        'pattern'   => '^2BIT',
     592                                                        'group'     => 'audio',
     593                                                        'module'    => 'avr',
     594                                                        'mime_type' => 'application/octet-stream',
     595                                                ),
     596
     597                                // BONK - audio       - Bonk v0.9+
     598                                'bonk' => array(
     599                                                        'pattern'   => '^\x00(BONK|INFO|META| ID3)',
     600                                                        'group'     => 'audio',
     601                                                        'module'    => 'bonk',
     602                                                        'mime_type' => 'audio/xmms-bonk',
     603                                                ),
     604
     605                                // DSS  - audio       - Digital Speech Standard
     606                                'dss'  => array(
     607                                                        'pattern'   => '^[\x02-\x03]ds[s2]',
     608                                                        'group'     => 'audio',
     609                                                        'module'    => 'dss',
     610                                                        'mime_type' => 'application/octet-stream',
     611                                                ),
     612
     613                                // DTS  - audio       - Dolby Theatre System
     614                                'dts'  => array(
     615                                                        'pattern'   => '^\x7F\xFE\x80\x01',
     616                                                        'group'     => 'audio',
     617                                                        'module'    => 'dts',
     618                                                        'mime_type' => 'audio/dts',
     619                                                ),
     620
     621                                // FLAC - audio       - Free Lossless Audio Codec
     622                                'flac' => array(
     623                                                        'pattern'   => '^fLaC',
     624                                                        'group'     => 'audio',
     625                                                        'module'    => 'flac',
     626                                                        'mime_type' => 'audio/x-flac',
     627                                                ),
     628
     629                                // LA   - audio       - Lossless Audio (LA)
     630                                'la'   => array(
     631                                                        'pattern'   => '^LA0[2-4]',
     632                                                        'group'     => 'audio',
     633                                                        'module'    => 'la',
     634                                                        'mime_type' => 'application/octet-stream',
     635                                                ),
     636
     637                                // LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
     638                                'lpac' => array(
     639                                                        'pattern'   => '^LPAC',
     640                                                        'group'     => 'audio',
     641                                                        'module'    => 'lpac',
     642                                                        'mime_type' => 'application/octet-stream',
     643                                                ),
     644
     645                                // MIDI - audio       - MIDI (Musical Instrument Digital Interface)
     646                                'midi' => array(
     647                                                        'pattern'   => '^MThd',
     648                                                        'group'     => 'audio',
     649                                                        'module'    => 'midi',
     650                                                        'mime_type' => 'audio/midi',
     651                                                ),
     652
     653                                // MAC  - audio       - Monkey's Audio Compressor
     654                                'mac'  => array(
     655                                                        'pattern'   => '^MAC ',
     656                                                        'group'     => 'audio',
     657                                                        'module'    => 'monkey',
     658                                                        'mime_type' => 'application/octet-stream',
     659                                                ),
     660
     661// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
     662//                              // MOD  - audio       - MODule (assorted sub-formats)
     663//                              'mod'  => array(
     664//                                                      'pattern'   => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
     665//                                                      'group'     => 'audio',
     666//                                                      'module'    => 'mod',
     667//                                                      'option'    => 'mod',
     668//                                                      'mime_type' => 'audio/mod',
     669//                                              ),
     670
     671                                // MOD  - audio       - MODule (Impulse Tracker)
     672                                'it'   => array(
     673                                                        'pattern'   => '^IMPM',
     674                                                        'group'     => 'audio',
     675                                                        'module'    => 'mod',
     676                                                        //'option'    => 'it',
     677                                                        'mime_type' => 'audio/it',
     678                                                ),
     679
     680                                // MOD  - audio       - MODule (eXtended Module, various sub-formats)
     681                                'xm'   => array(
     682                                                        'pattern'   => '^Extended Module',
     683                                                        'group'     => 'audio',
     684                                                        'module'    => 'mod',
     685                                                        //'option'    => 'xm',
     686                                                        'mime_type' => 'audio/xm',
     687                                                ),
     688
     689                                // MOD  - audio       - MODule (ScreamTracker)
     690                                's3m'  => array(
     691                                                        'pattern'   => '^.{44}SCRM',
     692                                                        'group'     => 'audio',
     693                                                        'module'    => 'mod',
     694                                                        //'option'    => 's3m',
     695                                                        'mime_type' => 'audio/s3m',
     696                                                ),
     697
     698                                // MPC  - audio       - Musepack / MPEGplus
     699                                'mpc'  => array(
     700                                                        '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])',
     701                                                        'group'     => 'audio',
     702                                                        'module'    => 'mpc',
     703                                                        'mime_type' => 'audio/x-musepack',
     704                                                ),
     705
     706                                // MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
     707                                'mp3'  => array(
     708                                                        '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]',
     709                                                        'group'     => 'audio',
     710                                                        'module'    => 'mp3',
     711                                                        'mime_type' => 'audio/mpeg',
     712                                                ),
     713
     714                                // OFR  - audio       - OptimFROG
     715                                'ofr'  => array(
     716                                                        'pattern'   => '^(\*RIFF|OFR)',
     717                                                        'group'     => 'audio',
     718                                                        'module'    => 'optimfrog',
     719                                                        'mime_type' => 'application/octet-stream',
     720                                                ),
     721
     722                                // RKAU - audio       - RKive AUdio compressor
     723                                'rkau' => array(
     724                                                        'pattern'   => '^RKA',
     725                                                        'group'     => 'audio',
     726                                                        'module'    => 'rkau',
     727                                                        'mime_type' => 'application/octet-stream',
     728                                                ),
     729
     730                                // SHN  - audio       - Shorten
     731                                'shn'  => array(
     732                                                        'pattern'   => '^ajkg',
     733                                                        'group'     => 'audio',
     734                                                        'module'    => 'shorten',
     735                                                        'mime_type' => 'audio/xmms-shn',
     736                                                        'fail_id3'  => 'ERROR',
     737                                                        'fail_ape'  => 'ERROR',
     738                                                ),
     739
     740                                // TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
     741                                'tta'  => array(
     742                                                        'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
     743                                                        'group'     => 'audio',
     744                                                        'module'    => 'tta',
     745                                                        'mime_type' => 'application/octet-stream',
     746                                                ),
     747
     748                                // VOC  - audio       - Creative Voice (VOC)
     749                                'voc'  => array(
     750                                                        'pattern'   => '^Creative Voice File',
     751                                                        'group'     => 'audio',
     752                                                        'module'    => 'voc',
     753                                                        'mime_type' => 'audio/voc',
     754                                                ),
     755
     756                                // VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
     757                                'vqf'  => array(
     758                                                        'pattern'   => '^TWIN',
     759                                                        'group'     => 'audio',
     760                                                        'module'    => 'vqf',
     761                                                        'mime_type' => 'application/octet-stream',
     762                                                ),
     763
     764                                // WV  - audio        - WavPack (v4.0+)
     765                                'wv'   => array(
     766                                                        'pattern'   => '^wvpk',
     767                                                        'group'     => 'audio',
     768                                                        'module'    => 'wavpack',
     769                                                        'mime_type' => 'application/octet-stream',
     770                                                ),
     771
     772
     773                                // Audio-Video formats
     774
     775                                // ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
     776                                'asf'  => array(
     777                                                        'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
     778                                                        'group'     => 'audio-video',
     779                                                        'module'    => 'asf',
     780                                                        'mime_type' => 'video/x-ms-asf',
     781                                                        'iconv_req' => false,
     782                                                ),
     783
     784                                // BINK - audio/video - Bink / Smacker
     785                                'bink' => array(
     786                                                        'pattern'   => '^(BIK|SMK)',
     787                                                        'group'     => 'audio-video',
     788                                                        'module'    => 'bink',
     789                                                        'mime_type' => 'application/octet-stream',
     790                                                ),
     791
     792                                // FLV  - audio/video - FLash Video
     793                                'flv' => array(
     794                                                        'pattern'   => '^FLV\x01',
     795                                                        'group'     => 'audio-video',
     796                                                        'module'    => 'flv',
     797                                                        'mime_type' => 'video/x-flv',
     798                                                ),
     799
     800                                // MKAV - audio/video - Mastroka
     801                                'matroska' => array(
     802                                                        'pattern'   => '^\x1A\x45\xDF\xA3',
     803                                                        'group'     => 'audio-video',
     804                                                        'module'    => 'matroska',
     805                                                        'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
     806                                                ),
     807
     808                                // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
     809                                'mpeg' => array(
     810                                                        'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
     811                                                        'group'     => 'audio-video',
     812                                                        'module'    => 'mpeg',
     813                                                        'mime_type' => 'video/mpeg',
     814                                                ),
     815
     816                                // NSV  - audio/video - Nullsoft Streaming Video (NSV)
     817                                'nsv'  => array(
     818                                                        'pattern'   => '^NSV[sf]',
     819                                                        'group'     => 'audio-video',
     820                                                        'module'    => 'nsv',
     821                                                        'mime_type' => 'application/octet-stream',
     822                                                ),
     823
     824                                // Ogg  - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
     825                                'ogg'  => array(
     826                                                        'pattern'   => '^OggS',
     827                                                        'group'     => 'audio',
     828                                                        'module'    => 'ogg',
     829                                                        'mime_type' => 'application/ogg',
     830                                                        'fail_id3'  => 'WARNING',
     831                                                        'fail_ape'  => 'WARNING',
     832                                                ),
     833
     834                                // QT   - audio/video - Quicktime
     835                                'quicktime' => array(
     836                                                        'pattern'   => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
     837                                                        'group'     => 'audio-video',
     838                                                        'module'    => 'quicktime',
     839                                                        'mime_type' => 'video/quicktime',
     840                                                ),
     841
     842                                // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
     843                                'riff' => array(
     844                                                        'pattern'   => '^(RIFF|SDSS|FORM)',
     845                                                        'group'     => 'audio-video',
     846                                                        'module'    => 'riff',
     847                                                        'mime_type' => 'audio/x-wave',
     848                                                        'fail_ape'  => 'WARNING',
     849                                                ),
     850
     851                                // Real - audio/video - RealAudio, RealVideo
     852                                'real' => array(
     853                                                        'pattern'   => '^(\\.RMF|\\.ra)',
     854                                                        'group'     => 'audio-video',
     855                                                        'module'    => 'real',
     856                                                        'mime_type' => 'audio/x-realaudio',
     857                                                ),
     858
     859                                // SWF - audio/video - ShockWave Flash
     860                                'swf' => array(
     861                                                        'pattern'   => '^(F|C)WS',
     862                                                        'group'     => 'audio-video',
     863                                                        'module'    => 'swf',
     864                                                        'mime_type' => 'application/x-shockwave-flash',
     865                                                ),
     866
     867                                // TS - audio/video - MPEG-2 Transport Stream
     868                                'ts' => array(
     869                                                        'pattern'   => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G".  Check for at least 10 packets matching this pattern
     870                                                        'group'     => 'audio-video',
     871                                                        'module'    => 'ts',
     872                                                        'mime_type' => 'video/MP2T',
     873                                                ),
     874
     875
     876                                // Still-Image formats
     877
     878                                // BMP  - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
     879                                'bmp'  => array(
     880                                                        'pattern'   => '^BM',
     881                                                        'group'     => 'graphic',
     882                                                        'module'    => 'bmp',
     883                                                        'mime_type' => 'image/bmp',
     884                                                        'fail_id3'  => 'ERROR',
     885                                                        'fail_ape'  => 'ERROR',
     886                                                ),
     887
     888                                // GIF  - still image - Graphics Interchange Format
     889                                'gif'  => array(
     890                                                        'pattern'   => '^GIF',
     891                                                        'group'     => 'graphic',
     892                                                        'module'    => 'gif',
     893                                                        'mime_type' => 'image/gif',
     894                                                        'fail_id3'  => 'ERROR',
     895                                                        'fail_ape'  => 'ERROR',
     896                                                ),
     897
     898                                // JPEG - still image - Joint Photographic Experts Group (JPEG)
     899                                'jpg'  => array(
     900                                                        'pattern'   => '^\xFF\xD8\xFF',
     901                                                        'group'     => 'graphic',
     902                                                        'module'    => 'jpg',
     903                                                        'mime_type' => 'image/jpeg',
     904                                                        'fail_id3'  => 'ERROR',
     905                                                        'fail_ape'  => 'ERROR',
     906                                                ),
     907
     908                                // PCD  - still image - Kodak Photo CD
     909                                'pcd'  => array(
     910                                                        'pattern'   => '^.{2048}PCD_IPI\x00',
     911                                                        'group'     => 'graphic',
     912                                                        'module'    => 'pcd',
     913                                                        'mime_type' => 'image/x-photo-cd',
     914                                                        'fail_id3'  => 'ERROR',
     915                                                        'fail_ape'  => 'ERROR',
     916                                                ),
     917
     918
     919                                // PNG  - still image - Portable Network Graphics (PNG)
     920                                'png'  => array(
     921                                                        'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
     922                                                        'group'     => 'graphic',
     923                                                        'module'    => 'png',
     924                                                        'mime_type' => 'image/png',
     925                                                        'fail_id3'  => 'ERROR',
     926                                                        'fail_ape'  => 'ERROR',
     927                                                ),
     928
     929
     930                                // SVG  - still image - Scalable Vector Graphics (SVG)
     931                                'svg'  => array(
     932                                                        'pattern'   => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
     933                                                        'group'     => 'graphic',
     934                                                        'module'    => 'svg',
     935                                                        'mime_type' => 'image/svg+xml',
     936                                                        'fail_id3'  => 'ERROR',
     937                                                        'fail_ape'  => 'ERROR',
     938                                                ),
     939
     940
     941                                // TIFF - still image - Tagged Information File Format (TIFF)
     942                                'tiff' => array(
     943                                                        'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
     944                                                        'group'     => 'graphic',
     945                                                        'module'    => 'tiff',
     946                                                        'mime_type' => 'image/tiff',
     947                                                        'fail_id3'  => 'ERROR',
     948                                                        'fail_ape'  => 'ERROR',
     949                                                ),
     950
     951
     952                                // EFAX - still image - eFax (TIFF derivative)
     953                                'efax'  => array(
     954                                                        'pattern'   => '^\xDC\xFE',
     955                                                        'group'     => 'graphic',
     956                                                        'module'    => 'efax',
     957                                                        'mime_type' => 'image/efax',
     958                                                        'fail_id3'  => 'ERROR',
     959                                                        'fail_ape'  => 'ERROR',
     960                                                ),
     961
     962
     963                                // Data formats
     964
     965                                // ISO  - data        - International Standards Organization (ISO) CD-ROM Image
     966                                'iso'  => array(
     967                                                        'pattern'   => '^.{32769}CD001',
     968                                                        'group'     => 'misc',
     969                                                        'module'    => 'iso',
     970                                                        'mime_type' => 'application/octet-stream',
     971                                                        'fail_id3'  => 'ERROR',
     972                                                        'fail_ape'  => 'ERROR',
     973                                                        'iconv_req' => false,
     974                                                ),
     975
     976                                // RAR  - data        - RAR compressed data
     977                                'rar'  => array(
     978                                                        'pattern'   => '^Rar\!',
     979                                                        'group'     => 'archive',
     980                                                        'module'    => 'rar',
     981                                                        'mime_type' => 'application/octet-stream',
     982                                                        'fail_id3'  => 'ERROR',
     983                                                        'fail_ape'  => 'ERROR',
     984                                                ),
     985
     986                                // SZIP - audio/data  - SZIP compressed data
     987                                'szip' => array(
     988                                                        'pattern'   => '^SZ\x0A\x04',
     989                                                        'group'     => 'archive',
     990                                                        'module'    => 'szip',
     991                                                        'mime_type' => 'application/octet-stream',
     992                                                        'fail_id3'  => 'ERROR',
     993                                                        'fail_ape'  => 'ERROR',
     994                                                ),
     995
     996                                // TAR  - data        - TAR compressed data
     997                                'tar'  => array(
     998                                                        '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}',
     999                                                        'group'     => 'archive',
     1000                                                        'module'    => 'tar',
     1001                                                        'mime_type' => 'application/x-tar',
     1002                                                        'fail_id3'  => 'ERROR',
     1003                                                        'fail_ape'  => 'ERROR',
     1004                                                ),
     1005
     1006                                // GZIP  - data        - GZIP compressed data
     1007                                'gz'  => array(
     1008                                                        'pattern'   => '^\x1F\x8B\x08',
     1009                                                        'group'     => 'archive',
     1010                                                        'module'    => 'gzip',
     1011                                                        'mime_type' => 'application/x-gzip',
     1012                                                        'fail_id3'  => 'ERROR',
     1013                                                        'fail_ape'  => 'ERROR',
     1014                                                ),
     1015
     1016                                // ZIP  - data         - ZIP compressed data
     1017                                'zip'  => array(
     1018                                                        'pattern'   => '^PK\x03\x04',
     1019                                                        'group'     => 'archive',
     1020                                                        'module'    => 'zip',
     1021                                                        'mime_type' => 'application/zip',
     1022                                                        'fail_id3'  => 'ERROR',
     1023                                                        'fail_ape'  => 'ERROR',
     1024                                                ),
     1025
     1026
     1027                                // Misc other formats
     1028
     1029                                // PAR2 - data        - Parity Volume Set Specification 2.0
     1030                                'par2' => array (
     1031                                                        'pattern'   => '^PAR2\x00PKT',
     1032                                                        'group'     => 'misc',
     1033                                                        'module'    => 'par2',
     1034                                                        'mime_type' => 'application/octet-stream',
     1035                                                        'fail_id3'  => 'ERROR',
     1036                                                        'fail_ape'  => 'ERROR',
     1037                                                ),
     1038
     1039                                // PDF  - data        - Portable Document Format
     1040                                'pdf'  => array(
     1041                                                        'pattern'   => '^\x25PDF',
     1042                                                        'group'     => 'misc',
     1043                                                        'module'    => 'pdf',
     1044                                                        'mime_type' => 'application/pdf',
     1045                                                        'fail_id3'  => 'ERROR',
     1046                                                        'fail_ape'  => 'ERROR',
     1047                                                ),
     1048
     1049                                // MSOFFICE  - data   - ZIP compressed data
     1050                                'msoffice' => array(
     1051                                                        'pattern'   => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
     1052                                                        'group'     => 'misc',
     1053                                                        'module'    => 'msoffice',
     1054                                                        'mime_type' => 'application/octet-stream',
     1055                                                        'fail_id3'  => 'ERROR',
     1056                                                        'fail_ape'  => 'ERROR',
     1057                                                ),
     1058
     1059                                 // CUE  - data       - CUEsheet (index to single-file disc images)
     1060                                 'cue' => array(
     1061                                                        'pattern'   => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
     1062                                                        'group'     => 'misc',
     1063                                                        'module'    => 'cue',
     1064                                                        'mime_type' => 'application/octet-stream',
     1065                                                   ),
     1066
     1067                        );
     1068                }
     1069
     1070                return $format_info;
     1071        }
     1072
     1073
     1074
     1075        public function GetFileFormat(&$filedata, $filename='') {
     1076                // this function will determine the format of a file based on usually
     1077                // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
     1078                // and in the case of ISO CD image, 6 bytes offset 32kb from the start
     1079                // of the file).
     1080
     1081                // Identify file format - loop through $format_info and detect with reg expr
     1082                foreach ($this->GetFileFormatArray() as $format_name => $info) {
     1083                        // The /s switch on preg_match() forces preg_match() NOT to treat
     1084                        // newline (0x0A) characters as special chars but do a binary match
     1085                        if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
     1086                                $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
     1087                                return $info;
     1088                        }
     1089                }
     1090
     1091
     1092                if (preg_match('#\.mp[123a]$#i', $filename)) {
     1093                        // Too many mp3 encoders on the market put gabage in front of mpeg files
     1094                        // use assume format on these if format detection failed
     1095                        $GetFileFormatArray = $this->GetFileFormatArray();
     1096                        $info = $GetFileFormatArray['mp3'];
     1097                        $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
     1098                        return $info;
     1099                } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
     1100                        // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
     1101                        // so until I think of something better, just go by filename if all other format checks fail
     1102                        // and verify there's at least one instance of "TRACK xx AUDIO" in the file
     1103                        $GetFileFormatArray = $this->GetFileFormatArray();
     1104                        $info = $GetFileFormatArray['cue'];
     1105                        $info['include']   = 'module.'.$info['group'].'.'.$info['module'].'.php';
     1106                        return $info;
     1107                }
     1108
     1109                return false;
     1110        }
     1111
     1112
     1113        // converts array to $encoding charset from $this->encoding
     1114        public function CharConvert(&$array, $encoding) {
     1115
     1116                // identical encoding - end here
     1117                if ($encoding == $this->encoding) {
     1118                        return;
     1119                }
     1120
     1121                // loop thru array
     1122                foreach ($array as $key => $value) {
     1123
     1124                        // go recursive
     1125                        if (is_array($value)) {
     1126                                $this->CharConvert($array[$key], $encoding);
     1127                        }
     1128
     1129                        // convert string
     1130                        elseif (is_string($value)) {
     1131                                $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
     1132                        }
     1133                }
     1134        }
     1135
     1136
     1137        public function HandleAllTags() {
     1138
     1139                // key name => array (tag name, character encoding)
     1140                static $tags;
     1141                if (empty($tags)) {
     1142                        $tags = array(
     1143                                'asf'       => array('asf'           , 'UTF-16LE'),
     1144                                'midi'      => array('midi'          , 'ISO-8859-1'),
     1145                                'nsv'       => array('nsv'           , 'ISO-8859-1'),
     1146                                'ogg'       => array('vorbiscomment' , 'UTF-8'),
     1147                                'png'       => array('png'           , 'UTF-8'),
     1148                                'tiff'      => array('tiff'          , 'ISO-8859-1'),
     1149                                'quicktime' => array('quicktime'     , 'UTF-8'),
     1150                                'real'      => array('real'          , 'ISO-8859-1'),
     1151                                'vqf'       => array('vqf'           , 'ISO-8859-1'),
     1152                                'zip'       => array('zip'           , 'ISO-8859-1'),
     1153                                'riff'      => array('riff'          , 'ISO-8859-1'),
     1154                                'lyrics3'   => array('lyrics3'       , 'ISO-8859-1'),
     1155                                'id3v1'     => array('id3v1'         , $this->encoding_id3v1),
     1156                                'id3v2'     => array('id3v2'         , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
     1157                                'ape'       => array('ape'           , 'UTF-8'),
     1158                                'cue'       => array('cue'           , 'ISO-8859-1'),
     1159                                'matroska'  => array('matroska'      , 'UTF-8'),
     1160                                'flac'      => array('vorbiscomment' , 'UTF-8'),
     1161                                'divxtag'   => array('divx'          , 'ISO-8859-1'),
     1162                        );
     1163                }
     1164
     1165                // loop through comments array
     1166                foreach ($tags as $comment_name => $tagname_encoding_array) {
     1167                        list($tag_name, $encoding) = $tagname_encoding_array;
     1168
     1169                        // fill in default encoding type if not already present
     1170                        if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
     1171                                $this->info[$comment_name]['encoding'] = $encoding;
     1172                        }
     1173
     1174                        // copy comments if key name set
     1175                        if (!empty($this->info[$comment_name]['comments'])) {
     1176                                foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
     1177                                        foreach ($valuearray as $key => $value) {
     1178                                                if (is_string($value)) {
     1179                                                        $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
     1180                                                }
     1181                                                if ($value) {
     1182                                                        $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
     1183                                                }
     1184                                        }
     1185                                        if ($tag_key == 'picture') {
     1186                                                unset($this->info[$comment_name]['comments'][$tag_key]);
     1187                                        }
     1188                                }
     1189
     1190                                if (!isset($this->info['tags'][$tag_name])) {
     1191                                        // comments are set but contain nothing but empty strings, so skip
     1192                                        continue;
     1193                                }
     1194
     1195                                if ($this->option_tags_html) {
     1196                                        foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
     1197                                                foreach ($valuearray as $key => $value) {
     1198                                                        if (is_string($value)) {
     1199                                                                //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
     1200                                                                $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', trim(getid3_lib::MultiByteCharString2HTML($value, $encoding)));
     1201                                                        } else {
     1202                                                                $this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
     1203                                                        }
     1204                                                }
     1205                                        }
     1206                                }
     1207
     1208                                $this->CharConvert($this->info['tags'][$tag_name], $encoding);           // only copy gets converted!
     1209                        }
     1210
     1211                }
     1212
     1213                // pictures can take up a lot of space, and we don't need multiple copies of them
     1214                // let there be a single copy in [comments][picture], and not elsewhere
     1215                if (!empty($this->info['tags'])) {
     1216                        $unset_keys = array('tags', 'tags_html');
     1217                        foreach ($this->info['tags'] as $tagtype => $tagarray) {
     1218                                foreach ($tagarray as $tagname => $tagdata) {
     1219                                        if ($tagname == 'picture') {
     1220                                                foreach ($tagdata as $key => $tagarray) {
     1221                                                        $this->info['comments']['picture'][] = $tagarray;
     1222                                                        if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
     1223                                                                if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
     1224                                                                        unset($this->info['tags'][$tagtype][$tagname][$key]);
     1225                                                                }
     1226                                                                if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
     1227                                                                        unset($this->info['tags_html'][$tagtype][$tagname][$key]);
     1228                                                                }
     1229                                                        }
     1230                                                }
     1231                                        }
     1232                                }
     1233                                foreach ($unset_keys as $unset_key) {
     1234                                        // remove possible empty keys from (e.g. [tags][id3v2][picture])
     1235                                        if (empty($this->info[$unset_key][$tagtype]['picture'])) {
     1236                                                unset($this->info[$unset_key][$tagtype]['picture']);
     1237                                        }
     1238                                        if (empty($this->info[$unset_key][$tagtype])) {
     1239                                                unset($this->info[$unset_key][$tagtype]);
     1240                                        }
     1241                                        if (empty($this->info[$unset_key])) {
     1242                                                unset($this->info[$unset_key]);
     1243                                        }
     1244                                }
     1245                                // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture])
     1246                                if (isset($this->info[$tagtype]['comments']['picture'])) {
     1247                                        unset($this->info[$tagtype]['comments']['picture']);
     1248                                }
     1249                                if (empty($this->info[$tagtype]['comments'])) {
     1250                                        unset($this->info[$tagtype]['comments']);
     1251                                }
     1252                                if (empty($this->info[$tagtype])) {
     1253                                        unset($this->info[$tagtype]);
     1254                                }
     1255                        }
     1256                }
     1257                return true;
     1258        }
     1259
     1260
     1261        public function getHashdata($algorithm) {
     1262                switch ($algorithm) {
     1263                        case 'md5':
     1264                        case 'sha1':
     1265                                break;
     1266
     1267                        default:
     1268                                return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
     1269                                break;
     1270                }
     1271
     1272                if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
     1273
     1274                        // We cannot get an identical md5_data value for Ogg files where the comments
     1275                        // span more than 1 Ogg page (compared to the same audio data with smaller
     1276                        // comments) using the normal getID3() method of MD5'ing the data between the
     1277                        // end of the comments and the end of the file (minus any trailing tags),
     1278                        // because the page sequence numbers of the pages that the audio data is on
     1279                        // do not match. Under normal circumstances, where comments are smaller than
     1280                        // the nominal 4-8kB page size, then this is not a problem, but if there are
     1281                        // very large comments, the only way around it is to strip off the comment
     1282                        // tags with vorbiscomment and MD5 that file.
     1283                        // This procedure must be applied to ALL Ogg files, not just the ones with
     1284                        // comments larger than 1 page, because the below method simply MD5's the
     1285                        // whole file with the comments stripped, not just the portion after the
     1286                        // comments block (which is the standard getID3() method.
     1287
     1288                        // The above-mentioned problem of comments spanning multiple pages and changing
     1289                        // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
     1290                        // currently vorbiscomment only works on OggVorbis files.
     1291
     1292                        if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
     1293
     1294                                $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
     1295                                $this->info[$algorithm.'_data'] = false;
     1296
     1297                        } else {
     1298
     1299                                // Prevent user from aborting script
     1300                                $old_abort = ignore_user_abort(true);
     1301
     1302                                // Create empty file
     1303                                $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
     1304                                touch($empty);
     1305
     1306                                // Use vorbiscomment to make temp file without comments
     1307                                $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
     1308                                $file = $this->info['filenamepath'];
     1309
     1310                                if (GETID3_OS_ISWINDOWS) {
     1311
     1312                                        if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
     1313
     1314                                                $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
     1315                                                $VorbisCommentError = `$commandline`;
     1316
     1317                                        } else {
     1318
     1319                                                $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
     1320
     1321                                        }
     1322
     1323                                } else {
     1324
     1325                                        $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
     1326                                        $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
     1327                                        $VorbisCommentError = `$commandline`;
     1328
     1329                                }
     1330
     1331                                if (!empty($VorbisCommentError)) {
     1332
     1333                                        $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;
     1334                                        $this->info[$algorithm.'_data']  = false;
     1335
     1336                                } else {
     1337
     1338                                        // Get hash of newly created file
     1339                                        switch ($algorithm) {
     1340                                                case 'md5':
     1341                                                        $this->info[$algorithm.'_data'] = md5_file($temp);
     1342                                                        break;
     1343
     1344                                                case 'sha1':
     1345                                                        $this->info[$algorithm.'_data'] = sha1_file($temp);
     1346                                                        break;
     1347                                        }
     1348                                }
     1349
     1350                                // Clean up
     1351                                unlink($empty);
     1352                                unlink($temp);
     1353
     1354                                // Reset abort setting
     1355                                ignore_user_abort($old_abort);
     1356
     1357                        }
     1358
     1359                } else {
     1360
     1361                        if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
     1362
     1363                                // get hash from part of file
     1364                                $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
     1365
     1366                        } else {
     1367
     1368                                // get hash from whole file
     1369                                switch ($algorithm) {
     1370                                        case 'md5':
     1371                                                $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
     1372                                                break;
     1373
     1374                                        case 'sha1':
     1375                                                $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
     1376                                                break;
     1377                                }
     1378                        }
     1379
     1380                }
     1381                return true;
     1382        }
     1383
     1384
     1385        public function ChannelsBitratePlaytimeCalculations() {
     1386
     1387                // set channelmode on audio
     1388                if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
     1389                        // ignore
     1390                } elseif ($this->info['audio']['channels'] == 1) {
     1391                        $this->info['audio']['channelmode'] = 'mono';
     1392                } elseif ($this->info['audio']['channels'] == 2) {
     1393                        $this->info['audio']['channelmode'] = 'stereo';
     1394                }
     1395
     1396                // Calculate combined bitrate - audio + video
     1397                $CombinedBitrate  = 0;
     1398                $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
     1399                $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
     1400                if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
     1401                        $this->info['bitrate'] = $CombinedBitrate;
     1402                }
     1403                //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
     1404                //      // for example, VBR MPEG video files cannot determine video bitrate:
     1405                //      // should not set overall bitrate and playtime from audio bitrate only
     1406                //      unset($this->info['bitrate']);
     1407                //}
     1408
     1409                // video bitrate undetermined, but calculable
     1410                if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
     1411                        // if video bitrate not set
     1412                        if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
     1413                                // AND if audio bitrate is set to same as overall bitrate
     1414                                if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
     1415                                        // AND if playtime is set
     1416                                        if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
     1417                                                // AND if AV data offset start/end is known
     1418                                                // THEN we can calculate the video bitrate
     1419                                                $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
     1420                                                $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
     1421                                        }
     1422                                }
     1423                        }
     1424                }
     1425
     1426                if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
     1427                        $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
     1428                }
     1429
     1430                if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
     1431                        $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
     1432                }
     1433                if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
     1434                        if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
     1435                                // audio only
     1436                                $this->info['audio']['bitrate'] = $this->info['bitrate'];
     1437                        } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
     1438                                // video only
     1439                                $this->info['video']['bitrate'] = $this->info['bitrate'];
     1440                        }
     1441                }
     1442
     1443                // Set playtime string
     1444                if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
     1445                        $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
     1446                }
     1447        }
     1448
     1449
     1450        public function CalculateCompressionRatioVideo() {
     1451                if (empty($this->info['video'])) {
     1452                        return false;
     1453                }
     1454                if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
     1455                        return false;
     1456                }
     1457                if (empty($this->info['video']['bits_per_sample'])) {
     1458                        return false;
     1459                }
     1460
     1461                switch ($this->info['video']['dataformat']) {
     1462                        case 'bmp':
     1463                        case 'gif':
     1464                        case 'jpeg':
     1465                        case 'jpg':
     1466                        case 'png':
     1467                        case 'tiff':
     1468                                $FrameRate = 1;
     1469                                $PlaytimeSeconds = 1;
     1470                                $BitrateCompressed = $this->info['filesize'] * 8;
     1471                                break;
     1472
     1473                        default:
     1474                                if (!empty($this->info['video']['frame_rate'])) {
     1475                                        $FrameRate = $this->info['video']['frame_rate'];
     1476                                } else {
     1477                                        return false;
     1478                                }
     1479                                if (!empty($this->info['playtime_seconds'])) {
     1480                                        $PlaytimeSeconds = $this->info['playtime_seconds'];
     1481                                } else {
     1482                                        return false;
     1483                                }
     1484                                if (!empty($this->info['video']['bitrate'])) {
     1485                                        $BitrateCompressed = $this->info['video']['bitrate'];
     1486                                } else {
     1487                                        return false;
     1488                                }
     1489                                break;
     1490                }
     1491                $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
     1492
     1493                $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
     1494                return true;
     1495        }
     1496
     1497
     1498        public function CalculateCompressionRatioAudio() {
     1499                if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
     1500                        return false;
     1501                }
     1502                $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
     1503
     1504                if (!empty($this->info['audio']['streams'])) {
     1505                        foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
     1506                                if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
     1507                                        $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
     1508                                }
     1509                        }
     1510                }
     1511                return true;
     1512        }
     1513
     1514
     1515        public function CalculateReplayGain() {
     1516                if (isset($this->info['replay_gain'])) {
     1517                        if (!isset($this->info['replay_gain']['reference_volume'])) {
     1518                                $this->info['replay_gain']['reference_volume'] = (double) 89.0;
     1519                        }
     1520                        if (isset($this->info['replay_gain']['track']['adjustment'])) {
     1521                                $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
     1522                        }
     1523                        if (isset($this->info['replay_gain']['album']['adjustment'])) {
     1524                                $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
     1525                        }
     1526
     1527                        if (isset($this->info['replay_gain']['track']['peak'])) {
     1528                                $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
     1529                        }
     1530                        if (isset($this->info['replay_gain']['album']['peak'])) {
     1531                                $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
     1532                        }
     1533                }
     1534                return true;
     1535        }
     1536
     1537        public function ProcessAudioStreams() {
     1538                if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
     1539                        if (!isset($this->info['audio']['streams'])) {
     1540                                foreach ($this->info['audio'] as $key => $value) {
     1541                                        if ($key != 'streams') {
     1542                                                $this->info['audio']['streams'][0][$key] = $value;
     1543                                        }
     1544                                }
     1545                        }
     1546                }
     1547                return true;
     1548        }
     1549
     1550        public function getid3_tempnam() {
     1551                return tempnam($this->tempdir, 'gI3');
     1552        }
     1553
     1554        public function include_module($name) {
     1555                //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
     1556                if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
     1557                        throw new getid3_exception('Required module.'.$name.'.php is missing.');
     1558                }
     1559                include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
     1560                return true;
     1561        }
     1562
     1563}
     1564
     1565
     1566abstract class getid3_handler
     1567{
     1568        protected $getid3;                       // pointer
     1569
     1570        protected $data_string_flag     = false; // analyzing filepointer or string
     1571        protected $data_string          = '';    // string to analyze
     1572        protected $data_string_position = 0;     // seek position in string
     1573        protected $data_string_length   = 0;     // string length
     1574
     1575        private $dependency_to = null;
     1576
     1577
     1578        public function __construct(getID3 $getid3, $call_module=null) {
     1579                $this->getid3 = $getid3;
     1580
     1581                if ($call_module) {
     1582                        $this->dependency_to = str_replace('getid3_', '', $call_module);
     1583                }
     1584        }
     1585
     1586
     1587        // Analyze from file pointer
     1588        abstract public function Analyze();
     1589
     1590
     1591        // Analyze from string instead
     1592        public function AnalyzeString($string) {
     1593                // Enter string mode
     1594            $this->setStringMode($string);
     1595
     1596                // Save info
     1597                $saved_avdataoffset = $this->getid3->info['avdataoffset'];
     1598                $saved_avdataend    = $this->getid3->info['avdataend'];
     1599                $saved_filesize     = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call
     1600
     1601                // Reset some info
     1602                $this->getid3->info['avdataoffset'] = 0;
     1603                $this->getid3->info['avdataend']    = $this->getid3->info['filesize'] = $this->data_string_length;
     1604
     1605                // Analyze
     1606                $this->Analyze();
     1607
     1608                // Restore some info
     1609                $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
     1610                $this->getid3->info['avdataend']    = $saved_avdataend;
     1611                $this->getid3->info['filesize']     = $saved_filesize;
     1612
     1613                // Exit string mode
     1614                $this->data_string_flag = false;
     1615        }
     1616
     1617        public function setStringMode($string) {
     1618                $this->data_string_flag   = true;
     1619                $this->data_string        = $string;
     1620                $this->data_string_length = strlen($string);
     1621        }
     1622
     1623        protected function ftell() {
     1624                if ($this->data_string_flag) {
     1625                        return $this->data_string_position;
     1626                }
     1627                return ftell($this->getid3->fp);
     1628        }
     1629
     1630        protected function fread($bytes) {
     1631                if ($this->data_string_flag) {
     1632                        $this->data_string_position += $bytes;
     1633                        return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
     1634                }
     1635            $pos = $this->ftell() + $bytes;
     1636            if (!getid3_lib::intValueSupported($pos)) {
     1637                        throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
     1638            }
     1639                return fread($this->getid3->fp, $bytes);
     1640        }
     1641
     1642        protected function fseek($bytes, $whence=SEEK_SET) {
     1643                if ($this->data_string_flag) {
     1644                        switch ($whence) {
     1645                                case SEEK_SET:
     1646                                        $this->data_string_position = $bytes;
     1647                                        break;
     1648
     1649                                case SEEK_CUR:
     1650                                        $this->data_string_position += $bytes;
     1651                                        break;
     1652
     1653                                case SEEK_END:
     1654                                        $this->data_string_position = $this->data_string_length + $bytes;
     1655                                        break;
     1656                        }
     1657                        return 0;
     1658            } else {
     1659                $pos = $bytes;
     1660                if ($whence == SEEK_CUR) {
     1661                                $pos = $this->ftell() + $bytes;
     1662                } elseif ($whence == SEEK_END) {
     1663                                $pos = $this->info['filesize'] + $bytes;
     1664                }
     1665                if (!getid3_lib::intValueSupported($pos)) {
     1666                                throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
     1667                        }
     1668                }
     1669                return fseek($this->getid3->fp, $bytes, $whence);
     1670        }
     1671
     1672        protected function feof() {
     1673                if ($this->data_string_flag) {
     1674                        return $this->data_string_position >= $this->data_string_length;
     1675                }
     1676                return feof($this->getid3->fp);
     1677        }
     1678
     1679        final protected function isDependencyFor($module) {
     1680                return $this->dependency_to == $module;
     1681        }
     1682
     1683        protected function error($text)
     1684        {
     1685                $this->getid3->info['error'][] = $text;
     1686
     1687                return false;
     1688        }
     1689
     1690        protected function warning($text)
     1691        {
     1692                return $this->getid3->warning($text);
     1693        }
     1694
     1695        protected function notice($text)
     1696        {
     1697                // does nothing for now
     1698        }
     1699
     1700        public function saveAttachment($name, $offset, $length, $image_mime=null) {
     1701                try {
     1702
     1703                        // do not extract at all
     1704                        if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
     1705
     1706                                $attachment = null; // do not set any
     1707
     1708                        // extract to return array
     1709                        } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
     1710
     1711                                $this->fseek($offset);
     1712                                $attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory
     1713                                if ($attachment === false || strlen($attachment) != $length) {
     1714                                        throw new Exception('failed to read attachment data');
     1715                                }
     1716
     1717                        // assume directory path is given
     1718                        } else {
     1719
     1720                                // set up destination path
     1721                                $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
     1722                                if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory
     1723                                        throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
     1724                                }
     1725                                $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
     1726
     1727                                // create dest file
     1728                                if (($fp_dest = fopen($dest, 'wb')) == false) {
     1729                                        throw new Exception('failed to create file '.$dest);
     1730                                }
     1731
     1732                                // copy data
     1733                                $this->fseek($offset);
     1734                                $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
     1735                                $bytesleft = $length;
     1736                                while ($bytesleft > 0) {
     1737                                        if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
     1738                                                throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
     1739                                        }
     1740                                        $bytesleft -= $byteswritten;
     1741                                }
     1742
     1743                                fclose($fp_dest);
     1744                                $attachment = $dest;
     1745
     1746                        }
     1747
     1748                } catch (Exception $e) {
     1749
     1750                        // close and remove dest file if created
     1751                        if (isset($fp_dest) && is_resource($fp_dest)) {
     1752                                fclose($fp_dest);
     1753                                unlink($dest);
     1754                        }
     1755
     1756                        // do not set any is case of error
     1757                        $attachment = null;
     1758                        $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
     1759
     1760                }
     1761
     1762                // seek to the end of attachment
     1763                $this->fseek($offset + $length);
     1764
     1765                return $attachment;
     1766        }
     1767}
     1768
     1769class getid3_exception extends Exception {
     1770        public $message;
     1771}
     1772 No newline at end of file
  • new file wp-includes/ID3/getid3.lib.php

    diff --git wp-includes/ID3/getid3.lib.php wp-includes/ID3/getid3.lib.php
    new file mode 100644
    index 0000000..f0b9c94
    - +  
     1<?php
     2/////////////////////////////////////////////////////////////////
     3/// getID3() by James Heinrich <info@getid3.org>               //
     4//  available at http://getid3.sourceforge.net                 //
     5//            or http://www.getid3.org                         //
     6/////////////////////////////////////////////////////////////////
     7//                                                             //
     8// getid3.lib.php - part of getID3()                           //
     9// See readme.txt for more details                             //
     10//                                                            ///
     11/////////////////////////////////////////////////////////////////
     12
     13
     14class getid3_lib
     15{
     16
     17        public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
     18                $returnstring = '';
     19                for ($i = 0; $i < strlen($string); $i++) {
     20                        if ($hex) {
     21                                $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
     22                        } else {
     23                                $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
     24                        }
     25                        if ($spaces) {
     26                                $returnstring .= ' ';
     27                        }
     28                }
     29                if (!empty($htmlencoding)) {
     30                        if ($htmlencoding === true) {
     31                                $htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
     32                        }
     33                        $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
     34                }
     35                return $returnstring;
     36        }
     37
     38        public static function trunc($floatnumber) {
     39                // truncates a floating-point number at the decimal point
     40                // returns int (if possible, otherwise float)
     41                if ($floatnumber >= 1) {
     42                        $truncatednumber = floor($floatnumber);
     43                } elseif ($floatnumber <= -1) {
     44                        $truncatednumber = ceil($floatnumber);
     45                } else {
     46                        $truncatednumber = 0;
     47                }
     48                if (self::intValueSupported($truncatednumber)) {
     49                        $truncatednumber = (int) $truncatednumber;
     50                }
     51                return $truncatednumber;
     52        }
     53
     54
     55        public static function safe_inc(&$variable, $increment=1) {
     56                if (isset($variable)) {
     57                        $variable += $increment;
     58                } else {
     59                        $variable = $increment;
     60                }
     61                return true;
     62        }
     63
     64        public static function CastAsInt($floatnum) {
     65                // convert to float if not already
     66                $floatnum = (float) $floatnum;
     67
     68                // convert a float to type int, only if possible
     69                if (self::trunc($floatnum) == $floatnum) {
     70                        // it's not floating point
     71                        if (self::intValueSupported($floatnum)) {
     72                                // it's within int range
     73                                $floatnum = (int) $floatnum;
     74                        }
     75                }
     76                return $floatnum;
     77        }
     78
     79        public static function intValueSupported($num) {
     80                // check if integers are 64-bit
     81                static $hasINT64 = null;
     82                if ($hasINT64 === null) { // 10x faster than is_null()
     83                        $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
     84                        if (!$hasINT64 && !defined('PHP_INT_MIN')) {
     85                                define('PHP_INT_MIN', ~PHP_INT_MAX);
     86                        }
     87                }
     88                // if integers are 64-bit - no other check required
     89                if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
     90                        return true;
     91                }
     92                return false;
     93        }
     94
     95        public static function DecimalizeFraction($fraction) {
     96                list($numerator, $denominator) = explode('/', $fraction);
     97                return $numerator / ($denominator ? $denominator : 1);
     98        }
     99
     100
     101        public static function DecimalBinary2Float($binarynumerator) {
     102                $numerator   = self::Bin2Dec($binarynumerator);
     103                $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
     104                return ($numerator / $denominator);
     105        }
     106
     107
     108        public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
     109                // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
     110                if (strpos($binarypointnumber, '.') === false) {
     111                        $binarypointnumber = '0.'.$binarypointnumber;
     112                } elseif ($binarypointnumber{0} == '.') {
     113                        $binarypointnumber = '0'.$binarypointnumber;
     114                }
     115                $exponent = 0;
     116                while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
     117                        if (substr($binarypointnumber, 1, 1) == '.') {
     118                                $exponent--;
     119                                $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
     120                        } else {
     121                                $pointpos = strpos($binarypointnumber, '.');
     122                                $exponent += ($pointpos - 1);
     123                                $binarypointnumber = str_replace('.', '', $binarypointnumber);
     124                                $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
     125                        }
     126                }
     127                $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
     128                return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
     129        }
     130
     131
     132        public static function Float2BinaryDecimal($floatvalue) {
     133                // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
     134                $maxbits = 128; // to how many bits of precision should the calculations be taken?
     135                $intpart   = self::trunc($floatvalue);
     136                $floatpart = abs($floatvalue - $intpart);
     137                $pointbitstring = '';
     138                while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
     139                        $floatpart *= 2;
     140                        $pointbitstring .= (string) self::trunc($floatpart);
     141                        $floatpart -= self::trunc($floatpart);
     142                }
     143                $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
     144                return $binarypointnumber;
     145        }
     146
     147
     148        public static function Float2String($floatvalue, $bits) {
     149                // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
     150                switch ($bits) {
     151                        case 32:
     152                                $exponentbits = 8;
     153                                $fractionbits = 23;
     154                                break;
     155
     156                        case 64:
     157                                $exponentbits = 11;
     158                                $fractionbits = 52;
     159                                break;
     160
     161                        default:
     162                                return false;
     163                                break;
     164                }
     165                if ($floatvalue >= 0) {
     166                        $signbit = '0';
     167                } else {
     168                        $signbit = '1';
     169                }
     170                $normalizedbinary  = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits);
     171                $biasedexponent    = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
     172                $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
     173                $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
     174
     175                return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
     176        }
     177
     178
     179        public static function LittleEndian2Float($byteword) {
     180                return self::BigEndian2Float(strrev($byteword));
     181        }
     182
     183
     184        public static function BigEndian2Float($byteword) {
     185                // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
     186                // http://www.psc.edu/general/software/packages/ieee/ieee.html
     187                // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
     188
     189                $bitword = self::BigEndian2Bin($byteword);
     190                if (!$bitword) {
     191                        return 0;
     192                }
     193                $signbit = $bitword{0};
     194
     195                switch (strlen($byteword) * 8) {
     196                        case 32:
     197                                $exponentbits = 8;
     198                                $fractionbits = 23;
     199                                break;
     200
     201                        case 64:
     202                                $exponentbits = 11;
     203                                $fractionbits = 52;
     204                                break;
     205
     206                        case 80:
     207                                // 80-bit Apple SANE format
     208                                // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
     209                                $exponentstring = substr($bitword, 1, 15);
     210                                $isnormalized = intval($bitword{16});
     211                                $fractionstring = substr($bitword, 17, 63);
     212                                $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
     213                                $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
     214                                $floatvalue = $exponent * $fraction;
     215                                if ($signbit == '1') {
     216                                        $floatvalue *= -1;
     217                                }
     218                                return $floatvalue;
     219                                break;
     220
     221                        default:
     222                                return false;
     223                                break;
     224                }
     225                $exponentstring = substr($bitword, 1, $exponentbits);
     226                $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
     227                $exponent = self::Bin2Dec($exponentstring);
     228                $fraction = self::Bin2Dec($fractionstring);
     229
     230                if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
     231                        // Not a Number
     232                        $floatvalue = false;
     233                } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
     234                        if ($signbit == '1') {
     235                                $floatvalue = '-infinity';
     236                        } else {
     237                                $floatvalue = '+infinity';
     238                        }
     239                } elseif (($exponent == 0) && ($fraction == 0)) {
     240                        if ($signbit == '1') {
     241                                $floatvalue = -0;
     242                        } else {
     243                                $floatvalue = 0;
     244                        }
     245                        $floatvalue = ($signbit ? 0 : -0);
     246                } elseif (($exponent == 0) && ($fraction != 0)) {
     247                        // These are 'unnormalized' values
     248                        $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
     249                        if ($signbit == '1') {
     250                                $floatvalue *= -1;
     251                        }
     252                } elseif ($exponent != 0) {
     253                        $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
     254                        if ($signbit == '1') {
     255                                $floatvalue *= -1;
     256                        }
     257                }
     258                return (float) $floatvalue;
     259        }
     260
     261
     262        public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
     263                $intvalue = 0;
     264                $bytewordlen = strlen($byteword);
     265                if ($bytewordlen == 0) {
     266                        return false;
     267                }
     268                for ($i = 0; $i < $bytewordlen; $i++) {
     269                        if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
     270                                //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
     271                                $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
     272                        } else {
     273                                $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
     274                        }
     275                }
     276                if ($signed && !$synchsafe) {
     277                        // synchsafe ints are not allowed to be signed
     278                        if ($bytewordlen <= PHP_INT_SIZE) {
     279                                $signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
     280                                if ($intvalue & $signMaskBit) {
     281                                        $intvalue = 0 - ($intvalue & ($signMaskBit - 1));
     282                                }
     283                        } else {
     284                                throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
     285                                break;
     286                        }
     287                }
     288                return self::CastAsInt($intvalue);
     289        }
     290
     291
     292        public static function LittleEndian2Int($byteword, $signed=false) {
     293                return self::BigEndian2Int(strrev($byteword), false, $signed);
     294        }
     295
     296
     297        public static function BigEndian2Bin($byteword) {
     298                $binvalue = '';
     299                $bytewordlen = strlen($byteword);
     300                for ($i = 0; $i < $bytewordlen; $i++) {
     301                        $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
     302                }
     303                return $binvalue;
     304        }
     305
     306
     307        public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
     308                if ($number < 0) {
     309                        throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
     310                }
     311                $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
     312                $intstring = '';
     313                if ($signed) {
     314                        if ($minbytes > PHP_INT_SIZE) {
     315                                throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
     316                        }
     317                        $number = $number & (0x80 << (8 * ($minbytes - 1)));
     318                }
     319                while ($number != 0) {
     320                        $quotient = ($number / ($maskbyte + 1));
     321                        $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
     322                        $number = floor($quotient);
     323                }
     324                return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
     325        }
     326
     327
     328        public static function Dec2Bin($number) {
     329                while ($number >= 256) {
     330                        $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
     331                        $number = floor($number / 256);
     332                }
     333                $bytes[] = $number;
     334                $binstring = '';
     335                for ($i = 0; $i < count($bytes); $i++) {
     336                        $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
     337                }
     338                return $binstring;
     339        }
     340
     341
     342        public static function Bin2Dec($binstring, $signed=false) {
     343                $signmult = 1;
     344                if ($signed) {
     345                        if ($binstring{0} == '1') {
     346                                $signmult = -1;
     347                        }
     348                        $binstring = substr($binstring, 1);
     349                }
     350                $decvalue = 0;
     351                for ($i = 0; $i < strlen($binstring); $i++) {
     352                        $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
     353                }
     354                return self::CastAsInt($decvalue * $signmult);
     355        }
     356
     357
     358        public static function Bin2String($binstring) {
     359                // return 'hi' for input of '0110100001101001'
     360                $string = '';
     361                $binstringreversed = strrev($binstring);
     362                for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
     363                        $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
     364                }
     365                return $string;
     366        }
     367
     368
     369        public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
     370                $intstring = '';
     371                while ($number > 0) {
     372                        if ($synchsafe) {
     373                                $intstring = $intstring.chr($number & 127);
     374                                $number >>= 7;
     375                        } else {
     376                                $intstring = $intstring.chr($number & 255);
     377                                $number >>= 8;
     378                        }
     379                }
     380                return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
     381        }
     382
     383
     384        public static function array_merge_clobber($array1, $array2) {
     385                // written by kcØhireability*com
     386                // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
     387                if (!is_array($array1) || !is_array($array2)) {
     388                        return false;
     389                }
     390                $newarray = $array1;
     391                foreach ($array2 as $key => $val) {
     392                        if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
     393                                $newarray[$key] = self::array_merge_clobber($newarray[$key], $val);
     394                        } else {
     395                                $newarray[$key] = $val;
     396                        }
     397                }
     398                return $newarray;
     399        }
     400
     401
     402        public static function array_merge_noclobber($array1, $array2) {
     403                if (!is_array($array1) || !is_array($array2)) {
     404                        return false;
     405                }
     406                $newarray = $array1;
     407                foreach ($array2 as $key => $val) {
     408                        if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
     409                                $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val);
     410                        } elseif (!isset($newarray[$key])) {
     411                                $newarray[$key] = $val;
     412                        }
     413                }
     414                return $newarray;
     415        }
     416
     417
     418        public static function ksort_recursive(&$theArray) {
     419                ksort($theArray);
     420                foreach ($theArray as $key => $value) {
     421                        if (is_array($value)) {
     422                                self::ksort_recursive($theArray[$key]);
     423                        }
     424                }
     425                return true;
     426        }
     427
     428        public static function fileextension($filename, $numextensions=1) {
     429                if (strstr($filename, '.')) {
     430                        $reversedfilename = strrev($filename);
     431                        $offset = 0;
     432                        for ($i = 0; $i < $numextensions; $i++) {
     433                                $offset = strpos($reversedfilename, '.', $offset + 1);
     434                                if ($offset === false) {
     435                                        return '';
     436                                }
     437                        }
     438                        return strrev(substr($reversedfilename, 0, $offset));
     439                }
     440                return '';
     441        }
     442
     443
     444        public static function PlaytimeString($seconds) {
     445                $sign = (($seconds < 0) ? '-' : '');
     446                $seconds = round(abs($seconds));
     447                $H = (int) floor( $seconds                            / 3600);
     448                $M = (int) floor(($seconds - (3600 * $H)            ) /   60);
     449                $S = (int) round( $seconds - (3600 * $H) - (60 * $M)        );
     450                return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT);
     451        }
     452
     453
     454        public static function DateMac2Unix($macdate) {
     455                // Macintosh timestamp: seconds since 00:00h January 1, 1904
     456                // UNIX timestamp:      seconds since 00:00h January 1, 1970
     457                return self::CastAsInt($macdate - 2082844800);
     458        }
     459
     460
     461        public static function FixedPoint8_8($rawdata) {
     462                return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
     463        }
     464
     465
     466        public static function FixedPoint16_16($rawdata) {
     467                return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
     468        }
     469
     470
     471        public static function FixedPoint2_30($rawdata) {
     472                $binarystring = self::BigEndian2Bin($rawdata);
     473                return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
     474        }
     475
     476
     477        public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
     478                // assigns $Value to a nested array path:
     479                //   $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt')
     480                // is the same as:
     481                //   $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
     482                // or
     483                //   $foo['path']['to']['my'] = 'file.txt';
     484                $ArrayPath = ltrim($ArrayPath, $Separator);
     485                if (($pos = strpos($ArrayPath, $Separator)) !== false) {
     486                        $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
     487                } else {
     488                        $ReturnedArray[$ArrayPath] = $Value;
     489                }
     490                return $ReturnedArray;
     491        }
     492
     493        public static function array_max($arraydata, $returnkey=false) {
     494                $maxvalue = false;
     495                $maxkey = false;
     496                foreach ($arraydata as $key => $value) {
     497                        if (!is_array($value)) {
     498                                if ($value > $maxvalue) {
     499                                        $maxvalue = $value;
     500                                        $maxkey = $key;
     501                                }
     502                        }
     503                }
     504                return ($returnkey ? $maxkey : $maxvalue);
     505        }
     506
     507        public static function array_min($arraydata, $returnkey=false) {
     508                $minvalue = false;
     509                $minkey = false;
     510                foreach ($arraydata as $key => $value) {
     511                        if (!is_array($value)) {
     512                                if ($value > $minvalue) {
     513                                        $minvalue = $value;
     514                                        $minkey = $key;
     515                                }
     516                        }
     517                }
     518                return ($returnkey ? $minkey : $minvalue);
     519        }
     520
     521        public static function XML2array($XMLstring) {
     522                if (function_exists('simplexml_load_string')) {
     523                        if (function_exists('get_object_vars')) {
     524                                $XMLobject = simplexml_load_string($XMLstring);
     525                                return self::SimpleXMLelement2array($XMLobject);
     526                        }
     527                }
     528                return false;
     529        }
     530
     531        public static function SimpleXMLelement2array($XMLobject) {
     532                if (!is_object($XMLobject) && !is_array($XMLobject)) {
     533                        return $XMLobject;
     534                }
     535                $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject);
     536                foreach ($XMLarray as $key => $value) {
     537                        $XMLarray[$key] = self::SimpleXMLelement2array($value);
     538                }
     539                return $XMLarray;
     540        }
     541
     542
     543        // Allan Hansen <ahØartemis*dk>
     544        // self::md5_data() - returns md5sum for a file from startuing position to absolute end position
     545        public static function hash_data($file, $offset, $end, $algorithm) {
     546                static $tempdir = '';
     547                if (!self::intValueSupported($end)) {
     548                        return false;
     549                }
     550                switch ($algorithm) {
     551                        case 'md5':
     552                                $hash_function = 'md5_file';
     553                                $unix_call     = 'md5sum';
     554                                $windows_call  = 'md5sum.exe';
     555                                $hash_length   = 32;
     556                                break;
     557
     558                        case 'sha1':
     559                                $hash_function = 'sha1_file';
     560                                $unix_call     = 'sha1sum';
     561                                $windows_call  = 'sha1sum.exe';
     562                                $hash_length   = 40;
     563                                break;
     564
     565                        default:
     566                                throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
     567                                break;
     568                }
     569                $size = $end - $offset;
     570                while (true) {
     571                        if (GETID3_OS_ISWINDOWS) {
     572
     573                                // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
     574                                // Fall back to create-temp-file method:
     575                                if ($algorithm == 'sha1') {
     576                                        break;
     577                                }
     578
     579                                $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
     580                                foreach ($RequiredFiles as $required_file) {
     581                                        if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
     582                                                // helper apps not available - fall back to old method
     583                                                break 2;
     584                                        }
     585                                }
     586                                $commandline  = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
     587                                $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
     588                                $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
     589
     590                        } else {
     591
     592                                $commandline  = 'head -c'.$end.' '.escapeshellarg($file).' | ';
     593                                $commandline .= 'tail -c'.$size.' | ';
     594                                $commandline .= $unix_call;
     595
     596                        }
     597                        if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
     598                                //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
     599                                break;
     600                        }
     601                        return substr(`$commandline`, 0, $hash_length);
     602                }
     603
     604                if (empty($tempdir)) {
     605                        // yes this is ugly, feel free to suggest a better way
     606                        require_once(dirname(__FILE__).'/getid3.php');
     607                        $getid3_temp = new getID3();
     608                        $tempdir = $getid3_temp->tempdir;
     609                        unset($getid3_temp);
     610                }
     611                // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
     612                if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
     613                        // can't find anywhere to create a temp file, just fail
     614                        return false;
     615                }
     616
     617                // Init
     618                $result = false;
     619
     620                // copy parts of file
     621                try {
     622                        self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
     623                        $result = $hash_function($data_filename);
     624                } catch (Exception $e) {
     625                        throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
     626                }
     627                unlink($data_filename);
     628                return $result;
     629        }
     630
     631        public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
     632                if (!self::intValueSupported($offset + $length)) {
     633                        throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
     634                }
     635                if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
     636                        if (($fp_dest = fopen($filename_dest, 'wb'))) {
     637                                if (fseek($fp_src, $offset, SEEK_SET) == 0) {
     638                                        $byteslefttowrite = $length;
     639                                        while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
     640                                                $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
     641                                                $byteslefttowrite -= $byteswritten;
     642                                        }
     643                                        return true;
     644                                } else {
     645                                        throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
     646                                }
     647                                fclose($fp_dest);
     648                        } else {
     649                                throw new Exception('failed to create file for writing '.$filename_dest);
     650                        }
     651                        fclose($fp_src);
     652                } else {
     653                        throw new Exception('failed to open file for reading '.$filename_source);
     654                }
     655                return false;
     656        }
     657
     658        public static function iconv_fallback_int_utf8($charval) {
     659                if ($charval < 128) {
     660                        // 0bbbbbbb
     661                        $newcharstring = chr($charval);
     662                } elseif ($charval < 2048) {
     663                        // 110bbbbb 10bbbbbb
     664                        $newcharstring  = chr(($charval >>   6) | 0xC0);
     665                        $newcharstring .= chr(($charval & 0x3F) | 0x80);
     666                } elseif ($charval < 65536) {
     667                        // 1110bbbb 10bbbbbb 10bbbbbb
     668                        $newcharstring  = chr(($charval >>  12) | 0xE0);
     669                        $newcharstring .= chr(($charval >>   6) | 0xC0);
     670                        $newcharstring .= chr(($charval & 0x3F) | 0x80);
     671                } else {
     672                        // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
     673                        $newcharstring  = chr(($charval >>  18) | 0xF0);
     674                        $newcharstring .= chr(($charval >>  12) | 0xC0);
     675                        $newcharstring .= chr(($charval >>   6) | 0xC0);
     676                        $newcharstring .= chr(($charval & 0x3F) | 0x80);
     677                }
     678                return $newcharstring;
     679        }
     680
     681        // ISO-8859-1 => UTF-8
     682        public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
     683                if (function_exists('utf8_encode')) {
     684                        return utf8_encode($string);
     685                }
     686                // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
     687                $newcharstring = '';
     688                if ($bom) {
     689                        $newcharstring .= "\xEF\xBB\xBF";
     690                }
     691                for ($i = 0; $i < strlen($string); $i++) {
     692                        $charval = ord($string{$i});
     693                        $newcharstring .= self::iconv_fallback_int_utf8($charval);
     694                }
     695                return $newcharstring;
     696        }
     697
     698        // ISO-8859-1 => UTF-16BE
     699        public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
     700                $newcharstring = '';
     701                if ($bom) {
     702                        $newcharstring .= "\xFE\xFF";
     703                }
     704                for ($i = 0; $i < strlen($string); $i++) {
     705                        $newcharstring .= "\x00".$string{$i};
     706                }
     707                return $newcharstring;
     708        }
     709
     710        // ISO-8859-1 => UTF-16LE
     711        public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
     712                $newcharstring = '';
     713                if ($bom) {
     714                        $newcharstring .= "\xFF\xFE";
     715                }
     716                for ($i = 0; $i < strlen($string); $i++) {
     717                        $newcharstring .= $string{$i}."\x00";
     718                }
     719                return $newcharstring;
     720        }
     721
     722        // ISO-8859-1 => UTF-16LE (BOM)
     723        public static function iconv_fallback_iso88591_utf16($string) {
     724                return self::iconv_fallback_iso88591_utf16le($string, true);
     725        }
     726
     727        // UTF-8 => ISO-8859-1
     728        public static function iconv_fallback_utf8_iso88591($string) {
     729                if (function_exists('utf8_decode')) {
     730                        return utf8_decode($string);
     731                }
     732                // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
     733                $newcharstring = '';
     734                $offset = 0;
     735                $stringlength = strlen($string);
     736                while ($offset < $stringlength) {
     737                        if ((ord($string{$offset}) | 0x07) == 0xF7) {
     738                                // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
     739                                $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
     740                                                   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
     741                                                   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
     742                                                        (ord($string{($offset + 3)}) & 0x3F);
     743                                $offset += 4;
     744                        } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
     745                                // 1110bbbb 10bbbbbb 10bbbbbb
     746                                $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
     747                                                   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
     748                                                        (ord($string{($offset + 2)}) & 0x3F);
     749                                $offset += 3;
     750                        } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
     751                                // 110bbbbb 10bbbbbb
     752                                $charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
     753                                                        (ord($string{($offset + 1)}) & 0x3F);
     754                                $offset += 2;
     755                        } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
     756                                // 0bbbbbbb
     757                                $charval = ord($string{$offset});
     758                                $offset += 1;
     759                        } else {
     760                                // error? throw some kind of warning here?
     761                                $charval = false;
     762                                $offset += 1;
     763                        }
     764                        if ($charval !== false) {
     765                                $newcharstring .= (($charval < 256) ? chr($charval) : '?');
     766                        }
     767                }
     768                return $newcharstring;
     769        }
     770
     771        // UTF-8 => UTF-16BE
     772        public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
     773                $newcharstring = '';
     774                if ($bom) {
     775                        $newcharstring .= "\xFE\xFF";
     776                }
     777                $offset = 0;
     778                $stringlength = strlen($string);
     779                while ($offset < $stringlength) {
     780                        if ((ord($string{$offset}) | 0x07) == 0xF7) {
     781                                // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
     782                                $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
     783                                                   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
     784                                                   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
     785                                                        (ord($string{($offset + 3)}) & 0x3F);
     786                                $offset += 4;
     787                        } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
     788                                // 1110bbbb 10bbbbbb 10bbbbbb
     789                                $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
     790                                                   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
     791                                                        (ord($string{($offset + 2)}) & 0x3F);
     792                                $offset += 3;
     793                        } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
     794                                // 110bbbbb 10bbbbbb
     795                                $charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
     796                                                        (ord($string{($offset + 1)}) & 0x3F);
     797                                $offset += 2;
     798                        } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
     799                                // 0bbbbbbb
     800                                $charval = ord($string{$offset});
     801                                $offset += 1;
     802                        } else {
     803                                // error? throw some kind of warning here?
     804                                $charval = false;
     805                                $offset += 1;
     806                        }
     807                        if ($charval !== false) {
     808                                $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?');
     809                        }
     810                }
     811                return $newcharstring;
     812        }
     813
     814        // UTF-8 => UTF-16LE
     815        public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
     816                $newcharstring = '';
     817                if ($bom) {
     818                        $newcharstring .= "\xFF\xFE";
     819                }
     820                $offset = 0;
     821                $stringlength = strlen($string);
     822                while ($offset < $stringlength) {
     823                        if ((ord($string{$offset}) | 0x07) == 0xF7) {
     824                                // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
     825                                $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
     826                                                   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
     827                                                   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
     828                                                        (ord($string{($offset + 3)}) & 0x3F);
     829                                $offset += 4;
     830                        } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
     831                                // 1110bbbb 10bbbbbb 10bbbbbb
     832                                $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
     833                                                   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
     834                                                        (ord($string{($offset + 2)}) & 0x3F);
     835                                $offset += 3;
     836                        } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
     837                                // 110bbbbb 10bbbbbb
     838                                $charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
     839                                                        (ord($string{($offset + 1)}) & 0x3F);
     840                                $offset += 2;
     841                        } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
     842                                // 0bbbbbbb
     843                                $charval = ord($string{$offset});
     844                                $offset += 1;
     845                        } else {
     846                                // error? maybe throw some warning here?
     847                                $charval = false;
     848                                $offset += 1;
     849                        }
     850                        if ($charval !== false) {
     851                                $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00");
     852                        }
     853                }
     854                return $newcharstring;
     855        }
     856
     857        // UTF-8 => UTF-16LE (BOM)
     858        public static function iconv_fallback_utf8_utf16($string) {
     859                return self::iconv_fallback_utf8_utf16le($string, true);
     860        }
     861
     862        // UTF-16BE => UTF-8
     863        public static function iconv_fallback_utf16be_utf8($string) {
     864                if (substr($string, 0, 2) == "\xFE\xFF") {
     865                        // strip BOM
     866                        $string = substr($string, 2);
     867                }
     868                $newcharstring = '';
     869                for ($i = 0; $i < strlen($string); $i += 2) {
     870                        $charval = self::BigEndian2Int(substr($string, $i, 2));
     871                        $newcharstring .= self::iconv_fallback_int_utf8($charval);
     872                }
     873                return $newcharstring;
     874        }
     875
     876        // UTF-16LE => UTF-8
     877        public static function iconv_fallback_utf16le_utf8($string) {
     878                if (substr($string, 0, 2) == "\xFF\xFE") {
     879                        // strip BOM
     880                        $string = substr($string, 2);
     881                }
     882                $newcharstring = '';
     883                for ($i = 0; $i < strlen($string); $i += 2) {
     884                        $charval = self::LittleEndian2Int(substr($string, $i, 2));
     885                        $newcharstring .= self::iconv_fallback_int_utf8($charval);
     886                }
     887                return $newcharstring;
     888        }
     889
     890        // UTF-16BE => ISO-8859-1
     891        public static function iconv_fallback_utf16be_iso88591($string) {
     892                if (substr($string, 0, 2) == "\xFE\xFF") {
     893                        // strip BOM
     894                        $string = substr($string, 2);
     895                }
     896                $newcharstring = '';
     897                for ($i = 0; $i < strlen($string); $i += 2) {
     898                        $charval = self::BigEndian2Int(substr($string, $i, 2));
     899                        $newcharstring .= (($charval < 256) ? chr($charval) : '?');
     900                }
     901                return $newcharstring;
     902        }
     903
     904        // UTF-16LE => ISO-8859-1
     905        public static function iconv_fallback_utf16le_iso88591($string) {
     906                if (substr($string, 0, 2) == "\xFF\xFE") {
     907                        // strip BOM
     908                        $string = substr($string, 2);
     909                }
     910                $newcharstring = '';
     911                for ($i = 0; $i < strlen($string); $i += 2) {
     912                        $charval = self::LittleEndian2Int(substr($string, $i, 2));
     913                        $newcharstring .= (($charval < 256) ? chr($charval) : '?');
     914                }
     915                return $newcharstring;
     916        }
     917
     918        // UTF-16 (BOM) => ISO-8859-1
     919        public static function iconv_fallback_utf16_iso88591($string) {
     920                $bom = substr($string, 0, 2);
     921                if ($bom == "\xFE\xFF") {
     922                        return self::iconv_fallback_utf16be_iso88591(substr($string, 2));
     923                } elseif ($bom == "\xFF\xFE") {
     924                        return self::iconv_fallback_utf16le_iso88591(substr($string, 2));
     925                }
     926                return $string;
     927        }
     928
     929        // UTF-16 (BOM) => UTF-8
     930        public static function iconv_fallback_utf16_utf8($string) {
     931                $bom = substr($string, 0, 2);
     932                if ($bom == "\xFE\xFF") {
     933                        return self::iconv_fallback_utf16be_utf8(substr($string, 2));
     934                } elseif ($bom == "\xFF\xFE") {
     935                        return self::iconv_fallback_utf16le_utf8(substr($string, 2));
     936                }
     937                return $string;
     938        }
     939
     940        public static function iconv_fallback($in_charset, $out_charset, $string) {
     941
     942                if ($in_charset == $out_charset) {
     943                        return $string;
     944                }
     945
     946                // iconv() availble
     947                if (function_exists('iconv')) {
     948                        if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
     949                                switch ($out_charset) {
     950                                        case 'ISO-8859-1':
     951                                                $converted_string = rtrim($converted_string, "\x00");
     952                                                break;
     953                                }
     954                                return $converted_string;
     955                        }
     956
     957                        // iconv() may sometimes fail with "illegal character in input string" error message
     958                        // and return an empty string, but returning the unconverted string is more useful
     959                        return $string;
     960                }
     961
     962
     963                // iconv() not available
     964                static $ConversionFunctionList = array();
     965                if (empty($ConversionFunctionList)) {
     966                        $ConversionFunctionList['ISO-8859-1']['UTF-8']    = 'iconv_fallback_iso88591_utf8';
     967                        $ConversionFunctionList['ISO-8859-1']['UTF-16']   = 'iconv_fallback_iso88591_utf16';
     968                        $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
     969                        $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
     970                        $ConversionFunctionList['UTF-8']['ISO-8859-1']    = 'iconv_fallback_utf8_iso88591';
     971                        $ConversionFunctionList['UTF-8']['UTF-16']        = 'iconv_fallback_utf8_utf16';
     972                        $ConversionFunctionList['UTF-8']['UTF-16BE']      = 'iconv_fallback_utf8_utf16be';
     973                        $ConversionFunctionList['UTF-8']['UTF-16LE']      = 'iconv_fallback_utf8_utf16le';
     974                        $ConversionFunctionList['UTF-16']['ISO-8859-1']   = 'iconv_fallback_utf16_iso88591';
     975                        $ConversionFunctionList['UTF-16']['UTF-8']        = 'iconv_fallback_utf16_utf8';
     976                        $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
     977                        $ConversionFunctionList['UTF-16LE']['UTF-8']      = 'iconv_fallback_utf16le_utf8';
     978                        $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
     979                        $ConversionFunctionList['UTF-16BE']['UTF-8']      = 'iconv_fallback_utf16be_utf8';
     980                }
     981                if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
     982                        $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
     983                        return self::$ConversionFunction($string);
     984                }
     985                throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
     986        }
     987
     988
     989        public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
     990                $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
     991                $HTMLstring = '';
     992
     993                switch ($charset) {
     994                        case '1251':
     995                        case '1252':
     996                        case '866':
     997                        case '932':
     998                        case '936':
     999                        case '950':
     1000                        case 'BIG5':
     1001                        case 'BIG5-HKSCS':
     1002                        case 'cp1251':
     1003                        case 'cp1252':
     1004                        case 'cp866':
     1005                        case 'EUC-JP':
     1006                        case 'EUCJP':
     1007                        case 'GB2312':
     1008                        case 'ibm866':
     1009                        case 'ISO-8859-1':
     1010                        case 'ISO-8859-15':
     1011                        case 'ISO8859-1':
     1012                        case 'ISO8859-15':
     1013                        case 'KOI8-R':
     1014                        case 'koi8-ru':
     1015                        case 'koi8r':
     1016                        case 'Shift_JIS':
     1017                        case 'SJIS':
     1018                        case 'win-1251':
     1019                        case 'Windows-1251':
     1020                        case 'Windows-1252':
     1021                                $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
     1022                                break;
     1023
     1024                        case 'UTF-8':
     1025                                $strlen = strlen($string);
     1026                                for ($i = 0; $i < $strlen; $i++) {
     1027                                        $char_ord_val = ord($string{$i});
     1028                                        $charval = 0;
     1029                                        if ($char_ord_val < 0x80) {
     1030                                                $charval = $char_ord_val;
     1031                                        } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F  &&  $i+3 < $strlen) {
     1032                                                $charval  = (($char_ord_val & 0x07) << 18);
     1033                                                $charval += ((ord($string{++$i}) & 0x3F) << 12);
     1034                                                $charval += ((ord($string{++$i}) & 0x3F) << 6);
     1035                                                $charval +=  (ord($string{++$i}) & 0x3F);
     1036                                        } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07  &&  $i+2 < $strlen) {
     1037                                                $charval  = (($char_ord_val & 0x0F) << 12);
     1038                                                $charval += ((ord($string{++$i}) & 0x3F) << 6);
     1039                                                $charval +=  (ord($string{++$i}) & 0x3F);
     1040                                        } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03  &&  $i+1 < $strlen) {
     1041                                                $charval  = (($char_ord_val & 0x1F) << 6);
     1042                                                $charval += (ord($string{++$i}) & 0x3F);
     1043                                        }
     1044                                        if (($charval >= 32) && ($charval <= 127)) {
     1045                                                $HTMLstring .= htmlentities(chr($charval));
     1046                                        } else {
     1047                                                $HTMLstring .= '&#'.$charval.';';
     1048                                        }
     1049                                }
     1050                                break;
     1051
     1052                        case 'UTF-16LE':
     1053                                for ($i = 0; $i < strlen($string); $i += 2) {
     1054                                        $charval = self::LittleEndian2Int(substr($string, $i, 2));
     1055                                        if (($charval >= 32) && ($charval <= 127)) {
     1056                                                $HTMLstring .= chr($charval);
     1057                                        } else {
     1058                                                $HTMLstring .= '&#'.$charval.';';
     1059                                        }
     1060                                }
     1061                                break;
     1062
     1063                        case 'UTF-16BE':
     1064                                for ($i = 0; $i < strlen($string); $i += 2) {
     1065                                        $charval = self::BigEndian2Int(substr($string, $i, 2));
     1066                                        if (($charval >= 32) && ($charval <= 127)) {
     1067                                                $HTMLstring .= chr($charval);
     1068                                        } else {
     1069                                                $HTMLstring .= '&#'.$charval.';';
     1070                                        }
     1071                                }
     1072                                break;
     1073
     1074                        default:
     1075                                $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
     1076                                break;
     1077                }
     1078                return $HTMLstring;
     1079        }
     1080
     1081
     1082
     1083        public static function RGADnameLookup($namecode) {
     1084                static $RGADname = array();
     1085                if (empty($RGADname)) {
     1086                        $RGADname[0] = 'not set';
     1087                        $RGADname[1] = 'Track Gain Adjustment';
     1088                        $RGADname[2] = 'Album Gain Adjustment';
     1089                }
     1090
     1091                return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
     1092        }
     1093
     1094
     1095        public static function RGADoriginatorLookup($originatorcode) {
     1096                static $RGADoriginator = array();
     1097                if (empty($RGADoriginator)) {
     1098                        $RGADoriginator[0] = 'unspecified';
     1099                        $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
     1100                        $RGADoriginator[2] = 'set by user';
     1101                        $RGADoriginator[3] = 'determined automatically';
     1102                }
     1103
     1104                return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
     1105        }
     1106
     1107
     1108        public static function RGADadjustmentLookup($rawadjustment, $signbit) {
     1109                $adjustment = $rawadjustment / 10;
     1110                if ($signbit == 1) {
     1111                        $adjustment *= -1;
     1112                }
     1113                return (float) $adjustment;
     1114        }
     1115
     1116
     1117        public static function RGADgainString($namecode, $originatorcode, $replaygain) {
     1118                if ($replaygain < 0) {
     1119                        $signbit = '1';
     1120                } else {
     1121                        $signbit = '0';
     1122                }
     1123                $storedreplaygain = intval(round($replaygain * 10));
     1124                $gainstring  = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
     1125                $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
     1126                $gainstring .= $signbit;
     1127                $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
     1128
     1129                return $gainstring;
     1130        }
     1131
     1132        public static function RGADamplitude2dB($amplitude) {
     1133                return 20 * log10($amplitude);
     1134        }
     1135
     1136
     1137        public static function GetDataImageSize($imgData, &$imageinfo=array()) {
     1138                static $tempdir = '';
     1139                if (empty($tempdir)) {
     1140                        // yes this is ugly, feel free to suggest a better way
     1141                        require_once(dirname(__FILE__).'/class-getid3.php');
     1142                        $getid3_temp = new getID3();
     1143                        $tempdir = $getid3_temp->tempdir;
     1144                        unset($getid3_temp);
     1145                }
     1146                $GetDataImageSize = false;
     1147                if ($tempfilename = tempnam($tempdir, 'gI3')) {
     1148                        if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
     1149                                fwrite($tmp, $imgData);
     1150                                fclose($tmp);
     1151                                $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
     1152                        }
     1153                        unlink($tempfilename);
     1154                }
     1155                return $GetDataImageSize;
     1156        }
     1157
     1158        public static function ImageExtFromMime($mime_type) {
     1159                // temporary way, works OK for now, but should be reworked in the future
     1160                return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type);
     1161        }
     1162
     1163        public static function ImageTypesLookup($imagetypeid) {
     1164                static $ImageTypesLookup = array();
     1165                if (empty($ImageTypesLookup)) {
     1166                        $ImageTypesLookup[1]  = 'gif';
     1167                        $ImageTypesLookup[2]  = 'jpeg';
     1168                        $ImageTypesLookup[3]  = 'png';
     1169                        $ImageTypesLookup[4]  = 'swf';
     1170                        $ImageTypesLookup[5]  = 'psd';
     1171                        $ImageTypesLookup[6]  = 'bmp';
     1172                        $ImageTypesLookup[7]  = 'tiff (little-endian)';
     1173                        $ImageTypesLookup[8]  = 'tiff (big-endian)';
     1174                        $ImageTypesLookup[9]  = 'jpc';
     1175                        $ImageTypesLookup[10] = 'jp2';
     1176                        $ImageTypesLookup[11] = 'jpx';
     1177                        $ImageTypesLookup[12] = 'jb2';
     1178                        $ImageTypesLookup[13] = 'swc';
     1179                        $ImageTypesLookup[14] = 'iff';
     1180                }
     1181                return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
     1182        }
     1183
     1184        public static function CopyTagsToComments(&$ThisFileInfo) {
     1185
     1186                // Copy all entries from ['tags'] into common ['comments']
     1187                if (!empty($ThisFileInfo['tags'])) {
     1188                        foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
     1189                                foreach ($tagarray as $tagname => $tagdata) {
     1190                                        foreach ($tagdata as $key => $value) {
     1191                                                if (!empty($value)) {
     1192                                                        if (empty($ThisFileInfo['comments'][$tagname])) {
     1193
     1194                                                                // fall through and append value
     1195
     1196                                                        } elseif ($tagtype == 'id3v1') {
     1197
     1198                                                                $newvaluelength = strlen(trim($value));
     1199                                                                foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
     1200                                                                        $oldvaluelength = strlen(trim($existingvalue));
     1201                                                                        if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
     1202                                                                                // new value is identical but shorter-than (or equal-length to) one already in comments - skip
     1203                                                                                break 2;
     1204                                                                        }
     1205                                                                }
     1206
     1207                                                        } elseif (!is_array($value)) {
     1208
     1209                                                                $newvaluelength = strlen(trim($value));
     1210                                                                foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
     1211                                                                        $oldvaluelength = strlen(trim($existingvalue));
     1212                                                                        if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
     1213                                                                                $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
     1214                                                                                break 2;
     1215                                                                        }
     1216                                                                }
     1217
     1218                                                        }
     1219                                                        if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
     1220                                                                $value = (is_string($value) ? trim($value) : $value);
     1221                                                                $ThisFileInfo['comments'][$tagname][] = $value;
     1222                                                        }
     1223                                                }
     1224                                        }
     1225                                }
     1226                        }
     1227
     1228                        // Copy to ['comments_html']
     1229                        foreach ($ThisFileInfo['comments'] as $field => $values) {
     1230                                if ($field == 'picture') {
     1231                                        // pictures can take up a lot of space, and we don't need multiple copies of them
     1232                                        // let there be a single copy in [comments][picture], and not elsewhere
     1233                                        continue;
     1234                                }
     1235                                foreach ($values as $index => $value) {
     1236                                        if (is_array($value)) {
     1237                                                $ThisFileInfo['comments_html'][$field][$index] = $value;
     1238                                        } else {
     1239                                                $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
     1240                                        }
     1241                                }
     1242                        }
     1243                }
     1244                return true;
     1245        }
     1246
     1247
     1248        public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
     1249
     1250                // Cached
     1251                static $cache;
     1252                if (isset($cache[$file][$name])) {
     1253                        return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
     1254                }
     1255
     1256                // Init
     1257                $keylength  = strlen($key);
     1258                $line_count = $end - $begin - 7;
     1259
     1260                // Open php file
     1261                $fp = fopen($file, 'r');
     1262
     1263                // Discard $begin lines
     1264                for ($i = 0; $i < ($begin + 3); $i++) {
     1265                        fgets($fp, 1024);
     1266                }
     1267
     1268                // Loop thru line
     1269                while (0 < $line_count--) {
     1270
     1271                        // Read line
     1272                        $line = ltrim(fgets($fp, 1024), "\t ");
     1273
     1274                        // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
     1275                        //$keycheck = substr($line, 0, $keylength);
     1276                        //if ($key == $keycheck)  {
     1277                        //      $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
     1278                        //      break;
     1279                        //}
     1280
     1281                        // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
     1282                        //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
     1283                        $explodedLine = explode("\t", $line, 2);
     1284                        $ThisKey   = (isset($explodedLine[0]) ? $explodedLine[0] : '');
     1285                        $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
     1286                        $cache[$file][$name][$ThisKey] = trim($ThisValue);
     1287                }
     1288
     1289                // Close and return
     1290                fclose($fp);
     1291                return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
     1292        }
     1293
     1294        public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
     1295                global $GETID3_ERRORARRAY;
     1296
     1297                if (file_exists($filename)) {
     1298                        if (include_once($filename)) {
     1299                                return true;
     1300                        } else {
     1301                                $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
     1302                        }
     1303                } else {
     1304                        $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
     1305                }
     1306                if ($DieOnFailure) {
     1307                        throw new Exception($diemessage);
     1308                } else {
     1309                        $GETID3_ERRORARRAY[] = $diemessage;
     1310                }
     1311                return false;
     1312        }
     1313
     1314        public static function trimNullByte($string) {
     1315                return trim($string, "\x00");
     1316        }
     1317
     1318        public static function getFileSizeSyscall($path) {
     1319                $filesize = false;
     1320
     1321                if (GETID3_OS_ISWINDOWS) {
     1322                        if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
     1323                                $filesystem = new COM('Scripting.FileSystemObject');
     1324                                $file = $filesystem->GetFile($path);
     1325                                $filesize = $file->Size();
     1326                                unset($filesystem, $file);
     1327                        } else {
     1328                                $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
     1329                        }
     1330                } else {
     1331                        $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
     1332                }
     1333                if (isset($commandline)) {
     1334                        $output = trim(`$commandline`);
     1335                        if (ctype_digit($output)) {
     1336                                $filesize = (float) $output;
     1337                        }
     1338                }
     1339                return $filesize;
     1340        }
     1341}
     1342 No newline at end of file
  • new file wp-includes/ID3/module.audio-video.asf.php

    diff --git wp-includes/ID3/module.audio-video.asf.php wp-includes/ID3/module.audio-video.asf.php
    new file mode 100644
    index 0000000..cfc60a7
    - +  
     1<?php
     2/////////////////////////////////////////////////////////////////
     3/// getID3() by James Heinrich <info@getid3.org>               //
     4//  available at http://getid3.sourceforge.net                 //
     5//            or http://www.getid3.org                         //
     6/////////////////////////////////////////////////////////////////
     7// See readme.txt for more details                             //
     8/////////////////////////////////////////////////////////////////
     9//                                                             //
     10// module.audio-video.asf.php                                  //
     11// module for analyzing ASF, WMA and WMV files                 //
     12// dependencies: module.audio-video.riff.php                   //
     13//                                                            ///
     14/////////////////////////////////////////////////////////////////
     15
     16getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
     17
     18class getid3_asf extends getid3_handler
     19{
     20
     21        public function __construct(getID3 $getid3) {
     22                parent::__construct($getid3);  // extends getid3_handler::__construct()
     23
     24                // initialize all GUID constants
     25                $GUIDarray = $this->KnownGUIDs();
     26                foreach ($GUIDarray as $GUIDname => $hexstringvalue) {
     27                        if (!defined($GUIDname)) {
     28                                define($GUIDname, $this->GUIDtoBytestring($hexstringvalue));
     29                        }
     30                }
     31        }
     32
     33        public function Analyze() {
     34                $info = &$this->getid3->info;
     35
     36                // Shortcuts
     37                $thisfile_audio = &$info['audio'];
     38                $thisfile_video = &$info['video'];
     39                $info['asf']  = array();
     40                $thisfile_asf = &$info['asf'];
     41                $thisfile_asf['comments'] = array();
     42                $thisfile_asf_comments    = &$thisfile_asf['comments'];
     43                $thisfile_asf['header_object'] = array();
     44                $thisfile_asf_headerobject     = &$thisfile_asf['header_object'];
     45
     46
     47                // ASF structure:
     48                // * Header Object [required]
     49                //   * File Properties Object [required]   (global file attributes)
     50                //   * Stream Properties Object [required] (defines media stream & characteristics)
     51                //   * Header Extension Object [required]  (additional functionality)
     52                //   * Content Description Object          (bibliographic information)
     53                //   * Script Command Object               (commands for during playback)
     54                //   * Marker Object                       (named jumped points within the file)
     55                // * Data Object [required]
     56                //   * Data Packets
     57                // * Index Object
     58
     59                // Header Object: (mandatory, one only)
     60                // Field Name                   Field Type   Size (bits)
     61                // Object ID                    GUID         128             // GUID for header object - GETID3_ASF_Header_Object
     62                // Object Size                  QWORD        64              // size of header object, including 30 bytes of Header Object header
     63                // Number of Header Objects     DWORD        32              // number of objects in header object
     64                // Reserved1                    BYTE         8               // hardcoded: 0x01
     65                // Reserved2                    BYTE         8               // hardcoded: 0x02
     66
     67                $info['fileformat'] = 'asf';
     68
     69                fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     70                $HeaderObjectData = fread($this->getid3->fp, 30);
     71
     72                $thisfile_asf_headerobject['objectid']      = substr($HeaderObjectData, 0, 16);
     73                $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']);
     74                if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) {
     75                        $info['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}';
     76                        unset($info['fileformat']);
     77                        unset($info['asf']);
     78                        return false;
     79                        break;
     80                }
     81                $thisfile_asf_headerobject['objectsize']    = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8));
     82                $thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4));
     83                $thisfile_asf_headerobject['reserved1']     = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1));
     84                $thisfile_asf_headerobject['reserved2']     = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1));
     85
     86                $NextObjectOffset = ftell($this->getid3->fp);
     87                $ASFHeaderData = fread($this->getid3->fp, $thisfile_asf_headerobject['objectsize'] - 30);
     88                $offset = 0;
     89
     90                for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) {
     91                        $NextObjectGUID = substr($ASFHeaderData, $offset, 16);
     92                        $offset += 16;
     93                        $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID);
     94                        $NextObjectSize = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     95                        $offset += 8;
     96                        switch ($NextObjectGUID) {
     97
     98                                case GETID3_ASF_File_Properties_Object:
     99                                        // File Properties Object: (mandatory, one only)
     100                                        // Field Name                   Field Type   Size (bits)
     101                                        // Object ID                    GUID         128             // GUID for file properties object - GETID3_ASF_File_Properties_Object
     102                                        // Object Size                  QWORD        64              // size of file properties object, including 104 bytes of File Properties Object header
     103                                        // File ID                      GUID         128             // unique ID - identical to File ID in Data Object
     104                                        // File Size                    QWORD        64              // entire file in bytes. Invalid if Broadcast Flag == 1
     105                                        // Creation Date                QWORD        64              // date & time of file creation. Maybe invalid if Broadcast Flag == 1
     106                                        // Data Packets Count           QWORD        64              // number of data packets in Data Object. Invalid if Broadcast Flag == 1
     107                                        // Play Duration                QWORD        64              // playtime, in 100-nanosecond units. Invalid if Broadcast Flag == 1
     108                                        // Send Duration                QWORD        64              // time needed to send file, in 100-nanosecond units. Players can ignore this value. Invalid if Broadcast Flag == 1
     109                                        // Preroll                      QWORD        64              // time to buffer data before starting to play file, in 1-millisecond units. If <> 0, PlayDuration and PresentationTime have been offset by this amount
     110                                        // Flags                        DWORD        32              //
     111                                        // * Broadcast Flag             bits         1  (0x01)       // file is currently being written, some header values are invalid
     112                                        // * Seekable Flag              bits         1  (0x02)       // is file seekable
     113                                        // * Reserved                   bits         30 (0xFFFFFFFC) // reserved - set to zero
     114                                        // Minimum Data Packet Size     DWORD        32              // in bytes. should be same as Maximum Data Packet Size. Invalid if Broadcast Flag == 1
     115                                        // Maximum Data Packet Size     DWORD        32              // in bytes. should be same as Minimum Data Packet Size. Invalid if Broadcast Flag == 1
     116                                        // Maximum Bitrate              DWORD        32              // maximum instantaneous bitrate in bits per second for entire file, including all data streams and ASF overhead
     117
     118                                        // shortcut
     119                                        $thisfile_asf['file_properties_object'] = array();
     120                                        $thisfile_asf_filepropertiesobject      = &$thisfile_asf['file_properties_object'];
     121
     122                                        $thisfile_asf_filepropertiesobject['offset']             = $NextObjectOffset + $offset;
     123                                        $thisfile_asf_filepropertiesobject['objectid']           = $NextObjectGUID;
     124                                        $thisfile_asf_filepropertiesobject['objectid_guid']      = $NextObjectGUIDtext;
     125                                        $thisfile_asf_filepropertiesobject['objectsize']         = $NextObjectSize;
     126                                        $thisfile_asf_filepropertiesobject['fileid']             = substr($ASFHeaderData, $offset, 16);
     127                                        $offset += 16;
     128                                        $thisfile_asf_filepropertiesobject['fileid_guid']        = $this->BytestringToGUID($thisfile_asf_filepropertiesobject['fileid']);
     129                                        $thisfile_asf_filepropertiesobject['filesize']           = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     130                                        $offset += 8;
     131                                        $thisfile_asf_filepropertiesobject['creation_date']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     132                                        $thisfile_asf_filepropertiesobject['creation_date_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_filepropertiesobject['creation_date']);
     133                                        $offset += 8;
     134                                        $thisfile_asf_filepropertiesobject['data_packets']       = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     135                                        $offset += 8;
     136                                        $thisfile_asf_filepropertiesobject['play_duration']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     137                                        $offset += 8;
     138                                        $thisfile_asf_filepropertiesobject['send_duration']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     139                                        $offset += 8;
     140                                        $thisfile_asf_filepropertiesobject['preroll']            = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     141                                        $offset += 8;
     142                                        $thisfile_asf_filepropertiesobject['flags_raw']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     143                                        $offset += 4;
     144                                        $thisfile_asf_filepropertiesobject['flags']['broadcast'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0001);
     145                                        $thisfile_asf_filepropertiesobject['flags']['seekable']  = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0002);
     146
     147                                        $thisfile_asf_filepropertiesobject['min_packet_size']    = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     148                                        $offset += 4;
     149                                        $thisfile_asf_filepropertiesobject['max_packet_size']    = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     150                                        $offset += 4;
     151                                        $thisfile_asf_filepropertiesobject['max_bitrate']        = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     152                                        $offset += 4;
     153
     154                                        if ($thisfile_asf_filepropertiesobject['flags']['broadcast']) {
     155
     156                                                // broadcast flag is set, some values invalid
     157                                                unset($thisfile_asf_filepropertiesobject['filesize']);
     158                                                unset($thisfile_asf_filepropertiesobject['data_packets']);
     159                                                unset($thisfile_asf_filepropertiesobject['play_duration']);
     160                                                unset($thisfile_asf_filepropertiesobject['send_duration']);
     161                                                unset($thisfile_asf_filepropertiesobject['min_packet_size']);
     162                                                unset($thisfile_asf_filepropertiesobject['max_packet_size']);
     163
     164                                        } else {
     165
     166                                                // broadcast flag NOT set, perform calculations
     167                                                $info['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000);
     168
     169                                                //$info['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate'];
     170                                                $info['bitrate'] = ((isset($thisfile_asf_filepropertiesobject['filesize']) ? $thisfile_asf_filepropertiesobject['filesize'] : $info['filesize']) * 8) / $info['playtime_seconds'];
     171                                        }
     172                                        break;
     173
     174                                case GETID3_ASF_Stream_Properties_Object:
     175                                        // Stream Properties Object: (mandatory, one per media stream)
     176                                        // Field Name                   Field Type   Size (bits)
     177                                        // Object ID                    GUID         128             // GUID for stream properties object - GETID3_ASF_Stream_Properties_Object
     178                                        // Object Size                  QWORD        64              // size of stream properties object, including 78 bytes of Stream Properties Object header
     179                                        // Stream Type                  GUID         128             // GETID3_ASF_Audio_Media, GETID3_ASF_Video_Media or GETID3_ASF_Command_Media
     180                                        // Error Correction Type        GUID         128             // GETID3_ASF_Audio_Spread for audio-only streams, GETID3_ASF_No_Error_Correction for other stream types
     181                                        // Time Offset                  QWORD        64              // 100-nanosecond units. typically zero. added to all timestamps of samples in the stream
     182                                        // Type-Specific Data Length    DWORD        32              // number of bytes for Type-Specific Data field
     183                                        // Error Correction Data Length DWORD        32              // number of bytes for Error Correction Data field
     184                                        // Flags                        WORD         16              //
     185                                        // * Stream Number              bits         7 (0x007F)      // number of this stream.  1 <= valid <= 127
     186                                        // * Reserved                   bits         8 (0x7F80)      // reserved - set to zero
     187                                        // * Encrypted Content Flag     bits         1 (0x8000)      // stream contents encrypted if set
     188                                        // Reserved                     DWORD        32              // reserved - set to zero
     189                                        // Type-Specific Data           BYTESTREAM   variable        // type-specific format data, depending on value of Stream Type
     190                                        // Error Correction Data        BYTESTREAM   variable        // error-correction-specific format data, depending on value of Error Correct Type
     191
     192                                        // There is one GETID3_ASF_Stream_Properties_Object for each stream (audio, video) but the
     193                                        // stream number isn't known until halfway through decoding the structure, hence it
     194                                        // it is decoded to a temporary variable and then stuck in the appropriate index later
     195
     196                                        $StreamPropertiesObjectData['offset']             = $NextObjectOffset + $offset;
     197                                        $StreamPropertiesObjectData['objectid']           = $NextObjectGUID;
     198                                        $StreamPropertiesObjectData['objectid_guid']      = $NextObjectGUIDtext;
     199                                        $StreamPropertiesObjectData['objectsize']         = $NextObjectSize;
     200                                        $StreamPropertiesObjectData['stream_type']        = substr($ASFHeaderData, $offset, 16);
     201                                        $offset += 16;
     202                                        $StreamPropertiesObjectData['stream_type_guid']   = $this->BytestringToGUID($StreamPropertiesObjectData['stream_type']);
     203                                        $StreamPropertiesObjectData['error_correct_type'] = substr($ASFHeaderData, $offset, 16);
     204                                        $offset += 16;
     205                                        $StreamPropertiesObjectData['error_correct_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['error_correct_type']);
     206                                        $StreamPropertiesObjectData['time_offset']        = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     207                                        $offset += 8;
     208                                        $StreamPropertiesObjectData['type_data_length']   = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     209                                        $offset += 4;
     210                                        $StreamPropertiesObjectData['error_data_length']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     211                                        $offset += 4;
     212                                        $StreamPropertiesObjectData['flags_raw']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     213                                        $offset += 2;
     214                                        $StreamPropertiesObjectStreamNumber               = $StreamPropertiesObjectData['flags_raw'] & 0x007F;
     215                                        $StreamPropertiesObjectData['flags']['encrypted'] = (bool) ($StreamPropertiesObjectData['flags_raw'] & 0x8000);
     216
     217                                        $offset += 4; // reserved - DWORD
     218                                        $StreamPropertiesObjectData['type_specific_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['type_data_length']);
     219                                        $offset += $StreamPropertiesObjectData['type_data_length'];
     220                                        $StreamPropertiesObjectData['error_correct_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['error_data_length']);
     221                                        $offset += $StreamPropertiesObjectData['error_data_length'];
     222
     223                                        switch ($StreamPropertiesObjectData['stream_type']) {
     224
     225                                                case GETID3_ASF_Audio_Media:
     226                                                        $thisfile_audio['dataformat']   = (!empty($thisfile_audio['dataformat'])   ? $thisfile_audio['dataformat']   : 'asf');
     227                                                        $thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr');
     228
     229                                                        $audiodata = getid3_riff::parseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16));
     230                                                        unset($audiodata['raw']);
     231                                                        $thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio);
     232                                                        break;
     233
     234                                                case GETID3_ASF_Video_Media:
     235                                                        $thisfile_video['dataformat']   = (!empty($thisfile_video['dataformat'])   ? $thisfile_video['dataformat']   : 'asf');
     236                                                        $thisfile_video['bitrate_mode'] = (!empty($thisfile_video['bitrate_mode']) ? $thisfile_video['bitrate_mode'] : 'cbr');
     237                                                        break;
     238
     239                                                case GETID3_ASF_Command_Media:
     240                                                default:
     241                                                        // do nothing
     242                                                        break;
     243
     244                                        }
     245
     246                                        $thisfile_asf['stream_properties_object'][$StreamPropertiesObjectStreamNumber] = $StreamPropertiesObjectData;
     247                                        unset($StreamPropertiesObjectData); // clear for next stream, if any
     248                                        break;
     249
     250                                case GETID3_ASF_Header_Extension_Object:
     251                                        // Header Extension Object: (mandatory, one only)
     252                                        // Field Name                   Field Type   Size (bits)
     253                                        // Object ID                    GUID         128             // GUID for Header Extension object - GETID3_ASF_Header_Extension_Object
     254                                        // Object Size                  QWORD        64              // size of Header Extension object, including 46 bytes of Header Extension Object header
     255                                        // Reserved Field 1             GUID         128             // hardcoded: GETID3_ASF_Reserved_1
     256                                        // Reserved Field 2             WORD         16              // hardcoded: 0x00000006
     257                                        // Header Extension Data Size   DWORD        32              // in bytes. valid: 0, or > 24. equals object size minus 46
     258                                        // Header Extension Data        BYTESTREAM   variable        // array of zero or more extended header objects
     259
     260                                        // shortcut
     261                                        $thisfile_asf['header_extension_object'] = array();
     262                                        $thisfile_asf_headerextensionobject      = &$thisfile_asf['header_extension_object'];
     263
     264                                        $thisfile_asf_headerextensionobject['offset']              = $NextObjectOffset + $offset;
     265                                        $thisfile_asf_headerextensionobject['objectid']            = $NextObjectGUID;
     266                                        $thisfile_asf_headerextensionobject['objectid_guid']       = $NextObjectGUIDtext;
     267                                        $thisfile_asf_headerextensionobject['objectsize']          = $NextObjectSize;
     268                                        $thisfile_asf_headerextensionobject['reserved_1']          = substr($ASFHeaderData, $offset, 16);
     269                                        $offset += 16;
     270                                        $thisfile_asf_headerextensionobject['reserved_1_guid']     = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']);
     271                                        if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) {
     272                                                $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).')';
     273                                                //return false;
     274                                                break;
     275                                        }
     276                                        $thisfile_asf_headerextensionobject['reserved_2']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     277                                        $offset += 2;
     278                                        if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) {
     279                                                $info['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"';
     280                                                //return false;
     281                                                break;
     282                                        }
     283                                        $thisfile_asf_headerextensionobject['extension_data_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     284                                        $offset += 4;
     285                                        $thisfile_asf_headerextensionobject['extension_data']      =                              substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']);
     286                                        $unhandled_sections = 0;
     287                                        $thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->ASF_HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections);
     288                                        if ($unhandled_sections === 0) {
     289                                                unset($thisfile_asf_headerextensionobject['extension_data']);
     290                                        }
     291                                        $offset += $thisfile_asf_headerextensionobject['extension_data_size'];
     292                                        break;
     293
     294                                case GETID3_ASF_Codec_List_Object:
     295                                        // Codec List Object: (optional, one only)
     296                                        // Field Name                   Field Type   Size (bits)
     297                                        // Object ID                    GUID         128             // GUID for Codec List object - GETID3_ASF_Codec_List_Object
     298                                        // Object Size                  QWORD        64              // size of Codec List object, including 44 bytes of Codec List Object header
     299                                        // Reserved                     GUID         128             // hardcoded: 86D15241-311D-11D0-A3A4-00A0C90348F6
     300                                        // Codec Entries Count          DWORD        32              // number of entries in Codec Entries array
     301                                        // Codec Entries                array of:    variable        //
     302                                        // * Type                       WORD         16              // 0x0001 = Video Codec, 0x0002 = Audio Codec, 0xFFFF = Unknown Codec
     303                                        // * Codec Name Length          WORD         16              // number of Unicode characters stored in the Codec Name field
     304                                        // * Codec Name                 WCHAR        variable        // array of Unicode characters - name of codec used to create the content
     305                                        // * Codec Description Length   WORD         16              // number of Unicode characters stored in the Codec Description field
     306                                        // * Codec Description          WCHAR        variable        // array of Unicode characters - description of format used to create the content
     307                                        // * Codec Information Length   WORD         16              // number of Unicode characters stored in the Codec Information field
     308                                        // * Codec Information          BYTESTREAM   variable        // opaque array of information bytes about the codec used to create the content
     309
     310                                        // shortcut
     311                                        $thisfile_asf['codec_list_object'] = array();
     312                                        $thisfile_asf_codeclistobject      = &$thisfile_asf['codec_list_object'];
     313
     314                                        $thisfile_asf_codeclistobject['offset']                    = $NextObjectOffset + $offset;
     315                                        $thisfile_asf_codeclistobject['objectid']                  = $NextObjectGUID;
     316                                        $thisfile_asf_codeclistobject['objectid_guid']             = $NextObjectGUIDtext;
     317                                        $thisfile_asf_codeclistobject['objectsize']                = $NextObjectSize;
     318                                        $thisfile_asf_codeclistobject['reserved']                  = substr($ASFHeaderData, $offset, 16);
     319                                        $offset += 16;
     320                                        $thisfile_asf_codeclistobject['reserved_guid']             = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']);
     321                                        if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) {
     322                                                $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}';
     323                                                //return false;
     324                                                break;
     325                                        }
     326                                        $thisfile_asf_codeclistobject['codec_entries_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     327                                        $offset += 4;
     328                                        for ($CodecEntryCounter = 0; $CodecEntryCounter < $thisfile_asf_codeclistobject['codec_entries_count']; $CodecEntryCounter++) {
     329                                                // shortcut
     330                                                $thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter] = array();
     331                                                $thisfile_asf_codeclistobject_codecentries_current = &$thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter];
     332
     333                                                $thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     334                                                $offset += 2;
     335                                                $thisfile_asf_codeclistobject_codecentries_current['type'] = $this->ASFCodecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']);
     336
     337                                                $CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
     338                                                $offset += 2;
     339                                                $thisfile_asf_codeclistobject_codecentries_current['name'] = substr($ASFHeaderData, $offset, $CodecNameLength);
     340                                                $offset += $CodecNameLength;
     341
     342                                                $CodecDescriptionLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
     343                                                $offset += 2;
     344                                                $thisfile_asf_codeclistobject_codecentries_current['description'] = substr($ASFHeaderData, $offset, $CodecDescriptionLength);
     345                                                $offset += $CodecDescriptionLength;
     346
     347                                                $CodecInformationLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     348                                                $offset += 2;
     349                                                $thisfile_asf_codeclistobject_codecentries_current['information'] = substr($ASFHeaderData, $offset, $CodecInformationLength);
     350                                                $offset += $CodecInformationLength;
     351
     352                                                if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) { // audio codec
     353
     354                                                        if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) {
     355                                                                $info['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"';
     356                                                        } else {
     357
     358                                                                list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']));
     359                                                                $thisfile_audio['codec'] = $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['name']);
     360
     361                                                                if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) {
     362                                                                        $thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000);
     363                                                                }
     364                                                                //if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) {
     365                                                                if (empty($thisfile_video['bitrate']) && !empty($thisfile_audio['bitrate']) && !empty($info['bitrate'])) {
     366                                                                        //$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate'];
     367                                                                        $thisfile_video['bitrate'] = $info['bitrate'] - $thisfile_audio['bitrate'];
     368                                                                }
     369
     370                                                                $AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency));
     371                                                                switch ($AudioCodecFrequency) {
     372                                                                        case 8:
     373                                                                        case 8000:
     374                                                                                $thisfile_audio['sample_rate'] = 8000;
     375                                                                                break;
     376
     377                                                                        case 11:
     378                                                                        case 11025:
     379                                                                                $thisfile_audio['sample_rate'] = 11025;
     380                                                                                break;
     381
     382                                                                        case 12:
     383                                                                        case 12000:
     384                                                                                $thisfile_audio['sample_rate'] = 12000;
     385                                                                                break;
     386
     387                                                                        case 16:
     388                                                                        case 16000:
     389                                                                                $thisfile_audio['sample_rate'] = 16000;
     390                                                                                break;
     391
     392                                                                        case 22:
     393                                                                        case 22050:
     394                                                                                $thisfile_audio['sample_rate'] = 22050;
     395                                                                                break;
     396
     397                                                                        case 24:
     398                                                                        case 24000:
     399                                                                                $thisfile_audio['sample_rate'] = 24000;
     400                                                                                break;
     401
     402                                                                        case 32:
     403                                                                        case 32000:
     404                                                                                $thisfile_audio['sample_rate'] = 32000;
     405                                                                                break;
     406
     407                                                                        case 44:
     408                                                                        case 441000:
     409                                                                                $thisfile_audio['sample_rate'] = 44100;
     410                                                                                break;
     411
     412                                                                        case 48:
     413                                                                        case 48000:
     414                                                                                $thisfile_audio['sample_rate'] = 48000;
     415                                                                                break;
     416
     417                                                                        default:
     418                                                                                $info['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')';
     419                                                                                break;
     420                                                                }
     421
     422                                                                if (!isset($thisfile_audio['channels'])) {
     423                                                                        if (strstr($AudioCodecChannels, 'stereo')) {
     424                                                                                $thisfile_audio['channels'] = 2;
     425                                                                        } elseif (strstr($AudioCodecChannels, 'mono')) {
     426                                                                                $thisfile_audio['channels'] = 1;
     427                                                                        }
     428                                                                }
     429
     430                                                        }
     431                                                }
     432                                        }
     433                                        break;
     434
     435                                case GETID3_ASF_Script_Command_Object:
     436                                        // Script Command Object: (optional, one only)
     437                                        // Field Name                   Field Type   Size (bits)
     438                                        // Object ID                    GUID         128             // GUID for Script Command object - GETID3_ASF_Script_Command_Object
     439                                        // Object Size                  QWORD        64              // size of Script Command object, including 44 bytes of Script Command Object header
     440                                        // Reserved                     GUID         128             // hardcoded: 4B1ACBE3-100B-11D0-A39B-00A0C90348F6
     441                                        // Commands Count               WORD         16              // number of Commands structures in the Script Commands Objects
     442                                        // Command Types Count          WORD         16              // number of Command Types structures in the Script Commands Objects
     443                                        // Command Types                array of:    variable        //
     444                                        // * Command Type Name Length   WORD         16              // number of Unicode characters for Command Type Name
     445                                        // * Command Type Name          WCHAR        variable        // array of Unicode characters - name of a type of command
     446                                        // Commands                     array of:    variable        //
     447                                        // * Presentation Time          DWORD        32              // presentation time of that command, in milliseconds
     448                                        // * Type Index                 WORD         16              // type of this command, as a zero-based index into the array of Command Types of this object
     449                                        // * Command Name Length        WORD         16              // number of Unicode characters for Command Name
     450                                        // * Command Name               WCHAR        variable        // array of Unicode characters - name of this command
     451
     452                                        // shortcut
     453                                        $thisfile_asf['script_command_object'] = array();
     454                                        $thisfile_asf_scriptcommandobject      = &$thisfile_asf['script_command_object'];
     455
     456                                        $thisfile_asf_scriptcommandobject['offset']               = $NextObjectOffset + $offset;
     457                                        $thisfile_asf_scriptcommandobject['objectid']             = $NextObjectGUID;
     458                                        $thisfile_asf_scriptcommandobject['objectid_guid']        = $NextObjectGUIDtext;
     459                                        $thisfile_asf_scriptcommandobject['objectsize']           = $NextObjectSize;
     460                                        $thisfile_asf_scriptcommandobject['reserved']             = substr($ASFHeaderData, $offset, 16);
     461                                        $offset += 16;
     462                                        $thisfile_asf_scriptcommandobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']);
     463                                        if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) {
     464                                                $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}';
     465                                                //return false;
     466                                                break;
     467                                        }
     468                                        $thisfile_asf_scriptcommandobject['commands_count']       = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     469                                        $offset += 2;
     470                                        $thisfile_asf_scriptcommandobject['command_types_count']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     471                                        $offset += 2;
     472                                        for ($CommandTypesCounter = 0; $CommandTypesCounter < $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) {
     473                                                $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
     474                                                $offset += 2;
     475                                                $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength);
     476                                                $offset += $CommandTypeNameLength;
     477                                        }
     478                                        for ($CommandsCounter = 0; $CommandsCounter < $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) {
     479                                                $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['presentation_time']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     480                                                $offset += 4;
     481                                                $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['type_index']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     482                                                $offset += 2;
     483
     484                                                $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
     485                                                $offset += 2;
     486                                                $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength);
     487                                                $offset += $CommandTypeNameLength;
     488                                        }
     489                                        break;
     490
     491                                case GETID3_ASF_Marker_Object:
     492                                        // Marker Object: (optional, one only)
     493                                        // Field Name                   Field Type   Size (bits)
     494                                        // Object ID                    GUID         128             // GUID for Marker object - GETID3_ASF_Marker_Object
     495                                        // Object Size                  QWORD        64              // size of Marker object, including 48 bytes of Marker Object header
     496                                        // Reserved                     GUID         128             // hardcoded: 4CFEDB20-75F6-11CF-9C0F-00A0C90349CB
     497                                        // Markers Count                DWORD        32              // number of Marker structures in Marker Object
     498                                        // Reserved                     WORD         16              // hardcoded: 0x0000
     499                                        // Name Length                  WORD         16              // number of bytes in the Name field
     500                                        // Name                         WCHAR        variable        // name of the Marker Object
     501                                        // Markers                      array of:    variable        //
     502                                        // * Offset                     QWORD        64              // byte offset into Data Object
     503                                        // * Presentation Time          QWORD        64              // in 100-nanosecond units
     504                                        // * Entry Length               WORD         16              // length in bytes of (Send Time + Flags + Marker Description Length + Marker Description + Padding)
     505                                        // * Send Time                  DWORD        32              // in milliseconds
     506                                        // * Flags                      DWORD        32              // hardcoded: 0x00000000
     507                                        // * Marker Description Length  DWORD        32              // number of bytes in Marker Description field
     508                                        // * Marker Description         WCHAR        variable        // array of Unicode characters - description of marker entry
     509                                        // * Padding                    BYTESTREAM   variable        // optional padding bytes
     510
     511                                        // shortcut
     512                                        $thisfile_asf['marker_object'] = array();
     513                                        $thisfile_asf_markerobject     = &$thisfile_asf['marker_object'];
     514
     515                                        $thisfile_asf_markerobject['offset']               = $NextObjectOffset + $offset;
     516                                        $thisfile_asf_markerobject['objectid']             = $NextObjectGUID;
     517                                        $thisfile_asf_markerobject['objectid_guid']        = $NextObjectGUIDtext;
     518                                        $thisfile_asf_markerobject['objectsize']           = $NextObjectSize;
     519                                        $thisfile_asf_markerobject['reserved']             = substr($ASFHeaderData, $offset, 16);
     520                                        $offset += 16;
     521                                        $thisfile_asf_markerobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']);
     522                                        if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) {
     523                                                $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}';
     524                                                break;
     525                                        }
     526                                        $thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     527                                        $offset += 4;
     528                                        $thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     529                                        $offset += 2;
     530                                        if ($thisfile_asf_markerobject['reserved_2'] != 0) {
     531                                                $info['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"';
     532                                                break;
     533                                        }
     534                                        $thisfile_asf_markerobject['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     535                                        $offset += 2;
     536                                        $thisfile_asf_markerobject['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['name_length']);
     537                                        $offset += $thisfile_asf_markerobject['name_length'];
     538                                        for ($MarkersCounter = 0; $MarkersCounter < $thisfile_asf_markerobject['markers_count']; $MarkersCounter++) {
     539                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['offset']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     540                                                $offset += 8;
     541                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['presentation_time']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
     542                                                $offset += 8;
     543                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length']              = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     544                                                $offset += 2;
     545                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['send_time']                 = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     546                                                $offset += 4;
     547                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['flags']                     = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     548                                                $offset += 4;
     549                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     550                                                $offset += 4;
     551                                                $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description']        = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']);
     552                                                $offset += $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'];
     553                                                $PaddingLength = $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] - 4 -  4 - 4 - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'];
     554                                                if ($PaddingLength > 0) {
     555                                                        $thisfile_asf_markerobject['markers'][$MarkersCounter]['padding']               = substr($ASFHeaderData, $offset, $PaddingLength);
     556                                                        $offset += $PaddingLength;
     557                                                }
     558                                        }
     559                                        break;
     560
     561                                case GETID3_ASF_Bitrate_Mutual_Exclusion_Object:
     562                                        // Bitrate Mutual Exclusion Object: (optional)
     563                                        // Field Name                   Field Type   Size (bits)
     564                                        // Object ID                    GUID         128             // GUID for Bitrate Mutual Exclusion object - GETID3_ASF_Bitrate_Mutual_Exclusion_Object
     565                                        // Object Size                  QWORD        64              // size of Bitrate Mutual Exclusion object, including 42 bytes of Bitrate Mutual Exclusion Object header
     566                                        // Exlusion Type                GUID         128             // nature of mutual exclusion relationship. one of: (GETID3_ASF_Mutex_Bitrate, GETID3_ASF_Mutex_Unknown)
     567                                        // Stream Numbers Count         WORD         16              // number of video streams
     568                                        // Stream Numbers               WORD         variable        // array of mutually exclusive video stream numbers. 1 <= valid <= 127
     569
     570                                        // shortcut
     571                                        $thisfile_asf['bitrate_mutual_exclusion_object'] = array();
     572                                        $thisfile_asf_bitratemutualexclusionobject       = &$thisfile_asf['bitrate_mutual_exclusion_object'];
     573
     574                                        $thisfile_asf_bitratemutualexclusionobject['offset']               = $NextObjectOffset + $offset;
     575                                        $thisfile_asf_bitratemutualexclusionobject['objectid']             = $NextObjectGUID;
     576                                        $thisfile_asf_bitratemutualexclusionobject['objectid_guid']        = $NextObjectGUIDtext;
     577                                        $thisfile_asf_bitratemutualexclusionobject['objectsize']           = $NextObjectSize;
     578                                        $thisfile_asf_bitratemutualexclusionobject['reserved']             = substr($ASFHeaderData, $offset, 16);
     579                                        $thisfile_asf_bitratemutualexclusionobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']);
     580                                        $offset += 16;
     581                                        if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) {
     582                                                $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).'}';
     583                                                //return false;
     584                                                break;
     585                                        }
     586                                        $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     587                                        $offset += 2;
     588                                        for ($StreamNumberCounter = 0; $StreamNumberCounter < $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) {
     589                                                $thisfile_asf_bitratemutualexclusionobject['stream_numbers'][$StreamNumberCounter] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     590                                                $offset += 2;
     591                                        }
     592                                        break;
     593
     594                                case GETID3_ASF_Error_Correction_Object:
     595                                        // Error Correction Object: (optional, one only)
     596                                        // Field Name                   Field Type   Size (bits)
     597                                        // Object ID                    GUID         128             // GUID for Error Correction object - GETID3_ASF_Error_Correction_Object
     598                                        // Object Size                  QWORD        64              // size of Error Correction object, including 44 bytes of Error Correction Object header
     599                                        // Error Correction Type        GUID         128             // type of error correction. one of: (GETID3_ASF_No_Error_Correction, GETID3_ASF_Audio_Spread)
     600                                        // Error Correction Data Length DWORD        32              // number of bytes in Error Correction Data field
     601                                        // Error Correction Data        BYTESTREAM   variable        // structure depends on value of Error Correction Type field
     602
     603                                        // shortcut
     604                                        $thisfile_asf['error_correction_object'] = array();
     605                                        $thisfile_asf_errorcorrectionobject      = &$thisfile_asf['error_correction_object'];
     606
     607                                        $thisfile_asf_errorcorrectionobject['offset']                = $NextObjectOffset + $offset;
     608                                        $thisfile_asf_errorcorrectionobject['objectid']              = $NextObjectGUID;
     609                                        $thisfile_asf_errorcorrectionobject['objectid_guid']         = $NextObjectGUIDtext;
     610                                        $thisfile_asf_errorcorrectionobject['objectsize']            = $NextObjectSize;
     611                                        $thisfile_asf_errorcorrectionobject['error_correction_type'] = substr($ASFHeaderData, $offset, 16);
     612                                        $offset += 16;
     613                                        $thisfile_asf_errorcorrectionobject['error_correction_guid'] = $this->BytestringToGUID($thisfile_asf_errorcorrectionobject['error_correction_type']);
     614                                        $thisfile_asf_errorcorrectionobject['error_correction_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
     615                                        $offset += 4;
     616                                        switch ($thisfile_asf_errorcorrectionobject['error_correction_type']) {
     617                                                case GETID3_ASF_No_Error_Correction:
     618                                                        // should be no data, but just in case there is, skip to the end of the field
     619                                                        $offset += $thisfile_asf_errorcorrectionobject['error_correction_data_length'];
     620                                                        break;
     621
     622                                                case GETID3_ASF_Audio_Spread:
     623                                                        // Field Name                   Field Type   Size (bits)
     624                                                        // Span                         BYTE         8               // number of packets over which audio will be spread.
     625                                                        // Virtual Packet Length        WORD         16              // size of largest audio payload found in audio stream
     626                                                        // Virtual Chunk Length         WORD         16              // size of largest audio payload found in audio stream
     627                                                        // Silence Data Length          WORD         16              // number of bytes in Silence Data field
     628                                                        // Silence Data                 BYTESTREAM   variable        // hardcoded: 0x00 * (Silence Data Length) bytes
     629
     630                                                        $thisfile_asf_errorcorrectionobject['span']                  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 1));
     631                                                        $offset += 1;
     632                                                        $thisfile_asf_errorcorrectionobject['virtual_packet_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     633                                                        $offset += 2;
     634                                                        $thisfile_asf_errorcorrectionobject['virtual_chunk_length']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     635                                                        $offset += 2;
     636                                                        $thisfile_asf_errorcorrectionobject['silence_data_length']   = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     637                                                        $offset += 2;
     638                                                        $thisfile_asf_errorcorrectionobject['silence_data']          = substr($ASFHeaderData, $offset, $thisfile_asf_errorcorrectionobject['silence_data_length']);
     639                                                        $offset += $thisfile_asf_errorcorrectionobject['silence_data_length'];
     640                                                        break;
     641
     642                                                default:
     643                                                        $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).'}';
     644                                                        //return false;
     645                                                        break;
     646                                        }
     647
     648                                        break;
     649
     650                                case GETID3_ASF_Content_Description_Object:
     651                                        // Content Description Object: (optional, one only)
     652                                        // Field Name                   Field Type   Size (bits)
     653                                        // Object ID                    GUID         128             // GUID for Content Description object - GETID3_ASF_Content_Description_Object
     654                                        // Object Size                  QWORD        64              // size of Content Description object, including 34 bytes of Content Description Object header
     655                                        // Title Length                 WORD         16              // number of bytes in Title field
     656                                        // Author Length                WORD         16              // number of bytes in Author field
     657                                        // Copyright Length             WORD         16              // number of bytes in Copyright field
     658                                        // Description Length           WORD         16              // number of bytes in Description field
     659                                        // Rating Length                WORD         16              // number of bytes in Rating field
     660                                        // Title                        WCHAR        16              // array of Unicode characters - Title
     661                                        // Author                       WCHAR        16              // array of Unicode characters - Author
     662                                        // Copyright                    WCHAR        16              // array of Unicode characters - Copyright
     663                                        // Description                  WCHAR        16              // array of Unicode characters - Description
     664                                        // Rating                       WCHAR        16              // array of Unicode characters - Rating
     665
     666                                        // shortcut
     667                                        $thisfile_asf['content_description_object'] = array();
     668                                        $thisfile_asf_contentdescriptionobject      = &$thisfile_asf['content_description_object'];
     669
     670                                        $thisfile_asf_contentdescriptionobject['offset']                = $NextObjectOffset + $offset;
     671                                        $thisfile_asf_contentdescriptionobject['objectid']              = $NextObjectGUID;
     672                                        $thisfile_asf_contentdescriptionobject['objectid_guid']         = $NextObjectGUIDtext;
     673                                        $thisfile_asf_contentdescriptionobject['objectsize']            = $NextObjectSize;
     674                                        $thisfile_asf_contentdescriptionobject['title_length']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     675                                        $offset += 2;
     676                                        $thisfile_asf_contentdescriptionobject['author_length']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     677                                        $offset += 2;
     678                                        $thisfile_asf_contentdescriptionobject['copyright_length']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     679                                        $offset += 2;
     680                                        $thisfile_asf_contentdescriptionobject['description_length']    = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     681                                        $offset += 2;
     682                                        $thisfile_asf_contentdescriptionobject['rating_length']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     683                                        $offset += 2;
     684                                        $thisfile_asf_contentdescriptionobject['title']                 = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['title_length']);
     685                                        $offset += $thisfile_asf_contentdescriptionobject['title_length'];
     686                                        $thisfile_asf_contentdescriptionobject['author']                = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['author_length']);
     687                                        $offset += $thisfile_asf_contentdescriptionobject['author_length'];
     688                                        $thisfile_asf_contentdescriptionobject['copyright']             = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['copyright_length']);
     689                                        $offset += $thisfile_asf_contentdescriptionobject['copyright_length'];
     690                                        $thisfile_asf_contentdescriptionobject['description']           = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['description_length']);
     691                                        $offset += $thisfile_asf_contentdescriptionobject['description_length'];
     692                                        $thisfile_asf_contentdescriptionobject['rating']                = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['rating_length']);
     693                                        $offset += $thisfile_asf_contentdescriptionobject['rating_length'];
     694
     695                                        $ASFcommentKeysToCopy = array('title'=>'title', 'author'=>'artist', 'copyright'=>'copyright', 'description'=>'comment', 'rating'=>'rating');
     696                                        foreach ($ASFcommentKeysToCopy as $keytocopyfrom => $keytocopyto) {
     697                                                if (!empty($thisfile_asf_contentdescriptionobject[$keytocopyfrom])) {
     698                                                        $thisfile_asf_comments[$keytocopyto][] = $this->TrimTerm($thisfile_asf_contentdescriptionobject[$keytocopyfrom]);
     699                                                }
     700                                        }
     701                                        break;
     702
     703                                case GETID3_ASF_Extended_Content_Description_Object:
     704                                        // Extended Content Description Object: (optional, one only)
     705                                        // Field Name                   Field Type   Size (bits)
     706                                        // Object ID                    GUID         128             // GUID for Extended Content Description object - GETID3_ASF_Extended_Content_Description_Object
     707                                        // Object Size                  QWORD        64              // size of ExtendedContent Description object, including 26 bytes of Extended Content Description Object header
     708                                        // Content Descriptors Count    WORD         16              // number of entries in Content Descriptors list
     709                                        // Content Descriptors          array of:    variable        //
     710                                        // * Descriptor Name Length     WORD         16              // size in bytes of Descriptor Name field
     711                                        // * Descriptor Name            WCHAR        variable        // array of Unicode characters - Descriptor Name
     712                                        // * Descriptor Value Data Type WORD         16              // Lookup array:
     713                                                                                                                                                                        // 0x0000 = Unicode String (variable length)
     714                                                                                                                                                                        // 0x0001 = BYTE array     (variable length)
     715                                                                                                                                                                        // 0x0002 = BOOL           (DWORD, 32 bits)
     716                                                                                                                                                                        // 0x0003 = DWORD          (DWORD, 32 bits)
     717                                                                                                                                                                        // 0x0004 = QWORD          (QWORD, 64 bits)
     718                                                                                                                                                                        // 0x0005 = WORD           (WORD,  16 bits)
     719                                        // * Descriptor Value Length    WORD         16              // number of bytes stored in Descriptor Value field
     720                                        // * Descriptor Value           variable     variable        // value for Content Descriptor
     721
     722                                        // shortcut
     723                                        $thisfile_asf['extended_content_description_object'] = array();
     724                                        $thisfile_asf_extendedcontentdescriptionobject       = &$thisfile_asf['extended_content_description_object'];
     725
     726                                        $thisfile_asf_extendedcontentdescriptionobject['offset']                    = $NextObjectOffset + $offset;
     727                                        $thisfile_asf_extendedcontentdescriptionobject['objectid']                  = $NextObjectGUID;
     728                                        $thisfile_asf_extendedcontentdescriptionobject['objectid_guid']             = $NextObjectGUIDtext;
     729                                        $thisfile_asf_extendedcontentdescriptionobject['objectsize']                = $NextObjectSize;
     730                                        $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     731                                        $offset += 2;
     732                                        for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) {
     733                                                // shortcut
     734                                                $thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array();
     735                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current                 = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter];
     736
     737                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['base_offset']  = $offset + 30;
     738                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     739                                                $offset += 2;
     740                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']         = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']);
     741                                                $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length'];
     742                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']   = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     743                                                $offset += 2;
     744                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
     745                                                $offset += 2;
     746                                                $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']        = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']);
     747                                                $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'];
     748                                                switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) {
     749                                                        case 0x0000: // Unicode string
     750