[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/ID3/ -> getid3.php (source)

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


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7